Merge branch 'main' into ai-codefixes

This commit is contained in:
Nathan Shively-Sanders
2023-08-23 08:15:47 -07:00
359 changed files with 8368 additions and 3237 deletions
+1 -1
View File
@@ -486,7 +486,7 @@
],
"action": "comment",
"addLabel": "info-needed",
"comment": "Please perform the following **three tasks** to diagnose the root cause of the issue:\n\n* [ ] **1.) Disable Extensions**\n * Select `View` and pick `Command Palette...`\n * Run `Developer: Reload With Extensions Disabled`\n * 👉 See if the issue reproduces\n\n* [ ] **2.) Disable Configuration**\n * Select `View` and pick `Command Palette...`\n * Run `Profiles: Create a Temporary Profile`\n * 👉 See if the issue reproduces\n\n* [ ] **3.) Try VS Code Insiders**\n * Download [VS Code Insiders](https://code.visualstudio.com/insiders/)\n * Install and Run it\n * 👉 See if the issue reproduces\n \nThen pick one of the three resolutions depending on which step has helped:\n\n<details>\n <summary>Disabling my Extensions helped</summary>\n\nPlease run the command `Start Extension Bisect` and follow the instructions to find the extension that is causing this issue.\n\n<img width='631' alt='image' src='https://user-images.githubusercontent.com/900690/228760008-d5790ad1-74da-46a2-917f-e1bd26281ca8.png'>\n\nPlease report the issue to the extension causing this.\n</details>\n\n<details>\n <summary>Disabling my configuration helped</summary>\nPlease report back more details about your configuration, including settings.\n</details>\n\n<details>\n <summary>Using VS Code Insiders has helped</summary>\n✅ This likely means that the issue has been addressed already and will be available in an upcoming release. You can safely use VS Code Insiders until the new stable version is available.\n</details>"
"comment": "Please diagnose the root cause of the issue by running the command `F1 > Help: Troubleshoot Issue` and following the instructions. Once you have done that, please update the issue with the results.\n\nHappy Coding!"
},
{
"__comment__": "Allows folks on the team to label issues by commenting: `\\label My-Label` ",
+1
View File
@@ -18,3 +18,4 @@ vscode.db
/cli/target
/cli/openssl
product.overrides.json
*.snap.actual
+10
View File
@@ -124,6 +124,16 @@
"language": "github-issues",
"value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified label:unreleased"
},
{
"kind": 1,
"language": "markdown",
"value": "## All Unverified Fixes"
},
{
"kind": 2,
"language": "github-issues",
"value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:z-author-verified "
},
{
"kind": 1,
"language": "markdown",
+12 -2
View File
@@ -7,7 +7,7 @@
{
"kind": 2,
"language": "github-issues",
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"August 2023\"\n\n$MINE=assignee:@me"
"value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\r\n\r\n$MILESTONE=milestone:\"August 2023\"\r\n\r\n$MINE=assignee:@me"
},
{
"kind": 1,
@@ -169,6 +169,16 @@
"language": "github-issues",
"value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found"
},
{
"kind": 1,
"language": "markdown",
"value": "## Test steps needed from others"
},
{
"kind": 2,
"language": "github-issues",
"value": "$REPOS $MILESTONE -$MINE is:issue label:bug label:verification-steps-needed"
},
{
"kind": 1,
"language": "markdown",
@@ -177,6 +187,6 @@
{
"kind": 2,
"language": "github-issues",
"value": "repo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\nrepo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\nrepo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes"
"value": "repo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\r\nrepo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\r\nrepo:microsoft/vscode $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes"
}
]
+1 -1
View File
@@ -6,7 +6,7 @@
// Placeholders with the same ids are connected.
// Example:
"MSFT Copyright Header": {
"scope": "javascript,typescript,css",
"scope": "javascript,typescript,css,rust",
"prefix": [
"header",
"stub",
+2 -2
View File
@@ -1,5 +1,5 @@
disturl "https://electronjs.org/headers"
target "25.4.0"
ms_build_id "22958381"
target "25.5.0"
ms_build_id "23084831"
runtime "electron"
build_from_source "true"
+27 -27
View File
@@ -1,27 +1,27 @@
fc51b54078335a8d687e0dda5e65e0b9916d9678d13d8f84b1dc91bceaac6b94 *electron-v25.4.0-darwin-arm64-symbols.zip
8a1f0cc5c8fea926360347f9448a766f56fe4fea8418ce11ca5755be933dcae2 *electron-v25.4.0-darwin-arm64.zip
a221e0fb48d178940e77bb96c8bfe26fb5f4eb5b916c93d7b771d0f216e9f310 *electron-v25.4.0-darwin-x64-symbols.zip
f6e65c0d27e99121e53eb52335ef42653108bfa5cb8de06c35853c253fafc000 *electron-v25.4.0-darwin-x64.zip
e1006e2a327f23fe73c172789a9957c83e1b7563358908c0d1afb6f2a95f305c *electron-v25.4.0-linux-arm64-symbols.zip
2225b64fbe7ff602160ad371ede9a4eb79534d865dbac3a6fcc252faf0214240 *electron-v25.4.0-linux-arm64.zip
7ff71baa0e0c55728569a3a05fb1aadd079e2dd79bbc7bb4ce460c8fefe1c422 *electron-v25.4.0-linux-armv7l-symbols.zip
71469e85d3b3bbf40b698ffb3e86b96eef854c2bc63096900d1c1a606f8b9bb4 *electron-v25.4.0-linux-armv7l.zip
28a29971aa65a72ead251bb3be5135b54a1a1c0a858e80959b877aea16975acb *electron-v25.4.0-linux-x64-symbols.zip
7cce96b1f33c7f6333d24f6966bd0613e6d95a9fd3e7fba4f20d11869adc22c6 *electron-v25.4.0-linux-x64.zip
615e880460216419d9b6dfa8cdf59d067ad3f629497e4419cd723351e544645a *electron-v25.4.0-win32-arm64-pdb.zip
548ac3cbfe5a4bd1c8628aa4a73cc424116c0870ab119cf8077993b932baf51b *electron-v25.4.0-win32-arm64-symbols.zip
c51677babba862a0b98ceee992b42eaf7d2c9e8e9dfb1496d15d7381b908dc72 *electron-v25.4.0-win32-arm64.zip
9a6271c5d9eaf07fb01b38bd743b2cdc0642bcf16f9add2724eccad2aea2b3a8 *electron-v25.4.0-win32-ia32-pdb.zip
e961dd15cfde46c9765eebd4f421e17850b85c3f25e0e6891b4f55a9834805f6 *electron-v25.4.0-win32-ia32-symbols.zip
14320e1f7db40e885ee101acd39698be58745cdfe6a519e0a5316269837f5bf7 *electron-v25.4.0-win32-ia32.zip
b055eb2b0bf037fc54f8af9d76c5894ffaffe6bd24e231da7ce847048ed60c97 *electron-v25.4.0-win32-x64-pdb.zip
0d84815a05dd0dd96266808d3cefde52cdea149c5faf2abb2d4d3c028e5f30d0 *electron-v25.4.0-win32-x64-symbols.zip
6f0c6da70a4daa50ec46c389896d08009ce14cdac5896c608e5af8956fd359de *electron-v25.4.0-win32-x64.zip
0e4fd74705d098d616b313b9078e74ba3b44ead2f3abd083acd8fab68048a563 *ffmpeg-v25.4.0-darwin-arm64.zip
a4c1b013448c5b9955d4927be7a72f8836a7af909c795aeecdb43086e76bd0d8 *ffmpeg-v25.4.0-darwin-x64.zip
bd52d57ff97fb56ac01a3482af905d04f0d4e9c13c53858c6d9f99957eca82da *ffmpeg-v25.4.0-linux-arm64.zip
9b3d09177fa1e63e2a6beecfa70aeec30aeb5c1873ff21128a68051c4e23f95d *ffmpeg-v25.4.0-linux-armv7l.zip
edc7b1c9f1a0733f109a2c0375a4e40c5bfe0bf28b7f06dcc76e1ada0aa2f125 *ffmpeg-v25.4.0-linux-x64.zip
39d5389ff83fd30bacfd82e3beea5cb95eaa46aaa7d2ddaccc705f2490210f2e *ffmpeg-v25.4.0-win32-arm64.zip
059c72026437c4d97ec55ef6fab85526d2fa1de3571c478a55dec3489487fdf4 *ffmpeg-v25.4.0-win32-ia32.zip
01871604f273c3206c808954c604afda0294da15dd02dd6360fe736e5b5c82a8 *ffmpeg-v25.4.0-win32-x64.zip
c3fb8cb4804143eb25fce55a179a6f2df8c215ed709104ec235c96e357b20f42 *electron-v25.5.0-darwin-arm64-symbols.zip
e6d2a09348d4fe7c9fbd92bf796489a95e625642f0f1ce96169212554cfa6841 *electron-v25.5.0-darwin-arm64.zip
15c28e613dfee0f7e46a296bb4aed64e17f9644d7ef19129aeb6480b8da230ab *electron-v25.5.0-darwin-x64-symbols.zip
a5c5c0b621daf8242258c89edb2387fbdf1c69125c984f8564d89b87927373e3 *electron-v25.5.0-darwin-x64.zip
69d60a69b7f692b9069cdf9e518bcbaca9cec561f40199cc87196929936a34ce *electron-v25.5.0-linux-arm64-symbols.zip
adec2ba09faf5f8d8af8997c8bed43c7712eba5546db1e7d06f8357bf4613921 *electron-v25.5.0-linux-arm64.zip
0fe7a5d152c9c401671e02ebcd9da34ab9bb0c28598ede3657077a79f8b3e70a *electron-v25.5.0-linux-armv7l-symbols.zip
eccb66e4a308a0bd2d90474894370e3d687e32f78672e6b5077c1822c7bc526b *electron-v25.5.0-linux-armv7l.zip
82bd9bc9e66f8ae802fe48a51b8d7e2fb599403e9715fa4b859190200a7376b1 *electron-v25.5.0-linux-x64-symbols.zip
485cbeb206fccfb4ed42f694100eebb80c9db6639b3537a95823c4fcb7f210cd *electron-v25.5.0-linux-x64.zip
ed2c2a7da571b53bcb336b9a2a024753a272df82ece45df83df888df51bf1912 *electron-v25.5.0-win32-arm64-pdb.zip
c316f6364e9b4cd61e19d4763c96abda7247a2c31ae7d30e81219c9a7754f11a *electron-v25.5.0-win32-arm64-symbols.zip
582ccbfc5a85a093f5639ee476bb5fef18c2d25dab4a60e5f5cb47e64c99f7fb *electron-v25.5.0-win32-arm64.zip
5776e650c23e3847b0c52d850f61c57a84a4fa30943c3cc82197250112911b5b *electron-v25.5.0-win32-ia32-pdb.zip
730b429ad2c4cae0fe3ba9600a6ee86c79dada77976bac1c75ca2404993495bc *electron-v25.5.0-win32-ia32-symbols.zip
7e6e68aa33a89c0d647575b06daf415a2401feb170ebb9cd795e221af321f751 *electron-v25.5.0-win32-ia32.zip
0792665fda9255b340a829cdd24601887a2ca8f04cc49f9a6d4557db0c0cd2f2 *electron-v25.5.0-win32-x64-pdb.zip
dc2459546951f8418e866857e9111dd83a0789d401906b2200c2f8dd59d9146b *electron-v25.5.0-win32-x64-symbols.zip
9bf7980fbc024ba77ea8ab3e2d32088a5f69bf32506a7d2db72ede17028abdf4 *electron-v25.5.0-win32-x64.zip
5b1ea601b737842eacf88d7456c4d14e697822753e9a08ede889cce9e20bafb2 *ffmpeg-v25.5.0-darwin-arm64.zip
37bf5c75edefc0b6735b44d0b5b06fdd427179a8501f6e93df9840283cdb4a95 *ffmpeg-v25.5.0-darwin-x64.zip
bd52d57ff97fb56ac01a3482af905d04f0d4e9c13c53858c6d9f99957eca82da *ffmpeg-v25.5.0-linux-arm64.zip
9b3d09177fa1e63e2a6beecfa70aeec30aeb5c1873ff21128a68051c4e23f95d *ffmpeg-v25.5.0-linux-armv7l.zip
edc7b1c9f1a0733f109a2c0375a4e40c5bfe0bf28b7f06dcc76e1ada0aa2f125 *ffmpeg-v25.5.0-linux-x64.zip
2e28767b3570ea247869a20988cddd23af710eb994d6099404f123390cedeba6 *ffmpeg-v25.5.0-win32-arm64.zip
715568eefd7267573a30186ade3de901587baeb1f013200d8ae50b35941b613f *ffmpeg-v25.5.0-win32-ia32.zip
29876504452aaa505f696642178968e24b8dc8cc4b055071e6f0f3f073088acc *ffmpeg-v25.5.0-win32-x64.zip
+2
View File
@@ -150,10 +150,12 @@ function hygiene(some, linting = true) {
}
const productJsonFilter = filter('product.json', { restore: true });
const snapshotFilter = filter(['**', '!**/*.snap', '!**/*.snap.actual']);
const unicodeFilterStream = filter(unicodeFilter, { restore: true });
const result = input
.pipe(filter((f) => !f.stat.isDirectory()))
.pipe(snapshotFilter)
.pipe(productJsonFilter)
.pipe(process.env['BUILD_SOURCEVERSION'] ? es.through() : productJson)
.pipe(productJsonFilter.restore)
File diff suppressed because one or more lines are too long
+14 -6
View File
@@ -93,9 +93,9 @@ function createCompile(src: string, build: boolean, emitError: boolean, transpil
return pipeline;
}
export function transpileTask(src: string, out: string, swc: boolean): () => NodeJS.ReadWriteStream {
export function transpileTask(src: string, out: string, swc: boolean): task.StreamTask {
return function () {
const task = () => {
const transpile = createCompile(src, false, true, { swc });
const srcPipe = gulp.src(`${src}/**`, { base: `${src}` });
@@ -104,11 +104,14 @@ export function transpileTask(src: string, out: string, swc: boolean): () => Nod
.pipe(transpile())
.pipe(gulp.dest(out));
};
task.taskName = `transpile-${path.basename(src)}`;
return task;
}
export function compileTask(src: string, out: string, build: boolean, options: { disableMangle?: boolean } = {}): () => NodeJS.ReadWriteStream {
export function compileTask(src: string, out: string, build: boolean, options: { disableMangle?: boolean } = {}): task.StreamTask {
return function () {
const task = () => {
if (os.totalmem() < 4_000_000_000) {
throw new Error('compilation requires 4GB of RAM');
@@ -150,11 +153,14 @@ export function compileTask(src: string, out: string, build: boolean, options: {
.pipe(compile())
.pipe(gulp.dest(out));
};
task.taskName = `compile-${path.basename(src)}`;
return task;
}
export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteStream {
export function watchTask(out: string, build: boolean): task.StreamTask {
return function () {
const task = () => {
const compile = createCompile('src', build, false, false);
const src = gulp.src('src/**', { base: 'src' });
@@ -168,6 +174,8 @@ export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteSt
.pipe(util.incremental(compile, src, true))
.pipe(gulp.dest(out));
};
task.taskName = `watch-${path.basename(out)}`;
return task;
}
const REPO_SRC_FOLDER = path.join(__dirname, '../../src');
+4
View File
@@ -58,6 +58,10 @@
"name": "vs/workbench/contrib/commands",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/mappedEdits",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/comments",
"project": "vscode-workbench"
@@ -99,6 +99,7 @@
"--vscode-diffEditor-insertedTextBackground",
"--vscode-diffEditor-insertedTextBorder",
"--vscode-diffEditor-move-border",
"--vscode-diffEditor-moveActive-border",
"--vscode-diffEditor-removedLineBackground",
"--vscode-diffEditor-removedTextBackground",
"--vscode-diffEditor-removedTextBorder",
@@ -545,21 +546,31 @@
"--vscode-statusBar-noFolderBackground",
"--vscode-statusBar-noFolderBorder",
"--vscode-statusBar-noFolderForeground",
"--vscode-statusBar-offlineBackground",
"--vscode-statusBar-offlineForeground",
"--vscode-statusBarItem-offlineBackground",
"--vscode-statusBarItem-offlineForeground",
"--vscode-statusBarItem-offlineHoverBackground",
"--vscode-statusBarItem-offlineHoverForeground",
"--vscode-statusBarItem-activeBackground",
"--vscode-statusBarItem-compactHoverBackground",
"--vscode-statusBarItem-errorBackground",
"--vscode-statusBarItem-errorForeground",
"--vscode-statusBarItem-errorHoverBackground",
"--vscode-statusBarItem-errorHoverForeground",
"--vscode-statusBarItem-focusBorder",
"--vscode-statusBarItem-hoverBackground",
"--vscode-statusBarItem-hoverForeground",
"--vscode-statusBarItem-prominentBackground",
"--vscode-statusBarItem-prominentForeground",
"--vscode-statusBarItem-prominentHoverBackground",
"--vscode-statusBarItem-prominentHoverForeground",
"--vscode-statusBarItem-remoteBackground",
"--vscode-statusBarItem-remoteForeground",
"--vscode-statusBarItem-remoteHoverBackground",
"--vscode-statusBarItem-remoteHoverForeground",
"--vscode-statusBarItem-warningBackground",
"--vscode-statusBarItem-warningForeground",
"--vscode-statusBarItem-warningHoverBackground",
"--vscode-statusBarItem-warningHoverForeground",
"--vscode-symbolIcon-arrayForeground",
"--vscode-symbolIcon-booleanForeground",
"--vscode-symbolIcon-classForeground",
@@ -768,4 +779,4 @@
"--z-index-notebook-sticky-scroll",
"--zoom-factor"
]
}
}
+4 -4
View File
@@ -6,7 +6,7 @@
"git": {
"name": "chromium",
"repositoryUrl": "https://chromium.googlesource.com/chromium/src",
"commitHash": "8bab63006effd27646be3d5c07eb09d3282c9b4d"
"commitHash": "88c0a3606e1a856ad43651130d27bb880e1833d6"
}
},
"licenseDetail": [
@@ -40,7 +40,7 @@
"SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
],
"isOnlyProductionDependency": true,
"version": "114.0.5735.248"
"version": "114.0.5735.289"
},
{
"component": {
@@ -528,12 +528,12 @@
"git": {
"name": "electron",
"repositoryUrl": "https://github.com/electron/electron",
"commitHash": "d219fcc8b61dbeb459e92c90a069076f18a9a12a"
"commitHash": "34be316c404e84cdd967fa0e10fceeb6424eed90"
}
},
"isOnlyProductionDependency": true,
"license": "MIT",
"version": "25.4.0"
"version": "25.5.0"
},
{
"component": {
+2 -2
View File
@@ -983,9 +983,9 @@ dependencies = [
[[package]]
name = "http"
version = "0.2.8"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
dependencies = [
"bytes",
"fnv",
+1 -1
View File
@@ -37,7 +37,7 @@ libc = "0.2.144"
tunnels = { git = "https://github.com/microsoft/dev-tunnels", rev = "2621784a9ad72aa39500372391332a14bad581a3", default-features = false, features = ["connections"] }
keyring = { version = "2.0.3", default-features = false, features = ["linux-secret-service-rt-tokio-crypto-openssl"] }
dialoguer = "0.10.4"
hyper = "0.14.26"
hyper = { version = "0.14.26", features = ["server", "http1", "runtime"] }
indicatif = "0.17.4"
tempfile = "3.5.0"
clap_lex = "0.5.0"
+5 -1
View File
@@ -8,7 +8,7 @@ use std::process::Command;
use clap::Parser;
use cli::{
commands::{args, tunnels, update, version, CommandContext},
commands::{args, serve_web, tunnels, update, version, CommandContext},
constants::get_default_user_agent,
desktop, log,
state::LauncherPaths,
@@ -99,6 +99,10 @@ async fn main() -> Result<(), std::convert::Infallible> {
tunnels::command_shell(context!(), cs_args).await
}
Some(args::Commands::ServeWeb(sw_args)) => {
serve_web::serve_web(context!(), sw_args).await
}
Some(args::Commands::Tunnel(tunnel_args)) => match tunnel_args.subcommand {
Some(args::TunnelSubcommand::Prune) => tunnels::prune(context!()).await,
Some(args::TunnelSubcommand::Unregister) => tunnels::unregister(context!()).await,
+1
View File
@@ -9,4 +9,5 @@ pub mod args;
pub mod tunnels;
pub mod update;
pub mod version;
pub mod serve_web;
pub use context::CommandContext;
+31
View File
@@ -172,11 +172,42 @@ pub enum Commands {
/// Changes the version of the editor you're using.
Version(VersionArgs),
/// Runs a local web version of VS Code.
ServeWeb(ServeWebArgs),
/// Runs the control server on process stdin/stdout
#[clap(hide = true)]
CommandShell(CommandShellArgs),
}
#[derive(Args, Debug, Clone)]
pub struct ServeWebArgs {
/// Host to listen on, defaults to 'localhost'
#[clap(long)]
pub host: Option<String>,
/// Port to listen on. If 0 is passed a random free port is picked.
#[clap(long, default_value_t = 8000)]
pub port: u16,
/// A secret that must be included with all requests.
#[clap(long)]
pub connection_token: Option<String>,
/// Run without a connection token. Only use this if the connection is secured by other means.
#[clap(long)]
pub without_connection_token: bool,
/// If set, the user accepts the server license terms and the server will be started without a user prompt.
#[clap(long)]
pub accept_server_license_terms: bool,
/// Specifies the directory that server data is kept in.
#[clap(long)]
pub server_data_dir: Option<String>,
/// Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.
#[clap(long)]
pub user_data_dir: Option<String>,
/// Set the root path for extensions.
#[clap(long)]
pub extensions_dir: Option<String>,
}
#[derive(Args, Debug, Clone)]
pub struct CommandShellArgs {
/// Listen on a socket instead of stdin/stdout.
+617
View File
@@ -0,0 +1,617 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use std::collections::HashMap;
use std::convert::Infallible;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::pin;
use tokio::process::Command;
use crate::async_pipe::{get_socket_name, get_socket_rw_stream, AsyncPipe};
use crate::constants::VSCODE_CLI_QUALITY;
use crate::download_cache::DownloadCache;
use crate::log;
use crate::options::Quality;
use crate::update_service::{
unzip_downloaded_release, Platform, Release, TargetKind, UpdateService,
};
use crate::util::errors::AnyError;
use crate::util::http::{self, ReqwestSimpleHttp};
use crate::util::io::SilentCopyProgress;
use crate::util::sync::{new_barrier, Barrier, BarrierOpener};
use crate::{
tunnels::legal,
util::{errors::CodeError, prereqs::PreReqChecker},
};
use super::{args::ServeWebArgs, CommandContext};
/// Length of a commit hash, for validation
const COMMIT_HASH_LEN: usize = 40;
/// Number of seconds where, if there's no connections to a VS Code server,
/// the server is shut down.
const SERVER_IDLE_TIMEOUT_SECS: u64 = 60 * 60;
/// Number of seconds in which the server times out when there is a connection
/// (should be large enough to basically never happen)
const SERVER_ACTIVE_TIMEOUT_SECS: u64 = SERVER_IDLE_TIMEOUT_SECS * 24 * 30 * 12;
/// How long to cache the "latest" version we get from the update service.
const RELEASE_CACHE_SECS: u64 = 60 * 60;
/// Implements the vscode "server of servers". Clients who go to the URI get
/// served the latest version of the VS Code server whenever they load the
/// page. The VS Code server prefixes all assets and connections it loads with
/// its version string, so existing clients can continue to get served even
/// while new clients get new VS Code Server versions.
pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result<i32, AnyError> {
legal::require_consent(&ctx.paths, args.accept_server_license_terms)?;
let mut addr: SocketAddr = match &args.host {
Some(h) => h.parse().map_err(CodeError::InvalidHostAddress)?,
None => SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
};
addr.set_port(args.port);
let platform: crate::update_service::Platform = PreReqChecker::new().verify().await?;
if !args.without_connection_token {
// Ensure there's a defined connection token, since if multiple server versions
// are excuted, they will need to have a single shared token.
let connection_token = args
.connection_token
.clone()
.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
ctx.log.result(format!(
"Web UI available at http://{}?tkn={}",
addr, connection_token,
));
args.connection_token = Some(connection_token);
} else {
ctx.log
.result(format!("Web UI available at http://{}", addr));
args.connection_token = None;
}
let cm = ConnectionManager::new(&ctx, platform, args);
let make_svc = make_service_fn(move |_conn| {
let cm = cm.clone();
let log = ctx.log.clone();
let service = service_fn(move |req| handle(cm.clone(), log.clone(), req));
async move { Ok::<_, Infallible>(service) }
});
let server = Server::bind(&addr).serve(make_svc);
server.await.map_err(CodeError::CouldNotListenOnInterface)?;
Ok(0)
}
/// Handler function for an inbound request
async fn handle(
cm: Arc<ConnectionManager>,
log: log::Logger,
req: Request<Body>,
) -> Result<Response<Body>, Infallible> {
let release = if let Some((r, _)) = get_release_from_path(req.uri().path(), cm.platform) {
r
} else {
match cm.get_latest_release().await {
Ok(r) => r,
Err(e) => {
error!(log, "error getting latest version: {}", e);
return Ok(response::code_err(e));
}
}
};
Ok(match cm.get_connection(release).await {
Ok(rw) => {
if req.headers().contains_key(hyper::header::UPGRADE) {
forward_ws_req_to_server(cm.log.clone(), rw, req).await
} else {
forward_http_req_to_server(rw, req).await
}
}
Err(CodeError::ServerNotYetDownloaded) => response::wait_for_download(),
Err(e) => response::code_err(e),
})
}
/// Gets the release info from the VS Code path prefix, which is in the
/// format `/<quality>-<commit>/...`
fn get_release_from_path(path: &str, platform: Platform) -> Option<(Release, String)> {
if !path.starts_with('/') {
return None; // paths must start with '/'
}
let path = &path[1..];
let i = path.find('/').unwrap_or(path.len());
let quality_commit_sep = path.get(..i).and_then(|p| p.find('-'))?;
let (quality_commit, remaining) = path.split_at(i);
let (quality, commit) = quality_commit.split_at(quality_commit_sep);
if !is_commit_hash(commit) {
return None;
}
Some((
Release {
// remember to trim off the leading '/' which is now part of th quality
quality: Quality::try_from(quality).ok()?,
commit: commit.to_string(),
platform,
target: TargetKind::Web,
name: "".to_string(),
},
remaining.to_string(),
))
}
/// Proxies the standard HTTP request to the async pipe, returning the piped response
async fn forward_http_req_to_server(
(rw, handle): (AsyncPipe, ConnectionHandle),
req: Request<Body>,
) -> Response<Body> {
let (mut request_sender, connection) =
match hyper::client::conn::Builder::new().handshake(rw).await {
Ok(r) => r,
Err(e) => return response::connection_err(e),
};
tokio::spawn(connection);
let res = request_sender
.send_request(req)
.await
.unwrap_or_else(response::connection_err);
// technically, we should buffer the body into memory since it may not be
// read at this point, but because the keepalive time is very large
// there's not going to be responses that take hours to send and x
// cause us to kill the server before the response is sent
drop(handle);
res
}
/// Proxies the websocket request to the async pipe
async fn forward_ws_req_to_server(
log: log::Logger,
(rw, handle): (AsyncPipe, ConnectionHandle),
mut req: Request<Body>,
) -> Response<Body> {
// splicing of client and servers inspired by https://github.com/hyperium/hyper/blob/fece9f7f50431cf9533cfe7106b53a77b48db699/examples/upgrades.rs
let (mut request_sender, connection) =
match hyper::client::conn::Builder::new().handshake(rw).await {
Ok(r) => r,
Err(e) => return response::connection_err(e),
};
tokio::spawn(connection);
let mut proxied_req = Request::builder().uri(req.uri());
for (k, v) in req.headers() {
proxied_req = proxied_req.header(k, v);
}
let mut res = request_sender
.send_request(proxied_req.body(Body::empty()).unwrap())
.await
.unwrap_or_else(response::connection_err);
let mut proxied_res = Response::new(Body::empty());
*proxied_res.status_mut() = res.status();
for (k, v) in res.headers() {
proxied_res.headers_mut().insert(k, v.clone());
}
// only start upgrade at this point in case the server decides to deny socket
if res.status() == hyper::StatusCode::SWITCHING_PROTOCOLS {
tokio::spawn(async move {
let (s_req, s_res) =
tokio::join!(hyper::upgrade::on(&mut req), hyper::upgrade::on(&mut res));
match (s_req, s_res) {
(Err(e1), Err(e2)) => debug!(
log,
"client ({}) and server ({}) websocket upgrade failed", e1, e2
),
(Err(e1), _) => debug!(log, "client ({}) websocket upgrade failed", e1),
(_, Err(e2)) => debug!(log, "server ({}) websocket upgrade failed", e2),
(Ok(mut s_req), Ok(mut s_res)) => {
trace!(log, "websocket upgrade succeeded");
let r = tokio::io::copy_bidirectional(&mut s_req, &mut s_res).await;
trace!(log, "websocket closed (error: {:?})", r.err());
}
}
drop(handle);
});
}
proxied_res
}
/// Returns whether the string looks like a commit hash.
fn is_commit_hash(s: &str) -> bool {
s.len() == COMMIT_HASH_LEN && s.chars().all(|c| c.is_ascii_hexdigit())
}
/// Module holding original responses the CLI's server makes.
mod response {
use const_format::concatcp;
use crate::constants::QUALITYLESS_SERVER_NAME;
use super::*;
pub fn connection_err(err: hyper::Error) -> Response<Body> {
Response::builder()
.status(503)
.body(Body::from(format!("Error connecting to server: {:?}", err)))
.unwrap()
}
pub fn code_err(err: CodeError) -> Response<Body> {
Response::builder()
.status(500)
.body(Body::from(format!("Error serving request: {}", err)))
.unwrap()
}
pub fn wait_for_download() -> Response<Body> {
Response::builder()
.status(202)
.header("Content-Type", "text/html") // todo: get latest
.body(Body::from(concatcp!("The latest version of the ", QUALITYLESS_SERVER_NAME, " is downloading, please wait a moment...<script>setTimeout(()=>location.reload(),1500)</script>", )))
.unwrap()
}
}
/// Handle returned when getting a stream to the server, used to refcount
/// connections to a server so it can be disposed when there are no more clients.
struct ConnectionHandle {
client_counter: Arc<tokio::sync::watch::Sender<usize>>,
}
impl ConnectionHandle {
pub fn new(client_counter: Arc<tokio::sync::watch::Sender<usize>>) -> Self {
client_counter.send_modify(|v| {
*v += 1;
});
Self { client_counter }
}
}
impl Drop for ConnectionHandle {
fn drop(&mut self) {
self.client_counter.send_modify(|v| {
*v -= 1;
});
}
}
type StartData = (PathBuf, Arc<tokio::sync::watch::Sender<usize>>);
/// State stored in the ConnectionManager for each server version.
struct VersionState {
downloaded: bool,
socket_path: Barrier<Result<StartData, String>>,
}
type ConnectionStateMap = Arc<Mutex<HashMap<(Quality, String), VersionState>>>;
/// Manages the connections to running web UI instances. Multiple web servers
/// can run concurrently, with routing based on the URL path.
struct ConnectionManager {
pub platform: Platform,
pub log: log::Logger,
args: ServeWebArgs,
/// Cache where servers are stored
cache: DownloadCache,
/// Mapping of (Quality, Commit) to the state each server is in
state: ConnectionStateMap,
/// Update service instance
update_service: UpdateService,
/// Cache of the latest released version, storing the time we checked as well
latest_version: tokio::sync::Mutex<Option<(Instant, Release)>>,
}
fn key_for_release(release: &Release) -> (Quality, String) {
(release.quality, release.commit.clone())
}
impl ConnectionManager {
pub fn new(ctx: &CommandContext, platform: Platform, args: ServeWebArgs) -> Arc<Self> {
Arc::new(Self {
platform,
args,
log: ctx.log.clone(),
cache: DownloadCache::new(ctx.paths.web_server_storage()),
update_service: UpdateService::new(
ctx.log.clone(),
Arc::new(ReqwestSimpleHttp::with_client(ctx.http.clone())),
),
state: ConnectionStateMap::default(),
latest_version: tokio::sync::Mutex::default(),
})
}
/// Gets a connection to a server version
pub async fn get_connection(
&self,
release: Release,
) -> Result<(AsyncPipe, ConnectionHandle), CodeError> {
// todo@connor4312: there is likely some performance benefit to
// implementing a 'keepalive' for these connections.
let (path, counter) = self.get_version_data(release).await?;
let handle = ConnectionHandle::new(counter);
let rw = get_socket_rw_stream(&path).await?;
Ok((rw, handle))
}
/// Gets the latest release for the CLI quality, caching its result for some
/// time to allow for fast loads.
pub async fn get_latest_release(&self) -> Result<Release, CodeError> {
let mut latest = self.latest_version.lock().await;
let now = Instant::now();
if let Some((checked_at, release)) = &*latest {
if checked_at.elapsed() < Duration::from_secs(RELEASE_CACHE_SECS) {
return Ok(release.clone());
}
}
let quality = VSCODE_CLI_QUALITY
.ok_or_else(|| CodeError::UpdatesNotConfigured("no configured quality"))
.and_then(|q| {
Quality::try_from(q).map_err(|_| CodeError::UpdatesNotConfigured("unknown quality"))
})?;
let release = self
.update_service
.get_latest_commit(self.platform, TargetKind::Web, quality)
.await
.map_err(|e| CodeError::UpdateCheckFailed(e.to_string()));
// If the update service is unavailable and we have stale data, use that
if let (Err(e), Some((_, previous))) = (&release, &*latest) {
warning!(self.log, "error getting latest release, using stale: {}", e);
return Ok(previous.clone());
}
let release = release?;
debug!(self.log, "refreshed latest release: {}", release);
*latest = Some((now, release.clone()));
Ok(release)
}
/// Gets the StartData for the a version of the VS Code server, triggering
/// download/start if necessary. It returns `CodeError::ServerNotYetDownloaded`
/// while the server is downloading, which is used to have a refresh loop on the page.
async fn get_version_data(&self, release: Release) -> Result<StartData, CodeError> {
self.get_version_data_inner(release)?
.wait()
.await
.unwrap()
.map_err(CodeError::ServerDownloadError)
}
fn get_version_data_inner(
&self,
release: Release,
) -> Result<Barrier<Result<StartData, String>>, CodeError> {
let mut state = self.state.lock().unwrap();
let key = key_for_release(&release);
if let Some(s) = state.get_mut(&key) {
if !s.downloaded {
if s.socket_path.is_open() {
s.downloaded = true;
} else {
return Err(CodeError::ServerNotYetDownloaded);
}
}
return Ok(s.socket_path.clone());
}
let (socket_path, opener) = new_barrier();
let state_map_dup = self.state.clone();
let args = StartArgs {
args: self.args.clone(),
log: self.log.clone(),
opener,
release,
};
if let Some(p) = self.cache.exists(&args.release.commit) {
state.insert(
key.clone(),
VersionState {
socket_path: socket_path.clone(),
downloaded: true,
},
);
tokio::spawn(async move {
Self::start_version(args, p).await;
state_map_dup.lock().unwrap().remove(&key);
});
Ok(socket_path)
} else {
state.insert(
key.clone(),
VersionState {
socket_path,
downloaded: false,
},
);
let update_service = self.update_service.clone();
let cache = self.cache.clone();
tokio::spawn(async move {
Self::download_version(args, update_service.clone(), cache.clone()).await;
state_map_dup.lock().unwrap().remove(&key);
});
Err(CodeError::ServerNotYetDownloaded)
}
}
/// Downloads a server version into the cache and starts it.
async fn download_version(
args: StartArgs,
update_service: UpdateService,
cache: DownloadCache,
) {
let release_for_fut = args.release.clone();
let log_for_fut = args.log.clone();
let dir_fut = cache.create(&args.release.commit, |target_dir| async move {
info!(log_for_fut, "Downloading server {}", release_for_fut.commit);
let tmpdir = tempfile::tempdir().unwrap();
let response = update_service.get_download_stream(&release_for_fut).await?;
let name = response.url_path_basename().unwrap();
let archive_path = tmpdir.path().join(name);
http::download_into_file(
&archive_path,
log_for_fut.get_download_logger("Downloading server:"),
response,
)
.await?;
unzip_downloaded_release(&archive_path, &target_dir, SilentCopyProgress())?;
Ok(())
});
match dir_fut.await {
Err(e) => args.opener.open(Err(e.to_string())),
Ok(dir) => Self::start_version(args, dir).await,
}
}
/// Starts a downloaded server that can be found in the given `path`.
async fn start_version(args: StartArgs, path: PathBuf) {
info!(args.log, "Starting server {}", args.release.commit);
let executable = path
.join("bin")
.join(args.release.quality.server_entrypoint());
let socket_path = get_socket_name();
#[cfg(not(windows))]
let mut cmd = Command::new(&executable);
#[cfg(windows)]
let mut cmd = {
let mut cmd = Command::new("cmd");
cmd.arg("/Q");
cmd.arg("/C");
cmd.arg(&executable);
cmd
};
cmd.stdin(std::process::Stdio::null());
cmd.stderr(std::process::Stdio::piped());
cmd.stdout(std::process::Stdio::piped());
cmd.arg("--socket-path");
cmd.arg(&socket_path);
// License agreement already checked by the `server_web` function.
cmd.args(["--accept-server-license-terms"]);
if let Some(a) = &args.args.server_data_dir {
cmd.arg("--server-data-dir");
cmd.arg(a);
}
if let Some(a) = &args.args.user_data_dir {
cmd.arg("--user-data-dir");
cmd.arg(a);
}
if let Some(a) = &args.args.extensions_dir {
cmd.arg("--extensions-dir");
cmd.arg(a);
}
if args.args.without_connection_token {
cmd.arg("--without-connection-token");
}
if let Some(ct) = &args.args.connection_token {
cmd.arg("--connection-token");
cmd.arg(ct);
}
// removed, otherwise the workbench will not be usable when running the CLI from sources.
cmd.env_remove("VSCODE_DEV");
let mut child = match cmd.spawn() {
Ok(c) => c,
Err(e) => {
args.opener.open(Err(e.to_string()));
return;
}
};
let (mut stdout, mut stderr) = (
BufReader::new(child.stdout.take().unwrap()).lines(),
BufReader::new(child.stderr.take().unwrap()).lines(),
);
// wrapped option to prove that we only use this once in the loop
let (counter_tx, mut counter_rx) = tokio::sync::watch::channel(0);
let mut opener = Some((args.opener, socket_path, Arc::new(counter_tx)));
let commit_prefix = &args.release.commit[..7];
let kill_timer = tokio::time::sleep(Duration::from_secs(SERVER_IDLE_TIMEOUT_SECS));
pin!(kill_timer);
loop {
tokio::select! {
Ok(Some(l)) = stdout.next_line() => {
info!(args.log, "[{} stdout]: {}", commit_prefix, l);
if l.contains("Server bound to") {
if let Some((opener, path, counter_tx)) = opener.take() {
opener.open(Ok((path, counter_tx)));
}
}
}
Ok(Some(l)) = stderr.next_line() => {
info!(args.log, "[{} stderr]: {}", commit_prefix, l);
},
n = counter_rx.changed() => {
kill_timer.as_mut().reset(match n {
// err means that the record was dropped
Err(_) => tokio::time::Instant::now(),
Ok(_) => {
if *counter_rx.borrow() == 0 {
tokio::time::Instant::now() + Duration::from_secs(SERVER_IDLE_TIMEOUT_SECS)
} else {
tokio::time::Instant::now() + Duration::from_secs(SERVER_ACTIVE_TIMEOUT_SECS)
}
}
});
}
_ = &mut kill_timer => {
info!(args.log, "[{} process]: idle timeout reached, ending", commit_prefix);
let _ = child.kill().await;
break;
}
e = child.wait() => {
info!(args.log, "[{} process]: exited: {:?}", commit_prefix, e);
break;
}
}
}
}
}
struct StartArgs {
log: log::Logger,
args: ServeWebArgs,
release: Release,
opener: BarrierOpener<Result<StartData, String>>,
}
+7 -5
View File
@@ -11,7 +11,7 @@ use crate::{
options::Quality,
update_service::{unzip_downloaded_release, Platform, Release, TargetKind, UpdateService},
util::{
errors::{wrap, AnyError, CorruptDownload, UpdatesNotConfigured},
errors::{wrap, AnyError, CodeError, CorruptDownload},
http,
io::{ReportCopyProgress, SilentCopyProgress},
},
@@ -27,14 +27,16 @@ pub struct SelfUpdate<'a> {
impl<'a> SelfUpdate<'a> {
pub fn new(update_service: &'a UpdateService) -> Result<Self, AnyError> {
let commit = VSCODE_CLI_COMMIT
.ok_or_else(|| UpdatesNotConfigured("unknown build commit".to_string()))?;
.ok_or_else(|| CodeError::UpdatesNotConfigured("unknown build commit"))?;
let quality = VSCODE_CLI_QUALITY
.ok_or_else(|| UpdatesNotConfigured("no configured quality".to_string()))
.and_then(|q| Quality::try_from(q).map_err(UpdatesNotConfigured))?;
.ok_or_else(|| CodeError::UpdatesNotConfigured("no configured quality"))
.and_then(|q| {
Quality::try_from(q).map_err(|_| CodeError::UpdatesNotConfigured("unknown quality"))
})?;
let platform = Platform::env_default().ok_or_else(|| {
UpdatesNotConfigured("Unknown platform, please report this error".to_string())
CodeError::UpdatesNotConfigured("Unknown platform, please report this error")
})?;
Ok(Self {
+6
View File
@@ -212,4 +212,10 @@ impl LauncherPaths {
)
})
}
/// Suggested path for web server storage
pub fn web_server_storage(&self) -> PathBuf {
self.root.join("serve-web")
}
}
+5 -14
View File
@@ -2,9 +2,9 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use crate::constants::{IS_INTERACTIVE_CLI, PRODUCT_NAME_LONG};
use crate::constants::IS_INTERACTIVE_CLI;
use crate::state::{LauncherPaths, PersistedState};
use crate::util::errors::{AnyError, MissingLegalConsent};
use crate::util::errors::{AnyError, CodeError};
use crate::util::input::prompt_yn;
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
@@ -46,23 +46,14 @@ pub fn require_consent(
if accept_server_license_terms {
load.consented = Some(true);
} else if !*IS_INTERACTIVE_CLI {
return Err(MissingLegalConsent(
"Run this command again with --accept-server-license-terms to indicate your agreement."
.to_string(),
)
.into());
return Err(CodeError::NeedsInteractiveLegalConsent.into());
} else {
match prompt_yn(prompt) {
Ok(true) => {
load.consented = Some(true);
}
Ok(false) => {
return Err(AnyError::from(MissingLegalConsent(format!(
"Sorry you cannot use {} CLI without accepting the terms.",
PRODUCT_NAME_LONG
))))
}
Err(e) => return Err(AnyError::from(MissingLegalConsent(e.to_string()))),
Ok(false) => return Err(CodeError::DeniedLegalConset.into()),
Err(_) => return Err(CodeError::NeedsInteractiveLegalConsent.into()),
}
}
+9 -7
View File
@@ -11,7 +11,7 @@ use crate::{
constants::VSCODE_CLI_UPDATE_ENDPOINT,
debug, log, options, spanf,
util::{
errors::{AnyError, CodeError, UpdatesNotConfigured, WrappedError},
errors::{AnyError, CodeError, WrappedError},
http::{BoxedHttp, SimpleResponse},
io::ReportCopyProgress,
tar, zipper,
@@ -19,6 +19,7 @@ use crate::{
};
/// Implementation of the VS Code Update service for use in the CLI.
#[derive(Clone)]
pub struct UpdateService {
client: BoxedHttp,
log: log::Logger,
@@ -54,6 +55,10 @@ fn quality_download_segment(quality: options::Quality) -> &'static str {
}
}
fn get_update_endpoint() -> Result<&'static str, CodeError> {
VSCODE_CLI_UPDATE_ENDPOINT.ok_or_else(|| CodeError::UpdatesNotConfigured("no service url"))
}
impl UpdateService {
pub fn new(log: log::Logger, http: BoxedHttp) -> Self {
UpdateService { client: http, log }
@@ -66,8 +71,7 @@ impl UpdateService {
quality: options::Quality,
version: &str,
) -> Result<Release, AnyError> {
let update_endpoint =
VSCODE_CLI_UPDATE_ENDPOINT.ok_or_else(UpdatesNotConfigured::no_url)?;
let update_endpoint = get_update_endpoint()?;
let download_segment = target
.download_segment(platform)
.ok_or_else(|| CodeError::UnsupportedPlatform(platform.to_string()))?;
@@ -108,8 +112,7 @@ impl UpdateService {
target: TargetKind,
quality: options::Quality,
) -> Result<Release, AnyError> {
let update_endpoint =
VSCODE_CLI_UPDATE_ENDPOINT.ok_or_else(UpdatesNotConfigured::no_url)?;
let update_endpoint = get_update_endpoint()?;
let download_segment = target
.download_segment(platform)
.ok_or_else(|| CodeError::UnsupportedPlatform(platform.to_string()))?;
@@ -144,8 +147,7 @@ impl UpdateService {
/// Gets the download stream for the release.
pub async fn get_download_stream(&self, release: &Release) -> Result<SimpleResponse, AnyError> {
let update_endpoint =
VSCODE_CLI_UPDATE_ENDPOINT.ok_or_else(UpdatesNotConfigured::no_url)?;
let update_endpoint = get_update_endpoint()?;
let download_segment = release
.target
.download_segment(release.platform)
+19 -26
View File
@@ -108,16 +108,6 @@ impl StatusError {
}
}
// When the user has not consented to the licensing terms in using the Launcher
#[derive(Debug)]
pub struct MissingLegalConsent(pub String);
impl std::fmt::Display for MissingLegalConsent {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
// When the provided connection token doesn't match the one used to set up the original VS Code Server
// This is most likely due to a new user joining.
#[derive(Debug)]
@@ -313,20 +303,6 @@ impl std::fmt::Display for ServerHasClosed {
}
}
#[derive(Debug)]
pub struct UpdatesNotConfigured(pub String);
impl UpdatesNotConfigured {
pub fn no_url() -> Self {
UpdatesNotConfigured("no service url".to_owned())
}
}
impl std::fmt::Display for UpdatesNotConfigured {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Update service is not configured: {}", self.0)
}
}
#[derive(Debug)]
pub struct ServiceAlreadyRegistered();
@@ -517,10 +493,28 @@ pub enum CodeError {
KeyringTimeout,
#[error("no host is connected to the tunnel relay")]
NoTunnelEndpoint,
#[error("could not parse `host`: {0}")]
InvalidHostAddress(std::net::AddrParseError),
#[error("could not start server on the given host/port: {0}")]
CouldNotListenOnInterface(hyper::Error),
#[error(
"Run this command again with --accept-server-license-terms to indicate your agreement."
)]
NeedsInteractiveLegalConsent,
#[error("Sorry, you cannot use this CLI without accepting the terms.")]
DeniedLegalConset,
#[error("The server is not yet downloaded, try again shortly.")]
ServerNotYetDownloaded,
#[error("An error was encountered downloading the server, please retry: {0}")]
ServerDownloadError(String),
#[error("Updates are are not available: {0}")]
UpdatesNotConfigured(&'static str),
// todo: can be specialized when update service is moved to CodeErrors
#[error("Could not check for update: {0}")]
UpdateCheckFailed(String),
}
makeAnyError!(
MissingLegalConsent,
MismatchConnectionToken,
DevTunnelError,
StatusError,
@@ -543,7 +537,6 @@ makeAnyError!(
ServerHasClosed,
ServiceAlreadyRegistered,
WindowsNeedsElevation,
UpdatesNotConfigured,
CorruptDownload,
MissingHomeDirectory,
OAuthError,
+1 -1
View File
@@ -63,7 +63,7 @@ impl<T: Clone> BarrierOpener<T> {
/// and is thereafter permanently closed. It can contain a value.
pub fn new_barrier<T>() -> (Barrier<T>, BarrierOpener<T>)
where
T: Copy,
T: Clone,
{
let (closed_tx, closed_rx) = watch::channel(None);
(Barrier(closed_rx), BarrierOpener(Arc::new(closed_tx)))
@@ -24,7 +24,7 @@
},
{
"type": "string",
"pattern": "^([a-z0-9-]+):(\\d{1,5})$"
"pattern": "^([a-z0-9_-]+):(\\d{1,5})$"
}
]
}
+2 -2
View File
@@ -481,7 +481,7 @@ export class Git {
const result = await this.exec(pathInsidePossibleRepository, ['rev-parse', '--show-toplevel']);
// Keep trailing spaces which are part of the directory name
const repositoryRootPath = path.normalize(result.stdout.trimLeft().replace(/[\r\n]+$/, ''));
const repositoryRootPath = path.normalize(result.stdout.trimStart().replace(/[\r\n]+$/, ''));
if (isWindows) {
// On Git 2.25+ if you call `rev-parse --show-toplevel` on a mapped drive, instead of getting the mapped
@@ -528,7 +528,7 @@ export class Git {
!isDescendant(pathInsidePossibleRepository, repositoryRootPath) &&
this.compareGitVersionTo('2.31.0') !== -1) {
const relativePathResult = await this.exec(pathInsidePossibleRepository, ['rev-parse', '--path-format=relative', '--show-toplevel',]);
return path.resolve(pathInsidePossibleRepository, relativePathResult.stdout.trimLeft().replace(/[\r\n]+$/, ''));
return path.resolve(pathInsidePossibleRepository, relativePathResult.stdout.trimStart().replace(/[\r\n]+$/, ''));
}
return repositoryRootPath;
+3 -3
View File
@@ -1,6 +1,6 @@
{
"editor.insertSpaces": false,
"prettier.semi": true,
"prettier.singleQuote": true,
"prettier.printWidth": 120,
}
"prettier.singleQuote": true,
"prettier.printWidth": 120
}
+12 -2
View File
@@ -56,7 +56,11 @@
{
"command": "ipynb.cleanInvalidImageAttachment",
"title": "%cleanInvalidImageAttachment.title%"
}
},
{
"command": "notebook.cellOutput.copyToClipboard",
"title": "%copyOutputToClipboard.title%"
}
],
"notebooks": [
{
@@ -99,7 +103,13 @@
"command": "ipynb.cleanInvalidImageAttachment",
"when": "false"
}
]
],
"webview/context": [
{
"command": "notebook.cellOutput.copyToClipboard",
"when": "webviewId == 'notebook.output' && webviewSection == 'image'"
}
]
}
},
"scripts": {
+1
View File
@@ -6,6 +6,7 @@
"newUntitledIpynb.shortTitle": "Jupyter Notebook",
"openIpynbInNotebookEditor.title": "Open IPYNB File In Notebook Editor",
"cleanInvalidImageAttachment.title": "Clean Invalid Image Attachment Reference",
"copyOutputToClipboard.title": "Copy Output to Clipboard",
"markdownAttachmentRenderer.displayName": {
"message": "Markdown-It ipynb Cell Attachment renderer",
"comment": [
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext, OutputChannel, window, workspace, l10n } from 'vscode';
import { ExtensionContext, OutputChannel, window, workspace, l10n, env } from 'vscode';
import { startClient, LanguageClientConstructor, SchemaRequestService, languageServerDescription } from '../jsonClient';
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node';
@@ -129,7 +129,10 @@ async function getSchemaRequestService(context: ExtensionContext, log: Log): Pro
const isXHRResponse = (error: any): error is XHRResponse => typeof error?.status === 'number';
const request = async (uri: string, etag?: string): Promise<string> => {
const headers: Headers = { 'Accept-Encoding': 'gzip, deflate' };
const headers: Headers = {
'Accept-Encoding': 'gzip, deflate',
'User-Agent': `${env.appName} (${env.appHost})`
};
if (etag) {
headers['If-None-Match'] = etag;
}
@@ -2,9 +2,9 @@
"displayName": "JSON Language Features",
"description": "Provides rich language support for JSON files.",
"json.schemas.desc": "Associate schemas to JSON files in the current project.",
"json.schemas.url.desc": "A URL to a schema or a relative path to a schema in the current directory",
"json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas. `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.",
"json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.",
"json.schemas.url.desc": "A URL or absolute file path to a schema. Can be a relative path in workspace and workspace folder settings.",
"json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas. `*` and '**' can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.",
"json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' and '**' to match against when resolving JSON files to schemas. When beginning with '!', it defines an exclusion pattern.",
"json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.",
"json.format.enable.desc": "Enable/disable default JSON formatter",
"json.format.keepLines.desc" : "Keep all existing new lines when formatting.",
@@ -106,10 +106,10 @@ sup {
line-height: 0;
}
ul ul,
ul ol,
ol ul,
ol ol {
ul ul:first-child,
ul ol:first-child,
ol ul:first-child,
ol ol:first-child {
margin-bottom: 0;
}
@@ -138,6 +138,10 @@ p {
margin-bottom: 16px;
}
li p {
margin-bottom: 0.7em;
}
ul,
ol {
margin-bottom: 0.7em;
@@ -123,7 +123,8 @@
"commands": [
{
"command": "_markdown.copyImage",
"title": "%markdown.copyImage.title%"
"title": "%markdown.copyImage.title%",
"category": "Markdown"
},
{
"command": "markdown.showPreview",
@@ -244,6 +245,10 @@
}
],
"commandPalette": [
{
"command": "_markdown.copyImage",
"when": "false"
},
{
"command": "markdown.showPreview",
"when": "editorLangId == markdown && !notebookEditorFocused",
@@ -37,6 +37,7 @@ function renderImage(outputInfo: OutputItem, element: HTMLElement): IDisposable
if (alt) {
image.alt = alt;
}
image.setAttribute('data-vscode-context', JSON.stringify({ webviewSection: 'image', outputId: outputInfo.id, 'preventDefaultContextMenuItems': true }));
const display = document.createElement('div');
display.classList.add('display');
display.appendChild(image);
+1 -1
View File
@@ -4,7 +4,7 @@
"license": "MIT",
"description": "Dependencies shared by all extensions",
"dependencies": {
"typescript": "^5.2.0-dev.20230807"
"typescript": "^5.2.1-rc"
},
"scripts": {
"postinstall": "node ./postinstall.mjs"
+2 -1
View File
@@ -19,6 +19,7 @@
"fileSearchProvider",
"findTextInFiles",
"fsChunks",
"mappedEditsProvider",
"notebookCellExecutionState",
"notebookDeprecated",
"notebookLiveShare",
@@ -45,7 +46,7 @@
"timeline",
"tokenInformation",
"treeItemCheckbox",
"treeViewActiveItem",
"treeViewActiveItem",
"treeViewReveal",
"workspaceTrust",
"telemetry",
@@ -0,0 +1,102 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as vscode from 'vscode';
import * as assert from 'assert';
suite('mapped edits provider', () => {
test('mapped edits does not provide edits for unregistered langs', async function () {
const uri = vscode.Uri.file(path.join(vscode.workspace.rootPath || '', './myFile.ts'));
const tsDocFilter = [{ language: 'json' }];
const r1 = vscode.chat.registerMappedEditsProvider(tsDocFilter, {
provideMappedEdits: (_doc: vscode.TextDocument, codeBlocks: string[], context: vscode.MappedEditsContext, _token: vscode.CancellationToken) => {
assert(context.selections.length === 1);
assert(context.related.length === 1);
assert('uri' in context.related[0] && 'range' in context.related[0]);
const edit = new vscode.WorkspaceEdit();
const text = codeBlocks.join('\n//----\n');
edit.replace(uri, context.selections[0], text);
return edit;
}
});
await vscode.workspace.openTextDocument(uri);
const result = await vscode.commands.executeCommand<vscode.ProviderResult<vscode.WorkspaceEdit | null>>(
'vscode.executeMappedEditsProvider',
uri,
[
'// hello',
`function foo() {\n\treturn 1;\n}`,
],
{
selections: [new vscode.Selection(0, 0, 1, 0)],
related: [
{
uri,
range: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(1, 0))
}
]
}
);
r1.dispose();
assert(result === null, 'returned null');
});
test('mapped edits provides a single edit replacing the selection', async function () {
const uri = vscode.Uri.file(path.join(vscode.workspace.rootPath || '', './myFile.ts'));
const tsDocFilter = [{ language: 'typescript' }];
const r1 = vscode.chat.registerMappedEditsProvider(tsDocFilter, {
provideMappedEdits: (_doc: vscode.TextDocument, codeBlocks: string[], context: vscode.MappedEditsContext, _token: vscode.CancellationToken) => {
assert(context.selections.length === 1);
assert(context.related.length === 1);
assert('uri' in context.related[0] && 'range' in context.related[0]);
const edit = new vscode.WorkspaceEdit();
const text = codeBlocks.join('\n//----\n');
edit.replace(uri, context.selections[0], text);
return edit;
}
});
await vscode.workspace.openTextDocument(uri);
const result = await vscode.commands.executeCommand<vscode.ProviderResult<vscode.WorkspaceEdit | null>>(
'vscode.executeMappedEditsProvider',
uri,
[
'// hello',
`function foo() {\n\treturn 1;\n}`,
],
{
selections: [new vscode.Selection(0, 0, 1, 0)],
related: [
{
uri,
range: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(1, 0))
}
]
}
);
r1.dispose();
assert(result, 'non null response');
const edits = result.get(uri);
assert(edits.length === 1);
assert(edits[0].range.start.line === 0);
assert(edits[0].range.start.character === 0);
assert(edits[0].range.end.line === 1);
assert(edits[0].range.end.character === 0);
});
});
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { deepStrictEqual, doesNotThrow, equal, ok, strictEqual, throws } from 'assert';
import { commands, ConfigurationTarget, Disposable, env, EnvironmentVariableCollection, EnvironmentVariableMutator, EnvironmentVariableMutatorOptions, EnvironmentVariableMutatorType, EnvironmentVariableScope, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, Pseudoterminal, Terminal, TerminalDimensions, TerminalExitReason, TerminalOptions, TerminalState, UIKind, Uri, window, workspace } from 'vscode';
import { commands, ConfigurationTarget, Disposable, env, EnvironmentVariableMutator, EnvironmentVariableMutatorOptions, EnvironmentVariableMutatorType, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, GlobalEnvironmentVariableCollection, Pseudoterminal, Terminal, TerminalDimensions, TerminalExitReason, TerminalOptions, TerminalState, UIKind, Uri, window, workspace } from 'vscode';
import { assertNoRpc, poll } from '../utils';
// Disable terminal tests:
@@ -913,10 +913,10 @@ import { assertNoRpc, poll } from '../utils';
test('get and forEach should work (scope)', () => {
// TODO: Remove cast once `envCollectionWorkspace` API is finalized.
const collection = extensionContext.environmentVariableCollection as (EnvironmentVariableCollection & { getScopedEnvironmentVariableCollection(scope: EnvironmentVariableScope): EnvironmentVariableCollection });
const collection = extensionContext.environmentVariableCollection as GlobalEnvironmentVariableCollection;
disposables.push({ dispose: () => collection.clear() });
const scope = { workspaceFolder: { uri: Uri.file('workspace1'), name: 'workspace1', index: 0 } };
const scopedCollection = collection.getScopedEnvironmentVariableCollection(scope);
const scopedCollection = collection.getScoped(scope);
scopedCollection.replace('A', 'scoped~a2~');
scopedCollection.append('B', 'scoped~b2~');
scopedCollection.prepend('C', 'scoped~c2~');
@@ -928,7 +928,7 @@ import { assertNoRpc, poll } from '../utils';
applyAtProcessCreation: true,
applyAtShellIntegration: false
};
const expectedScopedCollection = collection.getScopedEnvironmentVariableCollection(scope);
const expectedScopedCollection = collection.getScoped(scope);
deepStrictEqual(expectedScopedCollection.get('A'), { value: 'scoped~a2~', type: EnvironmentVariableMutatorType.Replace, options: defaultOptions });
deepStrictEqual(expectedScopedCollection.get('B'), { value: 'scoped~b2~', type: EnvironmentVariableMutatorType.Append, options: defaultOptions });
deepStrictEqual(expectedScopedCollection.get('C'), { value: 'scoped~c2~', type: EnvironmentVariableMutatorType.Prepend, options: defaultOptions });
@@ -0,0 +1,3 @@
// 1
// 2
// 3
+4 -4
View File
@@ -228,10 +228,10 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
typescript@^5.2.0-dev.20230807:
version "5.2.0-dev.20230807"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.0-dev.20230807.tgz#652fa8a3fcc46958035d9b911c815299deb9f58e"
integrity sha512-sF8sZl3r/mpAdKAxASaWaoU+mNPF3g8OrZ601HraU2l4WEwAf6nO7sq0Bzl+Y3FV7sWjy+gb6kdM1CtLjVne/g==
typescript@^5.2.1-rc:
version "5.2.1-rc"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.1-rc.tgz#9cf33ff6bc39ba9e1fa59761124f596ecf5e0c07"
integrity sha512-gsOdmedQZEWLrYhNqHuzPmcV+4wX7UujzYqszDC5mVMjcN6Nm7lN2eAtndmjWl24aGdAwJqL2ooywkxpaTx8QQ==
vscode-grammar-updater@^1.1.0:
version "1.1.0"
+13 -13
View File
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.82.0",
"distro": "510a8d740286d6895197995d1edc978ffe23cadf",
"distro": "1591281180fd2cd18935e6847131d2d4213b7b69",
"author": {
"name": "Microsoft Corporation"
},
@@ -65,8 +65,8 @@
"perf": "node scripts/code-perf.js"
},
"dependencies": {
"@microsoft/1ds-core-js": "^4.0.1",
"@microsoft/1ds-post-js": "^4.0.1",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.1.0",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/policy-watcher": "^1.1.4",
@@ -95,14 +95,14 @@
"vscode-oniguruma": "1.7.0",
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "9.0.0",
"xterm": "5.3.0-beta.39",
"xterm-addon-canvas": "0.5.0-beta.9",
"xterm-addon-image": "0.6.0-beta.1",
"xterm-addon-search": "0.13.0-beta.7",
"xterm-addon-serialize": "0.11.0-beta.7",
"xterm-addon-unicode11": "0.5.0",
"xterm-addon-webgl": "0.16.0-beta.13",
"xterm-headless": "5.3.0-beta.39",
"xterm": "5.3.0-beta.61",
"xterm-addon-canvas": "0.5.0-beta.22",
"xterm-addon-image": "0.6.0-beta.14",
"xterm-addon-search": "0.13.0-beta.20",
"xterm-addon-serialize": "0.11.0-beta.20",
"xterm-addon-unicode11": "0.6.0-beta.12",
"xterm-addon-webgl": "0.16.0-beta.30",
"xterm-headless": "5.3.0-beta.61",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},
@@ -150,7 +150,7 @@
"cssnano": "^4.1.11",
"debounce": "^1.0.0",
"deemon": "^1.8.0",
"electron": "25.4.0",
"electron": "25.5.0",
"eslint": "8.36.0",
"eslint-plugin-header": "3.1.1",
"eslint-plugin-jsdoc": "^39.3.2",
@@ -212,7 +212,7 @@
"ts-loader": "^9.4.2",
"ts-node": "^10.9.1",
"tsec": "0.2.7",
"typescript": "^5.2.0-dev.20230807",
"typescript": "^5.3.0-dev.20230816",
"typescript-formatter": "7.1.0",
"underscore": "^1.12.1",
"util": "^0.12.4",
+10 -10
View File
@@ -3,8 +3,8 @@
"version": "0.0.0",
"private": true,
"dependencies": {
"@microsoft/1ds-core-js": "^4.0.1",
"@microsoft/1ds-post-js": "^4.0.1",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "2.1.0",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/proxy-agent": "^0.17.2",
@@ -27,14 +27,14 @@
"vscode-oniguruma": "1.7.0",
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "9.0.0",
"xterm": "5.3.0-beta.39",
"xterm-addon-canvas": "0.5.0-beta.9",
"xterm-addon-image": "0.6.0-beta.1",
"xterm-addon-search": "0.13.0-beta.7",
"xterm-addon-serialize": "0.11.0-beta.7",
"xterm-addon-unicode11": "0.5.0",
"xterm-addon-webgl": "0.16.0-beta.13",
"xterm-headless": "5.3.0-beta.39",
"xterm": "5.3.0-beta.61",
"xterm-addon-canvas": "0.5.0-beta.22",
"xterm-addon-image": "0.6.0-beta.14",
"xterm-addon-search": "0.13.0-beta.20",
"xterm-addon-serialize": "0.11.0-beta.20",
"xterm-addon-unicode11": "0.6.0-beta.12",
"xterm-addon-webgl": "0.16.0-beta.30",
"xterm-headless": "5.3.0-beta.61",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
}
+8 -8
View File
@@ -3,19 +3,19 @@
"version": "0.0.0",
"private": true,
"dependencies": {
"@microsoft/1ds-core-js": "^4.0.1",
"@microsoft/1ds-post-js": "^4.0.1",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@vscode/iconv-lite-umd": "0.7.0",
"@vscode/vscode-languagedetection": "1.0.21",
"jschardet": "3.0.0",
"tas-client-umd": "0.1.8",
"vscode-oniguruma": "1.7.0",
"vscode-textmate": "9.0.0",
"xterm": "5.3.0-beta.39",
"xterm-addon-canvas": "0.5.0-beta.9",
"xterm-addon-image": "0.6.0-beta.1",
"xterm-addon-search": "0.13.0-beta.7",
"xterm-addon-unicode11": "0.5.0",
"xterm-addon-webgl": "0.16.0-beta.13"
"xterm": "5.3.0-beta.61",
"xterm-addon-canvas": "0.5.0-beta.22",
"xterm-addon-image": "0.6.0-beta.14",
"xterm-addon-search": "0.13.0-beta.20",
"xterm-addon-unicode11": "0.6.0-beta.12",
"xterm-addon-webgl": "0.16.0-beta.30"
}
}
+51 -73
View File
@@ -2,63 +2,41 @@
# yarn lockfile v1
"@microsoft/1ds-core-js@4.0.1", "@microsoft/1ds-core-js@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-4.0.1.tgz#6fcf129276c933934d618f7fc3de49613259e3ec"
integrity sha512-BcTKhcRixCqLYEo1xVlQofvvZpgey9qmkzxQd1j9bBZS4iQl9IOmGBzlf/vGSITC7VXtEk2NZOlHQN0llAIfSw==
"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13":
version "3.2.13"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb"
integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg==
dependencies:
"@microsoft/applicationinsights-core-js" "3.0.2"
"@microsoft/applicationinsights-shims" "^3.0.1"
"@microsoft/dynamicproto-js" "^2.0.2"
"@nevware21/ts-async" ">= 0.2.4 < 2.x"
"@nevware21/ts-utils" ">= 0.9.6 < 2.x"
"@microsoft/applicationinsights-core-js" "2.8.15"
"@microsoft/applicationinsights-shims" "^2.0.2"
"@microsoft/dynamicproto-js" "^1.1.7"
"@microsoft/1ds-post-js@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-4.0.1.tgz#8630927d10518067686104baec98557b74317e4e"
integrity sha512-uyYkG9mxzzj/4HgxVJsgcz9abfakW+Rjrsnor/hXS5I5ezE3YUX+54DLvAh/c6mEgoDI7u8wGkGl3HgCtAYbDQ==
"@microsoft/1ds-post-js@^3.2.13":
version "3.2.13"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa"
integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA==
dependencies:
"@microsoft/1ds-core-js" "4.0.1"
"@microsoft/applicationinsights-shims" "^3.0.1"
"@microsoft/dynamicproto-js" "^2.0.2"
"@nevware21/ts-async" ">= 0.2.4 < 2.x"
"@nevware21/ts-utils" ">= 0.9.6 < 2.x"
"@microsoft/1ds-core-js" "3.2.13"
"@microsoft/applicationinsights-shims" "^2.0.2"
"@microsoft/dynamicproto-js" "^1.1.7"
"@microsoft/applicationinsights-core-js@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a"
integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ==
"@microsoft/applicationinsights-core-js@2.8.15":
version "2.8.15"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8"
integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ==
dependencies:
"@microsoft/applicationinsights-shims" "3.0.1"
"@microsoft/dynamicproto-js" "^2.0.2"
"@nevware21/ts-async" ">= 0.2.4 < 2.x"
"@nevware21/ts-utils" ">= 0.9.5 < 2.x"
"@microsoft/applicationinsights-shims" "2.0.2"
"@microsoft/dynamicproto-js" "^1.1.9"
"@microsoft/applicationinsights-shims@3.0.1", "@microsoft/applicationinsights-shims@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f"
integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==
dependencies:
"@nevware21/ts-utils" ">= 0.9.4 < 2.x"
"@microsoft/dynamicproto-js@^2.0.2":
"@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6"
integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==
dependencies:
"@nevware21/ts-utils" ">= 0.9.4 < 2.x"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085"
integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg==
"@nevware21/ts-async@>= 0.2.4 < 2.x":
version "0.2.6"
resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.2.6.tgz#9fad1c4a56b01430b407d454817f68bb6ace92b0"
integrity sha512-NCUqEZSbsy7LVtKlUScd/eTst6djkWauLlzoIPVKCOxalEBdO8lrgNRIm4Xy68JNudNN5faqa2WA12X8m0BVhA==
dependencies:
"@nevware21/ts-utils" ">= 0.9.7 < 2.x"
"@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x", "@nevware21/ts-utils@>= 0.9.6 < 2.x", "@nevware21/ts-utils@>= 0.9.7 < 2.x":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.9.8.tgz#6db23211a5c7539d3b4362e273087effcfc7d2f8"
integrity sha512-kZ8s8hcn9jPVX/M7kSsBYrOGlHjqLahmxrG7QeKTk5paeVwfgKdvVCjj5Acb4UGb/ukU1G34U1Z3eb7bbVanyA==
"@microsoft/dynamicproto-js@^1.1.7", "@microsoft/dynamicproto-js@^1.1.9":
version "1.1.9"
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6"
integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ==
"@vscode/iconv-lite-umd@0.7.0":
version "0.7.0"
@@ -90,32 +68,32 @@ vscode-textmate@9.0.0:
resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c"
integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg==
xterm-addon-canvas@0.5.0-beta.9:
version "0.5.0-beta.9"
resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.9.tgz#d4a9536cf586f78a54527751e03abf6445613886"
integrity sha512-AMyFctHbIWx0aLcACINuZqFFNoz4ndGtIAb4pQguIl9t8r+2otiV11MY4FJmPM0kUzEzoLWzVFJaGxOzykCWuw==
xterm-addon-canvas@0.5.0-beta.22:
version "0.5.0-beta.22"
resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.22.tgz#513f0c2b7cf96073f47627b27e8965c1b1a22431"
integrity sha512-9F6ZI0DMRgffVfHkLkDwl5n8VscvCaV10tWI3skXOX7Y7Aws6OEeglkOPoU3IllofCU792kHKM4pPoToUxTltg==
xterm-addon-image@0.6.0-beta.1:
version "0.6.0-beta.1"
resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.1.tgz#058c171a4866f1cde7608e3b00050476b55ece4c"
integrity sha512-vLxUEo/KAsEaRhMshJ4uxnfc/Si8YfT3kilaK5WuiJflVODzQjytzZrl2iLeZaWKiqQp0NR6AEhuXlYASK675A==
xterm-addon-image@0.6.0-beta.14:
version "0.6.0-beta.14"
resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.14.tgz#75fc3f824123183a4bbb5306e22f8b2c6966b0a6"
integrity sha512-D5Gh5JTKhHaPt1KwQNf6diF37KA4eToJw3XId1wy62tWmSqfq+QflhOGTfd+SnSQYCktU05ETzM+0tncIU62pQ==
xterm-addon-search@0.13.0-beta.7:
version "0.13.0-beta.7"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.7.tgz#2f9bf9d8380c4c40f021c47dc583aa32bea8796e"
integrity sha512-Dnuws0ThaqzxRDo8TjkSb5WlMYycONVDSr/irx2pk2I4vC5RuSpOifmD46ALlryNSDkMasjfCLjHPILHsFDCfw==
xterm-addon-search@0.13.0-beta.20:
version "0.13.0-beta.20"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.20.tgz#8ddd0513e2a70fcefa325722100d2e1bfaf3b9cb"
integrity sha512-wrx6187cJ1UenGL6ZeYv3jFvRPhhENTfbC+Hv1Fnww8LmsKhcj+0+Pm6yInNjX/9hNVsNzdqKyqNeEMoykyoyA==
xterm-addon-unicode11@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7"
integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg==
xterm-addon-unicode11@0.6.0-beta.12:
version "0.6.0-beta.12"
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.12.tgz#ac6df9d635325dc692e4c602e74a2fc27a09405c"
integrity sha512-9wWWf/5nFafYgq0pn9EgAWnXaXGleVxfjNOqavpLRYFv0nw42QbaYyGvnGcxyYHM5Aqx/8rYE/DDVWZBqQZdYA==
xterm-addon-webgl@0.16.0-beta.13:
version "0.16.0-beta.13"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.13.tgz#ffacd22b8d86ca7f9ba903a64bc85654523955fb"
integrity sha512-1avJBoAKl+0dOjRK0pvu2im6gaXyP/6oJcEBQXCTz3V8YlgcG/jom8A6FEQSEDGZ/4dnyDvnLbsg3+aLZIr6gw==
xterm-addon-webgl@0.16.0-beta.30:
version "0.16.0-beta.30"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.30.tgz#820d5c65f868b14ec4177bfb8a294931a53616bf"
integrity sha512-39qPHPFmNENxcHf8/CzGHS6wzKMMegoRkHB1+scqtBhSxFaD8tX5Ye33HZIEdQ9nXe9xtr4FWVp77T+n9hdrew==
xterm@5.3.0-beta.39:
version "5.3.0-beta.39"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.39.tgz#b64da3fd1b374fd535db61294a49c16453dbb77e"
integrity sha512-Z1r0ijTgad5MjFoz8qZ+2Qc3bgjG6kYY/kZI3DTA1pibArHeOMDYk9+IeCb9adF74zvZurjytRfXKJgX6M9hwg==
xterm@5.3.0-beta.61:
version "5.3.0-beta.61"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.61.tgz#a6c27d90a5314da51d80deeb32f3bd77f1e1c8f6"
integrity sha512-rJHpCc48GSpHnu0SSERynQ80D5ikvFVsqhv6JdmeONTrnAFRr134OglJRIpbi2YK8UPbV6F6Dfqm/AQh+9GZzA==
+59 -81
View File
@@ -2,63 +2,41 @@
# yarn lockfile v1
"@microsoft/1ds-core-js@4.0.1", "@microsoft/1ds-core-js@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-4.0.1.tgz#6fcf129276c933934d618f7fc3de49613259e3ec"
integrity sha512-BcTKhcRixCqLYEo1xVlQofvvZpgey9qmkzxQd1j9bBZS4iQl9IOmGBzlf/vGSITC7VXtEk2NZOlHQN0llAIfSw==
"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13":
version "3.2.13"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb"
integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg==
dependencies:
"@microsoft/applicationinsights-core-js" "3.0.2"
"@microsoft/applicationinsights-shims" "^3.0.1"
"@microsoft/dynamicproto-js" "^2.0.2"
"@nevware21/ts-async" ">= 0.2.4 < 2.x"
"@nevware21/ts-utils" ">= 0.9.6 < 2.x"
"@microsoft/applicationinsights-core-js" "2.8.15"
"@microsoft/applicationinsights-shims" "^2.0.2"
"@microsoft/dynamicproto-js" "^1.1.7"
"@microsoft/1ds-post-js@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-4.0.1.tgz#8630927d10518067686104baec98557b74317e4e"
integrity sha512-uyYkG9mxzzj/4HgxVJsgcz9abfakW+Rjrsnor/hXS5I5ezE3YUX+54DLvAh/c6mEgoDI7u8wGkGl3HgCtAYbDQ==
"@microsoft/1ds-post-js@^3.2.13":
version "3.2.13"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa"
integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA==
dependencies:
"@microsoft/1ds-core-js" "4.0.1"
"@microsoft/applicationinsights-shims" "^3.0.1"
"@microsoft/dynamicproto-js" "^2.0.2"
"@nevware21/ts-async" ">= 0.2.4 < 2.x"
"@nevware21/ts-utils" ">= 0.9.6 < 2.x"
"@microsoft/1ds-core-js" "3.2.13"
"@microsoft/applicationinsights-shims" "^2.0.2"
"@microsoft/dynamicproto-js" "^1.1.7"
"@microsoft/applicationinsights-core-js@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a"
integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ==
"@microsoft/applicationinsights-core-js@2.8.15":
version "2.8.15"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8"
integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ==
dependencies:
"@microsoft/applicationinsights-shims" "3.0.1"
"@microsoft/dynamicproto-js" "^2.0.2"
"@nevware21/ts-async" ">= 0.2.4 < 2.x"
"@nevware21/ts-utils" ">= 0.9.5 < 2.x"
"@microsoft/applicationinsights-shims" "2.0.2"
"@microsoft/dynamicproto-js" "^1.1.9"
"@microsoft/applicationinsights-shims@3.0.1", "@microsoft/applicationinsights-shims@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f"
integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==
dependencies:
"@nevware21/ts-utils" ">= 0.9.4 < 2.x"
"@microsoft/dynamicproto-js@^2.0.2":
"@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6"
integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==
dependencies:
"@nevware21/ts-utils" ">= 0.9.4 < 2.x"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085"
integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg==
"@nevware21/ts-async@>= 0.2.4 < 2.x":
version "0.2.6"
resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.2.6.tgz#9fad1c4a56b01430b407d454817f68bb6ace92b0"
integrity sha512-NCUqEZSbsy7LVtKlUScd/eTst6djkWauLlzoIPVKCOxalEBdO8lrgNRIm4Xy68JNudNN5faqa2WA12X8m0BVhA==
dependencies:
"@nevware21/ts-utils" ">= 0.9.7 < 2.x"
"@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x", "@nevware21/ts-utils@>= 0.9.6 < 2.x", "@nevware21/ts-utils@>= 0.9.7 < 2.x":
version "0.9.8"
resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.9.8.tgz#6db23211a5c7539d3b4362e273087effcfc7d2f8"
integrity sha512-kZ8s8hcn9jPVX/M7kSsBYrOGlHjqLahmxrG7QeKTk5paeVwfgKdvVCjj5Acb4UGb/ukU1G34U1Z3eb7bbVanyA==
"@microsoft/dynamicproto-js@^1.1.7", "@microsoft/dynamicproto-js@^1.1.9":
version "1.1.9"
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6"
integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ==
"@parcel/watcher@2.1.0":
version "2.1.0"
@@ -899,45 +877,45 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
xterm-addon-canvas@0.5.0-beta.9:
version "0.5.0-beta.9"
resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.9.tgz#d4a9536cf586f78a54527751e03abf6445613886"
integrity sha512-AMyFctHbIWx0aLcACINuZqFFNoz4ndGtIAb4pQguIl9t8r+2otiV11MY4FJmPM0kUzEzoLWzVFJaGxOzykCWuw==
xterm-addon-canvas@0.5.0-beta.22:
version "0.5.0-beta.22"
resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.22.tgz#513f0c2b7cf96073f47627b27e8965c1b1a22431"
integrity sha512-9F6ZI0DMRgffVfHkLkDwl5n8VscvCaV10tWI3skXOX7Y7Aws6OEeglkOPoU3IllofCU792kHKM4pPoToUxTltg==
xterm-addon-image@0.6.0-beta.1:
version "0.6.0-beta.1"
resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.1.tgz#058c171a4866f1cde7608e3b00050476b55ece4c"
integrity sha512-vLxUEo/KAsEaRhMshJ4uxnfc/Si8YfT3kilaK5WuiJflVODzQjytzZrl2iLeZaWKiqQp0NR6AEhuXlYASK675A==
xterm-addon-image@0.6.0-beta.14:
version "0.6.0-beta.14"
resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.14.tgz#75fc3f824123183a4bbb5306e22f8b2c6966b0a6"
integrity sha512-D5Gh5JTKhHaPt1KwQNf6diF37KA4eToJw3XId1wy62tWmSqfq+QflhOGTfd+SnSQYCktU05ETzM+0tncIU62pQ==
xterm-addon-search@0.13.0-beta.7:
version "0.13.0-beta.7"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.7.tgz#2f9bf9d8380c4c40f021c47dc583aa32bea8796e"
integrity sha512-Dnuws0ThaqzxRDo8TjkSb5WlMYycONVDSr/irx2pk2I4vC5RuSpOifmD46ALlryNSDkMasjfCLjHPILHsFDCfw==
xterm-addon-search@0.13.0-beta.20:
version "0.13.0-beta.20"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.20.tgz#8ddd0513e2a70fcefa325722100d2e1bfaf3b9cb"
integrity sha512-wrx6187cJ1UenGL6ZeYv3jFvRPhhENTfbC+Hv1Fnww8LmsKhcj+0+Pm6yInNjX/9hNVsNzdqKyqNeEMoykyoyA==
xterm-addon-serialize@0.11.0-beta.7:
version "0.11.0-beta.7"
resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.7.tgz#934a48a4490c2ca94a5cfcdd646fb0813b4e56d0"
integrity sha512-TwYsEHq6TflY0vmq3yAI2VKNqzEAZ0gCm3TThDMPrPQiv4LcewQSeGu9RujEqF9RAhujhceaM3GajBxySAWyqQ==
xterm-addon-serialize@0.11.0-beta.20:
version "0.11.0-beta.20"
resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.20.tgz#e879b34d214761403f1081833f9221c6903bf0c3"
integrity sha512-OXnC1SATaz7kEFjFWhyv9MJaXi8yHdPjazpGLNi11h33CRTKtCQiqqPBHU87dztnXmpEX6Jw0/jr3zlyXuAmnw==
xterm-addon-unicode11@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7"
integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg==
xterm-addon-unicode11@0.6.0-beta.12:
version "0.6.0-beta.12"
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.12.tgz#ac6df9d635325dc692e4c602e74a2fc27a09405c"
integrity sha512-9wWWf/5nFafYgq0pn9EgAWnXaXGleVxfjNOqavpLRYFv0nw42QbaYyGvnGcxyYHM5Aqx/8rYE/DDVWZBqQZdYA==
xterm-addon-webgl@0.16.0-beta.13:
version "0.16.0-beta.13"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.13.tgz#ffacd22b8d86ca7f9ba903a64bc85654523955fb"
integrity sha512-1avJBoAKl+0dOjRK0pvu2im6gaXyP/6oJcEBQXCTz3V8YlgcG/jom8A6FEQSEDGZ/4dnyDvnLbsg3+aLZIr6gw==
xterm-addon-webgl@0.16.0-beta.30:
version "0.16.0-beta.30"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.30.tgz#820d5c65f868b14ec4177bfb8a294931a53616bf"
integrity sha512-39qPHPFmNENxcHf8/CzGHS6wzKMMegoRkHB1+scqtBhSxFaD8tX5Ye33HZIEdQ9nXe9xtr4FWVp77T+n9hdrew==
xterm-headless@5.3.0-beta.39:
version "5.3.0-beta.39"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.39.tgz#84a509dbe1dc9b2229b5274ae3430ce968986346"
integrity sha512-Q4vfqAaYoWh7/T4HearlLlKkn9vjN5EugsNyuPd6hq+Ug1FwJIJYj+I9vY67+Rqxr24XGnrQdXfvvEas29KXsw==
xterm-headless@5.3.0-beta.61:
version "5.3.0-beta.61"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.61.tgz#28654550cb572709b99ea3eb8672d4568ae141c9"
integrity sha512-yfkbPLUtKjE4K7DsZ204A1BuOKpu6Usqi6rIYWT4XRMi+LjnkTbBjGr2BSjyJ3Gmtm+cSgBD0SvRN+V3xNxbxA==
xterm@5.3.0-beta.39:
version "5.3.0-beta.39"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.39.tgz#b64da3fd1b374fd535db61294a49c16453dbb77e"
integrity sha512-Z1r0ijTgad5MjFoz8qZ+2Qc3bgjG6kYY/kZI3DTA1pibArHeOMDYk9+IeCb9adF74zvZurjytRfXKJgX6M9hwg==
xterm@5.3.0-beta.61:
version "5.3.0-beta.61"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.61.tgz#a6c27d90a5314da51d80deeb32f3bd77f1e1c8f6"
integrity sha512-rJHpCc48GSpHnu0SSERynQ80D5ikvFVsqhv6JdmeONTrnAFRr134OglJRIpbi2YK8UPbV6F6Dfqm/AQh+9GZzA==
yallist@^4.0.0:
version "4.0.0"
+3 -1
View File
@@ -32,6 +32,8 @@
"vs/platform/files/browser/htmlFileSystemProvider.ts",
"vs/platform/files/browser/webFileSystemAccess.ts",
"vs/platform/telemetry/*",
"vs/platform/assignment/*"
"vs/platform/assignment/*",
"vs/platform/terminal/*",
"vs/platform/externalTerminal/*"
]
}
+1
View File
@@ -705,6 +705,7 @@ class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
this.splitviewSashResetDisposable.dispose();
this.childrenSashResetDisposable.dispose();
this.childrenChangeDisposable.dispose();
this.onDidScrollDisposable.dispose();
this.splitview.dispose();
}
}
+23 -6
View File
@@ -16,7 +16,7 @@ import { timeout } from 'vs/base/common/async';
import { Color } from 'vs/base/common/color';
import { memoize } from 'vs/base/common/decorators';
import { Emitter, Event, EventBufferer } from 'vs/base/common/event';
import { matchesPrefix } from 'vs/base/common/filters';
import { matchesFuzzy2, matchesPrefix } from 'vs/base/common/filters';
import { KeyCode } from 'vs/base/common/keyCodes';
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { clamp } from 'vs/base/common/numbers';
@@ -527,11 +527,27 @@ class TypeNavigationController<T> implements IDisposable {
const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(this.view.element(index));
const labelStr = label && label.toString();
if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) {
this.previouslyFocused = start;
this.list.setFocus([index]);
this.list.reveal(index);
return;
if (this.list.options.typeNavigationEnabled) {
if (typeof labelStr !== 'undefined') {
const prefix = matchesPrefix(word, labelStr);
const fuzzy = matchesFuzzy2(word, labelStr);
// ensures that when fuzzy matching, it doesn't clash with prefix matching (1 input vs 1+ should be prefix and fuzzy respecitvely)
const fuzzyScore = fuzzy ? fuzzy[0].end - fuzzy[0].start : 0;
if (prefix || fuzzyScore > 1) {
this.previouslyFocused = start;
this.list.setFocus([index]);
this.list.reveal(index);
return;
}
}
} else {
if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) {
this.previouslyFocused = start;
this.list.setFocus([index]);
this.list.reveal(index);
return;
}
}
}
}
@@ -978,6 +994,7 @@ export interface IListOptions<T> extends IListOptionsUpdate {
readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider<T>;
readonly keyboardNavigationDelegate?: IKeyboardNavigationDelegate;
readonly keyboardSupport?: boolean;
readonly keyboardNavigationEnabled?: boolean;
readonly multipleSelectionController?: IMultipleSelectionController<T>;
readonly styleController?: (suffix: string) => IStyleController;
readonly accessibilityProvider?: IListAccessibilityProvider<T>;
@@ -565,11 +565,11 @@ export class SplitView<TLayoutContext = undefined> extends Disposable {
this.sashContainer = append(this.el, $('.sash-container'));
this.viewContainer = $('.split-view-container');
this.scrollable = new Scrollable({
this.scrollable = this._register(new Scrollable({
forceIntegerValues: true,
smoothScrollDuration: 125,
scheduleAtNextAnimationFrame
});
}));
this.scrollableElement = this._register(new SmoothScrollableElement(this.viewContainer, {
vertical: this.orientation === Orientation.VERTICAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden,
horizontal: this.orientation === Orientation.HORIZONTAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden
+1 -1
View File
@@ -52,7 +52,7 @@ export class ToolBar extends Disposable {
private _onDidChangeDropdownVisibility = this._register(new EventMultiplexer<boolean>());
readonly onDidChangeDropdownVisibility = this._onDidChangeDropdownVisibility.event;
private disposables = new DisposableStore();
private disposables = this._register(new DisposableStore());
constructor(container: HTMLElement, contextMenuProvider: IContextMenuProvider, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) {
super();
+17
View File
@@ -707,6 +707,8 @@ export function tieBreakComparators<TItem>(...comparators: Comparator<TItem>[]):
*/
export const numberComparator: Comparator<number> = (a, b) => a - b;
export const booleanComparator: Comparator<boolean> = (a, b) => numberComparator(a ? 1 : 0, b ? 1 : 0);
export function reverseOrder<TItem>(comparator: Comparator<TItem>): Comparator<TItem> {
return (a, b) => -comparator(a, b);
}
@@ -754,6 +756,21 @@ export function findMinBy<T>(items: readonly T[], comparator: Comparator<T>): T
return findMaxBy(items, (a, b) => -comparator(a, b));
}
export function findMaxIdxBy<T>(items: readonly T[], comparator: Comparator<T>): number {
if (items.length === 0) {
return -1;
}
let maxIdx = 0;
for (let i = 1; i < items.length; i++) {
const item = items[i];
if (comparator(item, items[maxIdx]) > 0) {
maxIdx = i;
}
}
return maxIdx;
}
export class ArrayQueue<T> {
private firstIdx = 0;
private lastIdx = this.items.length - 1;
+1 -1
View File
@@ -47,7 +47,7 @@ export namespace Iterable {
return false;
}
export function find<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): T | undefined;
export function find<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): R | undefined;
export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined;
export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined {
for (const element of iterable) {
+1 -2
View File
@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import { regExpFlags } from 'vs/base/common/strings';
import { URI, UriComponents } from 'vs/base/common/uri';
import { MarshalledId } from './marshallingIds';
@@ -28,7 +27,7 @@ function replacer(key: string, value: any): any {
return {
$mid: MarshalledId.Regexp,
source: value.source,
flags: regExpFlags(value),
flags: value.flags,
};
}
return value;
+6 -8
View File
@@ -3,8 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedError } from 'vs/base/common/errors';
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
/**
* The payload that flows in readable stream events.
@@ -567,17 +568,16 @@ export interface IStreamListener<T> {
/**
* Helper to listen to all events of a T stream in proper order.
*/
export function listenStream<T>(stream: ReadableStreamEvents<T>, listener: IStreamListener<T>): IDisposable {
let destroyed = false;
export function listenStream<T>(stream: ReadableStreamEvents<T>, listener: IStreamListener<T>, token?: CancellationToken): void {
stream.on('error', error => {
if (!destroyed) {
if (!token?.isCancellationRequested) {
listener.onError(error);
}
});
stream.on('end', () => {
if (!destroyed) {
if (!token?.isCancellationRequested) {
listener.onEnd();
}
});
@@ -586,12 +586,10 @@ export function listenStream<T>(stream: ReadableStreamEvents<T>, listener: IStre
// into flowing mode. As such it is important to
// add this listener last (DO NOT CHANGE!)
stream.on('data', data => {
if (!destroyed) {
if (!token?.isCancellationRequested) {
listener.onData(data);
}
});
return toDisposable(() => destroyed = true);
}
/**
-11
View File
@@ -217,17 +217,6 @@ export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean {
return !!(match && regexp.lastIndex === 0);
}
export function regExpContainsBackreference(regexpValue: string): boolean {
return !!regexpValue.match(/([^\\]|^)(\\\\)*\\\d+/);
}
export function regExpFlags(regexp: RegExp): string {
return (regexp.global ? 'g' : '')
+ (regexp.ignoreCase ? 'i' : '')
+ (regexp.multiline ? 'm' : '')
+ ((regexp as any /* standalone editor compilation */).unicode ? 'u' : '');
}
export function splitLines(str: string): string[] {
return str.split(/\r\n|\r|\n/);
}
+185
View File
@@ -0,0 +1,185 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Lazy } from 'vs/base/common/lazy';
import { FileAccess } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
declare const __readFileInTests: (path: string) => Promise<string>;
declare const __writeFileInTests: (path: string, contents: string) => Promise<void>;
declare const __readDirInTests: (path: string) => Promise<string[]>;
declare const __unlinkInTests: (path: string) => Promise<void>;
declare const __mkdirPInTests: (path: string) => Promise<void>;
// setup on import so assertSnapshot has the current context without explicit passing
let context: Lazy<SnapshotContext> | undefined;
const sanitizeName = (name: string) => name.replace(/[^a-z0-9_-]/gi, '_');
const normalizeCrlf = (str: string) => str.replace(/\r\n/g, '\n');
export interface ISnapshotOptions {
/** Name for snapshot file, rather than an incremented number */
name?: string;
/** Extension name of the snapshot file, defaults to `.snap` */
extension?: string;
}
/**
* This is exported only for tests against the snapshotting itself! Use
* {@link assertSnapshot} as a consumer!
*/
export class SnapshotContext {
private nextIndex = 0;
protected snapshotsDir: URI;
private readonly namePrefix: string;
private readonly usedNames = new Set();
constructor(private readonly test: Mocha.Test | undefined) {
if (!test) {
throw new Error('assertSnapshot can only be used in a test');
}
if (!test.file) {
throw new Error('currentTest.file is not set, please open an issue with the test you\'re trying to run');
}
const src = FileAccess.asFileUri('');
const parts = test.file.split(/[/\\]/g);
this.namePrefix = sanitizeName(test.fullTitle()) + '_';
this.snapshotsDir = URI.joinPath(src, ...[...parts.slice(0, -1), '__snapshots__']);
}
public async assert(value: any, options?: ISnapshotOptions) {
const originalStack = new Error().stack!; // save to make the stack nicer on failure
const nameOrIndex = (options?.name ? sanitizeName(options.name) : this.nextIndex++);
const fileName = this.namePrefix + nameOrIndex + '.' + (options?.extension || 'snap');
this.usedNames.add(fileName);
const fpath = URI.joinPath(this.snapshotsDir, fileName).fsPath;
const actual = formatValue(value);
let expected: string;
try {
expected = await __readFileInTests(fpath);
} catch {
console.info(`Creating new snapshot in: ${fpath}`);
await __mkdirPInTests(this.snapshotsDir.fsPath);
await __writeFileInTests(fpath, actual);
return;
}
if (normalizeCrlf(expected) !== normalizeCrlf(actual)) {
await __writeFileInTests(fpath + '.actual', actual);
const err: any = new Error(`Snapshot #${nameOrIndex} does not match expected output`);
err.expected = expected;
err.actual = actual;
err.snapshotPath = fpath;
err.stack = (err.stack as string)
.split('\n')
// remove all frames from the async stack and keep the original caller's frame
.slice(0, 1)
.concat(originalStack.split('\n').slice(3))
.join('\n');
throw err;
}
}
public async removeOldSnapshots() {
const contents = await __readDirInTests(this.snapshotsDir.fsPath);
const toDelete = contents.filter(f => f.startsWith(this.namePrefix) && !this.usedNames.has(f));
if (toDelete.length) {
console.info(`Deleting ${toDelete.length} old snapshots for ${this.test?.fullTitle()}`);
}
await Promise.all(toDelete.map(f => __unlinkInTests(URI.joinPath(this.snapshotsDir, f).fsPath)));
}
}
const debugDescriptionSymbol = Symbol.for('debug.description');
function formatValue(value: unknown, level = 0, seen: unknown[] = []): string {
switch (typeof value) {
case 'bigint':
case 'boolean':
case 'number':
case 'symbol':
case 'undefined':
return String(value);
case 'string':
return level === 0 ? value : JSON.stringify(value);
case 'function':
return `[Function ${value.name}]`;
case 'object': {
if (value === null) {
return 'null';
}
if (value instanceof RegExp) {
return String(value);
}
if (seen.includes(value)) {
return '[Circular]';
}
if (debugDescriptionSymbol in value && typeof (value as any)[debugDescriptionSymbol] === 'function') {
return (value as any)[debugDescriptionSymbol]();
}
const oi = ' '.repeat(level);
const ci = ' '.repeat(level + 1);
if (Array.isArray(value)) {
const children = value.map(v => formatValue(v, level + 1, [...seen, value]));
const multiline = children.some(c => c.includes('\n')) || children.join(', ').length > 80;
return multiline ? `[\n${ci}${children.join(`,\n${ci}`)}\n${oi}]` : `[ ${children.join(', ')} ]`;
}
let entries;
let prefix = '';
if (value instanceof Map) {
prefix = 'Map ';
entries = [...value.entries()];
} else if (value instanceof Set) {
prefix = 'Set ';
entries = [...value.entries()];
} else {
entries = Object.entries(value);
}
const lines = entries.map(([k, v]) => `${k}: ${formatValue(v, level + 1, [...seen, value])}`);
return prefix + (lines.length > 1
? `{\n${ci}${lines.join(`,\n${ci}`)}\n${oi}}`
: `{ ${lines.join(',\n')} }`);
}
default:
throw new Error(`Unknown type ${value}`);
}
}
setup(function () {
const currentTest = this.currentTest;
context = new Lazy(() => new SnapshotContext(currentTest));
});
teardown(async function () {
if (this.currentTest?.state === 'passed') {
await context?.rawValue?.removeOldSnapshots();
}
context = undefined;
});
/**
* Implements a snapshot testing utility. This is async!
*
* The first time a snapshot test is run, it'll record the value it's called
* with as the expected value. Subsequent runs will fail if the value differs,
* but the snapshot can be regenerated by hand or using the Selfhost Test
* Provider Extension which'll offer to update it.
*
* The snapshot will be associated with the currently running test and stored
* in a `__snapshots__` directory next to the test file, which is expected to
* be the first `.test.js` file in the callstack.
*/
export function assertSnapshot(value: any, options?: ISnapshotOptions): Promise<void> {
if (!context) {
throw new Error('assertSnapshot can only be used in a test');
}
return context.value.assert(value, options);
}
+7 -4
View File
@@ -6,6 +6,7 @@
import * as assert from 'assert';
import { timeout } from 'vs/base/common/async';
import { bufferToReadable, VSBuffer } from 'vs/base/common/buffer';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { consumeReadable, consumeStream, isReadable, isReadableBufferedStream, isReadableStream, listenStream, newWriteableStream, peekReadable, peekStream, prefixedReadable, prefixedStream, Readable, ReadableStream, toReadable, toStream, transform } from 'vs/base/common/stream';
suite('Stream', () => {
@@ -351,14 +352,16 @@ suite('Stream', () => {
assert.strictEqual(end, true);
});
test('listenStream - dispose', () => {
test('listenStream - cancellation', () => {
const stream = newWriteableStream<string>(strings => strings.join());
let error = false;
let end = false;
let data = '';
const disposable = listenStream(stream, {
const cts = new CancellationTokenSource();
listenStream(stream, {
onData: d => {
data = d;
},
@@ -368,9 +371,9 @@ suite('Stream', () => {
onEnd: () => {
end = true;
}
});
}, cts.token);
disposable.dispose();
cts.cancel();
stream.write('Hello');
assert.strictEqual(data, '');
-16
View File
@@ -265,22 +265,6 @@ suite('Strings', () => {
assert(regExpWithFlags.multiline);
});
test('regExpContainsBackreference', () => {
assert(strings.regExpContainsBackreference('foo \\5 bar'));
assert(strings.regExpContainsBackreference('\\2'));
assert(strings.regExpContainsBackreference('(\\d)(\\n)(\\1)'));
assert(strings.regExpContainsBackreference('(A).*?\\1'));
assert(strings.regExpContainsBackreference('\\\\\\1'));
assert(strings.regExpContainsBackreference('foo \\\\\\1'));
assert(!strings.regExpContainsBackreference(''));
assert(!strings.regExpContainsBackreference('\\\\1'));
assert(!strings.regExpContainsBackreference('foo \\\\1'));
assert(!strings.regExpContainsBackreference('(A).*?\\\\1'));
assert(!strings.regExpContainsBackreference('foo \\d1 bar'));
assert(!strings.regExpContainsBackreference('123'));
});
test('getLeadingWhitespace', () => {
assert.strictEqual(strings.getLeadingWhitespace(' foo'), ' ');
assert.strictEqual(strings.getLeadingWhitespace(' foo', 2), '');
@@ -0,0 +1,8 @@
hello_world__0.snap:
{ cool: true }
hello_world__1.snap:
{ nifty: true }
hello_world__fourthTest.snap:
{ customName: 2 }
hello_world__thirdTest.txt:
{ customName: 1 }
@@ -0,0 +1,4 @@
hello_world__0.snap:
{ cool: true }
hello_world__thirdTest.snap:
{ customName: 1 }
@@ -0,0 +1,2 @@
hello_world__0.snap:
{ cool: true }
@@ -0,0 +1,35 @@
[
1,
true,
undefined,
null,
123,
Symbol(heyo),
"hello",
{ hello: "world" },
{ a: [Circular] },
Map {
hello: 1,
goodbye: 2
},
Set {
1: 1,
2: 2,
3: 3
},
[Function helloWorld],
/hello/g,
[
"long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string",
"long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string",
"long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string",
"long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string",
"long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string",
"long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string",
"long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string",
"long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string",
"long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string",
"long stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong stringlong string"
],
Range [1 -> 5]
]
+146
View File
@@ -0,0 +1,146 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { tmpdir } from 'os';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { Promises } from 'vs/base/node/pfs';
import { SnapshotContext, assertSnapshot } from 'vs/base/test/common/snapshot';
import { URI } from 'vs/base/common/uri';
import path = require('path');
import { assertThrowsAsync } from 'vs/base/test/common/utils';
// tests for snapshot are in Node so that we can use native FS operations to
// set up and validate things.
//
// Uses snapshots for testing snapshots. It's snapception!
suite('snapshot', () => {
let testDir: string;
setup(function () {
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'snapshot');
return Promises.mkdir(testDir, { recursive: true });
});
teardown(function () {
return Promises.rm(testDir);
});
const makeContext = (test: Partial<Mocha.Test> | undefined) => {
return new class extends SnapshotContext {
constructor() {
super(test as Mocha.Test);
this.snapshotsDir = URI.file(testDir);
}
};
};
const snapshotFileTree = async () => {
let str = '';
const printDir = async (dir: string, indent: number) => {
const children = await Promises.readdir(dir);
for (const child of children) {
const p = path.join(dir, child);
if ((await Promises.stat(p)).isFile()) {
const content = await Promises.readFile(p, 'utf-8');
str += `${' '.repeat(indent)}${child}:\n`;
for (const line of content.split('\n')) {
str += `${' '.repeat(indent + 2)}${line}\n`;
}
} else {
str += `${' '.repeat(indent)}${child}/\n`;
await printDir(p, indent + 2);
}
}
};
await printDir(testDir, 0);
await assertSnapshot(str);
};
test('creates a snapshot', async () => {
const ctx = makeContext({
file: 'foo/bar',
fullTitle: () => 'hello world!'
});
await ctx.assert({ cool: true });
await snapshotFileTree();
});
test('validates a snapshot', async () => {
const ctx1 = makeContext({
file: 'foo/bar',
fullTitle: () => 'hello world!'
});
await ctx1.assert({ cool: true });
const ctx2 = makeContext({
file: 'foo/bar',
fullTitle: () => 'hello world!'
});
// should pass:
await ctx2.assert({ cool: true });
const ctx3 = makeContext({
file: 'foo/bar',
fullTitle: () => 'hello world!'
});
// should fail:
await assertThrowsAsync(() => ctx3.assert({ cool: false }));
});
test('cleans up old snapshots', async () => {
const ctx1 = makeContext({
file: 'foo/bar',
fullTitle: () => 'hello world!'
});
await ctx1.assert({ cool: true });
await ctx1.assert({ nifty: true });
await ctx1.assert({ customName: 1 }, { name: 'thirdTest', extension: 'txt' });
await ctx1.assert({ customName: 2 }, { name: 'fourthTest' });
await snapshotFileTree();
const ctx2 = makeContext({
file: 'foo/bar',
fullTitle: () => 'hello world!'
});
await ctx2.assert({ cool: true });
await ctx2.assert({ customName: 1 }, { name: 'thirdTest' });
await ctx2.removeOldSnapshots();
await snapshotFileTree();
});
test('formats object nicely', async () => {
const circular: any = {};
circular.a = circular;
await assertSnapshot([
1,
true,
undefined,
null,
123n,
Symbol('heyo'),
'hello',
{ hello: 'world' },
circular,
new Map([['hello', 1], ['goodbye', 2]]),
new Set([1, 2, 3]),
function helloWorld() { },
/hello/g,
new Array(10).fill('long string'.repeat(10)),
{ [Symbol.for('debug.description')]() { return `Range [1 -> 5]`; } },
]);
});
});
+26 -24
View File
@@ -15,7 +15,7 @@ import { whenDeleted, writeFileSync } from 'vs/base/node/pfs';
import { findFreePort } from 'vs/base/node/ports';
import { watchFileContents } from 'vs/platform/files/node/watcher/nodejs/nodejsWatcherLib';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { buildHelpMessage, buildVersionMessage, OPTIONS } from 'vs/platform/environment/node/argv';
import { buildHelpMessage, buildVersionMessage, NATIVE_CLI_COMMANDS, OPTIONS } from 'vs/platform/environment/node/argv';
import { addArg, parseCLIProcessArgv } from 'vs/platform/environment/node/argvHelper';
import { getStdinFilePath, hasStdinWithoutTty, readFromStdin, stdinDataListener } from 'vs/platform/environment/node/stdin';
import { createWaitMarkerFileSync } from 'vs/platform/environment/node/wait';
@@ -51,31 +51,33 @@ export async function main(argv: string[]): Promise<any> {
return;
}
if (args.tunnel) {
if (!product.tunnelApplicationName) {
console.error(`'tunnel' command not supported in ${product.applicationName}`);
return;
}
const tunnelArgs = argv.slice(argv.indexOf('tunnel') + 1); // all arguments behind `tunnel`
return new Promise((resolve, reject) => {
let tunnelProcess: ChildProcess;
const stdio: StdioOptions = ['ignore', 'pipe', 'pipe'];
if (process.env['VSCODE_DEV']) {
tunnelProcess = spawn('cargo', ['run', '--', 'tunnel', ...tunnelArgs], { cwd: join(getAppRoot(), 'cli'), stdio });
} else {
const appPath = process.platform === 'darwin'
// ./Contents/MacOS/Electron => ./Contents/Resources/app/bin/code-tunnel-insiders
? join(dirname(dirname(process.execPath)), 'Resources', 'app')
: dirname(process.execPath);
const tunnelCommand = join(appPath, 'bin', `${product.tunnelApplicationName}${isWindows ? '.exe' : ''}`);
tunnelProcess = spawn(tunnelCommand, ['tunnel', ...tunnelArgs], { cwd: cwd(), stdio });
for (const subcommand of NATIVE_CLI_COMMANDS) {
if (args[subcommand]) {
if (!product.tunnelApplicationName) {
console.error(`'${subcommand}' command not supported in ${product.applicationName}`);
return;
}
const tunnelArgs = argv.slice(argv.indexOf(subcommand) + 1); // all arguments behind `tunnel`
return new Promise((resolve, reject) => {
let tunnelProcess: ChildProcess;
const stdio: StdioOptions = ['ignore', 'pipe', 'pipe'];
if (process.env['VSCODE_DEV']) {
tunnelProcess = spawn('cargo', ['run', '--', subcommand, ...tunnelArgs], { cwd: join(getAppRoot(), 'cli'), stdio });
} else {
const appPath = process.platform === 'darwin'
// ./Contents/MacOS/Electron => ./Contents/Resources/app/bin/code-tunnel-insiders
? join(dirname(dirname(process.execPath)), 'Resources', 'app')
: dirname(process.execPath);
const tunnelCommand = join(appPath, 'bin', `${product.tunnelApplicationName}${isWindows ? '.exe' : ''}`);
tunnelProcess = spawn(tunnelCommand, [subcommand, ...tunnelArgs], { cwd: cwd(), stdio });
}
tunnelProcess.stdout!.pipe(process.stdout);
tunnelProcess.stderr!.pipe(process.stderr);
tunnelProcess.on('exit', resolve);
tunnelProcess.on('error', reject);
});
tunnelProcess.stdout!.pipe(process.stdout);
tunnelProcess.stderr!.pipe(process.stderr);
tunnelProcess.on('exit', resolve);
tunnelProcess.on('error', reject);
});
}
}
// Help
@@ -17,7 +17,6 @@ import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker
import { DiffAlgorithmName, IDiffComputationResult, IEditorWorkerService, ILineChange, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorker';
import { IModelService } from 'vs/editor/common/services/model';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
import { regExpFlags } from 'vs/base/common/strings';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
import { StopWatch } from 'vs/base/common/stopwatch';
@@ -582,7 +581,7 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien
public async textualSuggest(resources: URI[], leadingWord: string | undefined, wordDefRegExp: RegExp): Promise<{ words: string[]; duration: number } | null> {
const proxy = await this._withSyncedResources(resources);
const wordDef = wordDefRegExp.source;
const wordDefFlags = regExpFlags(wordDefRegExp);
const wordDefFlags = wordDefRegExp.flags;
return proxy.textualSuggest(resources.map(r => r.toString()), leadingWord, wordDef, wordDefFlags);
}
@@ -594,7 +593,7 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien
}
const wordDefRegExp = this.languageConfigurationService.getLanguageConfiguration(model.getLanguageId()).getWordDefinition();
const wordDef = wordDefRegExp.source;
const wordDefFlags = regExpFlags(wordDefRegExp);
const wordDefFlags = wordDefRegExp.flags;
return proxy.computeWordRanges(resource.toString(), range, wordDef, wordDefFlags);
});
}
@@ -607,7 +606,7 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien
}
const wordDefRegExp = this.languageConfigurationService.getLanguageConfiguration(model.getLanguageId()).getWordDefinition();
const wordDef = wordDefRegExp.source;
const wordDefFlags = regExpFlags(wordDefRegExp);
const wordDefFlags = wordDefRegExp.flags;
return proxy.navigateValueSet(resource.toString(), range, up, wordDef, wordDefFlags);
});
}
@@ -20,8 +20,8 @@
}
.mtkoverflow {
background-color: var(--vscode-button-background, --vscode-editor-background);
color: var(--vscode-button-foreground, --vscode-editor-foreground);
background-color: var(--vscode-button-background, var(--vscode-editor-background));
color: var(--vscode-button-foreground, var(--vscode-editor-foreground));
border-width: 1px;
border-style: solid;
border-color: var(--vscode-contrastBorder);
@@ -11,3 +11,9 @@ export const diffMoveBorder = registerColor(
{ dark: '#8b8b8b9c', light: '#8b8b8b9c', hcDark: '#8b8b8b9c', hcLight: '#8b8b8b9c', },
localize('diffEditor.move.border', 'The border color for text that got moved in the diff editor.')
);
export const diffMoveBorderActive = registerColor(
'diffEditor.moveActive.border',
{ dark: '#FFA500', light: '#FFA500', hcDark: '#FFA500', hcLight: '#FFA500', },
localize('diffEditor.moveActive.border', 'The active border color for text that got moved in the diff editor.')
);
@@ -5,14 +5,12 @@
import { Disposable } from 'vs/base/common/lifecycle';
import { IObservable, derived } from 'vs/base/common/observable';
import { isDefined } from 'vs/base/common/types';
import { arrowRevertChange, diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineAddDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineDeleteDecorationBackgroundWithIndicator, diffWholeLineAddDecoration, diffWholeLineDeleteDecoration } from 'vs/editor/browser/widget/diffEditorWidget2/decorations';
import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors';
import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions';
import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel';
import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines';
import { applyObservableDecorations } from 'vs/editor/browser/widget/diffEditorWidget2/utils';
import { LineRange } from 'vs/editor/common/core/lineRange';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IModelDeltaDecoration } from 'vs/editor/common/model';
@@ -42,46 +40,37 @@ export class DiffEditorDecorations extends Disposable {
const originalDecorations: IModelDeltaDecoration[] = [];
const modifiedDecorations: IModelDeltaDecoration[] = [];
for (const m of diff.mappings) {
const fullRangeOriginal = LineRange.subtract(m.lineRangeMapping.originalRange, currentMove?.lineRangeMapping.original)
.map(i => i.toInclusiveRange()).filter(isDefined);
for (const range of fullRangeOriginal) {
originalDecorations.push({ range, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground });
}
const fullRangeModified = LineRange.subtract(m.lineRangeMapping.modifiedRange, currentMove?.lineRangeMapping.modified)
.map(i => i.toInclusiveRange()).filter(isDefined);
for (const range of fullRangeModified) {
modifiedDecorations.push({ range, options: renderIndicators ? diffLineAddDecorationBackgroundWithIndicator : diffLineAddDecorationBackground });
}
if (m.lineRangeMapping.modifiedRange.isEmpty || m.lineRangeMapping.originalRange.isEmpty) {
for (const range of fullRangeOriginal) {
originalDecorations.push({ range, options: diffWholeLineDeleteDecoration });
if (!currentMove) {
for (const m of diff.mappings) {
if (!m.lineRangeMapping.originalRange.isEmpty) {
originalDecorations.push({ range: m.lineRangeMapping.originalRange.toInclusiveRange()!, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground });
}
for (const range of fullRangeModified) {
modifiedDecorations.push({ range, options: diffWholeLineAddDecoration });
if (!m.lineRangeMapping.modifiedRange.isEmpty) {
modifiedDecorations.push({ range: m.lineRangeMapping.modifiedRange.toInclusiveRange()!, options: renderIndicators ? diffLineAddDecorationBackgroundWithIndicator : diffLineAddDecorationBackground });
}
} else {
for (const i of m.lineRangeMapping.innerChanges || []) {
if (currentMove
&& (currentMove.lineRangeMapping.original.intersect(new LineRange(i.originalRange.startLineNumber, i.originalRange.endLineNumber))
|| currentMove.lineRangeMapping.modified.intersect(new LineRange(i.modifiedRange.startLineNumber, i.modifiedRange.endLineNumber)))) {
continue;
}
// Don't show empty markers outside the line range
if (m.lineRangeMapping.originalRange.contains(i.originalRange.startLineNumber)) {
originalDecorations.push({ range: i.originalRange, options: (i.originalRange.isEmpty() && showEmptyDecorations) ? diffDeleteDecorationEmpty : diffDeleteDecoration });
if (m.lineRangeMapping.modifiedRange.isEmpty || m.lineRangeMapping.originalRange.isEmpty) {
if (!m.lineRangeMapping.originalRange.isEmpty) {
originalDecorations.push({ range: m.lineRangeMapping.originalRange.toInclusiveRange()!, options: diffWholeLineDeleteDecoration });
}
if (m.lineRangeMapping.modifiedRange.contains(i.modifiedRange.startLineNumber)) {
modifiedDecorations.push({ range: i.modifiedRange, options: (i.modifiedRange.isEmpty() && showEmptyDecorations) ? diffAddDecorationEmpty : diffAddDecoration });
if (!m.lineRangeMapping.modifiedRange.isEmpty) {
modifiedDecorations.push({ range: m.lineRangeMapping.modifiedRange.toInclusiveRange()!, options: diffWholeLineAddDecoration });
}
} else {
for (const i of m.lineRangeMapping.innerChanges || []) {
// Don't show empty markers outside the line range
if (m.lineRangeMapping.originalRange.contains(i.originalRange.startLineNumber)) {
originalDecorations.push({ range: i.originalRange, options: (i.originalRange.isEmpty() && showEmptyDecorations) ? diffDeleteDecorationEmpty : diffDeleteDecoration });
}
if (m.lineRangeMapping.modifiedRange.contains(i.modifiedRange.startLineNumber)) {
modifiedDecorations.push({ range: i.modifiedRange, options: (i.modifiedRange.isEmpty() && showEmptyDecorations) ? diffAddDecorationEmpty : diffAddDecoration });
}
}
}
}
if (!m.lineRangeMapping.modifiedRange.isEmpty && this._options.shouldRenderRevertArrows.read(reader) && !currentMove) {
modifiedDecorations.push({ range: Range.fromPositions(new Position(m.lineRangeMapping.modifiedRange.startLineNumber, 1)), options: arrowRevertChange });
if (!m.lineRangeMapping.modifiedRange.isEmpty && this._options.shouldRenderRevertArrows.read(reader) && !currentMove) {
modifiedDecorations.push({ range: Range.fromPositions(new Position(m.lineRangeMapping.modifiedRange.startLineNumber, 1)), options: arrowRevertChange });
}
}
}
@@ -107,7 +96,7 @@ export class DiffEditorDecorations extends Disposable {
originalDecorations.push({
range: m.lineRangeMapping.original.toInclusiveRange()!, options: {
description: 'moved',
blockClassName: 'movedOriginal',
blockClassName: 'movedOriginal' + (m === currentMove ? ' currentMove' : ''),
blockPadding: [MovedBlocksLinesPart.movedCodeBlockPadding, 0, MovedBlocksLinesPart.movedCodeBlockPadding, MovedBlocksLinesPart.movedCodeBlockPadding],
}
});
@@ -115,7 +104,7 @@ export class DiffEditorDecorations extends Disposable {
modifiedDecorations.push({
range: m.lineRangeMapping.modified.toInclusiveRange()!, options: {
description: 'moved',
blockClassName: 'movedModified',
blockClassName: 'movedModified' + (m === currentMove ? ' currentMove' : ''),
blockPadding: [4, 0, 4, 4],
}
});
@@ -35,8 +35,8 @@ export class DiffEditorEditors extends Disposable {
) {
super();
this.original = this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {});
this.modified = this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {});
this.original = this._register(this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {}));
this.modified = this._register(this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {}));
this._register(autorunHandleChanges({
createEmptyChangeSummary: () => ({} as IDiffEditorConstructionOptions),
@@ -11,13 +11,14 @@ import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange
import { Range } from 'vs/editor/common/core/range';
import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider';
import { LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
import { lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/standardLinesDiffComputer';
import { StandardLinesDiffComputer, lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/standardLinesDiffComputer';
import { IDiffEditorModel, IDiffEditorViewModel } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { TextEditInfo } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper';
import { combineTextEditInfos } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos';
import { lengthAdd, lengthDiffNonNegative, lengthGetLineCount, lengthOfRange, lengthToPosition, lengthZero, positionToLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length';
import { DiffEditorOptions } from './diffEditorOptions';
import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils';
export class DiffEditorViewModel extends Disposable implements IDiffEditorViewModel {
private readonly _isDiffUpToDate = observableValue<boolean>('isDiffUpToDate', false);
@@ -105,40 +106,36 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
this._register(model.modified.onDidChangeContent((e) => {
const diff = this._diff.get();
if (!diff) {
return;
}
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
const result = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified);
if (result) {
this._lastDiff = result;
transaction(tx => {
this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx);
updateUnchangedRegions(result, tx);
const currentSyncedMovedText = this.syncedMovedTexts.get();
this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx);
});
if (diff) {
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
const result = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified);
if (result) {
this._lastDiff = result;
transaction(tx => {
this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx);
updateUnchangedRegions(result, tx);
const currentSyncedMovedText = this.syncedMovedTexts.get();
this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx);
});
}
}
debouncer.schedule();
}));
this._register(model.original.onDidChangeContent((e) => {
const diff = this._diff.get();
if (!diff) {
return;
}
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
const result = applyOriginalEdits(this._lastDiff!, textEdits, model.original, model.modified);
if (result) {
this._lastDiff = result;
transaction(tx => {
this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx);
updateUnchangedRegions(result, tx);
const currentSyncedMovedText = this.syncedMovedTexts.get();
this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx);
});
if (diff) {
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
const result = applyOriginalEdits(this._lastDiff!, textEdits, model.original, model.modified);
if (result) {
this._lastDiff = result;
transaction(tx => {
this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx);
updateUnchangedRegions(result, tx);
const currentSyncedMovedText = this.syncedMovedTexts.get();
this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx);
});
}
}
debouncer.schedule();
@@ -151,6 +148,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
debouncer.cancel();
contentChangedSignal.read(reader);
documentDiffProviderOptionChanged.read(reader);
readHotReloadableExport(StandardLinesDiffComputer, reader);
this._isDiffUpToDate.set(false, undefined);
@@ -175,7 +173,6 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
result = applyOriginalEdits(result, originalTextEditInfos, model.original, model.modified) ?? result;
result = applyModifiedEdits(result, modifiedTextEditInfos, model.original, model.modified) ?? result;
transaction(tx => {
updateUnchangedRegions(result, tx);
@@ -91,14 +91,10 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
)
});
/*
TODO@hediet add this back once move detection is more polished.
Users can still enable this via settings.json (config.diffEditor.experimental.showMoves).
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: new ToggleShowMovedCodeBlocks().desc.id,
title: localize('showMoves', "Show Moves"),
title: localize('showMoves', "Show Moved Code Blocks"),
icon: Codicon.move,
toggled: ContextKeyEqualsExpr.create('config.diffEditor.experimental.showMoves', true),
},
@@ -106,7 +102,6 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
group: '1_diff',
when: ContextKeyEqualsExpr.create('diffEditorVersion', 2)
});
*/
const diffEditorCategory: ILocalizedString = {
value: localize('diffEditor', 'Diff Editor'),
@@ -79,6 +79,8 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
private readonly _options: DiffEditorOptions;
private readonly _editors: DiffEditorEditors;
private readonly movedBlocksLinesPart = observableValue<MovedBlocksLinesPart | undefined>('MovedBlocksLinesPart', undefined);
constructor(
private readonly _domElement: HTMLElement,
options: Readonly<IDiffEditorConstructionOptions>,
@@ -204,13 +206,15 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
this._register(keepAlive(this._layoutInfo, true));
this._register(new MovedBlocksLinesPart(
this.elements.root,
this._diffModel,
this._layoutInfo.map(i => i.originalEditor),
this._layoutInfo.map(i => i.modifiedEditor),
this._editors,
));
this._register(autorunWithStore((reader, store) => {
this.movedBlocksLinesPart.set(store.add(new (readHotReloadableExport(MovedBlocksLinesPart, reader))(
this.elements.root,
this._diffModel,
this._layoutInfo.map(i => i.originalEditor),
this._layoutInfo.map(i => i.modifiedEditor),
this._editors,
)), undefined);
}));
this._register(applyStyle(this.elements.overlay, {
width: this._layoutInfo.map((i, r) => i.originalEditor.width + (this._options.renderSideBySide.read(r) ? 0 : i.modifiedEditor.width)),
@@ -285,13 +289,15 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
const originalWidth = sashLeft ?? Math.max(5, this._editors.original.getLayoutInfo().decorationsLeft);
const modifiedWidth = width - originalWidth - (this._options.renderOverviewRuler.read(reader) ? OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH : 0);
this.elements.original.style.width = originalWidth + 'px';
const movedBlocksLinesWidth = this.movedBlocksLinesPart.read(reader)?.width.read(reader) ?? 0;
const originalWidthWithoutMovedBlockLines = originalWidth - movedBlocksLinesWidth;
this.elements.original.style.width = originalWidthWithoutMovedBlockLines + 'px';
this.elements.original.style.left = '0px';
this.elements.modified.style.width = modifiedWidth + 'px';
this.elements.modified.style.left = originalWidth + 'px';
this._editors.original.layout({ width: originalWidth, height });
this._editors.original.layout({ width: originalWidthWithoutMovedBlockLines, height });
this._editors.modified.layout({ width: modifiedWidth, height });
return {
@@ -265,7 +265,7 @@ export class ViewZoneManager extends Disposable {
} else {
const delta = a.modifiedHeightInPx - a.originalHeightInPx;
if (delta > 0) {
if (syncedMovedText?.lineRangeMapping.original.contains(a.originalRange.endLineNumberExclusive - 1)) {
if (syncedMovedText?.lineRangeMapping.original.delta(-1).deltaLength(2).contains(a.originalRange.endLineNumberExclusive - 1)) {
continue;
}
@@ -276,7 +276,7 @@ export class ViewZoneManager extends Disposable {
showInHiddenAreas: true,
});
} else {
if (syncedMovedText?.lineRangeMapping.modified.contains(a.modifiedRange.endLineNumberExclusive - 1)) {
if (syncedMovedText?.lineRangeMapping.modified.delta(-1).deltaLength(2).contains(a.modifiedRange.endLineNumberExclusive - 1)) {
continue;
}
@@ -3,17 +3,26 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { IObservable, autorun, observableFromEvent, observableSignalFromEvent } from 'vs/base/common/observable';
import { booleanComparator, compareBy, findMaxIdxBy, numberComparator, tieBreakComparators } from 'vs/base/common/arrays';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IObservable, autorun, derived, keepAlive, observableFromEvent, observableSignalFromEvent, observableValue } from 'vs/base/common/observable';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors';
import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel';
import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions';
import { LineRange } from 'vs/editor/common/core/lineRange';
import { OffsetRange, OffsetRangeSet } from 'vs/editor/common/core/offsetRange';
export class MovedBlocksLinesPart extends Disposable {
public static readonly movedCodeBlockPadding = 4;
private readonly _element: SVGElement;
private readonly _originalScrollTop = observableFromEvent(this._editors.original.onDidScrollChange, () => this._editors.original.getScrollTop());
private readonly _modifiedScrollTop = observableFromEvent(this._editors.modified.onDidScrollChange, () => this._editors.modified.getScrollTop());
private readonly _viewZonesChanged = observableSignalFromEvent('onDidChangeViewZones', this._editors.modified.onDidChangeViewZones);
public readonly width = observableValue('width', 0);
constructor(
private readonly _rootElement: HTMLElement,
private readonly _diffModel: IObservable<DiffEditorViewModel | undefined>,
@@ -23,9 +32,10 @@ export class MovedBlocksLinesPart extends Disposable {
) {
super();
const element = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
element.setAttribute('class', 'moved-blocks-lines');
this._rootElement.appendChild(element);
this._element = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
this._element.setAttribute('class', 'moved-blocks-lines');
this._rootElement.appendChild(this._element);
this._register(toDisposable(() => this._element.remove()));
this._register(autorun(reader => {
/** @description update moved blocks lines positioning */
@@ -35,58 +45,142 @@ export class MovedBlocksLinesPart extends Disposable {
return;
}
element.style.left = `${info.width - info.verticalScrollbarWidth}px`;
element.style.height = `${info.height}px`;
element.style.width = `${info.verticalScrollbarWidth + info.contentLeft - MovedBlocksLinesPart.movedCodeBlockPadding}px`;
this._element.style.left = `${info.width - info.verticalScrollbarWidth}px`;
this._element.style.height = `${info.height}px`;
this._element.style.width = `${info.verticalScrollbarWidth + info.contentLeft - MovedBlocksLinesPart.movedCodeBlockPadding + this.width.read(reader)}px`;
}));
const originalScrollTop = observableFromEvent(this._editors.original.onDidScrollChange, () => this._editors.original.getScrollTop());
const modifiedScrollTop = observableFromEvent(this._editors.modified.onDidScrollChange, () => this._editors.modified.getScrollTop());
const viewZonesChanged = observableSignalFromEvent('onDidChangeViewZones', this._editors.modified.onDidChangeViewZones);
this._register(keepAlive(this._state, true));
}
this._register(autorun(reader => {
element.replaceChildren();
private readonly _state = derived(reader => {
/** @description update moved blocks lines */
/** @description update moved blocks lines */
const moves = this._diffModel.read(reader)?.diff.read(reader)?.movedTexts;
if (!moves) {
return;
this._element.replaceChildren();
const model = this._diffModel.read(reader);
const moves = model?.diff.read(reader)?.movedTexts;
if (!moves || moves.length === 0) {
this.width.set(0, undefined);
return;
}
this._viewZonesChanged.read(reader);
const infoOrig = this._originalEditorLayoutInfo.read(reader);
const infoMod = this._modifiedEditorLayoutInfo.read(reader);
if (!infoOrig || !infoMod) {
this.width.set(0, undefined);
return;
}
const lines = moves.map((move) => {
function computeLineStart(range: LineRange, editor: ICodeEditor) {
const t1 = editor.getTopForLineNumber(range.startLineNumber);
const t2 = editor.getTopForLineNumber(range.endLineNumberExclusive);
return (t1 + t2) / 2;
}
viewZonesChanged.read(reader);
const start = computeLineStart(move.lineRangeMapping.original, this._editors.original);
const startOffset = this._originalScrollTop.read(reader);
const end = computeLineStart(move.lineRangeMapping.modified, this._editors.modified);
const endOffset = this._modifiedScrollTop.read(reader);
const info = this._originalEditorLayoutInfo.read(reader);
const info2 = this._modifiedEditorLayoutInfo.read(reader);
if (!info || !info2) {
return;
const from = start - startOffset;
const to = end - endOffset;
const top = Math.min(start, end);
const bottom = Math.max(start, end);
return { range: new OffsetRange(top, bottom), from, to, fromWithoutScroll: start, toWithoutScroll: end, move };
});
lines.sort(tieBreakComparators(
compareBy(l => l.fromWithoutScroll > l.toWithoutScroll, booleanComparator),
compareBy(l => -l.fromWithoutScroll, numberComparator)
));
const layout = LinesLayout.compute(lines.map(l => l.range));
const padding = 10;
const lineAreaLeft = infoOrig.verticalScrollbarWidth;
const lineAreaWidth = (layout.getTrackCount() - 1) * 10 + padding * 2;
const width = lineAreaLeft + lineAreaWidth + (infoMod.contentLeft - MovedBlocksLinesPart.movedCodeBlockPadding);
let idx = 0;
for (const line of lines) {
const track = layout.getTrack(idx);
const verticalY = lineAreaLeft + padding + track * 10;
const arrowHeight = 15;
const arrowWidth = 15;
const right = width;
const rectWidth = infoMod.glyphMarginWidth + infoMod.lineNumbersWidth;
const rectHeight = 18;
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.classList.add('arrow-rectangle');
rect.setAttribute('x', `${right - rectWidth}`);
rect.setAttribute('y', `${line.to - rectHeight / 2}`);
rect.setAttribute('width', `${rectWidth}`);
rect.setAttribute('height', `${rectHeight}`);
this._element.appendChild(rect);
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
if (line.move === model.syncedMovedTexts.read(reader)) {
path.classList.add('currentMove');
}
const width = info.verticalScrollbarWidth + info.contentLeft - MovedBlocksLinesPart.movedCodeBlockPadding;
path.setAttribute('d', `M ${0} ${line.from} L ${verticalY} ${line.from} L ${verticalY} ${line.to} L ${right - arrowWidth} ${line.to}`);
path.setAttribute('fill', 'none');
this._element.appendChild(path);
let idx = 0;
for (const m of moves) {
function computeLineStart(range: LineRange, editor: ICodeEditor) {
const t1 = editor.getTopForLineNumber(range.startLineNumber);
const t2 = editor.getTopForLineNumber(range.endLineNumberExclusive);
return (t1 + t2) / 2;
const arrowRight = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
arrowRight.classList.add('arrow');
if (line.move === model.syncedMovedTexts.read(reader)) {
arrowRight.classList.add('currentMove');
}
arrowRight.setAttribute('points', `${right - arrowWidth},${line.to - arrowHeight / 2} ${right},${line.to} ${right - arrowWidth},${line.to + arrowHeight / 2}`);
this._element.appendChild(arrowRight);
idx++;
}
this.width.set(lineAreaWidth, undefined);
});
}
class LinesLayout {
public static compute(lines: OffsetRange[]): LinesLayout {
const setsPerTrack: OffsetRangeSet[] = [];
const trackPerLineIdx: number[] = [];
for (const line of lines) {
let trackIdx = setsPerTrack.findIndex(set => !set.intersectsStrict(line));
if (trackIdx === -1) {
const maxTrackCount = 6;
if (setsPerTrack.length >= maxTrackCount) {
trackIdx = findMaxIdxBy(setsPerTrack, compareBy(set => set.intersectWithRangeLength(line), numberComparator));
} else {
trackIdx = setsPerTrack.length;
setsPerTrack.push(new OffsetRangeSet());
}
const start = computeLineStart(m.lineRangeMapping.original, this._editors.original);
const startOffset = originalScrollTop.read(reader);
const end = computeLineStart(m.lineRangeMapping.modified, this._editors.modified);
const endOffset = modifiedScrollTop.read(reader);
const top = start - startOffset;
const bottom = end - endOffset;
const center = (width / 2) - moves.length * 5 + idx * 10;
idx++;
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', `M ${0} ${top} L ${center} ${top} L ${center} ${bottom} L ${width} ${bottom}`);
path.setAttribute('fill', 'none');
element.appendChild(path);
}
}));
setsPerTrack[trackIdx].addRange(line);
trackPerLineIdx.push(trackIdx);
}
return new LinesLayout(setsPerTrack.length, trackPerLineIdx);
}
private constructor(
private readonly _trackCount: number,
private readonly trackPerLineIdx: number[]
) { }
getTrack(lineIdx: number): number {
return this.trackPerLineIdx[lineIdx];
}
getTrackCount(): number {
return this._trackCount;
}
}
@@ -78,6 +78,26 @@
border: 2px solid var(--vscode-diffEditor-move-border);
}
.monaco-editor .movedOriginal.currentMove, .monaco-editor .movedModified.currentMove {
border: 2px solid var(--vscode-diffEditor-moveActive-border);
}
.monaco-diff-editor .moved-blocks-lines path.currentMove {
stroke: var(--vscode-diffEditor-moveActive-border);
}
.monaco-diff-editor .moved-blocks-lines .arrow {
fill: var(--vscode-diffEditor-move-border);
}
.monaco-diff-editor .moved-blocks-lines .arrow.currentMove {
fill: var(--vscode-diffEditor-moveActive-border);
}
.monaco-diff-editor .moved-blocks-lines .arrow-rectangle {
fill: var(--vscode-editor-background);
}
.monaco-diff-editor .moved-blocks-lines {
position: absolute;
pointer-events: none;
@@ -81,7 +81,7 @@
}
.monaco-editor .line-insert, .monaco-diff-editor .line-insert {
background-color: var(--vscode-diffEditor-insertedLineBackground, --vscode-diffEditor-insertedTextBackground);
background-color: var(--vscode-diffEditor-insertedLineBackground, var(--vscode-diffEditor-insertedTextBackground));
}
.monaco-editor .line-insert,
@@ -106,7 +106,7 @@
.monaco-editor .inline-added-margin-view-zone,
.monaco-editor .gutter-insert, .monaco-diff-editor .gutter-insert {
background-color: var(--vscode-diffEditorGutter-insertedLineBackground, --vscode-diffEditor-insertedLineBackground, --vscode-diffEditor-insertedTextBackground);
background-color: var(--vscode-diffEditorGutter-insertedLineBackground, var(--vscode-diffEditor-insertedLineBackground), var(--vscode-diffEditor-insertedTextBackground));
}
.monaco-editor .char-delete, .monaco-diff-editor .char-delete {
@@ -114,12 +114,12 @@
}
.monaco-editor .line-delete, .monaco-diff-editor .line-delete {
background-color: var(--vscode-diffEditor-removedLineBackground, --vscode-diffEditor-removedTextBackground);
background-color: var(--vscode-diffEditor-removedLineBackground, var(--vscode-diffEditor-removedTextBackground));
}
.monaco-editor .inline-deleted-margin-view-zone,
.monaco-editor .gutter-delete, .monaco-diff-editor .gutter-delete {
background-color: var(--vscode-diffEditorGutter-removedLineBackground, --vscode-diffEditor-removedLineBackground, --vscode-diffEditor-removedTextBackground);
background-color: var(--vscode-diffEditorGutter-removedLineBackground, var(--vscode-diffEditor-removedLineBackground), var(--vscode-diffEditor-removedTextBackground));
}
.monaco-diff-editor.side-by-side .editor.modified {
+9 -1
View File
@@ -730,6 +730,11 @@ export interface IEditorOptions {
* Controls whether the editor receives tabs or defers them to the workbench for navigation.
*/
tabFocusMode?: boolean;
/**
* Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown.
*/
inlineCompletionsAccessibilityVerbose?: boolean;
}
/**
@@ -5143,7 +5148,8 @@ export const enum EditorOption {
layoutInfo,
wrappingInfo,
defaultColorDecorators,
colorDecoratorsActivatedOn
colorDecoratorsActivatedOn,
inlineCompletionsAccessibilityVerbose
}
export const EditorOptions = {
@@ -5734,6 +5740,8 @@ export const EditorOptions = {
)),
suggest: register(new EditorSuggest()),
inlineSuggest: register(new InlineEditorSuggest()),
inlineCompletionsAccessibilityVerbose: register(new EditorBooleanOption(EditorOption.inlineCompletionsAccessibilityVerbose, 'inlineCompletionsAccessibilityVerbose', false,
{ description: nls.localize('inlineCompletionsAccessibilityVerbose', "Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown.") })),
suggestFontSize: register(new EditorIntOption(
EditorOption.suggestFontSize, 'suggestFontSize',
0, 0, 1000,
+13
View File
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { BugIndicatingError } from 'vs/base/common/errors';
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
import { Range } from 'vs/editor/common/core/range';
/**
@@ -154,6 +155,10 @@ export class LineRange {
return new LineRange(this.startLineNumber + offset, this.endLineNumberExclusive + offset);
}
public deltaLength(offset: number): LineRange {
return new LineRange(this.startLineNumber, this.endLineNumberExclusive + offset);
}
/**
* The number of lines this line range spans.
*/
@@ -235,6 +240,14 @@ export class LineRange {
public includes(lineNumber: number): boolean {
return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive;
}
/**
* Converts this 1-based line range to a 0-based offset range (subtracts 1!).
* @internal
*/
public toOffsetRange(): OffsetRange {
return new OffsetRange(this.startLineNumber - 1, this.endLineNumberExclusive - 1);
}
}
export type ISerializedLineRange = [startLineNumber: number, endLineNumberExclusive: number];
+70
View File
@@ -48,6 +48,14 @@ export class OffsetRange {
return new OffsetRange(this.start + offset, this.endExclusive + offset);
}
public deltaStart(offset: number): OffsetRange {
return new OffsetRange(this.start + offset, this.endExclusive);
}
public deltaEnd(offset: number): OffsetRange {
return new OffsetRange(this.start, this.endExclusive + offset);
}
public get length(): number {
return this.endExclusive - this.start;
}
@@ -90,4 +98,66 @@ export class OffsetRange {
}
return undefined;
}
public slice<T>(arr: T[]): T[] {
return arr.slice(this.start, this.endExclusive);
}
}
export class OffsetRangeSet {
private readonly _sortedRanges: OffsetRange[] = [];
public addRange(range: OffsetRange): void {
let i = 0;
while (i < this._sortedRanges.length && this._sortedRanges[i].endExclusive < range.start) {
i++;
}
let j = i;
while (j < this._sortedRanges.length && this._sortedRanges[j].start <= range.endExclusive) {
j++;
}
if (i === j) {
this._sortedRanges.splice(i, 0, range);
} else {
const start = Math.min(range.start, this._sortedRanges[i].start);
const end = Math.max(range.endExclusive, this._sortedRanges[j - 1].endExclusive);
this._sortedRanges.splice(i, j - i, new OffsetRange(start, end));
}
}
public toString(): string {
return this._sortedRanges.map(r => r.toString()).join(', ');
}
/**
* Returns of there is a value that is contained in this instance and the given range.
*/
public intersectsStrict(other: OffsetRange): boolean {
// TODO use binary search
let i = 0;
while (i < this._sortedRanges.length && this._sortedRanges[i].endExclusive <= other.start) {
i++;
}
return i < this._sortedRanges.length && this._sortedRanges[i].start < other.endExclusive;
}
public intersectWithRange(other: OffsetRange): OffsetRangeSet {
// TODO use binary search + slice
const result = new OffsetRangeSet();
for (const range of this._sortedRanges) {
const intersection = range.intersect(other);
if (intersection) {
result.addRange(intersection);
}
}
return result;
}
public intersectWithRangeLength(other: OffsetRange): number {
return this.intersectWithRange(other).length;
}
public get length(): number {
return this._sortedRanges.reduce((prev, cur) => prev + cur.length, 0);
}
}
@@ -149,6 +149,29 @@ export function removeRandomMatches(sequence1: LinesSliceCharSequence, sequence2
diffs = result;
} while (counter++ < 10 && shouldRepeat);
// Remove short suffixes/prefixes
for (let i = 0; i < diffs.length; i++) {
const cur = diffs[i];
let range1 = cur.seq1Range;
let range2 = cur.seq2Range;
const fullRange1 = sequence1.extendToFullLines(cur.seq1Range);
const prefix = sequence1.getText(new OffsetRange(fullRange1.start, cur.seq1Range.start));
if (prefix.length > 0 && prefix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 100) {
range1 = cur.seq1Range.deltaStart(-prefix.length);
range2 = cur.seq2Range.deltaStart(-prefix.length);
}
const suffix = sequence1.getText(new OffsetRange(cur.seq1Range.endExclusive, fullRange1.endExclusive));
if (suffix.length > 0 && (suffix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 150)) {
range1 = range1.deltaEnd(suffix.length);
range2 = range2.deltaEnd(suffix.length);
}
diffs[i] = new SequenceDiff(range1, range2);
}
return diffs;
}
@@ -155,6 +155,13 @@ export class SimpleLineRangeMapping {
public flip(): SimpleLineRangeMapping {
return new SimpleLineRangeMapping(this.modified, this.original);
}
public join(other: SimpleLineRangeMapping): SimpleLineRangeMapping {
return new SimpleLineRangeMapping(
this.original.join(other.original),
this.modified.join(other.modified),
);
}
}
export class MovedText {
@@ -177,7 +177,7 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer {
}
private computeMoves(changes: LineRangeMapping[], originalLines: string[], modifiedLines: string[], hashedOriginalLines: number[], hashedModifiedLines: number[], timeout: ITimeout, considerWhitespaceChanges: boolean): MovedText[] {
const moves: MovedText[] = [];
const moves: SimpleLineRangeMapping[] = [];
const deletions = changes
.filter(c => c.modifiedRange.isEmpty && c.originalRange.length >= 3)
.map(d => new LineRangeFragment(d.originalRange, originalLines, d));
@@ -199,17 +199,15 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer {
}
if (highestSimilarity > 0.90 && best) {
const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff(
new OffsetRange(deletion.range.startLineNumber - 1, deletion.range.endLineNumberExclusive - 1),
new OffsetRange(best.range.startLineNumber - 1, best.range.endLineNumberExclusive - 1)
), timeout, considerWhitespaceChanges);
const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true);
insertions.delete(best);
moves.push(new MovedText(new SimpleLineRangeMapping(deletion.range, best.range), mappings));
moves.push(new SimpleLineRangeMapping(deletion.range, best.range));
excludedChanges.add(deletion.source);
excludedChanges.add(best.source);
}
if (!timeout.isValid()) {
return [];
}
}
const original3LineHashes = new SetMap<string, { range: LineRange }>();
@@ -242,9 +240,6 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer {
let lastMappings: PossibleMapping[] = [];
for (let i = change.modifiedRange.startLineNumber; i < change.modifiedRange.endLineNumberExclusive - 2; i++) {
const key = `${hashedModifiedLines[i - 1]}:${hashedModifiedLines[i + 1 - 1]}:${hashedModifiedLines[i + 2 - 1]}`;
//const isWeakKey = (originalLines[i].trim().length + originalLines[i + 1].trim().length + originalLines[i + 2].trim().length < 20);
const currentModifiedRange = new LineRange(i, i + 3);
const nextMappings: PossibleMapping[] = [];
@@ -269,6 +264,10 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer {
});
lastMappings = nextMappings;
}
if (!timeout.isValid()) {
return [];
}
}
possibleMappings.sort(reverseOrder(compareBy(m => m.modifiedLineRange.length, numberComparator)));
@@ -291,19 +290,49 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer {
const modifiedLineRange = s;
const originalLineRange = s.delta(-diffOrigToMod);
const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff(
new OffsetRange(originalLineRange.startLineNumber - 1, originalLineRange.endLineNumberExclusive - 1),
new OffsetRange(modifiedLineRange.startLineNumber - 1, modifiedLineRange.endLineNumberExclusive - 1)
), timeout, considerWhitespaceChanges);
const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true);
moves.push(new MovedText(new SimpleLineRangeMapping(originalLineRange, modifiedLineRange), mappings));
moves.push(new SimpleLineRangeMapping(originalLineRange, modifiedLineRange));
modifiedSet.addRange(modifiedLineRange);
originalSet.addRange(originalLineRange);
}
}
return moves;
// join moves
moves.sort(compareBy(m => m.original.startLineNumber, numberComparator));
if (moves.length === 0) {
return [];
}
const joinedMoves = [moves[0]];
for (let i = 1; i < moves.length; i++) {
const last = joinedMoves[joinedMoves.length - 1];
const current = moves[i];
const originalDist = current.original.startLineNumber - last.original.endLineNumberExclusive;
const modifiedDist = current.modified.startLineNumber - last.modified.endLineNumberExclusive;
const currentMoveAfterLast = originalDist >= 0 && modifiedDist >= 0;
if (currentMoveAfterLast && originalDist <= 1 && modifiedDist <= 1) {
joinedMoves[joinedMoves.length - 1] = last.join(current);
continue;
}
const originalText = current.original.toOffsetRange().slice(originalLines).map(l => l.trim()).join('\n');
if (originalText.length <= 10) {
// Ignore small moves
continue;
}
joinedMoves.push(current);
}
const fullMoves = joinedMoves.map(m => {
const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff(
m.original.toOffsetRange(),
m.modified.toOffsetRange(),
), timeout, considerWhitespaceChanges);
const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true);
return new MovedText(m, mappings);
});
return fullMoves;
}
private refineDiff(originalLines: string[], modifiedLines: string[], diff: SequenceDiff, timeout: ITimeout, considerWhitespaceChanges: boolean): { mappings: RangeMapping[]; hitTimeout: boolean } {
@@ -659,7 +688,7 @@ export class LinesSliceCharSequence implements ISequence {
private readonly firstCharOffsetByLineMinusOne: number[] = [];
public readonly lineRange: OffsetRange;
// To account for trimming
private readonly offsetByLine: number[] = [];
private readonly additionalOffsetByLine: number[] = [];
constructor(public readonly lines: string[], lineRange: OffsetRange, public readonly considerWhitespaceChanges: boolean) {
// This slice has to have lineRange.length many \n! (otherwise diffing against an empty slice will be problematic)
@@ -687,7 +716,7 @@ export class LinesSliceCharSequence implements ISequence {
line = trimmedStartLine.trimEnd();
}
this.offsetByLine.push(offset);
this.additionalOffsetByLine.push(offset);
for (let i = 0; i < line.length; i++) {
this.elements.push(line.charCodeAt(i));
@@ -700,7 +729,7 @@ export class LinesSliceCharSequence implements ISequence {
}
}
// To account for the last line
this.offsetByLine.push(0);
this.additionalOffsetByLine.push(0);
}
toString() {
@@ -767,7 +796,7 @@ export class LinesSliceCharSequence implements ISequence {
}
const offsetOfFirstCharInLine = i === 0 ? 0 : this.firstCharOffsetByLineMinusOne[i - 1];
return new Position(this.lineRange.start + i + 1, offset - offsetOfFirstCharInLine + 1 + this.offsetByLine[i]);
return new Position(this.lineRange.start + i + 1, offset - offsetOfFirstCharInLine + 1 + this.additionalOffsetByLine[i]);
}
public translateRange(range: OffsetRange): Range {
@@ -805,9 +834,52 @@ export class LinesSliceCharSequence implements ISequence {
return this.translateOffset(range.endExclusive).lineNumber - this.translateOffset(range.start).lineNumber;
}
isStronglyEqual(offset1: number, offset2: number): boolean {
public isStronglyEqual(offset1: number, offset2: number): boolean {
return this.elements[offset1] === this.elements[offset2];
}
public extendToFullLines(range: OffsetRange): OffsetRange {
const firstIdx = findLastIdxMonotonous(this.firstCharOffsetByLineMinusOne, x => x <= range.start);
const lastIdx = findFirstIdxMonotonous(this.firstCharOffsetByLineMinusOne, x => range.endExclusive <= x);
const start = firstIdx === -1 ? 0 : this.firstCharOffsetByLineMinusOne[firstIdx];
const end = lastIdx === this.firstCharOffsetByLineMinusOne.length ? this.elements.length : this.firstCharOffsetByLineMinusOne[lastIdx];
return new OffsetRange(start, end);
}
}
/**
* @returns -1 if predicate is false for all items
*/
function findLastIdxMonotonous<T>(arr: T[], predicate: (item: T) => boolean): number {
let i = 0;
let j = arr.length;
while (i < j) {
const k = Math.floor((i + j) / 2);
if (predicate(arr[k])) {
i = k + 1;
} else {
j = k;
}
}
return i - 1;
}
/**
* @returns arr.length if predicate is false for all items
*/
function findFirstIdxMonotonous<T>(arr: T[], predicate: (item: T) => boolean): number {
let i = 0;
let j = arr.length;
while (i < j) {
const k = Math.floor((i + j) / 2);
if (predicate(arr[k])) {
j = k;
} else {
i = k + 1;
}
}
return i;
}
function isWordChar(charCode: number): boolean {
+31 -1
View File
@@ -16,7 +16,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editOperation';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { LanguageId } from 'vs/editor/common/encodedTokenAttributes';
import * as model from 'vs/editor/common/model';
import { TokenizationRegistry as TokenizationRegistryImpl } from 'vs/editor/common/tokenizationRegistry';
@@ -2033,3 +2033,33 @@ export interface DocumentOnDropEditProvider {
provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): ProviderResult<DocumentOnDropEdit>;
}
export interface RelatedContextItem {
readonly uri: URI;
readonly range: IRange;
}
export interface MappedEditsContext {
selections: ISelection[];
related: RelatedContextItem[];
}
export interface MappedEditsProvider {
/**
* Provider maps code blocks from the chat into a workspace edit.
*
* @param document The document to provide mapped edits for.
* @param codeBlocks Code blocks that come from an LLM's reply.
* "Insert at cursor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them.
* @param context The context for providing mapped edits.
* @param token A cancellation token.
* @returns A provider result of text edits.
*/
provideMappedEdits(
document: model.ITextModel,
codeBlocks: string[],
context: MappedEditsContext,
token: CancellationToken
): Promise<WorkspaceEdit | null>;
}
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { LanguageFeatureRegistry, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentPasteEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentPasteEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, MappedEditsProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ILanguageFeaturesService = createDecorator<ILanguageFeaturesService>('ILanguageFeaturesService');
@@ -71,6 +71,8 @@ export interface ILanguageFeaturesService {
readonly documentOnDropEditProvider: LanguageFeatureRegistry<DocumentOnDropEditProvider>;
readonly mappedEditsProvider: LanguageFeatureRegistry<MappedEditsProvider>;
// --
setNotebookTypeResolver(resolver: NotebookInfoResolver | undefined): void;
@@ -5,7 +5,7 @@
import { URI } from 'vs/base/common/uri';
import { LanguageFeatureRegistry, NotebookInfo, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DocumentPasteEditProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DocumentPasteEditProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider, MappedEditsProvider } from 'vs/editor/common/languages';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
@@ -42,6 +42,7 @@ export class LanguageFeaturesService implements ILanguageFeaturesService {
readonly documentSemanticTokensProvider = new LanguageFeatureRegistry<DocumentSemanticTokensProvider>(this._score.bind(this));
readonly documentOnDropEditProvider = new LanguageFeatureRegistry<DocumentOnDropEditProvider>(this._score.bind(this));
readonly documentPasteEditProvider = new LanguageFeatureRegistry<DocumentPasteEditProvider>(this._score.bind(this));
readonly mappedEditsProvider: LanguageFeatureRegistry<MappedEditsProvider> = new LanguageFeatureRegistry<MappedEditsProvider>(this._score.bind(this));
private _notebookTypeResolver?: NotebookInfoResolver;
@@ -318,7 +318,8 @@ export enum EditorOption {
layoutInfo = 142,
wrappingInfo = 143,
defaultColorDecorators = 144,
colorDecoratorsActivatedOn = 145
colorDecoratorsActivatedOn = 145,
inlineCompletionsAccessibilityVerbose = 146
}
/**
@@ -12,9 +12,9 @@ import { instantiateTextModel } from 'vs/editor/test/common/testTextModel';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
suite('bracket matching', () => {
let disposables: DisposableStore;
let instantiationService: TestInstantiationService;
let languageConfigurationService: ILanguageConfigurationService;
@@ -31,6 +31,8 @@ suite('bracket matching', () => {
disposables.dispose();
});
ensureNoDisposablesAreLeakedInTestSuite();
function createTextModelWithBrackets(text: string) {
const languageId = 'bracketMode';
disposables.add(languageService.registerLanguage({ id: languageId }));
@@ -6,11 +6,14 @@
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorCommand, EditorContributionInstantiation, ServicesAccessor, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { editorConfigurationBaseNode } from 'vs/editor/common/config/editorConfigurationSchema';
import { registerEditorFeature } from 'vs/editor/common/editorFeatures';
import { DefaultDropProvidersFeature } from 'vs/editor/contrib/dropOrPasteInto/browser/defaultProviders';
import * as nls from 'vs/nls';
import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { DropIntoEditorController, changeDropTypeCommandId, dropWidgetVisibleCtx } from './dropIntoEditorController';
import { Registry } from 'vs/platform/registry/common/platform';
import { DropIntoEditorController, changeDropTypeCommandId, defaultProviderConfig, dropWidgetVisibleCtx } from './dropIntoEditorController';
registerEditorContribution(DropIntoEditorController.ID, DropIntoEditorController, EditorContributionInstantiation.BeforeFirstInteraction);
@@ -32,3 +35,18 @@ registerEditorCommand(new class extends EditorCommand {
});
registerEditorFeature(DefaultDropProvidersFeature);
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
...editorConfigurationBaseNode,
properties: {
[defaultProviderConfig]: {
type: 'object',
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
description: nls.localize('defaultProviderDescription', "Configures the default drop provider to use for content of a given mime type."),
default: {},
additionalProperties: {
type: 'string',
},
},
}
});
@@ -5,7 +5,7 @@
import { coalesce } from 'vs/base/common/arrays';
import { CancelablePromise, createCancelablePromise, raceCancellation } from 'vs/base/common/async';
import { VSDataTransfer } from 'vs/base/common/dataTransfer';
import { VSDataTransfer, matchesMimeType } from 'vs/base/common/dataTransfer';
import { Disposable } from 'vs/base/common/lifecycle';
import { toExternalVSDataTransfer } from 'vs/editor/browser/dnd';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -13,7 +13,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IPosition } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { DocumentOnDropEditProvider } from 'vs/editor/common/languages';
import { DocumentOnDropEdit, DocumentOnDropEditProvider } from 'vs/editor/common/languages';
import { ITextModel } from 'vs/editor/common/model';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { DraggedTreeItemsIdentifier } from 'vs/editor/common/services/treeViewsDnd';
@@ -21,11 +21,14 @@ import { ITreeViewsDnDService } from 'vs/editor/common/services/treeViewsDndServ
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState';
import { InlineProgressManager } from 'vs/editor/contrib/inlineProgress/browser/inlineProgress';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { LocalSelectionTransfer } from 'vs/platform/dnd/browser/dnd';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { sortEditsByYieldTo } from './edit';
import { PostEditWidgetManager } from './postEditWidget';
import { sortEditsByYieldTo as sortEditsByYieldTo } from './edit';
export const defaultProviderConfig = 'editor.experimental.dropIntoEditor.defaultProvider';
export const changeDropTypeCommandId = 'editor.changeDropType';
@@ -49,6 +52,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
constructor(
editor: ICodeEditor,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService private readonly _configService: IConfigurationService,
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
@ITreeViewsDnDService private readonly _treeViewsDragAndDropService: ITreeViewsDnDService
) {
@@ -108,9 +112,10 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
}
if (edits.length) {
const activeEditIndex = this.getInitialActiveEditIndex(model, edits);
const canShowWidget = editor.getOption(EditorOption.dropIntoEditor).showDropSelector === 'afterDrop';
// Pass in the parent token here as it tracks cancelling the entire drop operation
await this._postDropWidgetManager.applyEditAndShowIfNeeded([Range.fromPositions(position)], { activeEditIndex: 0, allEdits: edits }, canShowWidget, token);
await this._postDropWidgetManager.applyEditAndShowIfNeeded([Range.fromPositions(position)], { activeEditIndex, allEdits: edits }, canShowWidget, token);
}
} finally {
tokenSource.dispose();
@@ -124,7 +129,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
this._currentOperation = p;
}
private async getDropEdits(providers: DocumentOnDropEditProvider[], model: ITextModel, position: IPosition, dataTransfer: VSDataTransfer, tokenSource: EditorStateCancellationTokenSource) {
private async getDropEdits(providers: readonly DocumentOnDropEditProvider[], model: ITextModel, position: IPosition, dataTransfer: VSDataTransfer, tokenSource: EditorStateCancellationTokenSource) {
const results = await raceCancellation(Promise.all(providers.map(async provider => {
try {
const edit = await provider.provideDocumentOnDropEdits(model, position, dataTransfer, tokenSource.token);
@@ -136,9 +141,22 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
}
return undefined;
})), tokenSource.token);
const edits = coalesce(results ?? []);
sortEditsByYieldTo(edits);
return edits;
return sortEditsByYieldTo(edits);
}
private getInitialActiveEditIndex(model: ITextModel, edits: ReadonlyArray<DocumentOnDropEdit & { readonly providerId?: string }>) {
const preferredProviders = this._configService.getValue<Record<string, string>>(defaultProviderConfig, { resource: model.uri });
for (const [configMime, desiredId] of Object.entries(preferredProviders)) {
const editIndex = edits.findIndex(edit =>
desiredId === edit.providerId
&& edit.handledMimeType && matchesMimeType(configMime, [edit.handledMimeType]));
if (editIndex >= 0) {
return editIndex;
}
}
return 0;
}
private async extractDataTransferData(dragEvent: DragEvent): Promise<VSDataTransfer> {
@@ -32,21 +32,68 @@ export function sortEditsByYieldTo<T extends {
readonly providerId: string | undefined;
readonly handledMimeType?: string;
readonly yieldTo?: readonly DropYieldTo[];
}>(edits: T[]): void {
}>(edits: readonly T[]): T[] {
function yieldsTo(yTo: DropYieldTo, other: T): boolean {
return ('providerId' in yTo && yTo.providerId === other.providerId)
|| ('mimeType' in yTo && yTo.mimeType === other.handledMimeType);
}
edits.sort((a, b) => {
if (a.yieldTo?.some(yTo => yieldsTo(yTo, b))) {
return 1;
// Build list of nodes each node yields to
const yieldsToMap = new Map<T, T[]>();
for (const edit of edits) {
for (const yTo of edit.yieldTo ?? []) {
for (const other of edits) {
if (other === edit) {
continue;
}
if (yieldsTo(yTo, other)) {
let arr = yieldsToMap.get(edit);
if (!arr) {
arr = [];
yieldsToMap.set(edit, arr);
}
arr.push(other);
}
}
}
}
if (!yieldsToMap.size) {
return Array.from(edits);
}
// Topological sort
const visited = new Set<T>();
const tempStack: T[] = [];
function visit(nodes: T[]): T[] {
if (!nodes.length) {
return [];
}
if (b.yieldTo?.some(yTo => yieldsTo(yTo, a))) {
return -1;
const node = nodes[0];
if (tempStack.includes(node)) {
console.warn(`Yield to cycle detected for ${node.providerId}`);
return nodes;
}
return 0;
});
if (visited.has(node)) {
return visit(nodes.slice(1));
}
let pre: T[] = [];
const yTo = yieldsToMap.get(node);
if (yTo) {
tempStack.push(node);
pre = visit(yTo);
tempStack.pop();
}
visited.add(node);
return [...pre, node, ...visit(nodes.slice(1))];
}
return visit(Array.from(edits));
}
@@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { DocumentOnDropEdit } from 'vs/editor/common/languages';
import { sortEditsByYieldTo } from 'vs/editor/contrib/dropOrPasteInto/browser/edit';
type DropEdit = DocumentOnDropEdit & { providerId: string | undefined };
function createTestEdit(providerId: string, args?: Partial<DropEdit>): DropEdit {
return {
label: '',
insertText: '',
providerId,
...args,
};
}
suite('sortEditsByYieldTo', () => {
test('Should noop for empty edits', () => {
const edits: DropEdit[] = [];
assert.deepStrictEqual(sortEditsByYieldTo(edits), []);
});
test('Yielded to edit should get sorted after target', () => {
const edits: DropEdit[] = [
createTestEdit('a', { yieldTo: [{ providerId: 'b' }] }),
createTestEdit('b'),
];
assert.deepStrictEqual(sortEditsByYieldTo(edits).map(x => x.providerId), ['b', 'a']);
});
test('Should handle chain of yield to', () => {
{
const edits: DropEdit[] = [
createTestEdit('c', { yieldTo: [{ providerId: 'a' }] }),
createTestEdit('a', { yieldTo: [{ providerId: 'b' }] }),
createTestEdit('b'),
];
assert.deepStrictEqual(sortEditsByYieldTo(edits).map(x => x.providerId), ['b', 'a', 'c']);
}
{
const edits: DropEdit[] = [
createTestEdit('a', { yieldTo: [{ providerId: 'b' }] }),
createTestEdit('c', { yieldTo: [{ providerId: 'a' }] }),
createTestEdit('b'),
];
assert.deepStrictEqual(sortEditsByYieldTo(edits).map(x => x.providerId), ['b', 'a', 'c']);
}
});
test(`Should not reorder when yield to isn't used`, () => {
const edits: DropEdit[] = [
createTestEdit('c', { yieldTo: [{ providerId: 'x' }] }),
createTestEdit('a', { yieldTo: [{ providerId: 'y' }] }),
createTestEdit('b'),
];
assert.deepStrictEqual(sortEditsByYieldTo(edits).map(x => x.providerId), ['c', 'a', 'b']);
});
});
@@ -908,13 +908,13 @@ class UnfoldAllRegionsAction extends FoldingAction<void> {
}
}
class FoldAllRegionsExceptAction extends FoldingAction<void> {
class FoldAllExceptAction extends FoldingAction<void> {
constructor() {
super({
id: 'editor.foldAllExcept',
label: nls.localize('foldAllExcept.label', "Fold All Regions Except Selected"),
alias: 'Fold All Regions Except Selected',
label: nls.localize('foldAllExcept.label', "Fold All Except Selected"),
alias: 'Fold All Except Selected',
precondition: CONTEXT_FOLDING_ENABLED,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
@@ -931,13 +931,13 @@ class FoldAllRegionsExceptAction extends FoldingAction<void> {
}
class UnfoldAllRegionsExceptAction extends FoldingAction<void> {
class UnfoldAllExceptAction extends FoldingAction<void> {
constructor() {
super({
id: 'editor.unfoldAllExcept',
label: nls.localize('unfoldAllExcept.label', "Unfold All Regions Except Selected"),
alias: 'Unfold All Regions Except Selected',
label: nls.localize('unfoldAllExcept.label', "Unfold All Except Selected"),
alias: 'Unfold All Except Selected',
precondition: CONTEXT_FOLDING_ENABLED,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
@@ -1194,8 +1194,8 @@ registerEditorAction(UnfoldAllAction);
registerEditorAction(FoldAllBlockCommentsAction);
registerEditorAction(FoldAllRegionsAction);
registerEditorAction(UnfoldAllRegionsAction);
registerEditorAction(FoldAllRegionsExceptAction);
registerEditorAction(UnfoldAllRegionsExceptAction);
registerEditorAction(FoldAllExceptAction);
registerEditorAction(UnfoldAllExceptAction);
registerEditorAction(ToggleFoldAction);
registerEditorAction(GotoParentFoldAction);
registerEditorAction(GotoPreviousFoldAction);
@@ -854,7 +854,6 @@ export class ContentHoverWidget extends ResizableContentWidget {
width = dom.getTotalWidth(containerDomNode);
this._contentWidth = width;
this._updateMinimumWidth();
this._updateResizableNodeMaxDimensions();
this._resizableNode.layout(height, width);
if (this._hasHorizontalScrollbar()) {
@@ -22,11 +22,13 @@ import { InlineCompletionContextKeys } from 'vs/editor/contrib/inlineCompletions
import { InlineCompletionsHintsWidget, InlineSuggestionHintsContentWidget } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget';
import { InlineCompletionsModel, VersionIdChangeReason } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel';
import { SuggestWidgetAdaptor } from 'vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider';
import { localize } from 'vs/nls';
import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
export class InlineCompletionsController extends Disposable {
static ID = 'editor.contrib.inlineCompletionsController';
@@ -73,11 +75,11 @@ export class InlineCompletionsController extends Disposable {
@ILanguageFeatureDebounceService private readonly debounceService: ILanguageFeatureDebounceService,
@ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService,
@IAudioCueService private readonly audioCueService: IAudioCueService,
@IKeybindingService private readonly _keybindingService: IKeybindingService
) {
super();
this._register(new InlineCompletionContextKeys(this.contextKeyService, this.model));
this._register(Event.runAndSubscribe(editor.onDidChangeModel, () => transaction(tx => {
/** @description onDidChangeModel */
this.model.set(undefined, tx);
@@ -147,7 +149,7 @@ export class InlineCompletionsController extends Disposable {
this._register(this.editor.onDidBlurEditorWidget(() => {
// This is a hidden setting very useful for debugging
if (this.configurationService.getValue('editor.inlineSuggest.keepOnBlur') ||
if (this.contextKeyService.getContextKeyValue<boolean>('accessibleViewIsShown') || this.configurationService.getValue('editor.inlineSuggest.keepOnBlur') ||
editor.getOption(EditorOption.inlineSuggest).keepOnBlur) {
return;
}
@@ -190,13 +192,29 @@ export class InlineCompletionsController extends Disposable {
const lineText = model.textModel.getLineContent(state.ghostText.lineNumber);
this.audioCueService.playAudioCue(AudioCue.inlineSuggestion).then(() => {
if (this.editor.getOption(EditorOption.screenReaderAnnounceInlineSuggestion)) {
alert(state.ghostText.renderForScreenReader(lineText));
this.provideScreenReaderUpdate(state.ghostText.renderForScreenReader(lineText));
}
});
}
}));
this._register(new InlineCompletionsHintsWidget(this.editor, this.model, this.instantiationService));
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('accessibility.verbosity.inlineCompletions')) {
this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this.configurationService.getValue('accessibility.verbosity.inlineCompletions') });
}
}));
this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this.configurationService.getValue('accessibility.verbosity.inlineCompletions') });
}
private provideScreenReaderUpdate(content: string): void {
const accessibleViewShowing = this.contextKeyService.getContextKeyValue<boolean>('accessibleViewIsShown');
const accessibleViewKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleView');
let hint: string | undefined;
if (!accessibleViewShowing && accessibleViewKeybinding && this.editor.getOption(EditorOption.inlineCompletionsAccessibilityVerbose)) {
hint = localize('showAccessibleViewHint', "Inspect this in the accessible view ({0})", accessibleViewKeybinding.getAriaLabel());
}
hint ? alert(content + ', ' + hint) : alert(content);
}
/**
@@ -953,8 +953,8 @@ export class TransposeAction extends EditorAction {
constructor() {
super({
id: 'editor.action.transpose',
label: nls.localize('editor.transpose', "Transpose characters around the cursor"),
alias: 'Transpose characters around the cursor',
label: nls.localize('editor.transpose', "Transpose Characters around the Cursor"),
alias: 'Transpose Characters around the Cursor',
precondition: EditorContextKeys.writable
});
}
@@ -117,7 +117,7 @@ export class MoveLinesCommand implements ICommand {
const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber));
const newSpaceCnt = movingLineMatchResult + indentUtils.getSpaceCnt(oldIndentation, tabSize);
const newIndentation = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
insertingText = newIndentation + this.trimLeft(movingLineText);
insertingText = newIndentation + this.trimStart(movingLineText);
} else {
// no enter rule matches, let's check indentatin rules then.
virtualModel.getLineContent = (lineNumber: number) => {
@@ -141,7 +141,7 @@ export class MoveLinesCommand implements ICommand {
const oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
const newIndentation = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
insertingText = newIndentation + this.trimLeft(movingLineText);
insertingText = newIndentation + this.trimStart(movingLineText);
}
}
}
@@ -272,7 +272,7 @@ export class MoveLinesCommand implements ICommand {
enterPrefix = indentConverter.unshiftIndent(enter.indentation) + enter.appendText;
}
const movingLineText = model.getLineContent(line);
if (this.trimLeft(movingLineText).indexOf(this.trimLeft(enterPrefix)) >= 0) {
if (this.trimStart(movingLineText).indexOf(this.trimStart(enterPrefix)) >= 0) {
const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(line));
let newIndentation = strings.getLeadingWhitespace(enterPrefix);
const indentMetadataOfMovelingLine = getIndentMetadata(model, line, this._languageConfigurationService);
@@ -354,7 +354,7 @@ export class MoveLinesCommand implements ICommand {
return this.parseEnterResult(model, indentConverter, tabSize, line, enter);
}
private trimLeft(str: string) {
private trimStart(str: string) {
return str.replace(/^\s+/, '');
}
@@ -33,7 +33,7 @@ export class ToggleStickyScroll extends Action2 {
},
menu: [
{ id: MenuId.CommandPalette },
{ id: MenuId.MenubarViewMenu, group: '5_editor', order: 2 },
{ id: MenuId.MenubarAppearanceMenu, group: '4_editor', order: 3 },
{ id: MenuId.StickyScrollContext }
]
});
@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { IActiveCodeEditor, ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { IActiveCodeEditor, ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { EditorOption, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
import { StickyScrollWidget, StickyScrollWidgetState } from './stickyScrollWidget';
@@ -26,7 +26,7 @@ import { ILanguageConfigurationService } from 'vs/editor/common/languages/langua
import { ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce';
import * as dom from 'vs/base/browser/dom';
import { StickyRange } from 'vs/editor/contrib/stickyScroll/browser/stickyScrollElement';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
export interface IStickyScrollController {
get stickyScrollCandidateProvider(): IStickyLineCandidateProvider;
@@ -63,6 +63,8 @@ export class StickyScrollController extends Disposable implements IEditorContrib
private _focused = false;
private _positionRevealed = false;
private _onMouseDown = false;
private _endLineNumbers: number[] = [];
private _showEndForLine: number | null = null;
constructor(
private readonly _editor: ICodeEditor,
@@ -79,7 +81,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib
this._register(this._stickyScrollWidget);
this._register(this._stickyLineCandidateProvider);
this._widgetState = new StickyScrollWidgetState([], 0);
this._widgetState = new StickyScrollWidgetState([], [], 0);
this._readConfiguration();
const stickyScrollDomNode = this._stickyScrollWidget.getDomNode();
this._register(this._editor.onDidChangeConfiguration(e => {
@@ -183,8 +185,19 @@ export class StickyScrollController extends Disposable implements IEditorContrib
}
private _revealPosition(position: IPosition): void {
this._reveaInEditor(position, () => this._editor.revealPosition(position));
}
private _revealLineInCenterIfOutsideViewport(position: IPosition): void {
this._reveaInEditor(position, () => this._editor.revealLineInCenterIfOutsideViewport(position.lineNumber, ScrollType.Smooth));
}
private _reveaInEditor(position: IPosition, revealFunction: () => void): void {
if (this._focused) {
this._disposeFocusStickyScrollStore();
}
this._positionRevealed = true;
this._editor.revealPosition(position);
revealFunction();
this._editor.setSelection(Range.fromPositions(position));
this._editor.focus();
}
@@ -223,35 +236,59 @@ export class StickyScrollController extends Disposable implements IEditorContrib
};
};
this._register(this._editor.onMouseUp((mouseEvent: IEditorMouseEvent) => {
if (mouseEvent.target.type !== MouseTargetType.OVERLAY_WIDGET || mouseEvent.target.detail !== this._stickyScrollWidget.getId()) {
// not hovering over our widget
return;
}
if (mouseEvent.event.ctrlKey || mouseEvent.event.shiftKey || mouseEvent.event.altKey || mouseEvent.event.metaKey) {
const stickyScrollWidgetDomNode = this._stickyScrollWidget.getDomNode();
this._register(dom.addStandardDisposableListener(stickyScrollWidgetDomNode, dom.EventType.CLICK, (mouseEvent: IMouseEvent) => {
if (mouseEvent.ctrlKey || mouseEvent.altKey || mouseEvent.metaKey) {
// modifier pressed
return;
}
if (!mouseEvent.event.leftButton) {
if (!mouseEvent.leftButton) {
// not left click
return;
}
let position = this._stickyScrollWidget.getEditorPositionFromNode(mouseEvent.target.element);
if (mouseEvent.shiftKey) {
// shift click
const lineIndex = this._stickyScrollWidget.getStickyLineIndexFromChildDomNode(mouseEvent.target);
if (lineIndex === null) {
return;
}
const position = new Position(this._endLineNumbers[lineIndex], 1);
this._revealLineInCenterIfOutsideViewport(position);
return;
}
// normal click
let position = this._stickyScrollWidget.getEditorPositionFromNode(mouseEvent.target);
if (!position) {
const lineNumber = this._stickyScrollWidget.getLineNumberFromChildDomNode(mouseEvent.target.element);
const lineNumber = this._stickyScrollWidget.getLineNumberFromChildDomNode(mouseEvent.target);
if (lineNumber === null) {
// not hovering a sticky scroll line
return;
}
position = new Position(lineNumber, 1);
}
if (this._focused) {
this._disposeFocusStickyScrollStore();
}
this._revealPosition(position);
}));
this._register(dom.addStandardDisposableListener(stickyScrollWidgetDomNode, dom.EventType.MOUSE_MOVE, (mouseEvent: IMouseEvent) => {
if (mouseEvent.shiftKey) {
const currentEndForLineIndex = this._stickyScrollWidget.getStickyLineIndexFromChildDomNode(mouseEvent.target);
if (currentEndForLineIndex === null || this._showEndForLine !== null && this._showEndForLine === currentEndForLineIndex) {
return;
}
this._showEndForLine = currentEndForLineIndex;
this._renderStickyScroll();
return;
}
if (this._showEndForLine !== null) {
this._showEndForLine = null;
this._renderStickyScroll();
}
}));
this._register(dom.addDisposableListener(stickyScrollWidgetDomNode, dom.EventType.MOUSE_LEAVE, (e) => {
if (this._showEndForLine !== null) {
this._showEndForLine = null;
this._renderStickyScroll();
}
}));
this._register(gesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, _keyboardEvent]) => {
const mouseTarget = getMouseEventTarget(mouseEvent);
@@ -344,18 +381,25 @@ export class StickyScrollController extends Disposable implements IEditorContrib
this._editor.addOverlayWidget(this._stickyScrollWidget);
this._sessionStore.add(this._editor.onDidScrollChange((e) => {
if (e.scrollTopChanged) {
this._showEndForLine = null;
this._renderStickyScroll();
}
}));
this._sessionStore.add(this._editor.onDidLayoutChange(() => this._onDidResize()));
this._sessionStore.add(this._editor.onDidChangeModelTokens((e) => this._onTokensChange(e)));
this._sessionStore.add(this._stickyLineCandidateProvider.onDidChangeStickyScroll(() => this._renderStickyScroll()));
this._sessionStore.add(this._stickyLineCandidateProvider.onDidChangeStickyScroll(() => {
this._showEndForLine = null;
this._renderStickyScroll();
}));
this._enabled = true;
}
const lineNumberOption = this._editor.getOption(EditorOption.lineNumbers);
if (lineNumberOption.renderType === RenderLineNumbersType.Relative) {
this._sessionStore.add(this._editor.onDidChangeCursorPosition(() => this._renderStickyScroll()));
this._sessionStore.add(this._editor.onDidChangeCursorPosition(() => {
this._showEndForLine = null;
this._renderStickyScroll();
}));
}
}
@@ -392,7 +436,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib
const stickyLineVersion = this._stickyLineCandidateProvider.getVersionId();
if (stickyLineVersion === undefined || stickyLineVersion === model.getVersionId()) {
this._widgetState = this.findScrollWidgetState();
this._stickyScrollVisibleContextKey.set(!(this._widgetState.lineNumbers.length === 0));
this._stickyScrollVisibleContextKey.set(!(this._widgetState.startLineNumbers.length === 0));
if (!this._focused) {
this._stickyScrollWidget.setState(this._widgetState);
@@ -430,7 +474,8 @@ export class StickyScrollController extends Disposable implements IEditorContrib
const maxNumberStickyLines = Math.min(this._maxStickyLines, this._editor.getOption(EditorOption.stickyScroll).maxLineCount);
const scrollTop: number = this._editor.getScrollTop();
let lastLineRelativePosition: number = 0;
const lineNumbers: number[] = [];
const startLineNumbers: number[] = [];
const endLineNumbers: number[] = [];
const arrayVisibleRanges = this._editor.getVisibleRanges();
if (arrayVisibleRanges.length !== 0) {
const fullVisibleRange = new StickyRange(arrayVisibleRanges[0].startLineNumber, arrayVisibleRanges[arrayVisibleRanges.length - 1].endLineNumber);
@@ -448,20 +493,23 @@ export class StickyScrollController extends Disposable implements IEditorContrib
const bottomOfEndLine = this._editor.getBottomForLineNumber(end) - scrollTop;
if (topOfElementAtDepth > topOfEndLine && topOfElementAtDepth <= bottomOfEndLine) {
lineNumbers.push(start);
startLineNumbers.push(start);
endLineNumbers.push(end + 1);
lastLineRelativePosition = bottomOfEndLine - bottomOfElementAtDepth;
break;
}
else if (bottomOfElementAtDepth > bottomOfBeginningLine && bottomOfElementAtDepth <= bottomOfEndLine) {
lineNumbers.push(start);
startLineNumbers.push(start);
endLineNumbers.push(end + 1);
}
if (lineNumbers.length === maxNumberStickyLines) {
if (startLineNumbers.length === maxNumberStickyLines) {
break;
}
}
}
}
return new StickyScrollWidgetState(lineNumbers, lastLineRelativePosition);
this._endLineNumbers = endLineNumbers;
return new StickyScrollWidgetState(startLineNumbers, endLineNumbers, lastLineRelativePosition, this._showEndForLine);
}
override dispose(): void {
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DisposableStore } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/browser/outlineModel';
@@ -45,18 +45,20 @@ export interface IStickyModelProvider {
update(textModel: ITextModel, textModelVersionId: number, token: CancellationToken): Promise<StickyModel | null>;
}
export class StickyModelProvider implements IStickyModelProvider {
export class StickyModelProvider extends Disposable implements IStickyModelProvider {
private _modelProviders: IStickyModelCandidateProvider<any>[] = [];
private _modelPromise: CancelablePromise<any | null> | null = null;
private _updateScheduler: Delayer<StickyModel | null> = new Delayer<StickyModel | null>(300);
private readonly _store: DisposableStore;
private _updateScheduler: Delayer<StickyModel | null> = this._register(new Delayer<StickyModel | null>(300));
private readonly _updateOperation: DisposableStore = this._register(new DisposableStore());
constructor(
private readonly _editor: ICodeEditor,
@ILanguageConfigurationService readonly _languageConfigurationService: ILanguageConfigurationService,
@ILanguageFeaturesService readonly _languageFeaturesService: ILanguageFeaturesService,
defaultModel: string) {
defaultModel: string
) {
super();
const stickyModelFromCandidateOutlineProvider = new StickyModelFromCandidateOutlineProvider(_languageFeaturesService);
const stickyModelFromSyntaxFoldingProvider = new StickyModelFromCandidateSyntaxFoldingProvider(this._editor, _languageFeaturesService);
@@ -76,8 +78,6 @@ export class StickyModelProvider implements IStickyModelProvider {
this._modelProviders.push(stickyModelFromIndentationFoldingProvider);
break;
}
this._store = new DisposableStore();
}
private _cancelModelPromise(): void {
@@ -89,11 +89,11 @@ export class StickyModelProvider implements IStickyModelProvider {
public async update(textModel: ITextModel, textModelVersionId: number, token: CancellationToken): Promise<StickyModel | null> {
this._store.clear();
this._store.add({
this._updateOperation.clear();
this._updateOperation.add({
dispose: () => {
this._cancelModelPromise();
this._updateScheduler?.cancel();
this._updateScheduler.cancel();
}
});
this._cancelModelPromise();
@@ -113,7 +113,7 @@ export class StickyModelProvider implements IStickyModelProvider {
}
switch (status) {
case Status.CANCELED:
this._store.clear();
this._updateOperation.clear();
return null;
case Status.VALID:
return modelProvider.stickyModel;

Some files were not shown because too many files have changed in this diff Show More