diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d352b823d0..23c94155f3 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,7 @@ # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. #------------------------------------------------------------------------------------------------------------- -FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-10 +FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-12 ARG TARGET_DISPLAY=":1" diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 79fdfb487a..e16795062d 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -6,9 +6,9 @@ This repository includes configuration for a development container for working w ## Quick start - local -1. Install Docker Desktop or Docker on your local machine. (See [docs](https://aka.ms/vscode-remote/containers/getting-started) for additional details.) +1. Install Docker Desktop or Docker for Linux on your local machine. (See [docs](https://aka.ms/vscode-remote/containers/getting-started) for additional details.) -2. [Docker Desktop] If you are not using the new WSL2 Docker Desktop engine, increase the resources allocated to Docker Desktop to at least **4 Cores and 4 GB of RAM (8 GB recommended)**. Right-click on the Docker status bar item, go to **Preferences/Settings > Resources > Advanced** to do so. +2. **Important**: Docker needs at least **4 Cores and 6 GB of RAM (8 GB recommended)** to run full build. If you on macOS, or using the old Hyper-V engine for Windows, update these values for Docker Desktop by right-clicking on the Docker status bar item, going to **Preferences/Settings > Resources > Advanced**. > **Note:** The [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) extension is included in the container so you can keep an eye on CPU/Memory in the status bar. @@ -44,7 +44,7 @@ Next: **[Try it out!](#try-it)** 3. Press Ctrl/Cmd + Shift + P and select **Codespaces: Create New Codespace**. -4. Use default settings, select a plan, and then enter the repository URL `https://github.com/microsoft/vscode` (or a branch or PR URL) in the input box when prompted. +4. Use default settings (which should include **Standard** 4 core, 8 GB RAM Codespace), select a plan, and then enter the repository URL `https://github.com/microsoft/vscode` (or a branch or PR URL) in the input box when prompted. 5. After the container is running, open a web browser and go to [http://localhost:6080](http://localhost:6080) or use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password. diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b9f3e6d79b..722bace6df 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -12,7 +12,11 @@ } }, "overrideCommand": false, - "runArgs": ["--init"], + "runArgs": [ + "--init", + // seccomp=unconfined is required for Chrome sandboxing + "--security-opt", "seccomp=unconfined" + ], "settings": { // zsh is also available diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 76f0aeadfa..2cd0a32b9c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,7 +4,6 @@ "recommendations": [ "dbaeumer.vscode-eslint", "EditorConfig.EditorConfig", - "msjsdiag.debugger-for-chrome", - "ms-vscode.vscode-github-issue-notebooks" + "msjsdiag.debugger-for-chrome" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 1a19bbf549..04efa7aa70 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -92,6 +92,7 @@ "env": { "VSCODE_EXTHOST_WILL_SEND_SOCKET": null }, + "cleanUp": "wholeBrowser", "breakOnLoad": false, "urlFilter": "*workbench.html*", "runtimeArgs": [ diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues new file mode 100644 index 0000000000..f36bb39758 --- /dev/null +++ b/.vscode/notebooks/api.github-issues @@ -0,0 +1,38 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "#### Config", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"July 2020\"", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Finalization", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repo $milestone label:api-finalization", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Proposals", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repo $milestone is:open label:api-proposal ", + "editable": true + } +] \ No newline at end of file diff --git a/.vscode/notebooks/inbox.github-issues b/.vscode/notebooks/inbox.github-issues index 9edd342251..8931e7b0b3 100644 --- a/.vscode/notebooks/inbox.github-issues +++ b/.vscode/notebooks/inbox.github-issues @@ -2,12 +2,14 @@ { "kind": 1, "language": "markdown", - "value": "##### `Config`: defines the inbox query" + "value": "##### `Config`: defines the inbox query", + "editable": true }, { "kind": 2, "language": "github-issues", - "value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item " + "value": "$inbox=repo:microsoft/vscode is:open no:assignee -label:feature-request -label:testplan-item -label:plan-item ", + "editable": true }, { "kind": 1, @@ -18,7 +20,7 @@ { "kind": 2, "language": "github-issues", - "value": "$inbox -label:\"needs more info\"", + "value": "$inbox -label:\"needs more info\" -label:emmet", "editable": true }, { @@ -31,6 +33,6 @@ "kind": 2, "language": "github-issues", "value": "$inbox", - "editable": true + "editable": false } ] \ No newline at end of file diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index c6d2309b39..dc6d33365d 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -20,7 +20,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos $milestone assignee:@me is:open\n", + "value": "$repos $milestone assignee:@me is:open", "editable": false }, { diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues new file mode 100644 index 0000000000..6f32df8e07 --- /dev/null +++ b/.vscode/notebooks/verification.github-issues @@ -0,0 +1,55 @@ +[ + { + "kind": 1, + "language": "markdown", + "value": "### Bug Verification Queries\n\nBefore shipping we want to verify _all_ bugs. That means when a bug is fixed we check that the fix actually works. It's always best to start with bugs that you have filed and the proceed with bugs that have been filed from users outside the development team. ", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "#### Config: update list of `repos` and the `milestone`", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks \n$milestone=milestone:\"June 2020\"", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "### Bugs You Filed", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate author:@me", + "editable": false + }, + { + "kind": 1, + "language": "markdown", + "value": "### Bugs From Outside", + "editable": true + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate -author:@me -assignee:@me label:bug -label:verified -author:@me -author:aeschli -author:alexdima -author:alexr00 -author:bpasero -author:chrisdias -author:chrmarti -author:connor4312 -author:dbaeumer -author:deepak1556 -author:eamodio -author:egamma -author:gregvanl -author:isidorn -author:JacksonKearl -author:joaomoreno -author:jrieken -author:lramos15 -author:lszomoru -author:misolori -author:mjbvz -author:rebornix -author:RMacfarlane -author:roblourens -author:sana-ajani -author:sandy081 -author:sbatten -author:Tyriar -author:weinand", + "editable": false + }, + { + "kind": 1, + "language": "markdown", + "value": "### All" + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repos $milestone is:closed -assignee:@me label:bug -label:verified -label:*duplicate", + "editable": false + } +] \ No newline at end of file diff --git a/.vscode/searches/es6.code-search b/.vscode/searches/es6.code-search index c89264a3cf..9cf8cf0b26 100644 --- a/.vscode/searches/es6.code-search +++ b/.vscode/searches/es6.code-search @@ -2,7 +2,7 @@ # Flags: CaseSensitive WordMatch # ContextLines: 2 -16 results - 5 files +14 results - 4 files src/vs/base/browser/dom.ts: 81 }; @@ -34,24 +34,11 @@ src/vs/base/common/arrays.ts: 420 */ 421 export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; - 560 - 561 /** - 562: * @deprecated ES6: use `Array.find` - 563 */ - 564 export function find(arr: ArrayLike, predicate: (value: T, index: number, arr: ArrayLike) => any): T | undefined { - -src/vs/base/common/map.ts: - 11 - 12 /** - 13: * @deprecated ES6: use `[...SetOrMap.values()]` - 14 */ - 15 export function values(set: Set): V[]; - - 22 - 23 /** - 24: * @deprecated ES6: use `[...map.keys()]` - 25 */ - 26 export function keys(map: Map): K[] { + 569 + 570 /** + 571: * @deprecated ES6: use `Array.find` + 572 */ + 573 export function find(arr: ArrayLike, predicate: (value: T, index: number, arr: ArrayLike) => any): T | undefined { src/vs/base/common/objects.ts: 115 diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 076129b820..42d0ba0f26 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,12 +3,8 @@ "tasks": [ { "type": "npm", - "script": "watchd", - "label": "Build VS Code", - "group": { - "kind": "build", - "isDefault": true - }, + "script": "watch-clientd", + "label": "Build VS Code Core", "isBackground": true, "presentation": { "reveal": "never" @@ -31,6 +27,43 @@ } } }, + { + "type": "npm", + "script": "watch-extensionsd", + "label": "Build VS Code Extensions", + "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" + } + } + }, + { + "label": "Build VS Code", + "dependsOn": [ + "Build VS Code Core", + "Build VS Code Extensions" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, { "type": "npm", "script": "strict-vscode-watch", @@ -55,6 +88,14 @@ }, "problemMatcher": "$tsc" }, + { + "label": "Kill Build VS Code", + "dependsOn": [ + "Kill Build VS Code Core", + "Kill Build VS Code Extensions" + ], + "group": "build" + }, { "type": "npm", "script": "watch-webd", diff --git a/.yarnrc b/.yarnrc index 135e10442a..4c5125d892 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "7.3.2" +target "8.3.3" runtime "electron" diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 4c7e36bdc8..06dfe2b783 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -36,23 +36,6 @@ const { compileBuildTask } = require('./gulpfile.compile'); const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname)); -const baseModules = Object.keys(process.binding('natives')).filter(n => !/^_|\//.test(n)); -const nodeModules = [ // {{SQL CARBON EDIT}} - 'electron', - 'original-fs', - 'rxjs/Observable', - 'rxjs/add/observable/fromPromise', - 'rxjs/Subject', - 'rxjs/Observer', - 'slickgrid/lib/jquery.event.drag-2.3.0', - 'slickgrid/lib/jquery-ui-1.9.2', - 'slickgrid/slick.core', - 'slickgrid/slick.grid', - 'slickgrid/slick.editors', - 'slickgrid/slick.dataview'] - .concat(Object.keys(product.dependencies || {})) - .concat(_.uniq(productionDependencies.map(d => d.name))) - .concat(baseModules); // Build const vscodeEntryPoints = _.flatten([ @@ -75,6 +58,7 @@ const vscodeResources = [ 'out-build/paths.js', 'out-build/vs/**/*.{svg,png,html}', '!out-build/vs/code/browser/**/*.html', + '!out-build/vs/editor/standalone/**/*.svg', 'out-build/vs/base/common/performance.js', 'out-build/vs/base/node/languagePacks.js', 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', @@ -123,7 +107,7 @@ const optimizeVSCodeTask = task.define('optimize-vscode', task.series( src: 'out-build', entryPoints: vscodeEntryPoints, resources: vscodeResources, - loaderConfig: common.loaderConfig(nodeModules), + loaderConfig: common.loaderConfig(), out: 'out-vscode', bundleInfo: undefined }) @@ -134,12 +118,6 @@ const sourceMappingURLBase = `https://sqlopsbuilds.blob.core.windows.net/sourcem const minifyVSCodeTask = task.define('minify-vscode', task.series( optimizeVSCodeTask, util.rimraf('out-vscode-min'), - () => { - const fullpath = path.join(process.cwd(), 'out-vscode/bootstrap-window.js'); - const contents = fs.readFileSync(fullpath).toString(); - const newContents = contents.replace('[/*BUILD->INSERT_NODE_MODULES*/]', JSON.stringify(nodeModules)); - fs.writeFileSync(fullpath, newContents); - }, common.minifyTask('out-vscode', `${sourceMappingURLBase}/core`) )); gulp.task(minifyVSCodeTask); diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 1e0fd4b09a..c19c69bf8e 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -94,8 +94,8 @@ function buildWin32Setup(arch, target) { IncompatibleTargetAppId: { 'ia32': product.win32AppId, 'x64': product.win32x64AppId, 'arm64': product.win32arm64AppId }[arch], IncompatibleArchAppId: { 'ia32': x64AppId, 'x64': ia32AppId, 'arm64': ia32AppId }[arch], AppUserId: product.win32AppUserModelId, - ArchitecturesAllowed: { 'ia32': '', 'x64': 'x64', 'arm64': '' }[arch], - ArchitecturesInstallIn64BitMode: { 'ia32': '', 'x64': 'x64', 'arm64': '' }[arch], + ArchitecturesAllowed: { 'ia32': '', 'x64': 'x64', 'arm64': 'arm64' }[arch], + ArchitecturesInstallIn64BitMode: { 'ia32': '', 'x64': 'x64', 'arm64': 'arm64' }[arch], SourceDir: sourcePath, RepoDir: repoPath, OutputDir: outputPath, diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 7a36b15a2e..f8b50731c2 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -18,6 +18,7 @@ const reporter_1 = require("./reporter"); const util = require("./util"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); +const os = require("os"); const watch = require('./watch'); const reporter = reporter_1.createReporter(); function getTypeScriptCompilerOptions(src) { @@ -69,6 +70,9 @@ function createCompile(src, build, emitError) { } function compileTask(src, out, build) { return function () { + if (os.totalmem() < 4000000000) { + throw new Error('compilation requires 4GB of RAM'); + } const compile = createCompile(src, build, true); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); let generator = new MonacoGenerator(false); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 6409322743..4f1260b09a 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -18,6 +18,7 @@ import { createReporter } from './reporter'; import * as util from './util'; import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; +import * as os from 'os'; import ts = require('typescript'); const watch = require('./watch'); @@ -81,6 +82,11 @@ function createCompile(src: string, build: boolean, emitError?: boolean) { export function compileTask(src: string, out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { + + if (os.totalmem() < 4_000_000_000) { + throw new Error('compilation requires 4GB of RAM'); + } + const compile = createCompile(src, build, true); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); let generator = new MonacoGenerator(false); diff --git a/build/lib/i18n.js b/build/lib/i18n.js index 904ef2ab75..b35bf2336b 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -1147,12 +1147,7 @@ function createIslFile(originalFilePath, messages, language, innoSetup) { 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+ ${innoSetup.defaultInfo.name} messages ***`); - } - else { - content.push(line); - } + content.push(line); } else { let sections = line.split('='); diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index decc7dfd92..38947dd1aa 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -1316,11 +1316,7 @@ function createIslFile(originalFilePath: string, messages: Map, language 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+ ${innoSetup.defaultInfo!.name} messages ***`); - } else { - content.push(line); - } + content.push(line); } else { let sections: string[] = line.split('='); let key = sections[0]; diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 19104ca2d3..df7097a9fe 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -28,14 +28,15 @@ const REPO_ROOT_PATH = path.join(__dirname, '../..'); function log(prefix, message) { fancyLog(ansiColors.cyan('[' + prefix + ']'), message); } -function loaderConfig(emptyPaths) { +function loaderConfig() { const result = { paths: { 'vs': 'out-build/vs', 'sql': 'out-build/sql', - 'vscode': 'empty:' + 'vscode': 'empty:', + 'azdata': 'empty:' // {{SQL CARBON EDIT}} }, - nodeModules: emptyPaths || [] + amdModulesPattern: /^(vs|sql)\// // {{SQL CARBON EDIT}} include sql in regex }; result['vs/css'] = { inlineResources: true }; return result; diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index b3e1f95759..828fbca507 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -32,14 +32,15 @@ function log(prefix: string, message: string): void { fancyLog(ansiColors.cyan('[' + prefix + ']'), message); } -export function loaderConfig(emptyPaths?: string[]) { +export function loaderConfig() { const result: any = { paths: { 'vs': 'out-build/vs', 'sql': 'out-build/sql', // {{SQL CARBON EDIT}} - 'vscode': 'empty:' + 'vscode': 'empty:', + 'azdata': 'empty:' // {{SQL CARBON EDIT}} }, - nodeModules: emptyPaths || [] + amdModulesPattern: /^(vs|sql)\// // {{SQL CARBON EDIT}} include sql in regex }; result['vs/css'] = { inlineResources: true }; diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 9d2734da6d..ff9bb5498b 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -33,7 +33,7 @@ function yarnInstall(location, opts) { yarnInstall('extensions'); // node modules shared by all extensions -if (!(process.platform === 'win32' && process.env['npm_config_arch'] === 'arm64')) { +if (!(process.platform === 'win32' && (process.arch === 'arm64' || process.env['npm_config_arch'] === 'arm64'))) { yarnInstall('remote'); // node modules used by vscode server yarnInstall('remote/web'); // node modules used by vscode web } @@ -73,3 +73,5 @@ yarnInstall('test/automation'); // node modules required for smoketest yarnInstall('test/smoke'); // node modules required for smoketest yarnInstall('test/integration/browser'); // node modules required for integration yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron + +cp.execSync('git config pull.rebase true'); diff --git a/build/package.json b/build/package.json index 09863d4011..044f4c5fa1 100644 --- a/build/package.json +++ b/build/package.json @@ -40,7 +40,7 @@ "gulp-bom": "^1.0.0", "gulp-sourcemaps": "^1.11.0", "gulp-uglify": "^3.0.0", - "iconv-lite-umd": "0.6.5", + "iconv-lite-umd": "0.6.8", "mime": "^1.3.4", "minimatch": "3.0.4", "minimist": "^1.2.3", @@ -49,7 +49,7 @@ "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-node-resolve": "^5.2.0", "terser": "4.3.8", - "typescript": "^4.0.0-dev.20200629", + "typescript": "^4.0.0-dev.20200708", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.6.0", "xml2js": "^0.4.17" diff --git a/build/win32/code.iss b/build/win32/code.iss index 321f8be01b..81e14a8386 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -34,6 +34,7 @@ VersionInfoVersion={#RawVersion} ShowLanguageDialog=auto ArchitecturesAllowed={#ArchitecturesAllowed} ArchitecturesInstallIn64BitMode={#ArchitecturesInstallIn64BitMode} +WizardStyle=modern #ifdef Sign SignTool=esrp @@ -47,7 +48,7 @@ DefaultDirName={pf}\{#DirName} #endif [Languages] -Name: "english"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.isl,{#RepoDir}\build\win32\i18n\messages.en.isl" {#LocalizedLanguageFile} +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")} @@ -58,7 +59,7 @@ Name: "korean"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.ko.isl,{#Repo 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")} Name: "brazilianPortuguese"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl,{#RepoDir}\build\win32\i18n\messages.pt-br.isl" {#LocalizedLanguageFile("ptb")} -Name: "hungarian"; MessagesFile: "compiler:Languages\Hungarian.isl,{#RepoDir}\build\win32\i18n\messages.hu.isl" {#LocalizedLanguageFile("hun")} +Name: "hungarian"; MessagesFile: "{#RepoDir}\build\win32\i18n\Default.hu.isl,{#RepoDir}\build\win32\i18n\messages.hu.isl" {#LocalizedLanguageFile("hun")} Name: "turkish"; MessagesFile: "compiler:Languages\Turkish.isl,{#RepoDir}\build\win32\i18n\messages.tr.isl" {#LocalizedLanguageFile("trk")} [InstallDelete] @@ -147,7 +148,7 @@ begin Result := True; #if "user" == InstallTarget - if not WizardSilent() and IsAdminLoggedOn() then begin + if not WizardSilent() and IsAdmin() then begin if MsgBox('This User Installer is not meant to be run as an Administrator. If you would like to install Azure Data Studio for all users in this system, download the System Installer instead from https://docs.microsoft.com/sql/azure-data-studio/download. Are you sure you want to continue?', mbError, MB_OKCANCEL) = IDCANCEL then begin Result := False; end; diff --git a/build/win32/i18n/Default.hu.isl b/build/win32/i18n/Default.hu.isl new file mode 100644 index 0000000000..8c57d20a59 --- /dev/null +++ b/build/win32/i18n/Default.hu.isl @@ -0,0 +1,366 @@ +;Inno Setup version 6.0.3+ Hungarian messages +;Based on the translation of Kornél Pál, kornelpal@gmail.com +;István Szabó, E-mail: istvanszabo890629@gmail.com +; +; 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=Magyar +LanguageID=$040E +LanguageCodePage=1250 +; 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 CE +;TitleFontSize=29 +;CopyrightFontName=Arial CE +;CopyrightFontSize=8 + +[Messages] + +; *** Application titles +SetupAppTitle=Telepítő +SetupWindowTitle=%1 - Telepítő +UninstallAppTitle=Eltávolító +UninstallAppFullTitle=%1 Eltávolító + +; *** Misc. common +InformationTitle=Információk +ConfirmTitle=Megerősít +ErrorTitle=Hiba + +; *** SetupLdr messages +SetupLdrStartupMessage=%1 telepítve lesz. Szeretné folytatni? +LdrCannotCreateTemp=Átmeneti fájl létrehozása nem lehetséges. A telepítés megszakítva +LdrCannotExecTemp=Fájl futtatása nem lehetséges az átmeneti könyvtárban. A telepítés megszakítva +HelpTextNote= + +; *** Startup error messages +LastErrorMessage=%1.%n%nHiba %2: %3 +SetupFileMissing=A(z) %1 fájl hiányzik a telepítő könyvtárából. Kérem hárítsa el a problémát, vagy szerezzen be egy másik példányt a programból! +SetupFileCorrupt=A telepítési fájlok sérültek. Kérem, szerezzen be új másolatot a programból! +SetupFileCorruptOrWrongVer=A telepítési fájlok sérültek, vagy inkompatibilisek a telepítő ezen verziójával. Hárítsa el a problémát, vagy szerezzen be egy másik példányt a programból! +InvalidParameter=A parancssorba átadott paraméter érvénytelen:%n%n%1 +SetupAlreadyRunning=A Telepítő már fut. +WindowsVersionNotSupported=A program nem támogatja a Windows ezen verzióját. +WindowsServicePackRequired=A program futtatásához %1 Service Pack %2 vagy újabb szükséges. +NotOnThisPlatform=Ez a program nem futtatható %1 alatt. +OnlyOnThisPlatform=Ezt a programot %1 alatt kell futtatni. +OnlyOnTheseArchitectures=A program kizárólag a következő processzor architektúrákhoz tervezett Windows-on telepíthető:%n%n%1 +WinVersionTooLowError=A program futtatásához %1 %2 verziója vagy későbbi szükséges. +WinVersionTooHighError=Ez a program nem telepíthető %1 %2 vagy későbbire. +AdminPrivilegesRequired=Csak rendszergazdai módban telepíthető ez a program. +PowerUserPrivilegesRequired=Csak rendszergazdaként vagy kiemelt felhasználóként telepíthető ez a program. +SetupAppRunningError=A telepítő úgy észlelte %1 jelenleg fut.%n%nZárja be az összes példányt, majd kattintson az 'OK'-ra a folytatáshoz, vagy a 'Mégse'-re a kilépéshez. +UninstallAppRunningError=Az eltávolító úgy észlelte %1 jelenleg fut.%n%nZárja be az összes példányt, majd kattintson az 'OK'-ra a folytatáshoz, vagy a 'Mégse'-re a kilépéshez. + +; *** Startup questions +PrivilegesRequiredOverrideTitle=Telepítési mód kiválasztása +PrivilegesRequiredOverrideInstruction=Válasszon telepítési módot +PrivilegesRequiredOverrideText1=%1 telepíthető az összes felhasználónak (rendszergazdai jogok szükségesek), vagy csak magának. +PrivilegesRequiredOverrideText2=%1 csak magának telepíthető, vagy az összes felhasználónak (rendszergazdai jogok szükségesek). +PrivilegesRequiredOverrideAllUsers=Telepítés &mindenkinek +PrivilegesRequiredOverrideAllUsersRecommended=Telepítés &mindenkinek (ajánlott) +PrivilegesRequiredOverrideCurrentUser=Telepítés csak &nekem +PrivilegesRequiredOverrideCurrentUserRecommended=Telepítés csak &nekem (ajánlott) + +; *** Misc. errors +ErrorCreatingDir=A Telepítő nem tudta létrehozni a(z) "%1" könyvtárat +ErrorTooManyFilesInDir=Nem hozható létre fájl a(z) "%1" könyvtárban, mert az már túl sok fájlt tartalmaz + +; *** Setup common messages +ExitSetupTitle=Kilépés a telepítőből +ExitSetupMessage=A telepítés még folyamatban van. Ha most kilép, a program nem kerül telepítésre.%n%nMásik alkalommal is futtatható a telepítés befejezéséhez%n%nKilép a telepítőből? +AboutSetupMenuItem=&Névjegy... +AboutSetupTitle=Telepítő névjegye +AboutSetupMessage=%1 %2 verzió%n%3%n%nAz %1 honlapja:%n%4 +AboutSetupNote= +TranslatorNote= + +; *** Buttons +ButtonBack=< &Vissza +ButtonNext=&Tovább > +ButtonInstall=&Telepít +ButtonOK=OK +ButtonCancel=Mégse +ButtonYes=&Igen +ButtonYesToAll=&Mindet +ButtonNo=&Nem +ButtonNoToAll=&Egyiket se +ButtonFinish=&Befejezés +ButtonBrowse=&Tallózás... +ButtonWizardBrowse=T&allózás... +ButtonNewFolder=Új &könyvtár + +; *** "Select Language" dialog messages +SelectLanguageTitle=Telepítő nyelvi beállítás +SelectLanguageLabel=Válassza ki a telepítés alatt használt nyelvet. + +; *** Common wizard text +ClickNext=A folytatáshoz kattintson a 'Tovább'-ra, a kilépéshez a 'Mégse'-re. +BeveledLabel= +BrowseDialogTitle=Válasszon könyvtárt +BrowseDialogLabel=Válasszon egy könyvtárat az alábbi listából, majd kattintson az 'OK'-ra. +NewFolderName=Új könyvtár + +; *** "Welcome" wizard page +WelcomeLabel1=Üdvözli a(z) [name] Telepítővarázslója. +WelcomeLabel2=A(z) [name/ver] telepítésre kerül a számítógépén.%n%nAjánlott minden, egyéb futó alkalmazás bezárása a folytatás előtt. + +; *** "Password" wizard page +WizardPassword=Jelszó +PasswordLabel1=Ez a telepítés jelszóval védett. +PasswordLabel3=Kérem adja meg a jelszót, majd kattintson a 'Tovább'-ra. A jelszavak kis- és nagy betű érzékenyek lehetnek. +PasswordEditLabel=&Jelszó: +IncorrectPassword=Az ön által megadott jelszó helytelen. Próbálja újra. + +; *** "License Agreement" wizard page +WizardLicense=Licencszerződés +LicenseLabel=Olvassa el figyelmesen az információkat folytatás előtt. +LicenseLabel3=Kérem, olvassa el az alábbi licencszerződést. A telepítés folytatásához, el kell fogadnia a szerződést. +LicenseAccepted=&Elfogadom a szerződést +LicenseNotAccepted=&Nem fogadom el a szerződést + +; *** "Information" wizard pages +WizardInfoBefore=Információk +InfoBeforeLabel=Olvassa el a következő fontos információkat a folytatás előtt. +InfoBeforeClickLabel=Ha készen áll, kattintson a 'Tovább'-ra. +WizardInfoAfter=Információk +InfoAfterLabel=Olvassa el a következő fontos információkat a folytatás előtt. +InfoAfterClickLabel=Ha készen áll, kattintson a 'Tovább'-ra. + +; *** "User Information" wizard page +WizardUserInfo=Felhasználó adatai +UserInfoDesc=Kérem, adja meg az adatait +UserInfoName=&Felhasználónév: +UserInfoOrg=&Szervezet: +UserInfoSerial=&Sorozatszám: +UserInfoNameRequired=Meg kell adnia egy nevet. + +; *** "Select Destination Location" wizard page +WizardSelectDir=Válasszon célkönyvtárat +SelectDirDesc=Hova települjön a(z) [name]? +SelectDirLabel3=A(z) [name] az alábbi könyvtárba lesz telepítve. +SelectDirBrowseLabel=A folytatáshoz, kattintson a 'Tovább'-ra. Ha másik könyvtárat választana, kattintson a 'Tallózás'-ra. +DiskSpaceGBLabel=At least [gb] GB szabad területre van szükség. +DiskSpaceMBLabel=Legalább [mb] MB szabad területre van szükség. +CannotInstallToNetworkDrive=A Telepítő nem tud hálózati meghajtóra telepíteni. +CannotInstallToUNCPath=A Telepítő nem tud hálózati UNC elérési útra telepíteni. +InvalidPath=Teljes útvonalat adjon meg, a meghajtó betűjelével; például:%n%nC:\Alkalmazás%n%nvagy egy hálózati útvonalat a következő alakban:%n%n\\kiszolgáló\megosztás +InvalidDrive=A kiválasztott meghajtó vagy hálózati megosztás nem létezik vagy nem elérhető. Válasszon egy másikat. +DiskSpaceWarningTitle=Nincs elég szabad terület +DiskSpaceWarning=A Telepítőnek legalább %1 KB szabad lemezterületre van szüksége, viszont a kiválasztott meghajtón csupán %2 KB áll rendelkezésre.%n%nMindenképpen folytatja? +DirNameTooLong=A könyvtár neve vagy az útvonal túl hosszú. +InvalidDirName=A könyvtár neve érvénytelen. +BadDirName32=A könyvtárak nevei ezen karakterek egyikét sem tartalmazhatják:%n%n%1 +DirExistsTitle=A könyvtár már létezik +DirExists=A könyvtár:%n%n%1%n%nmár létezik. Mindenképp ide akar telepíteni? +DirDoesntExistTitle=A könyvtár nem létezik +DirDoesntExist=A könyvtár:%n%n%1%n%nnem létezik. Szeretné létrehozni? + +; *** "Select Components" wizard page +WizardSelectComponents=Összetevők kiválasztása +SelectComponentsDesc=Mely összetevők kerüljenek telepítésre? +SelectComponentsLabel2=Jelölje ki a telepítendő összetevőket; törölje a telepíteni nem kívánt összetevőket. Kattintson a 'Tovább'-ra, ha készen áll a folytatásra. +FullInstallation=Teljes telepítés +; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) +CompactInstallation=Szokásos telepítés +CustomInstallation=Egyéni telepítés +NoUninstallWarningTitle=Létező összetevő +NoUninstallWarning=A telepítő úgy találta, hogy a következő összetevők már telepítve vannak a számítógépre:%n%n%1%n%nEzen összetevők kijelölésének törlése, nem távolítja el azokat a számítógépről.%n%nMindenképpen folytatja? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceMBLabel=A jelenlegi kijelölés legalább [gb] GB lemezterületet igényel. +ComponentsDiskSpaceMBLabel=A jelenlegi kijelölés legalább [mb] MB lemezterületet igényel. + +; *** "Select Additional Tasks" wizard page +WizardSelectTasks=További feladatok +SelectTasksDesc=Mely kiegészítő feladatok kerüljenek végrehajtásra? +SelectTasksLabel2=Jelölje ki, mely kiegészítő feladatokat hajtsa végre a Telepítő a(z) [name] telepítése során, majd kattintson a 'Tovább'-ra. + +; *** "Select Start Menu Folder" wizard page +WizardSelectProgramGroup=Start Menü könyvtára +SelectStartMenuFolderDesc=Hova helyezze a Telepítő a program parancsikonjait? +SelectStartMenuFolderLabel3=A Telepítő a program parancsikonjait a Start menü következő mappájában fogja létrehozni. +SelectStartMenuFolderBrowseLabel=A folytatáshoz kattintson a 'Tovább'-ra. Ha másik mappát választana, kattintson a 'Tallózás'-ra. +MustEnterGroupName=Meg kell adnia egy mappanevet. +GroupNameTooLong=A könyvtár neve vagy az útvonal túl hosszú. +InvalidGroupName=A könyvtár neve érvénytelen. +BadGroupName=A könyvtárak nevei ezen karakterek egyikét sem tartalmazhatják:%n%n%1 +NoProgramGroupCheck2=&Ne hozzon létre mappát a Start menüben + +; *** "Ready to Install" wizard page +WizardReady=Készen állunk a telepítésre +ReadyLabel1=A Telepítő készen áll, a(z) [name] számítógépre telepítéshez. +ReadyLabel2a=Kattintson a 'Telepítés'-re a folytatáshoz, vagy a "Vissza"-ra a beállítások áttekintéséhez vagy megváltoztatásához. +ReadyLabel2b=Kattintson a 'Telepítés'-re a folytatáshoz. +ReadyMemoUserInfo=Felhasználó adatai: +ReadyMemoDir=Telepítés célkönyvtára: +ReadyMemoType=Telepítés típusa: +ReadyMemoComponents=Választott összetevők: +ReadyMemoGroup=Start menü mappája: +ReadyMemoTasks=Kiegészítő feladatok: + +; *** "Preparing to Install" wizard page +WizardPreparing=Felkészülés a telepítésre +PreparingDesc=A Telepítő felkészül a(z) [name] számítógépre történő telepítéshez. +PreviousInstallNotCompleted=gy korábbi program telepítése/eltávolítása nem fejeződött be. Újra kell indítania a számítógépét a másik telepítés befejezéséhez.%n%nA számítógépe újraindítása után ismét futtassa a Telepítőt a(z) [name] telepítésének befejezéséhez. +CannotContinue=A telepítés nem folytatható. A kilépéshez kattintson a 'Mégse'-re +ApplicationsFound=A következő alkalmazások olyan fájlokat használnak, amelyeket a Telepítőnek frissíteni kell. Ajánlott, hogy engedélyezze a Telepítőnek ezen alkalmazások automatikus bezárását. +ApplicationsFound2=A következő alkalmazások olyan fájlokat használnak, amelyeket a Telepítőnek frissíteni kell. Ajánlott, hogy engedélyezze a Telepítőnek ezen alkalmazások automatikus bezárását. A telepítés befejezése után a Telepítő megkísérli az alkalmazások újraindítását. +CloseApplications=&Alkalmazások automatikus bezárása +DontCloseApplications=&Ne zárja be az alkalmazásokat +ErrorCloseApplications=A Telepítő nem tudott minden alkalmazást automatikusan bezárni. A folytatás előtt ajánlott minden, a Telepítő által frissítendő fájlokat használó alkalmazást bezárni. +PrepareToInstallNeedsRestart=A telepítőnek újra kell indítania a számítógépet. Újraindítást követően, futtassa újból a telepítőt, a [name] telepítésének befejezéséhez .%n%nÚjra szeretné indítani most a számítógépet? + +; *** "Installing" wizard page +WizardInstalling=Telepítés +InstallingLabel=Kérem várjon, amíg a(z) [name] telepítése zajlik. + +; *** "Setup Completed" wizard page +FinishedHeadingLabel=A(z) [name] telepítésének befejezése +FinishedLabelNoIcons=A Telepítő végzett a(z) [name] telepítésével. +FinishedLabel=A Telepítő végzett a(z) [name] telepítésével. Az alkalmazást a létrehozott ikonok kiválasztásával indíthatja. +ClickFinish=Kattintson a 'Befejezés'-re a kilépéshez. +FinishedRestartLabel=A(z) [name] telepítésének befejezéséhez újra kell indítani a számítógépet. Újraindítja most? +FinishedRestartMessage=A(z) [name] telepítésének befejezéséhez, a Telepítőnek újra kell indítani a számítógépet.%n%nÚjraindítja most? +ShowReadmeCheck=Igen, szeretném elolvasni a FONTOS fájlt +YesRadio=&Igen, újraindítás most +NoRadio=&Nem, később indítom újra +; used for example as 'Run MyProg.exe' +RunEntryExec=%1 futtatása +; used for example as 'View Readme.txt' +RunEntryShellExec=%1 megtekintése + +; *** "Setup Needs the Next Disk" stuff +ChangeDiskTitle=A Telepítőnek szüksége van a következő lemezre +SelectDiskLabel2=Helyezze be a(z) %1. lemezt és kattintson az 'OK'-ra.%n%nHa a fájlok a lemez egy a megjelenítettől különböző mappájában találhatók, írja be a helyes útvonalat vagy kattintson a 'Tallózás'-ra. +PathLabel=Ú&tvonal: +FileNotInDir2=A(z) "%1" fájl nem található a következő helyen: "%2". Helyezze be a megfelelő lemezt vagy válasszon egy másik mappát. +SelectDirectoryLabel=Adja meg a következő lemez helyét. + +; *** Installation phase messages +SetupAborted=A telepítés nem fejeződött be.%n%nHárítsa el a hibát és futtassa újból a Telepítőt. +AbortRetryIgnoreSelectAction=Válasszon műveletet +AbortRetryIgnoreRetry=&Újra +AbortRetryIgnoreIgnore=&Hiba elvetése és folytatás +AbortRetryIgnoreCancel=Telepítés megszakítása + +; *** Installation status messages +StatusClosingApplications=Alkalmazások bezárása... +StatusCreateDirs=Könyvtárak létrehozása... +StatusExtractFiles=Fájlok kibontása... +StatusCreateIcons=Parancsikonok létrehozása... +StatusCreateIniEntries=INI bejegyzések létrehozása... +StatusCreateRegistryEntries=Rendszerleíró bejegyzések létrehozása... +StatusRegisterFiles=Fájlok regisztrálása... +StatusSavingUninstall=Eltávolító információk mentése... +StatusRunProgram=Telepítés befejezése... +StatusRestartingApplications=Alkalmazások újraindítása... +StatusRollback=Változtatások visszavonása... + +; *** Misc. errors +ErrorInternal2=Belső hiba: %1 +ErrorFunctionFailedNoCode=Sikertelen %1 +ErrorFunctionFailed=Sikertelen %1; kód: %2 +ErrorFunctionFailedWithMessage=Sikertelen %1; kód: %2.%n%3 +ErrorExecutingProgram=Nem hajtható végre a fájl:%n%1 + +; *** Registry errors +ErrorRegOpenKey=Nem nyitható meg a rendszerleíró kulcs:%n%1\%2 +ErrorRegCreateKey=Nem hozható létre a rendszerleíró kulcs:%n%1\%2 +ErrorRegWriteKey=Nem módosítható a rendszerleíró kulcs:%n%1\%2 + +; *** INI errors +ErrorIniEntry=Bejegyzés létrehozása sikertelen a következő INI fájlban: "%1". + +; *** File copying errors +FileAbortRetryIgnoreSkipNotRecommended=&Fájl kihagyása (nem ajánlott) +FileAbortRetryIgnoreIgnoreNotRecommended=&Hiba elvetése és folytatás (nem ajánlott) +SourceIsCorrupted=A forrásfájl megsérült +SourceDoesntExist=A(z) "%1" forrásfájl nem létezik +ExistingFileReadOnly2=A fájl csak olvashatóként van jelölve. +ExistingFileReadOnlyRetry=Csak &olvasható tulajdonság eltávolítása és újra próbálkozás +ExistingFileReadOnlyKeepExisting=&Létező fájl megtartása +ErrorReadingExistingDest=Hiba lépett fel a fájl olvasása közben: +FileExists=A fájl már létezik.%n%nFelül kívánja írni? +ExistingFileNewer=A létező fájl újabb a telepítésre kerülőnél. Ajánlott a létező fájl megtartása.%n%nMeg kívánja tartani a létező fájlt? +ErrorChangingAttr=Hiba lépett fel a fájl attribútumának módosítása közben: +ErrorCreatingTemp=Hiba lépett fel a fájl telepítési könyvtárban történő létrehozása közben: +ErrorReadingSource=Hiba lépett fel a forrásfájl olvasása közben: +ErrorCopying=Hiba lépett fel a fájl másolása közben: +ErrorReplacingExistingFile=Hiba lépett fel a létező fájl cseréje közben: +ErrorRestartReplace=A fájl cseréje az újraindítás után sikertelen volt: +ErrorRenamingTemp=Hiba lépett fel fájl telepítési könyvtárban történő átnevezése közben: +ErrorRegisterServer=Nem lehet regisztrálni a DLL-t/OCX-et: %1 +ErrorRegSvr32Failed=Sikertelen RegSvr32. A visszaadott kód: %1 +ErrorRegisterTypeLib=Nem lehet regisztrálni a típustárat: %1 + +; *** Uninstall display name markings +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32-bit +UninstallDisplayNameMark64Bit=64-bit +UninstallDisplayNameMarkAllUsers=Minden felhasználó +UninstallDisplayNameMarkCurrentUser=Jelenlegi felhasználó + +; *** Post-installation errors +ErrorOpeningReadme=Hiba lépett fel a FONTOS fájl megnyitása közben. +ErrorRestartingComputer=A Telepítő nem tudta újraindítani a számítógépet. Indítsa újra kézileg. + +; *** Uninstaller messages +UninstallNotFound=A(z) "%1" fájl nem létezik. Nem távolítható el. +UninstallOpenError=A(z) "%1" fájl nem nyitható meg. Nem távolítható el. +UninstallUnsupportedVer=A(z) "%1" eltávolítási naplófájl formátumát nem tudja felismerni az eltávolító jelen verziója. Az eltávolítás nem folytatható +UninstallUnknownEntry=Egy ismeretlen bejegyzés (%1) található az eltávolítási naplófájlban +ConfirmUninstall=Biztosan el kívánja távolítani a(z) %1 programot és minden összetevőjét? +UninstallOnlyOnWin64=Ezt a telepítést csak 64-bites Windowson lehet eltávolítani. +OnlyAdminCanUninstall=Ezt a telepítést csak adminisztrációs jogokkal rendelkező felhasználó távolíthatja el. +UninstallStatusLabel=Legyen türelemmel, amíg a(z) %1 számítógépéről történő eltávolítása befejeződik. +UninstalledAll=A(z) %1 sikeresen el lett távolítva a számítógépről. +UninstalledMost=A(z) %1 eltávolítása befejeződött.%n%nNéhány elemet nem lehetett eltávolítani. Törölje kézileg. +UninstalledAndNeedsRestart=A(z) %1 eltávolításának befejezéséhez újra kell indítania a számítógépét.%n%nÚjraindítja most? +UninstallDataCorrupted=A(z) "%1" fájl sérült. Nem távolítható el. + +; *** Uninstallation phase messages +ConfirmDeleteSharedFileTitle=Törli a megosztott fájlt? +ConfirmDeleteSharedFile2=A rendszer azt jelzi, hogy a következő megosztott fájlra már nincs szüksége egyetlen programnak sem. Eltávolítja a megosztott fájlt?%n%nHa más programok még mindig használják a megosztott fájlt, akkor az eltávolítása után lehet, hogy nem fognak megfelelően működni. Ha bizonytalan, válassza a Nemet. A fájl megtartása nem okoz problémát a rendszerben. +SharedFileNameLabel=Fájlnév: +SharedFileLocationLabel=Helye: +WizardUninstalling=Eltávolítás állapota +StatusUninstalling=%1 eltávolítása... + +; *** Shutdown block reasons +ShutdownBlockReasonInstallingApp=%1 telepítése. +ShutdownBlockReasonUninstallingApp=%1 eltávolítása. + +; 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, verzió: %2 +AdditionalIcons=További parancsikonok: +CreateDesktopIcon=&Asztali ikon létrehozása +CreateQuickLaunchIcon=&Gyorsindító parancsikon létrehozása +ProgramOnTheWeb=%1 az interneten +UninstallProgram=Eltávolítás - %1 +LaunchProgram=Indítás %1 +AssocFileExtension=A(z) %1 &társítása a(z) %2 fájlkiterjesztéssel +AssocingFileExtension=A(z) %1 társítása a(z) %2 fájlkiterjesztéssel... +AutoStartProgramGroupDescription=Indítópult: +AutoStartProgram=%1 automatikus indítása +AddonHostProgramNotFound=A(z) %1 nem található a kiválasztott könyvtárban.%n%nMindenképpen folytatja? diff --git a/build/win32/i18n/Default.isl b/build/win32/i18n/Default.isl deleted file mode 100644 index 370da6b37c..0000000000 --- a/build/win32/i18n/Default.isl +++ /dev/null @@ -1,336 +0,0 @@ -; *** 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 icons. -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? Extensions and settings will not be removed. -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 icons: -CreateDesktopIcon=Create a &desktop icon -CreateQuickLaunchIcon=Create a &Quick Launch icon -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 index a7c38d12b9..0f1b1a7ccf 100644 --- a/build/win32/i18n/Default.ko.isl +++ b/build/win32/i18n/Default.ko.isl @@ -1,12 +1,16 @@ -; *** Inno Setup version 5.5.3+ Korean messages *** -; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ +; *** Inno Setup version 6.0.0+ Korean messages *** ; +; ˘Ć 6.0.3+ Translator: SungDong Kim (acroedit@gmail.com) +; ˘Ć 5.5.3+ Translator: Domddol (domddol@gmail.com) +; ˘Ć Translation date: MAR 04, 2014 +; ˘Ć Contributors: Hansoo KIM (iryna7@gmail.com), Woong-Jae An (a183393@hanmail.net) +; ˘Ć Storage: 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. @@ -23,50 +27,68 @@ LanguageCodePage=949 ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 + [Messages] + ; *** Application titles SetupAppTitle=ĽłÄˇ -SetupWindowTitle=ĽłÄˇ - %1 +SetupWindowTitle=%1 ĽłÄˇ UninstallAppTitle=Á¦°Ĺ UninstallAppFullTitle=%1 Á¦°Ĺ + ; *** Misc. common InformationTitle=Á¤ş¸ ConfirmTitle=Č®ŔÎ ErrorTitle=żŔ·ů + ; *** SetupLdr messages -SetupLdrStartupMessage=±×·Ż¸é %1ŔĚ(°ˇ) ĽłÄˇµË´Ď´Ů. °čĽÓÇϽðڽŔ´Ď±î? -LdrCannotCreateTemp=Ŕӽà ĆÄŔĎŔ» ¸¸µé Ľö ľř˝Ŕ´Ď´Ů. ĽłÄˇ ÇÁ·Î±×·ĄŔĚ ÁߴܵǾú˝Ŕ´Ď´Ů. -LdrCannotExecTemp=Ŕӽà µđ·şĹ͸®żˇĽ­ ĆÄŔĎŔ» ˝ÇÇŕÇŇ Ľö ľř˝Ŕ´Ď´Ů. ĽłÄˇ ÇÁ·Î±×·ĄŔĚ ÁߴܵǾú˝Ŕ´Ď´Ů. +SetupLdrStartupMessage=%1Ŕ»(¸¦) ĽłÄˇÇŐ´Ď´Ů, °čĽÓÇϽðڽŔ´Ď±î? +LdrCannotCreateTemp=Ŕӽà ĆÄŔĎŔ» ¸¸µé Ľö ľř˝Ŕ´Ď´Ů, ĽłÄˇ¸¦ Áß´ÜÇŐ´Ď´Ů +LdrCannotExecTemp=Ŕӽà Ćú´őŔÇ ĆÄŔĎŔ» ˝ÇÇŕÇŇ Ľö ľř˝Ŕ´Ď´Ů, ĽłÄˇ¸¦ Áß´ÜÇŐ´Ď´Ů +HelpTextNote= + ; *** 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żˇĽ­ ˝ÇÇŕµÇÁö ľĘ˝Ŕ´Ď´Ů. +SetupFileMissing=%1 ĆÄŔĎŔĚ Á¸ŔçÇĎÁö ľĘ˝Ŕ´Ď´Ů, ą®Á¦¸¦ ÇذáÇŘ ş¸°ĹłŞ »ő·Îżî ĽłÄˇ ÇÁ·Î±×·ĄŔ» ±¸ÇϽñ⠹ٶř´Ď´Ů. +SetupFileCorrupt=ĽłÄˇ ĆÄŔĎŔĚ ĽŐ»óµÇľú˝Ŕ´Ď´Ů, »ő·Îżî ĽłÄˇ ÇÁ·Î±×·ĄŔ» ±¸ÇϽñ⠹ٶř´Ď´Ů. +SetupFileCorruptOrWrongVer=ĽłÄˇ ĆÄŔĎŔÇ ĽŐ»óŔĚ°ĹłŞ ŔĚ ĽłÄˇ ąöŔü°ú ČŁČŻµÇÁö ľĘ˝Ŕ´Ď´Ů, ą®Á¦¸¦ ÇذáÇŘ ş¸°ĹłŞ »ő·Îżî ĽłÄˇ ÇÁ·Î±×·ĄŔ» ±¸ÇϽñ⠹ٶř´Ď´Ů. +InvalidParameter=Ŕ߸řµČ ¸Ĺ°ł şŻĽöŔÔ´Ď´Ů:%n%n%1 +SetupAlreadyRunning=ĽłÄˇ°ˇ ŔĚąĚ ˝ÇÇŕ ÁßŔÔ´Ď´Ů. +WindowsVersionNotSupported=ŔĚ ÇÁ·Î±×·ĄŔş ±ÍÇĎŔÇ Windows ąöŔüŔ» ÁöżřÇĎÁö ľĘ˝Ŕ´Ď´Ů. +WindowsServicePackRequired=ŔĚ ÇÁ·Î±×·ĄŔ» ˝ÇÇŕÇĎ·Á¸é %1 sp%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ŔĚ Ç׸ńŔÇ ¸đµç ŔνşĹĎ˝ş¸¦ Áö±Ý ´Ý°í °čĽÓÇĎ·Á¸é [Č®ŔÎ]Ŕ», Áľ·áÇĎ·Á¸é [ĂëĽŇ]¸¦ Ŭ¸ŻÇĎĽĽżä. +OnlyOnTheseArchitectures=ŔĚ ÇÁ·Î±×·ĄŔş ľĆ·ˇ Ăł¸® ±¸Á¶żÍ ČŁČŻµÇ´Â Windows ąöŔüżˇ¸¸ ĽłÄˇÇŇ Ľö ŔÖ˝Ŕ´Ď´Ů:%n%n%1 +WinVersionTooLowError=ŔĚ ÇÁ·Î±×·ĄŔş %1 ąöŔü %2 ŔĚ»óŔĚ ÇĘżäÇŐ´Ď´Ů. +WinVersionTooHighError=ŔĚ ÇÁ·Î±×·ĄŔş %1 ąöŔü %2 Ŕ̻󿡼­ ĽłÄˇÇŇ Ľö ľř˝Ŕ´Ď´Ů. +AdminPrivilegesRequired=ŔĚ ÇÁ·Î±×·ĄŔ» ĽłÄˇÇĎ·Á¸é °ü¸®ŔÚ·Î ·Î±×ŔÎÇŘľß ÇŐ´Ď´Ů. +PowerUserPrivilegesRequired=ŔĚ ÇÁ·Î±×·ĄŔ» ĽłÄˇÇĎ·Á¸é °ü¸®ŔÚ ¶Ç´Â °í±Ţ »çżëŔÚ·Î ·Î±×ŔÎÇŘľß ÇŐ´Ď´Ů. +SetupAppRunningError=ÇöŔç %1ŔĚ(°ˇ) ˝ÇÇŕ ÁßŔÔ´Ď´Ů!%n%nÁö±Ý ±×°ÍŔÇ ¸đµç ŔνşĹĎ˝ş¸¦ ´ÝľĆ ÁֽʽÿŔ. ±×·± ´ŮŔ˝ °čĽÓÇĎ·Á¸é "Č®ŔÎ"Ŕ», Áľ·áÇĎ·Á¸é "ĂëĽŇ"¸¦ Ŭ¸ŻÇϽʽÿŔ. +UninstallAppRunningError=ÇöŔç %1ŔĚ(°ˇ) ˝ÇÇŕ ÁßŔÔ´Ď´Ů!%n%nÁö±Ý ±×°ÍŔÇ ¸đµç ŔνşĹĎ˝ş¸¦ ´ÝľĆ ÁֽʽÿŔ. ±×·± ´ŮŔ˝ °čĽÓÇĎ·Á¸é "Č®ŔÎ"Ŕ», Áľ·áÇĎ·Á¸é "ĂëĽŇ"¸¦ Ŭ¸ŻÇϽʽÿŔ. + +; *** Startup questions +PrivilegesRequiredOverrideTitle=ĽłÄˇ ¸đµĺ Ľ±ĹĂ +PrivilegesRequiredOverrideInstruction=ĽłÄˇ ¸đµĺ¸¦ Ľ±ĹĂÇŘ ÁֽʽÿŔ +PrivilegesRequiredOverrideText1=%1 Ŕş ¸đµç »çżëŔÚ(°ü¸®ŔÚ ±ÇÇŃ ÇĘżä) ¶Ç´Â ÇöŔç »çżëŔÚżëŔ¸·Î ĽłÄˇÇŐ´Ď´Ů. +PrivilegesRequiredOverrideText2=%1 Ŕş ÇöŔç »çżëŔÚ ¶Ç´Â ¸đµç »çżëŔÚ(°ü¸®ŔÚ ±ÇÇŃ ÇĘżä) żëŔ¸·Î ĽłÄˇÇŐ´Ď´Ů. +PrivilegesRequiredOverrideAllUsers=¸đµç »çżëŔÚżëŔ¸·Î ĽłÄˇ(&A) +PrivilegesRequiredOverrideAllUsersRecommended=¸đµç »çżëŔÚżëŔ¸·Î ĽłÄˇ(&A) (Ăßõ) +PrivilegesRequiredOverrideCurrentUser=ÇöŔç »çżëŔÚżëŔ¸·Î ĽłÄˇ(&M) +PrivilegesRequiredOverrideCurrentUserRecommended=ÇöŔç »çżëŔÚżëŔ¸·Î ĽłÄˇ(&M) (Ăßõ) + ; *** Misc. errors -ErrorCreatingDir=ĽłÄˇ ÇÁ·Î±×·ĄżˇĽ­ µđ·şĹ͸® "%1"Ŕ»(¸¦) ¸¸µé Ľö ľř˝Ŕ´Ď´Ů. -ErrorTooManyFilesInDir=µđ·şĹ͸® "%1"żˇ ĆÄŔĎŔĚ łĘą« ¸ąŔ¸ąÇ·Î ŔĚ µđ·şĹ͸®żˇ ĆÄŔĎŔ» ¸¸µé Ľö ľř˝Ŕ´Ď´Ů. +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 +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) > @@ -75,224 +97,271 @@ ButtonOK=Č® ButtonCancel=ĂëĽŇ ButtonYes=żą(&Y) ButtonYesToAll=¸đµÎ żą(&A) -ButtonNo=ľĆ´Ďżä(&N) -ButtonNoToAll=¸đµÎ ľĆ´Ďżä(&O) -ButtonFinish=¸¶Ä§(&F) +ButtonNo=ľĆ´ĎżŔ(&N) +ButtonNoToAll=¸đµÎ ľĆ´ĎżŔ(&O) +ButtonFinish=Áľ·á(&F) ButtonBrowse=ĂŁľĆş¸±â(&B)... -ButtonWizardBrowse=ĂŁľĆş¸±â(&R) +ButtonWizardBrowse=ĂŁľĆş¸±â(&R)... ButtonNewFolder=»ő Ćú´ő ¸¸µé±â(&M) + ; *** "Select Language" dialog messages SelectLanguageTitle=ĽłÄˇ ľđľî Ľ±ĹĂ -SelectLanguageLabel=ĽłÄˇ Áßżˇ »çżëÇŇ ľđľî¸¦ Ľ±ĹĂÇĎĽĽżä. +SelectLanguageLabel=ĽłÄˇżˇ »çżëÇŇ ľđľî¸¦ Ľ±ĹĂÇϽʽÿŔ. + ; *** Common wizard text -ClickNext=°čĽÓÇĎ·Á¸é [´ŮŔ˝]Ŕ» Ŭ¸ŻÇĎ°í ĽłÄˇ ÇÁ·Î±×·ĄŔ» Áľ·áÇĎ·Á¸é [ĂëĽŇ]¸¦ Ŭ¸ŻÇĎĽĽżä. +ClickNext=°čĽÓÇĎ·Á¸é "´ŮŔ˝"Ŕ» Ŭ¸ŻÇĎ°í ĽłÄˇ¸¦ Áľ·áÇĎ·Á¸é "ĂëĽŇ"¸¦ Ŭ¸ŻÇŐ´Ď´Ů. BeveledLabel= BrowseDialogTitle=Ćú´ő ĂŁľĆş¸±â -BrowseDialogLabel=ľĆ·ˇ ¸ń·ĎżˇĽ­ Ćú´ő¸¦ Ľ±ĹĂÇŃ ´ŮŔ˝ [Č®ŔÎ]Ŕ» Ŭ¸ŻÇĎĽĽżä. +BrowseDialogLabel=ľĆ·ˇ ¸ń·ĎżˇĽ­ Ćú´ő¸¦ Ľ±ĹĂÇŃ ´ŮŔ˝ "Č®ŔÎ"Ŕ» Ŭ¸ŻÇŐ´Ď´Ů. NewFolderName=»ő Ćú´ő + ; *** "Welcome" wizard page WelcomeLabel1=[name] ĽłÄˇ ¸¶ąý»ç ˝ĂŔŰ -WelcomeLabel2=ŔĚ ¸¶ąý»ç´Â ÄÄÇ»ĹÍżˇ [name/ver]Ŕ»(¸¦) ĽłÄˇÇŐ´Ď´Ů.%n%n°čĽÓÇϱâ Ŕüżˇ ´Ů¸Ą ¸đµç ŔŔżë ÇÁ·Î±×·ĄŔ» ´Ý´Â °ÍŔĚ ÁÁ˝Ŕ´Ď´Ů. +WelcomeLabel2=ŔĚ ¸¶ąý»ç´Â ±ÍÇĎŔÇ ÄÄÇ»ĹÍżˇ [name/ver]Ŕ»(¸¦) ĽłÄˇÇŇ °ÍŔÔ´Ď´Ů.%n%nĽłÄˇÇϱâ Ŕüżˇ ´Ů¸Ą ŔŔżëÇÁ·Î±×·ĄµéŔ» ¸đµÎ ´ÝŔ¸˝Ă±â ąŮ¶ř´Ď´Ů. + ; *** "Password" wizard page -WizardPassword=ľĎČŁ -PasswordLabel1=ŔĚ ĽłÄˇ´Â ľĎČŁ·Î ş¸ČŁµÇ°í ŔÖ˝Ŕ´Ď´Ů. -PasswordLabel3=°čĽÓÇĎ·Á¸é ľĎČŁ¸¦ ŔÔ·ÂÇŃ ´ŮŔ˝ [´ŮŔ˝]Ŕ» Ŭ¸ŻÇĎĽĽżä. ľĎČŁ´Â ´ëĽŇą®ŔÚ¸¦ ±¸şĐÇŐ´Ď´Ů. -PasswordEditLabel=ľĎČŁ(&P): -IncorrectPassword=ŔÔ·ÂÇŃ ľĎČŁ°ˇ Ŕ߸řµÇľú˝Ŕ´Ď´Ů. ´Ů˝Ă ˝ĂµµÇĎĽĽżä. +WizardPassword=şńąĐ ąřČŁ +PasswordLabel1=ŔĚ ĽłÄˇ ¸¶ąý»ç´Â şńąĐ ąřČŁ·Î ş¸ČŁµÇľî ŔÖ˝Ŕ´Ď´Ů. +PasswordLabel3=şńąĐ ąřČŁ¸¦ ŔÔ·ÂÇϰí "´ŮŔ˝"Ŕ» Ŭ¸ŻÇϽʽÿŔ. şńąĐ ąřČŁ´Â ´ëĽŇą®ŔÚ¸¦ ±¸şĐÇŘľß ÇŐ´Ď´Ů. +PasswordEditLabel=şńąĐ ąřČŁ(&P): +IncorrectPassword=şńąĐ ąřČŁ°ˇ Á¤Č®ÇĎÁö ľĘ˝Ŕ´Ď´Ů, ´Ů˝Ă ŔÔ·ÂÇϽʽÿŔ. + ; *** "License Agreement" wizard page WizardLicense=»çżë±Ç °čľŕ -LicenseLabel=°čĽÓÇϱâ Ŕüżˇ ´ŮŔ˝ ÁßżäÇŃ Á¤ş¸¸¦ ŔĐľî ş¸ĽĽżä. -LicenseLabel3=´ŮŔ˝ »çżë±Ç °čľŕŔ» ŔĐľî ÁÖĽĽżä. ĽłÄˇ¸¦ °čĽÓÇĎ·Á¸é ¸ŐŔú ŔĚ °čľŕ Á¶°Çżˇ µżŔÇÇŘľß ÇŐ´Ď´Ů. -LicenseAccepted=°čľŕżˇ µżŔÇÇÔ(&A) -LicenseNotAccepted=°čľŕżˇ µżŔÇ ľČ ÇÔ(&D) +LicenseLabel=°čĽÓÇϱâ Ŕüżˇ ´ŮŔ˝ŔÇ Áßżä Á¤ş¸¸¦ Ŕо˝Ę˝ĂżŔ. +LicenseLabel3=´ŮŔ˝ »çżë±Ç °čľŕŔ» Ŕо˝Ę˝ĂżŔ, ĽłÄˇ¸¦ °čĽÓÇĎ·Á¸é ŔĚ °čľŕżˇ µżŔÇÇŘľß ÇŐ´Ď´Ů. +LicenseAccepted=µżŔÇÇŐ´Ď´Ů(&A) +LicenseNotAccepted=µżŔÇÇĎÁö ľĘ˝Ŕ´Ď´Ů(&D) + ; *** "Information" wizard pages WizardInfoBefore=Á¤ş¸ -InfoBeforeLabel=°čĽÓÇϱâ Ŕüżˇ ´ŮŔ˝ ÁßżäÇŃ Á¤ş¸¸¦ ŔĐľî ş¸ĽĽżä. -InfoBeforeClickLabel=ĽłÄˇ¸¦ °čĽÓ ÁřÇŕÇŇ ÁŘşń°ˇ µÇ¸é [´ŮŔ˝]Ŕ» Ŭ¸ŻÇŐ´Ď´Ů. +InfoBeforeLabel=°čĽÓÇϱâ Ŕüżˇ ´ŮŔ˝ŔÇ Áßżä Á¤ş¸¸¦ Ŕо˝Ę˝ĂżŔ. +InfoBeforeClickLabel=ĽłÄˇ¸¦ °čĽÓÇĎ·Á¸é "´ŮŔ˝"Ŕ» Ŭ¸ŻÇϽʽÿŔ. WizardInfoAfter=Á¤ş¸ -InfoAfterLabel=°čĽÓÇϱâ Ŕüżˇ ´ŮŔ˝ ÁßżäÇŃ Á¤ş¸¸¦ ŔĐľî ş¸ĽĽżä. -InfoAfterClickLabel=ĽłÄˇ¸¦ °čĽÓ ÁřÇŕÇŇ ÁŘşń°ˇ µÇ¸é [´ŮŔ˝]Ŕ» Ŭ¸ŻÇŐ´Ď´Ů. +InfoAfterLabel=°čĽÓÇϱâ Ŕüżˇ ´ŮŔ˝ŔÇ Áßżä Á¤ş¸¸¦ Ŕо˝Ę˝ĂżŔ. +InfoAfterClickLabel=ĽłÄˇ¸¦ °čĽÓÇĎ·Á¸é "´ŮŔ˝"Ŕ» Ŭ¸ŻÇϽʽÿŔ. + ; *** "User Information" wizard page WizardUserInfo=»çżëŔÚ Á¤ş¸ -UserInfoDesc=Á¤ş¸¸¦ ŔÔ·ÂÇĎĽĽżä. +UserInfoDesc=»çżëŔÚ Á¤ş¸¸¦ ŔÔ·ÂÇϽʽÿŔ. UserInfoName=»çżëŔÚ Ŕ̸§(&U): UserInfoOrg=Á¶Á÷(&O): -UserInfoSerial=ŔĎ·Ă ąřČŁ(&S): -UserInfoNameRequired=Ŕ̸§Ŕ» ŔÔ·ÂÇŘľß ÇŐ´Ď´Ů. +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±×·ˇµµ °čĽÓÇϽðڽŔ´Ď±î? +WizardSelectDir=ĽłÄˇ Ŕ§Äˇ Ľ±ĹĂ +SelectDirDesc=[name]ŔÇ ĽłÄˇ Ŕ§Äˇ¸¦ Ľ±ĹĂÇϽʽÿŔ. +SelectDirLabel3=´ŮŔ˝ Ćú´őżˇ [name]Ŕ»(¸¦) ĽłÄˇÇŐ´Ď´Ů. +SelectDirBrowseLabel=°čĽÓÇĎ·Á¸é "´ŮŔ˝"Ŕ», ´Ů¸Ą Ćú´ő¸¦ Ľ±ĹĂÇĎ·Á¸é "ĂŁľĆş¸±â"¸¦ Ŭ¸ŻÇϽʽÿŔ. +DiskSpaceGBLabel=ŔĚ ÇÁ·Î±×·ĄŔş ĂÖĽŇ [gb] GBŔÇ µđ˝şĹ© ż©ŔŻ °ř°ŁŔĚ ÇĘżäÇŐ´Ď´Ů. +DiskSpaceMBLabel=ŔĚ ÇÁ·Î±×·ĄŔş ĂÖĽŇ [mb] MBŔÇ µđ˝şĹ© ż©ŔŻ °ř°ŁŔĚ ÇĘżäÇŐ´Ď´Ů. +CannotInstallToNetworkDrive=ł×Ć®żöĹ© µĺ¶óŔ̺꿡 ĽłÄˇÇŇ Ľö ľř˝Ŕ´Ď´Ů. +CannotInstallToUNCPath=UNC °ć·Îżˇ ĽłÄˇÇŇ Ľö ľř˝Ŕ´Ď´Ů. +InvalidPath=µĺ¶óŔĚşę ą®ŔÚ¸¦ Ć÷ÇÔÇŃ ŔüĂĽ °ć·Î¸¦ ŔÔ·ÂÇϽʽÿŔ.%nˇŘ żą: C:\APP %n%n¶Ç´Â, UNC Çü˝ÄŔÇ °ć·Î¸¦ ŔÔ·ÂÇϽʽÿŔ.%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ŔĚ(°ˇ) ľř˝Ŕ´Ď´Ů. Ćú´ő¸¦ ¸¸µĺ˝Ă°Ú˝Ŕ´Ď±î? +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=ŔüĂĽ ĽłÄˇ +SelectComponentsDesc=ĽłÄˇÇŇ ±¸Ľş żäĽŇ¸¦ Ľ±ĹĂÇϽʽÿŔ. +SelectComponentsLabel2=ÇĘżäÇŃ ±¸Ľş żäĽŇ´Â ĂĽĹ©ÇĎ°í şŇÇĘżäÇŃ ±¸Ľş żäĽŇ´Â ĂĽĹ© ÇŘÁ¦ÇŐ´Ď´Ů, °čĽÓÇĎ·Á¸é "´ŮŔ˝"Ŕ» Ŭ¸ŻÇϽʽÿŔ. +FullInstallation=¸đµÎ ĽłÄˇ ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) -CompactInstallation=Compact ĽłÄˇ +CompactInstallation=ĂÖĽŇ ĽłÄˇ CustomInstallation=»çżëŔÚ ÁöÁ¤ ĽłÄˇ -NoUninstallWarningTitle=±¸Ľş żäĽŇ°ˇ ŔÖŔ˝ -NoUninstallWarning=ĽłÄˇ ÇÁ·Î±×·ĄżˇĽ­ ±¸Ľş żäĽŇ %n%n%1%n%nŔĚ(°ˇ) ÄÄÇ»ĹÍżˇ ŔĚąĚ ĽłÄˇµÇľî ŔÖŔ˝Ŕ» °¨ÁöÇß˝Ŕ´Ď´Ů. ŔĚ·ŻÇŃ ±¸Ľş żäĽŇ´Â Ľ±ĹĂ ĂëĽŇÇصµ Á¦°ĹµÇÁö ľĘ˝Ŕ´Ď´Ů.%n%n±×·ˇµµ °čĽÓÇϽðڽŔ´Ď±î? -ComponentSize1=%1KB -ComponentSize2=%1MB -ComponentsDiskSpaceMBLabel=ÇöŔç Ľ±ĹĂŔ» Ŕ§ÇŘĽ­´Â Ŕűľîµµ [mb]MBŔÇ µđ˝şĹ© °ř°ŁŔĚ ÇĘżäÇŐ´Ď´Ů. +NoUninstallWarningTitle=±¸Ľş żäĽŇ°ˇ Á¸ŔçÇŐ´Ď´Ů +NoUninstallWarning=´ŮŔ˝ ±¸Ľş żäĽŇ°ˇ ŔĚąĚ ĽłÄˇµÇľî ŔÖ˝Ŕ´Ď´Ů:%n%n%1%n%nŔ§ ±¸Ľş żäĽŇŔ» Ľ±ĹĂÇĎÁö ľĘŔ¸¸é, ÇÁ·Î±×·Ą Á¦°Ĺ˝Ă ŔĚ ±¸Ľş żäĽŇµéŔş Á¦°ĹµÇÁö ľĘŔ» °Ě´Ď´Ů.%n%n±×·ˇµµ °čĽÓÇϽðڽŔ´Ď±î? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceGBLabel=ÇöŔç Ľ±ĹĂŔş ĂÖĽŇ [gb] GBŔÇ µđ˝şĹ© ż©ŔŻ °ř°ŁŔĚ ÇĘżäÇŐ´Ď´Ů. +ComponentsDiskSpaceMBLabel=ÇöŔç Ľ±ĹĂŔş ĂÖĽŇ [mb] MBŔÇ µđ˝şĹ© ż©ŔŻ °ř°ŁŔĚ ÇĘżäÇŐ´Ď´Ů. + ; *** "Select Additional Tasks" wizard page WizardSelectTasks=Ăß°ˇ ŔŰľ÷ Ľ±ĹĂ -SelectTasksDesc=ľî¶˛ ŔŰľ÷Ŕ» Ăß°ˇ·Î ĽöÇŕÇϽðڽŔ´Ď±î? -SelectTasksLabel2=ĽłÄˇ ÇÁ·Î±×·ĄżˇĽ­ [name]Ŕ»(¸¦) ĽłÄˇÇĎ´Â µżľČ ĽöÇŕÇŇ Ăß°ˇ ŔŰľ÷Ŕ» Ľ±ĹĂÇŃ ČÄ [´ŮŔ˝]Ŕ» Ŭ¸ŻÇĎĽĽżä. +SelectTasksDesc=ĽöÇŕÇŇ Ăß°ˇ ŔŰľ÷Ŕ» Ľ±ĹĂÇϽʽÿŔ. +SelectTasksLabel2=[name] ĽłÄˇ °úÁ¤żˇ Ć÷ÇÔÇŇ Ăß°ˇ ŔŰľ÷Ŕ» Ľ±ĹĂÇŃ ČÄ, "´ŮŔ˝"Ŕ» Ŭ¸ŻÇϽʽÿŔ. + ; *** "Select Start Menu Folder" wizard page WizardSelectProgramGroup=˝ĂŔŰ ¸Ţ´ş Ćú´ő Ľ±ĹĂ -SelectStartMenuFolderDesc=ĽłÄˇ ÇÁ·Î±×·ĄżˇĽ­ ÇÁ·Î±×·ĄŔÇ ąŮ·Î °ˇ±â¸¦ ľîµđżˇ ¸¸µéµµ·Ď ÇϽðڽŔ´Ď±î? -SelectStartMenuFolderLabel3=ĽłÄˇ ÇÁ·Î±×·ĄżˇĽ­ ÇÁ·Î±×·ĄŔÇ ąŮ·Î °ˇ±â¸¦ ´ŮŔ˝ ˝ĂŔŰ ¸Ţ´ş Ćú´őżˇ ¸¸µě´Ď´Ů. -SelectStartMenuFolderBrowseLabel=°čĽÓÇĎ·Á¸é [´ŮŔ˝]Ŕ» Ŭ¸ŻÇĎĽĽżä. ´Ů¸Ą Ćú´ő¸¦ Ľ±ĹĂÇĎ·Á¸é [ĂŁľĆş¸±â]¸¦ Ŭ¸ŻÇĎĽĽżä. -MustEnterGroupName=Ćú´ő Ŕ̸§Ŕ» ŔÔ·ÂÇŘľß ÇŐ´Ď´Ů. +SelectStartMenuFolderDesc=ľîµđżˇ ÇÁ·Î±×·Ą ąŮ·Î°ˇ±â¸¦ Ŕ§ÄˇÇϰڽŔ´Ď±î? +SelectStartMenuFolderLabel3=´ŮŔ˝ ˝ĂŔŰ ¸Ţ´ş Ćú´őżˇ ÇÁ·Î±×·Ą ąŮ·Î°ˇ±â¸¦ ¸¸µě´Ď´Ů. +SelectStartMenuFolderBrowseLabel=°čĽÓÇĎ·Á¸é "´ŮŔ˝"Ŕ» Ŭ¸ŻÇϰí, ´Ů¸Ą Ćú´ő¸¦ Ľ±ĹĂÇĎ·Á¸é "ĂŁľĆş¸±â"¸¦ Ŭ¸ŻÇϽʽÿŔ. +MustEnterGroupName=Ćú´ő Ŕ̸§Ŕ» ŔÔ·ÂÇϽʽÿŔ. GroupNameTooLong=Ćú´ő Ŕ̸§ ¶Ç´Â °ć·Î°ˇ łĘą« ±é´Ď´Ů. -InvalidGroupName=Ćú´ő Ŕ̸§ŔĚ Ŕ߸řµÇľú˝Ŕ´Ď´Ů. -BadGroupName=Ćú´ő Ŕ̸§żˇ´Â %n%n%1 ą®ŔÚ¸¦ »çżëÇŇ Ľö ľř˝Ŕ´Ď´Ů. +InvalidGroupName=Ćú´ő Ŕ̸§ŔĚ ŔŻČżÇĎÁö ľĘ˝Ŕ´Ď´Ů. +BadGroupName=Ćú´ő Ŕ̸§Ŕş ´ŮŔ˝ ą®ŔÚ¸¦ Ć÷ÇÔÇŇ Ľö ľř˝Ŕ´Ď´Ů:%n%n%1 NoProgramGroupCheck2=˝ĂŔŰ ¸Ţ´ş Ćú´ő¸¦ ¸¸µéÁö ľĘŔ˝(&D) + ; *** "Ready to Install" wizard page -WizardReady=ĽłÄˇ ÁŘşńµĘ -ReadyLabel1=ŔĚÁ¦ ĽłÄˇ ÇÁ·Î±×·ĄŔĚ ÄÄÇ»ĹÍżˇ [name] ĽłÄˇ¸¦ ˝ĂŔŰÇŇ ÁŘşń°ˇ µÇľú˝Ŕ´Ď´Ů. -ReadyLabel2a=ĽłÄˇ¸¦ °čĽÓÇĎ·Á¸é [ĽłÄˇ]¸¦ Ŭ¸ŻÇϰí, ĽłÁ¤Ŕ» °ËĹäÇĎ°ĹłŞ şŻ°ćÇĎ·Á¸é [µÚ·Î]¸¦ Ŭ¸ŻÇĎĽĽżä. -ReadyLabel2b=ĽłÄˇ¸¦ °čĽÓÇĎ·Á¸é [ĽłÄˇ]¸¦ Ŭ¸ŻÇĎĽĽżä. +WizardReady=ĽłÄˇ ÁŘşń żĎ·á +ReadyLabel1=±ÍÇĎŔÇ ÄÄÇ»ĹÍżˇ [name]Ŕ»(¸¦) ĽłÄˇÇŇ ÁŘşń°ˇ µÇľú˝Ŕ´Ď´Ů. +ReadyLabel2a=ĽłÄˇ¸¦ °čĽÓÇĎ·Á¸é "ĽłÄˇ"¸¦, ĽłÁ¤Ŕ» şŻ°ćÇĎ°ĹłŞ °ËĹäÇĎ·Á¸é "µÚ·Î"¸¦ Ŭ¸ŻÇϽʽÿŔ. +ReadyLabel2b=ĽłÄˇ¸¦ °čĽÓÇĎ·Á¸é "ĽłÄˇ"¸¦ Ŭ¸ŻÇϽʽÿŔ. ReadyMemoUserInfo=»çżëŔÚ Á¤ş¸: -ReadyMemoDir=´ë»ó Ŕ§Äˇ: +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=ĽłÄˇ ÇÁ·Î±×·ĄżˇĽ­ ŔϺΠŔŔżë ÇÁ·Î±×·ĄŔ» ŔÚµżŔ¸·Î ´ÝŔ» Ľö ľř˝Ŕ´Ď´Ů. °čĽÓÇϱâ Ŕüżˇ ĽłÄˇ ÇÁ·Î±×·ĄżˇĽ­ ľ÷µĄŔĚĆ®ÇŘľß ÇĎ´Â ĆÄŔĎŔ» »çżëÇĎ´Â ŔŔżë ÇÁ·Î±×·ĄŔ» ¸đµÎ ´Ý´Â °ÍŔĚ ÁÁ˝Ŕ´Ď´Ů. +PreparingDesc=±ÍÇĎŔÇ ÄÄÇ»ĹÍżˇ [name] ĽłÄˇ¸¦ ÁŘşńÇĎ´Â ÁßŔÔ´Ď´Ů. +PreviousInstallNotCompleted=ŔĚŔü ÇÁ·Î±×·ĄŔÇ ĽłÄˇ/Á¦°Ĺ ŔŰľ÷ŔĚ żĎ·áµÇÁö ľĘľŇ˝Ŕ´Ď´Ů, żĎ·áÇĎ·Á¸é ÄÄÇ»Ĺ͸¦ ´Ů˝Ă ˝ĂŔŰÇŘľß ÇŐ´Ď´Ů.%n%nÄÄÇ»Ĺ͸¦ ´Ů˝Ă ˝ĂŔŰÇŃ ČÄ, ĽłÄˇ ¸¶ąý»ç¸¦ ´Ů˝Ă ˝ÇÇŕÇĎż© [name] ĽłÄˇ¸¦ żĎ·áÇϽñ⠹ٶř´Ď´Ů. +CannotContinue=ĽłÄˇ¸¦ °čĽÓÇŇ Ľö ľř˝Ŕ´Ď´Ů, "ĂëĽŇ"¸¦ Ŭ¸ŻÇĎż© ĽłÄˇ¸¦ Áľ·áÇϽʽÿŔ. +ApplicationsFound=´ŮŔ˝ ŔŔżëÇÁ·Î±×·ĄŔĚ ĽłÄˇ ľ÷µĄŔĚĆ®°ˇ ÇĘżäÇŃ ĆÄŔĎŔ» »çżëÇϰí ŔÖ˝Ŕ´Ď´Ů, ĽłÄˇ ¸¶ąý»ç°ˇ ŔĚ·± ŔŔżëÇÁ·Î±×·ĄŔ» ŔÚµżŔ¸·Î Áľ·áÇŇ Ľö ŔÖµµ·Ď ÇăżëÇϽñ⠹ٶř´Ď´Ů. +ApplicationsFound2=´ŮŔ˝ ŔŔżëÇÁ·Î±×·ĄŔĚ ĽłÄˇ ľ÷µĄŔĚĆ®°ˇ ÇĘżäÇŃ ĆÄŔĎŔ» »çżëÇϰí ŔÖ˝Ŕ´Ď´Ů, ĽłÄˇ ¸¶ąý»ç°ˇ ŔĚ·± ŔŔżëÇÁ·Î±×·ĄŔ» ŔÚµżŔ¸·Î Áľ·áÇŇ Ľö ŔÖµµ·Ď ÇăżëÇϽñ⠹ٶř´Ď´Ů. ĽłÄˇ°ˇ żĎ·áµÇ¸é, ĽłÄˇ ¸¶ąý»ç´Â ŔĚ ŔŔżëÇÁ·Î±×·ĄŔĚ ´Ů˝Ă ˝ĂŔ۵ǵµ·Ď ˝ĂµµÇŇ °Ě´Ď´Ů. +CloseApplications=ŔÚµżŔ¸·Î ŔŔżëÇÁ·Î±×·ĄŔ» Áľ·áÇÔ(&A) +DontCloseApplications=ŔŔżëÇÁ·Î±×·ĄŔ» Áľ·áÇĎÁö ľĘŔ˝(&D) +ErrorCloseApplications=ĽłÄˇ ¸¶ąý»ç°ˇ ŔŔżëÇÁ·Î±×·ĄŔ» ŔÚµżŔ¸·Î Áľ·áÇŇ Ľö ľř˝Ŕ´Ď´Ů, °čĽÓÇϱâ Ŕüżˇ ĽłÄˇ ľ÷µĄŔĚĆ®°ˇ ÇĘżäÇŃ ĆÄŔĎŔ» »çżëÇϰí ŔÖ´Â ŔŔżëÇÁ·Î±×·ĄŔ» ¸đµÎ Áľ·áÇϽñ⠹ٶř´Ď´Ů. +PrepareToInstallNeedsRestart=ĽłÄˇ ¸¶ąý»ç´Â ±ÍÇĎŔÇ ÄÄÇ»Ĺ͸¦ Ŕç˝ĂŔŰÇŘľß ÇŐ´Ď´Ů. [name] ĽłÄˇ¸¦ żĎ·áÇϱâ Ŕ§ÇŘ ÄÄÇ»Ĺ͸¦ ´Ů˝Ă ˝ĂŔŰÇŃ ČÄżˇ ĽłÄˇ ¸¶ąý»ç¸¦ ´Ů˝Ă ˝ÇÇŕÇŘ ÁֽʽÿŔ.%n%nÁö±Ý ´Ů˝Ă ˝ĂŔŰÇϽðڽŔ´Ď±î? + ; *** "Installing" wizard page WizardInstalling=ĽłÄˇ Áß -InstallingLabel=ĽłÄˇ ÇÁ·Î±×·ĄżˇĽ­ ÄÄÇ»ĹÍżˇ [name]Ŕ»(¸¦) ĽłÄˇÇĎ´Â µżľČ ±â´Ů·Á ÁÖĽĽżä. +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). +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 ş¸±â +RunEntryShellExec=%1 ÇĄ˝Ă + ; *** "Setup Needs the Next Disk" stuff -ChangeDiskTitle=ĽłÄˇ ÇÁ·Î±×·ĄżˇĽ­ ´ŮŔ˝ µđ˝şĹ©°ˇ ÇĘżäÇÔ -SelectDiskLabel2=µđ˝şĹ© %1Ŕ»(¸¦) »đŔÔÇŃ ´ŮŔ˝ [Č®ŔÎ]Ŕ» Ŭ¸ŻÇĎĽĽżä.%n%nŔĚ µđ˝şĹ©ŔÇ ĆÄŔĎŔĚ ľĆ·ˇ ÇĄ˝ĂµČ Ćú´ő°ˇ ľĆ´Ń ´Ů¸Ą Ćú´őżˇ ŔÖŔ¸¸é żĂąŮ¸Ą °ć·Î¸¦ ŔÔ·ÂÇĎ°ĹłŞ [ĂŁľĆş¸±â]¸¦ Ŭ¸ŻÇĎĽĽżä. +ChangeDiskTitle=µđ˝şĹ©°ˇ ÇĘżäÇŐ´Ď´Ů +SelectDiskLabel2=µđ˝şĹ© %1Ŕ»(¸¦) »đŔÔÇϰí "Č®ŔÎ"Ŕ» Ŭ¸ŻÇϽʽÿŔ.%n%nŔĚ µđ˝şĹ© »óŔÇ ĆÄŔĎŔĚ ľĆ·ˇ °ć·Î°ˇ ľĆ´Ń °÷żˇ ŔÖ´Â °ćżě, żĂąŮ¸Ą °ć·Î¸¦ ŔÔ·ÂÇĎ°ĹłŞ "ĂŁľĆş¸±â"¸¦ Ŭ¸ŻÇϽñ⠹ٶř´Ď´Ů. PathLabel=°ć·Î(&P): -FileNotInDir2="%2"żˇĽ­ ĆÄŔĎ "%1"Ŕ»(¸¦) ĂŁŔ» Ľö ľř˝Ŕ´Ď´Ů. żĂąŮ¸Ą µđ˝şĹ©¸¦ »đŔÔÇĎ°ĹłŞ ´Ů¸Ą Ćú´ő¸¦ Ľ±ĹĂÇĎĽĽżä. -SelectDirectoryLabel=´ŮŔ˝ µđ˝şĹ©ŔÇ Ŕ§Äˇ¸¦ ÁöÁ¤ÇĎĽĽżä. +FileNotInDir2=%2żˇ ĆÄŔĎ %1Ŕ»(¸¦) Ŕ§ÄˇÇŇ Ľö ľř˝Ŕ´Ď´Ů, żĂąŮ¸Ą µđ˝şĹ©¸¦ »đŔÔÇĎ°ĹłŞ ´Ů¸Ą Ćú´ő¸¦ Ľ±ĹĂÇϽʽÿŔ. +SelectDirectoryLabel=´ŮŔ˝ µđ˝şĹ©ŔÇ Ŕ§Äˇ¸¦ ÁöÁ¤ÇϽʽÿŔ. + ; *** Installation phase messages -SetupAborted=ĽłÄˇ¸¦ żĎ·áÇĎÁö ¸řÇß˝Ŕ´Ď´Ů.%n%ną®Á¦¸¦ ÇذáÇŃ ´ŮŔ˝ ĽłÄˇ ÇÁ·Î±×·ĄŔ» ´Ů˝Ă ˝ÇÇŕÇĎĽĽżä. -EntryAbortRetryIgnore=´Ů˝Ă ˝ĂµµÇĎ·Á¸é [´Ů˝Ă ˝Ăµµ]¸¦, ±×·ˇµµ °čĽÓÇĎ·Á¸é [ą«˝Ă]¸¦, ĽłÄˇ¸¦ ĂëĽŇÇĎ·Á¸é [Áß´Ü]Ŕ» Ŭ¸ŻÇĎĽĽżä. +SetupAborted=ĽłÄˇ°ˇ żĎ·áµÇÁö ľĘľŇ˝Ŕ´Ď´Ů.%n%ną®Á¦¸¦ ÇذáÇŃ ČÄ, ´Ů˝Ă ĽłÄˇ¸¦ ˝ĂŔŰÇϽʽÿŔ. +AbortRetryIgnoreSelectAction=ľ×ĽÇŔ» Ľ±ĹĂÇŘ ÁֽʽÿŔ. +AbortRetryIgnoreRetry=Ŕç˝Ăµµ(&T) +AbortRetryIgnoreIgnore=żŔ·ů¸¦ ą«˝ĂÇϰí ÁřÇŕ(&I) +AbortRetryIgnoreCancel=ĽłÄˇ ĂëĽŇ + ; *** Installation status messages -StatusClosingApplications=ŔŔżë ÇÁ·Î±×·ĄŔ» ´Ý´Â Áß... -StatusCreateDirs=µđ·şĹ͸®¸¦ ¸¸µĺ´Â Áß... +StatusClosingApplications=ŔŔżëÇÁ·Î±×·ĄŔ» Áľ·áÇĎ´Â Áß... +StatusCreateDirs=Ćú´ő¸¦ ¸¸µĺ´Â Áß... StatusExtractFiles=ĆÄŔĎŔ» ĂßĂâÇĎ´Â Áß... -StatusCreateIcons=ąŮ·Î °ˇ±â¸¦ ¸¸µĺ´Â Áß... +StatusCreateIcons=ąŮ·Î°ˇ±â¸¦ »ýĽşÇĎ´Â Áß... StatusCreateIniEntries=INI Ç׸ńŔ» ¸¸µĺ´Â Áß... StatusCreateRegistryEntries=·ąÁö˝şĆ®¸® Ç׸ńŔ» ¸¸µĺ´Â Áß... StatusRegisterFiles=ĆÄŔĎŔ» µî·ĎÇĎ´Â Áß... StatusSavingUninstall=Á¦°Ĺ Á¤ş¸¸¦ ŔúŔĺÇĎ´Â Áß... StatusRunProgram=ĽłÄˇ¸¦ żĎ·áÇĎ´Â Áß... -StatusRestartingApplications=ŔŔżë ÇÁ·Î±×·ĄŔ» ´Ů˝Ă ˝ĂŔŰÇĎ´Â Áß... -StatusRollback=şŻ°ć »çÇ×Ŕ» ·ŃąéÇĎ´Â Áß... +StatusRestartingApplications=ŔŔżëÇÁ·Î±×·ĄŔ» ´Ů˝Ă ˝ĂŔŰÇĎ´Â Áß... +StatusRollback=şŻ°ćŔ» ĂëĽŇÇĎ´Â Áß... + ; *** Misc. errors ErrorInternal2=ł»şÎ żŔ·ů: %1 ErrorFunctionFailedNoCode=%1 ˝ÇĆĐ -ErrorFunctionFailed=%1 ˝ÇĆĐ, ÄÚµĺ %2 -ErrorFunctionFailedWithMessage=%1 ˝ÇĆĐ, ÄÚµĺ %2.%n%3 -ErrorExecutingProgram=ĆÄŔĎŔ» ˝ÇÇŕÇŇ Ľö ľřŔ˝:%n%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 +ErrorRegOpenKey=·ąÁö˝şĆ®¸® Ű ż­±â żŔ·ů:%n%1\%2 +ErrorRegCreateKey=·ąÁö˝şĆ®¸® Ű »ýĽş żŔ·ů:%n%1\%2 +ErrorRegWriteKey=·ąÁö˝şĆ®¸® Ű ľ˛±â żŔ·ů:%n%1\%2 + ; *** INI errors -ErrorIniEntry=ĆÄŔĎ "%1"żˇ INI Ç׸ńŔ» ¸¸µĺ´Â Áßżˇ żŔ·ů°ˇ ąß»ýÇß˝Ŕ´Ď´Ů. +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=±âÁ¸ ĆÄŔĎŔ» ąŮ˛Ů´Â Áß żŔ·ů ąß»ý: +FileAbortRetryIgnoreSkipNotRecommended=ŔĚ ĆÄŔĎŔ» °ÇłĘ¶ę(&S) (±ÇŔĺÇĎÁö ľĘ˝Ŕ´Ď´Ů) +FileAbortRetryIgnoreIgnoreNotRecommended=żŔ·ů¸¦ ą«˝ĂÇϰí ÁřÇŕ(&I) (±ÇŔĺÇĎÁö ľĘ˝Ŕ´Ď´Ů) +SourceIsCorrupted=żřş» ĆÄŔĎŔĚ ĽŐ»óµĘ +SourceDoesntExist=żřş» ĆÄŔĎ %1ŔĚ(°ˇ) Á¸ŔçÇĎÁö ľĘŔ˝ +ExistingFileReadOnly2=±âÁ¸ ĆÄŔĎŔş Ŕбâ ŔüżëŔĚ±â¶§ą®żˇ ´ëĂĽÇŇ Ľö ľř˝Ŕ´Ď´Ů. +ExistingFileReadOnlyRetry=Ŕбâ Ŕüżë ĽÓĽşŔ» ÇŘÁ¦ÇĎ°í ´Ů˝Ă ˝ĂµµÇĎ·Á¸é(&R) +ExistingFileReadOnlyKeepExisting=±âÁ¸ ĆÄŔĎŔ» ŔŻÁö(&K) +ErrorReadingExistingDest=±âÁ¸ ĆÄŔĎŔ» ŔĐ´Â µżľČ żŔ·ů ąß»ý: +FileExists=ĆÄŔĎŔĚ ŔĚąĚ Á¸ŔçÇŐ´Ď´Ů.%n%nĆÄŔĎŔ» µ¤ľîľ˛˝Ă°Ú˝Ŕ´Ď±î? +ExistingFileNewer=±âÁ¸ ĆÄŔĎŔĚ ĽłÄˇÇĎ·Á°í ÇĎ´Â ĆÄŔĎş¸´Ů »ő ĆÄŔĎŔÔ´Ď´Ů, ±âÁ¸ ĆÄŔĎŔ» ŔŻÁöÇϽñ⠹ٶř´Ď´Ů.%n%n±âÁ¸ ĆÄŔĎŔ» ŔŻÁöÇϽðڽŔ´Ď±î? +ErrorChangingAttr=±âÁ¸ ĆÄŔĎŔÇ ĽÓĽşŔ» şŻ°ćÇĎ´Â µżľČ żŔ·ů ąß»ý: +ErrorCreatingTemp=´ë»ó Ćú´őżˇ ĆÄŔĎŔ» ¸¸µĺ´Â µżľČ żŔ·ů ąß»ý: +ErrorReadingSource=żřş» ĆÄŔĎŔ» ŔĐ´Â µżľČ żŔ·ů ąß»ý: +ErrorCopying=ĆÄŔĎŔ» şą»çÇĎ´Â µżľČ żŔ·ů ąß»ý: +ErrorReplacingExistingFile=±âÁ¸ ĆÄŔĎŔ» ±łĂĽÇĎ´Â µżľČ żŔ·ů ąß»ý: ErrorRestartReplace=RestartReplace ˝ÇĆĐ: -ErrorRenamingTemp=´ë»ó µđ·şĹ͸®żˇ ŔÖ´Â ĆÄŔĎ Ŕ̸§Ŕ» ąŮ˛Ů´Â Áß żŔ·ů ąß»ý: -ErrorRegisterServer=DLL/OCX¸¦ µî·ĎÇŇ Ľö ľřŔ˝: %1 -ErrorRegSvr32Failed=Áľ·á ÄÚµĺ %1°ú(żÍ) ÇÔ˛˛ RegSvr32 ˝ÇĆĐ -ErrorRegisterTypeLib=Çü˝Ä ¶óŔĚşę·Ż¸®¸¦ µî·ĎÇŇ Ľö ľřŔ˝: %1 +ErrorRenamingTemp=´ë»ó Ćú´ő ł»ŔÇ ĆÄŔĎ Ŕ̸§Ŕ» ąŮ˛Ů´Â µżľČ żŔ·ů ąß»ý: +ErrorRegisterServer=DLL/OCX µî·Ď ˝ÇĆĐ: %1 +ErrorRegSvr32Failed=RegSvr32°ˇ ´ŮŔ˝ Áľ·á ÄÚµĺ·Î ˝ÇĆĐ: %1 +ErrorRegisterTypeLib=´ŮŔ˝ ŔŻÇüŔÇ ¶óŔĚşę·Ż¸® µî·Ďżˇ ˝ÇĆĐ: %1 + +; *** Uninstall display name markings +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32şńĆ® +UninstallDisplayNameMark64Bit=64şńĆ® +UninstallDisplayNameMarkAllUsers=¸đµç »çżëŔÚ +UninstallDisplayNameMarkCurrentUser=ÇöŔç »çżëŔÚ + ; *** Post-installation errors -ErrorOpeningReadme=README ĆÄŔĎŔ» ż©´Â Áßżˇ żŔ·ů°ˇ ąß»ýÇß˝Ŕ´Ď´Ů. -ErrorRestartingComputer=ĽłÄˇ ÇÁ·Î±×·ĄżˇĽ­ ÄÄÇ»Ĺ͸¦ ´Ů˝Ă ˝ĂŔŰÇŇ Ľö ľř˝Ŕ´Ď´Ů. ĽöµżŔ¸·Î ÁřÇŕÇĎĽĽżä. +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" ĆÄŔĎŔĚ ĽŐ»óµÇľú˝Ŕ´Ď´Ů. Á¦°ĹÇŇ Ľö ľř˝Ŕ´Ď´Ů. +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ľĆÁ÷ ŔĚ ĆÄŔĎŔ» »çżëÇĎ´Â ÇÁ·Î±×·ĄŔĚ ŔִµĄ ŔĚ ĆÄŔĎŔ» Á¦°ĹÇϸé ÇŘ´ç ÇÁ·Î±×·ĄŔĚ żĂąŮ¸Ł°Ô Ŕ۵żÇĎÁö ľĘŔ» Ľö ŔÖ˝Ŕ´Ď´Ů. Ŕß ¸đ¸Ł´Â °ćżě [ľĆ´Ďżä]¸¦ Ľ±ĹĂÇĎĽĽżä. ˝Ă˝şĹŰżˇ ĆÄŔĎŔ» ±×´ë·Î µÎľîµµ ľĆą«·± ą®Á¦°ˇ ąß»ýÇĎÁö ľĘ˝Ŕ´Ď´Ů. +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 ŔĄ Á¤ş¸ +AdditionalIcons=ľĆŔĚÄÜ Ăß°ˇ: +CreateDesktopIcon=ąŮĹÁ Č­¸éżˇ ąŮ·Î°ˇ±â ¸¸µé±â(&D) +CreateQuickLaunchIcon=şü¸Ą ˝ÇÇŕ ľĆŔĚÄÜ ¸¸µé±â(&Q) +ProgramOnTheWeb=%1 ŔĄĆäŔĚÁö UninstallProgram=%1 Á¦°Ĺ -LaunchProgram=%1 ˝ĂŔŰ -AssocFileExtension=%1Ŕ»(¸¦) %2 ĆÄŔĎ Č®Ŕĺ¸í°ú ż¬°á(&A) -AssocingFileExtension=%1Ŕ»(¸¦) %2 ĆÄŔĎ Č®Ŕĺ¸í°ú ż¬°á Áß... +LaunchProgram=%1 ˝ÇÇŕ +AssocFileExtension=ĆÄŔĎ Č®ŔĺŔÚ %2Ŕ»(¸¦) %1żˇ ż¬°áÇŐ´Ď´Ů. +AssocingFileExtension=ĆÄŔĎ Č®ŔĺŔÚ %2Ŕ»(¸¦) %1żˇ ż¬°áÇĎ´Â Áß... AutoStartProgramGroupDescription=˝ĂŔŰ: -AutoStartProgram=%1 ŔÚµż ˝ĂŔŰ -AddonHostProgramNotFound=Ľ±ĹĂÇŃ Ćú´őżˇĽ­ %1Ŕ»(¸¦) ĂŁŔ» Ľö ľř˝Ŕ´Ď´Ů.%n%n±×·ˇµµ °čĽÓÇϽðڽŔ´Ď±î? \ No newline at end of file +AutoStartProgram=%1Ŕ»(¸¦) ŔÚµżŔ¸·Î ˝ĂŔŰ +AddonHostProgramNotFound=%1Ŕş(´Â) Ľ±ĹĂÇŃ Ćú´őżˇ Ŕ§ÄˇÇŇ Ľö ľř˝Ŕ´Ď´Ů.%n%n±×·ˇµµ °čĽÓÇϽðڽŔ´Ď±î? diff --git a/build/win32/i18n/Default.zh-cn.isl b/build/win32/i18n/Default.zh-cn.isl index e384e83d30..5c5df9a166 100644 --- a/build/win32/i18n/Default.zh-cn.isl +++ b/build/win32/i18n/Default.zh-cn.isl @@ -1,16 +1,17 @@ -; *** Inno Setup version 5.5.3+ Simplified Chinese messages *** +; *** Inno Setup version 6.0.3+ Chinese Simplified messages *** ; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ +; Maintained by Zhenghan Yang +; Email: 847320916@QQ.com +; Translation based on network resource +; The latest Translation is on https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation ; -; 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 +LanguageName=简体中文 +; If Language Name display incorrect, uncomment next line +; LanguageName=<7B80><4F53><4E2D><6587> LanguageID=$0804 LanguageCodePage=936 ; If the language you are translating to requires special font faces or @@ -23,276 +24,342 @@ LanguageCodePage=936 ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 + [Messages] -; *** Application titles -SetupAppTitle=°˛×°łĚĐň -SetupWindowTitle=°˛×°łĚĐň - %1 -UninstallAppTitle=жÔŘ -UninstallAppFullTitle=%1 жÔŘ + +; *** ĺş”ç”¨ç¨‹ĺşŹć ‡é˘ +SetupAppTitle=安装 +SetupWindowTitle=安装 - %1 +UninstallAppTitle=卸载 +UninstallAppFullTitle=%1 卸载 + ; *** Misc. common -InformationTitle=ĐĹϢ -ConfirmTitle=Č·ČĎ -ErrorTitle=´íÎó +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 +SetupLdrStartupMessage=现在将安装 %1。您ćłč¦ç»§ç»­ĺ—? +LdrCannotCreateTemp=不č˝ĺ›ĺ»şä¸´ć—¶ć–‡ä»¶ă€‚安装中断。 +LdrCannotExecTemp=不č˝ć‰§čˇŚä¸´ć—¶ç›®ĺ˝•中的文件。安装中断。 +HelpTextNote= + +; *** ĺŻĺŠ¨é”™čŻŻć¶ćŻ +LastErrorMessage=%1.%n%n错误 %2: %3 +SetupFileMissing=安装目录中的文件 %1 丢失。请修正这个问é˘ć–获取一个新的程序副本。 +SetupFileCorrupt=安装文件已损坏。请获取一个新的程序副本。 +SetupFileCorruptOrWrongVer=安装文件已损坏,ć–ćŻä¸Žčż™ä¸Şĺ®‰čŁ…ç¨‹ĺşŹçš„ç‰ćś¬ä¸Ťĺ…Ľĺ®ąă€‚请修正这个问é˘ć–获取新的程序副本。 +InvalidParameter=ć— ć•的命令行参数: %n%n%1 +SetupAlreadyRunning=安装程序正在čżčˇŚă€‚ +WindowsVersionNotSupported=这个程序不支ćŚčŻĄç‰ćś¬çš„计算机čżčˇŚă€‚ +WindowsServicePackRequired=这个程序č¦ć±‚%1服务包%1ć–ć›´é«ă€‚ +NotOnThisPlatform=这个程序将不č˝čżčˇŚäşŽ %1。 +OnlyOnThisPlatform=这个程序必须čżčˇŚäşŽ %1。 +OnlyOnTheseArchitectures=这个程序只č˝ĺś¨ä¸şä¸‹ĺ—处ç†ĺ™¨ç»“构设计的 Windows ç‰ćś¬ä¸­čż›čˇŚĺ®‰čŁ…:%n%n%1 +WinVersionTooLowError=čż™ä¸Şç¨‹ĺşŹéś€č¦ %1 ç‰ćś¬ %2 ć–ć›´é«ă€‚ +WinVersionTooHighError=这个程序不č˝ĺ®‰čŁ…äşŽ %1 ç‰ćś¬ %2 ć–ć›´é«ă€‚ +AdminPrivilegesRequired=在安装这个程序时您必须以管ç†ĺ‘身份登录。 +PowerUserPrivilegesRequired=在安装这个程序时您必须以管ç†ĺ‘身份ć–有ćťé™çš„用ć·ç»„身份登录。 +SetupAppRunningError=安装程序发现 %1 当前正在čżčˇŚă€‚%n%n请ĺ…关闭所有čżčˇŚçš„窗口,然ĺŽĺŤ•击“确定”继续,ć–按“取ć¶â€ťé€€ĺ‡şă€‚ +UninstallAppRunningError=卸载程序发现 %1 当前正在čżčˇŚă€‚%n%n请ĺ…关闭所有čżčˇŚçš„窗口,然ĺŽĺŤ•击“确定”继续,ć–按“取ć¶â€ťé€€ĺ‡şă€‚ + +; *** ĺŻĺŠ¨é—®é˘ +PrivilegesRequiredOverrideTitle=选择安装程序模式 +PrivilegesRequiredOverrideInstruction=选择安装模式 +PrivilegesRequiredOverrideText1=%1 可以为所有用ć·ĺ®‰čŁ…(需č¦ç®ˇç†ĺ‘ćťé™),ć–仅为您安装。 +PrivilegesRequiredOverrideText2=%1 只č˝ä¸şć‚¨ĺ®‰čŁ…ďĽŚć–为所有用ć·ĺ®‰čŁ…(需č¦ç®ˇç†ĺ‘ćťé™)。 +PrivilegesRequiredOverrideAllUsers=为所有用ć·ĺ®‰čŁ…(&A) +PrivilegesRequiredOverrideAllUsersRecommended=为所有用ć·ĺ®‰čŁ…(建议选项)(&A) +PrivilegesRequiredOverrideCurrentUser=只为ć‘安装(&M) +PrivilegesRequiredOverrideCurrentUserRecommended=只为ć‘安装(建议选项)(&M) + +; *** ĺ…¶ĺ®é”™čŻŻ +ErrorCreatingDir=安装程序不č˝ĺ›ĺ»şç›®ĺ˝•“%1”。 +ErrorTooManyFilesInDir=不č˝ĺś¨ç›®ĺ˝•“%1”中ĺ›ĺ»şć–‡ä»¶ďĽŚĺ› ä¸şé‡Śéť˘çš„文件太多 + +; *** 安装程序公共ć¶ćŻ +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=µĄ»÷ˇ°ĎÂŇ»˛˝ˇ±ŇÔĽĚĐř٬»ňµĄ»÷ˇ°ČˇĎűˇ±ŇÔÍËłö°˛×°łĚĐňˇŁ + +; *** 按钮 +ButtonBack=< 上一步(&B) +ButtonNext=下一步(&N) > +ButtonInstall=安装(&I) +ButtonOK=确定 +ButtonCancel=ĺŹ–ć¶ +ButtonYes=ćŻ(&Y) +ButtonYesToAll=ĺ…¨ćŻ(&A) +ButtonNo=ĺ¦(&N) +ButtonNoToAll=ĺ…¨ĺ¦(&O) +ButtonFinish=完ć(&F) +ButtonBrowse=浏č§(&B)... +ButtonWizardBrowse=浏č§(&R)... +ButtonNewFolder=新建文件夹(&M) + +; *** “选择语言”对话框ć¶ćŻ +SelectLanguageTitle=选择安装语言 +SelectLanguageLabel=选择安装时č¦ä˝żç”¨çš„语言。 + +; *** 公共ĺ‘导文字 +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=ÍęČ«°˛×° +BrowseDialogTitle=浏č§ć–‡ä»¶ĺ¤ą +BrowseDialogLabel=在下ĺ—ĺ—表中选择一个文件夹,然ĺŽĺŤ•击“确定”。 +NewFolderName=新建文件夹 + +; *** “欢迎”ĺ‘导页 +WelcomeLabel1=欢迎使用 [name] 安装ĺ‘导 +WelcomeLabel2=现在将安装 [name/ver] ĺ°ć‚¨çš„电脑中。%n%n推čŤć‚¨ĺś¨ç»§ç»­ĺ®‰čŁ…ĺ‰Ťĺ…łé—­ć‰€ćś‰ĺ…¶ĺ®ĺş”用程序。 + +; *** “密ç â€ťĺ‘导页 +WizardPassword=ĺŻ†ç  +PasswordLabel1=这个安装程序有密ç äżťćŠ¤ă€‚ +PasswordLabel3=请输入密ç ďĽŚç„¶ĺŽĺŤ•击“下一步”继续。密ç ĺŚşĺ†ĺ¤§ĺ°Źĺ†™ă€‚ +PasswordEditLabel=密ç (&P): +IncorrectPassword=您输入的密ç ä¸Ťć­Łçˇ®ďĽŚčŻ·é‡ŤčŻ•ă€‚ + +; *** “许可协议”ĺ‘导页 +WizardLicense=许可协议 +LicenseLabel=继续安装前请é…读下ĺ—重č¦äżˇćŻă€‚ +LicenseLabel3=请仔细é…读下ĺ—许可协议。您在继续安装前必须ĺŚć„Źčż™äş›ĺŤŹč®®ćťˇć¬ľă€‚ +LicenseAccepted=ć‘ĺŚć„Źć­¤ĺŤŹč®®(&A) +LicenseNotAccepted=ć‘不ĺŚć„Źć­¤ĺŤŹč®®(&D) + +; *** “信ćŻâ€ťĺ‘导页 +WizardInfoBefore=äżˇćŻ +InfoBeforeLabel=请在继续安装前é…读下ĺ—重č¦äżˇćŻă€‚ +InfoBeforeClickLabel=如果您ćłç»§ç»­ĺ®‰čŁ…ďĽŚĺŤ•ĺ‡»â€śä¸‹ä¸€ć­Ąâ€ťă€‚ +WizardInfoAfter=äżˇćŻ +InfoAfterLabel=请在继续安装前é…读下ĺ—重č¦äżˇćŻă€‚ +InfoAfterClickLabel=如果您ćłç»§ç»­ĺ®‰čŁ…ďĽŚĺŤ•ĺ‡»â€śä¸‹ä¸€ć­Ąâ€ťă€‚ + +; *** “用ć·äżˇćŻâ€ťĺ‘导页 +WizardUserInfo=用ć·äżˇćŻ +UserInfoDesc=请输入您的信ćŻă€‚ +UserInfoName=用ć·ĺŤ(&U): +UserInfoOrg=组织(&O): +UserInfoSerial=序ĺ—号(&S): +UserInfoNameRequired=您必须输入ĺŤĺ­—。 + +; *** “选择目标目录”ĺ‘导面 +WizardSelectDir=选择目标位置 +SelectDirDesc=您ćłĺ°† [name] 安装在什äąĺś°ć–ąďĽź +SelectDirLabel3=安装程序将安装 [name] ĺ°ä¸‹ĺ—文件夹中。 +SelectDirBrowseLabel=单击“下一步”继续。如果您ćłé€‰ć‹©ĺ…¶ĺ®ć–‡ä»¶ĺ¤ąďĽŚĺŤ•击“浏č§â€ťă€‚ +DiskSpaceGBLabel=至少需č¦ćś‰ [gb] GB 的可用çŁç›ç©şé—´ă€‚ +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不ĺ­ĺś¨ă€‚您ćłč¦ĺ›ĺ»şć­¤ç›®ĺ˝•ĺ—? + +; *** “选择组件”ĺ‘导页 +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ĘÇ·ńČÔŇŞĽĚĐř? +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... +ComponentsDiskSpaceGBLabel=ĺ˝“ĺ‰Ťé€‰ć‹©çš„ç»„ä»¶č‡łĺ°‘éś€č¦ [gb] GB çš„çŁç›ç©şé—´ă€‚ +ComponentsDiskSpaceMBLabel=ĺ˝“ĺ‰Ťé€‰ć‹©çš„ç»„ä»¶č‡łĺ°‘éś€č¦ [mb] MB çš„çŁç›ç©şé—´ă€‚ + +; *** “选择附加任务”ĺ‘导页 +WizardSelectTasks=选择附加任务 +SelectTasksDesc=您ćłč¦ĺ®‰čŁ…ç¨‹ĺşŹć‰§čˇŚĺ“Şäş›é™„ĺŠ ä»»ĺŠˇďĽź +SelectTasksLabel2=选择您ćłč¦ĺ®‰čŁ…ç¨‹ĺşŹĺś¨ĺ®‰čŁ… [name] 时执行的附加任务,然ĺŽĺŤ•击“下一步”。 + +; *** “选择开始菜单文件夹”ĺ‘导页 +WizardSelectProgramGroup=选择开始菜单文件夹 +SelectStartMenuFolderDesc=您ćłĺś¨ĺ“Şé‡Ść”ľç˝®ç¨‹ĺşŹçš„快捷方式? +SelectStartMenuFolderLabel3=安装程序现在将在下ĺ—开始菜单文件夹中ĺ›ĺ»şç¨‹ĺşŹçš„快捷方式。 +SelectStartMenuFolderBrowseLabel=单击“下一步”继续。如果您ćłé€‰ć‹©ĺ…¶ĺ®ć–‡ä»¶ĺ¤ąďĽŚĺŤ•击“浏č§â€ťă€‚ +MustEnterGroupName=您必须输入一个文件夹ĺŤă€‚ +GroupNameTooLong=文件夹ĺŤć–路径太长。 +InvalidGroupName=文件夹ĺŤćŻć— ć•的。 +BadGroupName=文件夹ĺŤä¸Ťč˝ĺŚ…ĺ«ä¸‹ĺ—任何字符:%n%n%1 +NoProgramGroupCheck2=不ĺ›ĺ»şĺĽ€ĺ§‹čŹśĺŤ•ć–‡ä»¶ĺ¤ą(&D) + +; *** “准备安装”ĺ‘导页 +WizardReady=准备安装 +ReadyLabel1=安装程序现在准备开始安装 [name] ĺ°ć‚¨çš„电脑中。 +ReadyLabel2a=单击“安装”继续此安装程序。如果您ćłč¦ĺ›žéˇľć–改ĺŹč®ľç˝®ďĽŚčŻ·ĺŤ•ĺ‡»â€śä¸Šä¸€ć­Ąâ€ťă€‚ +ReadyLabel2b=单击“安装”继续此安装程序? +ReadyMemoUserInfo=用ć·äżˇćŻ: +ReadyMemoDir=目标位置: +ReadyMemoType=安装类型: +ReadyMemoComponents=选定组件: +ReadyMemoGroup=开始菜单文件夹: +ReadyMemoTasks=附加任务: + +; *** “正在准备安装”ĺ‘导页 +WizardPreparing=正在准备安装 +PreparingDesc=安装程序正在准备安装 [name] ĺ°ć‚¨çš„电脑中。 +PreviousInstallNotCompleted=ĺ…前程序的安装/卸载未完ć。您需č¦é‡Ťć–°ĺŻĺŠ¨ć‚¨çš„ç”µč„‘ć‰Ťč˝ĺ®Ść安装。%n%n在重新ĺŻĺŠ¨ç”µč„‘ĺŽďĽŚĺ†ŤčżčˇŚĺ®‰čŁ…ĺ®Ść [name] 的安装。 +CannotContinue=安装程序不č˝ç»§ç»­ă€‚请单击“取ć¶â€ťé€€ĺ‡şă€‚ +ApplicationsFound=下ĺ—应用程序正在使用的文件需č¦ć›´ć–°č®ľç˝®ă€‚ĺ®ćŻĺ»şč®®ć‚¨ĺ…许安装程序自动关闭这些应用程序。 +ApplicationsFound2=下ĺ—应用程序正在使用的文件需č¦ć›´ć–°č®ľç˝®ă€‚ĺ®ćŻĺ»şč®®ć‚¨ĺ…许安装程序自动关闭这些应用程序。安装完ćĺŽďĽŚĺ®‰čŁ…ç¨‹ĺşŹĺ°†ĺ°ťčŻ•é‡Ťć–°ĺŻĺŠ¨ĺş”ç”¨ç¨‹ĺşŹă€‚ +CloseApplications=自动关闭该应用程序(&A) +DontCloseApplications=不č¦ĺ…łé—­čŻĄĺş”ç”¨ç¨‹ĺşŹ(D) +ErrorCloseApplications=安装程序无法自动关闭所有应用程序。在继续之前,ć‘们建议您关闭所有使用需č¦ć›´ć–°çš„安装程序文件。 +PrepareToInstallNeedsRestart=安装程序必须重新ĺŻĺŠ¨č®ˇç®—ćśşă€‚é‡Ťć–°ĺŻĺŠ¨č®ˇç®—ćśşĺŽďĽŚčŻ·ĺ†Ťć¬ˇčżčˇŚĺ®‰čŁ…ç¨‹ĺşŹä»Ąĺ®Ść [name] 的安装。%n%nćŻĺ¦ç«‹ĺŤłé‡Ťć–°ĺŻĺŠ¨ďĽź + +; *** “正在安装”ĺ‘导页 +WizardInstalling=正在安装 +InstallingLabel=安装程序正在安装 [name] ĺ°ć‚¨çš„电脑中,请稍等。 + +; *** “安装完ć”ĺ‘导页 +FinishedHeadingLabel=[name] 安装完ć +FinishedLabelNoIcons=安装程序已在您的电脑中安装了 [name]。 +FinishedLabel=安装程序已在您的电脑中安装了 [name]。此应用程序可以通过选择安装的快捷方式čżčˇŚă€‚ +ClickFinish=单击“完ć”退出安装程序。 +FinishedRestartLabel=č¦ĺ®Ść [name] 的安装,安装程序必须重新ĺŻĺŠ¨ć‚¨çš„ç”µč„‘ă€‚ć‚¨ćłçŽ°ĺś¨é‡Ťć–°ĺŻĺЍĺ—? +FinishedRestartMessage=č¦ĺ®Ść [name] 的安装,安装程序必须重新ĺŻĺŠ¨ć‚¨çš„ç”µč„‘ă€‚%n%n您ćłçŽ°ĺś¨é‡Ťć–°ĺŻĺЍĺ—? +ShowReadmeCheck=ćŻďĽŚć‚¨ćłćźĄé…自述文件 +YesRadio=ćŻďĽŚç«‹ĺŤłé‡Ťć–°ĺŻĺŠ¨ç”µč„‘(&Y) +NoRadio=ĺ¦ďĽŚç¨ŤĺŽé‡Ťć–°ĺŻĺŠ¨ç”µč„‘(&N) +; 用于象“čżčˇŚ MyProg.exe” +RunEntryExec=čżčˇŚ %1 +; ç”¨äşŽč±ˇâ€śćźĄé… Readme.txt” +RunEntryShellExec=ćźĄé… %1 + +; *** “安装程序需č¦ä¸‹ä¸€ĺĽ çŁç›â€ťćŹç¤ş +ChangeDiskTitle=安装程序需č¦ä¸‹ä¸€ĺĽ çŁç› +SelectDiskLabel2=请插入çŁç› %1 并单击“确定”。%n%n如果这个çŁç›ä¸­çš„文件不č˝ĺś¨ä¸ŤĺŚäşŽä¸‹ĺ—ćľç¤şçš„文件夹中找ĺ°ďĽŚčľ“入正确的路径ć–单击“浏č§â€ťă€‚ +PathLabel=č·Żĺľ„(&P): +FileNotInDir2=文件“%1”不č˝ĺś¨â€ś%2”定位。请插入正确的çŁç›ć–选择其ĺ®ć–‡ä»¶ĺ¤ąă€‚ +SelectDirectoryLabel=请指定下一张çŁç›çš„位置。 + +; *** 安装状ć€ć¶ćŻ +SetupAborted=安装程序未完ć安装。%n%n请修正这个问é˘ĺą¶é‡Ťć–°čżčˇŚĺ®‰čŁ…ç¨‹ĺşŹă€‚ +AbortRetryIgnoreSelectAction=选项 +AbortRetryIgnoreRetry=重试(&T) +AbortRetryIgnoreIgnore=忽略错误并继续(&I) +AbortRetryIgnoreCancel=关闭安装程序 + +; *** 安装状ć€ć¶ćŻ +StatusClosingApplications=正在关闭应用程序... +StatusCreateDirs=正在ĺ›ĺ»şç›®ĺ˝•... +StatusExtractFiles=正在解压缩文件... +StatusCreateIcons=正在ĺ›ĺ»şĺż«ćŤ·ć–ąĺĽŹ... +StatusCreateIniEntries=正在ĺ›ĺ»ş INI 条目... +StatusCreateRegistryEntries=正在ĺ›ĺ»şćł¨ĺ†Śčˇ¨ćťˇç›®... +StatusRegisterFiles=正在注册文件... +StatusSavingUninstall=正在保ĺ­ĺŤ¸č˝˝äżˇćŻ... +StatusRunProgram=正在完ć安装... +StatusRestartingApplications=正在重ĺŻĺş”用程序... +StatusRollback=正在撤销更改... + +; *** ĺ…¶ĺ®é”™čŻŻ +ErrorInternal2=内é¨é”™čŻŻ: %1 +ErrorFunctionFailedNoCode=%1 失败 +ErrorFunctionFailed=%1 ĺ¤±č´ĄďĽ›é”™čŻŻä»Łç  %2 +ErrorFunctionFailedWithMessage=%1 ĺ¤±č´ĄďĽ›é”™čŻŻä»Łç  %2.%n%3 +ErrorExecutingProgram=不č˝ć‰§čˇŚć–‡ä»¶:%n%1 + +; *** 注册表错误 +ErrorRegOpenKey=打开注册表项时出错:%n%1\%2 +ErrorRegCreateKey=ĺ›ĺ»şćł¨ĺ†Śčˇ¨éˇąć—¶ĺ‡şé”™:%n%1\%2 +ErrorRegWriteKey=写入注册表项时出错:%n%1\%2 + +; *** INI 错误 +ErrorIniEntry=在文件“%1”ĺ›ĺ»ş INI 项目错误。 + +; *** 文件复ĺ¶é”™čŻŻ +FileAbortRetryIgnoreSkipNotRecommended=跳过这个文件 (不推čŤ)(&S) +FileAbortRetryIgnoreIgnoreNotRecommended=忽略错误并继续 (不推čŤ)(&I) +SourceIsCorrupted=ćşć–‡ä»¶ĺ·˛ćŤźĺťŹ +SourceDoesntExist=ćşć–‡ä»¶â€ś%1”不ĺ­ĺś¨ +ExistingFileReadOnly2=无法替换现有文件,因为ĺ®ćŻĺŹŞčŻ»çš„ă€‚ +ExistingFileReadOnlyRetry=移除只读属性并重试(&R) +ExistingFileReadOnlyKeepExisting=保留现有文件(&K) +ErrorReadingExistingDest=尝试读取现有文件时发生一个错误: +FileExists=文件已经ĺ­ĺś¨ă€‚%n%n您ćłč¦ĺ®‰čŁ…ç¨‹ĺşŹč¦†ç›–ĺ®ĺ—? +ExistingFileNewer=现有的文件新与安装程序č¦ĺ®‰čŁ…çš„ć–‡ä»¶ă€‚ćŽ¨čŤć‚¨äżťç•™çŽ°ćś‰ć–‡ä»¶ă€‚%n%n您ćłč¦äżťç•™çŽ°ćś‰çš„ć–‡ä»¶ĺ—? +ErrorChangingAttr=尝试改ĺŹä¸‹ĺ—现有的文件的属性时发生一个错误: +ErrorCreatingTemp=尝试在目标目录ĺ›ĺ»şć–‡ä»¶ć—¶ĺŹ‘ç”źä¸€ä¸Şé”™čŻŻ: +ErrorReadingSource=尝试读取下ĺ—ćşć–‡ä»¶ć—¶ĺŹ‘ç”źä¸€ä¸Şé”™čŻŻ: +ErrorCopying=尝试复ĺ¶ä¸‹ĺ—文件时发生一个错误: +ErrorReplacingExistingFile=尝试替换现有的文件时发生错误: +ErrorRestartReplace=重ĺŻç”µč„‘ĺŽć›żćŤ˘ć–‡ä»¶ĺ¤±č´Ą: +ErrorRenamingTemp=尝试重新命ĺŤä»Ąä¸‹ç›®ć ‡ç›®ĺ˝•中的一个文件时发生错误: +ErrorRegisterServer=不č˝ćł¨ĺ†Ś DLL/OCX: %1 +ErrorRegSvr32Failed=RegSvr32 ĺ¤±č´ĄďĽ›é€€ĺ‡şä»Łç  %1 +ErrorRegisterTypeLib=不č˝ćł¨ĺ†Śç±»ĺž‹ĺş“: %1 + +; *** 卸载ćľç¤şĺŤĺ­—标记 +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32位 +UninstallDisplayNameMark64Bit=64位 +UninstallDisplayNameMarkAllUsers=ć‰€ćś‰ç”¨ć· +UninstallDisplayNameMarkCurrentUser=ĺ˝“ĺ‰Ťç”¨ć· + +; *** 安装ĺŽé”™čŻŻ +ErrorOpeningReadme=当尝试打开自述文件时发生一个错误。 +ErrorRestartingComputer=安装程序不č˝é‡Ťć–°ĺŻĺŠ¨ç”µč„‘ďĽŚčŻ·ć‰‹ĺŠ¨é‡ŤĺŻă€‚ + +; *** 卸载ć¶ćŻ +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”文件被破坏,不č˝ĺŤ¸č˝˝ + +; *** 卸载状ć€ć¶ćŻ +ConfirmDeleteSharedFileTitle=ĺ é™¤ĺ…±äş«ć–‡ä»¶ĺ—? +ConfirmDeleteSharedFile2=系统中包ĺ«çš„下ĺ—共享文件已经不被其ĺ®ç¨‹ĺşŹä˝żç”¨ă€‚您ćłč¦ĺŤ¸č˝˝ç¨‹ĺşŹĺ é™¤čż™äş›ĺ…±äş«ć–‡ä»¶ĺ—?%n%n如果这些文件被ĺ é™¤ďĽŚä˝†čżćś‰ç¨‹ĺşŹć­Łĺś¨ä˝żç”¨čż™äş›ć–‡ä»¶ďĽŚčż™äş›ç¨‹ĺşŹĺŹŻč˝ä¸Ťč˝ć­Łçˇ®ć‰§čˇŚă€‚如果您不č˝çˇ®ĺ®šďĽŚé€‰ć‹©â€śĺ¦â€ťă€‚把这些文件保留在系统中以免引起问é˘ă€‚ +SharedFileNameLabel=文件ĺŤ: +SharedFileLocationLabel=位置: +WizardUninstalling=ĺŤ¸č˝˝çŠ¶ć€ +StatusUninstalling=正在卸载 %1... + ; *** Shutdown block reasons -ShutdownBlockReasonInstallingApp=ŐýÔÚ°˛×° %1ˇŁ -ShutdownBlockReasonUninstallingApp=ŐýÔÚжÔŘ %1ˇŁ +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 + +NameAndVersion=%1 ç‰ćś¬ %2 +AdditionalIcons=附加快捷方式: +CreateDesktopIcon=ĺ›ĺ»şćˇŚéť˘ĺż«ćŤ·ć–ąĺĽŹ(&D) +CreateQuickLaunchIcon=ĺ›ĺ»şĺż«é€źčżčˇŚć Źĺż«ćŤ·ć–ąĺĽŹ(&Q) +ProgramOnTheWeb=%1 网站 +UninstallProgram=卸载 %1 +LaunchProgram=čżčˇŚ %1 +AssocFileExtension=ĺ°† %2 文件扩展ĺŤä¸Ž %1 建立关č”(&A) +AssocingFileExtension=正在将 %2 文件扩展ĺŤä¸Ž %1 建立关č”... +AutoStartProgramGroupDescription=ĺŻĺŠ¨ç»„: +AutoStartProgram=自动ĺŻĺЍ %1 +AddonHostProgramNotFound=%1无法找ĺ°ć‚¨ć‰€é€‰ć‹©çš„文件夹。%n%n您ćłč¦ç»§ç»­ĺ—? + diff --git a/build/win32/i18n/Default.zh-tw.isl b/build/win32/i18n/Default.zh-tw.isl index 4746349e19..fb748761f9 100644 --- a/build/win32/i18n/Default.zh-tw.isl +++ b/build/win32/i18n/Default.zh-tw.isl @@ -1,298 +1,359 @@ -; *** Inno Setup version 5.5.3+ Traditional Chinese messages *** +; *** Inno Setup version 6.0.0+ Chinese Traditional messages *** ; -; To download user-contributed translations of this file, go to: -; http://www.jrsoftware.org/files/istrans/ +; Name: John Wu, mr.johnwu@gmail.com +; Base on 5.5.3+ translations by Samuel Lee, Email: 751555749@qq.com +; Translation based on network resource ; -; 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 +; If Language Name display incorrect, uncomment next line +LanguageName=<7e41><9ad4><4e2d><6587> LanguageID=$0404 -LanguageCodePage=950 +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 +DialogFontName=ć–°ç´°ćŽé«” +DialogFontSize=9 +TitleFontName=Arial +TitleFontSize=28 +WelcomeFontName=ć–°ç´°ćŽé«” +WelcomeFontSize=12 +CopyrightFontName=ć–°ç´°ćŽé«” +CopyrightFontSize=9 + [Messages] + ; *** Application titles -SetupAppTitle=¦w¸Ëµ{¦ˇ -SetupWindowTitle=¦w¸Ëµ{¦ˇ - %1 -UninstallAppTitle=¸Ń°Ł¦w¸Ë -UninstallAppFullTitle=%1 ¸Ń°Ł¦w¸Ë +SetupAppTitle=安裝程式 +SetupWindowTitle=%1 安裝程式 +UninstallAppTitle=解除安裝 +UninstallAppFullTitle=解除安裝 %1 + ; *** Misc. common -InformationTitle=¸ę°T -ConfirmTitle=˝T»{ -ErrorTitle=żů»~ +InformationTitle=č¨ŠćŻ +ConfirmTitle=確認 +ErrorTitle=錯誤 + ; *** SetupLdr messages -SetupLdrStartupMessage=ło·|¦w¸Ë %1ˇC­nÄ~Äň¶Ü? -LdrCannotCreateTemp=µLŞk«ŘĄßĽČ¦sŔɡC¦w¸Ë¤w¤¤¤î -LdrCannotExecTemp=µLŞk°ő¦ćĽČ¦sĄŘżý¤¤ŞşŔɮסC¦w¸Ë¤w¤¤¤î +SetupLdrStartupMessage=這將ćśĺ®‰čŁť %1。您ćłč¦çąĽçşŚĺ—Ž? +LdrCannotCreateTemp=無法建立暫ĺ­ćŞ”ćˇă€‚安裝程式將ćśçµćťźă€‚ +LdrCannotExecTemp=無法執行暫ĺ­ćŞ”ćˇă€‚安裝程式將ćśçµćťźă€‚ +HelpTextNote= + ; *** Startup error messages -LastErrorMessage=%1ˇC%n%nżů»~ %2: %3 -SetupFileMissing=¦w¸ËĄŘżý¤¤ŻĘ¤ÖŔÉ®× %1ˇC˝Đ­×Ąż°ÝĂDˇA©Î­«·s¨ú±oµ{¦ˇŞş·s˝ĆĄ»ˇC -SetupFileCorrupt=¦w¸Ëµ{¦ˇŔɮפw·l·´ˇC˝Đ­«·s¨ú±o¸Óµ{¦ˇŞş˝ĆĄ»ˇC -SetupFileCorruptOrWrongVer=¦w¸Ëµ{¦ˇŔɮפw·l·´ˇA©Î¤Ł¬Ű®e©ó»P¦ąŞ©Şş¦w¸Ëµ{¦ˇˇC˝Đ­×Ąż°ÝĂDˇA©Î­«·s¨ú±oµ{¦ˇŞş·s˝ĆĄ»ˇC -InvalidParameter=¦b©RĄO¦C¤W¶Ç»Ľ¤FµL®ÄŞş°ŃĽĆ:%n%n%1 -SetupAlreadyRunning=¦w¸Ëµ{¦ˇ¤w¦b°ő¦ć¤¤ˇC -WindowsVersionNotSupported=¦ąµ{¦ˇ¤Ł¤ä´©ąq¸Ł©Ň°ő¦ćŞş Windows Ş©Ą»ˇC -WindowsServicePackRequired=¦ąµ{¦ˇ»Ý­n %1 Service Pack %2 ©Î§ó·sŞ©Ą»ˇC -NotOnThisPlatform=¦ąµ{¦ˇ¤Ł·|¦b %1 ¤W°ő¦ćˇC -OnlyOnThisPlatform=¦ąµ{¦ˇĄ˛¶·¦b %1 ¤W°ő¦ćˇC -OnlyOnTheseArchitectures=¦ąµ{¦ˇĄuĄi¦w¸Ë¦b±M¬°¤U¦CłB˛zľą¬[şcł]­pŞş Windows Ş©Ą»¤W:%n%n%1 -MissingWOW64APIs=±z°ő¦ćŞş Windows Ş©Ą»¤Ł§t¦w¸Ëµ{¦ˇ°ő¦ć 64 ¦ě¤¸¦w¸Ë©Ň»ÝŞşĄ\ŻŕˇC­Y­n­×Ąż¦ą°ÝĂDˇA˝Đ¦w¸Ë Service Pack %1ˇC -WinVersionTooLowError=¦ąµ{¦ˇ»Ý­n %1 Ş© %2 ©Î§ó·sŞ©Ą»ˇC -WinVersionTooHighError=¦ąµ{¦ˇµLŞk¦w¸Ë¦b %1 Ş© %2 ©Î§ó·sŞ©Ą»¤WˇC -AdminPrivilegesRequired=¦w¸Ë¦ąµ{¦ˇ®ÉˇAĄ˛¶·ĄH¨t˛ÎşŢ˛z­ű¨­¤Ŕµn¤JˇC -PowerUserPrivilegesRequired=·í±z¦w¸Ë¦ąµ{¦ˇ®ÉˇAĄ˛¶·ĄH¨t˛ÎşŢ˛z­ű©Î Power Users ¸s˛ŐŞş¦¨­ű¨­¤Ŕµn¤JˇC -SetupAppRunningError=¦w¸Ë®É°»´ú¨ě %1 ĄŘ«eĄż¦b°ő¦ć¤¤ˇC%n%n˝ĐĄß§YĂöł¬¨ä©Ň¦ł°ő¦ć­ÓĹéˇC­Y­nÄ~ÄňˇA˝Đ«ö¤@¤U [˝T©w]; ­Y­nµ˛§ôˇA˝Đ«ö¤@¤U [¨ú®ř]ˇC -UninstallAppRunningError=¸Ń°Ł¦w¸Ë®É°»´ú¨ě %1 ĄŘ«eĄż¦b°ő¦ć¤¤ˇC%n%n˝ĐĄß§YĂöł¬¨ä©Ň¦ł°ő¦ć­ÓĹéˇC­Y­nÄ~ÄňˇA˝Đ«ö¤@¤U [˝T©w]; ­Y­nµ˛§ôˇA˝Đ«ö¤@¤U [¨ú®ř]ˇC +LastErrorMessage=%1%n%n錯誤 %2: %3 +SetupFileMissing=安裝資料夾中éşĺ¤±ćŞ”ćˇ %1。請修正此問題ć–重新取得此軟體。 +SetupFileCorrupt=安裝檔ćˇĺ·˛ç¶“ćŤćŻ€ă€‚č«‹é‡Ťć–°ĺŹ–ĺľ—ć­¤č»źé«”ă€‚ +SetupFileCorruptOrWrongVer=安裝檔ćˇĺ·˛ç¶“ćŤćŻ€ďĽŚć–č‡ĺ®‰čŁťç¨‹ĺĽŹçš„ç‰ćś¬ä¸Ťç¬¦ă€‚請重新取得此軟體。 +InvalidParameter=ćźĺ€‹ç„ˇć•的變量已被傳éžĺ°äş†ĺ‘˝ä»¤ĺ—:%n%n%1 +SetupAlreadyRunning=安裝程式已經在執行。 +WindowsVersionNotSupported=本安裝程式並不支援目前在電腦所é‹čˇŚçš„ Windows ç‰ćś¬ă€‚ +WindowsServicePackRequired=ćś¬ĺ®‰čŁťç¨‹ĺĽŹéś€č¦ %1 Service Pack %2 ć–更新。 +NotOnThisPlatform=這個程式無法在 %1 執行。 +OnlyOnThisPlatform=這個程式必é ĺś¨ %1 執行。 +OnlyOnTheseArchitectures=這個程式只č˝ĺś¨ĺ°é–€ç‚şä»Ąä¸‹č™•ç†ĺ™¨ćž¶ć§‹č€Śč¨­č¨çš„ Windows 上安裝:%n%n%1 +WinVersionTooLowError=這個程式必é ĺś¨ %1 ç‰ćś¬ %2 ć–以上的系統執行。 +WinVersionTooHighError=這個程式無法安裝在 %1 ç‰ćś¬ %2 ć–以上的系統。 +AdminPrivilegesRequired=您必é ç™»ĺ…Ąć系統管ç†ĺ“ˇä»Ąĺ®‰čŁťé€™ĺ€‹ç¨‹ĺĽŹă€‚ +PowerUserPrivilegesRequired=您必é ç™»ĺ…Ąć具有系統管ç†ĺ“ˇć– Power User 權é™çš„使用者以安裝這個程式。 +SetupAppRunningError=安裝程式ĺµć¸¬ĺ° %1 正在執行。%n%n請關閉該程式後按 [確定] 繼續,ć–按 [取ć¶] 離開。 +UninstallAppRunningError=解除安裝程式ĺµć¸¬ĺ° %1 正在執行。%n%n請關閉該程式後按 [確定] 繼續,ć–按 [取ć¶] 離開。 + +; *** Startup questions +PrivilegesRequiredOverrideTitle=é¸ć“‡ĺ®‰čŁťç¨‹ĺĽŹĺ®‰čŁťć¨ˇĺĽŹ +PrivilegesRequiredOverrideInstruction=é¸ć“‡ĺ®‰čŁťć¨ˇĺĽŹ +PrivilegesRequiredOverrideText1=可以為所有使用者安裝 %1 (需č¦çł»çµ±ç®ˇç†ć¬Šé™),ć–ćŻĺ…為您安裝。 +PrivilegesRequiredOverrideText2=可以ĺ…為您安裝 %1,ć–ćŻç‚şć‰€ćś‰ä˝żç”¨č€…安裝 (需č¦çł»çµ±ç®ˇç†ć¬Šé™)。 +PrivilegesRequiredOverrideAllUsers=為所有使用者安裝 (&A) +PrivilegesRequiredOverrideAllUsersRecommended=為所有使用者安裝 (建議é¸é …) (&A) +PrivilegesRequiredOverrideCurrentUser=ĺ…為ć‘安裝 (&M) +PrivilegesRequiredOverrideCurrentUserRecommended=ĺ…為ć‘安裝 (建議é¸é …) (&M) + ; *** Misc. errors -ErrorCreatingDir=¦w¸Ëµ{¦ˇµLŞk«ŘĄßĄŘżý "%1" -ErrorTooManyFilesInDir=¦]¬°ĄŘżý "%1" Ą]§t¤Ó¦hŔɮסA©ŇĄHµLŞk¦b¨ä¤¤«ŘĄßŔÉ®× +ErrorCreatingDir=安裝程式無法建立資料夾“%1”。 +ErrorTooManyFilesInDir=無法在資料夾“%1”內建立檔ćˇďĽŚĺ› ç‚şčł‡ć–™ĺ¤ľĺ…§ćś‰ĺ¤Şĺ¤šçš„檔ćˇă€‚ + ; *** Setup common messages -ExitSetupTitle=µ˛§ô¦w¸Ë -ExitSetupMessage=¦w¸ËĄĽ§ą¦¨ˇC­YĄß§Yµ˛§ôˇA±N¤Ł·|¦w¸Ëµ{¦ˇˇC%n%n±zĄiĄHµy«á¦A°ő¦ć¦w¸Ëµ{¦ˇ¨Ó§ą¦¨¦w¸ËˇC%n%n­nµ˛§ô¦w¸Ë¶Ü? -AboutSetupMenuItem=Ăö©ó¦w¸Ëµ{¦ˇ(&A)... -AboutSetupTitle=Ăö©ó¦w¸Ëµ{¦ˇ -AboutSetupMessage=%1 Ş© %2%n%3%n%n%1 ­ş­¶:%n%4 +ExitSetupTitle=çµćťźĺ®‰čŁťç¨‹ĺĽŹ +ExitSetupMessage=安裝尚未完ć。如果您現在çµćťźĺ®‰čŁťç¨‹ĺĽŹďĽŚé€™ĺ€‹ç¨‹ĺĽŹĺ°‡ä¸Ťćśč˘«ĺ®‰čŁťă€‚%n%n您可以稍後再執行安裝程式以完ć安裝程序。您現在č¦çµćťźĺ®‰čŁťç¨‹ĺĽŹĺ—Ž? +AboutSetupMenuItem=關於安裝程式(&A)... +AboutSetupTitle=關於安裝程式 +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=˝T©w -ButtonCancel=¨ú®ř -ButtonYes=¬O(&Y) -ButtonYesToAll=Ąţłˇ¬Ň¬O(&A) -ButtonNo=§_(&N) -ButtonNoToAll=Ąţłˇ¬Ň§_(&O) -ButtonFinish=§ą¦¨(&F) -ButtonBrowse=ÂsÄý(&B)... -ButtonWizardBrowse=ÂsÄý(&R)... -ButtonNewFolder=«ŘĄß·s¸ę®Ć§¨(&M) +ButtonBack=< 上一步(&B) +ButtonInstall=安裝(&I) +ButtonNext=下一步(&N) > +ButtonOK=確定 +ButtonCancel=ĺŹ–ć¶ +ButtonYes=ćŻ(&Y) +ButtonYesToAll=ĺ…¨é¨çš†ćŻ(&A) +ButtonNo=ĺ¦(&N) +ButtonNoToAll=ĺ…¨é¨çš†ĺ¦(&O) +ButtonFinish=完ć(&F) +ButtonBrowse=瀏覽(&B)... +ButtonWizardBrowse=瀏覽(&R)... +ButtonNewFolder=建立新資料夾(&M) + ; *** "Select Language" dialog messages -SelectLanguageTitle=żď¨ú¦w¸Ëµ{¦ˇ»y¨Ą -SelectLanguageLabel=żď¨ú¦w¸Ë´Á¶ˇ©Ň­n¨ĎĄÎŞş»y¨Ą: +SelectLanguageTitle=é¸ć“‡ĺ®‰čŁťčŞžč¨€ +SelectLanguageLabel=é¸ć“‡ĺś¨ĺ®‰čŁťéŽç¨‹ä¸­ä˝żç”¨çš„語言: + ; *** Common wizard text -ClickNext=­Y­nÄ~ÄňˇA˝Đ«ö¤@¤U [¤U¤@¨B]; ­Y­nµ˛§ô¦w¸ËˇA˝Đ«ö¤@¤U [¨ú®ř]ˇC +ClickNext=按 [下一步] 繼續安裝,ć–按 [取ć¶] çµćťźĺ®‰čŁťç¨‹ĺĽŹă€‚ BeveledLabel= -BrowseDialogTitle=ÂsÄý¸ę®Ć§¨ -BrowseDialogLabel=˝Đ±q¤U¦C˛Młć¤¤żď¨ú¸ę®Ć§¨ˇAµM«á«ö¤@¤U [˝T©w]ˇC -NewFolderName=·sĽW¸ę®Ć§¨ +BrowseDialogTitle=瀏覽資料夾 +BrowseDialogLabel=在下面的資料夾ĺ—表中é¸ć“‡ä¸€ĺ€‹čł‡ć–™ĺ¤ľďĽŚç„¶ĺľŚćډ [確定]。 +NewFolderName=新資料夾 + ; *** "Welcome" wizard page -WelcomeLabel1=ĹwŞď¨ĎĄÎ [name] ¦w¸ËşëĆF -WelcomeLabel2=ło·|¦b±zŞşąq¸Ł¤W¦w¸Ë [name/ver]ˇC%n%n«ŘÄł±zĄýĂöł¬©Ň¦ł¨äĄLŔłĄÎµ{¦ˇˇAµM«á¦AÄ~ÄňˇC +WelcomeLabel1=歡迎使用 [name] 安裝程式 +WelcomeLabel2=這個安裝程式將ćśĺ®‰čŁť [name/ver] ĺ°ć‚¨çš„電腦。%n%nć‘們強ç建議您在安裝éŽç¨‹ä¸­é—śé–‰ĺ…¶ĺ®çš„應用程式,以éżĺ…Ťč‡ĺ®‰čŁťç¨‹ĺĽŹç™Ľç”źć˛–çŞă€‚ + ; *** "Password" wizard page -WizardPassword=±K˝X -PasswordLabel1=¦ą¦w¸Ë¨ü±K˝X«OĹ@ˇC -PasswordLabel3=˝Đ´Ł¨Ń±K˝XˇAµM«á«ö¤@¤U [¤U¤@¨B] ĄHÄ~ÄňˇC±K˝X°Ď¤Ŕ¤j¤pĽgˇC -PasswordEditLabel=±K˝X(&P): -IncorrectPassword=żé¤JŞş±K˝X¤ŁĄż˝TˇC˝Đ¦A¸Ő¤@¦¸ˇC +WizardPassword=密碼 +PasswordLabel1=這個安裝程式具有密碼保護。 +PasswordLabel3=請輸入密碼,然後按 [下一步] 繼續。密碼ćŻĺŤ€ĺ†ĺ¤§ĺ°ŹĺŻ«çš„ă€‚ +PasswordEditLabel=密碼(&P): +IncorrectPassword=您輸入的密碼不正確,請重新輸入。 + ; *** "License Agreement" wizard page -WizardLicense=±ÂĹv¦X¬ů -LicenseLabel=˝ĐĄýľ\ĹŞ¤U¦C­«­n¸ę°T¦AÄ~ÄňˇC -LicenseLabel3=˝Đľ\ĹŞ¤U¦C±ÂĹv¦X¬ůˇC±zĄ˛¶·±µ¨ü¦ą¦X¬ů±ř´ÚˇA¤~ŻŕÄ~Äň¦w¸ËˇC -LicenseAccepted=§Ú±µ¨ü¦X¬ů(&A) -LicenseNotAccepted=§Ú¤Ł±µ¨ü¦X¬ů(&D) +WizardLicense=ćŽć¬Šĺç´„ +LicenseLabel=請閱讀以下ćŽć¬Šĺ約。 +LicenseLabel3=請閱讀以下ćŽć¬Šĺ約,您必é ćŽĄĺŹ—ĺç´„çš„ĺ„項條款才č˝çąĽçşŚĺ®‰čŁťă€‚ +LicenseAccepted=ć‘ĺŚć„Ź(&A) +LicenseNotAccepted=ć‘不ĺŚć„Ź(&D) + ; *** "Information" wizard pages -WizardInfoBefore=¸ę°T -InfoBeforeLabel=˝ĐĄýľ\ĹŞ¤U¦C­«­n¸ę°T¦AÄ~ÄňˇC -InfoBeforeClickLabel=·í±z·ÇłĆ¦n­nÄ~Äň¦w¸Ë®ÉˇA˝Đ«ö¤@¤U [¤U¤@¨B]ˇC -WizardInfoAfter=¸ę°T -InfoAfterLabel=˝ĐĄýľ\ĹŞ¤U¦C­«­n¸ę°T¦AÄ~ÄňˇC -InfoAfterClickLabel=·í±z·ÇłĆ¦n­nÄ~Äň¦w¸Ë®ÉˇA˝Đ«ö¤@¤U [¤U¤@¨B]ˇC +WizardInfoBefore=č¨ŠćŻ +InfoBeforeLabel=在繼續安裝之前請閱讀以下重č¦čł‡č¨Šă€‚ +InfoBeforeClickLabel=當您準備好繼續安裝,請按 [下一步]。 +WizardInfoAfter=č¨ŠćŻ +InfoAfterLabel=在繼續安裝之前請閱讀以下重č¦čł‡č¨Šă€‚ +InfoAfterClickLabel=當您準備好繼續安裝,請按 [下一步]。 + ; *** "User Information" wizard page -WizardUserInfo=¨ĎĄÎŞĚ¸ę°T -UserInfoDesc=˝Đżé¤J±zŞş¸ę°TˇC -UserInfoName=¨ĎĄÎŞĚ¦WşŮ(&U): -UserInfoOrg=˛Ő´(&O): -UserInfoSerial=§Ç¸ą(&S): -UserInfoNameRequired=Ą˛¶·żé¤J¦WşŮˇC +WizardUserInfo=使用者資訊 +UserInfoDesc=請輸入您的資料。 +UserInfoName=使用者ĺŤç¨±(&U): +UserInfoOrg=組織(&O): +UserInfoSerial=序號(&S): +UserInfoNameRequired=您必é čĽ¸ĺ…Ąć‚¨çš„ĺŤç¨±ă€‚ + ; *** "Select Destination Location" wizard page -WizardSelectDir=żď¨úĄŘŞş¦a¦ě¸m -SelectDirDesc=Ŕł±N [name] ¦w¸Ë¦b¦ółB? -SelectDirLabel3=¦w¸Ëµ{¦ˇ·|±N [name] ¦w¸Ë¦b¤U¦C¸ę®Ć§¨¤¤ˇC -SelectDirBrowseLabel=­Y­nÄ~ÄňˇA˝Đ«ö¤@¤U [¤U¤@¨B]ˇC­Y±z·Qżď¨ú¤Ł¦PŞş¸ę®Ć§¨ˇA˝Đ«ö¤@¤U [ÂsÄý]ˇC -DiskSpaceMBLabel=¦Ü¤Ö¶·¦ł [mb] MB ŞşĄiĄÎşĎşĐŞĹ¶ˇˇC -CannotInstallToNetworkDrive=¦w¸Ëµ{¦ˇµLŞk¦w¸Ë¨ěşô¸ôşĎşĐľ÷ˇC -CannotInstallToUNCPath=¦w¸Ëµ{¦ˇµLŞk¦w¸Ë¨ě UNC ¸ô®|ˇC -InvalidPath=Ą˛¶·żé¤JĄ]§tşĎşĐľ÷ĄN¸ąŞş§ąľă¸ô®|ˇA¨Ň¦p:%n%nC:\APP%n%n©Îżé¤J¤U¦C®ć¦ˇŞş UNC ¸ô®|:%n%n\\¦řŞAľą\¦@ĄÎ -InvalidDrive=żď¨úŞşşĎşĐľ÷©Î UNC ¦@ĄÎ¤Ł¦s¦b©ÎµLŞk¦s¨úˇC˝Đżď¨ú¨äĄLşĎşĐľ÷©Î UNC ¦@ĄÎˇC -DiskSpaceWarningTitle=şĎşĐŞĹ¶ˇ¤Ł¨¬ -DiskSpaceWarning=¦w¸Ëµ{¦ˇ¦Ü¤Ö»Ý­n %1 KB ŞşĄiĄÎŞĹ¶ˇ¤~Żŕ¦w¸ËˇA¦ý©ŇżďşĎşĐľ÷ŞşĄiĄÎŞĹ¶ˇĄu¦ł %2 KBˇC%n%n¤´­nÄ~Äň¶Ü? -DirNameTooLong=¸ę®Ć§¨¦WşŮ©Î¸ô®|ąLŞřˇC -InvalidDirName=¦ą¸ę®Ć§¨¦WşŮµL®ÄˇC -BadDirName32=¸ę®Ć§¨¦WşŮ¤Ł±oĄ]§t¤U¦CĄô¤@¦r¤¸:%n%n%1 -DirExistsTitle=¸ę®Ć§¨¤w¦s¦b -DirExists=¤w¦ł¸ę®Ć§¨ %n%n%1%n%nˇC¤´­n¦w¸Ë¨ě¸Ó¸ę®Ć§¨¶Ü? -DirDoesntExistTitle=¸ę®Ć§¨¤Ł¦s¦b -DirDoesntExist=¸ę®Ć§¨ %n%n%1%n%n ¤Ł¦s¦bˇC­n«ŘĄß¸Ó¸ę®Ć§¨¶Ü? +WizardSelectDir=é¸ć“‡ç›®çš„資料夾 +SelectDirDesc=é¸ć“‡ĺ®‰čŁťç¨‹ĺĽŹĺ®‰čŁť [name] 的位置。 +SelectDirLabel3=安裝程式將ćśćŠŠ [name] 安裝ĺ°ä¸‹éť˘çš„資料夾。 +SelectDirBrowseLabel=按 [下一步] 繼續,如果您ćłé¸ć“‡ĺŹ¦ä¸€ĺ€‹čł‡ć–™ĺ¤ľďĽŚč«‹ćŚ‰ [瀏覽]。 +DiskSpaceMBLabel=ćś€ĺ°‘éś€č¦ [mb] MB çŁç˘źç©şé–“。 +CannotInstallToNetworkDrive=安裝程式無法安裝於網絡çŁç˘źć©źă€‚ +CannotInstallToUNCPath=安裝程式無法安裝於 UNC 路徑。 +InvalidPath=您必é čĽ¸ĺ…Ąĺ®Ść•´çš„č·Żĺľ‘ĺŤç¨±ĺŹŠçŁç˘źć©źä»Łç˘Ľă€‚%n%n例如 C:\App ć– UNC 路徑格式 \\伺服器\共用資料夾。 +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=Ŕł¦w¸Ë­ţ¨Ç¤¸Ąó? -SelectComponentsLabel2=żď¨ú±z­n¦w¸ËŞş¤¸Ąó; ˛M°Ł±z¤Ł­n¦w¸ËŞş¤¸ĄóˇC·í±z·ÇłĆ¦n­nÄ~Äň®ÉˇA˝Đ«ö¤@¤U [¤U¤@¨B]ˇC -FullInstallation=§ąľă¦w¸Ë +WizardSelectComponents=é¸ć“‡ĺ…ä»¶ +SelectComponentsDesc=é¸ć“‡ĺ°‡ćśč˘«ĺ®‰čŁťçš„ĺ…件。 +SelectComponentsLabel2=é¸ć“‡ć‚¨ćłč¦ĺ®‰čŁťçš„ĺ…件;清除您不ćłĺ®‰čŁťçš„ĺ…件。然後按 [下一步] 繼續安裝。 +FullInstallation=完整安裝 ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) -CompactInstallation=şë²¦w¸Ë -CustomInstallation=¦Ű­q¦w¸Ë -NoUninstallWarningTitle=¤w¦ł¦ą¤¸Ąó -NoUninstallWarning=¦w¸Ëµ{¦ˇ°»´ú¨ě±zŞşąq¸Ł¤w¦w¸Ë¤F¤U¦C¤¸Ąó:%n%n%1%n%n±Nło¨Ç¤¸Ąó¨ú®řżď¨ú¨Ă¤Ł·|¨Ď¤¸Ąó¸Ń°Ł¦w¸ËˇC%n%n¤´­nÄ~Äň¶Ü? +CompactInstallation=最小安裝 +CustomInstallation=自訂安裝 +NoUninstallWarningTitle=ĺ…ä»¶ĺ·˛ĺ­ĺś¨ +NoUninstallWarning=安裝程式ĺµć¸¬ĺ°ä»Ąä¸‹ĺ…件已經安裝在您的電腦上:%n%n%1%n%n取ć¶é¸ć“‡é€™äş›ĺ…件將不ćśç§»é™¤ĺ®ĺ€‘。%n%n您仍然č¦çąĽçşŚĺ—Ž? ComponentSize1=%1 KB ComponentSize2=%1 MB -ComponentsDiskSpaceMBLabel=ĄŘ«eŞşżďľÜ¦Ü¤Ö»Ý­n [mb] MB ŞşşĎşĐŞĹ¶ˇˇC +ComponentsDiskSpaceMBLabel=目前的é¸ć“‡éś€č¦č‡łĺ°‘ [mb] MB çŁç˘źç©şé–“。 + ; *** "Select Additional Tasks" wizard page -WizardSelectTasks=żď¨ú¨äĄL¤u§@ -SelectTasksDesc=Áٶ·°ő¦ć­ţ¨Ç¨äĄL¤u§@? -SelectTasksLabel2=˝Đżď¨ú¦w¸Ëµ{¦ˇ¦b¦w¸Ë [name] ®ÉˇA¶·ĂBĄ~°ő¦ćŞş¨äĄL¤u§@ˇAµM«á«ö¤@¤U [¤U¤@¨B]ˇC +WizardSelectTasks=é¸ć“‡é™„加的工作 +SelectTasksDesc=é¸ć“‡č¦ĺź·čˇŚçš„附加工作。 +SelectTasksLabel2=é¸ć“‡ĺ®‰čŁťç¨‹ĺĽŹĺś¨ĺ®‰čŁť [name] 時č¦ĺź·čˇŚçš„附加工作,然後按 [下一步]。 + ; *** "Select Start Menu Folder" wizard page -WizardSelectProgramGroup=żď¨ú [¶}©l] Ą\ŻŕŞí¸ę®Ć§¨ -SelectStartMenuFolderDesc=¦w¸Ëµ{¦ˇŔł±Nµ{¦ˇ±¶®|¸m©ó¦ółB? -SelectStartMenuFolderLabel3=¦w¸Ëµ{¦ˇ±N¦b¤U¦C [¶}©l] Ą\ŻŕŞí¸ę®Ć§¨¤¤«ŘĄßµ{¦ˇ±¶®|ˇC -SelectStartMenuFolderBrowseLabel=­Y­nÄ~ÄňˇA˝Đ«ö¤@¤U [¤U¤@¨B]ˇC­Y±z·Qżď¨ú¤Ł¦PŞş¸ę®Ć§¨ˇA˝Đ«ö¤@¤U [ÂsÄý]ˇC -MustEnterGroupName=Ą˛¶·żé¤J¸ę®Ć§¨¦WşŮˇC -GroupNameTooLong=¸ę®Ć§¨¦WşŮ©Î¸ô®|ąLŞřˇC -InvalidGroupName=¦ą¸ę®Ć§¨¦WşŮµL®ÄˇC -BadGroupName=¸ę®Ć§¨¦WşŮ¤Ł±oĄ]§t¤U¦CĄô¤@¦r¤¸:%n%n%1 -NoProgramGroupCheck2=¤Ł­n«ŘĄß [¶}©l] Ą\ŻŕŞí¸ę®Ć§¨(&D) +WizardSelectProgramGroup=é¸ć“‡ă€Śé–‹ĺ§‹ă€ŤĺŠźč˝čˇ¨çš„資料夾 +SelectStartMenuFolderDesc=é¸ć“‡ĺ®‰čŁťç¨‹ĺĽŹĺ»şç«‹ç¨‹ĺĽŹçš„ćŤ·ĺľ‘çš„ä˝Ťç˝®ă€‚ +SelectStartMenuFolderLabel3=安裝程式將ćśćŠŠç¨‹ĺĽŹçš„ćŤ·ĺľ‘ĺ»şç«‹ĺś¨ä¸‹éť˘çš„ă€Śé–‹ĺ§‹ă€ŤĺŠźč˝čˇ¨čł‡ć–™ĺ¤ľă€‚ +SelectStartMenuFolderBrowseLabel=按 [下一步] 繼續,如果您ćłé¸ć“‡ĺŹ¦ä¸€ĺ€‹čł‡ć–™ĺ¤ľďĽŚč«‹ćŚ‰ [瀏覽]。 +MustEnterGroupName=您必é čĽ¸ĺ…Ąä¸€ĺ€‹čł‡ć–™ĺ¤ľçš„ĺŤç¨±ă€‚ +GroupNameTooLong=資料夾ĺŤç¨±ć–路徑太長。 +InvalidGroupName=資料夾ĺŤç¨±ä¸Ťć­Łç˘şă€‚ +BadGroupName=資料夾ĺŤç¨±ä¸Ťĺľ—包ĺ«ä¸‹ĺ—ĺ­—ĺ…:%n%n%1 +NoProgramGroupCheck2=不č¦ĺś¨ă€Śé–‹ĺ§‹ă€ŤĺŠźč˝čˇ¨ä¸­ĺ»şç«‹čł‡ć–™ĺ¤ľ(&D) + ; *** "Ready to Install" wizard page -WizardReady=¤wĄi¶}©l¦w¸Ë -ReadyLabel1=¦w¸Ëµ{¦ˇ˛{¦b¤wĄi¶}©l±N [name] ¦w¸Ë¨ě±zŞşąq¸Ł¤WˇC -ReadyLabel2a=­Y­nÄ~Äň¦w¸ËˇA˝Đ«ö¤@¤U [¦w¸Ë]; ­Y­nŔËľ\©ÎĹܧóĄô¦ół]©wˇA˝Đ«ö¤@¤U [¤W¤@¨B]ˇC -ReadyLabel2b=­Y­nÄ~Äň¦w¸ËˇA˝Đ«ö¤@¤U [¦w¸Ë]ˇC -ReadyMemoUserInfo=¨ĎĄÎŞĚ¸ę°T: -ReadyMemoDir=ĄŘŞş¦a¦ě¸m: -ReadyMemoType=¦w¸ËĂţ«¬: -ReadyMemoComponents=żď¨úŞş¤¸Ąó: -ReadyMemoGroup=[¶}©l] Ą\ŻŕŞí¸ę®Ć§¨: -ReadyMemoTasks=¨äĄL¤u§@: +WizardReady=準備安裝 +ReadyLabel1=安裝程式將開始安裝 [name] ĺ°ć‚¨çš„電腦中。 +ReadyLabel2a=按下 [安裝] 繼續安裝,ć–按 [上一步] 重新檢視ć–設定ĺ„é¸é …的內容。 +ReadyLabel2b=按下 [安裝] 繼續安裝。 +ReadyMemoUserInfo=使用者資訊 +ReadyMemoDir=目的資料夾: +ReadyMemoType=安裝型態: +ReadyMemoComponents=é¸ć“‡çš„ĺ…ä»¶: +ReadyMemoGroup=「開始」功č˝čˇ¨čł‡ć–™ĺ¤ľ: +ReadyMemoTasks=附加工作: + ; *** "Preparing to Install" wizard page -WizardPreparing=Ąż¦b·ÇłĆ¦w¸Ë -PreparingDesc=¦w¸Ëµ{¦ˇĄż¦b·ÇłĆ±N [name] ¦w¸Ë¨ě±zŞşąq¸Ł¤WˇC -PreviousInstallNotCompleted=¤W¤@­Óµ{¦ˇŞş¦w¸Ë/˛ľ°Ł©|ĄĽ§ą¦¨ˇCĄ˛¶·­«·s±Ň°Ęąq¸ŁˇA¤~Żŕ§ą¦¨¸Ó¦w¸ËˇC%n%n˝Đ¦b­«·s±Ň°Ęąq¸Ł¤§«áˇA­«·s°ő¦ć¦w¸Ëµ{¦ˇˇAĄH§ą¦¨ [name] Şş¦w¸ËˇC -CannotContinue=¦w¸Ëµ{¦ˇµLŞkÄ~ÄňˇC˝Đ«ö¤@¤U [¨ú®ř] ĄHµ˛§ôˇC -ApplicationsFound=¦w¸Ëµ{¦ˇĄ˛¶·§ó·s¤U¦CŔłĄÎµ{¦ˇĄż¦b¨ĎĄÎŞş¤@¨ÇŔɮסC«ŘÄł±z¤ął\¦w¸Ëµ{¦ˇ¦Ű°ĘĂöł¬ło¨ÇŔłĄÎµ{¦ˇˇC -ApplicationsFound2=¦w¸Ëµ{¦ˇĄ˛¶·§ó·s¤U¦CŔłĄÎµ{¦ˇĄż¦b¨ĎĄÎŞş¤@¨ÇŔɮסC«ŘÄł±z¤ął\¦w¸Ëµ{¦ˇ¦Ű°ĘĂöł¬ło¨ÇŔłĄÎµ{¦ˇˇC·í¦w¸Ë§ą¦¨¤§«áˇA¦w¸Ëµ{¦ˇ±N·|ąÁ¸Ő­«·s±Ň°Ęło¨ÇŔłĄÎµ{¦ˇˇC -CloseApplications=¦Ű°ĘĂöł¬ŔłĄÎµ{¦ˇ(&A) -DontCloseApplications=¤Ł­nĂöł¬ŔłĄÎµ{¦ˇ(&D) -ErrorCloseApplications=¦w¸Ëµ{¦ˇµLŞk¦Ű°ĘĂöł¬©Ň¦łŔłĄÎµ{¦ˇˇC«ŘÄł±zĂöł¬©Ň¦łĄż¦b¨ĎĄÎ¦w¸Ëµ{¦ˇĄ˛¶·§ó·s¤§ŔɮתşŔłĄÎµ{¦ˇˇAµM«á¦AÄ~ÄňˇC +WizardPreparing=準備安裝程式 +PreparingDesc=安裝程式準備將 [name] 安裝ĺ°ć‚¨çš„電腦上。 +PreviousInstallNotCompleted=ĺ…前的安裝/ 解除安裝尚未完ć,您必é é‡Ťć–°ĺ•źĺ‹•電腦以完ć該安裝。%n%n在重新啟動電腦之後,請再執行這個程式來安裝 [name]。 +CannotContinue=安裝程式無法繼續。請按 [取ć¶] 離開。 +ApplicationsFound=下面的應用程式正在使用安裝程式所需č¦ć›´ć–°çš„文檔。建議您ĺ…許安裝程式自動關閉這些應用程式。 +ApplicationsFound2=下面的應用程式正在使用安裝程式所需č¦ć›´ć–°çš„文檔。建議您ĺ…許安裝程式自動關閉這些應用程式。當安裝éŽç¨‹çµćťźĺľŚďĽŚćś¬ĺ®‰čŁťç¨‹ĺĽŹĺ°‡ćśĺ—試重新開啟該應用程式。 +CloseApplications=關閉應用程式(&A) +DontCloseApplications=不č¦é—śé–‰ć‡‰ç”¨ç¨‹ĺĽŹ (&D) +ErrorCloseApplications=安裝程式無法自動關閉所有應用程式。建議您在繼續前ĺ…關閉所有應用程式使用的檔ćˇă€‚ + ; *** "Installing" wizard page -WizardInstalling=¦w¸Ë¤¤ -InstallingLabel=˝Đµy­ÔˇA¦w¸Ëµ{¦ˇĄż¦b±N [name] ¦w¸Ë¨ě±zŞşąq¸Ł¤WˇC +WizardInstalling=正在安裝 +InstallingLabel=請稍候,安裝程式正在將 [name] 安裝ĺ°ć‚¨çš„電腦上 + ; *** "Setup Completed" wizard page -FinishedHeadingLabel=Ąż¦b§ą¦¨ [name] ¦w¸ËşëĆF -FinishedLabelNoIcons=¦w¸Ëµ{¦ˇ¤w§ą¦¨±ząq¸Ł¤W [name] Şş¦w¸ËˇC -FinishedLabel=¦w¸Ëµ{¦ˇ¤w§ą¦¨±ząq¸Ł¤W [name] Şş¦w¸ËˇC±zĄiĄHżď¨ú©Ň¦w¸ËŞş±¶®|¨Ó±Ň°ĘŔłĄÎµ{¦ˇˇC -ClickFinish=˝Đ«ö¤@¤U [§ą¦¨]ˇAĄHµ˛§ô¦w¸ËˇC -FinishedRestartLabel=¦w¸Ëµ{¦ˇĄ˛¶·­«·s±Ň°Ę±zŞşąq¸ŁˇA¤~Żŕ§ą¦¨ [name] Şş¦w¸ËˇC­nĄß§Y­«·s±Ň°Ę¶Ü? -FinishedRestartMessage=¦w¸Ëµ{¦ˇĄ˛¶·­«·s±Ň°Ę±zŞşąq¸ŁˇA¤~Żŕ§ą¦¨ [name] Şş¦w¸ËˇC%n%n­nĄß§Y­«·s±Ň°Ę¶Ü? -ShowReadmeCheck=¬OˇA§Ú­nŔ˵řĹŞ§ÚŔÉ®× -YesRadio=¬OˇAĄß§Y­«·s±Ň°Ęąq¸Ł(&Y) -NoRadio=§_ˇAµy­Ô¦A­«·s±Ň°Ęąq¸Ł(&N) +FinishedHeadingLabel=安裝完ć +FinishedLabelNoIcons=安裝程式已經將 [name] 安裝在您的電腦上。 +FinishedLabel=安裝程式已經將 [name] 安裝在您的電腦中,您可以é¸ć“‡ç¨‹ĺĽŹçš„圖示來執行該應用程式。 +ClickFinish=按 [完ć] 以çµćťźĺ®‰čŁťç¨‹ĺĽŹă€‚ +FinishedRestartLabel=č¦ĺ®Ść [name] 的安裝,安裝程式必é é‡Ťć–°ĺ•źĺ‹•您的電腦。您ćłč¦çŹľĺś¨é‡Ťć–°ĺ•źĺ‹•é›»č…¦ĺ—Ž? +FinishedRestartMessage=č¦ĺ®Ść [name] 的安裝,安裝程式必é é‡Ťć–°ĺ•źĺ‹•您的電腦。%n%n您ćłč¦çŹľĺś¨é‡Ťć–°ĺ•źĺ‹•é›»č…¦ĺ—Ž? +ShowReadmeCheck=ćŻďĽŚć‘č¦é–±č®€č®€ć‘檔ćˇă€‚ +YesRadio=ćŻďĽŚç«‹ĺŤłé‡Ťć–°ĺ•źĺ‹•電腦(&Y) +NoRadio=ĺ¦ďĽŚć‘稍後重新啟動電腦(&N) ; used for example as 'Run MyProg.exe' -RunEntryExec=°ő¦ć %1 +RunEntryExec=執行 %1 ; used for example as 'View Readme.txt' -RunEntryShellExec=Ŕ˵ř %1 -; *** "Setup Needs the Next Disk" stuff -ChangeDiskTitle=¦w¸Ëµ{¦ˇ»Ý­n¤U¤@±işĎ¤ůˇC -SelectDiskLabel2=˝Đ´ˇ¤JşĎ¤ů %1ˇAµM«á«ö¤@¤U [˝T©w]ˇC%n%n­Y¦ąşĎ¤ů¤WŞşŔɮץiĄH¦b¤U¦CĹăĄÜ¤§¸ę®Ć§¨ĄHĄ~Şş¸ę®Ć§¨¤¤§ä¨ěˇA˝Đżé¤JĄż˝TŞş¸ô®|ˇA©Î«ö¤@¤U [ÂsÄý]ˇC -PathLabel=¸ô®|(&P): -FileNotInDir2=¦b "%2" ¤¤§ä¤Ł¨ěŔÉ®× "%1"ˇC˝Đ´ˇ¤JĄż˝TŞşşĎ¤ůˇA©Îżď¨ú¨äĄL¸ę®Ć§¨ˇC -SelectDirectoryLabel=˝Đ«ü©w¤U¤@±işĎ¤ůŞş¦ě¸mˇC +RunEntryShellExec=檢視 %1 + +; *** "Setup Needs the Next Disk" +ChangeDiskTitle=安裝程式需č¦ä¸‹ä¸€ĺĽµçŁç‰‡ +SelectDiskLabel2=請插入çŁç‰‡ %1,然後按 [確定]。%n%n如果檔ćˇä¸Ťĺś¨ä»Ąä¸‹ć‰€éˇŻç¤şçš„資料夾之中,請輸入正確的資料夾ĺŤç¨±ć–按 [瀏覽] é¸ĺŹ–ă€‚ +PathLabel=č·Żĺľ‘(&P): +FileNotInDir2=檔ćˇâ€ś%1”無法在“%2”找ĺ°ă€‚請插入正確的çŁç‰‡ć–é¸ć“‡ĺ…¶ĺ®çš„資料夾。 +SelectDirectoryLabel=請指定下一張çŁç‰‡çš„位置。 + ; *** Installation phase messages -SetupAborted=¦w¸ËĄĽ¦w¦¨ˇC%n%n˝Đ­×Ąż°ÝĂDˇA¦A­«·s°ő¦ć¦w¸Ëµ{¦ˇˇC -EntryAbortRetryIgnore=­Y­n¦A¸Ő¤@¦¸ˇA˝Đ«ö¤@¤U [­«¸Ő]; ­Y­nÄ~ÄňˇA˝Đ«ö¤@¤U [©ż˛¤]; ­Y­n¨ú®ř¦w¸ËˇA˝Đ«ö¤@¤U [¤¤¤î]ˇC +SetupAborted=安裝沒有完ć。%n%n請更正問題後重新安裝一次。 +AbortRetryIgnoreSelectAction=é¸ĺŹ–ĺ‹•ä˝ś +AbortRetryIgnoreRetry=請再試一次 (&T) +AbortRetryIgnoreIgnore=略éŽéŚŻčŞ¤ä¸¦çąĽçşŚ (&I) +AbortRetryIgnoreCancel=取ć¶ĺ®‰čŁť + ; *** Installation status messages -StatusClosingApplications=Ąż¦bĂöł¬ŔłĄÎµ{¦ˇ... -StatusCreateDirs=Ąż¦b«ŘĄßĄŘżý... -StatusExtractFiles=Ąż¦b¸ŃŔŁÁYŔÉ®×... -StatusCreateIcons=Ąż¦b«ŘĄß±¶®|... -StatusCreateIniEntries=Ąż¦b«ŘĄß INI ¶µĄŘ... -StatusCreateRegistryEntries=Ąż¦b«ŘĄßµnżý¶µĄŘ... -StatusRegisterFiles=Ąż¦bµnżýŔÉ®×... -StatusSavingUninstall=Ąż¦bŔx¦s¸Ń°Ł¦w¸Ë¸ę°T... -StatusRunProgram=Ąż¦b§ą¦¨¦w¸Ë... -StatusRestartingApplications=Ąż¦b­«·s±Ň°ĘŔłĄÎµ{¦ˇ... -StatusRollback=Ąż¦b´_­ěĹܧó... +StatusClosingApplications=正在關閉應用程式... +StatusCreateDirs=正在建立資料夾... +StatusExtractFiles=正在解壓縮檔ćˇ... +StatusCreateIcons=正在建立程式集圖示... +StatusCreateIniEntries=寫入 INI 檔ćˇçš„é …ç›®... +StatusCreateRegistryEntries=正在更新系統登錄... +StatusRegisterFiles=正在登錄檔ćˇ... +StatusSavingUninstall=儲ĺ­č§Łé™¤ĺ®‰čŁťčł‡č¨Š... +StatusRunProgram=正在完ć安裝... +StatusRestartingApplications=正在重新開啟應用程式... +StatusRollback=正在復原變更... + ; *** Misc. errors -ErrorInternal2=¤şłˇżů»~: %1 -ErrorFunctionFailedNoCode=%1 Ą˘±Ń -ErrorFunctionFailed=%1 Ą˘±Ń; ĄN˝X %2 -ErrorFunctionFailedWithMessage=%1 Ą˘±Ń; ĄN˝X %2ˇC%n%3 -ErrorExecutingProgram=µLŞk°ő¦ćŔÉ®×:%n%1 +ErrorInternal2=ĺ…§é¨éŚŻčŞ¤: %1 +ErrorFunctionFailedNoCode=%1 失敗 +ErrorFunctionFailed=%1 失敗;代碼 %2 +ErrorFunctionFailedWithMessage=%1 失敗;代碼 %2.%n%3 +ErrorExecutingProgram=無法執行檔ćˇ:%n%1 + ; *** Registry errors -ErrorRegOpenKey=¶}±Ňµnżýľ÷˝X®ÉµoĄÍżů»~:%n%1\%2 -ErrorRegCreateKey=«ŘĄßµnżýľ÷˝X®ÉµoĄÍżů»~:%n%1\%2 -ErrorRegWriteKey=Ľg¤Jµnżýľ÷˝X®ÉµoĄÍżů»~:%n%1\%2 +ErrorRegOpenKey=無法開啟登錄鍵:%n%1\%2 +ErrorRegCreateKey=無法建立登錄項目:%n%1\%2 +ErrorRegWriteKey=無法變更登錄項目:%n%1\%2 + ; *** INI errors -ErrorIniEntry=¦bŔÉ®× "%1" ¤¤«ŘĄß INI ¶µĄŘ®ÉµoĄÍżů»~ˇC +ErrorIniEntry=在檔ćˇâ€ś%1”建立 INI 項目錯誤。 + ; *** File copying errors -FileAbortRetryIgnore=­Y­n¦A¸Ő¤@¦¸ˇA˝Đ«ö¤@¤U [­«¸Ő]; ­Y­n˛¤ąL¦ąŔɮסA˝Đ«ö¤@¤U [©ż˛¤] (¤Ł«ŘÄł¨ĎĄÎ); ­Y­n¨ú®ř¦w¸ËˇA˝Đ«ö¤@¤U [¤¤¤î]ˇC -FileAbortRetryIgnore2=­Y­n¦A¸Ő¤@¦¸ˇA˝Đ«ö¤@¤U [­«¸Ő]; ­Y­nÄ~ÄňˇA˝Đ«ö¤@¤U [©ż˛¤] (¤Ł«ŘÄł¨ĎĄÎ); ­Y­n¨ú®ř¦w¸ËˇA˝Đ«ö¤@¤U [¤¤¤î]ˇC -SourceIsCorrupted=­ě©lµ{¦ˇŔɤw·l·´ -SourceDoesntExist=­ě©lµ{¦ˇŔÉ "%1" ¤Ł¦s¦b -ExistingFileReadOnly=˛{¦łŔɮפwĽĐ°O¬°°ßĹŞˇC%n%n­Y­n˛ľ°Ł°ßĹŞÄݩʡAµM«á¦A¸Ő¤@¦¸ˇA˝Đ«ö¤@¤U [­«¸Ő]; ­Y­n˛¤ąL¦ąŔɮסA˝Đ«ö¤@¤U [©ż˛¤]; ­Y­n¨ú®ř¦w¸ËˇA˝Đ«ö¤@¤U [¤¤¤î]ˇC -ErrorReadingExistingDest=ąÁ¸ŐĹŞ¨ú˛{¦łŔɮ׮ɵoĄÍżů»~: -FileExists=¤w¦ł¦ąŔɮסC%n%n­nĄŃ¦w¸Ëµ{¦ˇĄ[ĄHÂĐĽg¶Ü? -ExistingFileNewer=˛{¦łŔɮ׸ű¦w¸Ëµ{¦ˇąÁ¸Ő¦w¸ËŞşŔÉ®×·sˇC«ŘÄł±z«OŻd˛{¦łŔɮסC%n%n­n«OŻd˛{¦łŞşŔÉ®×¶Ü? -ErrorChangingAttr=ąÁ¸ŐĹܧó˛{¦łŔɮתşÄݩʮɵoĄÍżů»~: -ErrorCreatingTemp=ąÁ¸Ő¦bĄŘŞş¦aĄŘżý¤¤«ŘĄßŔɮ׮ɵoĄÍżů»~: -ErrorReadingSource=ąÁ¸ŐĹŞ¨ú­ě©lµ{¦ˇŔɮɵoĄÍżů»~: -ErrorCopying=ąÁ¸Ő˝Ć»sŔɮ׮ɵoĄÍżů»~: -ErrorReplacingExistingFile=ąÁ¸Ő¨úĄN˛{¦łŔɮ׮ɵoĄÍżů»~: -ErrorRestartReplace=RestartReplace Ą˘±Ń: -ErrorRenamingTemp=ąÁ¸Ő­«·s©R¦WĄŘŞş¦aĄŘżý¤¤ŞşŔɮ׮ɵoĄÍżů»~: -ErrorRegisterServer=µLŞkµnżý DLL/OCX: %1 -ErrorRegSvr32Failed=RegSvr32 Ą˘±ŃˇAµ˛§ôĄN˝X¬° %1 -ErrorRegisterTypeLib=µLŞkµnżýĂţ«¬µ{¦ˇ®w: %1 +FileAbortRetryIgnoreSkipNotRecommended=略éŽé€™ĺ€‹ćŞ”ćˇ (不建議) (&S) +FileAbortRetryIgnoreIgnoreNotRecommended=略éŽéŚŻčŞ¤ä¸¦çąĽçşŚ (不建議) (&I) +SourceDoesntExist=來ćşćŞ”ćˇâ€ś%1”不ĺ­ĺś¨ă€‚ +SourceIsCorrupted=來ćşćŞ”ćˇĺ·˛ç¶“ćŤćŻ€ă€‚ +ExistingFileReadOnly2=無法取代現有檔ćˇďĽŚĺ› ç‚şćŞ”ćˇĺ·˛ć¨™ç¤şç‚şĺ”Żč®€ă€‚ +ExistingFileReadOnlyRetry=移除唯讀屬性並重試 (&R) +ExistingFileReadOnlyKeepExisting=äżťç•™çŹľćś‰ćŞ”ćˇ (&K) +ErrorReadingExistingDest=讀取一個已ĺ­ĺś¨çš„檔ćˇć™‚發生錯誤: +FileExists=檔ćˇĺ·˛ç¶“ĺ­ĺś¨ă€‚%n%n č¦č®“安裝程式加以覆寫嗎? +ExistingFileNewer=ĺ­ĺś¨çš„檔ćˇç‰ćś¬ćŻ”čĽć–°ďĽŚĺ»şč­°ć‚¨äżťç•™ç›®ĺ‰Ťĺ·˛ĺ­ĺś¨çš„檔ćˇă€‚%n%n您č¦äżťç•™ç›®ĺ‰Ťĺ·˛ĺ­ĺś¨çš„檔ćˇĺ—Ž? +ErrorChangingAttr=在變更檔ćˇĺ±¬ć€§ć™‚發生錯誤: +ErrorCreatingTemp=在目的資料夾中建立檔ćˇć™‚發生錯誤: +ErrorReadingSource=讀取原始檔ćˇć™‚發生錯誤: +ErrorCopying=ĺľ©ĺ¶ćŞ”ćˇć™‚發生錯誤: +ErrorReplacingExistingFile=取代檔ćˇć™‚發生錯誤: +ErrorRestartReplace=重新啟動電腦後取代檔ćˇĺ¤±ć•—: +ErrorRenamingTemp=在目的資料夾變更檔ćˇĺŤç¨±ć™‚發生錯誤: +ErrorRegisterServer=無法注冊 DLL/OCX 檔ćˇ: %1。 +ErrorRegSvr32Failed=RegSvr32 失敗;退出代碼 %1 +ErrorRegisterTypeLib=無法注冊類型庫: %1。 + +; *** Uninstall display name markings +; used for example as 'My Program (32-bit)' +UninstallDisplayNameMark=%1 (%2) +; used for example as 'My Program (32-bit, All users)' +UninstallDisplayNameMarks=%1 (%2, %3) +UninstallDisplayNameMark32Bit=32-bit +UninstallDisplayNameMark64Bit=64-bit +UninstallDisplayNameMarkAllUsers=所有使用者 +UninstallDisplayNameMarkCurrentUser=目前使用者 + ; *** Post-installation errors -ErrorOpeningReadme=ąÁ¸Ő¶}±ŇĹŞ§ÚŔɮ׮ɵoĄÍżů»~ˇC -ErrorRestartingComputer=¦w¸Ëµ{¦ˇµLŞk­«·s±Ň°Ęąq¸ŁˇC˝Đ¤â°Ę°ő¦ć¦ą§@·~ˇC +ErrorOpeningReadme=開啟讀ć‘檔ćˇć™‚發生錯誤。 +ErrorRestartingComputer=安裝程式無法重新啟動電腦,請以手動方式自行重新啟動電腦。 + ; *** Uninstaller messages -UninstallNotFound=¨S¦łŔÉ®× "%1"ˇCµLŞk¸Ń°Ł¦w¸ËˇC -UninstallOpenError=µLŞk¶}±ŇŔÉ®× "%1"ˇCµLŞk¸Ń°Ł¦w¸Ë -UninstallUnsupportedVer=¦ąŞ©¸Ń°Ł¦w¸Ëµ{¦ˇµLŞkżëĂяѰ٦w¸Ë°OżýŔÉ "%1" Şş®ć¦ˇˇCµLŞk¸Ń°Ł¦w¸Ë -UninstallUnknownEntry=¦b¸Ń°Ł¦w¸Ë°Ożý¤¤§ä¨ě¤Ł©úŞş¶µĄŘ (%1) -ConfirmUninstall=˝T©w­n§ąĄţ˛ľ°Ł %1 ¤Î¨ä©Ň¦ł¤¸Ąó¶Ü? -UninstallOnlyOnWin64=ĄuĄi¦b 64 ¦ě¤¸ Windows ¤W¸Ń°Ł¦w¸Ë¦ą¦w¸ËˇC -OnlyAdminCanUninstall=Ąu¦ł¨ăłĆ¨t˛ÎşŢ˛zĹv­­Şş¨ĎĄÎŞĚˇA¤~Żŕ¸Ń°Ł¦w¸Ë¦ą¦w¸ËˇC -UninstallStatusLabel=Ąż¦b±q±zŞşąq¸Ł˛ľ°Ł %1ˇA˝Đµy­ÔˇC -UninstalledAll=¤w¦¨Ą\±q±zŞşąq¸Ł˛ľ°Ł %1ˇC -UninstalledMost=¸Ń°Ł¦w¸Ë %1 ¤w§ą¦¨ˇC%n%n¦łłˇ¤Ŕ¶µĄŘµLŞk˛ľ°ŁˇC±zĄiĄH¤â°ĘĄ[ĄH˛ľ°ŁˇC -UninstalledAndNeedsRestart=­Y­n§ą¦¨ %1 Şş¸Ń°Ł¦w¸ËˇAĄ˛¶·­«·s±Ň°Ę±zŞşąq¸ŁˇC%n%n­nĄß§Y­«·s±Ň°Ę¶Ü? -UninstallDataCorrupted="%1" Ŕɮפw·l·´ˇCµLŞk¸Ń°Ł¦w¸Ë +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=­n˛ľ°Ł¦@ĄÎŔÉ®×¶Ü? -ConfirmDeleteSharedFile2=¨t˛Î«üĄX¤wµLĄô¦óµ{¦ˇ¦b¨ĎĄÎ¤U¦C¦@ĄÎŔɮסC±z­n¸Ń°Ł¦w¸ËˇAĄH˛ľ°Ł¦ą¦@ĄÎŔÉ®×¶Ü?%n%n¦p¦łĄô¦óµ{¦ˇ¤´¦b¨ĎĄÎ¦ąŔɮצӱN¸ÓŔɮײľ°ŁˇAło¨Çµ{¦ˇĄiŻŕµLŞkĄż±`ąB§@ˇC­Y¤Ł˝T©wˇA˝ĐżďľÜ [§_]ˇC±NŔɮ׫OŻd¦b¨t˛Î¤W¨Ă¤Ł·|ły¦¨Ąô¦ó¤Ł¨}ĽvĹTˇC -SharedFileNameLabel=ŔɮצWşŮ: -SharedFileLocationLabel=¦ě¸m: -WizardUninstalling=¸Ń°Ł¦w¸ËެşA -StatusUninstalling=Ąż¦b¸Ń°Ł¦w¸Ë %1... +ConfirmDeleteSharedFileTitle=ç§»é™¤ĺ…±ç”¨ćŞ”ćˇ +ConfirmDeleteSharedFile2=系統顯示下ĺ—共用檔ćˇĺ·˛ä¸Ťĺ†Ťč˘«ä»»ä˝•程式所使用,您č¦ç§»é™¤é€™äş›ćŞ”ćˇĺ—Ž?%n%n%1%n%nĺ€č‹Ąć‚¨ç§»é™¤äş†ä»Ąä¸ŠćŞ”ćˇä˝†ä»Ťćś‰ç¨‹ĺĽŹéś€č¦ä˝żç”¨ĺ®ĺ€‘,將造ć這些程式無法正常執行,因此您若無法確定請é¸ć“‡ [ĺ¦]。保留這些檔ćˇĺś¨ć‚¨çš„系統中不ćśé€ ć任何ćŤĺ®łă€‚ +SharedFileNameLabel=檔ćˇĺŤç¨±: +SharedFileLocationLabel=位置: +WizardUninstalling=解除安裝狀態 +StatusUninstalling=正在解除安裝 %1... + ; *** Shutdown block reasons -ShutdownBlockReasonInstallingApp=Ąż¦b¦w¸Ë %1ˇC -ShutdownBlockReasonUninstallingApp=Ąż¦b¸Ń°Ł¦w¸Ë %1ˇC +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=¨äĄ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 °ĆŔɦWŞşĂöÁpˇK -AutoStartProgramGroupDescription=±Ň°Ę: -AutoStartProgram=¦Ű°Ę±Ň°Ę %1 -AddonHostProgramNotFound=¦bżď¨úŞş¸ę®Ć§¨¤¤§ä¤Ł¨ě %1ˇC%n%n¤´­nÄ~Äň¶Ü? \ No newline at end of file + +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您ćŻĺ¦é‚„č¦çąĽçşŚďĽź diff --git a/build/win32/i18n/messages.en.isl b/build/win32/i18n/messages.en.isl index 12189c080d..986eba00d3 100644 --- a/build/win32/i18n/messages.en.isl +++ b/build/win32/i18n/messages.en.isl @@ -1,4 +1,11 @@ +[Messages] +FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed shortcuts. +ConfirmUninstall=Are you sure you want to completely remove %1 and all of its components? + [CustomMessages] +AdditionalIcons=Additional icons: +CreateDesktopIcon=Create a &desktop icon +CreateQuickLaunchIcon=Create a &Quick Launch icon 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 diff --git a/build/yarn.lock b/build/yarn.lock index 61f64b4f43..a292262675 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -1817,10 +1817,10 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" -iconv-lite-umd@0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.5.tgz#6a1f621a3b4d125f72feff813a9839e1ebd6c722" - integrity sha512-WDegH4al+e3n3jTOStRvm+jzDA3JMUQGgzdAsMxAgcgB0Oi72HjfdsoX08ieKsy3rKexXVjWZr41aOIUaCZnMg== +iconv-lite-umd@0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" + integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== iconv-lite@^0.4.4: version "0.4.24" @@ -3539,10 +3539,10 @@ typescript@^3.0.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== -typescript@^4.0.0-dev.20200629: - version "4.0.0-dev.20200629" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200629.tgz#4631667ebffe3a340beee885a4bebe3a73b6f18e" - integrity sha512-c4DUu7KvTcx4x7V8sBWexYNkCfioiH1huOJL6WFAA8Oot0Gr/+PlKKDBS9fYjsadEv1JI1qboJKobwLQn0kQXw== +typescript@^4.0.0-dev.20200708: + version "4.0.0-dev.20200708" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.0-dev.20200708.tgz#9cf526945590456f34b158b180a6f8a2e0b57306" + integrity sha512-A1brZPJRwkm2pxOSTIOVqLvrhF1swfLvDAgN+jRP/yUWNn+0OqbfunuY/jvRbtkEP4/AWZ+M2P625Y+JDN/j2A== typical@^4.0.0: version "4.0.0" diff --git a/cgmanifest.json b/cgmanifest.json index 25178f2f20..1fb78691e7 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "e4745133a1d3745f066e068b8033c6a269b59caf" + "commitHash": "052d3b44972e6d94ef40054d46c150b7cdd7a5d8" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "78.0.3904.130" + "version": "80.0.3987.165" }, { "component": { @@ -48,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "787378879acfb212ed4ff824bf9f767a24a5cb43a" + "commitHash": "42cce5a9d0fd905bf4ad7a2528c36572dfb8b5ad" } }, "isOnlyProductionDependency": true, - "version": "12.8.1" + "version": "12.13.0" }, { "component": { @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "5f93e889020d279d5a9cd1ecab080ab467312447" + "commitHash": "87fd06bc96bce8f46ca05b8315657fd230bcac85" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "7.3.2" + "version": "8.3.3" }, { "component": { @@ -533,7 +533,7 @@ "git": { "name": "ripgrep", "repositoryUrl": "https://github.com/BurntSushi/ripgrep", - "commitHash": "8a7db1a918e969b85cd933d8ed9fa5285b281ba4" + "commitHash": "973de50c9ef451da2cfcdfa86f2b2711d8d6ff48" } }, "isOnlyProductionDependency": true, diff --git a/extensions/extension-editing/extension-browser.webpack.config.js b/extensions/extension-editing/extension-browser.webpack.config.js new file mode 100644 index 0000000000..5870e94564 --- /dev/null +++ b/extensions/extension-editing/extension-browser.webpack.config.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/extensionEditingBrowserMain.ts' + }, + output: { + filename: 'extensionEditingBrowserMain.js' + } +}); + diff --git a/extensions/extension-editing/extension.webpack.config.js b/extensions/extension-editing/extension.webpack.config.js index 887933358e..9fcce7e999 100644 --- a/extensions/extension-editing/extension.webpack.config.js +++ b/extensions/extension-editing/extension.webpack.config.js @@ -12,7 +12,7 @@ const withDefaults = require('../shared.webpack.config'); module.exports = withDefaults({ context: __dirname, entry: { - extension: './src/extension.ts', + extension: './src/extensionEditingMain.ts', }, externals: { '../../../product.json': 'commonjs ../../../product.json', diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json index 75d77925b0..bdd02d870a 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -13,7 +13,8 @@ "onLanguage:markdown", "onLanguage:typescript" ], - "main": "./out/extension", + "main": "./out/extensionEditingMain", + "browser": "./dist/browser/extensionEditingBrowserMain", "scripts": { "compile": "gulp compile-extension:extension-editing", "watch": "gulp watch-extension:extension-editing" diff --git a/extensions/extension-editing/src/extensionEditingBrowserMain.ts b/extensions/extension-editing/src/extensionEditingBrowserMain.ts new file mode 100644 index 0000000000..fea2db000f --- /dev/null +++ b/extensions/extension-editing/src/extensionEditingBrowserMain.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 * as vscode from 'vscode'; +import { PackageDocument } from './packageDocumentHelper'; + +export function activate(context: vscode.ExtensionContext) { + //package.json suggestions + context.subscriptions.push(registerPackageDocumentCompletions()); + +} + +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/extension.ts b/extensions/extension-editing/src/extensionEditingMain.ts similarity index 100% rename from extensions/extension-editing/src/extension.ts rename to extensions/extension-editing/src/extensionEditingMain.ts diff --git a/extensions/git/package.json b/extensions/git/package.json index e5e78fd3d8..e96ef36aba 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1789,8 +1789,8 @@ "diff" ], "extensions": [ - ".patch", ".diff", + ".patch", ".rej" ], "configuration": "./languages/diff.language-configuration.json" @@ -1878,8 +1878,8 @@ "dependencies": { "byline": "^5.0.0", "file-type": "^7.2.0", - "iconv-lite-umd": "0.6.5", - "jschardet": "2.1.1", + "iconv-lite-umd": "0.6.8", + "jschardet": "2.2.1", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0", "vscode-uri": "^2.0.0", diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index f7326a39f2..5e3a8b69ce 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -5,10 +5,12 @@ import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; -import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, GitExtension, RefType, RemoteSourceProvider, CredentialsProvider, BranchQuery } from './git'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, RemoteSourceProvider, CredentialsProvider, BranchQuery, PushErrorHandler } from './git'; import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands } from 'vscode'; import { mapEvent } from '../util'; import { toGitUri } from '../uri'; +import { pickRemoteSource, PickRemoteSourceOptions } from '../remoteSource'; +import { GitExtensionImpl } from './extension'; class ApiInputBox implements InputBox { set value(value: string) { this._inputBox.value = value; } @@ -271,6 +273,10 @@ export class ApiImpl implements API { return this._model.registerCredentialsProvider(provider); } + registerPushErrorHandler(handler: PushErrorHandler): Disposable { + return this._model.registerPushErrorHandler(handler); + } + constructor(private _model: Model) { } } @@ -308,41 +314,51 @@ function getStatus(status: Status): string { return 'UNKNOWN'; } -export function registerAPICommands(extension: GitExtension): Disposable { - return Disposable.from( - commands.registerCommand('git.api.getRepositories', () => { - const api = extension.getAPI(1); - return api.repositories.map(r => r.rootUri.toString()); - }), +export function registerAPICommands(extension: GitExtensionImpl): Disposable { + const disposables: Disposable[] = []; - commands.registerCommand('git.api.getRepositoryState', (uri: string) => { - const api = extension.getAPI(1); - const repository = api.getRepository(Uri.parse(uri)); + disposables.push(commands.registerCommand('git.api.getRepositories', () => { + const api = extension.getAPI(1); + return api.repositories.map(r => r.rootUri.toString()); + })); - if (!repository) { - return null; - } + disposables.push(commands.registerCommand('git.api.getRepositoryState', (uri: string) => { + const api = extension.getAPI(1); + const repository = api.getRepository(Uri.parse(uri)); - const state = repository.state; + if (!repository) { + return null; + } - const ref = (ref: Ref | undefined) => (ref && { ...ref, type: getRefType(ref.type) }); - const change = (change: Change) => ({ - uri: change.uri.toString(), - originalUri: change.originalUri.toString(), - renameUri: change.renameUri?.toString(), - status: getStatus(change.status) - }); + const state = repository.state; - return { - HEAD: ref(state.HEAD), - refs: state.refs.map(ref), - remotes: state.remotes, - submodules: state.submodules, - rebaseCommit: state.rebaseCommit, - mergeChanges: state.mergeChanges.map(change), - indexChanges: state.indexChanges.map(change), - workingTreeChanges: state.workingTreeChanges.map(change) - }; - }) - ); + const ref = (ref: Ref | undefined) => (ref && { ...ref, type: getRefType(ref.type) }); + const change = (change: Change) => ({ + uri: change.uri.toString(), + originalUri: change.originalUri.toString(), + renameUri: change.renameUri?.toString(), + status: getStatus(change.status) + }); + + return { + HEAD: ref(state.HEAD), + refs: state.refs.map(ref), + remotes: state.remotes, + submodules: state.submodules, + rebaseCommit: state.rebaseCommit, + mergeChanges: state.mergeChanges.map(change), + indexChanges: state.indexChanges.map(change), + workingTreeChanges: state.workingTreeChanges.map(change) + }; + })); + + disposables.push(commands.registerCommand('git.api.getRemoteSources', (opts?: PickRemoteSourceOptions) => { + if (!extension.model) { + return; + } + + return pickRemoteSource(extension.model, opts); + })); + + return Disposable.from(...disposables); } diff --git a/extensions/git/src/api/extension.ts b/extensions/git/src/api/extension.ts index b74925145e..73c8638caa 100644 --- a/extensions/git/src/api/extension.ts +++ b/extensions/git/src/api/extension.ts @@ -7,7 +7,6 @@ import { Model } from '../model'; import { GitExtension, Repository, API } from './git'; import { ApiRepository, ApiImpl } from './api1'; import { Event, EventEmitter } from 'vscode'; -import { latchEvent } from '../util'; export function deprecated(_target: any, key: string, descriptor: any): void { if (typeof descriptor.value !== 'function') { @@ -26,17 +25,27 @@ export class GitExtensionImpl implements GitExtension { enabled: boolean = false; private _onDidChangeEnablement = new EventEmitter(); - readonly onDidChangeEnablement: Event = latchEvent(this._onDidChangeEnablement.event); + readonly onDidChangeEnablement: Event = this._onDidChangeEnablement.event; private _model: Model | undefined = undefined; set model(model: Model | undefined) { this._model = model; - this.enabled = !!model; + const enabled = !!model; + + if (this.enabled === enabled) { + return; + } + + this.enabled = enabled; this._onDidChangeEnablement.fire(this.enabled); } + get model(): Model | undefined { + return this._model; + } + constructor(model?: Model) { if (model) { this.enabled = true; @@ -73,4 +82,4 @@ export class GitExtensionImpl implements GitExtension { return new ApiImpl(this._model); } -} \ No newline at end of file +} diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 6f7c8eb674..9593d8bc70 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -223,6 +223,10 @@ export interface CredentialsProvider { getCredentials(host: Uri): ProviderResult; } +export interface PushErrorHandler { + handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { gitErrorCode: GitErrorCodes }): Promise; +} + export type APIState = 'uninitialized' | 'initialized'; export interface API { @@ -239,6 +243,7 @@ export interface API { registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; registerCredentialsProvider(provider: CredentialsProvider): Disposable; + registerPushErrorHandler(handler: PushErrorHandler): Disposable; } export interface GitExtension { @@ -276,6 +281,7 @@ export const enum GitErrorCodes { CantOpenResource = 'CantOpenResource', GitNotFound = 'GitNotFound', CantCreatePipe = 'CantCreatePipe', + PermissionDenied = 'PermissionDenied', CantAccessRemote = 'CantAccessRemote', RepositoryNotFound = 'RepositoryNotFound', RepositoryIsLocked = 'RepositoryIsLocked', diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 4450cc36df..ce80a053fc 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -6,10 +6,10 @@ import { lstat, Stats } from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, QuickPick } from 'vscode'; +import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env } from 'vscode'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; -import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider, RemoteSource } from './api/git'; +import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider } from './api/git'; import { ForcePushMode, Git, Stash } from './git'; import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; @@ -18,8 +18,8 @@ import { fromGitUri, toGitUri, isGitUri } from './uri'; import { grep, isDescendant, pathEquals } from './util'; import { Log, LogLevel } from './log'; import { GitTimelineItem } from './timelineProvider'; -import { throttle, debounce } from './decorators'; import { ApiRepository } from './api/api1'; +import { pickRemoteSource } from './remoteSource'; const localize = nls.loadMessageBundle(); @@ -240,72 +240,6 @@ interface PushOptions { silent?: boolean; } -async function getQuickPickResult(quickpick: QuickPick): Promise { - const result = await new Promise(c => { - quickpick.onDidAccept(() => c(quickpick.selectedItems[0])); - quickpick.onDidHide(() => c(undefined)); - quickpick.show(); - }); - - quickpick.hide(); - return result; -} - -class RemoteSourceProviderQuickPick { - - private quickpick: QuickPick; - - constructor(private provider: RemoteSourceProvider) { - this.quickpick = window.createQuickPick(); - this.quickpick.ignoreFocusOut = true; - - if (provider.supportsQuery) { - this.quickpick.placeholder = localize('type to search', "Repository name (type to search)"); - this.quickpick.onDidChangeValue(this.onDidChangeValue, this); - } else { - this.quickpick.placeholder = localize('type to filter', "Repository name"); - } - } - - @debounce(300) - onDidChangeValue(): void { - this.query(); - } - - @throttle - async query(): Promise { - this.quickpick.busy = true; - - try { - const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; - - if (remoteSources.length === 0) { - this.quickpick.items = [{ - label: localize('none found', "No remote repositories found."), - alwaysShow: true - }]; - } else { - this.quickpick.items = remoteSources.map(remoteSource => ({ - label: remoteSource.name, - description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), - remoteSource - })); - } - } catch (err) { - this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; - console.error(err); - } finally { - this.quickpick.busy = false; - } - } - - async pick(): Promise { - this.query(); - const result = await getQuickPickResult(this.quickpick); - return result?.remoteSource; - } -} - export class CommandCenter { private disposables: Disposable[]; @@ -527,51 +461,10 @@ export class CommandCenter { @command('git.clone') async clone(url?: string, parentPath?: string): Promise { if (!url) { - const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); - quickpick.ignoreFocusOut = true; - - const providers = this.model.getRemoteProviders() - .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + localize('clonefrom', "Clone from {0}", provider.name), alwaysShow: true, provider })); - - quickpick.placeholder = providers.length === 0 - ? localize('provide url', "Provide repository URL") - : localize('provide url or pick', "Provide repository URL or pick a repository source."); - - const updatePicks = (value?: string) => { - if (value) { - quickpick.items = [{ - label: localize('repourl', "Clone from URL"), - description: value, - alwaysShow: true, - url: value - }, - ...providers]; - } else { - quickpick.items = providers; - } - }; - - quickpick.onDidChangeValue(updatePicks); - updatePicks(); - - const result = await getQuickPickResult(quickpick); - - if (result) { - if (result.url) { - url = result.url; - } else if (result.provider) { - const quickpick = new RemoteSourceProviderQuickPick(result.provider); - const remote = await quickpick.pick(); - - if (remote) { - if (typeof remote.url === 'string') { - url = remote.url; - } else if (remote.url.length > 0) { - url = await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); - } - } - } - } + url = await pickRemoteSource(this.model, { + providerLabel: provider => localize('clonefrom', "Clone from {0}", provider.name), + urlLabel: localize('repourl', "Clone from URL") + }); } if (!url) { @@ -845,7 +738,7 @@ export class CommandCenter { try { document = await workspace.openTextDocument(uri); } catch (error) { - await commands.executeCommand('vscode.open', uri, opts); + await commands.executeCommand('vscode.open', uri, opts); continue; } @@ -858,7 +751,7 @@ export class CommandCenter { const editor = await window.showTextDocument(document, opts); editor.revealRange(previousVisibleRanges[0]); } else { - await window.showTextDocument(document, opts); + await commands.executeCommand('vscode.open', uri, opts); } } } @@ -2146,52 +2039,10 @@ export class CommandCenter { @command('git.addRemote', { repository: true }) async addRemote(repository: Repository): Promise { - const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); - quickpick.ignoreFocusOut = true; - - const providers = this.model.getRemoteProviders() - .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + localize('addfrom', "Add remote from {0}", provider.name), alwaysShow: true, provider })); - - quickpick.placeholder = providers.length === 0 - ? localize('provide url', "Provide repository URL") - : localize('provide url or pick', "Provide repository URL or pick a repository source."); - - const updatePicks = (value?: string) => { - if (value) { - quickpick.items = [{ - label: localize('addFrom', "Add remote from URL"), - description: value, - alwaysShow: true, - url: value - }, - ...providers]; - } else { - quickpick.items = providers; - } - }; - - quickpick.onDidChangeValue(updatePicks); - updatePicks(); - - const result = await getQuickPickResult(quickpick); - let url: string | undefined; - - if (result) { - if (result.url) { - url = result.url; - } else if (result.provider) { - const quickpick = new RemoteSourceProviderQuickPick(result.provider); - const remote = await quickpick.pick(); - - if (remote) { - if (typeof remote.url === 'string') { - url = remote.url; - } else if (remote.url.length > 0) { - url = await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); - } - } - } - } + const url = await pickRemoteSource(this.model, { + providerLabel: provider => localize('addfrom', "Add remote from {0}", provider.name), + urlLabel: localize('addFrom', "Add remote from URL") + }); if (!url) { return; @@ -2533,8 +2384,7 @@ export class CommandCenter { @command('git.timeline.openDiff', { repository: false }) async timelineOpenDiff(item: TimelineItem, uri: Uri | undefined, _source: string) { - // eslint-disable-next-line eqeqeq - if (uri == null || !GitTimelineItem.is(item)) { + if (uri === undefined || uri === null || !GitTimelineItem.is(item)) { return undefined; } diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 326bc19e4e..938b81b2f4 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -419,7 +419,7 @@ export class Git { } async getRepositoryRoot(repositoryPath: string): Promise { - const result = await this.exec(repositoryPath, ['rev-parse', '--show-toplevel']); + const result = await this.exec(repositoryPath, ['rev-parse', '--show-toplevel'], { log: false }); // Keep trailing spaces which are part of the directory name const repoPath = path.normalize(result.stdout.trimLeft().replace(/[\r\n]+$/, '')); @@ -437,8 +437,7 @@ export class Git { try { const networkPath = await new Promise(resolve => realpath.native(`${letter}:`, { encoding: 'utf8' }, (err, resolvedPath) => - // eslint-disable-next-line eqeqeq - resolve(err != null ? undefined : resolvedPath), + resolve(err !== null ? undefined : resolvedPath), ), ); if (networkPath !== undefined) { @@ -1628,6 +1627,8 @@ export class Repository { err.gitErrorCode = GitErrorCodes.RemoteConnectionError; } else if (/^fatal: The current branch .* has no upstream branch/.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.NoUpstreamBranch; + } else if (/Permission.*denied/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.PermissionDenied; } throw err; diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 456b485cb7..8102413024 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -128,7 +128,7 @@ async function warnAboutMissingGit(): Promise { } }*/ -export async function _activate(context: ExtensionContext): Promise { +export async function _activate(context: ExtensionContext): Promise { const disposables: Disposable[] = []; context.subscriptions.push(new Disposable(() => Disposable.from(...disposables).dispose())); @@ -176,8 +176,7 @@ export async function activate(context: ExtensionContext): Promise return result; } -// @ts-expect-error -async function checkGitVersion(info: IGit): Promise { +async function checkGitv1(info: IGit): Promise { const config = workspace.getConfiguration('git'); const shouldIgnore = config.get('ignoreLegacyWarning') === true; @@ -204,3 +203,28 @@ async function checkGitVersion(info: IGit): Promise { await config.update('ignoreLegacyWarning', true, true); } } + +async function checkGitWindows(info: IGit): Promise { + if (!/^2\.(25|26)\./.test(info.version)) { + return; + } + + const update = localize('updateGit', "Update Git"); + const choice = await window.showWarningMessage( + localize('git2526', "There are known issues with the installed Git {0}. Please update to Git >= 2.27 for the git features to work correctly.", info.version), + update + ); + + if (choice === update) { + commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/')); + } +} + +// @ts-expect-error +async function checkGitVersion(info: IGit): Promise { + await checkGitv1(info); + + if (process.platform === 'win32') { + await checkGitWindows(info); + } +} diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index d41e202ebf..8f4e1ac6a3 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -12,9 +12,10 @@ import * as path from 'path'; import * as fs from 'fs'; import * as nls from 'vscode-nls'; import { fromGitUri } from './uri'; -import { APIState as State, RemoteSourceProvider, CredentialsProvider } from './api/git'; +import { APIState as State, RemoteSourceProvider, CredentialsProvider, PushErrorHandler } from './api/git'; import { Askpass } from './askpass'; import { IRemoteSourceProviderRegistry } from './remoteProvider'; +import { IPushErrorHandlerRegistry } from './pushError'; const localize = nls.loadMessageBundle(); @@ -46,7 +47,7 @@ interface OpenRepository extends Disposable { repository: Repository; } -export class Model implements IRemoteSourceProviderRegistry { +export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRegistry { private _onDidOpenRepository = new EventEmitter(); readonly onDidOpenRepository: Event = this._onDidOpenRepository.event; @@ -94,6 +95,8 @@ export class Model implements IRemoteSourceProviderRegistry { private _onDidRemoveRemoteSourceProvider = new EventEmitter(); readonly onDidRemoveRemoteSourceProvider = this._onDidRemoveRemoteSourceProvider.event; + private pushErrorHandlers = new Set(); + private disposables: Disposable[] = []; constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, private outputChannel: OutputChannel) { @@ -269,7 +272,7 @@ export class Model implements IRemoteSourceProviderRegistry { } const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); - const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this.globalState, this.outputChannel); + const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannel); this.open(repository); await repository.status(); @@ -485,6 +488,15 @@ export class Model implements IRemoteSourceProviderRegistry { return [...this.remoteSourceProviders.values()]; } + registerPushErrorHandler(handler: PushErrorHandler): Disposable { + this.pushErrorHandlers.add(handler); + return toDisposable(() => this.pushErrorHandlers.delete(handler)); + } + + getPushErrorHandlers(): PushErrorHandler[] { + return [...this.pushErrorHandlers]; + } + dispose(): void { const openRepositories = [...this.openRepositories]; openRepositories.forEach(r => r.dispose()); diff --git a/extensions/git/src/pushError.ts b/extensions/git/src/pushError.ts new file mode 100644 index 0000000000..ddecb6d7e1 --- /dev/null +++ b/extensions/git/src/pushError.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. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vscode'; +import { PushErrorHandler } from './api/git'; + +export interface IPushErrorHandlerRegistry { + registerPushErrorHandler(provider: PushErrorHandler): Disposable; + getPushErrorHandlers(): PushErrorHandler[]; +} diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts new file mode 100644 index 0000000000..f8361eb6a2 --- /dev/null +++ b/extensions/git/src/remoteSource.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 { QuickPickItem, window, QuickPick } from 'vscode'; +import * as nls from 'vscode-nls'; +import { RemoteSourceProvider, RemoteSource } from './api/git'; +import { Model } from './model'; +import { throttle, debounce } from './decorators'; + +const localize = nls.loadMessageBundle(); + +async function getQuickPickResult(quickpick: QuickPick): Promise { + const result = await new Promise(c => { + quickpick.onDidAccept(() => c(quickpick.selectedItems[0])); + quickpick.onDidHide(() => c(undefined)); + quickpick.show(); + }); + + quickpick.hide(); + return result; +} + +class RemoteSourceProviderQuickPick { + + private quickpick: QuickPick; + + constructor(private provider: RemoteSourceProvider) { + this.quickpick = window.createQuickPick(); + this.quickpick.ignoreFocusOut = true; + + if (provider.supportsQuery) { + this.quickpick.placeholder = localize('type to search', "Repository name (type to search)"); + this.quickpick.onDidChangeValue(this.onDidChangeValue, this); + } else { + this.quickpick.placeholder = localize('type to filter', "Repository name"); + } + } + + @debounce(300) + private onDidChangeValue(): void { + this.query(); + } + + @throttle + private async query(): Promise { + this.quickpick.busy = true; + + try { + const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; + + if (remoteSources.length === 0) { + this.quickpick.items = [{ + label: localize('none found', "No remote repositories found."), + alwaysShow: true + }]; + } else { + this.quickpick.items = remoteSources.map(remoteSource => ({ + label: remoteSource.name, + description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), + remoteSource + })); + } + } catch (err) { + this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; + console.error(err); + } finally { + this.quickpick.busy = false; + } + } + + async pick(): Promise { + this.query(); + const result = await getQuickPickResult(this.quickpick); + return result?.remoteSource; + } +} + +export interface PickRemoteSourceOptions { + readonly providerLabel?: (provider: RemoteSourceProvider) => string; + readonly urlLabel?: string; +} + +export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { + const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); + quickpick.ignoreFocusOut = true; + + const providers = model.getRemoteProviders() + .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); + + quickpick.placeholder = providers.length === 0 + ? localize('provide url', "Provide repository URL") + : localize('provide url or pick', "Provide repository URL or pick a repository source."); + + const updatePicks = (value?: string) => { + if (value) { + quickpick.items = [{ + label: options.urlLabel ?? localize('url', "URL"), + description: value, + alwaysShow: true, + url: value + }, + ...providers]; + } else { + quickpick.items = providers; + } + }; + + quickpick.onDidChangeValue(updatePicks); + updatePicks(); + + const result = await getQuickPickResult(quickpick); + + if (result) { + if (result.url) { + return result.url; + } else if (result.provider) { + const quickpick = new RemoteSourceProviderQuickPick(result.provider); + const remote = await quickpick.pick(); + + if (remote) { + if (typeof remote.url === 'string') { + return remote.url; + } else if (remote.url.length > 0) { + return await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); + } + } + } + } + + return undefined; +} diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index ea3feabd01..8217d2037e 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -17,6 +17,8 @@ import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, import { IFileWatcher, watch } from './watch'; import { Log, LogLevel } from './log'; import { IRemoteSourceProviderRegistry } from './remoteProvider'; +import { IPushErrorHandlerRegistry } from './pushError'; +import { ApiRepository } from './api/api1'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -683,6 +685,7 @@ export class Repository implements Disposable { constructor( private readonly repository: BaseRepository, remoteSourceProviderRegistry: IRemoteSourceProviderRegistry, + private pushErrorHandlerRegistry: IPushErrorHandlerRegistry, globalState: Memento, outputChannel: OutputChannel ) { @@ -865,7 +868,7 @@ export class Repository implements Disposable { } async getInputTemplate(): Promise { - const commitMessage = (await Promise.all([this.repository.getMergeMessage(), this.repository.getSquashMessage()])).find(msg => msg !== undefined); + const commitMessage = (await Promise.all([this.repository.getMergeMessage(), this.repository.getSquashMessage()])).find(msg => !!msg); if (commitMessage) { return commitMessage; @@ -1181,15 +1184,15 @@ export class Repository implements Disposable { branch = `${head.name}:${head.upstream.name}`; } - await this.run(Operation.Push, () => this.repository.push(remote, branch, undefined, undefined, forcePushMode)); + await this.run(Operation.Push, () => this._push(remote, branch, undefined, undefined, forcePushMode)); } async pushTo(remote?: string, name?: string, setUpstream: boolean = false, forcePushMode?: ForcePushMode): Promise { - await this.run(Operation.Push, () => this.repository.push(remote, name, setUpstream, undefined, forcePushMode)); + await this.run(Operation.Push, () => this._push(remote, name, setUpstream, undefined, forcePushMode)); } async pushFollowTags(remote?: string, forcePushMode?: ForcePushMode): Promise { - await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true, forcePushMode)); + await this.run(Operation.Push, () => this._push(remote, undefined, false, true, forcePushMode)); } async blame(path: string): Promise { @@ -1249,7 +1252,7 @@ export class Repository implements Disposable { const shouldPush = this.HEAD && (typeof this.HEAD.ahead === 'number' ? this.HEAD.ahead > 0 : true); if (shouldPush) { - await this.repository.push(remoteName, pushBranch); + await this._push(remoteName, pushBranch); } }); }); @@ -1411,6 +1414,31 @@ export class Repository implements Disposable { return ignored; } + private async _push(remote?: string, refspec?: string, setUpstream: boolean = false, tags = false, forcePushMode?: ForcePushMode): Promise { + try { + await this.repository.push(remote, refspec, setUpstream, tags, forcePushMode); + } catch (err) { + if (!remote || !refspec) { + throw err; + } + + const repository = new ApiRepository(this); + const remoteObj = repository.state.remotes.find(r => r.name === remote); + + if (!remoteObj) { + throw err; + } + + for (const handler of this.pushErrorHandlerRegistry.getPushErrorHandlers()) { + if (await handler.handlePushError(repository, remoteObj, refspec, err)) { + return; + } + } + + throw err; + } + } + private async run(operation: Operation, runOperation: () => Promise = () => Promise.resolve(null)): Promise { if (this.state !== RepositoryState.Idle) { throw new Error('Repository not initialized'); diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 221f2a65a2..23e8f9f8b7 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -44,18 +44,6 @@ export function filterEvent(event: Event, filter: (e: T) => boolean): Even return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); } -export function latchEvent(event: Event): Event { - let firstCall = true; - let cache: T; - - return filterEvent(event, value => { - let shouldEmit = firstCall || value !== cache; - firstCall = false; - cache = value; - return shouldEmit; - }); -} - export function anyEvent(...events: Event[]): Event { return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => { const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i)))); diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 35277c4713..b56e76c37a 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -425,10 +425,10 @@ https-proxy-agent@^2.2.1: agent-base "^4.3.0" debug "^3.1.0" -iconv-lite-umd@0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.5.tgz#6a1f621a3b4d125f72feff813a9839e1ebd6c722" - integrity sha512-WDegH4al+e3n3jTOStRvm+jzDA3JMUQGgzdAsMxAgcgB0Oi72HjfdsoX08ieKsy3rKexXVjWZr41aOIUaCZnMg== +iconv-lite-umd@0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" + integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== inflight@^1.0.4: version "1.0.6" @@ -468,10 +468,10 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jschardet@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" - integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== +jschardet@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.2.1.tgz#03b0264669a90c7a5c436a68c5a7d4e4cb0c9823" + integrity sha512-Ks2JNuUJoc7PGaZ7bVFtSEvOcr0rBq6Q1J5/7+zKWLT+g+4zziL63O0jg7y2jxhzIa1LVsHUbPXrbaWmz9iwDw== json-schema-traverse@^0.4.1: version "0.4.1" diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts index d5c8d203c0..9d4fa10b10 100644 --- a/extensions/github-authentication/src/extension.ts +++ b/extensions/github-authentication/src/extension.ts @@ -24,19 +24,27 @@ export async function activate(context: vscode.ExtensionContext) { vscode.authentication.registerAuthenticationProvider({ id: 'github', - displayName: 'GitHub', + label: 'GitHub', supportsMultipleAccounts: false, onDidChangeSessions: onDidChangeSessions.event, getSessions: () => Promise.resolve(loginService.sessions), login: async (scopeList: string[]) => { try { + /* __GDPR__ + "login" : { } + */ telemetryReporter.sendTelemetryEvent('login'); + const session = await loginService.login(scopeList.sort().join(' ')); Logger.info('Login success!'); onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] }); return session; } catch (e) { + /* __GDPR__ + "loginFailed" : { } + */ telemetryReporter.sendTelemetryEvent('loginFailed'); + vscode.window.showErrorMessage(`Sign in failed: ${e}`); Logger.error(e); throw e; @@ -44,11 +52,19 @@ export async function activate(context: vscode.ExtensionContext) { }, logout: async (id: string) => { try { + /* __GDPR__ + "logout" : { } + */ telemetryReporter.sendTelemetryEvent('logout'); + await loginService.logout(id); onDidChangeSessions.fire({ added: [], removed: [id], changed: [] }); } catch (e) { + /* __GDPR__ + "logoutFailed" : { } + */ telemetryReporter.sendTelemetryEvent('logoutFailed'); + vscode.window.showErrorMessage(`Sign out failed: ${e}`); Logger.error(e); throw e; diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index f86092fbd0..55376dcdfa 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -14,7 +14,8 @@ export const onDidChangeSessions = new vscode.EventEmitter { const userInfo = await this._githubServer.getUserInfo(token); - return new vscode.AuthenticationSession(uuid(), token, { displayName: userInfo.accountName, id: userInfo.id }, scopes); + return new vscode.AuthenticationSession(uuid(), token, { label: userInfo.accountName, id: userInfo.id }, scopes); } private async setToken(session: vscode.AuthenticationSession): Promise { diff --git a/extensions/github-browser/package.json b/extensions/github-browser/package.json index 6e3c071b70..9938e25ed5 100644 --- a/extensions/github-browser/package.json +++ b/extensions/github-browser/package.json @@ -21,6 +21,11 @@ "main": "./out/extension.js", "contributes": { "commands": [ + { + "command": "githubBrowser.openRepository", + "title": "Open GitHub Repository...", + "category": "GitHub Browser" + }, { "command": "githubBrowser.commit", "title": "Commit", @@ -48,6 +53,10 @@ ], "menus": { "commandPalette": [ + { + "command": "githubBrowser.openRepository", + "when": "config.githubBrowser.openRepository" + }, { "command": "githubBrowser.commit", "when": "false" diff --git a/extensions/github-browser/src/changeStore.ts b/extensions/github-browser/src/changeStore.ts index 26ba7f1071..516eee7c38 100644 --- a/extensions/github-browser/src/changeStore.ts +++ b/extensions/github-browser/src/changeStore.ts @@ -47,31 +47,17 @@ function fromSerialized(operations: StoredOperation): Operation { return { ...operations, uri: Uri.parse(operations.uri) }; } -interface CreatedFileChangeStoreEvent { - type: 'created'; +export interface ChangeStoreEvent { + type: 'created' | 'changed' | 'deleted'; rootUri: Uri; uri: Uri; } -interface ChangedFileChangeStoreEvent { - type: 'changed'; - rootUri: Uri; - uri: Uri; -} - -interface DeletedFileChangeStoreEvent { - type: 'deleted'; - rootUri: Uri; - uri: Uri; -} - -type ChangeStoreEvent = CreatedFileChangeStoreEvent | ChangedFileChangeStoreEvent | DeletedFileChangeStoreEvent; - function toChangeStoreEvent(operation: Operation | StoredOperation, rootUri: Uri, uri?: Uri): ChangeStoreEvent { return { type: operation.type, rootUri: rootUri, - uri: uri ?? (typeof operation.uri === 'string' ? Uri.parse(operation.uri) : operation.uri) + uri: uri ?? (typeof operation.uri === 'string' ? Uri.parse(operation.uri) : operation.uri), }; } @@ -82,6 +68,8 @@ export interface IChangeStore { discard(uri: Uri): Promise; discardAll(rootUri: Uri): Promise; + hasChanges(rootUri: Uri): boolean; + getChanges(rootUri: Uri): Operation[]; getContent(uri: Uri): string | undefined; @@ -116,9 +104,15 @@ export class ChangeStore implements IChangeStore, IWritableChangeStore { await this.saveWorkingOperations(rootUri, undefined); + const events: ChangeStoreEvent[] = []; + for (const operation of operations) { await this.discardWorkingContent(operation.uri); - this._onDidChange.fire(toChangeStoreEvent(operation, rootUri)); + events.push(toChangeStoreEvent(operation, rootUri)); + } + + for (const e of events) { + this._onDidChange.fire(e); } } @@ -143,7 +137,7 @@ export class ChangeStore implements IChangeStore, IWritableChangeStore { this._onDidChange.fire({ type: operation.type === 'created' ? 'deleted' : operation.type === 'deleted' ? 'created' : 'changed', rootUri: rootUri, - uri: uri + uri: uri, }); } @@ -152,9 +146,15 @@ export class ChangeStore implements IChangeStore, IWritableChangeStore { await this.saveWorkingOperations(rootUri, undefined); + const events: ChangeStoreEvent[] = []; + for (const operation of operations) { await this.discardWorkingContent(operation.uri); - this._onDidChange.fire(toChangeStoreEvent(operation, rootUri)); + events.push(toChangeStoreEvent(operation, rootUri)); + } + + for (const e of events) { + this._onDidChange.fire(e); } } diff --git a/extensions/github-browser/src/contextStore.ts b/extensions/github-browser/src/contextStore.ts index f0f418da36..f53d635dd2 100644 --- a/extensions/github-browser/src/contextStore.ts +++ b/extensions/github-browser/src/contextStore.ts @@ -4,9 +4,13 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { Event, EventEmitter, Memento, Uri } from 'vscode'; +import { Event, EventEmitter, Memento, Uri, workspace } from 'vscode'; -export const contextKeyPrefix = 'github.context|'; +export interface WorkspaceFolderContext { + context: T; + name: string; + folderUri: Uri; +} export class ContextStore { private _onDidChange = new EventEmitter(); @@ -14,23 +18,36 @@ export class ContextStore { return this._onDidChange.event; } - constructor(private readonly memento: Memento, private readonly scheme: string) { } + constructor( + private readonly scheme: string, + private readonly originalScheme: string, + private readonly memento: Memento, + ) { } delete(uri: Uri) { return this.set(uri, undefined); } get(uri: Uri): T | undefined { - return this.memento.get(`${contextKeyPrefix}${uri.toString()}`); + return this.memento.get(`${this.originalScheme}.context|${this.getOriginalResource(uri).toString()}`); } + getForWorkspace(): WorkspaceFolderContext[] { + const folders = workspace.workspaceFolders?.filter(f => f.uri.scheme === this.scheme || f.uri.scheme === this.originalScheme) ?? []; + return folders.map(f => ({ context: this.get(f.uri)!, name: f.name, folderUri: f.uri })).filter(c => c.context !== undefined); + } async set(uri: Uri, context: T | undefined) { - if (uri.scheme !== this.scheme) { - throw new Error(`Invalid context scheme: ${uri.scheme}`); - } - - await this.memento.update(`${contextKeyPrefix}${uri.toString()}`, context); + uri = this.getOriginalResource(uri); + await this.memento.update(`${this.originalScheme}.context|${uri.toString()}`, context); this._onDidChange.fire(uri); } + + getOriginalResource(uri: Uri): Uri { + return uri.with({ scheme: this.originalScheme }); + } + + getWorkspaceResource(uri: Uri): Uri { + return uri.with({ scheme: this.scheme }); + } } diff --git a/extensions/github-browser/src/extension.ts b/extensions/github-browser/src/extension.ts index d0df7d647a..d0f551b0c7 100644 --- a/extensions/github-browser/src/extension.ts +++ b/extensions/github-browser/src/extension.ts @@ -3,48 +3,50 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionContext, Uri, workspace } from 'vscode'; +import { commands, ExtensionContext, Uri, window, workspace } from 'vscode'; import { ChangeStore } from './changeStore'; import { ContextStore } from './contextStore'; import { VirtualFS } from './fs'; import { GitHubApiContext, GitHubApi } from './github/api'; import { GitHubFS } from './github/fs'; import { VirtualSCM } from './scm'; +import { StatusBar } from './statusbar'; -// const repositoryRegex = /^(?:(?:https:\/\/)?github.com\/)?([^\/]+)\/([^\/]+?)(?:\/|.git|$)/i; +const repositoryRegex = /^(?:(?:https:\/\/)?github.com\/)?([^\/]+)\/([^\/]+?)(?:\/|.git|$)/i; -export function activate(context: ExtensionContext) { - const contextStore = new ContextStore(context.workspaceState, GitHubFS.scheme); +export async function activate(context: ExtensionContext) { + const contextStore = new ContextStore('codespace', GitHubFS.scheme, context.workspaceState); const changeStore = new ChangeStore(context.workspaceState); const githubApi = new GitHubApi(contextStore); const gitHubFS = new GitHubFS(githubApi); - const virtualFS = new VirtualFS('codespace', GitHubFS.scheme, contextStore, changeStore, gitHubFS); + const virtualFS = new VirtualFS('codespace', contextStore, changeStore, gitHubFS); context.subscriptions.push( githubApi, gitHubFS, virtualFS, - new VirtualSCM(GitHubFS.scheme, githubApi, changeStore) + new VirtualSCM(GitHubFS.scheme, githubApi, changeStore), + new StatusBar(contextStore, changeStore), ); - // commands.registerCommand('githubBrowser.openRepository', async () => { - // const value = await window.showInputBox({ - // placeHolder: 'e.g. https://github.com/microsoft/vscode', - // prompt: 'Enter a GitHub repository url', - // validateInput: value => repositoryRegex.test(value) ? undefined : 'Invalid repository url' - // }); + commands.registerCommand('githubBrowser.openRepository', async () => { + const value = await window.showInputBox({ + placeHolder: 'e.g. https://github.com/microsoft/vscode', + prompt: 'Enter a GitHub repository url', + validateInput: value => repositoryRegex.test(value) ? undefined : 'Invalid repository url' + }); - // if (value) { - // const match = repositoryRegex.exec(value); - // if (match) { - // const [, owner, repo] = match; + if (value) { + const match = repositoryRegex.exec(value); + if (match) { + const [, owner, repo] = match; - // const uri = Uri.parse(`codespace://HEAD/${owner}/${repo}`); - // openWorkspace(uri, repo, 'currentWindow'); - // } - // } - // }); + const uri = Uri.parse(`codespace://HEAD/${owner}/${repo}`); + openWorkspace(uri, repo, 'currentWindow'); + } + } + }); } export function getRelativePath(rootUri: Uri, uri: Uri) { @@ -63,11 +65,16 @@ export function isDescendent(folderPath: string, filePath: string) { return folderPath.length === 0 || filePath.startsWith(folderPath.endsWith('/') ? folderPath : `${folderPath}/`); } -// function openWorkspace(uri: Uri, name: string, location: 'currentWindow' | 'newWindow' | 'addToCurrentWorkspace') { -// if (location === 'addToCurrentWorkspace') { -// const count = (workspace.workspaceFolders && workspace.workspaceFolders.length) || 0; -// return workspace.updateWorkspaceFolders(count, 0, { uri: uri, name: name }); -// } +const shaRegex = /^[0-9a-f]{40}$/; +export function isSha(ref: string) { + return shaRegex.test(ref); +} -// return commands.executeCommand('vscode.openFolder', uri, location === 'newWindow'); -// } +function openWorkspace(uri: Uri, name: string, location: 'currentWindow' | 'newWindow' | 'addToCurrentWorkspace') { + if (location === 'addToCurrentWorkspace') { + const count = (workspace.workspaceFolders && workspace.workspaceFolders.length) || 0; + return workspace.updateWorkspaceFolders(count, 0, { uri: uri, name: name }); + } + + return commands.executeCommand('vscode.openFolder', uri, location === 'newWindow'); +} diff --git a/extensions/github-browser/src/fs.ts b/extensions/github-browser/src/fs.ts index cb163c85b2..0a0ca250ee 100644 --- a/extensions/github-browser/src/fs.ts +++ b/extensions/github-browser/src/fs.ts @@ -43,26 +43,22 @@ export class VirtualFS implements FileSystemProvider, FileSearchProvider, TextSe constructor( readonly scheme: string, - private readonly originalScheme: string, - contextStore: ContextStore, + private readonly contextStore: ContextStore, private readonly changeStore: IWritableChangeStore, private readonly fs: FileSystemProvider & FileSearchProvider & TextSearchProvider ) { // TODO@eamodio listen for workspace folder changes - for (const folder of workspace.workspaceFolders ?? []) { - const uri = this.getOriginalResource(folder.uri); - + for (const context of contextStore.getForWorkspace()) { // If we have a saved context, but no longer have any changes, reset the context // We only do this on startup/reload to keep things consistent - if (contextStore.get(uri) !== undefined && !changeStore.hasChanges(folder.uri)) { - contextStore.delete(uri); + if (!changeStore.hasChanges(context.folderUri)) { + console.log('Clear context', context.folderUri.toString()); + contextStore.delete(context.folderUri); } } this.disposable = Disposable.from( - workspace.registerFileSystemProvider(scheme, this, { - isCaseSensitive: true, - }), + workspace.registerFileSystemProvider(scheme, this, { isCaseSensitive: true }), workspace.registerFileSearchProvider(scheme, this), workspace.registerTextSearchProvider(scheme, this), changeStore.onDidChange(e => { @@ -86,11 +82,11 @@ export class VirtualFS implements FileSystemProvider, FileSearchProvider, TextSe } private getOriginalResource(uri: Uri): Uri { - return uri.with({ scheme: this.originalScheme }); + return this.contextStore.getOriginalResource(uri); } - private getVirtualResource(uri: Uri): Uri { - return uri.with({ scheme: this.scheme }); + private getWorkspaceResource(uri: Uri): Uri { + return this.contextStore.getWorkspaceResource(uri); } //#region FileSystemProvider @@ -211,7 +207,7 @@ export class VirtualFS implements FileSystemProvider, FileSearchProvider, TextSe return this.fs.provideTextSearchResults( query, { ...options, folder: this.getOriginalResource(options.folder) }, - { report: (result: TextSearchResult) => progress.report({ ...result, uri: this.getVirtualResource(result.uri) }) }, + { report: (result: TextSearchResult) => progress.report({ ...result, uri: this.getWorkspaceResource(result.uri) }) }, token ); } diff --git a/extensions/github-browser/src/github/api.ts b/extensions/github-browser/src/github/api.ts index 7fffb4a6b7..186eb1a223 100644 --- a/extensions/github-browser/src/github/api.ts +++ b/extensions/github-browser/src/github/api.ts @@ -6,14 +6,16 @@ import { authentication, AuthenticationSession, Disposable, Event, EventEmitter, Range, Uri } from 'vscode'; import { graphql } from '@octokit/graphql'; import { Octokit } from '@octokit/rest'; -import { fromGitHubUri } from './fs'; import { ContextStore } from '../contextStore'; +import { fromGitHubUri } from './fs'; +import { isSha } from '../extension'; import { Iterables } from '../iterables'; -export const shaRegex = /^[0-9a-f]{40}$/; - export interface GitHubApiContext { - sha: string; + requestRef: string; + + branch: string; + sha: string | undefined; timestamp: number; } @@ -73,7 +75,7 @@ export class GitHubApi implements Disposable { if (!providers.includes('github')) { await new Promise(resolve => { authentication.onDidChangeAuthenticationProviders(e => { - if (e.added.includes('github')) { + if (e.added.find(provider => provider.id === 'github')) { resolve(); } }); @@ -110,19 +112,12 @@ export class GitHubApi implements Disposable { } async commit(rootUri: Uri, message: string, operations: CommitOperation[]): Promise { - let { owner, repo, ref } = fromGitHubUri(rootUri); + const { owner, repo } = fromGitHubUri(rootUri); try { - if (ref === undefined || ref === 'HEAD') { - ref = await this.defaultBranchQuery(rootUri); - if (ref === undefined) { - throw new Error('Cannot commit — invalid ref'); - } - } - const context = await this.getContext(rootUri); if (context.sha === undefined) { - throw new Error('Cannot commit — invalid context'); + throw new Error(`Cannot commit to Uri(${rootUri.toString(true)}); Invalid context sha`); } const hasDeletes = operations.some(op => op.type === 'deleted'); @@ -204,14 +199,14 @@ export class GitHubApi implements Disposable { parents: [context.sha] }); - this.updateContext(rootUri, { sha: resp.data.sha, timestamp: Date.now() }); + this.updateContext(rootUri, { ...context, sha: resp.data.sha, timestamp: Date.now() }); // TODO@eamodio need to send a file change for any open files await github.git.updateRef({ owner: owner, repo: repo, - ref: `heads/${ref}`, + ref: `heads/${context.branch}`, sha: resp.data.sha }); @@ -256,7 +251,7 @@ export class GitHubApi implements Disposable { owner: owner, repo: repo, recursive: '1', - tree_sha: context?.sha ?? ref ?? 'HEAD', + tree_sha: context?.sha ?? ref, }); return Iterables.filterMap(resp.data.tree, p => p.type === 'blob' ? p.path : undefined); } catch (ex) { @@ -283,7 +278,7 @@ export class GitHubApi implements Disposable { }>(query, { owner: owner, repo: repo, - path: `${context.sha ?? ref ?? 'HEAD'}:${path}`, + path: `${context.sha ?? ref}:${path}`, }); return rsp?.repository?.object ?? undefined; } catch (ex) { @@ -295,7 +290,7 @@ export class GitHubApi implements Disposable { const { owner, repo, ref } = fromGitHubUri(uri); try { - if (ref === undefined || ref === 'HEAD') { + if (ref === 'HEAD') { const query = `query latest($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { defaultBranchRef { @@ -322,6 +317,7 @@ export class GitHubApi implements Disposable { oid } } + } }`; const rsp = await this.gqlQuery<{ @@ -345,7 +341,7 @@ export class GitHubApi implements Disposable { const { owner, repo, ref } = fromGitHubUri(uri); // If we have a specific ref, don't try to search, because GitHub search only works against the default branch - if (ref === undefined) { + if (ref !== 'HEAD') { return { matches: [], limitHit: true }; } @@ -436,29 +432,46 @@ export class GitHubApi implements Disposable { private readonly rootUriToContextMap = new Map(); private async getContextCore(rootUri: Uri): Promise { - let context = this.rootUriToContextMap.get(rootUri.toString()); - if (context === undefined) { - const { ref } = fromGitHubUri(rootUri); - if (ref !== undefined && shaRegex.test(ref)) { - context = { sha: ref, timestamp: Date.now() }; - } else { - context = this.context.get(rootUri); - if (context?.sha === undefined) { - const sha = await this.latestCommitQuery(rootUri); - if (sha !== undefined) { - context = { sha: sha, timestamp: Date.now() }; - } else { - context = undefined; - } - } - } + const key = rootUri.toString(); + let context = this.rootUriToContextMap.get(key); - if (context !== undefined) { - this.updateContext(rootUri, context); - } + // Check if we have a cached a context + if (context?.sha !== undefined) { + return context; } - return context ?? { sha: rootUri.authority, timestamp: Date.now() }; + // Check if we have a saved context + context = this.context.get(rootUri); + if (context?.sha !== undefined) { + this.rootUriToContextMap.set(key, context); + + return context; + } + + const { ref } = fromGitHubUri(rootUri); + + // If the requested ref looks like a sha, then use it + if (isSha(ref)) { + context = { requestRef: ref, branch: ref, sha: ref, timestamp: Date.now() }; + } else { + let branch; + if (ref === 'HEAD') { + branch = await this.defaultBranchQuery(rootUri); + if (branch === undefined) { + throw new Error(`Cannot get context for Uri(${rootUri.toString(true)}); unable to get default branch`); + } + } else { + branch = ref; + } + + // Query for the latest sha for the give ref + const sha = await this.latestCommitQuery(rootUri); + context = { requestRef: ref, branch: branch, sha: sha, timestamp: Date.now() }; + } + + this.updateContext(rootUri, context); + + return context; } private updateContext(rootUri: Uri, context: GitHubApiContext) { diff --git a/extensions/github-browser/src/github/fs.ts b/extensions/github-browser/src/github/fs.ts index 9149a69b3a..389f3cc00e 100644 --- a/extensions/github-browser/src/github/fs.ts +++ b/extensions/github-browser/src/github/fs.ts @@ -299,7 +299,7 @@ function typenameToFileType(typename: string | undefined | null) { } } -type RepoInfo = { owner: string; repo: string; path: string | undefined; ref?: string }; +type RepoInfo = { owner: string; repo: string; path: string | undefined; ref: string }; export function fromGitHubUri(uri: Uri): RepoInfo { const [, owner, repo, ...rest] = uri.path.split('/'); @@ -311,7 +311,7 @@ export function fromGitHubUri(uri: Uri): RepoInfo { ref = 'HEAD'; } } - return { owner: owner, repo: repo, path: rest.join('/'), ref: ref }; + return { owner: owner, repo: repo, path: rest.join('/'), ref: ref ?? 'HEAD' }; } function getHashCode(s: string): number { diff --git a/extensions/github-browser/src/iterables.ts b/extensions/github-browser/src/iterables.ts index 0545b2bd70..502750caf9 100644 --- a/extensions/github-browser/src/iterables.ts +++ b/extensions/github-browser/src/iterables.ts @@ -12,8 +12,9 @@ export namespace Iterables { ): Iterable { for (const item of source) { const mapped = predicateMapper(item); - // eslint-disable-next-line eqeqeq - if (mapped != null) { yield mapped; } + if (mapped !== undefined && mapped !== null) { + yield mapped; + } } } diff --git a/extensions/github-browser/src/scm.ts b/extensions/github-browser/src/scm.ts index f1e039dc2c..ce0519d935 100644 --- a/extensions/github-browser/src/scm.ts +++ b/extensions/github-browser/src/scm.ts @@ -32,17 +32,15 @@ export class VirtualSCM implements Disposable { // TODO@eamodio listen for workspace folder changes for (const folder of workspace.workspaceFolders ?? []) { this.createScmProvider(folder.uri, folder.name); + + for (const operation of changeStore.getChanges(folder.uri)) { + this.update(folder.uri, operation.uri); + } } this.disposable = Disposable.from( changeStore.onDidChange(e => this.update(e.rootUri, e.uri)), ); - - for (const { uri } of workspace.workspaceFolders ?? []) { - for (const operation of changeStore.getChanges(uri)) { - this.update(uri, operation.uri); - } - } } dispose() { @@ -50,7 +48,18 @@ export class VirtualSCM implements Disposable { } private registerCommands() { - commands.registerCommand('githubBrowser.commit', (...args: any[]) => this.commitChanges(args[0])); + commands.registerCommand('githubBrowser.commit', (sourceControl: SourceControl | undefined) => { + // TODO@eamodio remove this hack once I figure out why the args are missing + if (sourceControl === undefined && this.providers.length === 1) { + sourceControl = this.providers[0].sourceControl; + } + + if (sourceControl === undefined) { + return; + } + + this.commitChanges(sourceControl); + }); commands.registerCommand('githubBrowser.discardChanges', (resourceState: SourceControlResourceState) => this.discardChanges(resourceState.resourceUri) diff --git a/extensions/github-browser/src/statusbar.ts b/extensions/github-browser/src/statusbar.ts new file mode 100644 index 0000000000..d949a95f7c --- /dev/null +++ b/extensions/github-browser/src/statusbar.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 { Disposable, StatusBarAlignment, StatusBarItem, Uri, window, workspace } from 'vscode'; +import { ChangeStoreEvent, IChangeStore } from './changeStore'; +import { GitHubApiContext } from './github/api'; +import { isSha } from './extension'; +import { ContextStore, WorkspaceFolderContext } from './contextStore'; + +export class StatusBar implements Disposable { + private readonly disposable: Disposable; + + private readonly items = new Map(); + + constructor( + private readonly contextStore: ContextStore, + private readonly changeStore: IChangeStore + ) { + this.disposable = Disposable.from( + contextStore.onDidChange(this.onContextsChanged, this), + changeStore.onDidChange(this.onChanged, this) + ); + + for (const context of this.contextStore.getForWorkspace()) { + this.createOrUpdateStatusBarItem(context); + } + } + + dispose() { + this.disposable?.dispose(); + this.items.forEach(i => i.dispose()); + } + + private createOrUpdateStatusBarItem(wc: WorkspaceFolderContext) { + let item = this.items.get(wc.folderUri.toString()); + if (item === undefined) { + item = window.createStatusBarItem({ + id: `githubBrowser.branch:${wc.folderUri.toString()}`, + name: `GitHub Browser: ${wc.name}`, + alignment: StatusBarAlignment.Left, + priority: 1000 + }); + } + + if (isSha(wc.context.branch)) { + item.text = `$(git-commit) ${wc.context.branch.substr(0, 8)}`; + item.tooltip = `${wc.name} \u2022 ${wc.context.branch.substr(0, 8)}`; + } else { + item.text = `$(git-branch) ${wc.context.branch}`; + item.tooltip = `${wc.name} \u2022 ${wc.context.branch}${wc.context.sha ? ` @ ${wc.context.sha?.substr(0, 8)}` : ''}`; + } + + const hasChanges = this.changeStore.hasChanges(wc.folderUri); + if (hasChanges) { + item.text += '*'; + } + + item.show(); + + this.items.set(wc.folderUri.toString(), item); + } + + private onContextsChanged(uri: Uri) { + const folder = workspace.getWorkspaceFolder(this.contextStore.getWorkspaceResource(uri)); + if (folder === undefined) { + return; + } + + const context = this.contextStore.get(uri); + if (context === undefined) { + return; + } + + this.createOrUpdateStatusBarItem({ + context: context, + name: folder.name, + folderUri: folder.uri, + }); + } + + private onChanged(e: ChangeStoreEvent) { + const item = this.items.get(e.rootUri.toString()); + if (item !== undefined) { + const hasChanges = this.changeStore.hasChanges(e.rootUri); + if (hasChanges) { + if (!item.text.endsWith('*')) { + item.text += '*'; + } + } else { + if (item.text.endsWith('*')) { + item.text = item.text.substr(0, item.text.length - 1); + } + } + } + } +} diff --git a/extensions/github/src/commands.ts b/extensions/github/src/commands.ts index 1a3fd851c0..1baf1a85d3 100644 --- a/extensions/github/src/commands.ts +++ b/extensions/github/src/commands.ts @@ -6,9 +6,10 @@ import * as vscode from 'vscode'; import { API as GitAPI } from './typings/git'; import { publishRepository } from './publish'; +import { combinedDisposable } from './util'; -export function registerCommands(gitAPI: GitAPI): vscode.Disposable[] { - const disposables = []; +export function registerCommands(gitAPI: GitAPI): vscode.Disposable { + const disposables: vscode.Disposable[] = []; disposables.push(vscode.commands.registerCommand('github.publish', async () => { try { @@ -18,5 +19,5 @@ export function registerCommands(gitAPI: GitAPI): vscode.Disposable[] { } })); - return disposables; + return combinedDisposable(disposables); } diff --git a/extensions/github/src/extension.ts b/extensions/github/src/extension.ts index ae43f9177f..982468853a 100644 --- a/extensions/github/src/extension.ts +++ b/extensions/github/src/extension.ts @@ -3,23 +3,43 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; +import { Disposable, ExtensionContext, extensions } from 'vscode'; import { GithubRemoteSourceProvider } from './remoteSourceProvider'; import { GitExtension } from './typings/git'; import { registerCommands } from './commands'; import { GithubCredentialProviderManager } from './credentialProvider'; +import { dispose, combinedDisposable } from './util'; +import { GithubPushErrorHandler } from './pushErrorHandler'; -export async function activate(context: vscode.ExtensionContext) { - const gitExtension = vscode.extensions.getExtension('vscode.git')!.exports; +export function activate(context: ExtensionContext): void { + const disposables = new Set(); + context.subscriptions.push(combinedDisposable(disposables)); - try { - const gitAPI = gitExtension.getAPI(1); + const init = () => { + try { + const gitAPI = gitExtension.getAPI(1); - context.subscriptions.push(...registerCommands(gitAPI)); - context.subscriptions.push(gitAPI.registerRemoteSourceProvider(new GithubRemoteSourceProvider(gitAPI))); - context.subscriptions.push(new GithubCredentialProviderManager(gitAPI)); - } catch (err) { - console.error('Could not initialize GitHub extension'); - console.warn(err); - } + disposables.add(registerCommands(gitAPI)); + disposables.add(gitAPI.registerRemoteSourceProvider(new GithubRemoteSourceProvider(gitAPI))); + disposables.add(new GithubCredentialProviderManager(gitAPI)); + disposables.add(gitAPI.registerPushErrorHandler(new GithubPushErrorHandler())); + } catch (err) { + console.error('Could not initialize GitHub extension'); + console.warn(err); + } + }; + + const onDidChangeGitExtensionEnablement = (enabled: boolean) => { + if (!enabled) { + dispose(disposables); + disposables.clear(); + } else { + init(); + } + }; + + + const gitExtension = extensions.getExtension('vscode.git')!.exports; + context.subscriptions.push(gitExtension.onDidChangeEnablement(onDidChangeGitExtensionEnablement)); + onDidChangeGitExtensionEnablement(gitExtension.enabled); } diff --git a/extensions/github/src/publish.ts b/extensions/github/src/publish.ts index fdb3bd90e9..d9ee2cff3b 100644 --- a/extensions/github/src/publish.ts +++ b/extensions/github/src/publish.ts @@ -5,10 +5,10 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as path from 'path'; -import { promises as fs } from 'fs'; import { API as GitAPI, Repository } from './typings/git'; import { getOctokit } from './auth'; +import { TextEncoder } from 'util'; +import { basename } from 'path'; const localize = nls.loadMessageBundle(); @@ -28,10 +28,12 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) return; } - let folder: vscode.WorkspaceFolder; + let folder: vscode.Uri; - if (vscode.workspace.workspaceFolders.length === 1) { - folder = vscode.workspace.workspaceFolders[0]; + if (repository) { + folder = repository.rootUri; + } else if (vscode.workspace.workspaceFolders.length === 1) { + folder = vscode.workspace.workspaceFolders[0].uri; } else { const picks = vscode.workspace.workspaceFolders.map(folder => ({ label: folder.name, folder })); const placeHolder = localize('pick folder', "Pick a folder to publish to GitHub"); @@ -41,14 +43,14 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) return; } - folder = pick.folder; + folder = pick.folder.uri; } let quickpick = vscode.window.createQuickPick(); quickpick.ignoreFocusOut = true; quickpick.placeholder = 'Repository Name'; - quickpick.value = folder.name; + quickpick.value = basename(folder.fsPath); quickpick.show(); quickpick.busy = true; @@ -97,37 +99,49 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) return; } - quickpick = vscode.window.createQuickPick(); - quickpick.placeholder = localize('ignore', "Select which files should be included in the repository."); - quickpick.canSelectMany = true; - quickpick.show(); + if (!repository) { + const gitignore = vscode.Uri.joinPath(folder, '.gitignore'); + let shouldGenerateGitignore = false; - try { - quickpick.busy = true; - - const repositoryPath = folder.uri.fsPath; - const currentPath = path.join(repositoryPath); - const children = await fs.readdir(currentPath); - quickpick.items = children.map(name => ({ label: name })); - quickpick.selectedItems = quickpick.items; - quickpick.busy = false; - - const result = await Promise.race([ - new Promise(c => quickpick.onDidAccept(() => c(quickpick.selectedItems))), - new Promise(c => quickpick.onDidHide(() => c(undefined))) - ]); - - if (!result) { - return; + try { + await vscode.workspace.fs.stat(gitignore); + } catch (err) { + shouldGenerateGitignore = true; } - const ignored = new Set(children); - result.forEach(c => ignored.delete(c.label)); + if (shouldGenerateGitignore) { + quickpick = vscode.window.createQuickPick(); + quickpick.placeholder = localize('ignore', "Select which files should be included in the repository."); + quickpick.canSelectMany = true; + quickpick.show(); - const raw = [...ignored].map(i => `/${i}`).join('\n'); - await fs.writeFile(path.join(repositoryPath, '.gitignore'), raw, 'utf8'); - } finally { - quickpick.dispose(); + try { + quickpick.busy = true; + + const children = (await vscode.workspace.fs.readDirectory(folder)).map(([name]) => name); + quickpick.items = children.map(name => ({ label: name })); + quickpick.selectedItems = quickpick.items; + quickpick.busy = false; + + const result = await Promise.race([ + new Promise(c => quickpick.onDidAccept(() => c(quickpick.selectedItems))), + new Promise(c => quickpick.onDidHide(() => c(undefined))) + ]); + + if (!result) { + return; + } + + const ignored = new Set(children); + result.forEach(c => ignored.delete(c.label)); + + const raw = [...ignored].map(i => `/${i}`).join('\n'); + const encoder = new TextEncoder(); + await vscode.workspace.fs.writeFile(gitignore, encoder.encode(raw)); + } finally { + quickpick.dispose(); + } + } } const githubRepository = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, cancellable: false, title: 'Publish to GitHub' }, async progress => { @@ -143,7 +157,7 @@ export async function publishRepository(gitAPI: GitAPI, repository?: Repository) progress.report({ message: 'Creating first commit', increment: 25 }); if (!repository) { - repository = await gitAPI.init(folder.uri) || undefined; + repository = await gitAPI.init(folder) || undefined; if (!repository) { return; diff --git a/extensions/github/src/pushErrorHandler.ts b/extensions/github/src/pushErrorHandler.ts new file mode 100644 index 0000000000..bc585d18b6 --- /dev/null +++ b/extensions/github/src/pushErrorHandler.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. + *--------------------------------------------------------------------------------------------*/ + +import { PushErrorHandler, GitErrorCodes, Repository, Remote } from './typings/git'; +import { window, ProgressLocation, commands, Uri } from 'vscode'; +import * as nls from 'vscode-nls'; +import { getOctokit } from './auth'; + +const localize = nls.loadMessageBundle(); + +async function handlePushError(repository: Repository, remote: Remote, refspec: string, owner: string, repo: string): Promise { + const yes = localize('create a fork', "Create Fork"); + const no = localize('no', "No"); + + const answer = await window.showInformationMessage(localize('fork', "You don't have permissions to push to '{0}/{1}' on GitHub. Would you like to create a fork and push to it instead?", owner, repo), yes, no); + + if (answer === no) { + return; + } + + const match = /^([^:]*):([^:]*)$/.exec(refspec); + const localName = match ? match[1] : refspec; + const remoteName = match ? match[2] : refspec; + + const [octokit, ghRepository] = await window.withProgress({ location: ProgressLocation.Notification, cancellable: false, title: localize('create fork', 'Create GitHub fork') }, async progress => { + progress.report({ message: localize('forking', "Forking '{0}/{1}'...", owner, repo), increment: 33 }); + + const octokit = await getOctokit(); + + // Issue: what if the repo already exists? + const res = await octokit.repos.createFork({ owner, repo }); + const ghRepository = res.data; + + progress.report({ message: localize('pushing', "Pushing changes..."), increment: 33 }); + + // Issue: what if there's already an `upstream` repo? + await repository.renameRemote(remote.name, 'upstream'); + + // Issue: what if there's already another `origin` repo? + await repository.addRemote('origin', ghRepository.clone_url); + await repository.fetch('origin', remoteName); + await repository.setBranchUpstream(localName, `origin/${remoteName}`); + await repository.push('origin', localName, true); + + return [octokit, ghRepository]; + }); + + // yield + (async () => { + const openInGitHub = localize('openingithub', "Open In GitHub"); + const createPR = localize('createpr', "Create PR"); + const action = await window.showInformationMessage(localize('done', "The fork '{0}' was successfully created on GitHub.", ghRepository.full_name), openInGitHub, createPR); + + if (action === openInGitHub) { + await commands.executeCommand('vscode.open', Uri.parse(ghRepository.html_url)); + } else if (action === createPR) { + const pr = await window.withProgress({ location: ProgressLocation.Notification, cancellable: false, title: localize('createghpr', "Creating GitHub Pull Request...") }, async _ => { + let title = `Update ${remoteName}`; + const head = repository.state.HEAD?.name; + + if (head) { + const commit = await repository.getCommit(head); + title = commit.message.replace(/\n.*$/m, ''); + } + + const res = await octokit.pulls.create({ + owner, + repo, + title, + head: `${ghRepository.owner.login}:${remoteName}`, + base: remoteName + }); + + await repository.setConfig(`branch.${localName}.remote`, 'upstream'); + await repository.setConfig(`branch.${localName}.merge`, `refs/heads/${remoteName}`); + await repository.setConfig(`branch.${localName}.github-pr-owner-number`, `${owner}#${repo}#${pr.number}`); + + return res.data; + }); + + const openPR = localize('openpr', "Open PR"); + const action = await window.showInformationMessage(localize('donepr', "The PR '{0}/{1}#{2}' was successfully created on GitHub.", owner, repo, pr.number), openPR); + + if (action === openPR) { + await commands.executeCommand('vscode.open', Uri.parse(pr.html_url)); + } + } + })(); +} + +export class GithubPushErrorHandler implements PushErrorHandler { + + async handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { gitErrorCode: GitErrorCodes }): Promise { + if (error.gitErrorCode !== GitErrorCodes.PermissionDenied) { + return false; + } + + if (!remote.pushUrl) { + return false; + } + + const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\.git/i.exec(remote.pushUrl) + || /^git@github\.com:([^/]+)\/([^/]+)\.git/i.exec(remote.pushUrl); + + if (!match) { + return false; + } + + if (/^:/.test(refspec)) { + return false; + } + + const [, owner, repo] = match; + await handlePushError(repository, remote, refspec, owner, repo); + + return true; + } +} diff --git a/extensions/github/src/typings/git.d.ts b/extensions/github/src/typings/git.d.ts index 2a8d1cdf9b..9593d8bc70 100644 --- a/extensions/github/src/typings/git.d.ts +++ b/extensions/github/src/typings/git.d.ts @@ -134,6 +134,8 @@ export interface CommitOptions { export interface BranchQuery { readonly remote?: boolean; + readonly pattern?: string; + readonly count?: number; readonly contains?: string; } @@ -221,6 +223,10 @@ export interface CredentialsProvider { getCredentials(host: Uri): ProviderResult; } +export interface PushErrorHandler { + handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { gitErrorCode: GitErrorCodes }): Promise; +} + export type APIState = 'uninitialized' | 'initialized'; export interface API { @@ -237,6 +243,7 @@ export interface API { registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; registerCredentialsProvider(provider: CredentialsProvider): Disposable; + registerPushErrorHandler(handler: PushErrorHandler): Disposable; } export interface GitExtension { @@ -274,6 +281,7 @@ export const enum GitErrorCodes { CantOpenResource = 'CantOpenResource', GitNotFound = 'GitNotFound', CantCreatePipe = 'CantCreatePipe', + PermissionDenied = 'PermissionDenied', CantAccessRemote = 'CantAccessRemote', RepositoryNotFound = 'RepositoryNotFound', RepositoryIsLocked = 'RepositoryIsLocked', diff --git a/extensions/github/src/util.ts b/extensions/github/src/util.ts new file mode 100644 index 0000000000..982ad7f07b --- /dev/null +++ b/extensions/github/src/util.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 * as vscode from 'vscode'; + +export function dispose(arg: vscode.Disposable | Iterable): void { + if (arg instanceof vscode.Disposable) { + arg.dispose(); + } else { + for (const disposable of arg) { + disposable.dispose(); + } + } +} + +export function combinedDisposable(disposables: Iterable): vscode.Disposable { + return { + dispose() { + dispose(disposables); + } + }; +} diff --git a/extensions/json/package.json b/extensions/json/package.json index af713956bf..73902a8ec4 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -48,14 +48,14 @@ "JSON with Comments" ], "extensions": [ - ".hintrc", - ".babelrc", ".jsonc", ".eslintrc", ".eslintrc.json", ".jsfmtrc", ".jshintrc", - ".swcrc" + ".swcrc", + ".hintrc", + ".babelrc" ], "configuration": "./language-configuration.json" } diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 0df87e91d3..e8feaa75fa 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "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-markdown-tm-grammar/commit/a7e4475626a505472c76d18e0a1b3cfcf46f9cf9", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/4be9cb335581f3559166c319607dac9100103083", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -1963,12 +1963,12 @@ "name": "markup.fenced_code.block.markdown" }, "heading": { - "match": "(?:^|\\G)[ ]{0,3}((#{1,6})\\s+(?=[\\S[^#]]).*?\\s*(#{1,6})?)$\\n?", + "match": "(?:^|\\G)[ ]{0,3}(#{1,6}\\s+(.*?)(\\s+#{1,6})?\\s*)$", "captures": { "1": { "patterns": [ { - "match": "(#{6})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{6})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.6.markdown", "captures": { "1": { @@ -1983,7 +1983,7 @@ } }, { - "match": "(#{5})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{5})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.5.markdown", "captures": { "1": { @@ -1998,7 +1998,7 @@ } }, { - "match": "(#{4})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{4})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.4.markdown", "captures": { "1": { @@ -2013,7 +2013,7 @@ } }, { - "match": "(#{3})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{3})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.3.markdown", "captures": { "1": { @@ -2028,7 +2028,7 @@ } }, { - "match": "(#{2})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{2})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.2.markdown", "captures": { "1": { @@ -2043,7 +2043,7 @@ } }, { - "match": "(#{1})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{1})\\s+(.*?)(?:\\s+(#+))?\\s*$", "name": "heading.1.markdown", "captures": { "1": { diff --git a/extensions/markdown-basics/test/colorize-results/test_md.json b/extensions/markdown-basics/test/colorize-results/test_md.json index 6963f66e79..1fee128741 100644 --- a/extensions/markdown-basics/test/colorize-results/test_md.json +++ b/extensions/markdown-basics/test/colorize-results/test_md.json @@ -33,7 +33,18 @@ } }, { - "c": " #", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.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": "#", "t": "text.html.markdown markup.heading.markdown heading.1.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", @@ -77,7 +88,18 @@ } }, { - "c": " ##", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.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 markup.heading.markdown heading.2.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", @@ -2189,7 +2211,18 @@ } }, { - "c": " ##", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.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 markup.heading.markdown heading.2.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", @@ -2343,7 +2376,18 @@ } }, { - "c": " ##", + "c": " ", + "t": "text.html.markdown markup.heading.markdown heading.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 markup.heading.markdown heading.2.markdown punctuation.definition.heading.markdown", "r": { "dark_plus": "markup.heading: #569CD6", diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index 64414ab281..c10a0198f2 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -11,6 +11,21 @@ html, body { word-wrap: break-word; } +body { + padding-top: 1em; +} + +/* Reset margin top for elements */ +h1, h2, h3, h4, h5, h6, +p, ol, ul, pre { + margin-top: 0; +} + +h2, h3, h4, h5, h6 { + font-weight: normal; + margin-bottom: 0.2em; +} + #code-csp-warning { position: fixed; top: 0; @@ -112,6 +127,20 @@ textarea:focus { outline-offset: -1px; } +p { + margin-bottom: 1.5em; +} + +/* don't space 2 paragraphs too far apart */ +p + p { + margin-top: -0.8em; +} + +ul, +ol { + margin-bottom: 1.5em; +} + hr { border: 0; height: 2px; @@ -123,9 +152,6 @@ h1 { line-height: 1.2; border-bottom-width: 1px; border-bottom-style: solid; -} - -h1, h2, h3 { font-weight: normal; } diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index becb8ad271..0cdddf075d 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -79,7 +79,7 @@ "editor/title": [ { "command": "markdown.showPreviewToSide", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "alt": "markdown.showPreview", "group": "navigation" }, @@ -115,23 +115,23 @@ { "command": "markdown.showPreview", "when": "resourceLangId == markdown", - "group": "navigation" + "group": "1_open" } ], "commandPalette": [ { "command": "markdown.showPreview", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "group": "navigation" }, { "command": "markdown.showPreviewToSide", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "group": "navigation" }, { "command": "markdown.showLockedPreviewToSide", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "group": "navigation" }, { @@ -141,7 +141,7 @@ }, { "command": "markdown.showPreviewSecuritySelector", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.showPreviewSecuritySelector", @@ -153,7 +153,7 @@ }, { "command": "markdown.preview.refresh", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.preview.refresh", @@ -166,13 +166,13 @@ "command": "markdown.showPreview", "key": "shift+ctrl+v", "mac": "shift+cmd+v", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.showPreviewToSide", "key": "ctrl+k v", "mac": "cmd+k v", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" } ], "configuration": { diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index 7bc765259b..8b9e50c85d 100644 --- a/extensions/markdown-language-features/src/commands/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/commands/openDocumentLink.ts @@ -13,9 +13,9 @@ import { isMarkdownFile } from '../util/file'; export interface OpenDocumentLinkArgs { - readonly path: string; + readonly path: {}; readonly fragment: string; - readonly fromResource: any; + readonly fromResource: {}; } enum OpenMarkdownLinks { @@ -29,13 +29,22 @@ export class OpenDocumentLinkCommand implements Command { public static createCommandUri( fromResource: vscode.Uri, - path: string, + path: vscode.Uri, fragment: string, ): vscode.Uri { + const toJson = (uri: vscode.Uri) => { + return { + scheme: uri.scheme, + authority: uri.authority, + path: uri.path, + fragment: uri.fragment, + query: uri.query, + }; + }; return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ - path: encodeURIComponent(path), + path: toJson(path), fragment, - fromResource: encodeURIComponent(fromResource.toString(true)), + fromResource: toJson(fromResource), }))}`); } @@ -43,26 +52,29 @@ export class OpenDocumentLinkCommand implements Command { private readonly engine: MarkdownEngine ) { } - public execute(args: OpenDocumentLinkArgs) { - const fromResource = vscode.Uri.parse(decodeURIComponent(args.fromResource)); - const targetPath = decodeURIComponent(args.path); - const column = this.getViewColumn(fromResource); - return this.tryOpen(targetPath, args, column).catch(() => { - if (targetPath && extname(targetPath) === '') { - return this.tryOpen(targetPath + '.md', args, column); - } - const targetResource = vscode.Uri.file(targetPath); - return Promise.resolve(undefined) - .then(() => vscode.commands.executeCommand('vscode.open', targetResource, column)) - .then(() => undefined); - }); + public async execute(args: OpenDocumentLinkArgs) { + return OpenDocumentLinkCommand.execute(this.engine, args); } - private async tryOpen(path: string, args: OpenDocumentLinkArgs, column: vscode.ViewColumn) { - const resource = vscode.Uri.file(path); + public static async execute(engine: MarkdownEngine, args: OpenDocumentLinkArgs) { + const fromResource = vscode.Uri.parse('').with(args.fromResource); + const targetResource = vscode.Uri.parse('').with(args.path); + const column = this.getViewColumn(fromResource); + try { + return await this.tryOpen(engine, targetResource, args, column); + } catch { + if (extname(targetResource.path) === '') { + return this.tryOpen(engine, targetResource.with({ path: targetResource.path + '.md' }), args, column); + } + await vscode.commands.executeCommand('vscode.open', targetResource, column); + return undefined; + } + } + + private static async tryOpen(engine: MarkdownEngine, resource: vscode.Uri, args: OpenDocumentLinkArgs, column: vscode.ViewColumn) { if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) { - if (!path || vscode.window.activeTextEditor.document.uri.fsPath === resource.fsPath) { - return this.tryRevealLine(vscode.window.activeTextEditor, args.fragment); + if (vscode.window.activeTextEditor.document.uri.fsPath === resource.fsPath) { + return this.tryRevealLine(engine, vscode.window.activeTextEditor, args.fragment); } } @@ -73,10 +85,10 @@ export class OpenDocumentLinkCommand implements Command { return vscode.workspace.openTextDocument(resource) .then(document => vscode.window.showTextDocument(document, column)) - .then(editor => this.tryRevealLine(editor, args.fragment)); + .then(editor => this.tryRevealLine(engine, editor, args.fragment)); } - private getViewColumn(resource: vscode.Uri): vscode.ViewColumn { + private static getViewColumn(resource: vscode.Uri): vscode.ViewColumn { const config = vscode.workspace.getConfiguration('markdown', resource); const openLinks = config.get('links.openLocation', OpenMarkdownLinks.currentGroup); switch (openLinks) { @@ -88,18 +100,22 @@ export class OpenDocumentLinkCommand implements Command { } } - private async tryRevealLine(editor: vscode.TextEditor, fragment?: string) { + private static async tryRevealLine(engine: MarkdownEngine, editor: vscode.TextEditor, fragment?: string) { if (editor && fragment) { - const toc = new TableOfContentsProvider(this.engine, editor.document); + const toc = new TableOfContentsProvider(engine, editor.document); const entry = await toc.lookup(fragment); if (entry) { - return editor.revealRange(new vscode.Range(entry.line, 0, entry.line, 0), vscode.TextEditorRevealType.AtTop); + const lineStart = new vscode.Range(entry.line, 0, entry.line, 0); + editor.selection = new vscode.Selection(lineStart.start, lineStart.end); + return editor.revealRange(lineStart, vscode.TextEditorRevealType.AtTop); } const lineNumberFragment = fragment.match(/^L(\d+)$/i); if (lineNumberFragment) { const line = +lineNumberFragment[1] - 1; if (!isNaN(line)) { - return editor.revealRange(new vscode.Range(line, 0, line, 0), vscode.TextEditorRevealType.AtTop); + const lineStart = new vscode.Range(line, 0, line, 0); + editor.selection = new vscode.Selection(lineStart.start, lineStart.end); + return editor.revealRange(lineStart, vscode.TextEditorRevealType.AtTop); } } } diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 9c3f4b7f81..0317d61698 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -15,9 +15,9 @@ import MarkdownWorkspaceSymbolProvider from './features/workspaceSymbolProvider' import { Logger } from './logger'; import { MarkdownEngine } from './markdownEngine'; import { getMarkdownExtensionContributions } from './markdownExtensions'; -import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector, ContentSecurityPolicyArbiter } from './security'; -import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter'; +import { ContentSecurityPolicyArbiter, ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security'; import { githubSlugifier } from './slugify'; +import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter'; export function activate(context: vscode.ExtensionContext) { @@ -33,7 +33,7 @@ export function activate(context: vscode.ExtensionContext) { const contentProvider = new MarkdownContentProvider(engine, context, cspArbiter, contributions, logger); const symbolProvider = new MDDocumentSymbolProvider(engine); - const previewManager = new MarkdownPreviewManager(contentProvider, logger, contributions); + const previewManager = new MarkdownPreviewManager(contentProvider, logger, contributions, engine); context.subscriptions.push(previewManager); context.subscriptions.push(registerMarkdownLanguageFeatures(symbolProvider, engine)); diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index e601c59b84..346d9e25ab 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -14,8 +14,7 @@ const localize = nls.loadMessageBundle(); function parseLink( document: vscode.TextDocument, link: string, - base: string -): { uri: vscode.Uri, tooltip?: string } { +): { uri: vscode.Uri, tooltip?: string } | undefined { const externalSchemeUri = getUriForLinkWithKnownExternalScheme(link); if (externalSchemeUri) { // Normalize VS Code links to target currently running version @@ -29,24 +28,43 @@ function parseLink( // Use a fake scheme to avoid parse warnings const tempUri = vscode.Uri.parse(`vscode-resource:${link}`); - let resourcePath = tempUri.path; - if (!tempUri.path && document.uri.scheme === 'file') { - resourcePath = document.uri.path; + let resourceUri: vscode.Uri | undefined; + if (!tempUri.path) { + resourceUri = document.uri; } else if (tempUri.path[0] === '/') { - const root = vscode.workspace.getWorkspaceFolder(document.uri); + const root = getWorkspaceFolder(document); if (root) { - resourcePath = path.join(root.uri.fsPath, tempUri.path); + resourceUri = vscode.Uri.joinPath(root, tempUri.path); } } else { - resourcePath = base ? path.join(base, tempUri.path) : tempUri.path; + if (document.uri.scheme === Schemes.untitled) { + const root = getWorkspaceFolder(document); + if (root) { + resourceUri = vscode.Uri.joinPath(root, tempUri.path); + } + } else { + const base = document.uri.with({ path: path.dirname(document.uri.fsPath) }); + resourceUri = vscode.Uri.joinPath(base, tempUri.path); + } } + if (!resourceUri) { + return undefined; + } + + resourceUri = resourceUri.with({ fragment: tempUri.fragment }); + return { - uri: OpenDocumentLinkCommand.createCommandUri(document.uri, resourcePath, tempUri.fragment), + uri: OpenDocumentLinkCommand.createCommandUri(document.uri, resourceUri, tempUri.fragment), tooltip: localize('documentLink.tooltip', 'Follow link') }; } +function getWorkspaceFolder(document: vscode.TextDocument) { + return vscode.workspace.getWorkspaceFolder(document.uri)?.uri + || vscode.workspace.workspaceFolders?.[0]?.uri; +} + function matchAll( pattern: RegExp, text: string @@ -62,7 +80,6 @@ function matchAll( function extractDocumentLink( document: vscode.TextDocument, - base: string, pre: number, link: string, matchIndex: number | undefined @@ -71,11 +88,14 @@ function extractDocumentLink( const linkStart = document.positionAt(offset); const linkEnd = document.positionAt(offset + link.length); try { - const { uri, tooltip } = parseLink(document, link, base); + const linkData = parseLink(document, link); + if (!linkData) { + return undefined; + } const documentLink = new vscode.DocumentLink( new vscode.Range(linkStart, linkEnd), - uri); - documentLink.tooltip = tooltip; + linkData.uri); + documentLink.tooltip = linkData.tooltip; return documentLink; } catch (e) { return undefined; @@ -91,27 +111,25 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { document: vscode.TextDocument, _token: vscode.CancellationToken ): vscode.DocumentLink[] { - const base = document.uri.scheme === 'file' ? path.dirname(document.uri.fsPath) : ''; const text = document.getText(); return [ - ...this.providerInlineLinks(text, document, base), - ...this.provideReferenceLinks(text, document, base) + ...this.providerInlineLinks(text, document), + ...this.provideReferenceLinks(text, document) ]; } private providerInlineLinks( text: string, document: vscode.TextDocument, - base: string ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { - const matchImage = match[4] && extractDocumentLink(document, base, match[3].length + 1, match[4], match.index); + const matchImage = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index); if (matchImage) { results.push(matchImage); } - const matchLink = extractDocumentLink(document, base, match[1].length, match[5], match.index); + const matchLink = extractDocumentLink(document, match[1].length, match[5], match.index); if (matchLink) { results.push(matchLink); } @@ -122,7 +140,6 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { private provideReferenceLinks( text: string, document: vscode.TextDocument, - base: string ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; @@ -159,8 +176,10 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { for (const definition of definitions.values()) { try { - const { uri } = parseLink(document, definition.link, base); - results.push(new vscode.DocumentLink(definition.linkRange, uri)); + const linkData = parseLink(document, definition.link); + if (linkData) { + results.push(new vscode.DocumentLink(definition.linkRange, linkData.uri)); + } } catch (e) { // noop } diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 13adb641a6..54296ad5f0 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -3,20 +3,20 @@ * 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 { Logger } from '../logger'; -import { MarkdownContentProvider } from './previewContentProvider'; -import { Disposable } from '../util/dispose'; - +import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import { OpenDocumentLinkCommand, resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; +import { Logger } from '../logger'; +import { MarkdownContributionProvider } from '../markdownExtensions'; +import { Disposable } from '../util/dispose'; +import { isMarkdownFile } from '../util/file'; +import { normalizeResource, WebviewResourceProvider } from '../util/resources'; import { getVisibleLine, TopmostLineMonitor } from '../util/topmostLineMonitor'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; -import { MarkdownContributionProvider } from '../markdownExtensions'; -import { isMarkdownFile } from '../util/file'; -import { resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; -import { WebviewResourceProvider, normalizeResource } from '../util/resources'; +import { MarkdownContentProvider } from './previewContentProvider'; +import { MarkdownEngine } from '../markdownEngine'; + const localize = nls.loadMessageBundle(); interface WebviewMessage { @@ -123,6 +123,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { resource: vscode.Uri, startingScroll: StartingScrollLocation | undefined, private readonly delegate: MarkdownPreviewDelegate, + private readonly engine: MarkdownEngine, private readonly _contentProvider: MarkdownContentProvider, private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, private readonly _logger: Logger, @@ -407,7 +408,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { } } - vscode.commands.executeCommand('_markdown.openDocumentLink', { path: hrefPath, fragment, fromResource: this.resource }); + OpenDocumentLinkCommand.execute(this.engine, { path: hrefPath, fragment, fromResource: this.resource.toJSON() }); } //#region WebviewResourceProvider @@ -452,8 +453,9 @@ export class StaticMarkdownPreview extends Disposable implements ManagedMarkdown previewConfigurations: MarkdownPreviewConfigurationManager, logger: Logger, contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, ): StaticMarkdownPreview { - return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, logger, contributionProvider); + return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, logger, contributionProvider, engine); } private readonly preview: MarkdownPreview; @@ -465,13 +467,14 @@ export class StaticMarkdownPreview extends Disposable implements ManagedMarkdown private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, logger: Logger, contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, ) { super(); this.preview = this._register(new MarkdownPreview(this._webviewPanel, resource, undefined, { getAdditionalState: () => { return {}; }, openPreviewLinkToMarkdownFile: () => { /* todo */ } - }, contentProvider, _previewConfigurations, logger, contributionProvider)); + }, engine, contentProvider, _previewConfigurations, logger, contributionProvider)); this._register(this._webviewPanel.onDidDispose(() => { this.dispose(); @@ -548,9 +551,10 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow logger: Logger, topmostLineMonitor: TopmostLineMonitor, contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, ): DynamicMarkdownPreview { return new DynamicMarkdownPreview(webview, input, - contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider); + contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider, engine); } public static create( @@ -560,7 +564,8 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow previewConfigurations: MarkdownPreviewConfigurationManager, logger: Logger, topmostLineMonitor: TopmostLineMonitor, - contributionProvider: MarkdownContributionProvider + contributionProvider: MarkdownContributionProvider, + engine: MarkdownEngine, ): DynamicMarkdownPreview { const webview = vscode.window.createWebviewPanel( DynamicMarkdownPreview.viewType, @@ -568,7 +573,7 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow previewColumn, { enableFindWidget: true, }); return new DynamicMarkdownPreview(webview, input, - contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider); + contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider, engine); } private constructor( @@ -579,6 +584,7 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow private readonly _logger: Logger, private readonly _topmostLineMonitor: TopmostLineMonitor, private readonly _contributionProvider: MarkdownContributionProvider, + private readonly _engine: MarkdownEngine, ) { super(); @@ -612,7 +618,12 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow })); this._register(vscode.window.onDidChangeActiveTextEditor(editor => { - if (editor && isMarkdownFile(editor.document) && !this._locked && !this._preview.isPreviewOf(editor.document.uri)) { + // Only allow previewing normal text editors which have a viewColumn: See #101514 + if (typeof editor?.viewColumn === 'undefined') { + return; + } + + if (isMarkdownFile(editor.document) && !this._locked && !this._preview.isPreviewOf(editor.document.uri)) { const line = getVisibleLine(editor); this.update(editor.document.uri, line ? new StartingScrollLine(line) : undefined); } @@ -724,6 +735,7 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow this.update(link, fragment ? new StartingScrollFragment(fragment) : undefined); } }, + this._engine, this._contentProvider, this._previewConfigurations, this._logger, diff --git a/extensions/markdown-language-features/src/features/previewManager.ts b/extensions/markdown-language-features/src/features/previewManager.ts index c830d0ca12..77d8ef2bc4 100644 --- a/extensions/markdown-language-features/src/features/previewManager.ts +++ b/extensions/markdown-language-features/src/features/previewManager.ts @@ -5,10 +5,11 @@ import * as vscode from 'vscode'; import { Logger } from '../logger'; +import { MarkdownEngine } from '../markdownEngine'; import { MarkdownContributionProvider } from '../markdownExtensions'; -import { disposeAll, Disposable } from '../util/dispose'; +import { Disposable, disposeAll } from '../util/dispose'; import { TopmostLineMonitor } from '../util/topmostLineMonitor'; -import { DynamicMarkdownPreview, StaticMarkdownPreview, ManagedMarkdownPreview } from './preview'; +import { DynamicMarkdownPreview, ManagedMarkdownPreview, StaticMarkdownPreview } from './preview'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; import { MarkdownContentProvider } from './previewContentProvider'; @@ -68,7 +69,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview public constructor( private readonly _contentProvider: MarkdownContentProvider, private readonly _logger: Logger, - private readonly _contributions: MarkdownContributionProvider + private readonly _contributions: MarkdownContributionProvider, + private readonly _engine: MarkdownEngine, ) { super(); this._register(vscode.window.registerWebviewPanelSerializer(DynamicMarkdownPreview.viewType, this)); @@ -145,7 +147,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this._previewConfigurations, this._logger, this._topmostLineMonitor, - this._contributions); + this._contributions, + this._engine); this.registerDynamicPreview(preview); } @@ -160,7 +163,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this._contentProvider, this._previewConfigurations, this._logger, - this._contributions); + this._contributions, + this._engine); this.registerStaticPreview(preview); } @@ -179,7 +183,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this._previewConfigurations, this._logger, this._topmostLineMonitor, - this._contributions); + this._contributions, + this._engine); this.setPreviewActiveContext(true); this._activePreview = preview; diff --git a/extensions/markdown-language-features/src/markdownExtensions.ts b/extensions/markdown-language-features/src/markdownExtensions.ts index 073ca3e972..0b55d66793 100644 --- a/extensions/markdown-language-features/src/markdownExtensions.ts +++ b/extensions/markdown-language-features/src/markdownExtensions.ts @@ -70,7 +70,7 @@ export namespace MarkdownContributions { const previewStyles = getContributedStyles(contributions, extension); const previewScripts = getContributedScripts(contributions, extension); - const previewResourceRoots = previewStyles.length || previewScripts.length ? [vscode.Uri.file(extension.extensionPath)] : []; + const previewResourceRoots = previewStyles.length || previewScripts.length ? [extension.extensionUri] : []; const markdownItPlugins = getContributedMarkdownItPlugins(contributions, extension); return { diff --git a/extensions/markdown-language-features/src/test/documentLink.test.ts b/extensions/markdown-language-features/src/test/documentLink.test.ts new file mode 100644 index 0000000000..e86b93cb74 --- /dev/null +++ b/extensions/markdown-language-features/src/test/documentLink.test.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. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { joinLines } from './util'; + +const testFileA = workspaceFile('a.md'); + +function workspaceFile(...segments: string[]) { + return vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, ...segments); +} + +async function getLinksForFile(file: vscode.Uri): Promise { + return (await vscode.commands.executeCommand('vscode.executeLinkProvider', file))!; +} + +suite('Markdown Document links', () => { + + teardown(async () => { + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('Should navigate to markdown file', async () => { + await withFileContents(testFileA, '[b](b.md)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('b.md')); + }); + + test('Should navigate to markdown file with leading ./', async () => { + await withFileContents(testFileA, '[b](./b.md)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('b.md')); + }); + + test('Should navigate to markdown file with leading /', async () => { + await withFileContents(testFileA, '[b](./b.md)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('b.md')); + }); + + test('Should navigate to markdown file without file extension', async () => { + await withFileContents(testFileA, '[b](b)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('b.md')); + }); + + test('Should navigate to markdown file in directory', async () => { + await withFileContents(testFileA, '[b](sub/c)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('sub', 'c.md')); + }); + + test('Should navigate to fragment by title in file', async () => { + await withFileContents(testFileA, '[b](sub/c#second)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('sub', 'c.md')); + assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 1); + }); + + test('Should navigate to fragment by line', async () => { + await withFileContents(testFileA, '[b](sub/c#L2)'); + + const [link] = await getLinksForFile(testFileA); + await executeLink(link); + + assertActiveDocumentUri(workspaceFile('sub', 'c.md')); + assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 1); + }); + + test('Should navigate to fragment within current file', async () => { + await withFileContents(testFileA, joinLines( + '[](a#header)', + '[](#header)', + '# Header')); + + const links = await getLinksForFile(testFileA); + { + await executeLink(links[0]); + assertActiveDocumentUri(workspaceFile('a.md')); + assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 2); + } + { + await executeLink(links[1]); + assertActiveDocumentUri(workspaceFile('a.md')); + assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 2); + } + }); + + test('Should navigate to fragment within current untitled file', async () => { + const testFile = workspaceFile('x.md').with({ scheme: 'untitled' }); + await withFileContents(testFile, joinLines( + '[](#second)', + '# Second')); + + const [link] = await getLinksForFile(testFile); + await executeLink(link); + + assertActiveDocumentUri(testFile); + assert.strictEqual(vscode.window.activeTextEditor!.selection.start.line, 1); + }); +}); + + +function assertActiveDocumentUri(expectedUri: vscode.Uri) { + assert.strictEqual( + vscode.window.activeTextEditor!.document.uri.fsPath, + expectedUri.fsPath + ); +} + +async function withFileContents(file: vscode.Uri, contents: string): Promise { + const document = await vscode.workspace.openTextDocument(file); + const editor = await vscode.window.showTextDocument(document); + await editor.edit(edit => { + edit.replace(new vscode.Range(0, 0, 1000, 0), contents); + }); +} + +async function executeLink(link: vscode.DocumentLink) { + const args = JSON.parse(decodeURIComponent(link.target!.query)); + await vscode.commands.executeCommand(link.target!.path, args); +} + diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index d1dec19917..c97c8fbeae 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -10,7 +10,7 @@ import LinkProvider from '../features/documentLinkProvider'; import { InMemoryDocument } from './inMemoryDocument'; -const testFileName = vscode.Uri.file('test.md'); +const testFile = vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, 'x.md'); const noopToken = new class implements vscode.CancellationToken { private _onCancellationRequestedEmitter = new vscode.EventEmitter(); @@ -20,7 +20,7 @@ const noopToken = new class implements vscode.CancellationToken { }; function getLinksForFile(fileContents: string) { - const doc = new InMemoryDocument(testFileName, fileContents); + const doc = new InMemoryDocument(testFile, fileContents); const provider = new LinkProvider(); return provider.provideDocumentLinks(doc, noopToken); } @@ -118,24 +118,24 @@ suite('markdown.DocumentLinkProvider', () => { const links = getLinksForFile('[![alt text](image.jpg)](https://example.com)'); assert.strictEqual(links.length, 2); const [link1, link2] = links; - assertRangeEqual(link1.range, new vscode.Range(0,13,0,22)); - assertRangeEqual(link2.range, new vscode.Range(0,25,0,44)); + assertRangeEqual(link1.range, new vscode.Range(0, 13, 0, 22)); + assertRangeEqual(link2.range, new vscode.Range(0, 25, 0, 44)); } { const links = getLinksForFile('[![a]( whitespace.jpg )]( https://whitespace.com )'); assert.strictEqual(links.length, 2); const [link1, link2] = links; - assertRangeEqual(link1.range, new vscode.Range(0,7,0,21)); - assertRangeEqual(link2.range, new vscode.Range(0,26,0,48)); + assertRangeEqual(link1.range, new vscode.Range(0, 7, 0, 21)); + assertRangeEqual(link2.range, new vscode.Range(0, 26, 0, 48)); } { const links = getLinksForFile('[![a](img1.jpg)](file1.txt) text [![a](img2.jpg)](file2.txt)'); assert.strictEqual(links.length, 4); const [link1, link2, link3, link4] = links; - assertRangeEqual(link1.range, new vscode.Range(0,6,0,14)); - assertRangeEqual(link2.range, new vscode.Range(0,17,0,26)); - assertRangeEqual(link3.range, new vscode.Range(0,39,0,47)); - assertRangeEqual(link4.range, new vscode.Range(0,50,0,59)); + assertRangeEqual(link1.range, new vscode.Range(0, 6, 0, 14)); + assertRangeEqual(link2.range, new vscode.Range(0, 17, 0, 26)); + assertRangeEqual(link3.range, new vscode.Range(0, 39, 0, 47)); + assertRangeEqual(link4.range, new vscode.Range(0, 50, 0, 59)); } }); }); diff --git a/extensions/markdown-language-features/src/test/test-fixtures/marker.txt b/extensions/markdown-language-features/src/test/test-fixtures/marker.txt deleted file mode 100644 index a4c9ad4073..0000000000 --- a/extensions/markdown-language-features/src/test/test-fixtures/marker.txt +++ /dev/null @@ -1 +0,0 @@ -DO NOT DELETE, USED BY INTEGRATION TESTS diff --git a/extensions/markdown-language-features/src/test/util.ts b/extensions/markdown-language-features/src/test/util.ts new file mode 100644 index 0000000000..5fb48ed01f --- /dev/null +++ b/extensions/markdown-language-features/src/test/util.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. + *--------------------------------------------------------------------------------------------*/ +import * as os from 'os'; + +export const joinLines = (...args: string[]) => + args.join(os.platform() === 'win32' ? '\r\n' : '\n'); diff --git a/extensions/markdown-language-features/src/util/links.ts b/extensions/markdown-language-features/src/util/links.ts index 8cce8221e8..1f74343119 100644 --- a/extensions/markdown-language-features/src/util/links.ts +++ b/extensions/markdown-language-features/src/util/links.ts @@ -9,6 +9,7 @@ export const Schemes = { http: 'http:', https: 'https:', file: 'file:', + untitled: 'untitled', mailto: 'mailto:', data: 'data:', vscode: 'vscode:', diff --git a/extensions/markdown-language-features/test-workspace/a.md b/extensions/markdown-language-features/test-workspace/a.md new file mode 100644 index 0000000000..9d70918067 --- /dev/null +++ b/extensions/markdown-language-features/test-workspace/a.md @@ -0,0 +1,4 @@ +[b](b) +[b](b.md) +[b](./b.md) +[b](/b.md) diff --git a/extensions/markdown-language-features/test-workspace/b.md b/extensions/markdown-language-features/test-workspace/b.md new file mode 100644 index 0000000000..730f53ee6c --- /dev/null +++ b/extensions/markdown-language-features/test-workspace/b.md @@ -0,0 +1,3 @@ +# b + +[](./a) \ No newline at end of file diff --git a/extensions/markdown-language-features/test-workspace/sub/c.md b/extensions/markdown-language-features/test-workspace/sub/c.md new file mode 100644 index 0000000000..f5c9bec43a --- /dev/null +++ b/extensions/markdown-language-features/test-workspace/sub/c.md @@ -0,0 +1,6 @@ +# First +# Second + +[b](/b.md) +[b](../b.md) +[b](./../b.md) diff --git a/extensions/markdown-language-features/test-workspace/sub/d.md b/extensions/markdown-language-features/test-workspace/sub/d.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 6fc1013a21..230e720db6 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -26,7 +26,7 @@ interface IToken { refreshToken: string; account: { - displayName: string; + label: string; id: string; }; scope: string; @@ -48,7 +48,8 @@ interface IStoredSession { refreshToken: string; scope: string; // Scopes are alphabetized and joined with a space account: { - displayName: string, + label?: string; + displayName?: string, id: string } } @@ -101,7 +102,7 @@ export class AzureActiveDirectoryService { accessToken: undefined, refreshToken: session.refreshToken, account: { - displayName: session.account.displayName, + label: session.account.label ?? session.account.displayName!, id: session.account.id }, scope: session.scope, @@ -437,7 +438,7 @@ export class AzureActiveDirectoryService { scope, sessionId: existingId || `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}/${uuid()}`, account: { - displayName: claims.email || claims.unique_name || 'user@example.com', + label: claims.email || claims.unique_name || 'user@example.com', id: `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}` } }; diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts index 874640c78c..4089548e6f 100644 --- a/extensions/microsoft-authentication/src/extension.ts +++ b/extensions/microsoft-authentication/src/extension.ts @@ -19,27 +19,42 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.authentication.registerAuthenticationProvider({ id: 'microsoft', - displayName: 'Microsoft', + label: 'Microsoft', supportsMultipleAccounts: true, onDidChangeSessions: onDidChangeSessions.event, getSessions: () => Promise.resolve(loginService.sessions), login: async (scopes: string[]) => { try { + /* __GDPR__ + "login" : { } + */ telemetryReporter.sendTelemetryEvent('login'); + const session = await loginService.login(scopes.sort().join(' ')); onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] }); return session; } catch (e) { + /* __GDPR__ + "loginFailed" : { } + */ telemetryReporter.sendTelemetryEvent('loginFailed'); + throw e; } }, logout: async (id: string) => { try { + /* __GDPR__ + "logout" : { } + */ telemetryReporter.sendTelemetryEvent('logout'); + await loginService.logout(id); onDidChangeSessions.fire({ added: [], removed: [id], changed: [] }); } catch (e) { + /* __GDPR__ + "logoutFailed" : { } + */ telemetryReporter.sendTelemetryEvent('logoutFailed'); } } diff --git a/extensions/package.json b/extensions/package.json index e54ff1794b..7c668c9744 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.9.5" + "typescript": "3.9.6" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json index 73309c34b4..436dfa5291 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -122,5 +122,11 @@ "foreground": "#CBEDCB", } } - ] + ], + "semanticTokenColors": { + "newOperator": "#FFFFFF", + "stringLiteral": "#ce9178", + "customLiteral": "#DCDCAA", + "numberLiteral": "#b5cea8", + } } diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json index a22dd21b4a..3fad0bc14f 100644 --- a/extensions/theme-defaults/themes/light_defaults.json +++ b/extensions/theme-defaults/themes/light_defaults.json @@ -20,10 +20,9 @@ "statusBarItem.remoteBackground": "#16825D", "sideBarSectionHeader.background": "#0000", "sideBarSectionHeader.border": "#61616130", - "notebook.cellFocusBackground": "#c8ddf150", + "notebook.focusedCellBackground": "#c8ddf150", "notebook.cellBorderColor": "#dae3e9", - "notebook.outputContainerBackgroundColor": "#c8ddf150", - "notebook.focusedCellShadow": "#00315040" + "notebook.outputContainerBackgroundColor": "#c8ddf150" }, "semanticHighlighting": true } diff --git a/extensions/vscode-web-playground/src/exampleFiles.ts b/extensions/vscode-web-playground/src/exampleFiles.ts index f382391bfc..5663067a36 100644 --- a/extensions/vscode-web-playground/src/exampleFiles.ts +++ b/extensions/vscode-web-playground/src/exampleFiles.ts @@ -282,7 +282,7 @@ Or discuss debug adapters on Gitter: You can now 'step through' the 'readme.md' file, set and hit breakpoints, and run into exceptions (if the word exception appears in a line). -![Mock Debug](images/mock-debug.gif) +![Mock Debug](file.jpg) ## Build and Run @@ -302,3 +302,9 @@ export function getImageFile(): Uint8Array { const data = atob(`/9j/4AAQSkZJRgABAQAASABIAAD/2wCEAA4ODg4ODhcODhchFxcXIS0hISEhLTktLS0tLTlFOTk5OTk5RUVFRUVFRUVSUlJSUlJgYGBgYGxsbGxsbGxsbGwBERISGxkbLxkZL3FMP0xxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcf/AABEIAFYAZAMBIgACEQEDEQH/xAB1AAACAwEBAQAAAAAAAAAAAAAABAMFBgIBBxAAAgIBAwMCBQQCAwAAAAAAAQIAAxEEBSESMUFRcRMiIzJhFIGRoQbBQlKxAQEBAQEAAAAAAAAAAAAAAAABAgADEQEBAQADAQEAAAAAAAAAAAAAARESITECQf/aAAwDAQACEQMRAD8A2LEZkLc/bKxbdYEHWoyfEze56zXpqRTTYUyPHiVrY2TVZyMzhFZMg8iYE6jcVXAusY98KMnj2lhRu+4aLoGuTNTYPV5APnyDNyPFp6EY3EsO3kxnVVLZVg8z2tw9YsXkGQpcbGIbxHQzep0vw8Jgc8n28CJJRY30lBwzf1iaa2ku/HmMV01VW/k/6hh0abTDTafpPcTytmckEewjeosAqJEj0yDo6yO/rFLzoGME5nIAXtGSM9uwnjLn8zFECw7QneITMWouR7gj9/Ep94061bjXa32WDGfzOGuCXKy9/wDc0FlFe5aX4OpHJHBHcSfT4w246bWJar6MsCwKnp9DOF0r6XRiu5snvg9hNK217vQeih0tXwzcED895R7voNfWoN9gOT2QH/2T3mHrda3Y+p9ppZuSV/qR0j6r+5ju2oun2ypOwCAASGikISzdySf5lxLsAdRPpIqw91xC/wDHvGbAAh88RnSVCjT9b8E/MYsguerTqWuYKo8k4ESTcttsPSmoQ+zCZPWPbvWqsvLE0IxCL4wPP7xEW7TXeKsvaGABOMdLef2ky7ejevX0tBWy5Qhh6jmS9IIxPm6XazbW69K56M/aeRibnSaqyytWtGCfE0+tazDhrHpCdixT5EJSWD1BPkcjsYxpN21FWEcdu0dG3hl8rIX0YqUgDqkSrq/0+6oyfOOZT7hqxqLMKMk8ARfS0fqGatAR04yCY+u3OpLt38e0rQl0tzsFrc8rxj0lqqDHMzujIXUMGPI4mjS1MTCvG8gRLddYE2811n5nHTJ9RaAsztzZ1AZhlX9fBi0VWgWzbSqahfpWfa/iSnatMuqOpVgVPIHGMzc6erS3aQVOoZSMFTK19i2pTwGA9Axx/E58b+K2M8lP6/Urp6BkA5Y+OPE112nrIFeOw8RMajQ7dWU0iAH8TyrVG0mw8EypMFuk7K9TS5RGJHiEYsuUtmEWO1KO2RGDRSVJzj1MiQhOQIx8QEYK5hGpUUJVc1lTgcDjEe1FPxqGQHBZSMiQqa8/Z38xgOoHB/aIfJNVZrdFqirsVbsfzLXT7+UQLYmcDHBlh/k+g+KP1dOCV+4efcTNbdtGq3CxQiMKyeX7CGqxqtDuK7lYK2BXnAz3JMuNZoPpDAyV5zHNt2bRbcA1S/Pjljyf7jerWxx0V4wQeZgynxrUXoUnIif629GJY595cptr1N9XJYjOfEi1G3LYMLgH1m04qxelrAtnj/qZYIvUPpMcHwYtTT8FzVaMN6+sslqVF6gcQ1sRivPccwjS314+bGYRBnqzws6FhUfL7CQ8gdI7+TDIHHgcSVGBYRznMXfUL2J5ngPUOYCpfM2tiq1tnUpVRnMe0DGtAKyQIw+mU4GJCKmrPy+I6V0lxYYIzxOCtdjZyVIMRqtPsYx8RT37+sdRhsFlHzcyC0J0kmcfqFX5cxC7VAk4OPUQtM+UVtYf7vH8iKP8SnKg5U9xHQwsGV7jxF9QnWACMEcgwlUjT4ZUE+YRRLGRehwciEpLRMAAT6SALlIQkF4kl7HEIQLwuQfac9RPeEJi5H3TruvvmEJo1QOcgGQuvVg+sITM8rDKeDHVItXkQhKgqM6esnJEIQlJf//Z`); return Uint8Array.from([...data].map(x => x.charCodeAt(0))); } + +// encoded from 'ĐБВГДЕЖЗĐЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрŃŃ‚ŃфхцчŃщъыьэюя' +export const windows1251File = Uint8Array.from([192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]); + +// encoded from '中国abc' +export const gbkFile = Uint8Array.from([214, 208, 185, 250, 97, 98, 99]); diff --git a/extensions/vscode-web-playground/src/memfs.ts b/extensions/vscode-web-playground/src/memfs.ts index f3a407443d..b3c4538f9e 100644 --- a/extensions/vscode-web-playground/src/memfs.ts +++ b/extensions/vscode-web-playground/src/memfs.ts @@ -29,7 +29,7 @@ import { Uri, workspace, } from 'vscode'; -import { largeTSFile, getImageFile, debuggableFile } from './exampleFiles'; +import { largeTSFile, getImageFile, debuggableFile, windows1251File, gbkFile } from './exampleFiles'; export class File implements FileStat { @@ -123,6 +123,19 @@ export class MemFS implements FileSystemProvider, FileSearchProvider, TextSearch this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/UPPER.txt`), textEncoder.encode('UPPER'), { create: true, overwrite: true }); this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/upper.txt`), textEncoder.encode('upper'), { create: true, overwrite: true }); this.writeFile(Uri.parse(`memfs:/sample-folder/xyz/def/foo.md`), textEncoder.encode('*MemFS*'), { create: true, overwrite: true }); + + // some files in different encodings + this.createDirectory(Uri.parse(`memfs:/sample-folder/encodings/`)); + this.writeFile( + Uri.parse(`memfs:/sample-folder/encodings/windows1251.txt`), + windows1251File, + { create: true, overwrite: true } + ); + this.writeFile( + Uri.parse(`memfs:/sample-folder/encodings/gbk.txt`), + gbkFile, + { create: true, overwrite: true } + ); } root = new Directory(Uri.parse('memfs:/'), ''); diff --git a/extensions/xml/package.json b/extensions/xml/package.json index 77e36951b7..3e5042df70 100644 --- a/extensions/xml/package.json +++ b/extensions/xml/package.json @@ -23,8 +23,8 @@ ".dita", ".ditamap", ".dtd", - ".ent", - ".mod", + ".ent", + ".mod", ".dtml", ".fsproj", ".fxml", diff --git a/extensions/yarn.lock b/extensions/yarn.lock index e0f5c7c4dd..d41a4ab48b 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -76,10 +76,10 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -typescript@3.9.5: - version "3.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36" - integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ== +typescript@3.9.6: + version "3.9.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" + integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== wrappy@1: version "1.0.2" diff --git a/package.json b/package.json index bfaf2f236a..2ca2543771 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "preinstall": "node build/npm/preinstall.js", "postinstall": "node build/npm/postinstall.js", "compile": "gulp compile --max_old_space_size=4095", - "watch": "gulp watch --max_old_space_size=4095", + "watch": "concurrently \"npm:watch-client\" \"npm:watch-extensions\"", "watchd": "deemon yarn watch", "watch-webd": "deemon yarn watch-web", "kill-watchd": "deemon --kill yarn watch", @@ -22,6 +22,11 @@ "restart-watchd": "deemon --restart yarn watch", "restart-watch-webd": "deemon --restart yarn watch-web", "watch-client": "gulp watch-client --max_old_space_size=4095", + "watch-clientd": "deemon yarn watch-client", + "kill-watch-clientd": "deemon --kill yarn watch-client", + "watch-extensions": "gulp watch-extensions --max_old_space_size=4095", + "watch-extensionsd": "deemon yarn watch-extensions", + "kill-watch-extensionsd": "deemon --kill yarn watch-extensions", "mocha": "mocha test/unit/node/all.js --delay", "precommit": "node build/gulpfile.hygiene.js", "gulp": "gulp --max_old_space_size=8192", @@ -62,9 +67,9 @@ "html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", - "iconv-lite-umd": "0.6.5", + "iconv-lite-umd": "0.6.8", "jquery": "3.5.0", - "jschardet": "2.1.1", + "jschardet": "2.2.1", "keytar": "^5.5.0", "minimist": "^1.2.5", "native-is-elevated": "0.4.1", @@ -84,10 +89,10 @@ "vscode-nsfw": "1.2.8", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.7.0", + "vscode-ripgrep": "^1.8.0", "vscode-sqlite3": "4.0.10", "vscode-textmate": "5.2.0", - "xterm": "4.7.0-beta.3", + "xterm": "4.8.1", "xterm-addon-search": "0.7.0", "xterm-addon-unicode11": "0.2.0", "xterm-addon-webgl": "0.7.0", @@ -124,12 +129,13 @@ "ansi-colors": "^3.2.3", "asar": "^0.14.0", "chromium-pickle-js": "^0.2.0", + "concurrently": "^5.2.0", "copy-webpack-plugin": "^4.5.2", "cson-parser": "^1.3.3", "css-loader": "^3.2.0", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "7.3.2", + "electron": "8.3.3", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", "event-stream": "3.3.4", @@ -157,7 +163,7 @@ "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", "husky": "^0.13.1", - "innosetup": "5.6.1", + "innosetup": "6.0.5", "is": "^3.1.0", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-instrument": "^4.0.0", diff --git a/remote/package.json b/remote/package.json index 0892761f3b..363a97d80c 100644 --- a/remote/package.json +++ b/remote/package.json @@ -20,9 +20,9 @@ "html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", - "iconv-lite-umd": "0.6.5", + "iconv-lite-umd": "0.6.8", "jquery": "3.5.0", - "jschardet": "2.1.1", + "jschardet": "2.2.1", "minimist": "^1.2.5", "native-watchdog": "1.3.0", "ng2-charts": "^1.6.0", @@ -36,9 +36,9 @@ "vscode-nsfw": "1.2.8", "vscode-oniguruma": "1.3.1", "vscode-proxy-agent": "^0.5.2", - "vscode-ripgrep": "^1.7.0", + "vscode-ripgrep": "^1.8.0", "vscode-textmate": "5.2.0", - "xterm": "4.7.0-beta.3", + "xterm": "4.8.1", "xterm-addon-search": "0.7.0", "xterm-addon-unicode11": "0.2.0", "xterm-addon-webgl": "0.7.0", diff --git a/remote/web/package.json b/remote/web/package.json index 8de1ac76b2..cef07e7f5d 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -14,8 +14,8 @@ "ansi_up": "^3.0.0", "chart.js": "^2.6.0", "html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6", - "iconv-lite-umd": "0.6.5", - "jschardet": "2.1.1", + "iconv-lite-umd": "0.6.8", + "jschardet": "2.2.1", "jquery": "3.5.0", "ng2-charts": "^1.6.0", "reflect-metadata": "^0.1.8", @@ -25,7 +25,7 @@ "slickgrid": "github:anthonydresser/SlickGrid#2.3.33", "vscode-oniguruma": "1.3.1", "vscode-textmate": "5.2.0", - "xterm": "4.7.0-beta.3", + "xterm": "4.8.1", "xterm-addon-search": "0.7.0", "xterm-addon-unicode11": "0.2.0", "xterm-addon-webgl": "0.7.0", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 6bc88bb10f..694113201c 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -182,10 +182,10 @@ htmlparser2@^3.10.0: inherits "^2.0.1" readable-stream "^3.1.1" -iconv-lite-umd@0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.5.tgz#6a1f621a3b4d125f72feff813a9839e1ebd6c722" - integrity sha512-WDegH4al+e3n3jTOStRvm+jzDA3JMUQGgzdAsMxAgcgB0Oi72HjfdsoX08ieKsy3rKexXVjWZr41aOIUaCZnMg== +iconv-lite-umd@0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" + integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== inherits@^2.0.1, inherits@^2.0.3: version "2.0.4" @@ -197,10 +197,10 @@ jquery@3.5.0: resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.0.tgz#9980b97d9e4194611c36530e7dc46a58d7340fc9" integrity sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ== -jschardet@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" - integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== +jschardet@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.2.1.tgz#03b0264669a90c7a5c436a68c5a7d4e4cb0c9823" + integrity sha512-Ks2JNuUJoc7PGaZ7bVFtSEvOcr0rBq6Q1J5/7+zKWLT+g+4zziL63O0jg7y2jxhzIa1LVsHUbPXrbaWmz9iwDw== lodash.clonedeep@^4.5.0: version "4.5.0" @@ -378,10 +378,10 @@ xterm-addon-webgl@0.7.0: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0.tgz#a13732ac937170e53ce02ec91963da042c80614b" integrity sha512-PMWLgccAF31GulCYkQxIA8qwMI4q4UbRi5O/zwMnSJWBozB0yy84lX31ZhJeJhcrlEn1Vpcd+OUGPE8Z1hBjnw== -xterm@4.7.0-beta.3: - version "4.7.0-beta.3" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.7.0-beta.3.tgz#d8997f190430d750201717adf3857f6c8052f149" - integrity sha512-mL9VCB7Ql7KSql2PJmRQYba77mMXlliK9lVKd3XCDqtOYYWjg+CKKeNtFljIrPoiI25nvoqlkrv5dFuuIAR5hA== +xterm@4.8.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.8.1.tgz#155a1729a43e1a89b406524e22c5634339e39ca1" + integrity sha512-ax91ny4tI5eklqIfH79OUSGE2PUX2rGbwONmB6DfqpyhSZO8/cf++sqiaMWEVCMjACyMfnISW7C3gGMoNvNolQ== zone.js@^0.8.4: version "0.8.29" diff --git a/remote/yarn.lock b/remote/yarn.lock index 98809ebf37..0ed3f67039 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -376,10 +376,10 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" -iconv-lite-umd@0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.5.tgz#6a1f621a3b4d125f72feff813a9839e1ebd6c722" - integrity sha512-WDegH4al+e3n3jTOStRvm+jzDA3JMUQGgzdAsMxAgcgB0Oi72HjfdsoX08ieKsy3rKexXVjWZr41aOIUaCZnMg== +iconv-lite-umd@0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" + integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== inherits@^2.0.1, inherits@^2.0.3: version "2.0.4" @@ -420,10 +420,10 @@ jquery@3.5.0: resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.0.tgz#9980b97d9e4194611c36530e7dc46a58d7340fc9" integrity sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ== -jschardet@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184" - integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q== +jschardet@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.2.1.tgz#03b0264669a90c7a5c436a68c5a7d4e4cb0c9823" + integrity sha512-Ks2JNuUJoc7PGaZ7bVFtSEvOcr0rBq6Q1J5/7+zKWLT+g+4zziL63O0jg7y2jxhzIa1LVsHUbPXrbaWmz9iwDw== jsonfile@^4.0.0: version "4.0.0" @@ -736,10 +736,10 @@ vscode-proxy-agent@^0.5.2: https-proxy-agent "^2.2.3" socks-proxy-agent "^4.0.1" -vscode-ripgrep@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.7.0.tgz#ec912e04aa29f7d73bcef04b7576b792f12c9b38" - integrity sha512-sQY/u0ymc9YMiPaSsMmdZSFQ6PTS2UxcGuiQkF7aoIezDxZcGE1sMarqftWEl9wYWYc9hElYm00WpoFgzj1oUg== +vscode-ripgrep@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.8.0.tgz#dfe7c2ae2a2032df8a8108765c2feef73474888a" + integrity sha512-/Q5XtePkTLLi8yplr5ai24pVEymRF62xH9xXrtj35GTaDCJg3zq1s1/L1UqhVbfNDv4OcMBYjyIAt/quEi3d5w== dependencies: https-proxy-agent "^4.0.0" proxy-from-env "^1.1.0" @@ -781,10 +781,10 @@ xterm-addon-webgl@0.7.0: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.7.0.tgz#a13732ac937170e53ce02ec91963da042c80614b" integrity sha512-PMWLgccAF31GulCYkQxIA8qwMI4q4UbRi5O/zwMnSJWBozB0yy84lX31ZhJeJhcrlEn1Vpcd+OUGPE8Z1hBjnw== -xterm@4.7.0-beta.3: - version "4.7.0-beta.3" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.7.0-beta.3.tgz#d8997f190430d750201717adf3857f6c8052f149" - integrity sha512-mL9VCB7Ql7KSql2PJmRQYba77mMXlliK9lVKd3XCDqtOYYWjg+CKKeNtFljIrPoiI25nvoqlkrv5dFuuIAR5hA== +xterm@4.8.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.8.1.tgz#155a1729a43e1a89b406524e22c5634339e39ca1" + integrity sha512-ax91ny4tI5eklqIfH79OUSGE2PUX2rGbwONmB6DfqpyhSZO8/cf++sqiaMWEVCMjACyMfnISW7C3gGMoNvNolQ== yauzl@^2.9.2: version "2.10.0" diff --git a/resources/serverless/code-web.js b/resources/serverless/code-web.js index bee6ca5b71..998d88b884 100644 --- a/resources/serverless/code-web.js +++ b/resources/serverless/code-web.js @@ -20,32 +20,35 @@ const ansiColors = require('ansi-colors'); const extensions = require('../../build/lib/extensions'); const APP_ROOT = path.join(__dirname, '..', '..'); -const EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions'); +const BUILTIN_EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions'); +const BUILTIN_MARKETPLACE_EXTENSIONS_ROOT = path.join(APP_ROOT, '.build', 'builtInExtensions'); const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html'); const args = minimist(process.argv, { boolean: [ - 'watch', 'no-launch', - 'help' + 'help', + 'verbose' ], string: [ 'scheme', 'host', 'port', - 'local_port' + 'local_port', + 'extension' ], }); if (args.help) { console.log( 'yarn web [options]\n' + - ' --watch Watch extensions that require browser specific builds\n' + - ' --no-launch Do not open VSCode web in the browser\n' + - ' --scheme Protocol (https or http)\n' + - ' --host Remote host\n' + - ' --port Remote/Local port\n' + - ' --local_port Local port override\n' + + ' --no-launch Do not open VSCode web in the browser\n' + + ' --scheme Protocol (https or http)\n' + + ' --host Remote host\n' + + ' --port Remote/Local port\n' + + ' --local_port Local port override\n' + + ' --extension Path of an extension to include\n' + + ' --verbose Print out more information\n' + ' --help\n' + '[Example]\n' + ' yarn web --scheme https --host example.com --port 8080 --local_port 30000' @@ -61,76 +64,124 @@ const AUTHORITY = process.env.VSCODE_AUTHORITY || `${HOST}:${PORT}`; const exists = (path) => util.promisify(fs.exists)(path); const readFile = (path) => util.promisify(fs.readFile)(path); +const readdir = (path) => util.promisify(fs.readdir)(path); +const readdirWithFileTypes = (path) => util.promisify(fs.readdir)(path, { withFileTypes: true }); -let unbuiltExensions = []; +async function getBuiltInExtensionInfos() { + const extensions = []; + /** @type {Object.} */ + const locations = {}; -async function initialize() { - const builtinExtensions = []; - - const children = await util.promisify(fs.readdir)(EXTENSIONS_ROOT, { withFileTypes: true }); - const folders = children.filter(c => !c.isFile()); - await Promise.all(folders.map(async folder => { - const folderName = folder.name; - const extensionPath = path.join(EXTENSIONS_ROOT, folderName); - - let children = []; - try { - children = await util.promisify(fs.readdir)(extensionPath); - } catch (error) { - console.log(error); - return; - } - - const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0]; - const readmePath = readme ? path.join(extensionPath, readme) : undefined; - const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; - const changelogPath = changelog ? path.join(extensionPath, changelog) : undefined; - - const packageJSONPath = path.join(EXTENSIONS_ROOT, folderName, 'package.json'); - if (await exists(packageJSONPath)) { - try { - let packageJSON = JSON.parse((await readFile(packageJSONPath)).toString()); - if (packageJSON.main && !packageJSON.browser) { - return; // unsupported - } - - if (packageJSON.browser) { - packageJSON.main = packageJSON.browser; - - let mainFilePath = path.join(EXTENSIONS_ROOT, folderName, packageJSON.browser); - if (path.extname(mainFilePath) !== '.js') { - mainFilePath += '.js'; - } - if (!await exists(mainFilePath)) { - unbuiltExensions.push(path.relative(EXTENSIONS_ROOT, mainFilePath)); + for (const extensionsRoot of [BUILTIN_EXTENSIONS_ROOT, BUILTIN_MARKETPLACE_EXTENSIONS_ROOT]) { + if (await exists(extensionsRoot)) { + const children = await readdirWithFileTypes(extensionsRoot); + await Promise.all(children.map(async child => { + if (child.isDirectory()) { + const extensionPath = path.join(extensionsRoot, child.name); + const info = await getBuiltInExtensionInfo(extensionPath); + if (info) { + extensions.push(info); + locations[path.basename(extensionPath)] = extensionPath; } } - packageJSON.extensionKind = ['web']; // enable for Web - - const packageNLSPath = path.join(folderName, 'package.nls.json'); - const packageNLSExists = await exists(path.join(EXTENSIONS_ROOT, packageNLSPath)); - if (packageNLSExists) { - packageJSON = extensions.translatePackageJSON(packageJSON, path.join(EXTENSIONS_ROOT, packageNLSPath)); // temporary, until fixed in core - } - builtinExtensions.push({ - extensionPath: folderName, - packageJSON, - packageNLSPath: packageNLSExists ? packageNLSPath : undefined, - readmePath, - changelogPath - }); - } catch (e) { - console.log(e); - } + })); } - })); - if (unbuiltExensions.length) { - fancyLog(`${ansiColors.yellow('Warning')}: Make sure to run ${ansiColors.cyan('yarn gulp watch-web')}\nCould not find the following browser main files: \n${unbuiltExensions.join('\n')}`); } - return builtinExtensions; + return { extensions, locations }; } -const builtinExtensionsPromise = initialize(); +async function getBuiltInExtensionInfo(extensionPath) { + const packageJSON = await getExtensionPackageJSON(extensionPath); + if (!packageJSON) { + return undefined; + } + const builtInExtensionPath = path.basename(extensionPath); + + let children = []; + try { + children = await readdir(extensionPath); + } catch (error) { + console.log(`Can not read extension folder ${extensionPath}: ${error}`); + return; + } + const readme = children.find(child => /^readme(\.txt|\.md|)$/i.test(child)); + const changelog = children.find(child => /^changelog(\.txt|\.md|)$/i.test(child)); + const packageJSONNLS = children.find(child => /^package.nls.json$/i.test(child)); + return { + extensionPath: builtInExtensionPath, + packageJSON, + packageNLSPath: packageJSONNLS ? `${builtInExtensionPath}/${packageJSONNLS}` : undefined, + readmePath: readme ? `${builtInExtensionPath}/${readme}` : undefined, + changelogPath: changelog ? `${builtInExtensionPath}/${changelog}` : undefined + }; +} + +async function getDefaultExtensionInfos() { + const extensions = []; + + /** @type {Object.} */ + const locations = {}; + + let extensionArg = args['extension']; + if (!extensionArg) { + return { extensions, locations } + } + + const extensionPaths = Array.isArray(extensionArg) ? extensionArg : [extensionArg]; + await Promise.all(extensionPaths.map(async extensionPath => { + extensionPath = path.resolve(process.cwd(), extensionPath); + const packageJSON = await getExtensionPackageJSON(extensionPath); + if (packageJSON) { + const extensionId = `${packageJSON.publisher}.${packageJSON.name}`; + extensions.push({ + packageJSON, + extensionLocation: { scheme: SCHEME, authority: AUTHORITY, path: `/extension/${extensionId}` } + }); + locations[extensionId] = extensionPath; + } + })); + return { extensions, locations }; +} + +async function getExtensionPackageJSON(extensionPath) { + + const packageJSONPath = path.join(extensionPath, 'package.json'); + if (await exists(packageJSONPath)) { + try { + let packageJSON = JSON.parse((await readFile(packageJSONPath)).toString()); + if (packageJSON.main && !packageJSON.browser) { + return; // unsupported + } + + if (packageJSON.browser) { + packageJSON.main = packageJSON.browser; + + let mainFilePath = path.join(extensionPath, packageJSON.browser); + if (path.extname(mainFilePath) !== '.js') { + mainFilePath += '.js'; + } + if (!await exists(mainFilePath)) { + fancyLog(`${ansiColors.yellow('Warning')}: Could not find ${mainFilePath}. Use ${ansiColors.cyan('yarn gulp watch-web')} to build the built-in extensions.`); + } + } + packageJSON.extensionKind = ['web']; // enable for Web + + const packageNLSPath = path.join(extensionPath, 'package.nls.json'); + const packageNLSExists = await exists(packageNLSPath); + if (packageNLSExists) { + packageJSON = extensions.translatePackageJSON(packageJSON, packageNLSPath); // temporary, until fixed in core + } + + return packageJSON; + } catch (e) { + console.log(e); + } + } + return undefined; +} + +const builtInExtensionsPromise = getBuiltInExtensionInfos(); +const defaultExtensionsPromise = getDefaultExtensionInfos(); const mapCallbackUriToRequestId = new Map(); @@ -158,9 +209,13 @@ const server = http.createServer((req, res) => { // static requests return handleStatic(req, res, parsedUrl); } - if (/^\/static-extension\//.test(pathname)) { - // static extension requests - return handleStaticExtension(req, res, parsedUrl); + if (/^\/extension\//.test(pathname)) { + // default extension requests + return handleExtension(req, res, parsedUrl); + } + if (/^\/builtin-extension\//.test(pathname)) { + // built-in extension requests + return handleBuiltInExtension(req, res, parsedUrl); } if (pathname === '/') { // main web @@ -211,13 +266,28 @@ function handleStatic(req, res, parsedUrl) { * @param {import('http').ServerResponse} res * @param {import('url').UrlWithParsedQuery} parsedUrl */ -function handleStaticExtension(req, res, parsedUrl) { - - // Strip `/static-extension/` from the path - const relativeFilePath = path.normalize(decodeURIComponent(parsedUrl.pathname.substr('/static-extension/'.length))); - - const filePath = path.join(EXTENSIONS_ROOT, relativeFilePath); +async function handleExtension(req, res, parsedUrl) { + // Strip `/extension/` from the path + const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/extension/'.length)); + const filePath = getExtensionFilePath(relativePath, (await defaultExtensionsPromise).locations); + if (!filePath) { + return serveError(req, res, 400, `Bad request.`); + } + return serveFile(req, res, filePath); +} +/** + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + * @param {import('url').UrlWithParsedQuery} parsedUrl + */ +async function handleBuiltInExtension(req, res, parsedUrl) { + // Strip `/builtin-extension/` from the path + const relativePath = decodeURIComponent(parsedUrl.pathname.substr('/builtin-extension/'.length)); + const filePath = getExtensionFilePath(relativePath, (await builtInExtensionsPromise).locations); + if (!filePath) { + return serveError(req, res, 400, `Bad request.`); + } return serveFile(req, res, filePath); } @@ -232,34 +302,44 @@ async function handleRoot(req, res) { if (match) { const qs = new URLSearchParams(match[1]); - let ghPath = qs.get('gh'); - if (ghPath) { - if (!ghPath.startsWith('/')) { - ghPath = '/' + ghPath; + let gh = qs.get('gh'); + if (gh) { + if (gh.startsWith('/')) { + gh = gh.substr(1); } - folderUri = { scheme: 'github', authority: 'HEAD', path: ghPath }; - } else { - let csPath = qs.get('cs'); - if (csPath) { - if (!csPath.startsWith('/')) { - csPath = '/' + csPath; + const [owner, repo, ...branch] = gh.split('/', 3); + folderUri = { scheme: 'github', authority: branch.join('/') || 'HEAD', path: `/${owner}/${repo}` }; + } else { + let cs = qs.get('cs'); + if (cs) { + if (cs.startsWith('/')) { + cs = cs.substr(1); } - folderUri = { scheme: 'codespace', authority: 'HEAD', path: csPath }; + + const [owner, repo, ...branch] = cs.split('/'); + folderUri = { scheme: 'codespace', authority: branch.join('/') || 'HEAD', path: `/${owner}/${repo}` }; } } } - const builtinExtensions = await builtinExtensionsPromise; + const { extensions: builtInExtensions } = await builtInExtensionsPromise; + const { extensions: staticExtensions } = await defaultExtensionsPromise; + + if (args.verbose) { + fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: ${builtInExtensions.map(e => path.basename(e.extensionPath)).join(', ')}`); + fancyLog(`${ansiColors.magenta('Additional extensions')}: ${staticExtensions.map(e => path.basename(e.extensionLocation.path)).join(', ') || 'None'}`); + } const webConfigJSON = escapeAttribute(JSON.stringify({ folderUri: folderUri, - builtinExtensionsServiceUrl: `${SCHEME}://${AUTHORITY}/static-extension` + staticExtensions, + builtinExtensionsServiceUrl: `${SCHEME}://${AUTHORITY}/builtin-extension` })); - const data = (await util.promisify(fs.readFile)(WEB_MAIN)).toString() + const data = (await readFile(WEB_MAIN)).toString() .replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => webConfigJSON) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied - .replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(builtinExtensions))) + .replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(builtInExtensions))) .replace('{{WEBVIEW_ENDPOINT}}', '') .replace('{{REMOTE_USER_DATA_URI}}', ''); @@ -368,6 +448,25 @@ function escapeAttribute(value) { return value.replace(/"/g, '"'); } +/** + * @param {string} relativePath + * @param {Object.} locations + * @returns {string | undefined} +*/ +function getExtensionFilePath(relativePath, locations) { + const firstSlash = relativePath.indexOf('/'); + if (firstSlash === -1) { + return undefined; + } + const extensionId = relativePath.substr(0, firstSlash); + + const extensionPath = locations[extensionId]; + if (!extensionPath) { + return undefined; + } + return path.join(extensionPath, relativePath.substr(firstSlash + 1)); +} + /** * @param {import('http').IncomingMessage} req * @param {import('http').ServerResponse} res @@ -419,10 +518,6 @@ async function serveFile(req, res, filePath, responseHeaders = Object.create(nul // Sanity checks filePath = path.normalize(filePath); // ensure no "." and ".." - if (filePath.indexOf(`${APP_ROOT}${path.sep}`) !== 0) { - // invalid location outside of APP_ROOT - return serveError(req, res, 400, `Bad request.`); - } const stat = await util.promisify(fs.stat)(filePath); diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index 1dc84176fc..69566719f4 100755 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -49,7 +49,10 @@ REM if %errorlevel% neq 0 exit /b %errorlevel% REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% REM if %errorlevel% neq 0 exit /b %errorlevel% -REM call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\markdown-language-features\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% . +REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\typescript-language-features\test-workspace --extensionDevelopmentPath=%~dp0\..\extensions\typescript-language-features --extensionTestsPath=%~dp0\..\extensions\typescript-language-features\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +REM if %errorlevel% neq 0 exit /b %errorlevel% + +REM call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\markdown-language-features\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% REM if %errorlevel% neq 0 exit /b %errorlevel% REM call "%INTEGRATION_TEST_ELECTRON_PATH%" $%~dp0\..\extensions\emmet\out\test\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% . diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index f92e3c44f1..f57534c448 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -46,8 +46,9 @@ fi # "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR # "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR # "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR +# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/typescript-language-features/test-workspace --extensionDevelopmentPath=$ROOT/extensions/typescript-language-features --extensionTestsPath=$ROOT/extensions/typescript-language-features/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR # "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/out/test/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHD +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $(mktemp -d 2>/dev/null) --enable-proposed-api=vscode.git --extensionDevelopmentPath=$ROOT/extensions/git --extensionTestsPath=$ROOT/extensions/git/out/test --disable-telemetry --crash-reporter-directory=$VSCODECRASHDIR --no-cached-data --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --no-cached-data --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --user-data-dir=$VSCODEUSERDATADIR diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 313a4f8be1..eb23b2f989 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -22,8 +22,6 @@ } }(this, function () { const path = require.__$__nodeRequire('path'); - const webFrame = require.__$__nodeRequire('electron').webFrame; - const ipc = require.__$__nodeRequire('electron').ipcRenderer; const bootstrap = globalThis.MonacoBootstrap; /** @@ -36,7 +34,6 @@ /** * // configuration: INativeWindowConfiguration * @type {{ - * zoomLevel?: number, * extensionDevelopmentPath?: string[], * extensionTestsPath?: string, * userEnv?: { [key: string]: string | undefined }, @@ -45,12 +42,6 @@ * }} */ const configuration = JSON.parse(args['config'] || '{}') || {}; - // Apply zoom level early to avoid glitches - const zoomLevel = configuration.zoomLevel; - if (typeof zoomLevel === 'number' && zoomLevel !== 0) { - webFrame.setZoomLevel(zoomLevel); - } - // Error handler process.on('uncaughtException', function (error) { onUnexpectedError(error, enableDeveloperTools); @@ -96,28 +87,9 @@ const loaderConfig = { baseUrl: `${bootstrap.uriFromPath(configuration.appRoot)}/out`, 'vs/nls': nlsConfig, - nodeModules: [/*BUILD->INSERT_NODE_MODULES*/] + amdModulesPattern: /^(vs|sql)\//, // {{SQL CARBON EDIT}} include sql in regex }; - loaderConfig.nodeModules = loaderConfig.nodeModules.concat([ - '@angular/common', - '@angular/core', - '@angular/forms', - '@angular/platform-browser', - '@angular/platform-browser-dynamic', - '@angular/router', - 'rxjs/Observable', - 'rxjs/add/observable/fromPromise', - 'rxjs/Subject', - 'rxjs/Observer', - 'slickgrid/lib/jquery.event.drag-2.3.0', - 'slickgrid/lib/jquery-ui-1.9.2', - 'slickgrid/slick.core', - 'slickgrid/slick.grid', - 'slickgrid/slick.editors', - 'slickgrid/slick.dataview' - ]); - // cached data config if (configuration.nodeCachedDataDir) { loaderConfig.nodeCachedData = { @@ -178,6 +150,8 @@ * @returns {() => void} */ function registerDeveloperKeybindings(disallowReloadKeybinding) { + const ipcRenderer = globals().ipcRenderer; + const extractKey = function (e) { return [ e.ctrlKey ? 'ctrl-' : '', @@ -196,9 +170,9 @@ let listener = function (e) { const key = extractKey(e); if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) { - ipc.send('vscode:toggleDevTools'); + ipcRenderer.send('vscode:toggleDevTools'); } else if (key === RELOAD_KB && !disallowReloadKeybinding) { - ipc.send('vscode:reloadWindow'); + ipcRenderer.send('vscode:reloadWindow'); } }; @@ -218,7 +192,8 @@ */ function onUnexpectedError(error, enableDeveloperTools) { if (enableDeveloperTools) { - ipc.send('vscode:openDevTools'); + const ipcRenderer = globals().ipcRenderer; + ipcRenderer.send('vscode:openDevTools'); } console.error(`[uncaught exception]: ${error}`); @@ -228,7 +203,16 @@ } } + /** + * @return {typeof import('./vs/base/parts/sandbox/electron-sandbox/globals')} + */ + function globals() { + // @ts-ignore (defined in globals.js) + return window.vscode; + } + return { - load + load, + globals }; })); diff --git a/src/main.js b/src/main.js index f6f59aceb0..183f9e072c 100644 --- a/src/main.js +++ b/src/main.js @@ -206,6 +206,8 @@ function configureCommandlineSwitchesSync(cliArgs) { ]; if (process.platform === 'linux') { + + // Force enable screen readers on Linux via this flag SUPPORTED_ELECTRON_SWITCHES.push('force-renderer-accessibility'); } @@ -223,6 +225,7 @@ function configureCommandlineSwitchesSync(cliArgs) { // Append Electron flags to Electron if (SUPPORTED_ELECTRON_SWITCHES.indexOf(argvKey) !== -1) { + // Color profile if (argvKey === 'force-color-profile') { if (argvValue) { diff --git a/src/sql/base/browser/ui/panel/panel.ts b/src/sql/base/browser/ui/panel/panel.ts index b9ba9a6ab4..73fc1814ea 100644 --- a/src/sql/base/browser/ui/panel/panel.ts +++ b/src/sql/base/browser/ui/panel/panel.ts @@ -14,7 +14,6 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { isUndefinedOrNull } from 'vs/base/common/types'; -import * as map from 'vs/base/common/map'; import { firstIndex } from 'vs/base/common/arrays'; export interface ITabbedPanelStyles { @@ -285,7 +284,7 @@ export class TabbedPanel extends Disposable { } } if (!this._shownTabId && this._tabMap.size > 0) { - this.showTab(map.values(this._tabMap)[0].tab.identifier); + this.showTab(this._tabMap.values()[0].tab.identifier); } } diff --git a/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts b/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts index 25f9237179..6384b6454a 100644 --- a/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts +++ b/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts @@ -31,7 +31,7 @@ import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/t import { UNSAVED_GROUP_ID, mssqlProviderName } from 'sql/platform/connection/common/constants'; import { $ } from 'vs/base/browser/dom'; import { OEManageConnectionAction } from 'sql/workbench/contrib/dashboard/browser/dashboardActions'; -import { IViewsService, IView, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views'; +import { IViewsService, IView, ViewContainerLocation, ViewContainer, IViewPaneContainer } from 'vs/workbench/common/views'; import { ConsoleLogService } from 'vs/platform/log/common/log'; import { IProgressIndicator } from 'vs/platform/progress/common/progress'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; @@ -111,6 +111,9 @@ suite('SQL Connection Tree Action tests', () => { }); const viewsService = new class implements IViewsService { + getActiveViewPaneContainerWithId(viewContainerId: string): IViewPaneContainer { + throw new Error('Method not implemented.'); + } getViewProgressIndicator(id: string): IProgressIndicator { throw new Error('Method not implemented.'); } diff --git a/src/sql/workbench/services/accountManagement/browser/accountDialog.ts b/src/sql/workbench/services/accountManagement/browser/accountDialog.ts index d99ecd278c..8de2cb578a 100644 --- a/src/sql/workbench/services/accountManagement/browser/accountDialog.ts +++ b/src/sql/workbench/services/accountManagement/browser/accountDialog.ts @@ -18,7 +18,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { SplitView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { values } from 'vs/base/common/map'; import * as azdata from 'azdata'; @@ -44,6 +43,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { Registry } from 'vs/platform/registry/common/platform'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { Iterable } from 'vs/base/common/iterator'; export const VIEWLET_ID = 'workbench.view.accountpanel'; @@ -219,7 +219,7 @@ export class AccountDialog extends Modal { this._addAccountButton.label = localize('accountDialog.addConnection', "Add an account"); this._register(this._addAccountButton.onDidClick(async () => { - const vals = values(this._providerViewsMap); + const vals = Iterable.consume(this._providerViewsMap.values())[0]; let pickedValue: string; if (vals.length === 0) { @@ -291,8 +291,8 @@ export class AccountDialog extends Modal { private showSplitView() { this._splitViewContainer.hidden = false; this._noaccountViewContainer.hidden = true; - if (values(this._providerViewsMap).length > 0) { - const firstView = values(this._providerViewsMap)[0]; + if (Iterable.consume(this._providerViewsMap.values()).length > 0) { + const firstView = this._providerViewsMap.values()[0]; if (firstView instanceof AccountPanel) { firstView.setSelection([0]); firstView.focus(); @@ -301,7 +301,7 @@ export class AccountDialog extends Modal { } private isEmptyLinkedAccount(): boolean { - for (const provider of values(this._providerViewsMap)) { + for (const provider of this._providerViewsMap.values()) { const listView = provider.view; if (listView && listView.length > 0) { return false; @@ -312,7 +312,7 @@ export class AccountDialog extends Modal { public dispose(): void { super.dispose(); - for (const provider of values(this._providerViewsMap)) { + for (const provider of this._providerViewsMap.values()) { if (provider.addAccountAction) { provider.addAccountAction.dispose(); } diff --git a/src/sql/workbench/services/bootstrap/browser/bootstrapService.ts b/src/sql/workbench/services/bootstrap/browser/bootstrapService.ts index 4b0b57038b..64248c51e5 100644 --- a/src/sql/workbench/services/bootstrap/browser/bootstrapService.ts +++ b/src/sql/workbench/services/bootstrap/browser/bootstrapService.ts @@ -8,7 +8,6 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { IInstantiationService, _util, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IEditorInput } from 'vs/workbench/common/editor'; import { Trace } from 'vs/platform/instantiation/common/instantiationService'; -import { values } from 'vs/base/common/map'; import { IModuleFactory, IBootstrapParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; @@ -16,7 +15,7 @@ import { ILogService } from 'vs/platform/log/common/log'; const selectorCounter = new Map(); export function providerIterator(service: IInstantiationService): Provider[] { - return Array.from(values(_util.serviceIds)).map(v => { + return Array.from(_util.serviceIds.values()).map(v => { let factory = () => { return (service)._getOrCreateServiceInstance(v, Trace.traceCreation(v)); }; diff --git a/src/sql/workbench/services/notebook/browser/models/notebookModel.ts b/src/sql/workbench/services/notebook/browser/models/notebookModel.ts index 58f678dc2a..fd30ac1e87 100644 --- a/src/sql/workbench/services/notebook/browser/models/notebookModel.ts +++ b/src/sql/workbench/services/notebook/browser/models/notebookModel.ts @@ -22,7 +22,6 @@ import { URI } from 'vs/base/common/uri'; import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { uriPrefixes } from 'sql/platform/connection/common/utils'; -import { keys } from 'vs/base/common/map'; import { ILogService } from 'vs/platform/log/common/log'; import { getErrorMessage } from 'vs/base/common/errors'; import { find, firstIndex } from 'vs/base/common/arrays'; @@ -213,7 +212,7 @@ export class NotebookModel extends Disposable implements INotebookModel { } public standardKernelsDisplayName(): string[] { - return Array.from(keys(this._kernelDisplayNameToNotebookProviderIds)); + return Array.from(this._kernelDisplayNameToNotebookProviderIds.keys()); } public get inErrorState(): boolean { diff --git a/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts b/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts index fc2a570171..8264b27a9a 100644 --- a/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts +++ b/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts @@ -27,7 +27,6 @@ import { IQueryManagementService } from 'sql/workbench/services/query/common/que import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { SqlNotebookProvider } from 'sql/workbench/services/notebook/browser/sql/sqlNotebookProvider'; -import { keys } from 'vs/base/common/map'; import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; import { ILogService } from 'vs/platform/log/common/log'; @@ -269,7 +268,7 @@ export class NotebookService extends Disposable implements INotebookService { } getSupportedFileExtensions(): string[] { - return Array.from(keys(this._fileToProviders)); + return Array.from(this._fileToProviders.keys()); } getProvidersForFileType(fileType: string): string[] { diff --git a/src/sql/workbench/services/query/common/queryManagement.ts b/src/sql/workbench/services/query/common/queryManagement.ts index 1a25b5d687..2d12ca42ab 100644 --- a/src/sql/workbench/services/query/common/queryManagement.ts +++ b/src/sql/workbench/services/query/common/queryManagement.ts @@ -10,7 +10,6 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import * as azdata from 'azdata'; import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; import { Event, Emitter } from 'vs/base/common/event'; -import { keys } from 'vs/base/common/map'; import { assign } from 'vs/base/common/objects'; import { IAdsTelemetryService, ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry'; import EditQueryRunner from 'sql/workbench/services/editData/common/editQueryRunner'; @@ -175,7 +174,7 @@ export class QueryManagementService implements IQueryManagementService { } public getRegisteredProviders(): string[] { - return Array.from(keys(this._requestHandlers)); + return Array.from(this._requestHandlers.keys()); } private addTelemetry(eventName: string, ownerUri: string, runOptions?: ExecutionPlanOptions): void { diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index c3909d78ac..7a7093aca6 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -221,10 +221,13 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende allowedSchemes.push(Schemas.command); } + // values that are too long will freeze the UI + let value = markdown.value ?? ''; + if (value.length > 100_000) { + value = `${value.substr(0, 100_000)}…`; + } const renderedMarkdown = marked.parse( - markdown.supportThemeIcons - ? markdownEscapeEscapedCodicons(markdown.value || '') - : (markdown.value || ''), + markdown.supportThemeIcons ? markdownEscapeEscapedCodicons(value) : value, markedOptions ); diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 98cca67642..73752865a0 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -406,6 +406,7 @@ export interface IActionBarOptions { animated?: boolean; triggerKeys?: ActionTrigger; allowContextMenu?: boolean; + preventLoopNavigation?: boolean; } const defaultOptions: IActionBarOptions = { @@ -509,9 +510,9 @@ export class ActionBar extends Disposable implements IActionRunner { let eventHandled = true; if (event.equals(previousKey)) { - this.focusPrevious(); + eventHandled = this.focusPrevious(); } else if (event.equals(nextKey)) { - this.focusNext(); + eventHandled = this.focusNext(); } else if (event.equals(KeyCode.Escape)) { this._onDidCancel.fire(); } else if (this.isTriggerKeyEvent(event)) { @@ -724,7 +725,7 @@ export class ActionBar extends Disposable implements IActionRunner { if (selectFirst && typeof this.focusedItem === 'undefined') { // Focus the first enabled item - this.focusedItem = this.viewItems.length - 1; + this.focusedItem = -1; this.focusNext(); } else { if (index !== undefined) { @@ -735,7 +736,7 @@ export class ActionBar extends Disposable implements IActionRunner { } } - protected focusNext(): void { + protected focusNext(): boolean { if (typeof this.focusedItem === 'undefined') { this.focusedItem = this.viewItems.length - 1; } @@ -744,6 +745,11 @@ export class ActionBar extends Disposable implements IActionRunner { let item: IActionViewItem; do { + if (this.options.preventLoopNavigation && this.focusedItem + 1 >= this.viewItems.length) { + this.focusedItem = startIndex; + return false; + } + this.focusedItem = (this.focusedItem + 1) % this.viewItems.length; item = this.viewItems[this.focusedItem]; } while (this.focusedItem !== startIndex && !item.isEnabled()); @@ -753,9 +759,10 @@ export class ActionBar extends Disposable implements IActionRunner { } this.updateFocus(); + return true; } - protected focusPrevious(): void { + protected focusPrevious(): boolean { if (typeof this.focusedItem === 'undefined') { this.focusedItem = 0; } @@ -767,6 +774,11 @@ export class ActionBar extends Disposable implements IActionRunner { this.focusedItem = this.focusedItem - 1; if (this.focusedItem < 0) { + if (this.options.preventLoopNavigation) { + this.focusedItem = startIndex; + return false; + } + this.focusedItem = this.viewItems.length - 1; } @@ -778,6 +790,7 @@ export class ActionBar extends Disposable implements IActionRunner { } this.updateFocus(true); + return true; } protected updateFocus(fromRight?: boolean, preventScroll?: boolean): void { diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 691a9c3798..575a779d5d 100644 Binary files a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index f1f6e9c6ac..b09b736630 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -162,6 +162,7 @@ export class ContextView extends Disposable { this.view.className = 'context-view'; this.view.style.top = '0px'; this.view.style.left = '0px'; + this.view.style.position = this.useFixedPosition ? 'fixed' : 'absolute'; DOM.show(this.view); // Render content diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index f30baff33a..27cc471d62 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -175,8 +175,8 @@ export class InputBox extends Widget { this.input.setAttribute('autocapitalize', 'off'); this.input.setAttribute('spellcheck', 'false'); - this.onfocus(this.input, () => dom.addClass(this.element, 'synthetic-focus')); - this.onblur(this.input, () => dom.removeClass(this.element, 'synthetic-focus')); + this.onfocus(this.input, () => this.element.classList.add('synthetic-focus')); + this.onblur(this.input, () => this.element.classList.remove('synthetic-focus')); if (this.options.flexibleHeight) { this.maxHeight = typeof this.options.flexibleMaxHeight === 'number' ? this.options.flexibleMaxHeight : Number.POSITIVE_INFINITY; @@ -392,11 +392,11 @@ export class InputBox extends Widget { public showMessage(message: IMessage, force?: boolean): 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)); + this.element.classList.remove('idle'); + this.element.classList.remove('info'); + this.element.classList.remove('warning'); + this.element.classList.remove('error'); + this.element.classList.add(this.classForType(message.type)); const styles = this.stylesForType(this.message.type); this.element.style.border = styles.border ? `1px solid ${styles.border}` : ''; @@ -409,10 +409,10 @@ export class InputBox extends Widget { 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.element.classList.remove('info'); + this.element.classList.remove('warning'); + this.element.classList.remove('error'); + this.element.classList.add('idle'); this._hideMessage(); this.applyStyles(); @@ -494,7 +494,7 @@ export class InputBox extends Widget { const spanElement = (this.message.formatContent ? renderFormattedText(this.message.content, renderOptions) : renderText(this.message.content, renderOptions)); - dom.addClass(spanElement, this.classForType(this.message.type)); + spanElement.classList.add(this.classForType(this.message.type)); const styles = this.stylesForType(this.message.type); spanElement.style.backgroundColor = styles.background ? styles.background.toString() : ''; @@ -543,7 +543,7 @@ export class InputBox extends Widget { this.validate(); this.updateMirror(); - dom.toggleClass(this.input, 'empty', !this.value); + this.input.classList.toggle('empty', !this.value); if (this.state === 'open' && this.contextViewProvider) { this.contextViewProvider.layout(); diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 7941364db9..c2bdc3ba11 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -411,7 +411,6 @@ export class ListView implements ISpliceable, IDisposable { if (this.supportDynamicHeights) { this._rerender(this.lastRenderTop, this.lastRenderHeight); } - return; } splice(start: number, deleteCount: number, elements: T[] = []): T[] { @@ -790,8 +789,8 @@ export class ListView implements ISpliceable, IDisposable { item.dragStartDisposable.dispose(); const renderer = this.renderers.get(item.templateId); - if (renderer && renderer.disposeElement) { - renderer.disposeElement(item.element, index, item.row!.templateData, item.size); + if (item.row && renderer && renderer.disposeElement) { + renderer.disposeElement(item.element, index, item.row.templateData, item.size); } this.cache.release(item.row!); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index b123935448..1374280514 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -854,6 +854,7 @@ export interface IListOptions { readonly horizontalScrolling?: boolean; readonly additionalScrollHeight?: number; readonly transformOptimization?: boolean; + readonly smoothScrolling?: boolean; } export interface IListStyles { diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index fcf6e34aab..aa54df4c43 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -395,10 +395,9 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } this._register(addDisposableListener(this.element, EventType.MOUSE_UP, e => { - if (e.defaultPrevented) { - return; - } - + // removed default prevention as it conflicts + // with BaseActionViewItem #101537 + // add back if issues arise and link new issue EventHelper.stop(e, true); this.onClick(e); })); diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index b02c483889..bf14a48e8c 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -326,7 +326,15 @@ export class MenuBar extends Disposable { let event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; - if ((event.equals(KeyCode.DownArrow) || event.equals(KeyCode.Enter) || (this.options.compactMode !== undefined && event.equals(KeyCode.Space))) && !this.isOpen) { + const triggerKeys = [KeyCode.Enter]; + if (this.options.compactMode === undefined) { + triggerKeys.push(KeyCode.DownArrow); + } else { + triggerKeys.push(KeyCode.Space); + triggerKeys.push(this.options.compactMode === Direction.Right ? KeyCode.RightArrow : KeyCode.LeftArrow); + } + + if ((triggerKeys.some(k => event.equals(k)) && !this.isOpen)) { this.focusedMenu = { index: MenuBar.OVERFLOW_INDEX }; this.openedViaKeyboard = true; this.focusState = MenubarState.OPEN; diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 88caef66f0..520db591f4 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -17,6 +17,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { getZoomFactor } from 'vs/base/browser/browser'; const HIDE_TIMEOUT = 500; const SCROLL_WHEEL_SENSITIVITY = 50; @@ -130,13 +131,18 @@ export class MouseWheelClassifier { // } } - if (Math.abs(item.deltaX - Math.round(item.deltaX)) > 0 || Math.abs(item.deltaY - Math.round(item.deltaY)) > 0) { + if (!this._isAlmostInt(item.deltaX) || !this._isAlmostInt(item.deltaY)) { // non-integer deltas => indicator that this is not a physical mouse wheel score += 0.25; } return Math.min(Math.max(score, 0), 1); } + + private _isAlmostInt(value: number): boolean { + const delta = Math.abs(Math.round(value) - value); + return (delta < 0.01); + } } export abstract class AbstractScrollableElement extends Widget { @@ -343,10 +349,11 @@ export abstract class AbstractScrollableElement extends Widget { const classifier = MouseWheelClassifier.INSTANCE; if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) { - if (platform.isWindows) { - // On Windows, the incoming delta events are multiplied with the device pixel ratio, - // so to get a better classification, simply undo that. - classifier.accept(Date.now(), e.deltaX / window.devicePixelRatio, e.deltaY / window.devicePixelRatio); + const osZoomFactor = window.devicePixelRatio / getZoomFactor(); + if (platform.isWindows || platform.isLinux) { + // On Windows and Linux, the incoming delta events are multiplied with the OS zoom factor. + // The OS zoom factor can be reverse engineered by using the device pixel ratio and the configured zoom factor into account. + classifier.accept(Date.now(), e.deltaX / osZoomFactor, e.deltaY / osZoomFactor); } else { classifier.accept(Date.now(), e.deltaX, e.deltaY); } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 13b707c2fd..d46b93ff42 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -951,6 +951,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { readonly filterOnType?: boolean; readonly smoothScrolling?: boolean; readonly horizontalScrolling?: boolean; + readonly expandOnlyOnDoubleClick?: boolean; } export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions { @@ -1094,7 +1095,10 @@ class TreeNodeListMouseController extends MouseController< return super.onViewPointer(e); } - const onTwistie = hasClass(e.browserEvent.target as HTMLElement, 'monaco-tl-twistie'); + const target = e.browserEvent.target as HTMLElement; + const onTwistie = hasClass(target, 'monaco-tl-twistie') + || (hasClass(target, 'monaco-icon-label') && hasClass(target, 'folder-icon') && e.browserEvent.offsetX < 16); + let expandOnlyOnTwistieClick = false; if (typeof this.tree.expandOnlyOnTwistieClick === 'function') { @@ -1107,6 +1111,10 @@ class TreeNodeListMouseController extends MouseController< return super.onViewPointer(e); } + if (this.tree.expandOnlyOnDoubleClick && e.browserEvent.detail !== 2 && !onTwistie) { + return super.onViewPointer(e); + } + if (node.collapsible) { const model = ((this.tree as any).model as ITreeModel); // internal const location = model.getNodeLocation(node); @@ -1244,6 +1252,7 @@ export abstract class AbstractTree implements IDisposable get filterOnType(): boolean { return !!this._options.filterOnType; } get onDidChangeTypeFilterPattern(): Event { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; } + get expandOnlyOnDoubleClick(): boolean { return this._options.expandOnlyOnDoubleClick ?? false; } get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; } private readonly _onDidUpdateOptions = new Emitter>(); diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 9a7e2d4762..772bd0b824 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -591,6 +591,14 @@ export function asArray(x: T | T[]): T[] { return Array.isArray(x) ? x : [x]; } +export function toArray(iterable: IterableIterator): T[] { + const result: T[] = []; + for (let element of iterable) { + result.push(element); + } + return result; +} + export function getRandomElement(arr: T[]): T | undefined { return arr[Math.floor(Math.random() * arr.length)]; } diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index af7c151f0c..496875b922 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -52,6 +52,8 @@ export class Codicon { _registry.add(this); } public get classNames() { return 'codicon codicon-' + this.id; } + // classNamesArray is useful for migrating to ES6 classlist + public get classNamesArray() { return ['codicon', 'codicon-' + this.id]; } public get cssSelector() { return '.codicon.codicon-' + this.id; } } @@ -472,6 +474,8 @@ export namespace Codicon { export const stopCircle = new Codicon('stop-circle', { character: '\\eba5' }); export const playCircle = new Codicon('play-circle', { character: '\\eba6' }); export const record = new Codicon('record', { character: '\\eba7' }); + export const debugAltSmall = new Codicon('debug-alt-small', { character: '\\eba8' }); + export const vmConnect = new Codicon('vm-connect', { character: '\\eba9' }); } diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 792526cdec..ba5434f2c1 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -203,3 +203,12 @@ export class NotImplementedError extends Error { } } } + +export class NotSupportedError extends Error { + constructor(message?: string) { + super('NotSupported'); + if (message) { + this.message = message; + } + } +} diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index d2c8abd615..a0b41f5901 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -42,8 +42,7 @@ export function scoreFuzzy(target: string, query: string, queryLower: string, fu // When not searching fuzzy, we require the query to be contained fully // in the target string contiguously. if (!fuzzy) { - const indexOfQueryInTarget = targetLower.indexOf(queryLower); - if (indexOfQueryInTarget === -1) { + if (!targetLower.includes(queryLower)) { // if (DEBUG) { // console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`); // } diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index b479cd4fb3..5bc4fc6bf4 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -6,6 +6,7 @@ import { equals } from 'vs/base/common/arrays'; import { UriComponents } from 'vs/base/common/uri'; import { escapeCodicons } from 'vs/base/common/codicons'; +import { illegalArgument } from 'vs/base/common/errors'; export interface IMarkdownString { readonly value: string; @@ -22,6 +23,10 @@ export class MarkdownString implements IMarkdownString { private _value: string = '', isTrustedOrOptions: boolean | { isTrusted?: boolean, supportThemeIcons?: boolean } = false, ) { + if (typeof this._value !== 'string') { + throw illegalArgument('value'); + } + if (typeof isTrustedOrOptions === 'boolean') { this._isTrusted = isTrustedOrOptions; this._supportThemeIcons = false; @@ -30,7 +35,6 @@ export class MarkdownString implements IMarkdownString { this._isTrusted = isTrustedOrOptions.isTrusted ?? false; this._supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; } - } get value() { return this._value; } diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 328cfffe57..80ecca6cd4 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -9,27 +9,6 @@ import { compareSubstringIgnoreCase, compare, compareSubstring } from 'vs/base/c import { Schemas } from 'vs/base/common/network'; import { isLinux } from 'vs/base/common/platform'; -/** - * @deprecated ES6: use `[...SetOrMap.values()]` - */ -export function values(set: Set): V[]; -export function values(map: Map): V[]; -export function values(forEachable: { forEach(callback: (value: V, ...more: any[]) => any): void }): V[] { - const result: V[] = []; - forEachable.forEach(value => result.push(value)); - return result; -} - -/** - * @deprecated ES6: use `[...map.keys()]` - */ -export function keys(map: Map): K[] { - const result: K[] = []; - map.forEach((_value, key) => result.push(key)); - - return result; -} - export function getOrSet(map: Map, key: K, value: V): V { let result = map.get(key); if (result === undefined) { diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index 03f3019ac9..54df373e1c 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -5,7 +5,6 @@ import { basename, posix, extname } from 'vs/base/common/path'; import { startsWithUTF8BOM } from 'vs/base/common/strings'; -import { coalesce } from 'vs/base/common/arrays'; import { match } from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; @@ -247,34 +246,6 @@ export function isUnspecific(mime: string[] | string): boolean { return mime.length === 1 && isUnspecific(mime[0]); } -/** - * Returns a suggestion for the filename by the following logic: - * 1. If a relevant extension exists and is an actual filename extension (starting with a dot), suggest the prefix appended by the first one. - * 2. Otherwise, if there are other extensions, suggest the first one. - * 3. Otherwise, suggest the prefix. - */ -export function suggestFilename(mode: string | undefined, prefix: string): string { - const extensions = registeredAssociations - .filter(assoc => !assoc.userConfigured && assoc.extension && assoc.id === mode) - .map(assoc => assoc.extension); - - const extensionsWithDotFirst = coalesce(extensions) - .filter(assoc => assoc.startsWith('.')); - - if (extensionsWithDotFirst.length > 0) { - const candidateExtension = extensionsWithDotFirst[0]; - if (prefix.endsWith(candidateExtension)) { - // do not add the prefix if it already exists - // https://github.com/microsoft/vscode/issues/83603 - return prefix; - } - - return prefix + candidateExtension; - } - - return extensions[0] || prefix; -} - interface MapExtToMediaMimes { [index: string]: string; } diff --git a/src/vs/base/common/performance.js b/src/vs/base/common/performance.js index c3891415b0..e1387b65d5 100644 --- a/src/vs/base/common/performance.js +++ b/src/vs/base/common/performance.js @@ -9,22 +9,22 @@ function _factory(sharedObj) { - sharedObj._performanceEntries = sharedObj._performanceEntries || []; + sharedObj.MonacoPerformanceMarks = sharedObj.MonacoPerformanceMarks || []; const _dataLen = 2; const _timeStamp = typeof console.timeStamp === 'function' ? console.timeStamp.bind(console) : () => { }; function importEntries(entries) { - sharedObj._performanceEntries.splice(0, 0, ...entries); + sharedObj.MonacoPerformanceMarks.splice(0, 0, ...entries); } function exportEntries() { - return sharedObj._performanceEntries.slice(0); + return sharedObj.MonacoPerformanceMarks.slice(0); } function getEntries() { const result = []; - const entries = sharedObj._performanceEntries; + const entries = sharedObj.MonacoPerformanceMarks; for (let i = 0; i < entries.length; i += _dataLen) { result.push({ name: entries[i], @@ -35,7 +35,7 @@ function _factory(sharedObj) { } function getDuration(from, to) { - const entries = sharedObj._performanceEntries; + const entries = sharedObj.MonacoPerformanceMarks; let target = to; let endIndex = 0; for (let i = entries.length - _dataLen; i >= 0; i -= _dataLen) { @@ -54,7 +54,7 @@ function _factory(sharedObj) { } function mark(name) { - sharedObj._performanceEntries.push(name, Date.now()); + sharedObj.MonacoPerformanceMarks.push(name, Date.now()); _timeStamp(name); } @@ -73,7 +73,8 @@ function _factory(sharedObj) { // Because we want both instances to use the same perf-data // we store them globally -let sharedObj; +// eslint-disable-next-line no-var +var sharedObj; if (typeof global === 'object') { // nodejs sharedObj = global; @@ -91,5 +92,5 @@ if (typeof define === 'function') { // commonjs module.exports = _factory(sharedObj); } else { - // invalid context... + sharedObj.perf = _factory(sharedObj); } diff --git a/src/vs/base/common/stream.ts b/src/vs/base/common/stream.ts index 331abf973a..d9a90ef5d9 100644 --- a/src/vs/base/common/stream.ts +++ b/src/vs/base/common/stream.ts @@ -474,7 +474,7 @@ export function peekReadable(readable: Readable, reducer: IReducer, max /** * Helper to fully read a T stream into a T. */ -export function consumeStream(stream: ReadableStream, reducer: IReducer): Promise { +export function consumeStream(stream: ReadableStreamEvents, reducer: IReducer): Promise { return new Promise((resolve, reject) => { const chunks: T[] = []; diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 9d6681764e..acd21d9eea 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; /** * @returns whether the provided parameter is a JavaScript Array or not. */ -export function isArray(array: any): array is any[] { +export function isArray(array: T | {}): array is T extends readonly any[] ? (unknown extends T ? never : readonly any[]) : any[] { return Array.isArray(array); } @@ -171,7 +171,7 @@ export function validateConstraint(arg: any, constraint: TypeConstraint | undefi if (arg instanceof constraint) { return; } - } catch{ + } catch { // ignore } if (!isUndefinedOrNull(arg) && arg.constructor === constraint) { diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 447e47d9ac..3a04006795 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -252,7 +252,7 @@ export class URI implements UriComponents { return this; } - return new _URI(scheme, authority, path, query, fragment); + return new CachingURI(scheme, authority, path, query, fragment); } // ---- parse & validate ------------------------ @@ -266,9 +266,9 @@ export class URI implements UriComponents { static parse(value: string, _strict: boolean = false): URI { const match = _regexp.exec(value); if (!match) { - return new _URI(_empty, _empty, _empty, _empty, _empty); + return new CachingURI(_empty, _empty, _empty, _empty, _empty); } - return new _URI( + return new CachingURI( match[2] || _empty, percentDecode(match[4] || _empty), percentDecode(match[5] || _empty), @@ -323,11 +323,11 @@ export class URI implements UriComponents { } } - return new _URI('file', authority, path, _empty, _empty); + return new CachingURI('file', authority, path, _empty, _empty); } static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI { - return new _URI( + return new CachingURI( components.scheme, components.authority, components.path, @@ -388,7 +388,7 @@ export class URI implements UriComponents { } else if (data instanceof URI) { return data; } else { - const result = new _URI(data); + const result = new CachingURI(data); result._formatted = (data).external; result._fsPath = (data)._sep === _pathSepMarker ? (data).fsPath : null; return result; @@ -413,8 +413,8 @@ interface UriState extends UriComponents { const _pathSepMarker = isWindows ? 1 : undefined; -// eslint-disable-next-line @typescript-eslint/naming-convention -class _URI extends URI { +// This class exists so that URI is compatibile with vscode.Uri (API). +class CachingURI extends URI { _formatted: string | null = null; _fsPath: string | null = null; diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 5fc3ef43f1..16e3721f0c 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -457,7 +457,7 @@ export namespace win32 { async function fileExists(path: string): Promise { if (await promisify(fs.exists)(path)) { - return !((await promisify(fs.stat)(path)).isDirectory); + return !((await promisify(fs.stat)(path)).isDirectory()); } return false; } diff --git a/src/vs/base/parts/contextmenu/common/contextmenu.ts b/src/vs/base/parts/contextmenu/common/contextmenu.ts index ce3f446359..a5aaa61441 100644 --- a/src/vs/base/parts/contextmenu/common/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/common/contextmenu.ts @@ -36,8 +36,7 @@ export interface IPopupOptions { x?: number; y?: number; positioningItem?: number; - onHide?: () => void; } export const CONTEXT_MENU_CHANNEL = 'vscode:contextmenu'; -export const CONTEXT_MENU_CLOSE_CHANNEL = 'vscode:onCloseContextMenu'; \ No newline at end of file +export const CONTEXT_MENU_CLOSE_CHANNEL = 'vscode:onCloseContextMenu'; diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts index 6d7fc4c11c..a679309536 100644 --- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -9,9 +9,10 @@ import { ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_ export function registerContextMenuListener(): void { ipcMain.on(CONTEXT_MENU_CHANNEL, (event: IpcMainEvent, contextMenuId: number, items: ISerializableContextMenuItem[], onClickChannel: string, options?: IPopupOptions) => { const menu = createMenu(event, onClickChannel, items); + const window = BrowserWindow.fromWebContents(event.sender); menu.popup({ - window: BrowserWindow.fromWebContents(event.sender), + window: window ? window : undefined, x: options ? options.x : undefined, y: options ? options.y : undefined, positioningItem: options ? options.positioningItem : undefined, diff --git a/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts index ab14d788a6..0379ab0925 100644 --- a/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts @@ -8,7 +8,7 @@ import { IContextMenuItem, ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHAN let contextMenuIdPool = 0; -export function popup(items: IContextMenuItem[], options?: IPopupOptions): void { +export function popup(items: IContextMenuItem[], options?: IPopupOptions, onHide?: () => void): void { const processedItems: IContextMenuItem[] = []; const contextMenuId = contextMenuIdPool++; @@ -28,8 +28,8 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions): void ipcRenderer.removeListener(onClickChannel, onClickChannelHandler); - if (options?.onHide) { - options.onHide(); + if (onHide) { + onHide(); } }); diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 361b82f816..a1775a9ac9 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -1230,10 +1230,10 @@ export class QuickInputController extends Disposable { this.previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined; }, true)); this._register(focusTracker.onDidBlur(() => { - this.previousFocusElement = undefined; if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) { - this.hide(true); + this.hide(); } + this.previousFocusElement = undefined; })); this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => { inputBox.setFocus(); @@ -1574,13 +1574,14 @@ export class QuickInputController extends Disposable { } } - hide(focusLost?: boolean) { + hide() { const controller = this.controller; if (controller) { + const focusChanged = !this.ui?.container.contains(document.activeElement); this.controller = null; this.onHideEmitter.fire(); this.getUI().container.style.display = 'none'; - if (!focusLost) { + if (!focusChanged) { if (this.previousFocusElement && this.previousFocusElement.offsetParent) { this.previousFocusElement.focus(); this.previousFocusElement = undefined; diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js index 47abbf0d02..bb4cec959a 100644 --- a/src/vs/base/parts/sandbox/electron-browser/preload.js +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -68,14 +68,6 @@ */ webFrame: { - getZoomFactor() { - return webFrame.getZoomFactor(); - }, - - getZoomLevel() { - return webFrame.getZoomLevel(); - }, - /** * @param {number} level */ diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts index 812e97e3a1..5449340452 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -43,16 +43,6 @@ export const ipcRenderer = (window as any).vscode.ipcRenderer as { export const webFrame = (window as any).vscode.webFrame as { - /** - * The current zoom factor. - */ - getZoomFactor(): number; - - /** - * The current zoom level. - */ - getZoomLevel(): number; - /** * 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 diff --git a/src/vs/base/test/common/mime.test.ts b/src/vs/base/test/common/mime.test.ts index e7788ae2eb..2a9d335be4 100644 --- a/src/vs/base/test/common/mime.test.ts +++ b/src/vs/base/test/common/mime.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { guessMimeTypes, registerTextMime, suggestFilename } from 'vs/base/common/mime'; +import { guessMimeTypes, registerTextMime } from 'vs/base/common/mime'; import { URI } from 'vs/base/common/uri'; suite('Mime', () => { @@ -126,53 +126,4 @@ suite('Mime', () => { assert.deepEqual(guessMimeTypes(URI.parse(`data:;label:something.data;description:data,`)), ['text/data', 'text/plain']); }); - - test('Filename Suggestion - Suggest prefix only when there are no relevant extensions', () => { - const id = 'plumbus0'; - const mime = `text/${id}`; - for (let extension of ['one', 'two']) { - registerTextMime({ id, mime, extension }); - } - - let suggested = suggestFilename('shleem', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1'); - }); - - test('Filename Suggestion - Suggest prefix with first extension that begins with a dot', () => { - const id = 'plumbus1'; - const mime = `text/${id}`; - for (let extension of ['plumbus', '.shleem', '.gazorpazorp']) { - registerTextMime({ id, mime, extension }); - } - - let suggested = suggestFilename('plumbus1', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1.shleem'); - }); - - test('Filename Suggestion - Suggest first relevant extension when there are none that begin with a dot', () => { - const id = 'plumbus2'; - const mime = `text/${id}`; - for (let extension of ['plumbus', 'shleem', 'gazorpazorp']) { - registerTextMime({ id, mime, extension }); - } - - let suggested = suggestFilename('plumbus2', 'Untitled-1'); - assert.equal(suggested, 'plumbus'); - }); - - test('Filename Suggestion - Should ignore user-configured associations', () => { - registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: 'plumbus', userConfigured: true }); - registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: '.shleem', userConfigured: true }); - registerTextMime({ id: 'plumbus3', mime: 'text/plumbus3', extension: '.gazorpazorp', userConfigured: false }); - - let suggested = suggestFilename('plumbus3', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1.gazorpazorp'); - - registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: 'plumbus', userConfigured: true }); - registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: '.shleem', userConfigured: true }); - registerTextMime({ id: 'plumbus4', mime: 'text/plumbus4', extension: '.gazorpazorp', userConfigured: true }); - - suggested = suggestFilename('plumbus4', 'Untitled-1'); - assert.equal(suggested, 'Untitled-1'); - }); }); diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index 02a8fd68ad..2619d72ac3 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -2,6 +2,10 @@ + @@ -29,6 +33,7 @@ // NOTE: Changes to inline scripts require update of content security policy self.require = { baseUrl: `${window.location.origin}/static/out`, + recordStats: true, paths: { 'vscode-textmate': `${window.location.origin}/static/remote/web/node_modules/vscode-textmate/release/main`, 'vscode-oniguruma': `${window.location.origin}/static/remote/web/node_modules/vscode-oniguruma/release/main`, @@ -90,7 +95,11 @@ + + @@ -30,6 +34,7 @@ // NOTE: Changes to inline scripts require update of content security policy self.require = { baseUrl: `${window.location.origin}/static/out`, + recordStats: true, paths: { 'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`, 'vscode-oniguruma': `${window.location.origin}/static/node_modules/vscode-oniguruma/release/main`, @@ -79,6 +84,9 @@ + diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index d414140029..adfeed1c32 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -12,6 +12,7 @@ import { request } from 'vs/base/parts/request/browser/request'; import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows'; import { isEqual } from 'vs/base/common/resources'; import { isStandalone } from 'vs/base/browser/browser'; +import { mark } from 'vs/base/common/performance'; interface ICredential { service: string; @@ -277,6 +278,10 @@ class WorkspaceProvider implements IWorkspaceProvider { (function () { + // Mark start of workbench + mark('didLoadWorkbenchMain'); + performance.mark('workbench-start'); + // Find config by checking for DOM const configElement = document.getElementById('vscode-workbench-web-configuration'); const configElementAttribute = configElement ? configElement.getAttribute('data-settings') : undefined; diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index c86fe478c8..184a195841 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/issueReporter'; -import { ElectronService, IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; -import { ipcRenderer, webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded import * as os from 'os'; -import * as browser from 'vs/base/browser/browser'; +import { ElectronService, IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { applyZoom, zoomIn, zoomOut } from 'vs/platform/windows/electron-sandbox/window'; import { $, windowOpenNoOpener, addClass } from 'vs/base/browser/dom'; import { Button } from 'vs/base/browser/ui/button/button'; -import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel'; import * as collections from 'vs/base/common/collections'; import { debounce } from 'vs/base/common/decorators'; @@ -152,7 +152,7 @@ export class IssueReporter extends Disposable { this.setUpTypes(); this.setEventHandlers(); - this.applyZoom(configuration.data.zoomLevel); + applyZoom(configuration.data.zoomLevel); this.applyStyles(configuration.data.styles); this.handleExtensionData(configuration.data.enabledExtensions); @@ -180,15 +180,6 @@ export class IssueReporter extends Disposable { } } - private applyZoom(zoomLevel: number) { - webFrame.setZoomLevel(zoomLevel); - 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); - } - private applyStyles(styles: IssueReporterStyles) { const styleTag = document.createElement('style'); const content: string[] = []; @@ -505,12 +496,12 @@ export class IssueReporter extends Disposable { // Cmd/Ctrl + zooms in if (cmdOrCtrlKey && e.keyCode === 187) { - this.applyZoom(webFrame.getZoomLevel() + 1); + zoomIn(); } // Cmd/Ctrl - zooms out if (cmdOrCtrlKey && e.keyCode === 189) { - this.applyZoom(webFrame.getZoomLevel() - 1); + zoomOut(); } // With latest electron upgrade, cmd+a is no longer propagating correctly for inputs in this window on mac diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts index 6a4eb290e6..77643c5f7c 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -5,13 +5,12 @@ import 'vs/css!./media/processExplorer'; import { clipboard } from 'electron'; -import { webFrame, ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; -import { repeat } from 'vs/base/common/strings'; import { totalmem } from 'os'; +import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import product from 'vs/platform/product/common/product'; import { localize } from 'vs/nls'; import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/common/issue'; -import * as browser from 'vs/base/browser/browser'; +import { applyZoom, zoomIn, zoomOut } from 'vs/platform/windows/electron-sandbox/window'; import * as platform from 'vs/base/common/platform'; import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu'; @@ -20,13 +19,8 @@ import { addDisposableListener, addClass } from 'vs/base/browser/dom'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; -let mapPidToWindowTitle = new Map(); - const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/; const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/; -const listeners = new DisposableStore(); -const collapsedStateCache: Map = new Map(); -let lastRequestTime: number; interface FormattedProcessItem { cpu: number; @@ -37,391 +31,400 @@ interface FormattedProcessItem { cmd: string; } -function getProcessList(rootProcess: ProcessItem, isLocal: boolean): FormattedProcessItem[] { - const processes: FormattedProcessItem[] = []; +class ProcessExplorer { + private lastRequestTime: number; - if (rootProcess) { - getProcessItem(processes, rootProcess, 0, isLocal); + private collapsedStateCache: Map = new Map(); + + private mapPidToWindowTitle = new Map(); + + private listeners = new DisposableStore(); + + constructor(data: ProcessExplorerData) { + this.applyStyles(data.styles); + + // Map window process pids to titles, annotate process names with this when rendering to distinguish between them + ipcRenderer.on('vscode:windowsInfoResponse', (event: unknown, windows: any[]) => { + this.mapPidToWindowTitle = new Map(); + windows.forEach(window => this.mapPidToWindowTitle.set(window.pid, window.title)); + }); + + ipcRenderer.on('vscode:listProcessesResponse', (event: unknown, processRoots: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]) => { + this.updateProcessInfo(processRoots); + this.requestProcessList(0); + }); + + this.lastRequestTime = Date.now(); + ipcRenderer.send('vscode:windowsInfoRequest'); + ipcRenderer.send('vscode:listProcesses'); } - return processes; -} + private getProcessList(rootProcess: ProcessItem, isLocal: boolean): FormattedProcessItem[] { + const processes: FormattedProcessItem[] = []; -function getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean): void { - const isRoot = (indent === 0); + if (rootProcess) { + this.getProcessItem(processes, rootProcess, 0, isLocal); + } - const MB = 1024 * 1024; - - let name = item.name; - if (isRoot) { - name = isLocal ? `${product.applicationName} main` : 'remote agent'; + return processes; } - if (name === 'window') { - const windowTitle = mapPidToWindowTitle.get(item.pid); - name = windowTitle !== undefined ? `${name} (${mapPidToWindowTitle.get(item.pid)})` : name; + private getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean): void { + const isRoot = (indent === 0); + + const MB = 1024 * 1024; + + let name = item.name; + if (isRoot) { + name = isLocal ? `${product.applicationName} main` : 'remote agent'; + } + + if (name === 'window') { + const windowTitle = this.mapPidToWindowTitle.get(item.pid); + name = windowTitle !== undefined ? `${name} (${this.mapPidToWindowTitle.get(item.pid)})` : name; + } + + // Format name with indent + const formattedName = isRoot ? name : `${' '.repeat(indent)} ${name}`; + const memory = process.platform === 'win32' ? item.mem : (totalmem() * (item.mem / 100)); + processes.push({ + cpu: item.load, + memory: (memory / MB), + pid: item.pid.toFixed(0), + name, + formattedName, + cmd: item.cmd + }); + + // Recurse into children if any + if (Array.isArray(item.children)) { + item.children.forEach(child => { + if (child) { + this.getProcessItem(processes, child, indent + 1, isLocal); + } + }); + } } - // Format name with indent - const formattedName = isRoot ? name : `${repeat(' ', indent)} ${name}`; - const memory = process.platform === 'win32' ? item.mem : (totalmem() * (item.mem / 100)); - processes.push({ - cpu: item.load, - memory: (memory / MB), - pid: item.pid.toFixed(0), - name, - formattedName, - cmd: item.cmd - }); + private isDebuggable(cmd: string): boolean { + const matches = DEBUG_FLAGS_PATTERN.exec(cmd); + return (matches && matches.length >= 2) || cmd.indexOf('node ') >= 0 || cmd.indexOf('node.exe') >= 0; + } - // Recurse into children if any - if (Array.isArray(item.children)) { - item.children.forEach(child => { - if (child) { - getProcessItem(processes, child, indent + 1, isLocal); + private attachTo(item: FormattedProcessItem) { + const config: any = { + type: 'node', + request: 'attach', + name: `process ${item.pid}` + }; + + let matches = DEBUG_FLAGS_PATTERN.exec(item.cmd); + if (matches && matches.length >= 2) { + // attach via port + if (matches.length === 4 && matches[3]) { + config.port = parseInt(matches[3]); + } + config.protocol = matches[1] === 'debug' ? 'legacy' : 'inspector'; + } else { + // no port -> try to attach via pid (send SIGUSR1) + config.processId = String(item.pid); + } + + // a debug-port=n or inspect-port=n overrides the port + matches = DEBUG_PORT_PATTERN.exec(item.cmd); + if (matches && matches.length === 3) { + // override port + config.port = parseInt(matches[2]); + } + + ipcRenderer.send('vscode:workbenchCommand', { id: 'debug.startFromConfig', from: 'processExplorer', args: [config] }); + } + + private getProcessIdWithHighestProperty(processList: any[], propertyName: string) { + let max = 0; + let maxProcessId; + processList.forEach(process => { + if (process[propertyName] > max) { + max = process[propertyName]; + maxProcessId = process.pid; } }); + + return maxProcessId; } -} -function isDebuggable(cmd: string): boolean { - const matches = DEBUG_FLAGS_PATTERN.exec(cmd); - return (matches && matches.length >= 2) || cmd.indexOf('node ') >= 0 || cmd.indexOf('node.exe') >= 0; -} - -function attachTo(item: FormattedProcessItem) { - const config: any = { - type: 'node', - request: 'attach', - name: `process ${item.pid}` - }; - - let matches = DEBUG_FLAGS_PATTERN.exec(item.cmd); - if (matches && matches.length >= 2) { - // attach via port - if (matches.length === 4 && matches[3]) { - config.port = parseInt(matches[3]); + private updateSectionCollapsedState(shouldExpand: boolean, body: HTMLElement, twistie: HTMLImageElement, sectionName: string) { + if (shouldExpand) { + body.classList.remove('hidden'); + this.collapsedStateCache.set(sectionName, false); + twistie.src = './media/expanded.svg'; + } else { + body.classList.add('hidden'); + this.collapsedStateCache.set(sectionName, true); + twistie.src = './media/collapsed.svg'; } - config.protocol = matches[1] === 'debug' ? 'legacy' : 'inspector'; - } else { - // no port -> try to attach via pid (send SIGUSR1) - config.processId = String(item.pid); } - // a debug-port=n or inspect-port=n overrides the port - matches = DEBUG_PORT_PATTERN.exec(item.cmd); - if (matches && matches.length === 3) { - // override port - config.port = parseInt(matches[2]); - } - - ipcRenderer.send('vscode:workbenchCommand', { id: 'debug.startFromConfig', from: 'processExplorer', args: [config] }); -} - -function getProcessIdWithHighestProperty(processList: any[], propertyName: string) { - let max = 0; - let maxProcessId; - processList.forEach(process => { - if (process[propertyName] > max) { - max = process[propertyName]; - maxProcessId = process.pid; + private renderProcessFetchError(sectionName: string, errorMessage: string) { + const container = document.getElementById('process-list'); + if (!container) { + return; } - }); - return maxProcessId; -} + const body = document.createElement('tbody'); -function updateSectionCollapsedState(shouldExpand: boolean, body: HTMLElement, twistie: HTMLImageElement, sectionName: string) { - if (shouldExpand) { - body.classList.remove('hidden'); - collapsedStateCache.set(sectionName, false); - twistie.src = './media/expanded.svg'; - } else { - body.classList.add('hidden'); - collapsedStateCache.set(sectionName, true); - twistie.src = './media/collapsed.svg'; - } -} + this.renderProcessGroupHeader(sectionName, body, container); -function renderProcessFetchError(sectionName: string, errorMessage: string) { - const container = document.getElementById('process-list'); - if (!container) { - return; + const errorRow = document.createElement('tr'); + const data = document.createElement('td'); + data.textContent = errorMessage; + data.className = 'error'; + data.colSpan = 4; + errorRow.appendChild(data); + + body.appendChild(errorRow); + container.appendChild(body); } - const body = document.createElement('tbody'); + private renderProcessGroupHeader(sectionName: string, body: HTMLElement, container: HTMLElement) { + const headerRow = document.createElement('tr'); + const data = document.createElement('td'); + data.textContent = sectionName; + data.colSpan = 4; + headerRow.appendChild(data); - renderProcessGroupHeader(sectionName, body, container); + const twistie = document.createElement('img'); + this.updateSectionCollapsedState(!this.collapsedStateCache.get(sectionName), body, twistie, sectionName); + data.prepend(twistie); - const errorRow = document.createElement('tr'); - const data = document.createElement('td'); - data.textContent = errorMessage; - data.className = 'error'; - data.colSpan = 4; - errorRow.appendChild(data); - - body.appendChild(errorRow); - container.appendChild(body); -} - -function renderProcessGroupHeader(sectionName: string, body: HTMLElement, container: HTMLElement) { - const headerRow = document.createElement('tr'); - const data = document.createElement('td'); - data.textContent = sectionName; - data.colSpan = 4; - headerRow.appendChild(data); - - const twistie = document.createElement('img'); - updateSectionCollapsedState(!collapsedStateCache.get(sectionName), body, twistie, sectionName); - data.prepend(twistie); - - listeners.add(addDisposableListener(data, 'click', (e) => { - const isHidden = body.classList.contains('hidden'); - updateSectionCollapsedState(isHidden, body, twistie, sectionName); - })); - - container.appendChild(headerRow); -} - -function renderTableSection(sectionName: string, processList: FormattedProcessItem[], renderManySections: boolean, sectionIsLocal: boolean): void { - const container = document.getElementById('process-list'); - if (!container) { - return; - } - - const highestCPUProcess = getProcessIdWithHighestProperty(processList, 'cpu'); - const highestMemoryProcess = getProcessIdWithHighestProperty(processList, 'memory'); - - const body = document.createElement('tbody'); - - if (renderManySections) { - renderProcessGroupHeader(sectionName, body, container); - } - - processList.forEach(p => { - const row = document.createElement('tr'); - row.id = p.pid.toString(); - - const cpu = document.createElement('td'); - p.pid === highestCPUProcess - ? cpu.classList.add('centered', 'highest') - : cpu.classList.add('centered'); - cpu.textContent = p.cpu.toFixed(0); - - const memory = document.createElement('td'); - p.pid === highestMemoryProcess - ? memory.classList.add('centered', 'highest') - : memory.classList.add('centered'); - memory.textContent = p.memory.toFixed(0); - - const pid = document.createElement('td'); - pid.classList.add('centered'); - pid.textContent = p.pid; - - const name = document.createElement('th'); - name.scope = 'row'; - name.classList.add('data'); - name.title = p.cmd; - name.textContent = p.formattedName; - - row.append(cpu, memory, pid, name); - - listeners.add(addDisposableListener(row, 'contextmenu', (e) => { - showContextMenu(e, p, sectionIsLocal); + this.listeners.add(addDisposableListener(data, 'click', (e) => { + const isHidden = body.classList.contains('hidden'); + this.updateSectionCollapsedState(isHidden, body, twistie, sectionName); })); - body.appendChild(row); - }); - - container.appendChild(body); -} - -function updateProcessInfo(processLists: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]): void { - const container = document.getElementById('process-list'); - if (!container) { - return; + container.appendChild(headerRow); } - container.innerHTML = ''; - listeners.clear(); - - const tableHead = document.createElement('thead'); - tableHead.innerHTML = ` - ${localize('cpu', "CPU %")} - ${localize('memory', "Memory (MB)")} - ${localize('pid', "pid")} - ${localize('name', "Name")} - `; - - container.append(tableHead); - - const hasMultipleMachines = Object.keys(processLists).length > 1; - processLists.forEach((remote, i) => { - const isLocal = i === 0; - if (isRemoteDiagnosticError(remote.rootProcess)) { - renderProcessFetchError(remote.name, remote.rootProcess.errorMessage); - } else { - renderTableSection(remote.name, getProcessList(remote.rootProcess, isLocal), hasMultipleMachines, isLocal); + private renderTableSection(sectionName: string, processList: FormattedProcessItem[], renderManySections: boolean, sectionIsLocal: boolean): void { + const container = document.getElementById('process-list'); + if (!container) { + return; } - }); -} -function applyStyles(styles: ProcessExplorerStyles): void { - const styleTag = document.createElement('style'); - const content: string[] = []; + const highestCPUProcess = this.getProcessIdWithHighestProperty(processList, 'cpu'); + const highestMemoryProcess = this.getProcessIdWithHighestProperty(processList, 'memory'); - if (styles.hoverBackground) { - content.push(`tbody > tr:hover, table > tr:hover { background-color: ${styles.hoverBackground}; }`); + const body = document.createElement('tbody'); + + if (renderManySections) { + this.renderProcessGroupHeader(sectionName, body, container); + } + + processList.forEach(p => { + const row = document.createElement('tr'); + row.id = p.pid.toString(); + + const cpu = document.createElement('td'); + p.pid === highestCPUProcess + ? cpu.classList.add('centered', 'highest') + : cpu.classList.add('centered'); + cpu.textContent = p.cpu.toFixed(0); + + const memory = document.createElement('td'); + p.pid === highestMemoryProcess + ? memory.classList.add('centered', 'highest') + : memory.classList.add('centered'); + memory.textContent = p.memory.toFixed(0); + + const pid = document.createElement('td'); + pid.classList.add('centered'); + pid.textContent = p.pid; + + const name = document.createElement('th'); + name.scope = 'row'; + name.classList.add('data'); + name.title = p.cmd; + name.textContent = p.formattedName; + + row.append(cpu, memory, pid, name); + + this.listeners.add(addDisposableListener(row, 'contextmenu', (e) => { + this.showContextMenu(e, p, sectionIsLocal); + })); + + body.appendChild(row); + }); + + container.appendChild(body); } - if (styles.hoverForeground) { - content.push(`tbody > tr:hover, table > tr:hover { color: ${styles.hoverForeground}; }`); + private updateProcessInfo(processLists: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]): void { + const container = document.getElementById('process-list'); + if (!container) { + return; + } + + container.innerHTML = ''; + this.listeners.clear(); + + const tableHead = document.createElement('thead'); + tableHead.innerHTML = ` + ${localize('cpu', "CPU %")} + ${localize('memory', "Memory (MB)")} + ${localize('pid', "pid")} + ${localize('name', "Name")} + `; + + container.append(tableHead); + + const hasMultipleMachines = Object.keys(processLists).length > 1; + processLists.forEach((remote, i) => { + const isLocal = i === 0; + if (isRemoteDiagnosticError(remote.rootProcess)) { + this.renderProcessFetchError(remote.name, remote.rootProcess.errorMessage); + } else { + this.renderTableSection(remote.name, this.getProcessList(remote.rootProcess, isLocal), hasMultipleMachines, isLocal); + } + }); } - if (styles.highlightForeground) { - content.push(`.highest { color: ${styles.highlightForeground}; }`); + private applyStyles(styles: ProcessExplorerStyles): void { + const styleTag = document.createElement('style'); + const content: string[] = []; + + if (styles.hoverBackground) { + content.push(`tbody > tr:hover, table > tr:hover { background-color: ${styles.hoverBackground}; }`); + } + + if (styles.hoverForeground) { + content.push(`tbody > tr:hover, table > tr:hover { color: ${styles.hoverForeground}; }`); + } + + if (styles.highlightForeground) { + content.push(`.highest { color: ${styles.highlightForeground}; }`); + } + + styleTag.innerHTML = content.join('\n'); + if (document.head) { + document.head.appendChild(styleTag); + } + if (styles.color) { + document.body.style.color = styles.color; + } } - styleTag.innerHTML = content.join('\n'); - if (document.head) { - document.head.appendChild(styleTag); - } - if (styles.color) { - document.body.style.color = styles.color; - } -} + private showContextMenu(e: MouseEvent, item: FormattedProcessItem, isLocal: boolean) { + e.preventDefault(); -function applyZoom(zoomLevel: number): void { - webFrame.setZoomLevel(zoomLevel); - 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); -} + const items: IContextMenuItem[] = []; + const pid = Number(item.pid); -function showContextMenu(e: MouseEvent, item: FormattedProcessItem, isLocal: boolean) { - e.preventDefault(); + if (isLocal) { + items.push({ + label: localize('killProcess', "Kill Process"), + click() { + process.kill(pid, 'SIGTERM'); + } + }); - const items: IContextMenuItem[] = []; - const pid = Number(item.pid); + items.push({ + label: localize('forceKillProcess', "Force Kill Process"), + click() { + process.kill(pid, 'SIGKILL'); + } + }); + + items.push({ + type: 'separator' + }); + } - if (isLocal) { items.push({ - label: localize('killProcess', "Kill Process"), + label: localize('copy', "Copy"), click() { - process.kill(pid, 'SIGTERM'); + const row = document.getElementById(pid.toString()); + if (row) { + clipboard.writeText(row.innerText); + } } }); items.push({ - label: localize('forceKillProcess', "Force Kill Process"), + label: localize('copyAll', "Copy All"), click() { - process.kill(pid, 'SIGKILL'); + const processList = document.getElementById('process-list'); + if (processList) { + clipboard.writeText(processList.innerText); + } } }); - items.push({ - type: 'separator' - }); + if (item && isLocal && this.isDebuggable(item.cmd)) { + items.push({ + type: 'separator' + }); + + items.push({ + label: localize('debug', "Debug"), + click: () => { + this.attachTo(item); + } + }); + } + + popup(items); } - items.push({ - label: localize('copy', "Copy"), - click() { - const row = document.getElementById(pid.toString()); - if (row) { - clipboard.writeText(row.innerText); - } - } - }); + private requestProcessList(totalWaitTime: number): void { + setTimeout(() => { + const nextRequestTime = Date.now(); + const waited = totalWaitTime + nextRequestTime - this.lastRequestTime; + this.lastRequestTime = nextRequestTime; - items.push({ - label: localize('copyAll', "Copy All"), - click() { - const processList = document.getElementById('process-list'); - if (processList) { - clipboard.writeText(processList.innerText); + // Wait at least a second between requests. + if (waited > 1000) { + ipcRenderer.send('vscode:windowsInfoRequest'); + ipcRenderer.send('vscode:listProcesses'); + } else { + this.requestProcessList(waited); } - } - }); - - if (item && isLocal && isDebuggable(item.cmd)) { - items.push({ - type: 'separator' - }); - - items.push({ - label: localize('debug', "Debug"), - click() { - attachTo(item); - } - }); + }, 200); } - popup(items); + public dispose() { + this.listeners.dispose(); + } } -function requestProcessList(totalWaitTime: number): void { - setTimeout(() => { - const nextRequestTime = Date.now(); - const waited = totalWaitTime + nextRequestTime - lastRequestTime; - lastRequestTime = nextRequestTime; - // Wait at least a second between requests. - if (waited > 1000) { - ipcRenderer.send('vscode:windowsInfoRequest'); - ipcRenderer.send('vscode:listProcesses'); - } else { - requestProcessList(waited); - } - }, 200); -} - -function createCloseListener(): void { - // Cmd/Ctrl + w closes process explorer - window.addEventListener('keydown', e => { - const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey; - if (cmdOrCtrlKey && e.keyCode === 87) { - ipcRenderer.send('vscode:closeProcessExplorer'); - } - }); -} export function startup(data: ProcessExplorerData): void { const platformClass = platform.isWindows ? 'windows' : platform.isLinux ? 'linux' : 'mac'; addClass(document.body, platformClass); // used by our fonts - - applyStyles(data.styles); applyZoom(data.zoomLevel); - createCloseListener(); - // Map window process pids to titles, annotate process names with this when rendering to distinguish between them - ipcRenderer.on('vscode:windowsInfoResponse', (event: unknown, windows: any[]) => { - mapPidToWindowTitle = new Map(); - windows.forEach(window => mapPidToWindowTitle.set(window.pid, window.title)); - }); - - ipcRenderer.on('vscode:listProcessesResponse', (event: unknown, processRoots: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]) => { - updateProcessInfo(processRoots); - requestProcessList(0); - }); - - lastRequestTime = Date.now(); - ipcRenderer.send('vscode:windowsInfoRequest'); - ipcRenderer.send('vscode:listProcesses'); + const processExplorer = new ProcessExplorer(data); document.onkeydown = (e: KeyboardEvent) => { const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey; // Cmd/Ctrl + zooms in if (cmdOrCtrlKey && e.keyCode === 187) { - applyZoom(webFrame.getZoomLevel() + 1); + zoomIn(); } // Cmd/Ctrl - zooms out if (cmdOrCtrlKey && e.keyCode === 189) { - applyZoom(webFrame.getZoomLevel() - 1); + zoomOut(); } }; + + // Cmd/Ctrl + w closes process explorer + window.addEventListener('keydown', e => { + const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey; + if (cmdOrCtrlKey && e.keyCode === 87) { + processExplorer.dispose(); + ipcRenderer.send('vscode:closeProcessExplorer'); + } + }); } diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts index 79b973aef5..f15410f935 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts @@ -35,7 +35,7 @@ export class StorageDataCleaner extends Disposable { const emptyWorkspaces = workspaces.emptyWorkspaceInfos.map(info => info.backupFolder); // Read all workspace storage folders that exist - return readdir(this.environmentService.workspaceStorageHome).then(storageFolders => { + return readdir(this.environmentService.workspaceStorageHome.fsPath).then(storageFolders => { const deletes: Promise[] = []; storageFolders.forEach(storageFolder => { @@ -44,7 +44,7 @@ export class StorageDataCleaner extends Disposable { } if (emptyWorkspaces.indexOf(storageFolder) === -1) { - deletes.push(rimraf(join(this.environmentService.workspaceStorageHome, storageFolder))); + deletes.push(rimraf(join(this.environmentService.workspaceStorageHome.fsPath, storageFolder))); } }); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index cda53693a9..a661bb2782 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -238,7 +238,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat server.registerChannel('userDataSyncAccount', authTokenChannel); const userDataSyncService = accessor.get(IUserDataSyncService); - const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService, logService); + const userDataSyncChannel = new UserDataSyncChannel(server, userDataSyncService, logService); server.registerChannel('userDataSync', userDataSyncChannel); const userDataAutoSync = instantiationService2.createInstance(UserDataAutoSyncService); diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 51e7290dbe..1a190b1046 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -9,41 +9,33 @@ 'use strict'; const perf = (function () { - let sharedObj; - if (typeof global === 'object') { - // nodejs - sharedObj = global; - } else if (typeof self === 'object') { - // browser - sharedObj = self; - } else { - sharedObj = {}; - } - // @ts-ignore - sharedObj._performanceEntries = sharedObj._performanceEntries || []; + globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; return { /** * @param {string} name */ mark(name) { - sharedObj._performanceEntries.push(name, Date.now()); + globalThis.MonacoPerformanceMarks.push(name, Date.now()); } }; })(); perf.mark('renderer/started'); -// Setup shell environment -process['lazyEnv'] = getLazyEnv(); - /** - * @type {{ load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown }} + * @type {{ + * load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown, + * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals') + * }} */ const bootstrapWindow = (() => { // @ts-ignore (defined in bootstrap-window.js) return window.MonacoBootstrapWindow; })(); +// Setup shell environment +process['lazyEnv'] = getLazyEnv(); + // Load workbench main JS, CSS and NLS all in parallel. This is an // optimization to prevent a waterfall of loading to happen, because // we know for a fact that workbench.desktop.main will depend on @@ -55,6 +47,8 @@ bootstrapWindow.load([ 'vs/css!vs/workbench/workbench.desktop.main' ], function (workbench, configuration) { + + // Mark start of workbench perf.mark('didLoadWorkbenchMain'); performance.mark('workbench-start'); @@ -183,8 +177,7 @@ function showPartsSplash(configuration) { * @returns {Promise} */ function getLazyEnv() { - - const ipc = require.__$__nodeRequire('electron').ipcRenderer; + const ipcRenderer = bootstrapWindow.globals().ipcRenderer; return new Promise(function (resolve) { const handle = setTimeout(function () { @@ -192,13 +185,13 @@ function getLazyEnv() { console.warn('renderer did not receive lazyEnv in time'); }, 10000); - ipc.once('vscode:acceptShellEnv', function (event, shellEnv) { + ipcRenderer.once('vscode:acceptShellEnv', function (event, shellEnv) { clearTimeout(handle); Object.assign(process.env, shellEnv); // @ts-ignore resolve(process.env); }); - ipc.send('vscode:fetchShellEnv'); + ipcRenderer.send('vscode:fetchShellEnv'); }); } diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index aaa7fb61bf..95048b0269 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -6,6 +6,7 @@ import { localize } from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; import { BrowserWindow, BrowserWindowConstructorOptions, app, AuthInfo, WebContents, Event as ElectronEvent } from 'electron'; type LoginEvent = { @@ -58,10 +59,12 @@ export class ProxyAuthHandler extends Disposable { show: true, title: 'VS Code', webPreferences: { - nodeIntegration: true, + preload: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).fsPath, enableWebSQL: false, + sandbox: true, + devTools: false, enableRemoteModule: false, - nativeWindowOpen: true + v8CacheOptions: 'bypassHeatCheck' } }; @@ -72,24 +75,27 @@ export class ProxyAuthHandler extends Disposable { } const win = new BrowserWindow(opts); - const config = {}; - const baseUrl = require.toUrl('vs/code/electron-browser/proxy/auth.html'); - const url = `${baseUrl}?config=${encodeURIComponent(JSON.stringify(config))}`; + const url = require.toUrl('vs/code/electron-sandbox/proxy/auth.html'); 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.setMenu(null); - win.loadURL(url); - win.webContents.executeJavaScript(javascript, true).then(({ username, password }: Credentials) => { - cb(username, password); + win.webContents.on('did-finish-load', () => { + const data = { title, message }; + win.webContents.send('vscode:openProxyAuthDialog', data); + }); + win.webContents.on('ipc-message', (event, channel, credentials: Credentials) => { + if (channel === 'vscode:proxyAuthResponse') { + const { username, password } = credentials; + cb(username, password); + } win.removeListener('close', onWindowClose); win.close(); }); + win.loadURL(url); } } diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 415107bb19..9a9e1c6376 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -178,8 +178,8 @@ class CodeMain { environmentService.extensionsPath, environmentService.nodeCachedDataDir, environmentService.logsPath, - environmentService.globalStorageHome, - environmentService.workspaceStorageHome, + environmentService.globalStorageHome.fsPath, + environmentService.workspaceStorageHome.fsPath, environmentService.backupHome.fsPath ].map((path): undefined | Promise => path ? mkdirp(path) : undefined)); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 45d7886ecf..4ab39785c0 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -15,7 +15,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { parseArgs, OPTIONS, ParsedArgs } from 'vs/platform/environment/node/argv'; import product from 'vs/platform/product/common/product'; -import { IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor } from 'vs/platform/windows/common/windows'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; @@ -153,6 +153,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { // 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 windowConfig = this.configurationService.getValue('window'); + const options: BrowserWindowConstructorOptions = { width: this.windowState.width, height: this.windowState.height, @@ -169,7 +171,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { webviewTag: true, enableWebSQL: false, enableRemoteModule: false, - nativeWindowOpen: true + nativeWindowOpen: true, + zoomFactor: zoomLevelToZoomFactor(windowConfig?.zoomLevel) } }; @@ -182,8 +185,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { options.icon = path.join(this.environmentService.appRoot, 'resources/win32/code_150x150.png'); } - const windowConfig = this.configurationService.getValue('window'); - if (isMacintosh && !this.useNativeFullScreen()) { options.fullscreenable = false; // enables simple fullscreen mode } @@ -214,6 +215,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._win = new BrowserWindow(options); this._id = this._win.id; + // Open devtools if instructed from command line args + if (this.environmentService.args['open-devtools'] === true) { + this._win.webContents.openDevTools(); + } + if (isMacintosh && useCustomTitleStyle) { this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any } diff --git a/src/vs/code/electron-browser/proxy/auth.html b/src/vs/code/electron-sandbox/proxy/auth.html similarity index 58% rename from src/vs/code/electron-browser/proxy/auth.html rename to src/vs/code/electron-sandbox/proxy/auth.html index e5cc351b14..788b68fce7 100644 --- a/src/vs/code/electron-browser/proxy/auth.html +++ b/src/vs/code/electron-sandbox/proxy/auth.html @@ -5,7 +5,7 @@ + content="default-src 'none'; img-src 'self' https: data:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https:;">