diff --git a/build/lib/electron.js b/build/lib/electron.js index c0d9db20a98..610d5bb6377 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -152,6 +152,7 @@ exports.config = { 'F# script': ['fsx', 'fsscript'], 'SVG document': ['svg', 'svgz'], 'TOML document': 'toml', + 'Swift source code': 'swift', }, 'default'), // Default icon with default name darwinBundleDocumentType([ diff --git a/build/lib/electron.ts b/build/lib/electron.ts index 76ad172e395..c37a0f6eae3 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -167,6 +167,7 @@ export const config = { 'F# script': ['fsx', 'fsscript'], 'SVG document': ['svg', 'svgz'], 'TOML document': 'toml', + 'Swift source code': 'swift', }, 'default'), // Default icon with default name darwinBundleDocumentType([ diff --git a/build/lib/policies.js b/build/lib/policies.js index 83ca05abc89..2fe3339ccea 100644 --- a/build/lib/policies.js +++ b/build/lib/policies.js @@ -13,6 +13,7 @@ const Parser = require("tree-sitter"); const node_fetch_1 = require("node-fetch"); const { typescript } = require('tree-sitter-typescript'); const product = require('../../product.json'); +const packageJson = require('../../package.json'); function isNlsString(value) { return value ? typeof value !== 'string' : false; } @@ -408,16 +409,11 @@ const Languages = { 'tr': 'tr-tr', 'pl': 'pl-pl', }; -async function getLatestStableVersion(updateUrl) { - const res = await (0, node_fetch_1.default)(`${updateUrl}/api/update/darwin/stable/latest`); - const { name: version } = await res.json(); - return version; -} async function getSpecificNLS(resourceUrlTemplate, languageId, version) { const resource = { publisher: 'ms-ceintl', name: `vscode-language-pack-${languageId}`, - version, + version: `${version[0]}.${version[1]}.${version[2]}`, path: 'extension/translations/main.i18n.json' }; const url = resourceUrlTemplate.replace(/\{([^}]+)\}/g, (_, key) => resource[key]); @@ -428,35 +424,47 @@ async function getSpecificNLS(resourceUrlTemplate, languageId, version) { const { contents: result } = await res.json(); return result; } -function previousVersion(version) { - const [, major, minor, patch] = /^(\d+)\.(\d+)\.(\d+)$/.exec(version); - return `${major}.${parseInt(minor) - 1}.${patch}`; +function parseVersion(version) { + const [, major, minor, patch] = /^(\d+)\.(\d+)\.(\d+)/.exec(version); + return [parseInt(major), parseInt(minor), parseInt(patch)]; } -async function getNLS(resourceUrlTemplate, languageId, version) { - try { - return await getSpecificNLS(resourceUrlTemplate, languageId, version); +function compareVersions(a, b) { + if (a[0] !== b[0]) { + return a[0] - b[0]; } - catch (err) { - if (/\[404\]/.test(err.message)) { - const thePreviousVersion = previousVersion(version); - console.warn(`Language pack ${languageId}@${version} is missing. Downloading previous version ${thePreviousVersion}...`); - try { - return await getSpecificNLS(resourceUrlTemplate, languageId, thePreviousVersion); - } - catch (err) { - if (/\[404\]/.test(err.message)) { - console.warn(`Language pack ${languageId}@${thePreviousVersion} is missing. Downloading previous version...`); - return await getSpecificNLS(resourceUrlTemplate, languageId, previousVersion(thePreviousVersion)); - } - else { - throw err; - } - } - } - else { - throw err; - } + if (a[1] !== b[1]) { + return a[1] - b[1]; } + return a[2] - b[2]; +} +async function queryVersions(serviceUrl, languageId) { + const res = await (0, node_fetch_1.default)(`${serviceUrl}/extensionquery`, { + method: 'POST', + headers: { + 'Accept': 'application/json;api-version=3.0-preview.1', + 'Content-Type': 'application/json', + 'User-Agent': 'VS Code Build', + }, + body: JSON.stringify({ + filters: [{ criteria: [{ filterType: 7, value: `ms-ceintl.vscode-language-pack-${languageId}` }] }], + flags: 0x1 + }) + }); + if (res.status !== 200) { + throw new Error(`[${res.status}] Error querying for extension: ${languageId}`); + } + const result = await res.json(); + return result.results[0].extensions[0].versions.map(v => parseVersion(v.version)).sort(compareVersions); +} +async function getNLS(extensionGalleryServiceUrl, resourceUrlTemplate, languageId, version) { + const versions = await queryVersions(extensionGalleryServiceUrl, languageId); + const nextMinor = [version[0], version[1] + 1, 0]; + const compatibleVersions = versions.filter(v => compareVersions(v, nextMinor) < 0); + const latestCompatibleVersion = compatibleVersions.at(-1); // order is newest to oldest + if (!latestCompatibleVersion) { + throw new Error(`No compatible language pack found for ${languageId} for version ${version}`); + } + return await getSpecificNLS(resourceUrlTemplate, languageId, latestCompatibleVersion); } async function parsePolicies() { const parser = new Parser(); @@ -473,9 +481,9 @@ async function parsePolicies() { return policies; } async function getTranslations() { - const updateUrl = product.updateUrl; - if (!updateUrl) { - console.warn(`Skipping policy localization: No 'updateUrl' found in 'product.json'.`); + const extensionGalleryServiceUrl = product.extensionsGallery?.serviceUrl; + if (!extensionGalleryServiceUrl) { + console.warn(`Skipping policy localization: No 'extensionGallery.serviceUrl' found in 'product.json'.`); return []; } const resourceUrlTemplate = product.extensionsGallery?.resourceUrlTemplate; @@ -483,9 +491,9 @@ async function getTranslations() { console.warn(`Skipping policy localization: No 'resourceUrlTemplate' found in 'product.json'.`); return []; } - const version = await getLatestStableVersion(updateUrl); + const version = parseVersion(packageJson.version); const languageIds = Object.keys(Languages); - return await Promise.all(languageIds.map(languageId => getNLS(resourceUrlTemplate, languageId, version) + return await Promise.all(languageIds.map(languageId => getNLS(extensionGalleryServiceUrl, resourceUrlTemplate, languageId, version) .then(languageTranslations => ({ languageId, languageTranslations })))); } async function main() { diff --git a/build/lib/policies.ts b/build/lib/policies.ts index cd3997e4e1d..0e89508fe8e 100644 --- a/build/lib/policies.ts +++ b/build/lib/policies.ts @@ -12,6 +12,7 @@ import * as Parser from 'tree-sitter'; import fetch from 'node-fetch'; const { typescript } = require('tree-sitter-typescript'); const product = require('../../product.json'); +const packageJson = require('../../package.json'); type NlsString = { value: string; nlsKey: string }; @@ -587,17 +588,13 @@ const Languages = { type LanguageTranslations = { [moduleName: string]: { [nlsKey: string]: string } }; type Translations = { languageId: string; languageTranslations: LanguageTranslations }[]; -async function getLatestStableVersion(updateUrl: string) { - const res = await fetch(`${updateUrl}/api/update/darwin/stable/latest`); - const { name: version } = await res.json() as { name: string }; - return version; -} +type Version = [number, number, number]; -async function getSpecificNLS(resourceUrlTemplate: string, languageId: string, version: string) { +async function getSpecificNLS(resourceUrlTemplate: string, languageId: string, version: Version) { const resource = { publisher: 'ms-ceintl', name: `vscode-language-pack-${languageId}`, - version, + version: `${version[0]}.${version[1]}.${version[2]}`, path: 'extension/translations/main.i18n.json' }; @@ -612,32 +609,50 @@ async function getSpecificNLS(resourceUrlTemplate: string, languageId: string, v return result; } -function previousVersion(version: string): string { - const [, major, minor, patch] = /^(\d+)\.(\d+)\.(\d+)$/.exec(version)!; - return `${major}.${parseInt(minor) - 1}.${patch}`; +function parseVersion(version: string): Version { + const [, major, minor, patch] = /^(\d+)\.(\d+)\.(\d+)/.exec(version)!; + return [parseInt(major), parseInt(minor), parseInt(patch)]; } -async function getNLS(resourceUrlTemplate: string, languageId: string, version: string) { - try { - return await getSpecificNLS(resourceUrlTemplate, languageId, version); - } catch (err) { - if (/\[404\]/.test(err.message)) { - const thePreviousVersion = previousVersion(version); - console.warn(`Language pack ${languageId}@${version} is missing. Downloading previous version ${thePreviousVersion}...`); - try { - return await getSpecificNLS(resourceUrlTemplate, languageId, thePreviousVersion); - } catch (err) { - if (/\[404\]/.test(err.message)) { - console.warn(`Language pack ${languageId}@${thePreviousVersion} is missing. Downloading previous version...`); - return await getSpecificNLS(resourceUrlTemplate, languageId, previousVersion(thePreviousVersion)); - } else { - throw err; - } - } - } else { - throw err; - } +function compareVersions(a: Version, b: Version): number { + if (a[0] !== b[0]) { return a[0] - b[0]; } + if (a[1] !== b[1]) { return a[1] - b[1]; } + return a[2] - b[2]; +} + +async function queryVersions(serviceUrl: string, languageId: string): Promise { + const res = await fetch(`${serviceUrl}/extensionquery`, { + method: 'POST', + headers: { + 'Accept': 'application/json;api-version=3.0-preview.1', + 'Content-Type': 'application/json', + 'User-Agent': 'VS Code Build', + }, + body: JSON.stringify({ + filters: [{ criteria: [{ filterType: 7, value: `ms-ceintl.vscode-language-pack-${languageId}` }] }], + flags: 0x1 + }) + }); + + if (res.status !== 200) { + throw new Error(`[${res.status}] Error querying for extension: ${languageId}`); } + + const result = await res.json() as { results: [{ extensions: { versions: { version: string }[] }[] }] }; + return result.results[0].extensions[0].versions.map(v => parseVersion(v.version)).sort(compareVersions); +} + +async function getNLS(extensionGalleryServiceUrl: string, resourceUrlTemplate: string, languageId: string, version: Version) { + const versions = await queryVersions(extensionGalleryServiceUrl, languageId); + const nextMinor: Version = [version[0], version[1] + 1, 0]; + const compatibleVersions = versions.filter(v => compareVersions(v, nextMinor) < 0); + const latestCompatibleVersion = compatibleVersions.at(-1)!; // order is newest to oldest + + if (!latestCompatibleVersion) { + throw new Error(`No compatible language pack found for ${languageId} for version ${version}`); + } + + return await getSpecificNLS(resourceUrlTemplate, languageId, latestCompatibleVersion); } async function parsePolicies(): Promise { @@ -659,10 +674,10 @@ async function parsePolicies(): Promise { } async function getTranslations(): Promise { - const updateUrl = product.updateUrl; + const extensionGalleryServiceUrl = product.extensionsGallery?.serviceUrl; - if (!updateUrl) { - console.warn(`Skipping policy localization: No 'updateUrl' found in 'product.json'.`); + if (!extensionGalleryServiceUrl) { + console.warn(`Skipping policy localization: No 'extensionGallery.serviceUrl' found in 'product.json'.`); return []; } @@ -673,11 +688,11 @@ async function getTranslations(): Promise { return []; } - const version = await getLatestStableVersion(updateUrl); + const version = parseVersion(packageJson.version); const languageIds = Object.keys(Languages); return await Promise.all(languageIds.map( - languageId => getNLS(resourceUrlTemplate, languageId, version) + languageId => getNLS(extensionGalleryServiceUrl, resourceUrlTemplate, languageId, version) .then(languageTranslations => ({ languageId, languageTranslations })) )); } diff --git a/cgmanifest.json b/cgmanifest.json index 5e4f5a29a32..3d2387b7ee9 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -55,135 +55,433 @@ "license": "LGPL-2.1+", "version": "4.4.git", "licenseDetail": [ - "# License", + " GNU LESSER GENERAL PUBLIC LICENSE", + " Version 2.1, February 1999", + " Copyright (C) 1991, 1999 Free Software Foundation, Inc.", + " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA", + " Everyone is permitted to copy and distribute verbatim copies", + " of this license document, but changing it is not allowed.", + "[This is the first released version of the Lesser GPL. It also counts", + " as the successor of the GNU Library Public License, version 2, hence", + " the version number 2.1.]", + " Preamble", + " The licenses for most software are designed to take away your", + "freedom to share and change it. By contrast, the GNU General Public", + "Licenses are intended to guarantee your freedom to share and change", + "free software--to make sure the software is free for all its users.", + " This license, the Lesser General Public License, applies to some", + "specially designated software packages--typically libraries--of the", + "Free Software Foundation and other authors who decide to use it. You", + "can use it too, but we suggest you first think carefully about whether", + "this license or the ordinary General Public License is the better", + "strategy to use in any particular case, based on the explanations below.", + " When we speak of free software, we are referring to freedom of use,", + "not price. Our General Public Licenses are designed to make sure that", + "you have the freedom to distribute copies of free software (and charge", + "for this service if you wish); that you receive source code or can get", + "it if you want it; that you can change the software and use pieces of", + "it in new free programs; and that you are informed that you can do", + "these things.", + " To protect your rights, we need to make restrictions that forbid", + "distributors to deny you these rights or to ask you to surrender these", + "rights. These restrictions translate to certain responsibilities for", + "you if you distribute copies of the library or if you modify it.", + " For example, if you distribute copies of the library, whether gratis", + "or for a fee, you must give the recipients all the rights that we gave", + "you. You must make sure that they, too, receive or can get the source", + "code. If you link other code with the library, you must provide", + "complete object files to the recipients, so that they can relink them", + "with the library after making changes to the library and recompiling", + "it. And you must show them these terms so they know their rights.", + " We protect your rights with a two-step method: (1) we copyright the", + "library, and (2) we offer you this license, which gives you legal", + "permission to copy, distribute and/or modify the library.", + " To protect each distributor, we want to make it very clear that", + "there is no warranty for the free library. Also, if the library is", + "modified by someone else and passed on, the recipients should know", + "that what they have is not the original version, so that the original", + "author's reputation will not be affected by problems that might be", + "introduced by others.", "", - "Most files in FFmpeg are under the GNU Lesser General Public License version 2.1", - "or later (LGPL v2.1+). Read the file `COPYING.LGPLv2.1` for details. Some other", - "files have MIT/X11/BSD-style licenses. In combination the LGPL v2.1+ applies to", - "FFmpeg.", + " Finally, software patents pose a constant threat to the existence of", + "any free program. We wish to make sure that a company cannot", + "effectively restrict the users of a free program by obtaining a", + "restrictive license from a patent holder. Therefore, we insist that", + "any patent license obtained for a version of the library must be", + "consistent with the full freedom of use specified in this license.", + " Most GNU software, including some libraries, is covered by the", + "ordinary GNU General Public License. This license, the GNU Lesser", + "General Public License, applies to certain designated libraries, and", + "is quite different from the ordinary General Public License. We use", + "this license for certain libraries in order to permit linking those", + "libraries into non-free programs.", + " When a program is linked with a library, whether statically or using", + "a shared library, the combination of the two is legally speaking a", + "combined work, a derivative of the original library. The ordinary", + "General Public License therefore permits such linking only if the", + "entire combination fits its criteria of freedom. The Lesser General", + "Public License permits more lax criteria for linking other code with", + "the library.", + " We call this license the \"Lesser\" General Public License because it", + "does Less to protect the user's freedom than the ordinary General", + "Public License. It also provides other free software developers Less", + "of an advantage over competing non-free programs. These disadvantages", + "are the reason we use the ordinary General Public License for many", + "libraries. However, the Lesser license provides advantages in certain", + "special circumstances.", + " For example, on rare occasions, there may be a special need to", + "encourage the widest possible use of a certain library, so that it becomes", + "a de-facto standard. To achieve this, non-free programs must be", + "allowed to use the library. A more frequent case is that a free", + "library does the same job as widely used non-free libraries. In this", + "case, there is little to gain by limiting the free library to free", + "software only, so we use the Lesser General Public License.", + " In other cases, permission to use a particular library in non-free", + "programs enables a greater number of people to use a large body of", + "free software. For example, permission to use the GNU C Library in", + "non-free programs enables many more people to use the whole GNU", + "operating system, as well as its variant, the GNU/Linux operating", + "system.", + " Although the Lesser General Public License is Less protective of the", + "users' freedom, it does ensure that the user of a program that is", + "linked with the Library has the freedom and the wherewithal to run", + "that program using a modified version of the Library.", + " The precise terms and conditions for copying, distribution and", + "modification follow. Pay close attention to the difference between a", + "\"work based on the library\" and a \"work that uses the library\". The", + "former contains code derived from the library, whereas the latter must", + "be combined with the library in order to run.", "", - "Some optional parts of FFmpeg are licensed under the GNU General Public License", - "version 2 or later (GPL v2+). See the file `COPYING.GPLv2` for details. None of", - "these parts are used by default, you have to explicitly pass `--enable-gpl` to", - "configure to activate them. In this case, FFmpeg's license changes to GPL v2+.", + " GNU LESSER GENERAL PUBLIC LICENSE", + " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION", + " 0. This License Agreement applies to any software library or other", + "program which contains a notice placed by the copyright holder or", + "other authorized party saying it may be distributed under the terms of", + "this Lesser General Public License (also called \"this License\").", + "Each licensee is addressed as \"you\".", + " A \"library\" means a collection of software functions and/or data", + "prepared so as to be conveniently linked with application programs", + "(which use some of those functions and data) to form executables.", + " The \"Library\", below, refers to any such software library or work", + "which has been distributed under these terms. A \"work based on the", + "Library\" means either the Library or any derivative work under", + "copyright law: that is to say, a work containing the Library or a", + "portion of it, either verbatim or with modifications and/or translated", + "straightforwardly into another language. (Hereinafter, translation is", + "included without limitation in the term \"modification\".)", + " \"Source code\" for a work means the preferred form of the work for", + "making modifications to it. For a library, complete source code means", + "all the source code for all modules it contains, plus any associated", + "interface definition files, plus the scripts used to control compilation", + "and installation of the library.", + " Activities other than copying, distribution and modification are not", + "covered by this License; they are outside its scope. The act of", + "running a program using the Library is not restricted, and output from", + "such a program is covered only if its contents constitute a work based", + "on the Library (independent of the use of the Library in a tool for", + "writing it). Whether that is true depends on what the Library does", + "and what the program that uses the Library does.", + " 1. You may copy and distribute verbatim copies of the Library's", + "complete source code as you receive it, in any medium, provided that", + "you conspicuously and appropriately publish on each copy an", + "appropriate copyright notice and disclaimer of warranty; keep intact", + "all the notices that refer to this License and to the absence of any", + "warranty; and distribute a copy of this License along with the", + "Library.", + " You may charge a fee for the physical act of transferring a copy,", + "and you may at your option offer warranty protection in exchange for a", + "fee.", "", - "Specifically, the GPL parts of FFmpeg are:", + " 2. You may modify your copy or copies of the Library or any portion", + "of it, thus forming a work based on the Library, and copy and", + "distribute such modifications or work under the terms of Section 1", + "above, provided that you also meet all of these conditions:", + " a) The modified work must itself be a software library.", + " b) You must cause the files modified to carry prominent notices", + " stating that you changed the files and the date of any change.", + " c) You must cause the whole of the work to be licensed at no", + " charge to all third parties under the terms of this License.", + " d) If a facility in the modified Library refers to a function or a", + " table of data to be supplied by an application program that uses", + " the facility, other than as an argument passed when the facility", + " is invoked, then you must make a good faith effort to ensure that,", + " in the event an application does not supply such function or", + " table, the facility still operates, and performs whatever part of", + " its purpose remains meaningful.", + " (For example, a function in a library to compute square roots has", + " a purpose that is entirely well-defined independent of the", + " application. Therefore, Subsection 2d requires that any", + " application-supplied function or table used by this function must", + " be optional: if the application does not supply it, the square", + " root function must still compute square roots.)", + "These requirements apply to the modified work as a whole. If", + "identifiable sections of that work are not derived from the Library,", + "and can be reasonably considered independent and separate works in", + "themselves, then this License, and its terms, do not apply to those", + "sections when you distribute them as separate works. But when you", + "distribute the same sections as part of a whole which is a work based", + "on the Library, the distribution of the whole must be on the terms of", + "this License, whose permissions for other licensees extend to the", + "entire whole, and thus to each and every part regardless of who wrote", + "it.", + "Thus, it is not the intent of this section to claim rights or contest", + "your rights to work written entirely by you; rather, the intent is to", + "exercise the right to control the distribution of derivative or", + "collective works based on the Library.", + "In addition, mere aggregation of another work not based on the Library", + "with the Library (or with a work based on the Library) on a volume of", + "a storage or distribution medium does not bring the other work under", + "the scope of this License.", + " 3. You may opt to apply the terms of the ordinary GNU General Public", + "License instead of this License to a given copy of the Library. To do", + "this, you must alter all the notices that refer to this License, so", + "that they refer to the ordinary GNU General Public License, version 2,", + "instead of to this License. (If a newer version than version 2 of the", + "ordinary GNU General Public License has appeared, then you can specify", + "that version instead if you wish.) Do not make any other change in", + "these notices.", "", - "- libpostproc", - "- optional x86 optimization in the files", - " - `libavcodec/x86/flac_dsp_gpl.asm`", - " - `libavcodec/x86/idct_mmx.c`", - " - `libavfilter/x86/vf_removegrain.asm`", - "- the following building and testing tools", - " - `compat/solaris/make_sunver.pl`", - " - `doc/t2h.pm`", - " - `doc/texi2pod.pl`", - " - `libswresample/tests/swresample.c`", - " - `tests/checkasm/*`", - " - `tests/tiny_ssim.c`", - "- the following filters in libavfilter:", - " - `signature_lookup.c`", - " - `vf_blackframe.c`", - " - `vf_boxblur.c`", - " - `vf_colormatrix.c`", - " - `vf_cover_rect.c`", - " - `vf_cropdetect.c`", - " - `vf_delogo.c`", - " - `vf_eq.c`", - " - `vf_find_rect.c`", - " - `vf_fspp.c`", - " - `vf_histeq.c`", - " - `vf_hqdn3d.c`", - " - `vf_kerndeint.c`", - " - `vf_lensfun.c` (GPL version 3 or later)", - " - `vf_mcdeint.c`", - " - `vf_mpdecimate.c`", - " - `vf_nnedi.c`", - " - `vf_owdenoise.c`", - " - `vf_perspective.c`", - " - `vf_phase.c`", - " - `vf_pp.c`", - " - `vf_pp7.c`", - " - `vf_pullup.c`", - " - `vf_repeatfields.c`", - " - `vf_sab.c`", - " - `vf_signature.c`", - " - `vf_smartblur.c`", - " - `vf_spp.c`", - " - `vf_stereo3d.c`", - " - `vf_super2xsai.c`", - " - `vf_tinterlace.c`", - " - `vf_uspp.c`", - " - `vf_vaguedenoiser.c`", - " - `vsrc_mptestsrc.c`", + " Once this change is made in a given copy, it is irreversible for", + "that copy, so the ordinary GNU General Public License applies to all", + "subsequent copies and derivative works made from that copy.", + " This option is useful when you wish to copy part of the code of", + "the Library into a program that is not a library.", + " 4. You may copy and distribute the Library (or a portion or", + "derivative of it, under Section 2) in object code or executable form", + "under the terms of Sections 1 and 2 above provided that you accompany", + "it with the complete corresponding machine-readable source code, which", + "must be distributed under the terms of Sections 1 and 2 above on a", + "medium customarily used for software interchange.", + " If distribution of object code is made by offering access to copy", + "from a designated place, then offering equivalent access to copy the", + "source code from the same place satisfies the requirement to", + "distribute the source code, even though third parties are not", + "compelled to copy the source along with the object code.", + " 5. A program that contains no derivative of any portion of the", + "Library, but is designed to work with the Library by being compiled or", + "linked with it, is called a \"work that uses the Library\". Such a", + "work, in isolation, is not a derivative work of the Library, and", + "therefore falls outside the scope of this License.", + " However, linking a \"work that uses the Library\" with the Library", + "creates an executable that is a derivative of the Library (because it", + "contains portions of the Library), rather than a \"work that uses the", + "library\". The executable is therefore covered by this License.", + "Section 6 states terms for distribution of such executables.", + " When a \"work that uses the Library\" uses material from a header file", + "that is part of the Library, the object code for the work may be a", + "derivative work of the Library even though the source code is not.", + "Whether this is true is especially significant if the work can be", + "linked without the Library, or if the work is itself a library. The", + "threshold for this to be true is not precisely defined by law.", + " If such an object file uses only numerical parameters, data", + "structure layouts and accessors, and small macros and small inline", + "functions (ten lines or less in length), then the use of the object", + "file is unrestricted, regardless of whether it is legally a derivative", + "work. (Executables containing this object code plus portions of the", + "Library will still fall under Section 6.)", + " Otherwise, if the work is a derivative of the Library, you may", + "distribute the object code for the work under the terms of Section 6.", + "Any executables containing that work also fall under Section 6,", + "whether or not they are linked directly with the Library itself.", "", - "Should you, for whatever reason, prefer to use version 3 of the (L)GPL, then", - "the configure parameter `--enable-version3` will activate this licensing option", - "for you. Read the file `COPYING.LGPLv3` or, if you have enabled GPL parts,", - "`COPYING.GPLv3` to learn the exact legal terms that apply in this case.", + " 6. As an exception to the Sections above, you may also combine or", + "link a \"work that uses the Library\" with the Library to produce a", + "work containing portions of the Library, and distribute that work", + "under terms of your choice, provided that the terms permit", + "modification of the work for the customer's own use and reverse", + "engineering for debugging such modifications.", + " You must give prominent notice with each copy of the work that the", + "Library is used in it and that the Library and its use are covered by", + "this License. You must supply a copy of this License. If the work", + "during execution displays copyright notices, you must include the", + "copyright notice for the Library among them, as well as a reference", + "directing the user to the copy of this License. Also, you must do one", + "of these things:", + " a) Accompany the work with the complete corresponding", + " machine-readable source code for the Library including whatever", + " changes were used in the work (which must be distributed under", + " Sections 1 and 2 above); and, if the work is an executable linked", + " with the Library, with the complete machine-readable \"work that", + " uses the Library\", as object code and/or source code, so that the", + " user can modify the Library and then relink to produce a modified", + " executable containing the modified Library. (It is understood", + " that the user who changes the contents of definitions files in the", + " Library will not necessarily be able to recompile the application", + " to use the modified definitions.)", + " b) Use a suitable shared library mechanism for linking with the", + " Library. A suitable mechanism is one that (1) uses at run time a", + " copy of the library already present on the user's computer system,", + " rather than copying library functions into the executable, and (2)", + " will operate properly with a modified version of the library, if", + " the user installs one, as long as the modified version is", + " interface-compatible with the version that the work was made with.", + " c) Accompany the work with a written offer, valid for at", + " least three years, to give the same user the materials", + " specified in Subsection 6a, above, for a charge no more", + " than the cost of performing this distribution.", + " d) If distribution of the work is made by offering access to copy", + " from a designated place, offer equivalent access to copy the above", + " specified materials from the same place.", + " e) Verify that the user has already received a copy of these", + " materials or that you have already sent this user a copy.", + " For an executable, the required form of the \"work that uses the", + "Library\" must include any data and utility programs needed for", + "reproducing the executable from it. However, as a special exception,", + "the materials to be distributed need not include anything that is", + "normally distributed (in either source or binary form) with the major", + "components (compiler, kernel, and so on) of the operating system on", + "which the executable runs, unless that component itself accompanies", + "the executable.", + " It may happen that this requirement contradicts the license", + "restrictions of other proprietary libraries that do not normally", + "accompany the operating system. Such a contradiction means you cannot", + "use both them and the Library together in an executable that you", + "distribute.", "", - "There are a handful of files under other licensing terms, namely:", + " 7. You may place library facilities that are a work based on the", + "Library side-by-side in a single library together with other library", + "facilities not covered by this License, and distribute such a combined", + "library, provided that the separate distribution of the work based on", + "the Library and of the other library facilities is otherwise", + "permitted, and provided that you do these two things:", + " a) Accompany the combined library with a copy of the same work", + " based on the Library, uncombined with any other library", + " facilities. This must be distributed under the terms of the", + " Sections above.", + " b) Give prominent notice with the combined library of the fact", + " that part of it is a work based on the Library, and explaining", + " where to find the accompanying uncombined form of the same work.", + " 8. You may not copy, modify, sublicense, link with, or distribute", + "the Library except as expressly provided under this License. Any", + "attempt otherwise to copy, modify, sublicense, link with, or", + "distribute the Library is void, and will automatically terminate your", + "rights under this License. However, parties who have received copies,", + "or rights, from you under this License will not have their licenses", + "terminated so long as such parties remain in full compliance.", + " 9. You are not required to accept this License, since you have not", + "signed it. However, nothing else grants you permission to modify or", + "distribute the Library or its derivative works. These actions are", + "prohibited by law if you do not accept this License. Therefore, by", + "modifying or distributing the Library (or any work based on the", + "Library), you indicate your acceptance of this License to do so, and", + "all its terms and conditions for copying, distributing or modifying", + "the Library or works based on it.", + " 10. Each time you redistribute the Library (or any work based on the", + "Library), the recipient automatically receives a license from the", + "original licensor to copy, distribute, link with or modify the Library", + "subject to these terms and conditions. You may not impose any further", + "restrictions on the recipients' exercise of the rights granted herein.", + "You are not responsible for enforcing compliance by third parties with", + "this License.", "", - "* The files `libavcodec/jfdctfst.c`, `libavcodec/jfdctint_template.c` and", - " `libavcodec/jrevdct.c` are taken from libjpeg, see the top of the files for", - " licensing details. Specifically note that you must credit the IJG in the", - " documentation accompanying your program if you only distribute executables.", - " You must also indicate any changes including additions and deletions to", - " those three files in the documentation.", - "* `tests/reference.pnm` is under the expat license.", + " 11. If, as a consequence of a court judgment or allegation of patent", + "infringement or for any other reason (not limited to patent issues),", + "conditions are imposed on you (whether by court order, agreement or", + "otherwise) that contradict the conditions of this License, they do not", + "excuse you from the conditions of this License. If you cannot", + "distribute so as to satisfy simultaneously your obligations under this", + "License and any other pertinent obligations, then as a consequence you", + "may not distribute the Library at all. For example, if a patent", + "license would not permit royalty-free redistribution of the Library by", + "all those who receive copies directly or indirectly through you, then", + "the only way you could satisfy both it and this License would be to", + "refrain entirely from distribution of the Library.", + "If any portion of this section is held invalid or unenforceable under any", + "particular circumstance, the balance of the section is intended to apply,", + "and the section as a whole is intended to apply in other circumstances.", + "It is not the purpose of this section to induce you to infringe any", + "patents or other property right claims or to contest validity of any", + "such claims; this section has the sole purpose of protecting the", + "integrity of the free software distribution system which is", + "implemented by public license practices. Many people have made", + "generous contributions to the wide range of software distributed", + "through that system in reliance on consistent application of that", + "system; it is up to the author/donor to decide if he or she is willing", + "to distribute software through any other system and a licensee cannot", + "impose that choice.", + "This section is intended to make thoroughly clear what is believed to", + "be a consequence of the rest of this License.", + " 12. If the distribution and/or use of the Library is restricted in", + "certain countries either by patents or by copyrighted interfaces, the", + "original copyright holder who places the Library under this License may add", + "an explicit geographical distribution limitation excluding those countries,", + "so that distribution is permitted only in or among countries not thus", + "excluded. In such case, this License incorporates the limitation as if", + "written in the body of this License.", + " 13. The Free Software Foundation may publish revised and/or new", + "versions of the Lesser General Public License from time to time.", + "Such new versions will be similar in spirit to the present version,", + "but may differ in detail to address new problems or concerns.", + "Each version is given a distinguishing version number. If the Library", + "specifies a version number of this License which applies to it and", + "\"any later version\", you have the option of following the terms and", + "conditions either of that version or of any later version published by", + "the Free Software Foundation. If the Library does not specify a", + "license version number, you may choose any version ever published by", + "the Free Software Foundation.", "", + " 14. If you wish to incorporate parts of the Library into other free", + "programs whose distribution conditions are incompatible with these,", + "write to the author to ask for permission. For software which is", + "copyrighted by the Free Software Foundation, write to the Free", + "Software Foundation; we sometimes make exceptions for this. Our", + "decision will be guided by the two goals of preserving the free status", + "of all derivatives of our free software and of promoting the sharing", + "and reuse of software generally.", + " NO WARRANTY", + " 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO", + "WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.", + "EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR", + "OTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY", + "KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE", + "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR", + "PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE", + "LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME", + "THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.", + " 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN", + "WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY", + "AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU", + "FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR", + "CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE", + "LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING", + "RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A", + "FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF", + "SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH", + "DAMAGES.", + " END OF TERMS AND CONDITIONS", "", - "## External libraries", - "", - "FFmpeg can be combined with a number of external libraries, which sometimes", - "affect the licensing of binaries resulting from the combination.", - "", - "### Compatible libraries", - "", - "The following libraries are under GPL version 2:", - "- avisynth", - "- frei0r", - "- libcdio", - "- libdavs2", - "- librubberband", - "- libvidstab", - "- libx264", - "- libx265", - "- libxavs", - "- libxavs2", - "- libxvid", - "", - "When combining them with FFmpeg, FFmpeg needs to be licensed as GPL as well by", - "passing `--enable-gpl` to configure.", - "", - "The following libraries are under LGPL version 3:", - "- gmp", - "- libaribb24", - "- liblensfun", - "", - "When combining them with FFmpeg, use the configure option `--enable-version3` to", - "upgrade FFmpeg to the LGPL v3.", - "", - "The VMAF, mbedTLS, RK MPI, OpenCORE and VisualOn libraries are under the Apache License", - "2.0. That license is incompatible with the LGPL v2.1 and the GPL v2, but not with", - "version 3 of those licenses. So to combine these libraries with FFmpeg, the", - "license version needs to be upgraded by passing `--enable-version3` to configure.", - "", - "The smbclient library is under the GPL v3, to combine it with FFmpeg,", - "the options `--enable-gpl` and `--enable-version3` have to be passed to", - "configure to upgrade FFmpeg to the GPL v3.", - "", - "### Incompatible libraries", - "", - "There are certain libraries you can combine with FFmpeg whose licenses are not", - "compatible with the GPL and/or the LGPL. If you wish to enable these", - "libraries, even in circumstances that their license may be incompatible, pass", - "`--enable-nonfree` to configure. This will cause the resulting binary to be", - "unredistributable.", - "", - "The Fraunhofer FDK AAC and OpenSSL libraries are under licenses which are", - "incompatible with the GPLv2 and v3. To the best of our knowledge, they are", - "compatible with the LGPL." + " How to Apply These Terms to Your New Libraries", + " If you develop a new library, and you want it to be of the greatest", + "possible use to the public, we recommend making it free software that", + "everyone can redistribute and change. You can do so by permitting", + "redistribution under these terms (or, alternatively, under the terms of the", + "ordinary General Public License).", + " To apply these terms, attach the following notices to the library. It is", + "safest to attach them to the start of each source file to most effectively", + "convey the exclusion of warranty; and each file should have at least the", + "\"copyright\" line and a pointer to where the full notice is found.", + " ", + " Copyright (C) ", + " This library is free software; you can redistribute it and/or", + " modify it under the terms of the GNU Lesser General Public", + " License as published by the Free Software Foundation; either", + " version 2.1 of the License, or (at your option) any later version.", + " This library is distributed in the hope that it will be useful,", + " but WITHOUT ANY WARRANTY; without even the implied warranty of", + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU", + " Lesser General Public License for more details.", + " You should have received a copy of the GNU Lesser General Public", + " License along with this library; if not, write to the Free Software", + " Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA", + "Also add information on how to contact you by electronic and paper mail.", + "You should also get your employer (if you work as a programmer) or your", + "school, if any, to sign a \"copyright disclaimer\" for the library, if", + "necessary. Here is a sample; alter the names:", + " Yoyodyne, Inc., hereby disclaims all copyright interest in the", + " library `Frob' (a library for tweaking knobs) written by James Random Hacker.", + " , 1 April 1990", + " Ty Coon, President of Vice", + "That's all there is to it!" ] }, { diff --git a/extensions/markdown-language-features/src/languageFeatures/updatePathsOnRename.ts b/extensions/markdown-language-features/src/languageFeatures/updatePathsOnRename.ts index 8b3536f0c47..173bf8d9876 100644 --- a/extensions/markdown-language-features/src/languageFeatures/updatePathsOnRename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/updatePathsOnRename.ts @@ -6,7 +6,7 @@ import * as path from 'path'; import * as picomatch from 'picomatch'; import * as vscode from 'vscode'; -import { BaseLanguageClient } from 'vscode-languageclient'; +import { BaseLanguageClient, TextDocumentEdit } from 'vscode-languageclient'; import * as nls from 'vscode-nls'; import { getEditForFileRenames } from '../protocol'; import { Delayer } from '../util/async'; @@ -206,13 +206,13 @@ class UpdateLinksOnFileRenameHandler extends Disposable { token: vscode.CancellationToken, ): Promise { const edit = await this.client.sendRequest(getEditForFileRenames, [{ oldUri: oldUri.toString(), newUri: newUri.toString() }], token); - if (!edit.changes) { + if (!edit.documentChanges?.length) { return false; } - for (const [path, edits] of Object.entries(edit.changes)) { - const uri = vscode.Uri.parse(path); - for (const edit of edits) { + for (const change of edit.documentChanges as TextDocumentEdit[]) { + const uri = vscode.Uri.parse(change.textDocument.uri); + for (const edit of change.edits) { workspaceEdit.replace(uri, convertRange(edit.range), edit.newText); } } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts index 11276f0d25e..11c4e86ae80 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts @@ -569,7 +569,7 @@ registerEditorCommand(new CodeActionContribution({ kbOpts: { weight: weight + 100000, primary: KeyCode.Enter, - secondary: [KeyMod.Shift | KeyCode.Tab], + secondary: [KeyMod.CtrlCmd | KeyCode.Period], } })); diff --git a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts index 03b9ee293a5..ec754afde11 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts @@ -94,7 +94,7 @@ export abstract class SymbolNavigationAction extends EditorAction { this.configuration = configuration; } - run(accessor: ServicesAccessor, editor: ICodeEditor, arg?: SymbolNavigationAnchor | unknown): Promise { + run(accessor: ServicesAccessor, editor: ICodeEditor, arg?: SymbolNavigationAnchor | unknown, range?: Range): Promise { if (!editor.hasModel()) { return Promise.resolve(undefined); } @@ -143,7 +143,7 @@ export abstract class SymbolNavigationAction extends EditorAction { } else { // normal results handling - return this._onResult(editorService, symbolNavService, editor, references); + return this._onResult(editorService, symbolNavService, editor, references, range); } }, (err) => { @@ -165,18 +165,18 @@ export abstract class SymbolNavigationAction extends EditorAction { protected abstract _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues; - private async _onResult(editorService: ICodeEditorService, symbolNavService: ISymbolNavigationService, editor: IActiveCodeEditor, model: ReferencesModel): Promise { + private async _onResult(editorService: ICodeEditorService, symbolNavService: ISymbolNavigationService, editor: IActiveCodeEditor, model: ReferencesModel, range?: Range): Promise { const gotoLocation = this._getGoToPreference(editor); if (!(editor instanceof EmbeddedCodeEditorWidget) && (this.configuration.openInPeek || (gotoLocation === 'peek' && model.references.length > 1))) { - this._openInPeek(editor, model); + this._openInPeek(editor, model, range); } else { const next = model.firstReference()!; const peek = model.references.length > 1 && gotoLocation === 'gotoAndPeek'; const targetEditor = await this._openReference(editor, editorService, next, this.configuration.openToSide, !peek); if (peek && targetEditor) { - this._openInPeek(targetEditor, model); + this._openInPeek(targetEditor, model, range); } else { model.dispose(); } @@ -229,10 +229,10 @@ export abstract class SymbolNavigationAction extends EditorAction { return targetEditor; } - private _openInPeek(target: ICodeEditor, model: ReferencesModel) { + private _openInPeek(target: ICodeEditor, model: ReferencesModel, range?: Range) { const controller = ReferencesController.get(target); if (controller && target.hasModel()) { - controller.toggleWidget(target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), this.configuration.openInPeek); + controller.toggleWidget(range ?? target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), this.configuration.openInPeek); } else { model.dispose(); } diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts index 864f63be400..d869a64d0e5 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts @@ -106,7 +106,7 @@ export async function goToDefinitionWithLocation(accessor: ServicesAccessor, eve const canPeek = !openToSide && editor.getOption(EditorOption.definitionLinkOpensInPeek) && !isInPeek; const action = new DefinitionAction({ openToSide, openInPeek: canPeek, muteMessage: true }, { alias: '', label: '', id: '', precondition: undefined }); - return action.run(accessor, editor, { model: ref.object.textEditorModel, position: Range.getStartPosition(location.range) }); + return action.run(accessor, editor, { model: ref.object.textEditorModel, position: Range.getStartPosition(location.range) }, Range.lift(location.range)); }); ref.dispose(); diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts index 6489adceee1..223d68267a4 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts @@ -12,6 +12,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Range } from 'vs/editor/common/core/range'; import { Emitter } from 'vs/base/common/event'; +import { binarySearch } from 'vs/base/common/arrays'; export class StickyRange { constructor( @@ -96,9 +97,34 @@ export class StickyLineCandidateProvider extends Disposable { } } + private updateIndex(index: number) { + if (index === -1) { + index = 0; + } else if (index < 0) { + index = -index - 2; + } + return index; + } + public getCandidateStickyLinesIntersectingFromOutline(range: StickyRange, outlineModel: StickyOutlineElement, result: StickyLineCandidate[], depth: number, lastStartLineNumber: number): void { + if (outlineModel.children.length === 0) { + return; + } let lastLine = lastStartLineNumber; - for (const child of outlineModel.children) { + const childrenStartLines: number[] = []; + for (let i = 0; i < outlineModel.children.length; i++) { + const child = outlineModel.children[i]; + if (child.range) { + childrenStartLines.push(child.range.startLineNumber); + } + } + const lowerBound = this.updateIndex(binarySearch(childrenStartLines, range.startLineNumber, (a: number, b: number) => { return a - b; })); + const upperBound = this.updateIndex(binarySearch(childrenStartLines, range.startLineNumber + depth, (a: number, b: number) => { return a - b; })); + for (let i = lowerBound; i <= upperBound; i++) { + const child = outlineModel.children[i]; + if (!child) { + return; + } if (child.range) { const childStartLine = child.range.startLineNumber; const childEndLine = child.range.endLineNumber; @@ -133,9 +159,13 @@ export class StickyLineCandidateProvider extends Disposable { class StickyOutlineElement { public static fromOutlineModel(outlineModel: OutlineModel | OutlineElement | OutlineGroup): StickyOutlineElement { - const children = [...outlineModel.children.values()].map(child => - StickyOutlineElement.fromOutlineModel(child) - ); + + const children: StickyOutlineElement[] = []; + for (const child of outlineModel.children.values()) { + if (child instanceof OutlineElement && child.symbol.selectionRange.startLineNumber !== child.symbol.range.endLineNumber || child instanceof OutlineGroup || child instanceof OutlineModel) { + children.push(StickyOutlineElement.fromOutlineModel(child)); + } + } children.sort((child1, child2) => { if (!child1.range || !child2.range) { return 1; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index b36e55ca70f..238e320eee9 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -160,7 +160,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return linkGestureStore; } - public get lineNumbers(): number[] { return this._lineNumbers; } diff --git a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts index ff50244610d..509a2d8b4d7 100644 --- a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts +++ b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts @@ -105,12 +105,10 @@ suite('Sticky Scroll Tests', () => { const languageService = instantiationService.get(ILanguageFeaturesService); languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel()); const provider: StickyLineCandidateProvider = new StickyLineCandidateProvider(editor, languageService); - await provider.update(); - assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 1, endLineNumber: 4 }), [new StickyLineCandidate(1, 2, 1)]); - assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 1, endLineNumber: 10 }), [new StickyLineCandidate(1, 2, 1), new StickyLineCandidate(7, 11, 1), new StickyLineCandidate(9, 11, 2), new StickyLineCandidate(10, 10, 3)]); - assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 1, endLineNumber: 13 }), [new StickyLineCandidate(1, 2, 1), new StickyLineCandidate(7, 11, 1), new StickyLineCandidate(9, 11, 2), new StickyLineCandidate(10, 10, 3), new StickyLineCandidate(13, 13, 1)]); + assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 8, endLineNumber: 10 }), [new StickyLineCandidate(7, 11, 1), new StickyLineCandidate(9, 11, 2), new StickyLineCandidate(10, 10, 3)]); + assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 10, endLineNumber: 13 }), [new StickyLineCandidate(7, 11, 1), new StickyLineCandidate(9, 11, 2), new StickyLineCandidate(10, 10, 3)]); provider.dispose(); model.dispose(); diff --git a/src/vs/platform/extensionManagement/electron-main/defaultExtensionsProfileInit.ts b/src/vs/platform/extensionManagement/electron-main/defaultExtensionsProfileInit.ts index 53bfc0190ae..12e63157320 100644 --- a/src/vs/platform/extensionManagement/electron-main/defaultExtensionsProfileInit.ts +++ b/src/vs/platform/extensionManagement/electron-main/defaultExtensionsProfileInit.ts @@ -9,6 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; import { EXTENSIONS_RESOURCE_NAME } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IUserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; @@ -18,18 +19,23 @@ export class DefaultExtensionsProfileInitHandler extends Disposable { @IFileService private readonly fileService: IFileService, @IExtensionsScannerService private readonly extensionsScannerService: IExtensionsScannerService, @IExtensionsProfileScannerService private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, + @ILogService logService: ILogService, ) { super(); - this._register(userDataProfilesService.onWillCreateProfile(e => { - if (userDataProfilesService.profiles.length === 1) { - e.join(this.initialize()); - } - })); - this._register(userDataProfilesService.onDidChangeProfiles(e => { - if (userDataProfilesService.profiles.length === 1) { - this.uninitialize(); - } - })); + if (userDataProfilesService.isEnabled()) { + this._register(userDataProfilesService.onWillCreateProfile(e => { + if (userDataProfilesService.profiles.length === 1) { + e.join(this.initialize()); + } + })); + this._register(userDataProfilesService.onDidChangeProfiles(e => { + if (userDataProfilesService.profiles.length === 1) { + this.uninitialize(); + } + })); + } else { + this.uninitialize().then(null, e => logService.error(e)); + } } private async initialize(): Promise { diff --git a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts index 9bef47e4a4b..7f3da1e2fe8 100644 --- a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts @@ -39,6 +39,15 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme super(stateMainService, uriIdentityService, environmentService, fileService, logService); } + override setEnablement(enabled: boolean): void { + super.setEnablement(enabled); + if (!this.enabled) { + // reset + this.saveStoredProfiles([]); + this.saveStoredProfileAssociations({}); + } + } + isEnabled(): boolean { return this.enabled; } @@ -69,11 +78,19 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme } protected override saveStoredProfiles(storedProfiles: StoredUserDataProfile[]): void { - this.stateMainService.setItem(UserDataProfilesMainService.PROFILES_KEY, storedProfiles); + if (storedProfiles.length) { + this.stateMainService.setItem(UserDataProfilesMainService.PROFILES_KEY, storedProfiles); + } else { + this.stateMainService.removeItem(UserDataProfilesMainService.PROFILES_KEY); + } } protected override saveStoredProfileAssociations(storedProfileAssociations: StoredProfileAssociations): void { - this.stateMainService.setItem(UserDataProfilesMainService.PROFILE_ASSOCIATIONS_KEY, storedProfileAssociations); + if (storedProfileAssociations.emptyWindow || storedProfileAssociations.workspaces) { + this.stateMainService.setItem(UserDataProfilesMainService.PROFILE_ASSOCIATIONS_KEY, storedProfileAssociations); + } else { + this.stateMainService.removeItem(UserDataProfilesMainService.PROFILE_ASSOCIATIONS_KEY); + } } protected override getStoredProfileAssociations(): StoredProfileAssociations { diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 4753211a74b..ad6142a9143 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -168,6 +168,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._register(treeView.onDidExpandItem(item => this._proxy.$setExpanded(treeViewId, item.handle, true))); this._register(treeView.onDidCollapseItem(item => this._proxy.$setExpanded(treeViewId, item.handle, false))); this._register(treeView.onDidChangeSelection(items => this._proxy.$setSelection(treeViewId, items.map(({ handle }) => handle)))); + this._register(treeView.onDidChangeFocus(item => this._proxy.$setFocus(treeViewId, item.handle))); this._register(treeView.onDidChangeVisibility(isVisible => this._proxy.$setVisible(treeViewId, isVisible))); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 10efae0f6dc..2316c9312a3 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1406,6 +1406,7 @@ export interface ExtHostTreeViewsShape { $handleDrag(sourceViewId: string, sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise; $setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void; $setSelection(treeViewId: string, treeItemHandles: string[]): void; + $setFocus(treeViewId: string, treeItemHandle: string): void; $setVisible(treeViewId: string, visible: boolean): void; $hasResolve(treeViewId: string): Promise; $resolve(treeViewId: string, treeItemHandle: string, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 97d23231f10..b5b3f8350be 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -59,7 +59,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { ) { function isTreeViewConvertableItem(arg: any): boolean { - return arg && arg.$treeViewId && (arg.$treeItemHandle || arg.$selectedTreeItems); + return arg && arg.$treeViewId && (arg.$treeItemHandle || arg.$selectedTreeItems || arg.$focusedTreeItem); } commands.registerArgumentProcessor({ processArgument: arg => { @@ -221,6 +221,14 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { treeView.setSelection(treeItemHandles); } + $setFocus(treeViewId: string, treeItemHandles: string) { + const treeView = this.treeViews.get(treeViewId); + if (!treeView) { + throw new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)); + } + treeView.setFocus(treeItemHandles); + } + $setVisible(treeViewId: string, isVisible: boolean): void { const treeView = this.treeViews.get(treeViewId); if (!treeView) { @@ -240,8 +248,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { if (treeView && '$treeItemHandle' in arg) { return treeView.getExtensionElement(arg.$treeItemHandle); } - if (treeView && '$selectedTreeItems' in arg && arg.$selectedTreeItems) { - return { selectedTreeItems: treeView.selectedElements }; + if (treeView && '$focusedTreeItem' in arg && arg.$focusedTreeItem) { + return treeView.focusedElement; } return null; } @@ -276,6 +284,9 @@ class ExtHostTreeView extends Disposable { private _selectedHandles: TreeItemHandle[] = []; get selectedElements(): T[] { return this._selectedHandles.map(handle => this.getExtensionElement(handle)).filter(element => !isUndefinedOrNull(element)); } + private _focusedHandle: TreeItemHandle | undefined = undefined; + get focusedElement(): T | undefined { return (this._focusedHandle ? this.getExtensionElement(this._focusedHandle) : undefined); } + private _onDidExpandElement: Emitter> = this._register(new Emitter>()); readonly onDidExpandElement: Event> = this._onDidExpandElement.event; @@ -455,6 +466,10 @@ class ExtHostTreeView extends Disposable { } } + setFocus(treeItemHandle: TreeItemHandle) { + this._focusedHandle = treeItemHandle; + } + setVisible(visible: boolean): void { if (visible !== this._visible) { this._visible = visible; diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css index 811a51dce7e..8491c818eee 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -151,7 +151,7 @@ } .customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item>.custom-view-tree-node-item-icon.disabled { - opacity: 60%; + opacity: 0.6; } /* makes spinning icons square */ .customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item > .custom-view-tree-node-item-icon.codicon.codicon-modifier-spin { diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index ec46f00c5c9..ba93aaf42ae 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -71,6 +71,7 @@ export class TreeViewPane extends ViewPane { protected readonly treeView: ITreeView; private _container: HTMLElement | undefined; + private _actionRunner: MultipleSelectionActionRunner; constructor( options: IViewletViewOptions, @@ -83,6 +84,7 @@ export class TreeViewPane extends ViewPane { @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, + @INotificationService notificationService: INotificationService ) { super({ ...(options as IViewPaneOptions), titleMenuId: MenuId.ViewTitle, donotForwardArgs: false }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(options.id)); @@ -103,6 +105,7 @@ export class TreeViewPane extends ViewPane { if (options.titleDescription !== this.treeView.description) { this.updateTitleDescription(this.treeView.description); } + this._actionRunner = new MultipleSelectionActionRunner(notificationService, () => this.treeView.getSelection()); this.updateTreeVisibility(); } @@ -143,11 +146,12 @@ export class TreeViewPane extends ViewPane { this.treeView.setVisibility(this.isBodyVisible()); } + override getActionRunner() { + return this._actionRunner; + } + override getActionsContext(): TreeViewPaneHandleArg { - return { - $selectedTreeItems: true, - $treeViewId: this.id - }; + return { $treeViewId: this.id, $focusedTreeItem: true, $selectedTreeItems: true }; } } @@ -214,6 +218,9 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { private _onDidChangeSelection: Emitter = this._register(new Emitter()); readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; + private _onDidChangeFocus: Emitter = this._register(new Emitter()); + readonly onDidChangeFocus: Event = this._onDidChangeFocus.event; + private readonly _onDidChangeVisibility: Emitter = this._register(new Emitter()); readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; @@ -617,6 +624,11 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { customTreeKey.set(true); this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner))); this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements))); + this._register(this.tree.onDidChangeFocus(e => { + if (e.elements.length) { + this._onDidChangeFocus.fire(e.elements[0]); + } + })); this._register(this.tree.onDidChangeCollapseState(e => { if (!e.node.element) { return; @@ -803,6 +815,10 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { this.tree?.setSelection(items); } + getSelection(): ITreeItem[] { + return this.tree?.getSelection() ?? []; + } + setFocus(item: ITreeItem): void { if (this.tree) { this.focus(true, item); @@ -1234,13 +1250,13 @@ class MultipleSelectionActionRunner extends ActionRunner { })); } - override async runAction(action: IAction, context: TreeViewItemHandleArg): Promise { + override async runAction(action: IAction, context: TreeViewItemHandleArg | TreeViewPaneHandleArg): Promise { const selection = this.getSelectedResources(); let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined; let actionInSelected: boolean = false; if (selection.length > 1) { selectionHandleArgs = selection.map(selected => { - if (selected.handle === context.$treeItemHandle) { + if ((selected.handle === (context as TreeViewItemHandleArg).$treeItemHandle) || (context as TreeViewPaneHandleArg).$selectedTreeItems) { actionInSelected = true; } return { $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle }; diff --git a/src/vs/workbench/browser/parts/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index 22a0701865b..84ee0c009f3 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -11,7 +11,7 @@ import { attachButtonStyler, attachProgressBarStyler } from 'vs/platform/theme/c import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { after, append, $, trackFocus, EventType, addDisposableListener, createCSSRule, asCSSUrl } from 'vs/base/browser/dom'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; import { ActionsOrientation, IActionViewItem, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar'; import { Registry } from 'vs/platform/registry/common/platform'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; @@ -298,7 +298,8 @@ export abstract class ViewPane extends Pane implements IView { actionViewItemProvider: action => this.getActionViewItem(action), ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title), getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), - renderDropdownAsChildElement: true + renderDropdownAsChildElement: true, + actionRunner: this.getActionRunner() }); this._register(this.toolbar); @@ -519,6 +520,10 @@ export abstract class ViewPane extends Pane implements IView { return undefined; } + getActionRunner(): IActionRunner | undefined { + return undefined; + } + getOptimalWidth(): number { return 0; } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 506b55641ec..b84662e5a6d 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -663,6 +663,8 @@ export interface ITreeView extends IDisposable { readonly onDidChangeSelection: Event; + readonly onDidChangeFocus: Event; + readonly onDidChangeVisibility: Event; readonly onDidChangeActions: Event; @@ -691,6 +693,8 @@ export interface ITreeView extends IDisposable { setSelection(items: ITreeItem[]): void; + getSelection(): ITreeItem[]; + setFocus(item: ITreeItem): void; show(container: any): void; @@ -712,7 +716,8 @@ export interface ITreeViewDescriptor extends IViewDescriptor { export type TreeViewPaneHandleArg = { $treeViewId: string; - $selectedTreeItems: boolean; + $selectedTreeItems?: boolean; + $focusedTreeItem?: boolean; }; export type TreeViewItemHandleArg = { diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts index 39c40200045..e35245d03aa 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts @@ -51,6 +51,8 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { equals } from 'vs/base/common/objects'; import { IEditSessionIdentityService } from 'vs/platform/workspace/common/editSessions'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; +import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; registerSingleton(IEditSessionsLogService, EditSessionsLogService); registerSingleton(IEditSessionsStorageService, EditSessionsWorkbenchService); @@ -68,6 +70,16 @@ const openLocalFolderCommand: IAction2Options = { category: EDIT_SESSION_SYNC_CATEGORY, precondition: IsWebContext }; +const showOutputChannelCommand: IAction2Options = { + id: 'workbench.experimental.editSessions.actions.showOutputChannel', + title: { value: localize('show log', 'Show Log'), original: 'Show Log' }, + category: EDIT_SESSION_SYNC_CATEGORY +}; +const resumingProgressOptions = { + location: ProgressLocation.Window, + type: 'syncing', + title: `[${localize('resuming edit session window', 'Resuming edit session...')}](command:${showOutputChannelCommand.id})` +}; const queryParamName = 'editSessionId'; const experimentalSettingName = 'workbench.experimental.editSessions.enabled'; @@ -120,31 +132,27 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo this.lifecycleService.onWillShutdown((e) => e.join(this.autoStoreEditSession(), { id: 'autoStoreEditSession', label: localize('autoStoreEditSession', 'Storing current edit session...') })); } - private async autoResumeEditSession() { - if (this.environmentService.editSessionId !== undefined) { - // In web, resume edit session based on an edit session GUID that - // was explicitly passed into the workbench construction options - void this.progressService.withProgress({ location: ProgressLocation.Window, type: 'syncing', title: localize('resuming edit session dialog', 'Resuming your latest edit session...') }, async () => { - performance.mark('code/willResumeEditSessionFromIdentifier'); + private autoResumeEditSession() { + void this.progressService.withProgress(resumingProgressOptions, async () => { + performance.mark('code/willResumeEditSessionFromIdentifier'); - type ResumeEvent = {}; - type ResumeClassification = { - owner: 'joyceerhl'; comment: 'Reporting when an action is resumed from an edit session identifier.'; - }; - this.telemetryService.publicLog2('editSessions.continue.resume'); + type ResumeEvent = {}; + type ResumeClassification = { + owner: 'joyceerhl'; comment: 'Reporting when an action is resumed from an edit session identifier.'; + }; + this.telemetryService.publicLog2('editSessions.continue.resume'); + if (this.environmentService.editSessionId !== undefined) { await this.resumeEditSession(this.environmentService.editSessionId).finally(() => this.environmentService.editSessionId = undefined); - - performance.mark('code/didResumeEditSessionFromIdentifier'); - }); - } else if (this.configurationService.getValue('workbench.experimental.editSessions.autoResume') === 'onReload' && this.editSessionsStorageService.isSignedIn) { - // Attempt to resume edit session based on edit workspace identifier - // Note: at this point if the user is not signed into edit sessions, - // we don't want them to be prompted to sign in and should just return early - void this.progressService.withProgress({ location: ProgressLocation.Window, type: 'syncing', title: localize('resuming edit session window', 'Resuming edit session...') }, async () => { + } else if (this.configurationService.getValue('workbench.experimental.editSessions.autoResume') === 'onReload' && this.editSessionsStorageService.isSignedIn) { + // Attempt to resume edit session based on edit workspace identifier + // Note: at this point if the user is not signed into edit sessions, + // we don't want them to be prompted to sign in and should just return early await this.resumeEditSession(undefined, true); - }); - } + } + + performance.mark('code/didResumeEditSessionFromIdentifier'); + }); } private async autoStoreEditSession() { @@ -187,10 +195,24 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo this.registerContinueInLocalFolderAction(); this.registerShowEditSessionViewAction(); + this.registerShowEditSessionOutputChannelAction(); this.registered = true; } + private registerShowEditSessionOutputChannelAction() { + this._register(registerAction2(class ShowEditSessionOutput extends Action2 { + constructor() { + super(showOutputChannelCommand); + } + + run(accessor: ServicesAccessor, ...args: any[]) { + const outputChannel = accessor.get(IOutputService); + void outputChannel.showChannel(Constants.editSessionsLogChannelId); + } + })); + } + private registerShowEditSessionViewAction() { const that = this; this._register(registerAction2(class ShowEditSessionView extends Action2 { @@ -262,10 +284,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo } async run(accessor: ServicesAccessor, editSessionId?: string): Promise { - await that.progressService.withProgress({ - location: ProgressLocation.Notification, - title: localize('resuming edit session', 'Resuming edit session...') - }, async () => { + await that.progressService.withProgress(resumingProgressOptions, async () => { type ResumeEvent = {}; type ResumeClassification = { owner: 'joyceerhl'; comment: 'Reporting when the resume edit session action is invoked.'; diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index e9566958c72..6f4d49ddd20 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -14,7 +14,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IRequestService } from 'vs/platform/request/common/request'; import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { createSyncHeaders, IAuthenticationProvider, IResourceRefHandle, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { createSyncHeaders, IAuthenticationProvider, IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -23,6 +23,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { generateUuid } from 'vs/base/common/uuid'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; +import { isWeb } from 'vs/base/common/platform'; type ExistingSession = IQuickPickItem & { session: AuthenticationSession & { providerId: string } }; type AuthenticationProviderOption = IQuickPickItem & { provider: IAuthenticationProvider }; @@ -57,7 +58,6 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes @IRequestService private readonly requestService: IRequestService, @IDialogService private readonly dialogService: IDialogService, @ICredentialsService private readonly credentialsService: ICredentialsService, - @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService, ) { super(); @@ -202,7 +202,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } // If settings sync is already enabled, avoid asking again to authenticate - if (this.userDataSyncEnablementService.isEnabled()) { + if (this.shouldAttemptEditSessionInit()) { this.logService.info(`Reusing user data sync enablement`); const authenticationSessionInfo = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.productService); if (authenticationSessionInfo !== undefined) { @@ -222,6 +222,10 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes return undefined; } + private shouldAttemptEditSessionInit(): boolean { + return isWeb && this.storageService.isNew(StorageScope.APPLICATION) && this.storageService.isNew(StorageScope.WORKSPACE); + } + /** * * Prompts the user to pick an authentication option for storing and getting edit sessions. diff --git a/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts b/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts index a145cb15b7c..284e76467a7 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts @@ -8,10 +8,9 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ILocalizedString } from 'vs/platform/action/common/action'; import { Action2, IAction2Options, MenuId } from 'vs/platform/actions/common/actions'; -import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { API_OPEN_DIFF_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { IResourceMergeEditorInput } from 'vs/workbench/common/editor'; import { MergeEditorInputData } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; @@ -213,14 +212,14 @@ export class OpenResultResource extends MergeEditorAction { } } -export class GoToNextConflict extends MergeEditorAction { +export class GoToNextUnhandledConflict extends MergeEditorAction { constructor() { super({ - id: 'merge.goToNextConflict', + id: 'merge.goToNextUnhandledConflict', category: mergeEditorCategory, title: { - value: localize('merge.goToNextConflict', 'Go to Next Conflict'), - original: 'Go to Next Conflict', + value: localize('merge.goToNextUnhandledConflict', 'Go to Next Unhandled Conflict'), + original: 'Go to Next Unhandled Conflict', }, icon: Codicon.arrowDown, menu: [ @@ -237,21 +236,21 @@ export class GoToNextConflict extends MergeEditorAction { } override runWithViewModel(viewModel: MergeEditorViewModel): void { - viewModel.goToNextModifiedBaseRange(true); + viewModel.goToNextModifiedBaseRange(r => !viewModel.model.isHandled(r).get()); } } -export class GoToPreviousConflict extends MergeEditorAction { +export class GoToPreviousUnhandledConflict extends MergeEditorAction { constructor() { super({ - id: 'merge.goToPreviousConflict', + id: 'merge.goToPreviousUnhandledConflict', category: mergeEditorCategory, title: { value: localize( - 'merge.goToPreviousConflict', - 'Go to Previous Conflict' + 'merge.goToPreviousUnhandledConflict', + 'Go to Previous Unhandled Conflict' ), - original: 'Go to Previous Conflict', + original: 'Go to Previous Unhandled Conflict', }, icon: Codicon.arrowUp, menu: [ @@ -268,7 +267,7 @@ export class GoToPreviousConflict extends MergeEditorAction { } override runWithViewModel(viewModel: MergeEditorViewModel): void { - viewModel.goToPreviousModifiedBaseRange(true); + viewModel.goToPreviousModifiedBaseRange(r => !viewModel.model.isHandled(r).get()); } } @@ -336,8 +335,8 @@ export class CompareInput1WithBaseCommand extends MergeEditorAction { } override runWithViewModel(viewModel: MergeEditorViewModel, accessor: ServicesAccessor): void { - const commandService = accessor.get(ICommandService); - mergeEditorCompare(viewModel, commandService, 1); + const editorService = accessor.get(IEditorService); + mergeEditorCompare(viewModel, editorService, 1); } } @@ -361,20 +360,29 @@ export class CompareInput2WithBaseCommand extends MergeEditorAction { } override runWithViewModel(viewModel: MergeEditorViewModel, accessor: ServicesAccessor): void { - const commandService = accessor.get(ICommandService); - mergeEditorCompare(viewModel, commandService, 2); + const editorService = accessor.get(IEditorService); + mergeEditorCompare(viewModel, editorService, 2); } } -function mergeEditorCompare(viewModel: MergeEditorViewModel, commandService: ICommandService, inputNumber: 1 | 2) { +async function mergeEditorCompare(viewModel: MergeEditorViewModel, editorService: IEditorService, inputNumber: 1 | 2) { const model = viewModel.model; - const base = model.base.uri; - const input = inputNumber === 1 ? model.input1.textModel.uri : model.input2.textModel.uri; - openDiffEditor(commandService, base, input); -} + const base = model.base; + const input = inputNumber === 1 ? viewModel.inputCodeEditorView1.editor : viewModel.inputCodeEditorView2.editor; -function openDiffEditor(commandService: ICommandService, left: URI, right: URI, label?: string) { - commandService.executeCommand(API_OPEN_DIFF_EDITOR_COMMAND_ID, left, right, label); + const lineNumber = input.getPosition()!.lineNumber; + await editorService.openEditor({ + original: { resource: base.uri }, + modified: { resource: input.getModel()!.uri }, + options: { + selection: { + startLineNumber: lineNumber, + startColumn: 1, + }, + revealIfOpened: true, + revealIfVisible: true, + } as ITextEditorOptions + }); } export class OpenBaseFile extends MergeEditorAction { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index e0acb9e304d..b370ddcad5f 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -11,7 +11,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; -import { AcceptAllInput1, AcceptAllInput2, CompareInput1WithBaseCommand, CompareInput2WithBaseCommand, GoToNextConflict, GoToPreviousConflict, OpenBaseFile, OpenMergeEditor, OpenResultResource, SetColumnLayout, SetMixedLayout, ToggleActiveConflictInput1, ToggleActiveConflictInput2 } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands'; +import { AcceptAllInput1, AcceptAllInput2, CompareInput1WithBaseCommand, CompareInput2WithBaseCommand, GoToNextUnhandledConflict, GoToPreviousUnhandledConflict, OpenBaseFile, OpenMergeEditor, OpenResultResource, SetColumnLayout, SetMixedLayout, ToggleActiveConflictInput1, ToggleActiveConflictInput2 } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { MergeEditor, MergeEditorResolverContribution, MergeEditorOpenHandlerContribution } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -53,8 +53,8 @@ registerAction2(SetColumnLayout); registerAction2(OpenMergeEditor); registerAction2(OpenBaseFile); -registerAction2(GoToNextConflict); -registerAction2(GoToPreviousConflict); +registerAction2(GoToNextUnhandledConflict); +registerAction2(GoToPreviousUnhandledConflict); registerAction2(ToggleActiveConflictInput1); registerAction2(ToggleActiveConflictInput2); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index 510c013d1a5..9cf1a828717 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -265,7 +265,7 @@ class MergeEditorCloseHandler implements IEditorCloseHandler { const actions: string[] = [ localize('saveWithConflict', "Save with Conflicts"), - localize('discard', "Don't save"), + localize('discard', "Don't Save"), localize('cancel', "Cancel"), ]; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts index 545b075c62f..679bc55cc03 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts @@ -54,12 +54,18 @@ export class MergeMarkersController extends Disposable { const startLine = model!.getLineContent(b.lineRange.startLineNumber).substring(0, 20); const endLine = model!.getLineContent(b.lineRange.endLineNumberExclusive - 1).substring(0, 20); + const conflictingLinesCount = b.lineRange.lineCount - 2; + const domNode = h('div', [ h('div.conflict-zone-root', [ h('pre', [startLine]), h('span.dots', ['...']), h('pre', [endLine]), - h('span.text', [nls.localize('conflictingLines', "Conflicting Lines")]), + h('span.text', [ + conflictingLinesCount === 1 + ? nls.localize('conflictingLine', "1 Conflicting Line") + : nls.localize('conflictingLines', "{0} Conflicting Lines", conflictingLinesCount) + ]), ]), ]).root; this.viewZoneIds.push(c.addZone({ diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts index de2e9e2c6df..9ff067339ea 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts @@ -42,6 +42,7 @@ export class LineEdits { constructor(public readonly edits: readonly LineRangeEdit[]) { } public apply(model: ITextModel): void { + model.pushStackElement(); model.pushEditOperations( null, this.edits.map((e) => { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts index 99c7ca7e93f..3ce1b8d12dd 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts @@ -317,7 +317,11 @@ export class MergeConflictGutterItemView extends Disposable implements IGutterIt this.item = observableValue('item', item); - const checkBox = new Toggle({ isChecked: false, title: localize('accept', "Accept"), icon: Codicon.check }); + const checkBox = new Toggle({ + isChecked: false, + title: '', + icon: Codicon.check + }); this._register(attachToggleStyler(checkBox, themeService)); @@ -351,14 +355,16 @@ export class MergeConflictGutterItemView extends Disposable implements IGutterIt autorun('Update Checkbox', (reader) => { const item = this.item.read(reader)!; const value = item.toggleState.read(reader); - const iconMap: Record = { - [InputState.excluded]: { icon: undefined, checked: false }, - [InputState.conflicting]: { icon: Codicon.circleFilled, checked: false }, - [InputState.first]: { icon: Codicon.check, checked: true }, - [InputState.second]: { icon: Codicon.checkAll, checked: true }, + const iconMap: Record = { + [InputState.excluded]: { icon: undefined, checked: false, title: localize('accept.excluded', "Accept") }, + [InputState.conflicting]: { icon: Codicon.circleFilled, checked: false, title: localize('accept.conflicting', "Accept (result is dirty)") }, + [InputState.first]: { icon: Codicon.check, checked: true, title: localize('accept.first', "Undo accept") }, + [InputState.second]: { icon: Codicon.checkAll, checked: true, title: localize('accept.second', "Undo accept (currently second)") }, }; - checkBox.setIcon(iconMap[value].icon); - checkBox.checked = iconMap[value].checked; + const state = iconMap[value]; + checkBox.setIcon(state.icon); + checkBox.checked = state.checked; + checkBox.setTitle(state.title); if (!item.enabled.read(reader)) { checkBox.disable(); @@ -393,7 +399,6 @@ export class MergeConflictGutterItemView extends Disposable implements IGutterIt } layout(top: number, height: number, viewTop: number, viewHeight: number): void { - const checkboxHeight = this.checkboxDiv.clientHeight; const middleHeight = height / 2 - checkboxHeight / 2; @@ -413,7 +418,7 @@ export class MergeConflictGutterItemView extends Disposable implements IGutterIt if (preferredParentRange[0] < preferredParentRange[1]) { effectiveCheckboxTop = clamp(effectiveCheckboxTop, preferredViewPortRange[0], preferredViewPortRange[1]); - effectiveCheckboxTop = clampIfIntervalIsNonEmpty(effectiveCheckboxTop, preferredParentRange[0], preferredParentRange[1]); + effectiveCheckboxTop = clamp(effectiveCheckboxTop, preferredParentRange[0], preferredParentRange[1]); } this.checkboxDiv.style.top = `${effectiveCheckboxTop - top}px`; @@ -431,10 +436,3 @@ export class MergeConflictGutterItemView extends Disposable implements IGutterIt }); } } - -function clampIfIntervalIsNonEmpty(value: number, min: number, max: number): number { - if (min >= max) { - return value; - } - return Math.min(Math.max(value, min), max); -} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts index b76e96844f6..8b789b859be 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts @@ -69,9 +69,9 @@ export class MergeEditorViewModel { constructor( public readonly model: MergeEditorModel, - private readonly inputCodeEditorView1: InputCodeEditorView, - private readonly inputCodeEditorView2: InputCodeEditorView, - private readonly resultCodeEditorView: ResultCodeEditorView + public readonly inputCodeEditorView1: InputCodeEditorView, + public readonly inputCodeEditorView2: InputCodeEditorView, + public readonly resultCodeEditorView: ResultCodeEditorView ) { } public setState( @@ -104,34 +104,34 @@ export class MergeEditorViewModel { } } - public goToNextModifiedBaseRange(onlyConflicting: boolean): void { + public goToNextModifiedBaseRange(predicate: (m: ModifiedBaseRange) => boolean): void { this.goToConflict( (e, l) => this.model.modifiedBaseRanges .get() .find( (r) => - (!onlyConflicting || r.isConflicting) && + predicate(r) && this.getRange(e, r, undefined).startLineNumber > l ) || this.model.modifiedBaseRanges .get() - .find((r) => !onlyConflicting || r.isConflicting) + .find((r) => predicate(r)) ); } - public goToPreviousModifiedBaseRange(onlyConflicting: boolean): void { + public goToPreviousModifiedBaseRange(predicate: (m: ModifiedBaseRange) => boolean): void { this.goToConflict( (e, l) => findLast( this.model.modifiedBaseRanges.get(), (r) => - (!onlyConflicting || r.isConflicting) && + predicate(r) && this.getRange(e, r, undefined).endLineNumberExclusive < l ) || findLast( this.model.modifiedBaseRanges.get(), - (r) => !onlyConflicting || r.isConflicting + (r) => predicate(r) ) ); } diff --git a/src/vs/workbench/contrib/offline/browser/offline.contribution.ts b/src/vs/workbench/contrib/offline/browser/offline.contribution.ts index 075a47e02c8..75b1671e327 100644 --- a/src/vs/workbench/contrib/offline/browser/offline.contribution.ts +++ b/src/vs/workbench/contrib/offline/browser/offline.contribution.ts @@ -94,4 +94,4 @@ export class OfflineStatusBarController implements IWorkbenchContribution { } Registry.as(Extensions.Workbench) - .registerWorkbenchContribution(OfflineStatusBarController, LifecyclePhase.Starting); + .registerWorkbenchContribution(OfflineStatusBarController, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts index 30d07dcc5b3..527ed9fdcd9 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts @@ -607,7 +607,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { tags['workspace.bower'] = nameSet.has('bower.json') || nameSet.has('bower_components'); tags['workspace.java.pom'] = nameSet.has('pom.xml'); - tags['workspace.java.gradle'] = nameSet.has('build.gradle') || nameSet.has('settings.gradle'); + tags['workspace.java.gradle'] = nameSet.has('build.gradle') || nameSet.has('settings.gradle') || nameSet.has('build.gradle.kts') || nameSet.has('settings.gradle.kts') || nameSet.has('gradlew') || nameSet.has('gradlew.bat'); tags['workspace.yeoman.code.ext'] = nameSet.has('vsc-extension-quickstart.md'); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts index 0a9890b3d0f..dde5f8c333d 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncMergesView.ts @@ -36,7 +36,7 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; @@ -66,8 +66,9 @@ export class UserDataSyncMergesViewPane extends TreeViewPane { @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, + @INotificationService notificationService: INotificationService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, notificationService); this.userDataSyncPreview = userDataSyncWorkbenchService.userDataSyncPreview; this._register(this.userDataSyncPreview.onDidChangeResources(() => this.updateSyncButtonEnablement())); diff --git a/src/vs/workbench/services/editor/common/editorGroupFinder.ts b/src/vs/workbench/services/editor/common/editorGroupFinder.ts index b0c70ee48ba..9ad20867507 100644 --- a/src/vs/workbench/services/editor/common/editorGroupFinder.ts +++ b/src/vs/workbench/services/editor/common/editorGroupFinder.ts @@ -7,7 +7,8 @@ import { isEqual } from 'vs/base/common/resources'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { EditorActivation } from 'vs/platform/editor/common/editor'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { EditorResourceAccessor, EditorInputWithOptions, isEditorInputWithOptions, IUntypedEditorInput, isEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; +import { EditorResourceAccessor, EditorInputWithOptions, isEditorInputWithOptions, IUntypedEditorInput, isEditorInput, EditorInputCapabilities, isResourceDiffEditorInput } from 'vs/workbench/common/editor'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IEditorGroup, GroupsOrder, preferredSideBySideGroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { PreferredGroup, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -205,6 +206,9 @@ function matchesEditor(typedEditor: EditorInput, editor: EditorInput | IUntypedE // editors that have no `override` defined. // // TODO@lramos15 https://github.com/microsoft/vscode/issues/131619 + if (typedEditor instanceof DiffEditorInput && isResourceDiffEditorInput(editor)) { + return matchesEditor(typedEditor.primary, editor.modified) && matchesEditor(typedEditor.secondary, editor.original); + } if (typedEditor.resource) { return isEqual(typedEditor.resource, EditorResourceAccessor.getCanonicalUri(editor)); } diff --git a/test/unit/electron/index.js b/test/unit/electron/index.js index e9f75612c7d..60cdcfd4ba3 100644 --- a/test/unit/electron/index.js +++ b/test/unit/electron/index.js @@ -26,7 +26,7 @@ const optimist = require('optimist') .describe('runGlob', 'only run tests matching ').alias('runGlob', 'glob').alias('runGlob', 'runGrep').string('runGlob') .describe('build', 'run with build output (out-build)').boolean('build') .describe('coverage', 'generate coverage report').boolean('coverage') - .describe('debug', 'open dev tools, keep window open, reuse app data').string('debug') + .describe('dev', 'open dev tools, keep window open, reuse app data').alias('dev', ['dev-tools', 'devTools']).string('dev') .describe('reporter', 'the mocha reporter').string('reporter').default('reporter', 'spec') .describe('reporter-options', 'the mocha reporter options').string('reporter-options').default('reporter-options', '') .describe('wait-server', 'port to connect to and wait before running tests') @@ -73,7 +73,7 @@ if (crashReporterDirectory) { }); } -if (!argv.debug) { +if (!argv.dev) { app.setPath('userData', path.join(tmpdir(), `vscode-tests-${Date.now()}`)); } @@ -152,7 +152,7 @@ class IPCRunner extends events.EventEmitter { app.on('ready', () => { ipcMain.on('error', (_, err) => { - if (!argv.debug) { + if (!argv.dev) { console.error(err); app.exit(1); } @@ -192,7 +192,7 @@ app.on('ready', () => { }); win.webContents.on('did-finish-load', () => { - if (argv.debug) { + if (argv.dev) { win.show(); win.webContents.openDevTools(); } @@ -270,7 +270,7 @@ app.on('ready', () => { applyReporter(runner, argv); } - if (!argv.debug) { + if (!argv.dev) { ipcMain.on('all done', () => app.exit(runner.didFail ? 1 : 0)); } });