mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-15 00:14:20 +01:00
Merge branch 'main' into quarrelsome-planarian
This commit is contained in:
@@ -65,7 +65,6 @@ src/vs/code/** @bpasero @deepak1556
|
||||
src/vs/workbench/services/activity/** @bpasero
|
||||
src/vs/workbench/services/authentication/** @TylerLeonhardt
|
||||
src/vs/workbench/services/auxiliaryWindow/** @bpasero
|
||||
src/vs/workbench/services/chat/** @bpasero
|
||||
src/vs/workbench/services/contextmenu/** @bpasero
|
||||
src/vs/workbench/services/dialogs/** @alexr00 @bpasero
|
||||
src/vs/workbench/services/editor/** @bpasero
|
||||
@@ -100,15 +99,6 @@ src/vs/workbench/electron-browser/** @bpasero
|
||||
src/vs/workbench/contrib/authentication/** @TylerLeonhardt
|
||||
src/vs/workbench/contrib/files/** @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @roblourens
|
||||
src/vs/workbench/contrib/chat/browser/chatSetup/** @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/chatStatus/** @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/chatViewPane.ts @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/media/chatViewPane.css @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/chatViewTitleControl.ts @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/media/chatViewTitleControl.css @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/chatManagement/chatUsageWidget.ts @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/chatManagement/media/chatUsageWidget.css @bpasero
|
||||
src/vs/workbench/contrib/chat/browser/agentSessions/** @bpasero
|
||||
src/vs/workbench/contrib/localization/** @TylerLeonhardt
|
||||
src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @TylerLeonhardt
|
||||
src/vs/workbench/contrib/scm/** @lszomoru
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
agent: agent
|
||||
description: 'Fix an unhandled error from the VS Code error telemetry dashboard'
|
||||
argument-hint: Paste the GitHub issue URL for the error-telemetry issue
|
||||
tools: ['edit', 'search', 'execute/getTerminalOutput', 'execute/runInTerminal', 'read/terminalLastCommand', 'read/terminalSelection', 'execute/createAndRunTask', 'execute/runTask', 'read/getTaskOutput', 'search/usages', 'read/problems', 'search/changes', 'execute/testFailure', 'todo', 'execute/runTests', 'web/fetch', 'web/githubRepo']
|
||||
---
|
||||
|
||||
The user has given you a GitHub issue URL for an unhandled error from the VS Code error telemetry dashboard. Fetch the issue to retrieve its details (error message, stack trace, hit count, affected users).
|
||||
|
||||
Follow the `fix-errors` skill guidelines to fix this error. Key principles:
|
||||
|
||||
1. **Do NOT fix at the crash site.** Do not add guards, try/catch, or fallback values at the bottom of the stack trace. That only masks the problem.
|
||||
2. **Trace the data flow upward** through the call stack to find the producer of invalid data.
|
||||
3. **If the producer is cross-process** (e.g., IPC) and cannot be identified from the stack alone, **enrich the error message** with diagnostic context (data type, truncated value, operation name) so the next telemetry cycle reveals the source. Do NOT silently swallow the error.
|
||||
4. **If the producer is identifiable**, fix it directly.
|
||||
|
||||
After making changes, check for compilation errors via the build task and run relevant unit tests.
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
name: fix-errors
|
||||
description: Guidelines for fixing unhandled errors from the VS Code error telemetry dashboard. Use when investigating error-telemetry issues with stack traces, error messages, and hit/user counts. Covers tracing data flow through call stacks, identifying producers of invalid data vs. consumers that crash, enriching error messages for telemetry diagnosis, and avoiding common anti-patterns like silently swallowing errors.
|
||||
---
|
||||
|
||||
When fixing an unhandled error from the telemetry dashboard, the issue typically contains an error message, a stack trace, hit count, and affected user count.
|
||||
|
||||
## Approach
|
||||
|
||||
### 1. Do NOT fix at the crash site
|
||||
|
||||
The error manifests at a specific line in the stack trace, but **the fix almost never belongs there**. Fixing at the crash site (e.g., adding a `typeof` guard in a `revive()` function, swallowing the error with a try/catch, or returning a fallback value) only masks the real problem. The invalid data still flows through the system and will cause failures elsewhere.
|
||||
|
||||
### 2. Trace the data flow upward through the call stack
|
||||
|
||||
Read each frame in the stack trace from bottom to top. For each frame, understand:
|
||||
- What data is being passed and what is expected
|
||||
- Where that data originated (IPC message, extension API call, storage, user input, etc.)
|
||||
- Whether the data could have been corrupted or malformed at that point
|
||||
|
||||
The goal is to find the **producer of invalid data**, not the consumer that crashes on it.
|
||||
|
||||
### 3. When the producer cannot be identified from the stack alone
|
||||
|
||||
Sometimes the stack trace only shows the receiving/consuming side (e.g., an IPC server handler). The sending side is in a different process and not in the stack. In this case:
|
||||
|
||||
- **Enrich the error message** at the consuming site with diagnostic context: the type of the invalid data, a truncated representation of its value, and which operation/command received it. This information flows into the error telemetry dashboard automatically via the unhandled error pipeline.
|
||||
- **Do NOT silently swallow the error** — let it still throw so it remains visible in telemetry, but with enough context to identify the sender in the next telemetry cycle.
|
||||
- Consider adding the same enrichment to the low-level validation function that throws (e.g., include the invalid value in the error message) so the telemetry captures it regardless of call site.
|
||||
|
||||
### 4. When the producer IS identifiable
|
||||
|
||||
Fix the producer directly:
|
||||
- Validate or sanitize data before sending it over IPC / storing it / passing it to APIs
|
||||
- Ensure serialization/deserialization preserves types correctly (e.g., URI objects should serialize as `UriComponents` objects, not as strings)
|
||||
|
||||
## Example
|
||||
|
||||
Given a stack trace like:
|
||||
```
|
||||
at _validateUri (uri.ts) ← validation throws
|
||||
at new Uri (uri.ts) ← constructor
|
||||
at URI.revive (uri.ts) ← revive assumes valid UriComponents
|
||||
at SomeChannel.call (ipc.ts) ← IPC handler receives arg from another process
|
||||
```
|
||||
|
||||
**Wrong fix**: Add a `typeof` guard in `URI.revive` to return `undefined` for non-object input. This silences the error but the caller still expects a valid URI and will fail later.
|
||||
|
||||
**Right fix (when producer is unknown)**: Enrich the error at the IPC handler level and in `_validateUri` itself to include the actual invalid value, so telemetry reveals what data is being sent and from where. Example:
|
||||
```typescript
|
||||
// In the IPC handler — validate before revive
|
||||
function reviveUri(data: UriComponents | URI | undefined | null, context: string): URI {
|
||||
if (data && typeof data !== 'object') {
|
||||
throw new Error(`[Channel] Invalid URI data for '${context}': type=${typeof data}, value=${String(data).substring(0, 100)}`);
|
||||
}
|
||||
// ...
|
||||
}
|
||||
|
||||
// In _validateUri — include the scheme value
|
||||
throw new Error(`[UriError]: Scheme contains illegal characters. scheme:"${ret.scheme.substring(0, 50)}" (len:${ret.scheme.length})`);
|
||||
```
|
||||
|
||||
**Right fix (when producer is known)**: Fix the code that sends malformed data. For example, if an authentication provider passes a stringified URI instead of a `UriComponents` object to a logger creation call, fix that call site to pass the proper object.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Prefer enriching error messages over adding try/catch guards
|
||||
- Truncate any user-controlled values included in error messages (to avoid PII and keep messages bounded)
|
||||
- Do not change the behavior of shared utility functions (like `URI.revive`) in ways that affect all callers — fix at the specific call site or producer
|
||||
- Run the relevant unit tests after making changes
|
||||
- Check for compilation errors via the build task before declaring work complete
|
||||
Vendored
+11
@@ -602,6 +602,17 @@
|
||||
"order": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Component Explorer",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:5199/___explorer",
|
||||
"preLaunchTask": "Launch Monaco Editor Vite",
|
||||
"presentation": {
|
||||
"group": "monaco",
|
||||
"order": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Monaco Editor - Self Contained Diff Editor",
|
||||
"type": "chrome",
|
||||
|
||||
Vendored
+48
-4
@@ -1,10 +1,40 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch-client-transpiled",
|
||||
"label": "Core - Transpile",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"group": "buildWatchers",
|
||||
"close": false
|
||||
},
|
||||
"problemMatcher": {
|
||||
"owner": "esbuild",
|
||||
"applyTo": "closedDocuments",
|
||||
"fileLocation": [
|
||||
"relative",
|
||||
"${workspaceFolder}/src"
|
||||
],
|
||||
"pattern": {
|
||||
"regexp": "^(.+?):(\\d+):(\\d+): ERROR: (.+)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"message": 4
|
||||
},
|
||||
"background": {
|
||||
"beginsPattern": "Starting transpilation...",
|
||||
"endsPattern": "Finished transpilation with"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch-clientd",
|
||||
"label": "Core - Build",
|
||||
"label": "Core - Typecheck",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
@@ -60,7 +90,8 @@
|
||||
{
|
||||
"label": "VS Code - Build",
|
||||
"dependsOn": [
|
||||
"Core - Build",
|
||||
"Core - Transpile",
|
||||
"Core - Typecheck",
|
||||
"Ext - Build"
|
||||
],
|
||||
"group": {
|
||||
@@ -69,10 +100,22 @@
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "kill-watch-client-transpiled",
|
||||
"label": "Kill Core - Transpile",
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
"group": "buildKillers",
|
||||
"close": true
|
||||
},
|
||||
"problemMatcher": "$tsc"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "kill-watch-clientd",
|
||||
"label": "Kill Core - Build",
|
||||
"label": "Kill Core - Typecheck",
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "never",
|
||||
@@ -96,7 +139,8 @@
|
||||
{
|
||||
"label": "Kill VS Code - Build",
|
||||
"dependsOn": [
|
||||
"Kill Core - Build",
|
||||
"Kill Core - Transpile",
|
||||
"Kill Core - Typecheck",
|
||||
"Kill Ext - Build"
|
||||
],
|
||||
"group": "build",
|
||||
|
||||
@@ -158,10 +158,6 @@ variables:
|
||||
name: "$(Date:yyyyMMdd).$(Rev:r) (${{ parameters.VSCODE_QUALITY }})"
|
||||
|
||||
resources:
|
||||
pipelines:
|
||||
- pipeline: vscode-7pm-kick-off
|
||||
source: 'VS Code 7PM Kick-Off'
|
||||
trigger: true
|
||||
repositories:
|
||||
- repository: 1esPipelines
|
||||
type: git
|
||||
|
||||
@@ -2,15 +2,11 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import withDefaults from '../shared.webpack.config.mjs';
|
||||
|
||||
export default withDefaults({
|
||||
context: import.meta.dirname,
|
||||
resolve: {
|
||||
mainFields: ['module', 'main']
|
||||
},
|
||||
entry: {
|
||||
extension: './src/extension.ts',
|
||||
}
|
||||
});
|
||||
/**
|
||||
* When `true`, self-hosting uses esbuild for fast transpilation (build/next)
|
||||
* and gulp-tsb only for type-checking (`noEmit`).
|
||||
*
|
||||
* When `false`, gulp-tsb does both transpilation and type-checking (old behavior).
|
||||
*/
|
||||
export const useEsbuildTranspile = true;
|
||||
@@ -166,7 +166,7 @@ const tasks = compilations.map(function (tsconfigFile) {
|
||||
const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, async () => {
|
||||
const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'], { dot: true }));
|
||||
const copyNonTs = util.streamToPromise(nonts.pipe(gulp.dest(out)));
|
||||
const tsgo = spawnTsgo(absolutePath, () => rewriteTsgoSourceMappingUrlsIfNeeded(false, out, baseUrl));
|
||||
const tsgo = spawnTsgo(absolutePath, { reporterId: 'extensions' }, () => rewriteTsgoSourceMappingUrlsIfNeeded(false, out, baseUrl));
|
||||
|
||||
await Promise.all([copyNonTs, tsgo]);
|
||||
}));
|
||||
@@ -175,7 +175,7 @@ const tasks = compilations.map(function (tsconfigFile) {
|
||||
const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'], { dot: true }));
|
||||
const watchInput = watcher(src, { ...srcOpts, ...{ readDelay: 200 } });
|
||||
const watchNonTs = watchInput.pipe(filter(['**', '!**/*.ts'], { dot: true })).pipe(gulp.dest(out));
|
||||
const tsgoStream = watchInput.pipe(util.debounce(() => createTsgoStream(absolutePath, () => rewriteTsgoSourceMappingUrlsIfNeeded(false, out, baseUrl)), 200));
|
||||
const tsgoStream = watchInput.pipe(util.debounce(() => createTsgoStream(absolutePath, { reporterId: 'extensions' }, () => rewriteTsgoSourceMappingUrlsIfNeeded(false, out, baseUrl)), 200));
|
||||
const watchStream = es.merge(nonts.pipe(gulp.dest(out)), watchNonTs, tsgoStream);
|
||||
|
||||
return watchStream;
|
||||
@@ -273,11 +273,33 @@ gulp.task(compileWebExtensionsTask);
|
||||
export const watchWebExtensionsTask = task.define('watch-web', () => buildWebExtensions(true));
|
||||
gulp.task(watchWebExtensionsTask);
|
||||
|
||||
async function buildWebExtensions(isWatch: boolean) {
|
||||
async function buildWebExtensions(isWatch: boolean): Promise<void> {
|
||||
const extensionsPath = path.join(root, 'extensions');
|
||||
const webpackConfigLocations = await nodeUtil.promisify(glob)(
|
||||
path.join(extensionsPath, '**', 'extension-browser.webpack.config.js'),
|
||||
|
||||
// Find all esbuild-browser.ts files
|
||||
const esbuildConfigLocations = await nodeUtil.promisify(glob)(
|
||||
path.join(extensionsPath, '**', 'esbuild-browser.ts'),
|
||||
{ ignore: ['**/node_modules'] }
|
||||
);
|
||||
return ext.webpackExtensions('packaging web extension', isWatch, webpackConfigLocations.map(configPath => ({ configPath })));
|
||||
|
||||
// Find all webpack configs, excluding those that will be esbuilt
|
||||
const esbuildExtensionDirs = new Set(esbuildConfigLocations.map(p => path.dirname(p)));
|
||||
const webpackConfigLocations = (await nodeUtil.promisify(glob)(
|
||||
path.join(extensionsPath, '**', 'extension-browser.webpack.config.js'),
|
||||
{ ignore: ['**/node_modules'] }
|
||||
)).filter(configPath => !esbuildExtensionDirs.has(path.dirname(configPath)));
|
||||
|
||||
const promises: Promise<unknown>[] = [];
|
||||
|
||||
// Esbuild for extensions
|
||||
if (esbuildConfigLocations.length > 0) {
|
||||
promises.push(ext.esbuildExtensions('packaging web extension (esbuild)', isWatch, esbuildConfigLocations.map(script => ({ script }))));
|
||||
}
|
||||
|
||||
// Run webpack for remaining extensions
|
||||
if (webpackConfigLocations.length > 0) {
|
||||
promises.push(ext.webpackExtensions('packaging web extension', isWatch, webpackConfigLocations.map(configPath => ({ configPath }))));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
+4
-1
@@ -13,6 +13,7 @@ import { compileExtensionMediaTask, compileExtensionsTask, watchExtensionsTask }
|
||||
import * as compilation from './lib/compilation.ts';
|
||||
import * as task from './lib/task.ts';
|
||||
import * as util from './lib/util.ts';
|
||||
import { useEsbuildTranspile } from './buildConfig.ts';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
@@ -32,7 +33,9 @@ gulp.task(transpileClientTask);
|
||||
const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), compilation.copyCodiconsTask, compilation.compileApiProposalNamesTask, compilation.compileTask('src', 'out', false)));
|
||||
gulp.task(compileClientTask);
|
||||
|
||||
const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), task.parallel(compilation.watchTask('out', false), compilation.watchApiProposalNamesTask, compilation.watchCodiconsTask)));
|
||||
const watchClientTask = useEsbuildTranspile
|
||||
? task.define('watch-client', task.parallel(compilation.watchTask('out', false, 'src', { noEmit: true }), compilation.watchApiProposalNamesTask, compilation.watchCodiconsTask))
|
||||
: task.define('watch-client', task.series(util.rimraf('out'), task.parallel(compilation.watchTask('out', false), compilation.watchApiProposalNamesTask, compilation.watchCodiconsTask)));
|
||||
gulp.task(watchClientTask);
|
||||
|
||||
// All
|
||||
|
||||
+131
-15
@@ -15,7 +15,7 @@ import electron from '@vscode/gulp-electron';
|
||||
import jsonEditor from 'gulp-json-editor';
|
||||
import * as util from './lib/util.ts';
|
||||
import { getVersion } from './lib/getVersion.ts';
|
||||
import { readISODate } from './lib/date.ts';
|
||||
import { readISODate, writeISODate } from './lib/date.ts';
|
||||
import * as task from './lib/task.ts';
|
||||
import buildfile from './buildfile.ts';
|
||||
import * as optimize from './lib/optimize.ts';
|
||||
@@ -30,9 +30,12 @@ import { createAsar } from './lib/asar.ts';
|
||||
import minimist from 'minimist';
|
||||
import { compileBuildWithoutManglingTask, compileBuildWithManglingTask } from './gulpfile.compile.ts';
|
||||
import { compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileAllExtensionsBuildTask, compileExtensionMediaBuildTask, cleanExtensionsBuildTask } from './gulpfile.extensions.ts';
|
||||
import { copyCodiconsTask } from './lib/compilation.ts';
|
||||
import { useEsbuildTranspile } from './buildConfig.ts';
|
||||
import { promisify } from 'util';
|
||||
import globCallback from 'glob';
|
||||
import rceditCallback from 'rcedit';
|
||||
import * as cp from 'child_process';
|
||||
|
||||
|
||||
const glob = promisify(globCallback);
|
||||
@@ -152,6 +155,81 @@ const bundleVSCodeTask = task.define('bundle-vscode', task.series(
|
||||
));
|
||||
gulp.task(bundleVSCodeTask);
|
||||
|
||||
// esbuild-based bundle tasks (drop-in replacement for bundle-vscode / minify-vscode)
|
||||
function runEsbuildTranspile(outDir: string, excludeTests: boolean): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const scriptPath = path.join(root, 'build/next/index.ts');
|
||||
const args = [scriptPath, 'transpile', '--out', outDir];
|
||||
if (excludeTests) {
|
||||
args.push('--exclude-tests');
|
||||
}
|
||||
|
||||
const proc = cp.spawn(process.execPath, args, {
|
||||
cwd: root,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
proc.on('error', reject);
|
||||
proc.on('close', code => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`esbuild transpile failed with exit code ${code} (outDir: ${outDir})`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function runEsbuildBundle(outDir: string, minify: boolean, nls: boolean, target: 'desktop' | 'server' | 'server-web' = 'desktop', sourceMapBaseUrl?: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// const tsxPath = path.join(root, 'build/node_modules/tsx/dist/cli.mjs');
|
||||
const scriptPath = path.join(root, 'build/next/index.ts');
|
||||
const args = [scriptPath, 'bundle', '--out', outDir, '--target', target];
|
||||
if (minify) {
|
||||
args.push('--minify');
|
||||
}
|
||||
if (nls) {
|
||||
args.push('--nls');
|
||||
}
|
||||
if (sourceMapBaseUrl) {
|
||||
args.push('--source-map-base-url', sourceMapBaseUrl);
|
||||
}
|
||||
|
||||
const proc = cp.spawn(process.execPath, args, {
|
||||
cwd: root,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
proc.on('error', reject);
|
||||
proc.on('close', code => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`esbuild bundle failed with exit code ${code} (outDir: ${outDir}, minify: ${minify}, nls: ${nls}, target: ${target})`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function runTsGoTypeCheck(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const proc = cp.spawn('tsgo', ['--project', 'src/tsconfig.json', '--noEmit', '--skipLibCheck'], {
|
||||
cwd: root,
|
||||
stdio: 'inherit',
|
||||
shell: true
|
||||
});
|
||||
|
||||
proc.on('error', reject);
|
||||
proc.on('close', code => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`tsgo typecheck failed with exit code ${code}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`;
|
||||
const minifyVSCodeTask = task.define('minify-vscode', task.series(
|
||||
bundleVSCodeTask,
|
||||
@@ -160,7 +238,7 @@ const minifyVSCodeTask = task.define('minify-vscode', task.series(
|
||||
));
|
||||
gulp.task(minifyVSCodeTask);
|
||||
|
||||
const coreCI = task.define('core-ci', task.series(
|
||||
const coreCIOld = task.define('core-ci-old', task.series(
|
||||
gulp.task('compile-build-with-mangling') as task.Task,
|
||||
task.parallel(
|
||||
gulp.task('minify-vscode') as task.Task,
|
||||
@@ -168,7 +246,28 @@ const coreCI = task.define('core-ci', task.series(
|
||||
gulp.task('minify-vscode-reh-web') as task.Task,
|
||||
)
|
||||
));
|
||||
gulp.task(coreCI);
|
||||
gulp.task(coreCIOld);
|
||||
|
||||
const coreCIEsbuild = task.define('core-ci-esbuild', task.series(
|
||||
copyCodiconsTask,
|
||||
cleanExtensionsBuildTask,
|
||||
compileNonNativeExtensionsBuildTask,
|
||||
compileExtensionMediaBuildTask,
|
||||
writeISODate('out-build'),
|
||||
// Type-check with tsgo (no emit)
|
||||
task.define('tsgo-typecheck', () => runTsGoTypeCheck()),
|
||||
// Transpile individual files to out-build first (for unit tests)
|
||||
task.define('esbuild-out-build', () => runEsbuildTranspile('out-build', false)),
|
||||
// Then bundle for shipping (bundles also write NLS files to out-build)
|
||||
task.parallel(
|
||||
task.define('esbuild-vscode-min', () => runEsbuildBundle('out-vscode-min', true, true, 'desktop', `${sourceMappingURLBase}/core`)),
|
||||
task.define('esbuild-vscode-reh-min', () => runEsbuildBundle('out-vscode-reh-min', true, true, 'server', `${sourceMappingURLBase}/core`)),
|
||||
task.define('esbuild-vscode-reh-web-min', () => runEsbuildBundle('out-vscode-reh-web-min', true, true, 'server-web', `${sourceMappingURLBase}/core`)),
|
||||
)
|
||||
));
|
||||
gulp.task(coreCIEsbuild);
|
||||
|
||||
gulp.task(task.define('core-ci', useEsbuildTranspile ? coreCIEsbuild : coreCIOld));
|
||||
|
||||
const coreCIPR = task.define('core-ci-pr', task.series(
|
||||
gulp.task('compile-build-without-mangling') as task.Task,
|
||||
@@ -516,27 +615,44 @@ BUILD_TARGETS.forEach(buildTarget => {
|
||||
const sourceFolderName = `out-vscode${dashed(minified)}`;
|
||||
const destinationFolderName = `VSCode${dashed(platform)}${dashed(arch)}`;
|
||||
|
||||
const tasks = [
|
||||
const packageTasks: task.Task[] = [
|
||||
compileNativeExtensionsBuildTask,
|
||||
util.rimraf(path.join(buildRoot, destinationFolderName)),
|
||||
packageTask(platform, arch, sourceFolderName, destinationFolderName, opts)
|
||||
];
|
||||
|
||||
if (platform === 'win32') {
|
||||
tasks.push(patchWin32DependenciesTask(destinationFolderName));
|
||||
packageTasks.push(patchWin32DependenciesTask(destinationFolderName));
|
||||
}
|
||||
|
||||
const vscodeTaskCI = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}-ci`, task.series(...tasks));
|
||||
const vscodeTaskCI = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}-ci`, task.series(...packageTasks));
|
||||
gulp.task(vscodeTaskCI);
|
||||
|
||||
const vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(
|
||||
minified ? compileBuildWithManglingTask : compileBuildWithoutManglingTask,
|
||||
cleanExtensionsBuildTask,
|
||||
compileNonNativeExtensionsBuildTask,
|
||||
compileExtensionMediaBuildTask,
|
||||
minified ? minifyVSCodeTask : bundleVSCodeTask,
|
||||
vscodeTaskCI
|
||||
));
|
||||
let vscodeTask: task.Task;
|
||||
if (useEsbuildTranspile) {
|
||||
const esbuildBundleTask = task.define(
|
||||
`esbuild-bundle${dashed(platform)}${dashed(arch)}${dashed(minified)}`,
|
||||
() => runEsbuildBundle(sourceFolderName, !!minified, true, 'desktop', minified ? `${sourceMappingURLBase}/core` : undefined)
|
||||
);
|
||||
vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(
|
||||
copyCodiconsTask,
|
||||
cleanExtensionsBuildTask,
|
||||
compileNonNativeExtensionsBuildTask,
|
||||
compileExtensionMediaBuildTask,
|
||||
writeISODate('out-build'),
|
||||
esbuildBundleTask,
|
||||
vscodeTaskCI
|
||||
));
|
||||
} else {
|
||||
vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(
|
||||
minified ? compileBuildWithManglingTask : compileBuildWithoutManglingTask,
|
||||
cleanExtensionsBuildTask,
|
||||
compileNonNativeExtensionsBuildTask,
|
||||
compileExtensionMediaBuildTask,
|
||||
minified ? minifyVSCodeTask : bundleVSCodeTask,
|
||||
vscodeTaskCI
|
||||
));
|
||||
}
|
||||
gulp.task(vscodeTask);
|
||||
|
||||
return vscodeTask;
|
||||
@@ -568,7 +684,7 @@ const innoSetupConfig: Record<string, { codePage: string; defaultInfo?: { name:
|
||||
gulp.task(task.define(
|
||||
'vscode-translations-export',
|
||||
task.series(
|
||||
coreCI,
|
||||
gulp.task('core-ci') as task.Task,
|
||||
compileAllExtensionsBuildTask,
|
||||
function () {
|
||||
const pathToMetadata = './out-build/nls.metadata.json';
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import gulp from 'gulp';
|
||||
import * as path from 'path';
|
||||
import * as cp from 'child_process';
|
||||
import es from 'event-stream';
|
||||
import * as util from './lib/util.ts';
|
||||
import { getVersion } from './lib/getVersion.ts';
|
||||
@@ -18,6 +19,7 @@ import { getProductionDependencies } from './lib/dependencies.ts';
|
||||
import vfs from 'vinyl-fs';
|
||||
import packageJson from '../package.json' with { type: 'json' };
|
||||
import { compileBuildWithManglingTask } from './gulpfile.compile.ts';
|
||||
import { copyCodiconsTask } from './lib/compilation.ts';
|
||||
import * as extensions from './lib/extensions.ts';
|
||||
import jsonEditor from 'gulp-json-editor';
|
||||
import buildfile from './buildfile.ts';
|
||||
@@ -30,6 +32,34 @@ const commit = getVersion(REPO_ROOT);
|
||||
const quality = (product as { quality?: string }).quality;
|
||||
const version = (quality && quality !== 'stable') ? `${packageJson.version}-${quality}` : packageJson.version;
|
||||
|
||||
// esbuild-based bundle for standalone web
|
||||
function runEsbuildBundle(outDir: string, minify: boolean, nls: boolean): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const scriptPath = path.join(REPO_ROOT, 'build/next/index.ts');
|
||||
const args = [scriptPath, 'bundle', '--out', outDir, '--target', 'web'];
|
||||
if (minify) {
|
||||
args.push('--minify');
|
||||
}
|
||||
if (nls) {
|
||||
args.push('--nls');
|
||||
}
|
||||
|
||||
const proc = cp.spawn(process.execPath, args, {
|
||||
cwd: REPO_ROOT,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
proc.on('error', reject);
|
||||
proc.on('close', code => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`esbuild web bundle failed with exit code ${code} (outDir: ${outDir}, minify: ${minify}, nls: ${nls})`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const vscodeWebResourceIncludes = [
|
||||
|
||||
// NLS
|
||||
@@ -110,7 +140,7 @@ export const createVSCodeWebFileContentMapper = (extensionsRoot: string, product
|
||||
};
|
||||
};
|
||||
|
||||
const bundleVSCodeWebTask = task.define('bundle-vscode-web', task.series(
|
||||
const bundleVSCodeWebTask = task.define('bundle-vscode-web-OLD', task.series(
|
||||
util.rimraf('out-vscode-web'),
|
||||
optimize.bundleTask(
|
||||
{
|
||||
@@ -125,13 +155,17 @@ const bundleVSCodeWebTask = task.define('bundle-vscode-web', task.series(
|
||||
)
|
||||
));
|
||||
|
||||
const minifyVSCodeWebTask = task.define('minify-vscode-web', task.series(
|
||||
const minifyVSCodeWebTask = task.define('minify-vscode-web-OLD', task.series(
|
||||
bundleVSCodeWebTask,
|
||||
util.rimraf('out-vscode-web-min'),
|
||||
optimize.minifyTask('out-vscode-web', `https://main.vscode-cdn.net/sourcemaps/${commit}/core`)
|
||||
));
|
||||
gulp.task(minifyVSCodeWebTask);
|
||||
|
||||
// esbuild-based tasks (new)
|
||||
const esbuildBundleVSCodeWebTask = task.define('esbuild-vscode-web', () => runEsbuildBundle('out-vscode-web', false, true));
|
||||
const esbuildBundleVSCodeWebMinTask = task.define('esbuild-vscode-web-min', () => runEsbuildBundle('out-vscode-web-min', true, true));
|
||||
|
||||
function packageTask(sourceFolderName: string, destinationFolderName: string) {
|
||||
const destination = path.join(BUILD_ROOT, destinationFolderName);
|
||||
|
||||
@@ -197,8 +231,9 @@ const dashed = (str: string) => (str ? `-${str}` : ``);
|
||||
const destinationFolderName = `vscode-web`;
|
||||
|
||||
const vscodeWebTaskCI = task.define(`vscode-web${dashed(minified)}-ci`, task.series(
|
||||
copyCodiconsTask,
|
||||
compileWebExtensionsBuildTask,
|
||||
minified ? minifyVSCodeWebTask : bundleVSCodeWebTask,
|
||||
minified ? esbuildBundleVSCodeWebMinTask : esbuildBundleVSCodeWebTask,
|
||||
util.rimraf(path.join(BUILD_ROOT, destinationFolderName)),
|
||||
packageTask(sourceFolderName, destinationFolderName)
|
||||
));
|
||||
|
||||
@@ -49,14 +49,18 @@ interface ICompileTaskOptions {
|
||||
readonly emitError: boolean;
|
||||
readonly transpileOnly: boolean | { esbuild: boolean };
|
||||
readonly preserveEnglish: boolean;
|
||||
readonly noEmit?: boolean;
|
||||
}
|
||||
|
||||
export function createCompile(src: string, { build, emitError, transpileOnly, preserveEnglish }: ICompileTaskOptions) {
|
||||
export function createCompile(src: string, { build, emitError, transpileOnly, preserveEnglish, noEmit }: ICompileTaskOptions) {
|
||||
const projectPath = path.join(import.meta.dirname, '../../', src, 'tsconfig.json');
|
||||
const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) };
|
||||
if (!build) {
|
||||
overrideOptions.inlineSourceMap = true;
|
||||
}
|
||||
if (noEmit) {
|
||||
overrideOptions.noEmit = true;
|
||||
}
|
||||
|
||||
const compilation = tsb.create(projectPath, overrideOptions, {
|
||||
verbose: false,
|
||||
@@ -163,10 +167,10 @@ export function compileTask(src: string, out: string, build: boolean, options: {
|
||||
return task;
|
||||
}
|
||||
|
||||
export function watchTask(out: string, build: boolean, srcPath: string = 'src'): task.StreamTask {
|
||||
export function watchTask(out: string, build: boolean, srcPath: string = 'src', options?: { noEmit?: boolean }): task.StreamTask {
|
||||
|
||||
const task = () => {
|
||||
const compile = createCompile(srcPath, { build, emitError: false, transpileOnly: false, preserveEnglish: false });
|
||||
const compile = createCompile(srcPath, { build, emitError: false, transpileOnly: false, preserveEnglish: false, noEmit: options?.noEmit });
|
||||
|
||||
const src = gulp.src(`${srcPath}/**`, { base: srcPath });
|
||||
const watchSrc = watch(`${srcPath}/**`, { base: srcPath, readDelay: 200 });
|
||||
|
||||
+68
-7
@@ -66,16 +66,31 @@ function updateExtensionPackageJSON(input: Stream, update: (data: any) => any):
|
||||
|
||||
function fromLocal(extensionPath: string, forWeb: boolean, disableMangle: boolean): Stream {
|
||||
|
||||
const esbuildConfigFileName = forWeb
|
||||
? 'esbuild-browser.ts'
|
||||
: 'esbuild.ts';
|
||||
|
||||
const webpackConfigFileName = forWeb
|
||||
? `extension-browser.webpack.config.js`
|
||||
: `extension.webpack.config.js`;
|
||||
|
||||
const hasEsbuild = fs.existsSync(path.join(extensionPath, esbuildConfigFileName));
|
||||
const isWebPacked = fs.existsSync(path.join(extensionPath, webpackConfigFileName));
|
||||
let input = isWebPacked
|
||||
? fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle)
|
||||
: fromLocalNormal(extensionPath);
|
||||
|
||||
if (isWebPacked) {
|
||||
let input: Stream;
|
||||
let isBundled = false;
|
||||
|
||||
if (hasEsbuild) {
|
||||
input = fromLocalEsbuild(extensionPath, esbuildConfigFileName);
|
||||
isBundled = true;
|
||||
} else if (isWebPacked) {
|
||||
input = fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle);
|
||||
isBundled = true;
|
||||
} else {
|
||||
input = fromLocalNormal(extensionPath);
|
||||
}
|
||||
|
||||
if (isBundled) {
|
||||
input = updateExtensionPackageJSON(input, (data: any) => {
|
||||
delete data.scripts;
|
||||
delete data.dependencies;
|
||||
@@ -240,6 +255,51 @@ function fromLocalNormal(extensionPath: string): Stream {
|
||||
return result.pipe(createStatsStream(path.basename(extensionPath)));
|
||||
}
|
||||
|
||||
function fromLocalEsbuild(extensionPath: string, esbuildConfigFileName: string): Stream {
|
||||
const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce');
|
||||
const result = es.through();
|
||||
|
||||
const esbuildScript = path.join(extensionPath, esbuildConfigFileName);
|
||||
|
||||
// Run esbuild, then collect the files
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const proc = cp.execFile(process.argv[0], [esbuildScript], {}, (error, _stdout, stderr) => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
const matches = (stderr || '').match(/\> (.+): error: (.+)?/g);
|
||||
fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), esbuildConfigFileName))} with ${matches ? matches.length : 0} errors.`);
|
||||
for (const match of matches || []) {
|
||||
fancyLog.error(match);
|
||||
}
|
||||
return resolve();
|
||||
});
|
||||
|
||||
proc.stdout!.on('data', (data) => {
|
||||
fancyLog(`${ansiColors.green('esbuilding')}: ${data.toString('utf8')}`);
|
||||
});
|
||||
}).then(() => {
|
||||
// After esbuild completes, collect all files using vsce
|
||||
return vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.None });
|
||||
}).then(fileNames => {
|
||||
const files = fileNames
|
||||
.map(fileName => path.join(extensionPath, fileName))
|
||||
.map(filePath => new File({
|
||||
path: filePath,
|
||||
stat: fs.statSync(filePath),
|
||||
base: extensionPath,
|
||||
contents: fs.createReadStream(filePath)
|
||||
}));
|
||||
|
||||
es.readArray(files).pipe(result);
|
||||
}).catch(err => {
|
||||
console.error(extensionPath);
|
||||
result.emit('error', err);
|
||||
});
|
||||
|
||||
return result.pipe(createStatsStream(path.basename(extensionPath)));
|
||||
}
|
||||
|
||||
const userAgent = 'VSCode Build';
|
||||
const baseHeaders = {
|
||||
'X-Market-Client-Id': 'VSCode Build',
|
||||
@@ -647,7 +707,7 @@ export async function webpackExtensions(taskName: string, isWatch: boolean, webp
|
||||
});
|
||||
}
|
||||
|
||||
async function esbuildExtensions(taskName: string, isWatch: boolean, scripts: { script: string; outputRoot?: string }[]) {
|
||||
export async function esbuildExtensions(taskName: string, isWatch: boolean, scripts: { script: string; outputRoot?: string }[]): Promise<void> {
|
||||
function reporter(stdError: string, script: string) {
|
||||
const matches = (stdError || '').match(/\> (.+): error: (.+)?/g);
|
||||
fancyLog(`Finished ${ansiColors.green(taskName)} ${script} with ${matches ? matches.length : 0} errors.`);
|
||||
@@ -678,10 +738,11 @@ async function esbuildExtensions(taskName: string, isWatch: boolean, scripts: {
|
||||
});
|
||||
});
|
||||
});
|
||||
return Promise.all(tasks);
|
||||
|
||||
await Promise.all(tasks);
|
||||
}
|
||||
|
||||
export async function buildExtensionMedia(isWatch: boolean, outputRoot?: string) {
|
||||
export function buildExtensionMedia(isWatch: boolean, outputRoot?: string): Promise<void> {
|
||||
return esbuildExtensions('esbuilding extension media', isWatch, esbuildMediaScripts.map(p => ({
|
||||
script: path.join(extensionsPath, p),
|
||||
outputRoot: outputRoot ? path.join(root, outputRoot, path.dirname(p)) : undefined
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
// ============================================================================
|
||||
|
||||
export interface ISpan {
|
||||
start: ts.LineAndCharacter;
|
||||
end: ts.LineAndCharacter;
|
||||
}
|
||||
|
||||
export interface ILocalizeCall {
|
||||
keySpan: ISpan;
|
||||
key: string;
|
||||
valueSpan: ISpan;
|
||||
value: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// AST Collection
|
||||
// ============================================================================
|
||||
|
||||
export const CollectStepResult = Object.freeze({
|
||||
Yes: 'Yes',
|
||||
YesAndRecurse: 'YesAndRecurse',
|
||||
No: 'No',
|
||||
NoAndRecurse: 'NoAndRecurse'
|
||||
});
|
||||
|
||||
export type CollectStepResult = typeof CollectStepResult[keyof typeof CollectStepResult];
|
||||
|
||||
export function collect(node: ts.Node, fn: (node: ts.Node) => CollectStepResult): ts.Node[] {
|
||||
const result: ts.Node[] = [];
|
||||
|
||||
function loop(node: ts.Node) {
|
||||
const stepResult = fn(node);
|
||||
|
||||
if (stepResult === CollectStepResult.Yes || stepResult === CollectStepResult.YesAndRecurse) {
|
||||
result.push(node);
|
||||
}
|
||||
|
||||
if (stepResult === CollectStepResult.YesAndRecurse || stepResult === CollectStepResult.NoAndRecurse) {
|
||||
ts.forEachChild(node, loop);
|
||||
}
|
||||
}
|
||||
|
||||
loop(node);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isImportNode(node: ts.Node): boolean {
|
||||
return node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration;
|
||||
}
|
||||
|
||||
export function isCallExpressionWithinTextSpanCollectStep(textSpan: ts.TextSpan, node: ts.Node): CollectStepResult {
|
||||
if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) {
|
||||
return CollectStepResult.No;
|
||||
}
|
||||
|
||||
return node.kind === ts.SyntaxKind.CallExpression ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Language Service Host
|
||||
// ============================================================================
|
||||
|
||||
export class SingleFileServiceHost implements ts.LanguageServiceHost {
|
||||
private file: ts.IScriptSnapshot;
|
||||
private lib: ts.IScriptSnapshot;
|
||||
private options: ts.CompilerOptions;
|
||||
private filename: string;
|
||||
|
||||
constructor(options: ts.CompilerOptions, filename: string, contents: string) {
|
||||
this.options = options;
|
||||
this.filename = filename;
|
||||
this.file = ts.ScriptSnapshot.fromString(contents);
|
||||
this.lib = ts.ScriptSnapshot.fromString('');
|
||||
}
|
||||
|
||||
getCompilationSettings = () => this.options;
|
||||
getScriptFileNames = () => [this.filename];
|
||||
getScriptVersion = () => '1';
|
||||
getScriptSnapshot = (name: string) => name === this.filename ? this.file : this.lib;
|
||||
getCurrentDirectory = () => '';
|
||||
getDefaultLibFileName = () => 'lib.d.ts';
|
||||
|
||||
readFile(path: string): string | undefined {
|
||||
if (path === this.filename) {
|
||||
return this.file.getText(0, this.file.getLength());
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
fileExists(path: string): boolean {
|
||||
return path === this.filename;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Analysis
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Analyzes TypeScript source code to find localize() or localize2() calls.
|
||||
*/
|
||||
export function analyzeLocalizeCalls(
|
||||
contents: string,
|
||||
functionName: 'localize' | 'localize2'
|
||||
): ILocalizeCall[] {
|
||||
const filename = 'file.ts';
|
||||
const options: ts.CompilerOptions = { noResolve: true };
|
||||
const serviceHost = new SingleFileServiceHost(options, filename, contents);
|
||||
const service = ts.createLanguageService(serviceHost);
|
||||
const sourceFile = ts.createSourceFile(filename, contents, ts.ScriptTarget.ES5, true);
|
||||
|
||||
// Find all imports
|
||||
const imports = collect(sourceFile, n => isImportNode(n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse);
|
||||
|
||||
// import nls = require('vs/nls');
|
||||
const importEqualsDeclarations = imports
|
||||
.filter(n => n.kind === ts.SyntaxKind.ImportEqualsDeclaration)
|
||||
.map(n => n as ts.ImportEqualsDeclaration)
|
||||
.filter(d => d.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference)
|
||||
.filter(d => {
|
||||
const text = (d.moduleReference as ts.ExternalModuleReference).expression.getText();
|
||||
return text.endsWith(`/nls'`) || text.endsWith(`/nls"`) || text.endsWith(`/nls.js'`) || text.endsWith(`/nls.js"`);
|
||||
});
|
||||
|
||||
// import ... from 'vs/nls';
|
||||
const importDeclarations = imports
|
||||
.filter(n => n.kind === ts.SyntaxKind.ImportDeclaration)
|
||||
.map(n => n as ts.ImportDeclaration)
|
||||
.filter(d => d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral)
|
||||
.filter(d => {
|
||||
const text = d.moduleSpecifier.getText();
|
||||
return text.endsWith(`/nls'`) || text.endsWith(`/nls"`) || text.endsWith(`/nls.js'`) || text.endsWith(`/nls.js"`);
|
||||
})
|
||||
.filter(d => !!d.importClause && !!d.importClause.namedBindings);
|
||||
|
||||
// `nls.localize(...)` calls via namespace import
|
||||
const nlsLocalizeCallExpressions: ts.CallExpression[] = [];
|
||||
|
||||
const namespaceImports = importDeclarations
|
||||
.filter(d => d.importClause?.namedBindings?.kind === ts.SyntaxKind.NamespaceImport)
|
||||
.map(d => (d.importClause!.namedBindings as ts.NamespaceImport).name);
|
||||
|
||||
const importEqualsNames = importEqualsDeclarations.map(d => d.name);
|
||||
|
||||
for (const name of [...namespaceImports, ...importEqualsNames]) {
|
||||
const refs = service.getReferencesAtPosition(filename, name.pos + 1) ?? [];
|
||||
for (const ref of refs) {
|
||||
if (ref.isWriteAccess) {
|
||||
continue;
|
||||
}
|
||||
const calls = collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ref.textSpan, n));
|
||||
const lastCall = calls[calls.length - 1] as ts.CallExpression | undefined;
|
||||
if (lastCall &&
|
||||
lastCall.expression.kind === ts.SyntaxKind.PropertyAccessExpression &&
|
||||
(lastCall.expression as ts.PropertyAccessExpression).name.getText() === functionName) {
|
||||
nlsLocalizeCallExpressions.push(lastCall);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `localize` named imports
|
||||
const namedImports = importDeclarations
|
||||
.filter(d => d.importClause?.namedBindings?.kind === ts.SyntaxKind.NamedImports)
|
||||
.flatMap(d => Array.from((d.importClause!.namedBindings! as ts.NamedImports).elements));
|
||||
|
||||
const localizeCallExpressions: ts.CallExpression[] = [];
|
||||
|
||||
// Direct named import: import { localize } from 'vs/nls'
|
||||
for (const namedImport of namedImports) {
|
||||
const isTarget = namedImport.name.getText() === functionName ||
|
||||
(namedImport.propertyName && namedImport.propertyName.getText() === functionName);
|
||||
|
||||
if (!isTarget) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const searchName = namedImport.propertyName ? namedImport.name : namedImport.name;
|
||||
const refs = service.getReferencesAtPosition(filename, searchName.pos + 1) ?? [];
|
||||
|
||||
for (const ref of refs) {
|
||||
if (ref.isWriteAccess) {
|
||||
continue;
|
||||
}
|
||||
const calls = collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ref.textSpan, n));
|
||||
const lastCall = calls[calls.length - 1] as ts.CallExpression | undefined;
|
||||
if (lastCall) {
|
||||
localizeCallExpressions.push(lastCall);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Combine and deduplicate
|
||||
const allCalls = [...nlsLocalizeCallExpressions, ...localizeCallExpressions];
|
||||
const seen = new Set<number>();
|
||||
const uniqueCalls = allCalls.filter(call => {
|
||||
const start = call.getStart();
|
||||
if (seen.has(start)) {
|
||||
return false;
|
||||
}
|
||||
seen.add(start);
|
||||
return true;
|
||||
});
|
||||
|
||||
// Convert to ILocalizeCall
|
||||
return uniqueCalls
|
||||
.filter(e => e.arguments.length > 1)
|
||||
.sort((a, b) => a.arguments[0].getStart() - b.arguments[0].getStart())
|
||||
.map(e => {
|
||||
const args = e.arguments;
|
||||
return {
|
||||
keySpan: {
|
||||
start: ts.getLineAndCharacterOfPosition(sourceFile, args[0].getStart()),
|
||||
end: ts.getLineAndCharacterOfPosition(sourceFile, args[0].getEnd())
|
||||
},
|
||||
key: args[0].getText(),
|
||||
valueSpan: {
|
||||
start: ts.getLineAndCharacterOfPosition(sourceFile, args[1].getStart()),
|
||||
end: ts.getLineAndCharacterOfPosition(sourceFile, args[1].getEnd())
|
||||
},
|
||||
value: args[1].getText()
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Text Model for patching
|
||||
// ============================================================================
|
||||
|
||||
export class TextModel {
|
||||
private lines: string[];
|
||||
private lineEndings: string[];
|
||||
|
||||
constructor(contents: string) {
|
||||
const regex = /\r\n|\r|\n/g;
|
||||
let index = 0;
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
this.lines = [];
|
||||
this.lineEndings = [];
|
||||
|
||||
while (match = regex.exec(contents)) {
|
||||
this.lines.push(contents.substring(index, match.index));
|
||||
this.lineEndings.push(match[0]);
|
||||
index = regex.lastIndex;
|
||||
}
|
||||
|
||||
if (contents.length > 0) {
|
||||
this.lines.push(contents.substring(index, contents.length));
|
||||
this.lineEndings.push('');
|
||||
}
|
||||
}
|
||||
|
||||
get(index: number): string {
|
||||
return this.lines[index];
|
||||
}
|
||||
|
||||
set(index: number, line: string): void {
|
||||
this.lines[index] = line;
|
||||
}
|
||||
|
||||
get lineCount(): number {
|
||||
return this.lines.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies patch(es) to the model.
|
||||
* Multiple patches must be ordered.
|
||||
* Does not support patches spanning multiple lines.
|
||||
*/
|
||||
apply(span: ISpan, content: string): void {
|
||||
const startLineNumber = span.start.line;
|
||||
const endLineNumber = span.end.line;
|
||||
|
||||
const startLine = this.lines[startLineNumber] || '';
|
||||
const endLine = this.lines[endLineNumber] || '';
|
||||
|
||||
this.lines[startLineNumber] = [
|
||||
startLine.substring(0, span.start.character),
|
||||
content,
|
||||
endLine.substring(span.end.character)
|
||||
].join('');
|
||||
|
||||
for (let i = startLineNumber + 1; i <= endLineNumber; i++) {
|
||||
this.lines[i] = '';
|
||||
}
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
let result = '';
|
||||
for (let i = 0; i < this.lines.length; i++) {
|
||||
result += this.lines[i] + this.lineEndings[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Utilities
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Parses a localize key or value expression.
|
||||
* sourceExpression can be "foo", 'foo', `foo` or { key: 'foo', comment: [...] }
|
||||
*/
|
||||
export function parseLocalizeKeyOrValue(sourceExpression: string): string | { key: string; comment?: string[] } {
|
||||
// eslint-disable-next-line no-eval
|
||||
return eval(`(${sourceExpression})`);
|
||||
}
|
||||
+6
-271
@@ -10,45 +10,10 @@ import File from 'vinyl';
|
||||
import sm from 'source-map';
|
||||
import path from 'path';
|
||||
import sort from 'gulp-sort';
|
||||
import { type ISpan, analyzeLocalizeCalls, TextModel, parseLocalizeKeyOrValue } from './nls-analysis.ts';
|
||||
|
||||
type FileWithSourcemap = File & { sourceMap: sm.RawSourceMap };
|
||||
|
||||
const CollectStepResult = Object.freeze({
|
||||
Yes: 'Yes',
|
||||
YesAndRecurse: 'YesAndRecurse',
|
||||
No: 'No',
|
||||
NoAndRecurse: 'NoAndRecurse'
|
||||
});
|
||||
|
||||
type CollectStepResult = typeof CollectStepResult[keyof typeof CollectStepResult];
|
||||
|
||||
function collect(ts: typeof import('typescript'), node: ts.Node, fn: (node: ts.Node) => CollectStepResult): ts.Node[] {
|
||||
const result: ts.Node[] = [];
|
||||
|
||||
function loop(node: ts.Node) {
|
||||
const stepResult = fn(node);
|
||||
|
||||
if (stepResult === CollectStepResult.Yes || stepResult === CollectStepResult.YesAndRecurse) {
|
||||
result.push(node);
|
||||
}
|
||||
|
||||
if (stepResult === CollectStepResult.YesAndRecurse || stepResult === CollectStepResult.NoAndRecurse) {
|
||||
ts.forEachChild(node, loop);
|
||||
}
|
||||
}
|
||||
|
||||
loop(node);
|
||||
return result;
|
||||
}
|
||||
|
||||
function clone<T extends object>(object: T): T {
|
||||
const result: Record<string, unknown> = {};
|
||||
for (const id in object) {
|
||||
result[id] = object[id];
|
||||
}
|
||||
return result as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream containing the patched JavaScript and source maps.
|
||||
*/
|
||||
@@ -117,10 +82,6 @@ globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(_nls.allNLSMessages)};`),
|
||||
return eventStream.duplex(input, output);
|
||||
}
|
||||
|
||||
function isImportNode(ts: typeof import('typescript'), node: ts.Node): boolean {
|
||||
return node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration;
|
||||
}
|
||||
|
||||
const _nls = (() => {
|
||||
|
||||
const moduleToNLSKeys: { [name: string /* module ID */]: ILocalizeKey[] /* keys */ } = {};
|
||||
@@ -138,22 +99,6 @@ const _nls = (() => {
|
||||
nlsKeys?: ILocalizeKey[];
|
||||
}
|
||||
|
||||
interface ISpan {
|
||||
start: ts.LineAndCharacter;
|
||||
end: ts.LineAndCharacter;
|
||||
}
|
||||
|
||||
interface ILocalizeCall {
|
||||
keySpan: ISpan;
|
||||
key: string;
|
||||
valueSpan: ISpan;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface ILocalizeAnalysisResult {
|
||||
localizeCalls: ILocalizeCall[];
|
||||
}
|
||||
|
||||
interface IPatch {
|
||||
span: ISpan;
|
||||
content: string;
|
||||
@@ -176,212 +121,11 @@ const _nls = (() => {
|
||||
return { line: position.line - 1, character: position.column };
|
||||
}
|
||||
|
||||
class SingleFileServiceHost implements ts.LanguageServiceHost {
|
||||
|
||||
private file: ts.IScriptSnapshot;
|
||||
private lib: ts.IScriptSnapshot;
|
||||
private options: ts.CompilerOptions;
|
||||
private filename: string;
|
||||
|
||||
constructor(ts: typeof import('typescript'), options: ts.CompilerOptions, filename: string, contents: string) {
|
||||
this.options = options;
|
||||
this.filename = filename;
|
||||
this.file = ts.ScriptSnapshot.fromString(contents);
|
||||
this.lib = ts.ScriptSnapshot.fromString('');
|
||||
}
|
||||
|
||||
getCompilationSettings = () => this.options;
|
||||
getScriptFileNames = () => [this.filename];
|
||||
getScriptVersion = () => '1';
|
||||
getScriptSnapshot = (name: string) => name === this.filename ? this.file : this.lib;
|
||||
getCurrentDirectory = () => '';
|
||||
getDefaultLibFileName = () => 'lib.d.ts';
|
||||
|
||||
readFile(path: string, _encoding?: string): string | undefined {
|
||||
if (path === this.filename) {
|
||||
return this.file.getText(0, this.file.getLength());
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
fileExists(path: string): boolean {
|
||||
return path === this.filename;
|
||||
}
|
||||
}
|
||||
|
||||
function isCallExpressionWithinTextSpanCollectStep(ts: typeof import('typescript'), textSpan: ts.TextSpan, node: ts.Node): CollectStepResult {
|
||||
if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) {
|
||||
return CollectStepResult.No;
|
||||
}
|
||||
|
||||
return node.kind === ts.SyntaxKind.CallExpression ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse;
|
||||
}
|
||||
|
||||
function analyze(
|
||||
ts: typeof import('typescript'),
|
||||
contents: string,
|
||||
functionName: 'localize' | 'localize2',
|
||||
options: ts.CompilerOptions = {}
|
||||
): ILocalizeAnalysisResult {
|
||||
const filename = 'file.ts';
|
||||
const serviceHost = new SingleFileServiceHost(ts, Object.assign(clone(options), { noResolve: true }), filename, contents);
|
||||
const service = ts.createLanguageService(serviceHost);
|
||||
const sourceFile = ts.createSourceFile(filename, contents, ts.ScriptTarget.ES5, true);
|
||||
|
||||
// all imports
|
||||
const imports = lazy(collect(ts, sourceFile, n => isImportNode(ts, n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse));
|
||||
|
||||
// import nls = require('vs/nls');
|
||||
const importEqualsDeclarations = imports
|
||||
.filter(n => n.kind === ts.SyntaxKind.ImportEqualsDeclaration)
|
||||
.map(n => n as ts.ImportEqualsDeclaration)
|
||||
.filter(d => d.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference)
|
||||
.filter(d => (d.moduleReference as ts.ExternalModuleReference).expression.getText().endsWith(`/nls.js'`));
|
||||
|
||||
// import ... from 'vs/nls';
|
||||
const importDeclarations = imports
|
||||
.filter(n => n.kind === ts.SyntaxKind.ImportDeclaration)
|
||||
.map(n => n as ts.ImportDeclaration)
|
||||
.filter(d => d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral)
|
||||
.filter(d => d.moduleSpecifier.getText().endsWith(`/nls.js'`))
|
||||
.filter(d => !!d.importClause && !!d.importClause.namedBindings);
|
||||
|
||||
// `nls.localize(...)` calls
|
||||
const nlsLocalizeCallExpressions = importDeclarations
|
||||
.filter(d => !!(d.importClause && d.importClause.namedBindings && d.importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport))
|
||||
.map(d => (d.importClause!.namedBindings as ts.NamespaceImport).name)
|
||||
.concat(importEqualsDeclarations.map(d => d.name))
|
||||
|
||||
// find read-only references to `nls`
|
||||
.map(n => service.getReferencesAtPosition(filename, n.pos + 1) ?? [])
|
||||
.flatten()
|
||||
.filter(r => !r.isWriteAccess)
|
||||
|
||||
// find the deepest call expressions AST nodes that contain those references
|
||||
.map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n)))
|
||||
.map(a => lazy(a).last())
|
||||
.filter(n => !!n)
|
||||
.map(n => n as ts.CallExpression)
|
||||
|
||||
// only `localize` calls
|
||||
.filter(n => n.expression.kind === ts.SyntaxKind.PropertyAccessExpression && (n.expression as ts.PropertyAccessExpression).name.getText() === functionName);
|
||||
|
||||
// `localize` named imports
|
||||
const allLocalizeImportDeclarations = importDeclarations
|
||||
.filter(d => !!(d.importClause && d.importClause.namedBindings && d.importClause.namedBindings.kind === ts.SyntaxKind.NamedImports))
|
||||
.map(d => (d.importClause!.namedBindings! as ts.NamedImports).elements)
|
||||
.flatten();
|
||||
|
||||
// `localize` read-only references
|
||||
const localizeReferences = allLocalizeImportDeclarations
|
||||
.filter(d => d.name.getText() === functionName)
|
||||
.map(n => service.getReferencesAtPosition(filename, n.pos + 1) ?? [])
|
||||
.flatten()
|
||||
.filter(r => !r.isWriteAccess);
|
||||
|
||||
// custom named `localize` read-only references
|
||||
const namedLocalizeReferences = allLocalizeImportDeclarations
|
||||
.filter(d => !!d.propertyName && d.propertyName.getText() === functionName)
|
||||
.map(n => service.getReferencesAtPosition(filename, n.name.pos + 1) ?? [])
|
||||
.flatten()
|
||||
.filter(r => !r.isWriteAccess);
|
||||
|
||||
// find the deepest call expressions AST nodes that contain those references
|
||||
const localizeCallExpressions = localizeReferences
|
||||
.concat(namedLocalizeReferences)
|
||||
.map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n)))
|
||||
.map(a => lazy(a).last())
|
||||
.filter(n => !!n)
|
||||
.map(n => n as ts.CallExpression);
|
||||
|
||||
// collect everything
|
||||
const localizeCalls = nlsLocalizeCallExpressions
|
||||
.concat(localizeCallExpressions)
|
||||
.map(e => e.arguments)
|
||||
.filter(a => a.length > 1)
|
||||
.sort((a, b) => a[0].getStart() - b[0].getStart())
|
||||
.map<ILocalizeCall>(a => ({
|
||||
keySpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[0].getEnd()) },
|
||||
key: a[0].getText(),
|
||||
valueSpan: { start: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getStart()), end: ts.getLineAndCharacterOfPosition(sourceFile, a[1].getEnd()) },
|
||||
value: a[1].getText()
|
||||
}));
|
||||
|
||||
return {
|
||||
localizeCalls: localizeCalls.toArray()
|
||||
};
|
||||
}
|
||||
|
||||
class TextModel {
|
||||
|
||||
private lines: string[];
|
||||
private lineEndings: string[];
|
||||
|
||||
constructor(contents: string) {
|
||||
const regex = /\r\n|\r|\n/g;
|
||||
let index = 0;
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
this.lines = [];
|
||||
this.lineEndings = [];
|
||||
|
||||
while (match = regex.exec(contents)) {
|
||||
this.lines.push(contents.substring(index, match.index));
|
||||
this.lineEndings.push(match[0]);
|
||||
index = regex.lastIndex;
|
||||
}
|
||||
|
||||
if (contents.length > 0) {
|
||||
this.lines.push(contents.substring(index, contents.length));
|
||||
this.lineEndings.push('');
|
||||
}
|
||||
}
|
||||
|
||||
public get(index: number): string {
|
||||
return this.lines[index];
|
||||
}
|
||||
|
||||
public set(index: number, line: string): void {
|
||||
this.lines[index] = line;
|
||||
}
|
||||
|
||||
public get lineCount(): number {
|
||||
return this.lines.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies patch(es) to the model.
|
||||
* Multiple patches must be ordered.
|
||||
* Does not support patches spanning multiple lines.
|
||||
*/
|
||||
public apply(patch: IPatch): void {
|
||||
const startLineNumber = patch.span.start.line;
|
||||
const endLineNumber = patch.span.end.line;
|
||||
|
||||
const startLine = this.lines[startLineNumber] || '';
|
||||
const endLine = this.lines[endLineNumber] || '';
|
||||
|
||||
this.lines[startLineNumber] = [
|
||||
startLine.substring(0, patch.span.start.character),
|
||||
patch.content,
|
||||
endLine.substring(patch.span.end.character)
|
||||
].join('');
|
||||
|
||||
for (let i = startLineNumber + 1; i <= endLineNumber; i++) {
|
||||
this.lines[i] = '';
|
||||
}
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return lazy(this.lines).zip(this.lineEndings)
|
||||
.flatten().toArray().join('');
|
||||
}
|
||||
}
|
||||
|
||||
function patchJavascript(patches: IPatch[], contents: string): string {
|
||||
const model = new TextModel(contents);
|
||||
|
||||
// patch the localize calls
|
||||
lazy(patches).reverse().each(p => model.apply(p));
|
||||
lazy(patches).reverse().each(p => model.apply(p.span, p.content));
|
||||
|
||||
return model.toString();
|
||||
}
|
||||
@@ -431,24 +175,16 @@ const _nls = (() => {
|
||||
return JSON.parse(smg.toString());
|
||||
}
|
||||
|
||||
function parseLocalizeKeyOrValue(sourceExpression: string) {
|
||||
// sourceValue can be "foo", 'foo', `foo` or { .... }
|
||||
// in its evalulated form
|
||||
// we want to return either the string or the object
|
||||
// eslint-disable-next-line no-eval
|
||||
return eval(`(${sourceExpression})`);
|
||||
}
|
||||
|
||||
function patch(ts: typeof import('typescript'), typescript: string, javascript: string, sourcemap: sm.RawSourceMap, options: { preserveEnglish: boolean }): INlsPatchResult {
|
||||
const { localizeCalls } = analyze(ts, typescript, 'localize');
|
||||
const { localizeCalls: localize2Calls } = analyze(ts, typescript, 'localize2');
|
||||
function patch(typescript: string, javascript: string, sourcemap: sm.RawSourceMap, options: { preserveEnglish: boolean }): INlsPatchResult {
|
||||
const localizeCalls = analyzeLocalizeCalls(typescript, 'localize');
|
||||
const localize2Calls = analyzeLocalizeCalls(typescript, 'localize2');
|
||||
|
||||
if (localizeCalls.length === 0 && localize2Calls.length === 0) {
|
||||
return { javascript, sourcemap };
|
||||
}
|
||||
|
||||
const nlsKeys = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.key)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.key)));
|
||||
const nlsMessages = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.value)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.value)));
|
||||
const nlsMessages = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.value) as string).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.value) as string));
|
||||
const smc = new sm.SourceMapConsumer(sourcemap);
|
||||
const positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]);
|
||||
|
||||
@@ -505,7 +241,6 @@ const _nls = (() => {
|
||||
.replace(/\\/g, '/');
|
||||
|
||||
const { javascript, sourcemap, nlsKeys, nlsMessages } = patch(
|
||||
ts,
|
||||
typescript,
|
||||
javascriptFile.contents!.toString(),
|
||||
javascriptFile.sourceMap,
|
||||
|
||||
+9
-11
@@ -12,8 +12,8 @@ const root = path.dirname(path.dirname(import.meta.dirname));
|
||||
const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
||||
const ansiRegex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
||||
|
||||
export function spawnTsgo(projectPath: string, onComplete?: () => Promise<void> | void): Promise<void> {
|
||||
const reporter = createReporter('extensions');
|
||||
export function spawnTsgo(projectPath: string, config: { reporterId: string }, onComplete?: () => Promise<void> | void): Promise<void> {
|
||||
const reporter = createReporter(config.reporterId);
|
||||
let report: NodeJS.ReadWriteStream | undefined;
|
||||
|
||||
const beginReport = (emitError: boolean) => {
|
||||
@@ -31,10 +31,9 @@ export function spawnTsgo(projectPath: string, onComplete?: () => Promise<void>
|
||||
report = undefined;
|
||||
};
|
||||
|
||||
const args = ['tsgo', '--project', projectPath, '--pretty', 'false', '--sourceMap', '--inlineSources'];
|
||||
|
||||
beginReport(false);
|
||||
|
||||
const args = ['tsgo', '--project', projectPath, '--pretty', 'false', '--sourceMap', '--inlineSources'];
|
||||
const child = cp.spawn(npx, args, {
|
||||
cwd: root,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
@@ -79,7 +78,7 @@ export function spawnTsgo(projectPath: string, onComplete?: () => Promise<void>
|
||||
child.stdout?.on('data', handleData);
|
||||
child.stderr?.on('data', handleData);
|
||||
|
||||
const done = new Promise<void>((resolve, reject) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
child.on('exit', code => {
|
||||
if (buffer.trim()) {
|
||||
handleLine(buffer);
|
||||
@@ -88,23 +87,22 @@ export function spawnTsgo(projectPath: string, onComplete?: () => Promise<void>
|
||||
endReport();
|
||||
if (code === 0) {
|
||||
Promise.resolve(onComplete?.()).then(() => resolve(), reject);
|
||||
return;
|
||||
} else {
|
||||
reject(new Error(`tsgo exited with code ${code ?? 'unknown'}`));
|
||||
}
|
||||
reject(new Error(`tsgo exited with code ${code ?? 'unknown'}`));
|
||||
});
|
||||
|
||||
child.on('error', err => {
|
||||
endReport();
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
export function createTsgoStream(projectPath: string, onComplete?: () => Promise<void> | void): NodeJS.ReadWriteStream {
|
||||
export function createTsgoStream(projectPath: string, config: { reporterId: string }, onComplete?: () => Promise<void> | void): NodeJS.ReadWriteStream {
|
||||
const stream = es.through();
|
||||
|
||||
spawnTsgo(projectPath, onComplete).then(() => {
|
||||
spawnTsgo(projectPath, config, onComplete).then(() => {
|
||||
stream.emit('end');
|
||||
}).catch(() => {
|
||||
// Errors are already reported by spawnTsgo via the reporter.
|
||||
|
||||
@@ -10,8 +10,9 @@ import File from 'vinyl';
|
||||
import es from 'event-stream';
|
||||
import filter from 'gulp-filter';
|
||||
import { Stream } from 'stream';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const watcherPath = path.join(import.meta.dirname, 'watcher.exe');
|
||||
const watcherPath = path.join(typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)), 'watcher.exe');
|
||||
|
||||
function toChangeType(type: '0' | '1' | '2'): 'change' | 'add' | 'unlink' {
|
||||
switch (type) {
|
||||
|
||||
+1134
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,319 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as esbuild from 'esbuild';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import {
|
||||
TextModel,
|
||||
analyzeLocalizeCalls,
|
||||
parseLocalizeKeyOrValue
|
||||
} from '../lib/nls-analysis.ts';
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
// ============================================================================
|
||||
|
||||
interface NLSEntry {
|
||||
moduleId: string;
|
||||
key: string | { key: string; comment: string[] };
|
||||
message: string;
|
||||
placeholder: string;
|
||||
}
|
||||
|
||||
export interface NLSPluginOptions {
|
||||
/**
|
||||
* Base path for computing module IDs (e.g., 'src')
|
||||
*/
|
||||
baseDir: string;
|
||||
|
||||
/**
|
||||
* Shared collector for NLS entries across multiple builds.
|
||||
* Create with createNLSCollector() and pass to multiple plugin instances.
|
||||
*/
|
||||
collector: NLSCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collector for NLS entries across multiple esbuild builds.
|
||||
*/
|
||||
export interface NLSCollector {
|
||||
entries: Map<string, NLSEntry>;
|
||||
add(entry: NLSEntry): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shared NLS collector that can be passed to multiple plugin instances.
|
||||
*/
|
||||
export function createNLSCollector(): NLSCollector {
|
||||
const entries = new Map<string, NLSEntry>();
|
||||
return {
|
||||
entries,
|
||||
add(entry: NLSEntry) {
|
||||
entries.set(entry.placeholder, entry);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes NLS collection and writes output files.
|
||||
* Call this after all esbuild builds have completed.
|
||||
*/
|
||||
export async function finalizeNLS(
|
||||
collector: NLSCollector,
|
||||
outDir: string,
|
||||
alsoWriteTo?: string[]
|
||||
): Promise<{ indexMap: Map<string, number>; messageCount: number }> {
|
||||
if (collector.entries.size === 0) {
|
||||
return { indexMap: new Map(), messageCount: 0 };
|
||||
}
|
||||
|
||||
// Sort entries by moduleId, then by key for stable indices
|
||||
const sortedEntries = [...collector.entries.values()].sort((a, b) => {
|
||||
const aKey = typeof a.key === 'string' ? a.key : a.key.key;
|
||||
const bKey = typeof b.key === 'string' ? b.key : b.key.key;
|
||||
const moduleCompare = a.moduleId.localeCompare(b.moduleId);
|
||||
if (moduleCompare !== 0) {
|
||||
return moduleCompare;
|
||||
}
|
||||
return aKey.localeCompare(bKey);
|
||||
});
|
||||
|
||||
// Create index map
|
||||
const indexMap = new Map<string, number>();
|
||||
sortedEntries.forEach((entry, idx) => {
|
||||
indexMap.set(entry.placeholder, idx);
|
||||
});
|
||||
|
||||
// Build NLS metadata
|
||||
const allMessages: string[] = [];
|
||||
const moduleToKeys: Map<string, (string | { key: string; comment: string[] })[]> = new Map();
|
||||
const moduleToMessages: Map<string, string[]> = new Map();
|
||||
|
||||
for (const entry of sortedEntries) {
|
||||
allMessages.push(entry.message);
|
||||
|
||||
if (!moduleToKeys.has(entry.moduleId)) {
|
||||
moduleToKeys.set(entry.moduleId, []);
|
||||
moduleToMessages.set(entry.moduleId, []);
|
||||
}
|
||||
moduleToKeys.get(entry.moduleId)!.push(entry.key);
|
||||
moduleToMessages.get(entry.moduleId)!.push(entry.message);
|
||||
}
|
||||
|
||||
// nls.keys.json: [["moduleId", ["key1", "key2"]], ...]
|
||||
const nlsKeysJson: [string, string[]][] = [];
|
||||
for (const [moduleId, keys] of moduleToKeys) {
|
||||
nlsKeysJson.push([moduleId, keys.map(k => typeof k === 'string' ? k : k.key)]);
|
||||
}
|
||||
|
||||
// nls.metadata.json: { keys: {...}, messages: {...} }
|
||||
const nlsMetadataJson = {
|
||||
keys: Object.fromEntries(moduleToKeys),
|
||||
messages: Object.fromEntries(moduleToMessages)
|
||||
};
|
||||
|
||||
// Write NLS files
|
||||
const allOutDirs = [outDir, ...(alsoWriteTo ?? [])];
|
||||
for (const dir of allOutDirs) {
|
||||
await fs.promises.mkdir(dir, { recursive: true });
|
||||
}
|
||||
|
||||
await Promise.all(allOutDirs.flatMap(dir => [
|
||||
fs.promises.writeFile(
|
||||
path.join(dir, 'nls.messages.json'),
|
||||
JSON.stringify(allMessages)
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(dir, 'nls.keys.json'),
|
||||
JSON.stringify(nlsKeysJson)
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(dir, 'nls.metadata.json'),
|
||||
JSON.stringify(nlsMetadataJson, null, '\t')
|
||||
),
|
||||
fs.promises.writeFile(
|
||||
path.join(dir, 'nls.messages.js'),
|
||||
`/*---------------------------------------------------------\n * Copyright (C) Microsoft Corporation. All rights reserved.\n *--------------------------------------------------------*/\nglobalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(allMessages)};`
|
||||
),
|
||||
]));
|
||||
|
||||
console.log(`[nls] Extracted ${allMessages.length} messages from ${moduleToKeys.size} modules`);
|
||||
|
||||
return { indexMap, messageCount: allMessages.length };
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-processes a JavaScript file to replace NLS placeholders with indices.
|
||||
*/
|
||||
export function postProcessNLS(
|
||||
content: string,
|
||||
indexMap: Map<string, number>,
|
||||
preserveEnglish: boolean
|
||||
): string {
|
||||
return replaceInOutput(content, indexMap, preserveEnglish);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Transformation
|
||||
// ============================================================================
|
||||
|
||||
function transformToPlaceholders(
|
||||
source: string,
|
||||
moduleId: string
|
||||
): { code: string; entries: NLSEntry[] } {
|
||||
const localizeCalls = analyzeLocalizeCalls(source, 'localize');
|
||||
const localize2Calls = analyzeLocalizeCalls(source, 'localize2');
|
||||
|
||||
// Tag calls with their type so we can handle them differently later
|
||||
const taggedLocalize = localizeCalls.map(call => ({ call, isLocalize2: false }));
|
||||
const taggedLocalize2 = localize2Calls.map(call => ({ call, isLocalize2: true }));
|
||||
const allCalls = [...taggedLocalize, ...taggedLocalize2].sort(
|
||||
(a, b) => a.call.keySpan.start.line - b.call.keySpan.start.line ||
|
||||
a.call.keySpan.start.character - b.call.keySpan.start.character
|
||||
);
|
||||
|
||||
if (allCalls.length === 0) {
|
||||
return { code: source, entries: [] };
|
||||
}
|
||||
|
||||
const entries: NLSEntry[] = [];
|
||||
const model = new TextModel(source);
|
||||
|
||||
// Process in reverse order to preserve positions
|
||||
for (const { call, isLocalize2 } of allCalls.reverse()) {
|
||||
const keyParsed = parseLocalizeKeyOrValue(call.key) as string | { key: string; comment: string[] };
|
||||
const messageParsed = parseLocalizeKeyOrValue(call.value);
|
||||
const keyString = typeof keyParsed === 'string' ? keyParsed : keyParsed.key;
|
||||
|
||||
// Use different placeholder prefix for localize vs localize2
|
||||
// localize: message will be replaced with null
|
||||
// localize2: message will be preserved (only key replaced)
|
||||
const prefix = isLocalize2 ? 'NLS2' : 'NLS';
|
||||
const placeholder = `%%${prefix}:${moduleId}#${keyString}%%`;
|
||||
|
||||
entries.push({
|
||||
moduleId,
|
||||
key: keyParsed,
|
||||
message: String(messageParsed),
|
||||
placeholder
|
||||
});
|
||||
|
||||
// Replace the key with the placeholder string
|
||||
model.apply(call.keySpan, `"${placeholder}"`);
|
||||
}
|
||||
|
||||
// Reverse entries to match source order
|
||||
entries.reverse();
|
||||
|
||||
return { code: model.toString(), entries };
|
||||
}
|
||||
|
||||
function replaceInOutput(
|
||||
content: string,
|
||||
indexMap: Map<string, number>,
|
||||
preserveEnglish: boolean
|
||||
): string {
|
||||
// Replace all placeholders in a single pass using regex
|
||||
// Two types of placeholders:
|
||||
// - %%NLS:moduleId#key%% for localize() - message replaced with null
|
||||
// - %%NLS2:moduleId#key%% for localize2() - message preserved
|
||||
// Note: esbuild may use single or double quotes, so we handle both
|
||||
|
||||
if (preserveEnglish) {
|
||||
// Just replace the placeholder with the index (both NLS and NLS2)
|
||||
return content.replace(/["']%%NLS2?:([^%]+)%%["']/g, (match, inner) => {
|
||||
// Try NLS first, then NLS2
|
||||
let placeholder = `%%NLS:${inner}%%`;
|
||||
let index = indexMap.get(placeholder);
|
||||
if (index === undefined) {
|
||||
placeholder = `%%NLS2:${inner}%%`;
|
||||
index = indexMap.get(placeholder);
|
||||
}
|
||||
if (index !== undefined) {
|
||||
return String(index);
|
||||
}
|
||||
// Placeholder not found in map, leave as-is (shouldn't happen)
|
||||
return match;
|
||||
});
|
||||
} else {
|
||||
// For NLS (localize): replace placeholder with index AND replace message with null
|
||||
// For NLS2 (localize2): replace placeholder with index, keep message
|
||||
// Note: Use (?:[^"\\]|\\.)* to properly handle escaped quotes like \" or \\
|
||||
// Note: esbuild may use single or double quotes, so we handle both
|
||||
|
||||
// First handle NLS (localize) - replace both key and message
|
||||
content = content.replace(
|
||||
/["']%%NLS:([^%]+)%%["'](\s*,\s*)(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)/g,
|
||||
(match, inner, comma) => {
|
||||
const placeholder = `%%NLS:${inner}%%`;
|
||||
const index = indexMap.get(placeholder);
|
||||
if (index !== undefined) {
|
||||
return `${index}${comma}null`;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
);
|
||||
|
||||
// Then handle NLS2 (localize2) - replace only key, keep message
|
||||
content = content.replace(
|
||||
/["']%%NLS2:([^%]+)%%["']/g,
|
||||
(match, inner) => {
|
||||
const placeholder = `%%NLS2:${inner}%%`;
|
||||
const index = indexMap.get(placeholder);
|
||||
if (index !== undefined) {
|
||||
return String(index);
|
||||
}
|
||||
return match;
|
||||
}
|
||||
);
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Plugin
|
||||
// ============================================================================
|
||||
|
||||
export function nlsPlugin(options: NLSPluginOptions): esbuild.Plugin {
|
||||
const { collector } = options;
|
||||
|
||||
return {
|
||||
name: 'nls',
|
||||
setup(build) {
|
||||
// Transform TypeScript files to replace localize() calls with placeholders
|
||||
build.onLoad({ filter: /\.ts$/ }, async (args) => {
|
||||
// Skip .d.ts files
|
||||
if (args.path.endsWith('.d.ts')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const source = await fs.promises.readFile(args.path, 'utf-8');
|
||||
|
||||
// Compute module ID (e.g., "vs/editor/editor" from "src/vs/editor/editor.ts")
|
||||
const relativePath = path.relative(options.baseDir, args.path);
|
||||
const moduleId = relativePath
|
||||
.replace(/\\/g, '/')
|
||||
.replace(/\.ts$/, '');
|
||||
|
||||
// Transform localize() calls to placeholders
|
||||
const { code, entries: fileEntries } = transformToPlaceholders(source, moduleId);
|
||||
|
||||
// Collect entries
|
||||
for (const entry of fileEntries) {
|
||||
collector.add(entry);
|
||||
}
|
||||
|
||||
if (fileEntries.length > 0) {
|
||||
return { contents: code, loader: 'ts' };
|
||||
}
|
||||
|
||||
// No NLS calls, return undefined to let esbuild handle normally
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
# Working Notes: New esbuild-based Build System
|
||||
|
||||
> These notes are for AI agents to help with context in new or summarized sessions.
|
||||
|
||||
## Important: Validating Changes
|
||||
|
||||
**The `VS Code - Build` task is NOT needed to validate changes in the `build/` folder!**
|
||||
|
||||
Build scripts in `build/` are TypeScript files that run directly with `tsx` (e.g., `npx tsx build/next/index.ts`). They are not compiled by the main VS Code build.
|
||||
|
||||
To test changes:
|
||||
```bash
|
||||
# Test transpile
|
||||
npx tsx build/next/index.ts transpile --out out-test
|
||||
|
||||
# Test bundle (server-web target to test the auth fix)
|
||||
npx tsx build/next/index.ts bundle --nls --target server-web --out out-vscode-reh-web-test
|
||||
|
||||
# Verify product config was injected
|
||||
grep -l "serverLicense" out-vscode-reh-web-test/vs/code/browser/workbench/workbench.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Files
|
||||
|
||||
- **[index.ts](index.ts)** - Main build orchestrator
|
||||
- `transpile` command: Fast TS → JS using `esbuild.transform()`
|
||||
- `bundle` command: TS → bundled JS using `esbuild.build()`
|
||||
- **[nls-plugin.ts](nls-plugin.ts)** - NLS (localization) esbuild plugin
|
||||
|
||||
### Integration with Old Build
|
||||
|
||||
In [gulpfile.vscode.ts](../gulpfile.vscode.ts#L228-L242), the `core-ci` task uses these new scripts:
|
||||
- `runEsbuildTranspile()` → transpile command
|
||||
- `runEsbuildBundle()` → bundle command
|
||||
|
||||
Old gulp-based bundling renamed to `core-ci-OLD`.
|
||||
|
||||
---
|
||||
|
||||
## Key Learnings
|
||||
|
||||
### 1. Comment Stripping by esbuild
|
||||
|
||||
**Problem:** esbuild strips comments like `/*BUILD->INSERT_PRODUCT_CONFIGURATION*/` during bundling.
|
||||
|
||||
**Solution:** Use an `onLoad` plugin to transform source files BEFORE esbuild processes them. See `fileContentMapperPlugin()` in index.ts.
|
||||
|
||||
**Why post-processing doesn't work:** By the time we post-process the bundled output, the comment placeholder has already been stripped.
|
||||
|
||||
### 2. Authorization Error: "Unauthorized client refused"
|
||||
|
||||
**Root cause:** Missing product configuration in browser bundle.
|
||||
|
||||
**Flow:**
|
||||
1. Browser loads with empty product config (placeholder was stripped)
|
||||
2. `productService.serverLicense` is empty/undefined
|
||||
3. Browser's `SignService.vsda()` can't decrypt vsda WASM (needs serverLicense as key)
|
||||
4. Browser's `sign()` returns original challenge instead of signed value
|
||||
5. Server validates signature → fails
|
||||
6. Server is in built mode (no `VSCODE_DEV`) → rejects connection
|
||||
|
||||
**Fix:** The `fileContentMapperPlugin` now runs during `onLoad`, replacing placeholders before esbuild strips them.
|
||||
|
||||
### 3. Build-Time Placeholders
|
||||
|
||||
Two placeholders that need injection:
|
||||
|
||||
| Placeholder | Location | Purpose |
|
||||
|-------------|----------|---------|
|
||||
| `/*BUILD->INSERT_PRODUCT_CONFIGURATION*/` | `src/vs/platform/product/common/product.ts` | Product config (commit, version, serverLicense, etc.) |
|
||||
| `/*BUILD->INSERT_BUILTIN_EXTENSIONS*/` | `src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts` | List of built-in extensions |
|
||||
|
||||
### 4. Server-web Target Specifics
|
||||
|
||||
- Removes `webEndpointUrlTemplate` from product config (see `tweakProductForServerWeb` in old build)
|
||||
- Uses `.build/extensions` for builtin extensions (not `.build/web/extensions`)
|
||||
|
||||
### 5. Entry Point Parity with Old Build
|
||||
|
||||
**Problem:** The desktop target had `keyboardMapEntryPoints` as separate esbuild entry points, producing `layout.contribution.darwin.js`, `layout.contribution.linux.js`, and `layout.contribution.win.js` as standalone files in the output.
|
||||
|
||||
**Root cause:** In the old build (`gulpfile.vscode.ts`), `vscodeEntryPoints` does NOT include `buildfile.keyboardMaps`. These files are only separate entry points for server-web (`gulpfile.reh.ts`) and web (`gulpfile.vscode.web.ts`). For desktop, they're imported as dependencies of `workbench.desktop.main` and get bundled into it.
|
||||
|
||||
**Fix:** Removed `...keyboardMapEntryPoints` from the `desktop` case in `getEntryPointsForTarget()`. Keep for `server-web` and `web`.
|
||||
|
||||
**Lesson:** Always verify new build entry points against the old build's per-target definitions in `buildfile.ts` and the respective gulpfiles.
|
||||
|
||||
### 6. NLS Output File Parity
|
||||
|
||||
**Problem:** `finalizeNLS()` was generating `nls.messages.js` (with `globalThis._VSCODE_NLS_MESSAGES=...`) in addition to the standard `.json` files. The old build only produces `nls.messages.json`, `nls.keys.json`, and `nls.metadata.json`.
|
||||
|
||||
**Fix:** Removed `nls.messages.js` generation from `finalizeNLS()` in `nls-plugin.ts`.
|
||||
|
||||
**Lesson:** Don't add new output file formats that create parity differences with the old build. The old build is the reference.
|
||||
|
||||
---
|
||||
|
||||
## Testing the Fix
|
||||
|
||||
```bash
|
||||
# Build server-web with new system
|
||||
npx tsx build/next/index.ts bundle --nls --target server-web --out out-vscode-reh-web-min
|
||||
|
||||
# Package it (uses gulp task)
|
||||
npm run gulp vscode-reh-web-darwin-arm64-min
|
||||
|
||||
# Run server
|
||||
./vscode-server-darwin-arm64-web/bin/code-server-oss --connection-token dev-token
|
||||
|
||||
# Open browser - should connect without "Unauthorized client refused"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Open Items / Future Work
|
||||
|
||||
1. **`BUILD_INSERT_PACKAGE_CONFIGURATION`** - Server bootstrap files ([bootstrap-meta.ts](../../src/bootstrap-meta.ts)) have this marker for package.json injection. Currently handled by [inlineMeta.ts](../lib/inlineMeta.ts) in the old build's packaging step.
|
||||
|
||||
2. **Mangling** - The new build doesn't do TypeScript-based mangling yet. Old `core-ci` with mangling is now `core-ci-OLD`.
|
||||
|
||||
3. **Entry point duplication** - Entry points are duplicated between [buildfile.ts](../buildfile.ts) and [index.ts](index.ts). Consider consolidating.
|
||||
|
||||
---
|
||||
|
||||
## Build Comparison: OLD (gulp-tsb) vs NEW (esbuild) — Desktop Build
|
||||
|
||||
### Summary
|
||||
|
||||
| Metric | OLD | NEW | Delta |
|
||||
|--------|-----|-----|-------|
|
||||
| Total files in `out/` | 3993 | 4301 | +309 extra, 1 missing |
|
||||
| Total size of `out/` | 25.8 MB | 64.6 MB | +38.8 MB (2.5×) |
|
||||
| `workbench.desktop.main.js` | 13.0 MB | 15.5 MB | +2.5 MB |
|
||||
|
||||
### 1 Missing File (in OLD, not in NEW)
|
||||
|
||||
| File | Why Missing | Fix |
|
||||
|------|-------------|-----|
|
||||
| `out/vs/platform/browserView/electron-browser/preload-browserView.js` | Not listed in `desktopStandaloneFiles` in index.ts. Only `preload.ts` and `preload-aux.ts` are compiled as standalone files. | **Add** `'vs/platform/browserView/electron-browser/preload-browserView.ts'` to the `desktopStandaloneFiles` array in `index.ts`. |
|
||||
|
||||
### 309 Extra Files (in NEW, not in OLD) — Breakdown
|
||||
|
||||
| Category | Count | Explanation |
|
||||
|----------|-------|-------------|
|
||||
| **CSS files** | 291 | `copyCssFiles()` copies ALL `.css` from `src/` to the output. The old bundler inlines CSS into the main `.css` bundle (e.g., `workbench.desktop.main.css`) and never ships individual CSS files. These individual files ARE needed at runtime because the new ESM system uses `import './foo.css'` resolved by an import map. |
|
||||
| **Vendor JS files** | 3 | `dompurify.js`, `marked.js`, `semver.js` — listed in `commonResourcePatterns`. The old bundler inlines these into the main bundle. The new system keeps them as separate files because they're plain JS (not TS). They're needed. |
|
||||
| **Web workbench bundle** | 1 | `vs/code/browser/workbench/workbench.js` (15.4 MB). This is the web workbench entry point bundle. It should NOT be in a desktop build — the old build explicitly excludes `out-build/vs/code/browser/**`. The `desktopResourcePatterns` in index.ts includes `vs/code/browser/workbench/*.html` and `callback.html` which is correct, but the actual bundle gets written by the esbuild desktop bundle step because the desktop entry points include web entry points. |
|
||||
| **Web workbench internal** | 1 | `vs/workbench/workbench.web.main.internal.js` (15.4 MB). Similar: shouldn't ship in a desktop build. It's output by the esbuild bundler. |
|
||||
| **Keyboard layout contributions** | 3 | `layout.contribution.{darwin,linux,win}.js` — the old bundler inlines these into the main bundle. These are new separate files from the esbuild bundler. |
|
||||
| **NLS files** | 2 | `nls.messages.js` (new) and `nls.metadata.json` (new). The old build has `nls.messages.json` and `nls.keys.json` but not a `.js` version or metadata. The `.js` version is produced by the NLS plugin. |
|
||||
| **HTML files** | 2 | `vs/code/browser/workbench/workbench.html` and `callback.html` — correctly listed in `desktopResourcePatterns` (these are needed for desktop's built-in web server). |
|
||||
| **SVG loading spinners** | 3 | `loading-dark.svg`, `loading-hc.svg`, `loading.svg` in `vs/workbench/contrib/extensions/browser/media/`. The old build only copies `theme-icon.png` and `language-icon.svg` from that folder; the new build's `desktopResourcePatterns` uses `*.svg` which is broader. |
|
||||
| **codicon.ttf (duplicate)** | 1 | At `vs/base/browser/ui/codicons/codicon/codicon.ttf`. The old build copies this to `out/media/codicon.ttf` only. The new build has BOTH: the copy in `out/media/` (from esbuild's `file` loader) AND the original path (from `commonResourcePatterns`). Duplicate. |
|
||||
| **PSReadLine.psm1** | 1 | `vs/workbench/contrib/terminal/common/scripts/psreadline/PSReadLine.psm1` — the old build uses `*.psm1` in `terminal/common/scripts/` (non-recursive?). The new build uses `**/*.psm1` (recursive), picking up this subdirectory file. Check if it's needed. |
|
||||
| **date file** | 1 | `out/date` — build timestamp, produced by the new build's `bundle()` function. The old build doesn't write this; it reads `package.json.date` instead. |
|
||||
|
||||
### Size Increase Breakdown by Area
|
||||
|
||||
| Area | OLD | NEW | Delta | Why |
|
||||
|------|-----|-----|-------|-----|
|
||||
| `vs/code` | 1.5 MB | 17.4 MB | +15.9 MB | Web workbench bundle (15.4 MB) shouldn't be in desktop build |
|
||||
| `vs/workbench` | 18.9 MB | 38.7 MB | +19.8 MB | `workbench.web.main.internal.js` (15.4 MB) + unmangled desktop bundle (+2.5 MB) + individual CSS files (~1 MB) |
|
||||
| `vs/base` | 0 MB | 0.4 MB | +0.4 MB | Individual CSS files + vendor JS |
|
||||
| `vs/editor` | 0.3 MB | 0.5 MB | +0.1 MB | Individual CSS files |
|
||||
| `vs/platform` | 1.7 MB | 1.9 MB | +0.2 MB | Individual CSS files |
|
||||
|
||||
### JS Files with >2× Size Change
|
||||
|
||||
| File | OLD | NEW | Ratio | Reason |
|
||||
|------|-----|-----|-------|--------|
|
||||
| `vs/workbench/contrib/webview/browser/pre/service-worker.js` | 7 KB | 15 KB | 2.2× | Not minified / includes more inlined code |
|
||||
| `vs/code/electron-browser/workbench/workbench.js` | 10 KB | 28 KB | 2.75× | OLD is minified to 6 lines; NEW is 380 lines (not compressed, includes tslib banner) |
|
||||
|
||||
### Action Items
|
||||
|
||||
1. **[CRITICAL] Missing `preload-browserView.ts`** — Add to `desktopStandaloneFiles` in index.ts. Without it, BrowserView (used for Simple Browser) may fail.
|
||||
2. **[SIZE] Web bundles in desktop build** — `workbench.web.main.internal.js` and `vs/code/browser/workbench/workbench.js` together add ~31 MB. These are written by the esbuild bundler and not filtered out. Consider: either don't bundle web entry points for the desktop target, or ensure the packaging step excludes them (currently `packageTask` takes `out-vscode-min/**` without filtering).
|
||||
3. **[SIZE] No mangling** — The desktop main bundle is 2.5 MB larger due to no property mangling. Known open item.
|
||||
4. **[MINOR] Duplicate codicon.ttf** — Exists at both `out/media/codicon.ttf` (from esbuild `file` loader) and `out/vs/base/browser/ui/codicons/codicon/codicon.ttf` (from `commonResourcePatterns`). Consider removing from `commonResourcePatterns` if it's already handled by the loader.
|
||||
5. **[MINOR] Extra SVGs** — `desktopResourcePatterns` uses `*.svg` for extensions media but old build only ships `language-icon.svg`. The loading spinners may be unused in the desktop build.
|
||||
6. **[MINOR] Extra PSReadLine.psm1** from recursive glob — verify if needed.
|
||||
|
||||
---
|
||||
|
||||
## Source Maps
|
||||
|
||||
### Fixes Applied
|
||||
|
||||
1. **`sourcesContent: true`** — Production bundles now embed original TypeScript source content in `.map` files, matching the old build's `includeContent: true` behavior. Without this, crash reports from CDN-hosted source maps can't show original source.
|
||||
|
||||
2. **`--source-map-base-url` option** — The `bundle` command accepts an optional `--source-map-base-url <url>` flag. When set, post-processing rewrites `sourceMappingURL` comments in `.js` and `.css` output files to point to the CDN (e.g., `https://main.vscode-cdn.net/sourcemaps/<commit>/core/vs/...`). This matches the old build's `sourceMappingURL` function in `minifyTask()`. Wired up in `gulpfile.vscode.ts` for `core-ci-esbuild` and `vscode-esbuild-min` tasks.
|
||||
|
||||
### NLS Source Map Accuracy (Decision: Accept Imprecision)
|
||||
|
||||
**Problem:** `postProcessNLS()` replaces `"%%NLS:moduleId#key%%"` placeholders (~40 chars) with short index values like `null` (4 chars) in the final JS output. This shifts column positions without updating the `.map` files.
|
||||
|
||||
**Options considered:**
|
||||
|
||||
| Option | Description | Effort | Accuracy |
|
||||
|--------|-------------|--------|----------|
|
||||
| A. Fixed-width placeholders | Pad placeholders to match replacement length | Hard — indices unknown until all modules are collected across parallel bundles | Perfect |
|
||||
| B. Post-process source map | Parse `.map`, track replacement offsets per line, adjust VLQ mappings | Medium | Perfect |
|
||||
| C. Two-pass build | Assign NLS indices during plugin phase | Not feasible with parallel bundling | N/A |
|
||||
| **D. Accept imprecision** | NLS replacements only affect column positions; line-level debugging works | Zero | Line-level |
|
||||
|
||||
**Decision: Option D — accept imprecision.** Rationale:
|
||||
|
||||
- NLS replacements only shift **columns**, never lines — line-level stack traces and breakpoints remain correct.
|
||||
- Production crash reporting (the primary consumer of CDN source maps) uses line numbers; column-level accuracy is rarely needed.
|
||||
- The old gulp build had the same fundamental issue in its `nls.nls()` step and used `SourceMapConsumer`/`SourceMapGenerator` to fix it — but that approach was fragile and slow.
|
||||
- If column-level precision becomes important later (e.g., for minified+NLS bundles), Option B can be implemented: after NLS replacement, re-parse the source map, walk replacement sites, and adjust column offsets. This is a localized change in the post-processing loop.
|
||||
|
||||
---
|
||||
|
||||
## Self-hosting Setup
|
||||
|
||||
The default `VS Code - Build` task now runs three parallel watchers:
|
||||
|
||||
| Task | What it does | Script |
|
||||
|------|-------------|--------|
|
||||
| **Core - Transpile** | esbuild single-file TS→JS (fast, no type checking) | `watch-client-transpiled` → `npx tsx build/next/index.ts transpile --watch` |
|
||||
| **Core - Typecheck** | gulp-tsb `noEmit` watch (type errors only, no output) | `watch-clientd` → `gulp watch-client` (with `noEmit: true`) |
|
||||
| **Ext - Build** | Extension compilation (unchanged) | `watch-extensionsd` |
|
||||
|
||||
### Key Changes
|
||||
|
||||
- **`compilation.ts`**: `ICompileTaskOptions` gained `noEmit?: boolean`. When set, `overrideOptions.noEmit = true` is passed to tsb. `watchTask()` accepts an optional 4th parameter `{ noEmit?: boolean }`.
|
||||
- **`gulpfile.ts`**: `watchClientTask` no longer runs `rimraf('out')` (the transpiler owns that). Passes `{ noEmit: true }` to `watchTask`.
|
||||
- **`index.ts`**: Watch mode emits `Starting transpilation...` / `Finished transpilation with N errors after X ms` for VS Code problem matcher.
|
||||
- **`tasks.json`**: Old "Core - Build" split into "Core - Transpile" + "Core - Typecheck" with separate problem matchers (owners: `esbuild` vs `typescript`).
|
||||
- **`package.json`**: Added `watch-client-transpile`, `watch-client-transpiled`, `kill-watch-client-transpiled` scripts.
|
||||
Generated
+272
-158
@@ -53,7 +53,7 @@
|
||||
"ansi-colors": "^3.2.3",
|
||||
"byline": "^5.0.0",
|
||||
"debug": "^4.3.2",
|
||||
"dmg-builder": "^26.7.0",
|
||||
"dmg-builder": "26.5.0",
|
||||
"esbuild": "0.27.2",
|
||||
"extract-zip": "^2.0.1",
|
||||
"gulp-merge-json": "^2.1.1",
|
||||
@@ -813,13 +813,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.0.3.tgz",
|
||||
"integrity": "sha512-u9vpTHRMkOYCs/1FLiSVAFZ7FbjsXK+bQuzviJZa+lG7BHZl1nz52/IcGvwa3sk80/fc3llutBkbCq10Vh8WQA==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.0.1.tgz",
|
||||
"integrity": "sha512-iMGXb6Ib7H/Q3v+BKZJoETgF9g6KMNZVbsO4b7Dmpgb5qTFqyFTzqW9F3TOSHdybv2vKYKzSS9OiZL+dcJb+1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@malept/cross-spawn-promise": "^2.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"detect-libc": "^2.0.1",
|
||||
"got": "^11.7.0",
|
||||
@@ -830,7 +831,7 @@
|
||||
"ora": "^5.1.0",
|
||||
"read-binary-file-arch": "^1.0.6",
|
||||
"semver": "^7.3.5",
|
||||
"tar": "^7.5.6",
|
||||
"tar": "^6.0.5",
|
||||
"yargs": "^17.0.1"
|
||||
},
|
||||
"bin": {
|
||||
@@ -840,6 +841,142 @@
|
||||
"node": ">=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/chownr": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/fs-minipass": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/fs-minipass/node_modules/minipass": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/minipass": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
||||
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/minizlib": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/minizlib/node_modules/minipass": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/node-abi": {
|
||||
"version": "4.26.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.26.0.tgz",
|
||||
@@ -854,9 +991,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -866,6 +1003,38 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/rebuild/node_modules/tar": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
|
||||
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
|
||||
"deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"minipass": "^5.0.0",
|
||||
"minizlib": "^2.1.1",
|
||||
"mkdirp": "^1.0.3",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/universal": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz",
|
||||
@@ -1402,9 +1571,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/brace-expansion": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
|
||||
"integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
|
||||
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1623,9 +1792,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@npmcli/fs/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -2967,19 +3136,18 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/app-builder-lib": {
|
||||
"version": "26.7.0",
|
||||
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.7.0.tgz",
|
||||
"integrity": "sha512-/UgCD8VrO79Wv8aBNpjMfsS1pIUfIPURoRn0Ik6tMe5avdZF+vQgl/juJgipcMmH3YS0BD573lCdCHyoi84USg==",
|
||||
"version": "26.5.0",
|
||||
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.5.0.tgz",
|
||||
"integrity": "sha512-iRRiJhM0uFMauDeIuv8ESHZSn+LESbdDEuHi7rKdeETjrvBObecXnWJx1f3vs3KtoGcd3hCk1zURKypyvZOtFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@develar/schema-utils": "~2.6.5",
|
||||
"@electron/asar": "3.4.1",
|
||||
"@electron/fuses": "^1.8.0",
|
||||
"@electron/get": "^3.0.0",
|
||||
"@electron/notarize": "2.5.0",
|
||||
"@electron/osx-sign": "1.3.3",
|
||||
"@electron/rebuild": "^4.0.3",
|
||||
"@electron/rebuild": "4.0.1",
|
||||
"@electron/universal": "2.0.3",
|
||||
"@malept/flatpak-bundler": "^0.4.0",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
@@ -2992,7 +3160,7 @@
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv-expand": "^11.0.6",
|
||||
"ejs": "^3.1.8",
|
||||
"electron-publish": "26.6.0",
|
||||
"electron-publish": "26.4.1",
|
||||
"fs-extra": "^10.1.0",
|
||||
"hosted-git-info": "^4.1.0",
|
||||
"isbinaryfile": "^5.0.0",
|
||||
@@ -3002,10 +3170,9 @@
|
||||
"lazy-val": "^1.0.5",
|
||||
"minimatch": "^10.0.3",
|
||||
"plist": "3.1.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"resedit": "^1.7.0",
|
||||
"semver": "~7.7.3",
|
||||
"tar": "^7.5.7",
|
||||
"tar": "7.5.3",
|
||||
"temp-file": "^3.4.0",
|
||||
"tiny-async-pool": "1.3.0",
|
||||
"which": "^5.0.0"
|
||||
@@ -3014,55 +3181,8 @@
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"dmg-builder": "26.7.0",
|
||||
"electron-builder-squirrel-windows": "26.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/@electron/get": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz",
|
||||
"integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"env-paths": "^2.2.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"got": "^11.8.5",
|
||||
"progress": "^2.0.3",
|
||||
"semver": "^6.2.0",
|
||||
"sumchecker": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"global-agent": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/@electron/get/node_modules/fs-extra": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6 <7 || >=8"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"dmg-builder": "26.5.0",
|
||||
"electron-builder-squirrel-windows": "26.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/@electron/osx-sign": {
|
||||
@@ -3122,37 +3242,14 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/fs-extra/node_modules/jsonfile": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/fs-extra/node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/isexe": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.2.tgz",
|
||||
"integrity": "sha512-mIcis6w+JiQf3P7t7mg/35GKB4T1FQsBOtMIvuKw4YErj5RjtbhcTd5/I30fmkmGMwvI0WlzSNN+27K0QCMkAw==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
|
||||
"integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/js-yaml": {
|
||||
@@ -3168,14 +3265,27 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/jsonfile": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/minimatch": {
|
||||
"version": "10.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz",
|
||||
"integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
|
||||
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.1"
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
@@ -3185,9 +3295,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -3197,6 +3307,16 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/app-builder-lib/node_modules/which": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
|
||||
@@ -3278,13 +3398,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async-done": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz",
|
||||
@@ -3709,7 +3822,6 @@
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
@@ -4483,13 +4595,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dmg-builder": {
|
||||
"version": "26.7.0",
|
||||
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.7.0.tgz",
|
||||
"integrity": "sha512-uOOBA3f+kW3o4KpSoMQ6SNpdXU7WtxlJRb9vCZgOvqhTz4b3GjcoWKstdisizNZLsylhTMv8TLHFPFW0Uxsj/g==",
|
||||
"version": "26.5.0",
|
||||
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.5.0.tgz",
|
||||
"integrity": "sha512-AyOCzpS1TCxDkSWxAzpfw5l7jBX4C8jKCucmT/6y6/24H5VKSHpjcVJD0W8o5BrFi+skC7Z7+F4aNyHmvn4AAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"app-builder-lib": "26.7.0",
|
||||
"app-builder-lib": "26.5.0",
|
||||
"builder-util": "26.4.1",
|
||||
"fs-extra": "^10.1.0",
|
||||
"iconv-lite": "^0.6.2",
|
||||
@@ -4771,9 +4883,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-publish": {
|
||||
"version": "26.6.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.6.0.tgz",
|
||||
"integrity": "sha512-LsyHMMqbvJ2vsOvuWJ19OezgF2ANdCiHpIucDHNiLhuI+/F3eW98ouzWSRmXXi82ZOPZXC07jnIravY4YYwCLQ==",
|
||||
"version": "26.4.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.4.1.tgz",
|
||||
"integrity": "sha512-nByal9K5Ar3BNJUfCSglXltpKUhJqpwivNpKVHnkwxTET9LKl+NxoojpGF1dSXVFcoBKVm+OhsVa28ZsoshEPA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4781,7 +4893,7 @@
|
||||
"builder-util": "26.4.1",
|
||||
"builder-util-runtime": "9.5.1",
|
||||
"chalk": "^4.1.2",
|
||||
"form-data": "^4.0.5",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"lazy-val": "^1.0.5",
|
||||
"mime": "^2.5.2"
|
||||
@@ -5401,9 +5513,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6220,6 +6332,13 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jake/node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
||||
@@ -6892,6 +7011,19 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||
@@ -6982,9 +7114,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-api-version/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -7032,19 +7164,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp/node_modules/isexe": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.2.tgz",
|
||||
"integrity": "sha512-mIcis6w+JiQf3P7t7mg/35GKB4T1FQsBOtMIvuKw4YErj5RjtbhcTd5/I30fmkmGMwvI0WlzSNN+27K0QCMkAw==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
|
||||
"integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -7775,25 +7907,6 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/proper-lockfile": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
|
||||
"integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"retry": "^0.12.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/proper-lockfile/node_modules/signal-exit": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
@@ -8941,9 +9054,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "7.5.7",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
|
||||
"integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
|
||||
"version": "7.5.3",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.3.tgz",
|
||||
"integrity": "sha512-ENg5JUHUm2rDD7IvKNFGzyElLXNjachNLp6RaGf4+JOgxXHkqA+gq81ZAMCUmtMtqBsoU62lcp6S27g1LCYGGQ==",
|
||||
"deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@
|
||||
"ansi-colors": "^3.2.3",
|
||||
"byline": "^5.0.0",
|
||||
"debug": "^4.3.2",
|
||||
"dmg-builder": "^26.7.0",
|
||||
"dmg-builder": "26.5.0",
|
||||
"esbuild": "0.27.2",
|
||||
"extract-zip": "^2.0.1",
|
||||
"gulp-merge-json": "^2.1.1",
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { observableValue } from '../../../src/vs/base/common/observable';
|
||||
import { createAiStatsHover, IAiStatsHoverData } from '../../../src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsStatusBar';
|
||||
import { ISessionData } from '../../../src/vs/workbench/contrib/editTelemetry/browser/editStats/aiStatsChart';
|
||||
import { Random } from '../../../src/vs/editor/test/common/core/random';
|
||||
import { ComponentFixtureContext, defineComponentFixture, defineThemedFixtureGroup } from './fixtureUtils';
|
||||
|
||||
export default defineThemedFixtureGroup({
|
||||
AiStatsHover: defineComponentFixture({
|
||||
render: (context) => renderAiStatsHover({ ...context, data: createSampleDataWithSessions() }),
|
||||
}),
|
||||
|
||||
AiStatsHoverNoData: defineComponentFixture({
|
||||
render: (context) => renderAiStatsHover({ ...context, data: createEmptyData() }),
|
||||
}),
|
||||
});
|
||||
|
||||
function createSampleDataWithSessions(): IAiStatsHoverData {
|
||||
const random = Random.create(42);
|
||||
|
||||
// Use a fixed base time for determinism (Jan 1, 2025, 12:00:00 UTC)
|
||||
const baseTime = 1735732800000;
|
||||
const dayMs = 24 * 60 * 60 * 1000;
|
||||
const sessionLengthMs = 5 * 60 * 1000;
|
||||
|
||||
// Generate fake session data for the last 7 days
|
||||
const fakeSessions: ISessionData[] = [];
|
||||
for (let day = 6; day >= 0; day--) {
|
||||
const dayStart = baseTime - day * dayMs;
|
||||
const sessionsPerDay = random.nextIntRange(3, 9);
|
||||
for (let s = 0; s < sessionsPerDay; s++) {
|
||||
const sessionTime = dayStart + s * sessionLengthMs * 2;
|
||||
fakeSessions.push({
|
||||
startTime: sessionTime,
|
||||
typedCharacters: random.nextIntRange(100, 600),
|
||||
aiCharacters: random.nextIntRange(200, 1000),
|
||||
acceptedInlineSuggestions: random.nextIntRange(1, 16),
|
||||
chatEditCount: random.nextIntRange(0, 5),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const totalAi = fakeSessions.reduce((sum, s) => sum + s.aiCharacters, 0);
|
||||
const totalTyped = fakeSessions.reduce((sum, s) => sum + s.typedCharacters, 0);
|
||||
const aiRate = totalAi / (totalAi + totalTyped);
|
||||
|
||||
// "Today" for the fixture is the baseTime day
|
||||
const startOfToday = baseTime - (baseTime % dayMs);
|
||||
const todaySessions = fakeSessions.filter(s => s.startTime >= startOfToday);
|
||||
const acceptedToday = todaySessions.reduce((sum, s) => sum + (s.acceptedInlineSuggestions ?? 0), 0);
|
||||
|
||||
return {
|
||||
aiRate: observableValue('aiRate', aiRate),
|
||||
acceptedInlineSuggestionsToday: observableValue('acceptedToday', acceptedToday),
|
||||
sessions: observableValue('sessions', fakeSessions),
|
||||
};
|
||||
}
|
||||
|
||||
function createEmptyData(): IAiStatsHoverData {
|
||||
return {
|
||||
aiRate: observableValue('aiRate', 0),
|
||||
acceptedInlineSuggestionsToday: observableValue('acceptedToday', 0),
|
||||
sessions: observableValue('sessions', []),
|
||||
};
|
||||
}
|
||||
|
||||
interface RenderAiStatsOptions extends ComponentFixtureContext {
|
||||
data: IAiStatsHoverData;
|
||||
}
|
||||
|
||||
function renderAiStatsHover({ container, disposableStore, data }: RenderAiStatsOptions): HTMLElement {
|
||||
container.style.width = '320px';
|
||||
container.style.padding = '8px';
|
||||
container.style.backgroundColor = 'var(--vscode-editorHoverWidget-background)';
|
||||
container.style.border = '1px solid var(--vscode-editorHoverWidget-border)';
|
||||
container.style.borderRadius = '4px';
|
||||
container.style.color = 'var(--vscode-editorHoverWidget-foreground)';
|
||||
|
||||
const hover = createAiStatsHover({
|
||||
data,
|
||||
onOpenSettings: () => console.log('Open settings clicked'),
|
||||
});
|
||||
|
||||
const elem = hover.keepUpdated(disposableStore).element;
|
||||
container.appendChild(elem);
|
||||
|
||||
return container;
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { $ } from '../../../src/vs/base/browser/dom';
|
||||
import { Codicon } from '../../../src/vs/base/common/codicons';
|
||||
import { ThemeIcon } from '../../../src/vs/base/common/themables';
|
||||
import { Action, Separator } from '../../../src/vs/base/common/actions';
|
||||
|
||||
// UI Components
|
||||
import { Button, ButtonBar, ButtonWithDescription, unthemedButtonStyles } from '../../../src/vs/base/browser/ui/button/button';
|
||||
import { Toggle, Checkbox, unthemedToggleStyles } from '../../../src/vs/base/browser/ui/toggle/toggle';
|
||||
import { InputBox, MessageType, unthemedInboxStyles } from '../../../src/vs/base/browser/ui/inputbox/inputBox';
|
||||
import { CountBadge } from '../../../src/vs/base/browser/ui/countBadge/countBadge';
|
||||
import { ActionBar } from '../../../src/vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ProgressBar } from '../../../src/vs/base/browser/ui/progressbar/progressbar';
|
||||
import { HighlightedLabel } from '../../../src/vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
|
||||
import { ComponentFixtureContext, defineComponentFixture, defineThemedFixtureGroup } from './fixtureUtils';
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Styles (themed versions for fixture display)
|
||||
// ============================================================================
|
||||
|
||||
const themedButtonStyles = {
|
||||
...unthemedButtonStyles,
|
||||
buttonBackground: 'var(--vscode-button-background)',
|
||||
buttonHoverBackground: 'var(--vscode-button-hoverBackground)',
|
||||
buttonForeground: 'var(--vscode-button-foreground)',
|
||||
buttonSecondaryBackground: 'var(--vscode-button-secondaryBackground)',
|
||||
buttonSecondaryHoverBackground: 'var(--vscode-button-secondaryHoverBackground)',
|
||||
buttonSecondaryForeground: 'var(--vscode-button-secondaryForeground)',
|
||||
buttonBorder: 'var(--vscode-button-border)',
|
||||
};
|
||||
|
||||
const themedToggleStyles = {
|
||||
...unthemedToggleStyles,
|
||||
inputActiveOptionBorder: 'var(--vscode-inputOption-activeBorder)',
|
||||
inputActiveOptionForeground: 'var(--vscode-inputOption-activeForeground)',
|
||||
inputActiveOptionBackground: 'var(--vscode-inputOption-activeBackground)',
|
||||
};
|
||||
|
||||
const themedCheckboxStyles = {
|
||||
checkboxBackground: 'var(--vscode-checkbox-background)',
|
||||
checkboxBorder: 'var(--vscode-checkbox-border)',
|
||||
checkboxForeground: 'var(--vscode-checkbox-foreground)',
|
||||
checkboxDisabledBackground: undefined,
|
||||
checkboxDisabledForeground: undefined,
|
||||
};
|
||||
|
||||
const themedInputBoxStyles = {
|
||||
...unthemedInboxStyles,
|
||||
inputBackground: 'var(--vscode-input-background)',
|
||||
inputForeground: 'var(--vscode-input-foreground)',
|
||||
inputBorder: 'var(--vscode-input-border)',
|
||||
inputValidationInfoBackground: 'var(--vscode-inputValidation-infoBackground)',
|
||||
inputValidationInfoBorder: 'var(--vscode-inputValidation-infoBorder)',
|
||||
inputValidationWarningBackground: 'var(--vscode-inputValidation-warningBackground)',
|
||||
inputValidationWarningBorder: 'var(--vscode-inputValidation-warningBorder)',
|
||||
inputValidationErrorBackground: 'var(--vscode-inputValidation-errorBackground)',
|
||||
inputValidationErrorBorder: 'var(--vscode-inputValidation-errorBorder)',
|
||||
};
|
||||
|
||||
const themedBadgeStyles = {
|
||||
badgeBackground: 'var(--vscode-badge-background)',
|
||||
badgeForeground: 'var(--vscode-badge-foreground)',
|
||||
badgeBorder: undefined,
|
||||
};
|
||||
|
||||
const themedProgressBarOptions = {
|
||||
progressBarBackground: 'var(--vscode-progressBar-background)',
|
||||
};
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Buttons
|
||||
// ============================================================================
|
||||
|
||||
function renderButtons({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '12px';
|
||||
|
||||
// Section: Primary Buttons
|
||||
const primarySection = $('div');
|
||||
primarySection.style.display = 'flex';
|
||||
primarySection.style.gap = '8px';
|
||||
primarySection.style.alignItems = 'center';
|
||||
container.appendChild(primarySection);
|
||||
|
||||
const primaryButton = disposableStore.add(new Button(primarySection, { ...themedButtonStyles, title: 'Primary button' }));
|
||||
primaryButton.label = 'Primary Button';
|
||||
|
||||
const primaryIconButton = disposableStore.add(new Button(primarySection, { ...themedButtonStyles, title: 'With Icon', supportIcons: true }));
|
||||
primaryIconButton.label = '$(add) Add Item';
|
||||
|
||||
const smallButton = disposableStore.add(new Button(primarySection, { ...themedButtonStyles, title: 'Small button', small: true }));
|
||||
smallButton.label = 'Small';
|
||||
|
||||
// Section: Secondary Buttons
|
||||
const secondarySection = $('div');
|
||||
secondarySection.style.display = 'flex';
|
||||
secondarySection.style.gap = '8px';
|
||||
secondarySection.style.alignItems = 'center';
|
||||
container.appendChild(secondarySection);
|
||||
|
||||
const secondaryButton = disposableStore.add(new Button(secondarySection, { ...themedButtonStyles, secondary: true, title: 'Secondary button' }));
|
||||
secondaryButton.label = 'Secondary Button';
|
||||
|
||||
const secondaryIconButton = disposableStore.add(new Button(secondarySection, { ...themedButtonStyles, secondary: true, title: 'Cancel', supportIcons: true }));
|
||||
secondaryIconButton.label = '$(close) Cancel';
|
||||
|
||||
// Section: Disabled Buttons
|
||||
const disabledSection = $('div');
|
||||
disabledSection.style.display = 'flex';
|
||||
disabledSection.style.gap = '8px';
|
||||
disabledSection.style.alignItems = 'center';
|
||||
container.appendChild(disabledSection);
|
||||
|
||||
const disabledButton = disposableStore.add(new Button(disabledSection, { ...themedButtonStyles, title: 'Disabled', disabled: true }));
|
||||
disabledButton.label = 'Disabled';
|
||||
disabledButton.enabled = false;
|
||||
|
||||
const disabledSecondary = disposableStore.add(new Button(disabledSection, { ...themedButtonStyles, secondary: true, title: 'Disabled Secondary', disabled: true }));
|
||||
disabledSecondary.label = 'Disabled Secondary';
|
||||
disabledSecondary.enabled = false;
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
function renderButtonBar({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '16px';
|
||||
|
||||
// Button Bar
|
||||
const barContainer = $('div');
|
||||
container.appendChild(barContainer);
|
||||
|
||||
const buttonBar = new ButtonBar(barContainer);
|
||||
disposableStore.add(buttonBar);
|
||||
|
||||
const okButton = buttonBar.addButton({ ...themedButtonStyles, title: 'OK' });
|
||||
okButton.label = 'OK';
|
||||
|
||||
const cancelButton = buttonBar.addButton({ ...themedButtonStyles, secondary: true, title: 'Cancel' });
|
||||
cancelButton.label = 'Cancel';
|
||||
|
||||
// Button with Description
|
||||
const descContainer = $('div');
|
||||
descContainer.style.width = '300px';
|
||||
container.appendChild(descContainer);
|
||||
|
||||
const buttonWithDesc = disposableStore.add(new ButtonWithDescription(descContainer, { ...themedButtonStyles, title: 'Install Extension', supportIcons: true }));
|
||||
buttonWithDesc.label = '$(extensions) Install Extension';
|
||||
buttonWithDesc.description = 'This will install the extension and enable it globally';
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Toggles and Checkboxes
|
||||
// ============================================================================
|
||||
|
||||
function renderToggles({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '12px';
|
||||
|
||||
// Toggles
|
||||
const toggleSection = $('div');
|
||||
toggleSection.style.display = 'flex';
|
||||
toggleSection.style.gap = '16px';
|
||||
toggleSection.style.alignItems = 'center';
|
||||
container.appendChild(toggleSection);
|
||||
|
||||
const toggle1 = disposableStore.add(new Toggle({
|
||||
...themedToggleStyles,
|
||||
title: 'Case Sensitive',
|
||||
isChecked: false,
|
||||
icon: Codicon.caseSensitive,
|
||||
}));
|
||||
toggleSection.appendChild(toggle1.domNode);
|
||||
|
||||
const toggle2 = disposableStore.add(new Toggle({
|
||||
...themedToggleStyles,
|
||||
title: 'Whole Word',
|
||||
isChecked: true,
|
||||
icon: Codicon.wholeWord,
|
||||
}));
|
||||
toggleSection.appendChild(toggle2.domNode);
|
||||
|
||||
const toggle3 = disposableStore.add(new Toggle({
|
||||
...themedToggleStyles,
|
||||
title: 'Use Regular Expression',
|
||||
isChecked: false,
|
||||
icon: Codicon.regex,
|
||||
}));
|
||||
toggleSection.appendChild(toggle3.domNode);
|
||||
|
||||
// Checkboxes
|
||||
const checkboxSection = $('div');
|
||||
checkboxSection.style.display = 'flex';
|
||||
checkboxSection.style.flexDirection = 'column';
|
||||
checkboxSection.style.gap = '8px';
|
||||
container.appendChild(checkboxSection);
|
||||
|
||||
const createCheckboxRow = (label: string, checked: boolean) => {
|
||||
const row = $('div');
|
||||
row.style.display = 'flex';
|
||||
row.style.alignItems = 'center';
|
||||
row.style.gap = '8px';
|
||||
|
||||
const checkbox = disposableStore.add(new Checkbox(label, checked, themedCheckboxStyles));
|
||||
row.appendChild(checkbox.domNode);
|
||||
|
||||
const labelEl = $('span');
|
||||
labelEl.textContent = label;
|
||||
labelEl.style.color = 'var(--vscode-foreground)';
|
||||
row.appendChild(labelEl);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
checkboxSection.appendChild(createCheckboxRow('Enable auto-save', true));
|
||||
checkboxSection.appendChild(createCheckboxRow('Show line numbers', true));
|
||||
checkboxSection.appendChild(createCheckboxRow('Word wrap', false));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Input Boxes
|
||||
// ============================================================================
|
||||
|
||||
function renderInputBoxes({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '16px';
|
||||
container.style.width = '350px';
|
||||
|
||||
// Normal input
|
||||
const normalInput = disposableStore.add(new InputBox(container, undefined, {
|
||||
placeholder: 'Enter search query...',
|
||||
inputBoxStyles: themedInputBoxStyles,
|
||||
}));
|
||||
|
||||
// Input with value
|
||||
const filledInput = disposableStore.add(new InputBox(container, undefined, {
|
||||
placeholder: 'File path',
|
||||
inputBoxStyles: themedInputBoxStyles,
|
||||
}));
|
||||
filledInput.value = '/src/vs/editor/browser';
|
||||
|
||||
// Input with info validation
|
||||
const infoInput = disposableStore.add(new InputBox(container, undefined, {
|
||||
placeholder: 'Username',
|
||||
inputBoxStyles: themedInputBoxStyles,
|
||||
validationOptions: {
|
||||
validation: (value) => value.length < 3 ? { content: 'Username must be at least 3 characters', type: MessageType.INFO } : null
|
||||
}
|
||||
}));
|
||||
infoInput.value = 'ab';
|
||||
infoInput.validate();
|
||||
|
||||
// Input with warning validation
|
||||
const warningInput = disposableStore.add(new InputBox(container, undefined, {
|
||||
placeholder: 'Password',
|
||||
inputBoxStyles: themedInputBoxStyles,
|
||||
validationOptions: {
|
||||
validation: (value) => value.length < 8 ? { content: 'Password should be at least 8 characters for security', type: MessageType.WARNING } : null
|
||||
}
|
||||
}));
|
||||
warningInput.value = 'pass';
|
||||
warningInput.validate();
|
||||
|
||||
// Input with error validation
|
||||
const errorInput = disposableStore.add(new InputBox(container, undefined, {
|
||||
placeholder: 'Email address',
|
||||
inputBoxStyles: themedInputBoxStyles,
|
||||
validationOptions: {
|
||||
validation: (value) => !value.includes('@') ? { content: 'Please enter a valid email address', type: MessageType.ERROR } : null
|
||||
}
|
||||
}));
|
||||
errorInput.value = 'invalid-email';
|
||||
errorInput.validate();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Count Badges
|
||||
// ============================================================================
|
||||
|
||||
function renderCountBadges({ container }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.gap = '12px';
|
||||
container.style.alignItems = 'center';
|
||||
|
||||
// Various badge counts
|
||||
const counts = [1, 5, 12, 99, 999];
|
||||
|
||||
for (const count of counts) {
|
||||
const badgeContainer = $('div');
|
||||
badgeContainer.style.display = 'flex';
|
||||
badgeContainer.style.alignItems = 'center';
|
||||
badgeContainer.style.gap = '8px';
|
||||
|
||||
const label = $('span');
|
||||
label.textContent = 'Issues';
|
||||
label.style.color = 'var(--vscode-foreground)';
|
||||
badgeContainer.appendChild(label);
|
||||
|
||||
new CountBadge(badgeContainer, { count }, themedBadgeStyles);
|
||||
container.appendChild(badgeContainer);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Action Bar
|
||||
// ============================================================================
|
||||
|
||||
function renderActionBar({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '16px';
|
||||
|
||||
// Horizontal action bar
|
||||
const horizontalLabel = $('div');
|
||||
horizontalLabel.textContent = 'Horizontal Actions:';
|
||||
horizontalLabel.style.color = 'var(--vscode-foreground)';
|
||||
horizontalLabel.style.marginBottom = '4px';
|
||||
container.appendChild(horizontalLabel);
|
||||
|
||||
const horizontalContainer = $('div');
|
||||
container.appendChild(horizontalContainer);
|
||||
|
||||
const horizontalBar = disposableStore.add(new ActionBar(horizontalContainer, {
|
||||
ariaLabel: 'Editor Actions',
|
||||
}));
|
||||
|
||||
horizontalBar.push([
|
||||
new Action('editor.action.save', 'Save', ThemeIcon.asClassName(Codicon.save), true, async () => console.log('Save')),
|
||||
new Action('editor.action.undo', 'Undo', ThemeIcon.asClassName(Codicon.discard), true, async () => console.log('Undo')),
|
||||
new Action('editor.action.redo', 'Redo', ThemeIcon.asClassName(Codicon.redo), true, async () => console.log('Redo')),
|
||||
new Separator(),
|
||||
new Action('editor.action.find', 'Find', ThemeIcon.asClassName(Codicon.search), true, async () => console.log('Find')),
|
||||
new Action('editor.action.replace', 'Replace', ThemeIcon.asClassName(Codicon.replaceAll), true, async () => console.log('Replace')),
|
||||
]);
|
||||
|
||||
// Action bar with disabled items
|
||||
const mixedLabel = $('div');
|
||||
mixedLabel.textContent = 'Mixed States:';
|
||||
mixedLabel.style.color = 'var(--vscode-foreground)';
|
||||
mixedLabel.style.marginBottom = '4px';
|
||||
container.appendChild(mixedLabel);
|
||||
|
||||
const mixedContainer = $('div');
|
||||
container.appendChild(mixedContainer);
|
||||
|
||||
const mixedBar = disposableStore.add(new ActionBar(mixedContainer, {
|
||||
ariaLabel: 'Mixed Actions',
|
||||
}));
|
||||
|
||||
mixedBar.push([
|
||||
new Action('action.enabled', 'Enabled', ThemeIcon.asClassName(Codicon.play), true, async () => { }),
|
||||
new Action('action.disabled', 'Disabled', ThemeIcon.asClassName(Codicon.debugPause), false, async () => { }),
|
||||
new Action('action.enabled2', 'Enabled', ThemeIcon.asClassName(Codicon.debugStop), true, async () => { }),
|
||||
]);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Progress Bar
|
||||
// ============================================================================
|
||||
|
||||
function renderProgressBars({ container, disposableStore }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '24px';
|
||||
container.style.width = '400px';
|
||||
|
||||
const createSection = (label: string) => {
|
||||
const section = $('div');
|
||||
const labelEl = $('div');
|
||||
labelEl.textContent = label;
|
||||
labelEl.style.color = 'var(--vscode-foreground)';
|
||||
labelEl.style.marginBottom = '8px';
|
||||
labelEl.style.fontSize = '12px';
|
||||
section.appendChild(labelEl);
|
||||
|
||||
// Progress bar container with proper constraints
|
||||
const barContainer = $('div');
|
||||
barContainer.style.position = 'relative';
|
||||
barContainer.style.width = '100%';
|
||||
barContainer.style.height = '4px';
|
||||
barContainer.style.overflow = 'hidden';
|
||||
section.appendChild(barContainer);
|
||||
|
||||
container.appendChild(section);
|
||||
return barContainer;
|
||||
};
|
||||
|
||||
// Infinite progress
|
||||
const infiniteSection = createSection('Infinite Progress (loading...)');
|
||||
const infiniteBar = disposableStore.add(new ProgressBar(infiniteSection, themedProgressBarOptions));
|
||||
infiniteBar.infinite();
|
||||
|
||||
// Discrete progress - 30%
|
||||
const progress30Section = createSection('Discrete Progress - 30%');
|
||||
const progress30Bar = disposableStore.add(new ProgressBar(progress30Section, themedProgressBarOptions));
|
||||
progress30Bar.total(100);
|
||||
progress30Bar.worked(30);
|
||||
|
||||
// Discrete progress - 60%
|
||||
const progress60Section = createSection('Discrete Progress - 60%');
|
||||
const progress60Bar = disposableStore.add(new ProgressBar(progress60Section, themedProgressBarOptions));
|
||||
progress60Bar.total(100);
|
||||
progress60Bar.worked(60);
|
||||
|
||||
// Discrete progress - 90%
|
||||
const progress90Section = createSection('Discrete Progress - 90%');
|
||||
const progress90Bar = disposableStore.add(new ProgressBar(progress90Section, themedProgressBarOptions));
|
||||
progress90Bar.total(100);
|
||||
progress90Bar.worked(90);
|
||||
|
||||
// Completed progress
|
||||
const doneSection = createSection('Completed (100%)');
|
||||
const doneBar = disposableStore.add(new ProgressBar(doneSection, themedProgressBarOptions));
|
||||
doneBar.total(100);
|
||||
doneBar.worked(100);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Highlighted Label
|
||||
// ============================================================================
|
||||
|
||||
function renderHighlightedLabels({ container }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.padding = '16px';
|
||||
container.style.display = 'flex';
|
||||
container.style.flexDirection = 'column';
|
||||
container.style.gap = '8px';
|
||||
container.style.color = 'var(--vscode-foreground)';
|
||||
|
||||
const createHighlightedLabel = (text: string, highlights: { start: number; end: number }[]) => {
|
||||
const row = $('div');
|
||||
row.style.display = 'flex';
|
||||
row.style.alignItems = 'center';
|
||||
row.style.gap = '8px';
|
||||
|
||||
const labelContainer = $('div');
|
||||
const label = new HighlightedLabel(labelContainer);
|
||||
label.set(text, highlights);
|
||||
row.appendChild(labelContainer);
|
||||
|
||||
const queryLabel = $('span');
|
||||
queryLabel.style.color = 'var(--vscode-descriptionForeground)';
|
||||
queryLabel.style.fontSize = '12px';
|
||||
queryLabel.textContent = `(matches highlighted)`;
|
||||
row.appendChild(queryLabel);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
// File search examples
|
||||
container.appendChild(createHighlightedLabel('codeEditorWidget.ts', [{ start: 0, end: 4 }])); // "code"
|
||||
container.appendChild(createHighlightedLabel('inlineCompletionsController.ts', [{ start: 6, end: 10 }])); // "Comp"
|
||||
container.appendChild(createHighlightedLabel('diffEditorViewModel.ts', [{ start: 0, end: 4 }, { start: 10, end: 14 }])); // "diff" and "View"
|
||||
container.appendChild(createHighlightedLabel('workbenchTestServices.ts', [{ start: 9, end: 13 }])); // "Test"
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Export Fixtures
|
||||
// ============================================================================
|
||||
|
||||
export default defineThemedFixtureGroup({
|
||||
Buttons: defineComponentFixture({
|
||||
render: renderButtons,
|
||||
}),
|
||||
|
||||
ButtonBar: defineComponentFixture({
|
||||
render: renderButtonBar,
|
||||
}),
|
||||
|
||||
Toggles: defineComponentFixture({
|
||||
render: renderToggles,
|
||||
}),
|
||||
|
||||
InputBoxes: defineComponentFixture({
|
||||
render: renderInputBoxes,
|
||||
}),
|
||||
|
||||
CountBadges: defineComponentFixture({
|
||||
render: renderCountBadges,
|
||||
}),
|
||||
|
||||
ActionBar: defineComponentFixture({
|
||||
render: renderActionBar,
|
||||
}),
|
||||
|
||||
ProgressBars: defineComponentFixture({
|
||||
render: renderProgressBars,
|
||||
}),
|
||||
|
||||
HighlightedLabels: defineComponentFixture({
|
||||
render: renderHighlightedLabels,
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from '../../../../src/vs/base/common/uri';
|
||||
import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../../../src/vs/editor/browser/widget/codeEditor/codeEditorWidget';
|
||||
import { ComponentFixtureContext, createEditorServices, createTextModel, defineComponentFixture, defineThemedFixtureGroup } from '../fixtureUtils';
|
||||
|
||||
const SAMPLE_CODE = `// Welcome to VS Code
|
||||
function greet(name: string): string {
|
||||
return \`Hello, \${name}!\`;
|
||||
}
|
||||
|
||||
class Counter {
|
||||
private _count = 0;
|
||||
|
||||
increment(): void {
|
||||
this._count++;
|
||||
}
|
||||
|
||||
get count(): number {
|
||||
return this._count;
|
||||
}
|
||||
}
|
||||
|
||||
const counter = new Counter();
|
||||
counter.increment();
|
||||
console.log(greet('World'));
|
||||
console.log(\`Count: \${counter.count}\`);
|
||||
`;
|
||||
|
||||
function renderCodeEditor({ container, disposableStore, theme }: ComponentFixtureContext): HTMLElement {
|
||||
container.style.width = '600px';
|
||||
container.style.height = '400px';
|
||||
container.style.border = '1px solid var(--vscode-editorWidget-border)';
|
||||
|
||||
const instantiationService = createEditorServices(disposableStore, { colorTheme: theme });
|
||||
|
||||
const model = disposableStore.add(createTextModel(
|
||||
instantiationService,
|
||||
SAMPLE_CODE,
|
||||
URI.parse('inmemory://sample.ts'),
|
||||
'typescript'
|
||||
));
|
||||
|
||||
const editorOptions: ICodeEditorWidgetOptions = {
|
||||
contributions: []
|
||||
};
|
||||
|
||||
const editor = disposableStore.add(instantiationService.createInstance(
|
||||
CodeEditorWidget,
|
||||
container,
|
||||
{
|
||||
automaticLayout: true,
|
||||
minimap: { enabled: true },
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
fontSize: 14,
|
||||
fontFamily: 'Consolas, "Courier New", monospace',
|
||||
renderWhitespace: 'selection',
|
||||
bracketPairColorization: { enabled: true },
|
||||
},
|
||||
editorOptions
|
||||
));
|
||||
|
||||
editor.setModel(model);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
export default defineThemedFixtureGroup({
|
||||
CodeEditor: defineComponentFixture({
|
||||
render: (context) => renderCodeEditor(context),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,164 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { constObservable } from '../../../../src/vs/base/common/observable';
|
||||
import { URI } from '../../../../src/vs/base/common/uri';
|
||||
import { Range } from '../../../../src/vs/editor/common/core/range';
|
||||
import { IEditorOptions } from '../../../../src/vs/editor/common/config/editorOptions';
|
||||
import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../../../src/vs/editor/browser/widget/codeEditor/codeEditorWidget';
|
||||
import { EditorExtensionsRegistry } from '../../../../src/vs/editor/browser/editorExtensions';
|
||||
import { InlineCompletionsController } from '../../../../src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController';
|
||||
import { InlineCompletionsSource, InlineCompletionsState } from '../../../../src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource';
|
||||
import { InlineEditItem } from '../../../../src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem';
|
||||
import { TextModelValueReference } from '../../../../src/vs/editor/contrib/inlineCompletions/browser/model/textModelValueReference';
|
||||
import { ComponentFixtureContext, createEditorServices, createTextModel, defineComponentFixture, defineThemedFixtureGroup } from '../fixtureUtils';
|
||||
|
||||
// Import to register the inline completions contribution
|
||||
import '../../../../src/vs/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution';
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Inline Edit Fixture
|
||||
// ============================================================================
|
||||
|
||||
interface InlineEditOptions extends ComponentFixtureContext {
|
||||
code: string;
|
||||
cursorLine: number;
|
||||
range: { startLineNumber: number; startColumn: number; endLineNumber: number; endColumn: number };
|
||||
newText: string;
|
||||
width?: string;
|
||||
height?: string;
|
||||
editorOptions?: IEditorOptions;
|
||||
}
|
||||
|
||||
function renderInlineEdit(options: InlineEditOptions): HTMLElement {
|
||||
const { container, disposableStore, theme } = options;
|
||||
container.style.width = options.width ?? '500px';
|
||||
container.style.height = options.height ?? '170px';
|
||||
container.style.border = '1px solid var(--vscode-editorWidget-border)';
|
||||
|
||||
const instantiationService = createEditorServices(disposableStore, { colorTheme: theme });
|
||||
|
||||
const textModel = disposableStore.add(createTextModel(
|
||||
instantiationService,
|
||||
options.code,
|
||||
URI.parse('inmemory://inline-edit.ts'),
|
||||
'typescript'
|
||||
));
|
||||
|
||||
// Mock the InlineCompletionsSource to provide our test completion
|
||||
instantiationService.stubInstance(InlineCompletionsSource, {
|
||||
cancelUpdate: () => { },
|
||||
clear: () => { },
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
clearOperationOnTextModelChange: constObservable(undefined) as any,
|
||||
clearSuggestWidgetInlineCompletions: () => { },
|
||||
dispose: () => { },
|
||||
fetch: async () => true,
|
||||
inlineCompletions: constObservable(new InlineCompletionsState([
|
||||
InlineEditItem.createForTest(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
TextModelValueReference.snapshot(textModel as any),
|
||||
new Range(
|
||||
options.range.startLineNumber,
|
||||
options.range.startColumn,
|
||||
options.range.endLineNumber,
|
||||
options.range.endColumn
|
||||
),
|
||||
options.newText
|
||||
)
|
||||
], undefined)),
|
||||
loading: constObservable(false),
|
||||
seedInlineCompletionsWithSuggestWidget: () => { },
|
||||
seedWithCompletion: () => { },
|
||||
suggestWidgetInlineCompletions: constObservable(InlineCompletionsState.createEmpty()),
|
||||
});
|
||||
|
||||
const editorWidgetOptions: ICodeEditorWidgetOptions = {
|
||||
contributions: EditorExtensionsRegistry.getEditorContributions()
|
||||
};
|
||||
|
||||
const editor = disposableStore.add(instantiationService.createInstance(
|
||||
CodeEditorWidget,
|
||||
container,
|
||||
{
|
||||
automaticLayout: true,
|
||||
minimap: { enabled: false },
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
fontSize: 14,
|
||||
cursorBlinking: 'solid',
|
||||
...options.editorOptions,
|
||||
},
|
||||
editorWidgetOptions
|
||||
));
|
||||
|
||||
editor.setModel(textModel);
|
||||
editor.setPosition({ lineNumber: options.cursorLine, column: 1 });
|
||||
editor.focus();
|
||||
|
||||
// Trigger inline completions
|
||||
const controller = InlineCompletionsController.get(editor);
|
||||
controller?.model?.get();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Fixtures
|
||||
// ============================================================================
|
||||
|
||||
export default defineThemedFixtureGroup({
|
||||
// Side-by-side view: Multi-line replacement
|
||||
SideBySideView: defineComponentFixture({
|
||||
render: (context) => renderInlineEdit({
|
||||
...context,
|
||||
code: `function greet(name) {
|
||||
console.log("Hello, " + name);
|
||||
}`,
|
||||
cursorLine: 2,
|
||||
range: { startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 100 },
|
||||
newText: ' console.log(`Hello, ${name}!`);',
|
||||
}),
|
||||
}),
|
||||
|
||||
// Word replacement view: Single word change
|
||||
WordReplacementView: defineComponentFixture({
|
||||
render: (context) => renderInlineEdit({
|
||||
...context,
|
||||
code: `class BufferData {
|
||||
append(data: number[]) {
|
||||
this.data.push(data);
|
||||
}
|
||||
}`,
|
||||
cursorLine: 2,
|
||||
range: { startLineNumber: 2, startColumn: 2, endLineNumber: 2, endColumn: 8 },
|
||||
newText: 'push',
|
||||
height: '200px',
|
||||
}),
|
||||
}),
|
||||
|
||||
// Insertion view: Insert new content
|
||||
InsertionView: defineComponentFixture({
|
||||
render: (context) => renderInlineEdit({
|
||||
...context,
|
||||
code: `class BufferData {
|
||||
append(data: number[]) {} // appends data
|
||||
}`,
|
||||
cursorLine: 2,
|
||||
range: { startLineNumber: 2, startColumn: 26, endLineNumber: 2, endColumn: 26 },
|
||||
newText: `
|
||||
console.log(data);
|
||||
`,
|
||||
height: '200px',
|
||||
editorOptions: {
|
||||
inlineSuggest: {
|
||||
edits: { allowCodeShifting: 'always' }
|
||||
}
|
||||
}
|
||||
}),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,512 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { defineFixture, defineFixtureGroup, defineFixtureVariants } from '@vscode/component-explorer';
|
||||
import { DisposableStore, toDisposable } from '../../../src/vs/base/common/lifecycle';
|
||||
import { URI } from '../../../src/vs/base/common/uri';
|
||||
import '../style.css';
|
||||
|
||||
// Theme
|
||||
import { COLOR_THEME_DARK_INITIAL_COLORS, COLOR_THEME_LIGHT_INITIAL_COLORS } from '../../../src/vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { ColorThemeData } from '../../../src/vs/workbench/services/themes/common/colorThemeData';
|
||||
import { ColorScheme } from '../../../src/vs/platform/theme/common/theme';
|
||||
import { generateColorThemeCSS } from '../../../src/vs/workbench/services/themes/browser/colorThemeCss';
|
||||
import { Registry } from '../../../src/vs/platform/registry/common/platform';
|
||||
import { Extensions as ThemingExtensions, IThemingRegistry } from '../../../src/vs/platform/theme/common/themeService';
|
||||
import { IEnvironmentService } from '../../../src/vs/platform/environment/common/environment';
|
||||
import { getIconsStyleSheet } from '../../../src/vs/platform/theme/browser/iconsStyleSheet';
|
||||
|
||||
// Instantiation
|
||||
import { ServiceCollection } from '../../../src/vs/platform/instantiation/common/serviceCollection';
|
||||
import { SyncDescriptor } from '../../../src/vs/platform/instantiation/common/descriptors';
|
||||
import { ServiceIdentifier } from '../../../src/vs/platform/instantiation/common/instantiation';
|
||||
import { TestInstantiationService } from '../../../src/vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
|
||||
// Test service implementations
|
||||
import { TestAccessibilityService } from '../../../src/vs/platform/accessibility/test/common/testAccessibilityService';
|
||||
import { MockKeybindingService, MockContextKeyService } from '../../../src/vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { TestClipboardService } from '../../../src/vs/platform/clipboard/test/common/testClipboardService';
|
||||
import { TestEditorWorkerService } from '../../../src/vs/editor/test/common/services/testEditorWorkerService';
|
||||
import { NullOpenerService } from '../../../src/vs/platform/opener/test/common/nullOpenerService';
|
||||
import { TestNotificationService } from '../../../src/vs/platform/notification/test/common/testNotificationService';
|
||||
import { TestDialogService } from '../../../src/vs/platform/dialogs/test/common/testDialogService';
|
||||
import { TestConfigurationService } from '../../../src/vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { TestTextResourcePropertiesService } from '../../../src/vs/editor/test/common/services/testTextResourcePropertiesService';
|
||||
import { TestThemeService } from '../../../src/vs/platform/theme/test/common/testThemeService';
|
||||
import { TestLanguageConfigurationService } from '../../../src/vs/editor/test/common/modes/testLanguageConfigurationService';
|
||||
import { TestCodeEditorService, TestCommandService } from '../../../src/vs/editor/test/browser/editorTestServices';
|
||||
import { TestTreeSitterLibraryService } from '../../../src/vs/editor/test/common/services/testTreeSitterLibraryService';
|
||||
import { TestMenuService } from '../../../src/vs/workbench/test/browser/workbenchTestServices';
|
||||
|
||||
// Service interfaces
|
||||
import { IAccessibilityService } from '../../../src/vs/platform/accessibility/common/accessibility';
|
||||
import { IKeybindingService } from '../../../src/vs/platform/keybinding/common/keybinding';
|
||||
import { IClipboardService } from '../../../src/vs/platform/clipboard/common/clipboardService';
|
||||
import { IEditorWorkerService } from '../../../src/vs/editor/common/services/editorWorker';
|
||||
import { IOpenerService } from '../../../src/vs/platform/opener/common/opener';
|
||||
import { INotificationService } from '../../../src/vs/platform/notification/common/notification';
|
||||
import { IDialogService } from '../../../src/vs/platform/dialogs/common/dialogs';
|
||||
import { IUndoRedoService } from '../../../src/vs/platform/undoRedo/common/undoRedo';
|
||||
import { UndoRedoService } from '../../../src/vs/platform/undoRedo/common/undoRedoService';
|
||||
import { ILanguageService } from '../../../src/vs/editor/common/languages/language';
|
||||
import { LanguageService } from '../../../src/vs/editor/common/services/languageService';
|
||||
import { ILanguageConfigurationService } from '../../../src/vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { IConfigurationService } from '../../../src/vs/platform/configuration/common/configuration';
|
||||
import { ITextResourcePropertiesService } from '../../../src/vs/editor/common/services/textResourceConfiguration';
|
||||
import { IColorTheme, IThemeService } from '../../../src/vs/platform/theme/common/themeService';
|
||||
import { ILogService, NullLogService, ILoggerService, NullLoggerService } from '../../../src/vs/platform/log/common/log';
|
||||
import { IModelService } from '../../../src/vs/editor/common/services/model';
|
||||
import { ModelService } from '../../../src/vs/editor/common/services/modelService';
|
||||
import { ICodeEditorService } from '../../../src/vs/editor/browser/services/codeEditorService';
|
||||
import { IContextKeyService } from '../../../src/vs/platform/contextkey/common/contextkey';
|
||||
import { ICommandService } from '../../../src/vs/platform/commands/common/commands';
|
||||
import { ITelemetryService } from '../../../src/vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryServiceShape } from '../../../src/vs/platform/telemetry/common/telemetryUtils';
|
||||
import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../../src/vs/editor/common/services/languageFeatureDebounce';
|
||||
import { ILanguageFeaturesService } from '../../../src/vs/editor/common/services/languageFeatures';
|
||||
import { LanguageFeaturesService } from '../../../src/vs/editor/common/services/languageFeaturesService';
|
||||
import { ITreeSitterLibraryService } from '../../../src/vs/editor/common/services/treeSitter/treeSitterLibraryService';
|
||||
import { IInlineCompletionsService, InlineCompletionsService } from '../../../src/vs/editor/browser/services/inlineCompletionsService';
|
||||
import { ICodeLensCache } from '../../../src/vs/editor/contrib/codelens/browser/codeLensCache';
|
||||
import { IHoverService } from '../../../src/vs/platform/hover/browser/hover';
|
||||
import { IDataChannelService, NullDataChannelService } from '../../../src/vs/platform/dataChannel/common/dataChannel';
|
||||
import { IContextMenuService, IContextViewService } from '../../../src/vs/platform/contextview/browser/contextView';
|
||||
import { ILabelService } from '../../../src/vs/platform/label/common/label';
|
||||
import { IMenuService } from '../../../src/vs/platform/actions/common/actions';
|
||||
import { IActionViewItemService, NullActionViewItemService } from '../../../src/vs/platform/actions/browser/actionViewItemService';
|
||||
import { IDefaultAccountService } from '../../../src/vs/platform/defaultAccount/common/defaultAccount';
|
||||
import { IStorageService, IStorageValueChangeEvent, IWillSaveStateEvent, StorageScope, StorageTarget, IStorageTargetChangeEvent, IStorageEntry, WillSaveStateReason, IWorkspaceStorageValueChangeEvent, IProfileStorageValueChangeEvent, IApplicationStorageValueChangeEvent } from '../../../src/vs/platform/storage/common/storage';
|
||||
import { Emitter, Event } from '../../../src/vs/base/common/event';
|
||||
import { mock } from '../../../src/vs/base/test/common/mock';
|
||||
import { IAnyWorkspaceIdentifier } from '../../../src/vs/platform/workspace/common/workspace';
|
||||
import { IUserDataProfile } from '../../../src/vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { IUserInteractionService, MockUserInteractionService } from '../../../src/vs/platform/userInteraction/browser/userInteractionService';
|
||||
|
||||
// Editor
|
||||
import { ITextModel } from '../../../src/vs/editor/common/model';
|
||||
|
||||
|
||||
|
||||
// Import color registrations to ensure colors are available
|
||||
import '../../../src/vs/platform/theme/common/colors/baseColors';
|
||||
import '../../../src/vs/platform/theme/common/colors/editorColors';
|
||||
import '../../../src/vs/platform/theme/common/colors/listColors';
|
||||
import '../../../src/vs/platform/theme/common/colors/miscColors';
|
||||
import '../../../src/vs/workbench/common/theme';
|
||||
|
||||
/**
|
||||
* A storage service that never stores anything and always returns the default/fallback value.
|
||||
* This is useful for fixtures where we want consistent behavior without persisted state.
|
||||
*/
|
||||
class NullStorageService implements IStorageService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChangeValue = new Emitter<IStorageValueChangeEvent>();
|
||||
onDidChangeValue(scope: StorageScope.WORKSPACE, key: string | undefined, disposable: DisposableStore): Event<IWorkspaceStorageValueChangeEvent>;
|
||||
onDidChangeValue(scope: StorageScope.PROFILE, key: string | undefined, disposable: DisposableStore): Event<IProfileStorageValueChangeEvent>;
|
||||
onDidChangeValue(scope: StorageScope.APPLICATION, key: string | undefined, disposable: DisposableStore): Event<IApplicationStorageValueChangeEvent>;
|
||||
onDidChangeValue(scope: StorageScope, key: string | undefined, disposable: DisposableStore): Event<IStorageValueChangeEvent> {
|
||||
return Event.filter(this._onDidChangeValue.event, e => e.scope === scope && (key === undefined || e.key === key), disposable);
|
||||
}
|
||||
|
||||
private readonly _onDidChangeTarget = new Emitter<IStorageTargetChangeEvent>();
|
||||
readonly onDidChangeTarget: Event<IStorageTargetChangeEvent> = this._onDidChangeTarget.event;
|
||||
|
||||
private readonly _onWillSaveState = new Emitter<IWillSaveStateEvent>();
|
||||
readonly onWillSaveState: Event<IWillSaveStateEvent> = this._onWillSaveState.event;
|
||||
|
||||
get(key: string, scope: StorageScope, fallbackValue: string): string;
|
||||
get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined;
|
||||
get(_key: string, _scope: StorageScope, fallbackValue?: string): string | undefined {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean;
|
||||
getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined;
|
||||
getBoolean(_key: string, _scope: StorageScope, fallbackValue?: boolean): boolean | undefined {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
getNumber(key: string, scope: StorageScope, fallbackValue: number): number;
|
||||
getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined;
|
||||
getNumber(_key: string, _scope: StorageScope, fallbackValue?: number): number | undefined {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
getObject<T extends object>(key: string, scope: StorageScope, fallbackValue: T): T;
|
||||
getObject<T extends object>(key: string, scope: StorageScope, fallbackValue?: T): T | undefined;
|
||||
getObject<T extends object>(_key: string, _scope: StorageScope, fallbackValue?: T): T | undefined {
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
store(_key: string, _value: string | boolean | number | undefined | null, _scope: StorageScope, _target: StorageTarget): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
storeAll(_entries: IStorageEntry[], _external: boolean): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
remove(_key: string, _scope: StorageScope): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
isNew(_scope: StorageScope): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
flush(_reason?: WillSaveStateReason): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
optimize(_scope: StorageScope): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
log(): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
keys(_scope: StorageScope, _target: StorageTarget): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
switch(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
hasScope(_scope: IAnyWorkspaceIdentifier | IUserDataProfile): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Themes
|
||||
// ============================================================================
|
||||
|
||||
const themingRegistry = Registry.as<IThemingRegistry>(ThemingExtensions.ThemingContribution);
|
||||
const mockEnvironmentService: IEnvironmentService = Object.create(null);
|
||||
|
||||
export const darkTheme = ColorThemeData.createUnloadedThemeForThemeType(
|
||||
ColorScheme.DARK,
|
||||
COLOR_THEME_DARK_INITIAL_COLORS
|
||||
);
|
||||
|
||||
export const lightTheme = ColorThemeData.createUnloadedThemeForThemeType(
|
||||
ColorScheme.LIGHT,
|
||||
COLOR_THEME_LIGHT_INITIAL_COLORS
|
||||
);
|
||||
|
||||
let globalStyleSheet: CSSStyleSheet | undefined;
|
||||
let iconsStyleSheetCache: CSSStyleSheet | undefined;
|
||||
let darkThemeStyleSheet: CSSStyleSheet | undefined;
|
||||
let lightThemeStyleSheet: CSSStyleSheet | undefined;
|
||||
|
||||
function getGlobalStyleSheet(): CSSStyleSheet {
|
||||
if (!globalStyleSheet) {
|
||||
globalStyleSheet = new CSSStyleSheet();
|
||||
const globalRules: string[] = [];
|
||||
for (const sheet of Array.from(document.styleSheets)) {
|
||||
try {
|
||||
for (const rule of Array.from(sheet.cssRules)) {
|
||||
globalRules.push(rule.cssText);
|
||||
}
|
||||
} catch {
|
||||
// Cross-origin stylesheets can't be read
|
||||
}
|
||||
}
|
||||
globalStyleSheet.replaceSync(globalRules.join('\n'));
|
||||
}
|
||||
return globalStyleSheet;
|
||||
}
|
||||
|
||||
function getIconsStyleSheetCached(): CSSStyleSheet {
|
||||
if (!iconsStyleSheetCache) {
|
||||
iconsStyleSheetCache = new CSSStyleSheet();
|
||||
const iconsSheet = getIconsStyleSheet(undefined);
|
||||
iconsStyleSheetCache.replaceSync(iconsSheet.getCSS() as string);
|
||||
iconsSheet.dispose();
|
||||
}
|
||||
return iconsStyleSheetCache;
|
||||
}
|
||||
|
||||
function getThemeStyleSheet(theme: ColorThemeData): CSSStyleSheet {
|
||||
const isDark = theme.type === ColorScheme.DARK;
|
||||
if (isDark && darkThemeStyleSheet) {
|
||||
return darkThemeStyleSheet;
|
||||
}
|
||||
if (!isDark && lightThemeStyleSheet) {
|
||||
return lightThemeStyleSheet;
|
||||
}
|
||||
|
||||
const sheet = new CSSStyleSheet();
|
||||
const css = generateColorThemeCSS(
|
||||
theme,
|
||||
':host',
|
||||
themingRegistry.getThemingParticipants(),
|
||||
mockEnvironmentService
|
||||
);
|
||||
sheet.replaceSync(css.code);
|
||||
|
||||
if (isDark) {
|
||||
darkThemeStyleSheet = sheet;
|
||||
} else {
|
||||
lightThemeStyleSheet = sheet;
|
||||
}
|
||||
return sheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies theme styling to a shadow DOM container.
|
||||
* Adds theme class names and adopts shared stylesheets.
|
||||
*/
|
||||
export function setupTheme(container: HTMLElement, theme: ColorThemeData): void {
|
||||
container.classList.add(...theme.classNames);
|
||||
|
||||
const shadowRoot = container.getRootNode() as ShadowRoot;
|
||||
if (shadowRoot.adoptedStyleSheets !== undefined) {
|
||||
shadowRoot.adoptedStyleSheets = [
|
||||
getGlobalStyleSheet(),
|
||||
getIconsStyleSheetCached(),
|
||||
getThemeStyleSheet(theme),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Services
|
||||
// ============================================================================
|
||||
|
||||
export interface ServiceRegistration {
|
||||
define<T>(id: ServiceIdentifier<T>, ctor: new (...args: never[]) => T): void;
|
||||
defineInstance<T>(id: ServiceIdentifier<T>, instance: T): void;
|
||||
}
|
||||
|
||||
export interface CreateServicesOptions {
|
||||
/**
|
||||
* The color theme to use for the theme service.
|
||||
*/
|
||||
colorTheme?: IColorTheme;
|
||||
/**
|
||||
* Additional services to register after the base editor services.
|
||||
*/
|
||||
additionalServices?: (registration: ServiceRegistration) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TestInstantiationService with all services needed for CodeEditorWidget.
|
||||
* Additional services can be registered via the options callback.
|
||||
*/
|
||||
export function createEditorServices(disposables: DisposableStore, options?: CreateServicesOptions): TestInstantiationService {
|
||||
const services = new ServiceCollection();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const serviceIdentifiers: ServiceIdentifier<any>[] = [];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const define = <T>(id: ServiceIdentifier<T>, ctor: new (...args: any[]) => T) => {
|
||||
if (!services.has(id)) {
|
||||
services.set(id, new SyncDescriptor(ctor));
|
||||
}
|
||||
serviceIdentifiers.push(id);
|
||||
};
|
||||
|
||||
const defineInstance = <T>(id: ServiceIdentifier<T>, instance: T) => {
|
||||
if (!services.has(id)) {
|
||||
services.set(id, instance);
|
||||
}
|
||||
serviceIdentifiers.push(id);
|
||||
};
|
||||
|
||||
// Base editor services
|
||||
define(IAccessibilityService, TestAccessibilityService);
|
||||
define(IKeybindingService, MockKeybindingService);
|
||||
define(IClipboardService, TestClipboardService);
|
||||
define(IEditorWorkerService, TestEditorWorkerService);
|
||||
defineInstance(IOpenerService, NullOpenerService);
|
||||
define(INotificationService, TestNotificationService);
|
||||
define(IDialogService, TestDialogService);
|
||||
define(IUndoRedoService, UndoRedoService);
|
||||
define(ILanguageService, LanguageService);
|
||||
define(ILanguageConfigurationService, TestLanguageConfigurationService);
|
||||
define(IConfigurationService, TestConfigurationService);
|
||||
define(ITextResourcePropertiesService, TestTextResourcePropertiesService);
|
||||
defineInstance(IStorageService, new NullStorageService());
|
||||
if (options?.colorTheme) {
|
||||
defineInstance(IThemeService, new TestThemeService(options.colorTheme));
|
||||
} else {
|
||||
define(IThemeService, TestThemeService);
|
||||
}
|
||||
define(ILogService, NullLogService);
|
||||
define(IModelService, ModelService);
|
||||
define(ICodeEditorService, TestCodeEditorService);
|
||||
define(IContextKeyService, MockContextKeyService);
|
||||
define(ICommandService, TestCommandService);
|
||||
define(ITelemetryService, NullTelemetryServiceShape);
|
||||
define(ILoggerService, NullLoggerService);
|
||||
define(IDataChannelService, NullDataChannelService);
|
||||
define(IEnvironmentService, class extends mock<IEnvironmentService>() {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
override isBuilt: boolean = true;
|
||||
override isExtensionDevelopment: boolean = false;
|
||||
});
|
||||
define(ILanguageFeatureDebounceService, LanguageFeatureDebounceService);
|
||||
define(ILanguageFeaturesService, LanguageFeaturesService);
|
||||
define(ITreeSitterLibraryService, TestTreeSitterLibraryService);
|
||||
define(IInlineCompletionsService, InlineCompletionsService);
|
||||
defineInstance(ICodeLensCache, {
|
||||
_serviceBrand: undefined,
|
||||
put: () => { },
|
||||
get: () => undefined,
|
||||
delete: () => { },
|
||||
} as ICodeLensCache);
|
||||
defineInstance(IHoverService, {
|
||||
_serviceBrand: undefined,
|
||||
showDelayedHover: () => undefined,
|
||||
setupDelayedHover: () => ({ dispose: () => { } }),
|
||||
setupDelayedHoverAtMouse: () => ({ dispose: () => { } }),
|
||||
showInstantHover: () => undefined,
|
||||
hideHover: () => { },
|
||||
showAndFocusLastHover: () => { },
|
||||
setupManagedHover: () => ({ dispose: () => { }, show: () => { }, hide: () => { }, update: () => { } }),
|
||||
showManagedHover: () => { },
|
||||
} as IHoverService);
|
||||
defineInstance(IDefaultAccountService, {
|
||||
_serviceBrand: undefined,
|
||||
onDidChangeDefaultAccount: new Emitter<null>().event,
|
||||
onDidChangePolicyData: new Emitter<null>().event,
|
||||
policyData: null,
|
||||
getDefaultAccount: async () => null,
|
||||
getDefaultAccountAuthenticationProvider: () => ({ id: 'test', name: 'Test', scopes: [], enterprise: false }),
|
||||
setDefaultAccountProvider: () => { },
|
||||
refresh: async () => null,
|
||||
signIn: async () => null,
|
||||
} as IDefaultAccountService);
|
||||
|
||||
// User interaction service with focus simulation enabled (all elements appear focused in fixtures)
|
||||
defineInstance(IUserInteractionService, new MockUserInteractionService(true, false));
|
||||
|
||||
// Allow additional services to be registered
|
||||
options?.additionalServices?.({ define, defineInstance });
|
||||
|
||||
const instantiationService = disposables.add(new TestInstantiationService(services, true));
|
||||
|
||||
disposables.add(toDisposable(() => {
|
||||
for (const id of serviceIdentifiers) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const instanceOrDescriptor = services.get(id) as any;
|
||||
if (typeof instanceOrDescriptor?.dispose === 'function') {
|
||||
instanceOrDescriptor.dispose();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return instantiationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers additional services needed by workbench components (merge editor, etc.).
|
||||
* Use with createEditorServices additionalServices option.
|
||||
*/
|
||||
export function registerWorkbenchServices(registration: ServiceRegistration): void {
|
||||
registration.defineInstance(IContextMenuService, {
|
||||
showContextMenu: () => { },
|
||||
onDidShowContextMenu: () => ({ dispose: () => { } }),
|
||||
onDidHideContextMenu: () => ({ dispose: () => { } }),
|
||||
} as unknown as IContextMenuService);
|
||||
|
||||
registration.defineInstance(IContextViewService, {
|
||||
showContextView: () => ({ dispose: () => { } }),
|
||||
hideContextView: () => { },
|
||||
getContextViewElement: () => null,
|
||||
layout: () => { },
|
||||
} as unknown as IContextViewService);
|
||||
|
||||
registration.defineInstance(ILabelService, {
|
||||
getUriLabel: (uri: URI) => uri.path,
|
||||
getUriBasenameLabel: (uri: URI) => uri.path.split('/').pop() ?? '',
|
||||
getWorkspaceLabel: () => '',
|
||||
getHostLabel: () => '',
|
||||
getSeparator: () => '/',
|
||||
registerFormatter: () => ({ dispose: () => { } }),
|
||||
onDidChangeFormatters: () => ({ dispose: () => { } }),
|
||||
registerCachedFormatter: () => ({ dispose: () => { } }),
|
||||
} as unknown as ILabelService);
|
||||
|
||||
registration.define(IMenuService, TestMenuService);
|
||||
registration.define(IActionViewItemService, NullActionViewItemService);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Text Models
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Creates a text model using the ModelService.
|
||||
*/
|
||||
export function createTextModel(
|
||||
instantiationService: TestInstantiationService,
|
||||
text: string,
|
||||
uri: URI,
|
||||
languageId?: string
|
||||
): ITextModel {
|
||||
const modelService = instantiationService.get(IModelService);
|
||||
const languageService = instantiationService.get(ILanguageService);
|
||||
const languageSelection = languageId ? languageService.createById(languageId) : null;
|
||||
return modelService.createModel(text, languageSelection, uri);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Fixture Adapters
|
||||
// ============================================================================
|
||||
|
||||
export interface ComponentFixtureContext {
|
||||
container: HTMLElement;
|
||||
disposableStore: DisposableStore;
|
||||
theme: ColorThemeData;
|
||||
}
|
||||
|
||||
export interface ComponentFixtureOptions {
|
||||
render: (context: ComponentFixtureContext) => HTMLElement | Promise<HTMLElement>;
|
||||
}
|
||||
|
||||
type ThemedFixtures = ReturnType<typeof defineFixtureVariants>;
|
||||
|
||||
/**
|
||||
* Creates Dark and Light fixture variants from a single render function.
|
||||
* The render function receives a context with container and disposableStore.
|
||||
*/
|
||||
export function defineComponentFixture(options: ComponentFixtureOptions): ThemedFixtures {
|
||||
const createFixture = (theme: typeof darkTheme | typeof lightTheme) => defineFixture({
|
||||
isolation: 'shadow-dom',
|
||||
displayMode: { type: 'component' },
|
||||
properties: [],
|
||||
background: theme === darkTheme ? 'dark' : 'light',
|
||||
render: async (container: HTMLElement) => {
|
||||
const disposableStore = new DisposableStore();
|
||||
setupTheme(container, theme);
|
||||
return options.render({ container, disposableStore, theme });
|
||||
},
|
||||
});
|
||||
|
||||
return defineFixtureVariants({
|
||||
Dark: createFixture(darkTheme),
|
||||
Light: createFixture(lightTheme),
|
||||
});
|
||||
}
|
||||
|
||||
type ThemedFixtureGroupInput = Record<string, ThemedFixtures>;
|
||||
|
||||
/**
|
||||
* Creates a nested fixture group from themed fixtures.
|
||||
* E.g., { MergeEditor: { Dark: ..., Light: ... } } becomes a nested group: MergeEditor > Dark/Light
|
||||
*/
|
||||
export function defineThemedFixtureGroup(group: ThemedFixtureGroupInput): ReturnType<typeof defineFixtureGroup> {
|
||||
return defineFixtureGroup(group);
|
||||
}
|
||||
Generated
+766
-452
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,14 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vscode/rollup-plugin-esm-url": "^1.0.1-0",
|
||||
"vite": "^7.1.11"
|
||||
"@vscode/rollup-plugin-esm-url": "^1.0.1-1",
|
||||
"vite": "npm:rolldown-vite@latest",
|
||||
"@vscode/component-explorer": "next",
|
||||
"@vscode/component-explorer-vite-plugin": "next"
|
||||
},
|
||||
"overrides": {
|
||||
"@vscode/component-explorer-vite-plugin": {
|
||||
"vite": "$vite"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { getSingletonServiceDescriptors, InstantiationType, registerSingleton }
|
||||
import { IWebWorkerService } from '../../src/vs/platform/webWorker/browser/webWorkerService.ts';
|
||||
// eslint-disable-next-line local/code-no-standalone-editor
|
||||
import { StandaloneWebWorkerService } from '../../src/vs/editor/standalone/browser/services/standaloneWebWorkerService.ts';
|
||||
import './style.css';
|
||||
|
||||
enableHotReload();
|
||||
|
||||
|
||||
@@ -7,3 +7,9 @@
|
||||
height: 400px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "codicon";
|
||||
font-display: block;
|
||||
src: url("~@vscode/codicons/dist/codicon.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
import { createLogger, defineConfig, Plugin } from 'vite';
|
||||
import path, { join } from 'path';
|
||||
import { rollupEsmUrlPlugin } from '@vscode/rollup-plugin-esm-url';
|
||||
import { componentExplorer } from '@vscode/component-explorer-vite-plugin';
|
||||
import { statSync } from 'fs';
|
||||
import { pathToFileURL } from 'url';
|
||||
import { rollupEsmUrlPlugin } from '@vscode/rollup-plugin-esm-url';
|
||||
|
||||
function injectBuiltinExtensionsPlugin(): Plugin {
|
||||
let builtinExtensionsCache: unknown[] | null = null;
|
||||
@@ -166,9 +167,18 @@ export default defineConfig({
|
||||
plugins: [
|
||||
rollupEsmUrlPlugin({}),
|
||||
injectBuiltinExtensionsPlugin(),
|
||||
createHotClassSupport()
|
||||
createHotClassSupport(),
|
||||
componentExplorer({
|
||||
logLevel: 'verbose',
|
||||
include: 'build/vite/**/*.fixture.ts',
|
||||
}),
|
||||
],
|
||||
customLogger: logger,
|
||||
resolve: {
|
||||
alias: {
|
||||
'~@vscode/codicons': '/node_modules/@vscode/codicons',
|
||||
}
|
||||
},
|
||||
esbuild: {
|
||||
tsconfigRaw: {
|
||||
compilerOptions: {
|
||||
|
||||
+14
-2
@@ -1453,6 +1453,12 @@ begin
|
||||
Result := not (IsBackgroundUpdate() and FileExists(Path));
|
||||
end;
|
||||
|
||||
// Check if VS Code created a cancel file to signal that the update should be aborted
|
||||
function CancelFileExists(): Boolean;
|
||||
begin
|
||||
Result := FileExists(ExpandConstant('{param:cancel}'))
|
||||
end;
|
||||
|
||||
function ShouldRunAfterUpdate(): Boolean;
|
||||
begin
|
||||
if IsBackgroundUpdate() then
|
||||
@@ -1639,11 +1645,17 @@ begin
|
||||
Log('Checking whether application is still running...');
|
||||
while (CheckForMutexes('{#AppMutex}')) do
|
||||
begin
|
||||
if CancelFileExists() then
|
||||
begin
|
||||
Log('Cancel file detected, aborting background update.');
|
||||
DeleteFile(ExpandConstant('{app}\updating_version'));
|
||||
Abort;
|
||||
end;
|
||||
Sleep(1000)
|
||||
end;
|
||||
Log('Application appears not to be running.');
|
||||
|
||||
if not SessionEndFileExists() then begin
|
||||
if not SessionEndFileExists() and not CancelFileExists() then begin
|
||||
StopTunnelServiceIfNeeded();
|
||||
Log('Invoking inno_updater for background update');
|
||||
Exec(ExpandConstant('{app}\{#VersionedResourcesFolder}\tools\inno_updater.exe'), ExpandConstant('"{app}\{#ExeBasename}.exe" ' + BoolToStr(LockFileExists()) + ' "{cm:UpdatingVisualStudioCode}"'), '', SW_SHOW, ewWaitUntilTerminated, UpdateResultCode);
|
||||
@@ -1657,7 +1669,7 @@ begin
|
||||
end;
|
||||
#endif
|
||||
end else begin
|
||||
Log('Skipping inno_updater.exe call because OS session is ending');
|
||||
Log('Skipping inno_updater.exe call because OS session is ending or cancel was requested');
|
||||
end;
|
||||
end else begin
|
||||
if IsVersionedUpdate() then begin
|
||||
|
||||
+6
-3
@@ -2064,9 +2064,12 @@ export default tseslint.config(
|
||||
// Additional extension strictness rules
|
||||
{
|
||||
files: [
|
||||
'extensions/markdown-language-features/**/*.ts',
|
||||
'extensions/mermaid-chat-features/**/*.ts',
|
||||
'extensions/media-preview/**/*.ts',
|
||||
'extensions/markdown-language-features/src/**/*.ts',
|
||||
'extensions/markdown-language-features/notebook/**/*.ts',
|
||||
'extensions/markdown-language-features/preview-src/**/*.ts',
|
||||
'extensions/mermaid-chat-features/chat-webview-src/**/*.ts',
|
||||
'extensions/mermaid-chat-features/src/**/*.ts',
|
||||
'extensions/media-preview/src/**/*.ts',
|
||||
'extensions/simple-browser/**/*.ts',
|
||||
'extensions/typescript-language-features/**/*.ts',
|
||||
],
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @fileoverview Common build script for extensions.
|
||||
*/
|
||||
import path from 'node:path';
|
||||
import esbuild from 'esbuild';
|
||||
|
||||
type BuildOptions = Partial<esbuild.BuildOptions> & {
|
||||
outdir: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the source code once using esbuild.
|
||||
*/
|
||||
async function build(options: BuildOptions, didBuild?: (outDir: string) => unknown): Promise<void> {
|
||||
await esbuild.build({
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: false,
|
||||
format: 'cjs',
|
||||
platform: 'node',
|
||||
target: ['es2024'],
|
||||
external: ['vscode'],
|
||||
...options,
|
||||
});
|
||||
|
||||
await didBuild?.(options.outdir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the source code once using esbuild, logging errors instead of throwing.
|
||||
*/
|
||||
async function tryBuild(options: BuildOptions, didBuild?: (outDir: string) => unknown): Promise<void> {
|
||||
try {
|
||||
await build(options, didBuild);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
interface RunConfig {
|
||||
srcDir: string;
|
||||
outdir: string;
|
||||
entryPoints: string[] | Record<string, string> | { in: string; out: string }[];
|
||||
additionalOptions?: Partial<esbuild.BuildOptions>;
|
||||
}
|
||||
|
||||
export async function run(config: RunConfig, args: string[], didBuild?: (outDir: string) => unknown): Promise<void> {
|
||||
let outdir = config.outdir;
|
||||
const outputRootIndex = args.indexOf('--outputRoot');
|
||||
if (outputRootIndex >= 0) {
|
||||
const outputRoot = args[outputRootIndex + 1];
|
||||
const outputDirName = path.basename(outdir);
|
||||
outdir = path.join(outputRoot, outputDirName);
|
||||
}
|
||||
|
||||
const resolvedOptions: BuildOptions = {
|
||||
entryPoints: config.entryPoints,
|
||||
outdir,
|
||||
logOverride: {
|
||||
'import-is-undefined': 'error',
|
||||
},
|
||||
...(config.additionalOptions || {}),
|
||||
};
|
||||
|
||||
const isWatch = args.indexOf('--watch') >= 0;
|
||||
if (isWatch) {
|
||||
await tryBuild(resolvedOptions, didBuild);
|
||||
const watcher = await import('@parcel/watcher');
|
||||
watcher.subscribe(config.srcDir, () => tryBuild(resolvedOptions, didBuild));
|
||||
} else {
|
||||
return build(resolvedOptions, didBuild).catch(() => process.exit(1));
|
||||
}
|
||||
}
|
||||
@@ -460,7 +460,7 @@ export class ApiImpl implements API {
|
||||
return null;
|
||||
}
|
||||
|
||||
await this.#model.openRepository(root.fsPath);
|
||||
await this.#model.openRepository(root.fsPath, true, true);
|
||||
return this.getRepository(root) || null;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,9 @@
|
||||
".ember-cli",
|
||||
"typedoc.json"
|
||||
],
|
||||
"filenamePatterns": [
|
||||
"**/.github/hooks/*.json"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
|
||||
|
||||
/**
|
||||
* Copy the language server worker main file to the output directory.
|
||||
*/
|
||||
async function copyServerWorkerMain(outDir: string): Promise<void> {
|
||||
const srcPath = path.join(import.meta.dirname, 'node_modules', 'vscode-markdown-languageserver', 'dist', 'browser', 'workerMain.js');
|
||||
const destPath = path.join(outDir, 'serverWorkerMain.js');
|
||||
await fs.promises.copyFile(srcPath, destPath);
|
||||
}
|
||||
|
||||
run({
|
||||
entryPoints: {
|
||||
'extension': path.join(srcDir, 'extension.browser.ts'),
|
||||
},
|
||||
srcDir,
|
||||
outdir: outDir,
|
||||
additionalOptions: {
|
||||
platform: 'browser',
|
||||
format: 'cjs',
|
||||
alias: {
|
||||
'path': 'path-browserify',
|
||||
},
|
||||
define: {
|
||||
'process.platform': JSON.stringify('web'),
|
||||
'process.env': JSON.stringify({}),
|
||||
'process.env.BROWSER_ENV': JSON.stringify('true'),
|
||||
},
|
||||
tsconfig: path.join(import.meta.dirname, 'tsconfig.browser.json'),
|
||||
},
|
||||
}, process.argv, copyServerWorkerMain);
|
||||
@@ -0,0 +1,27 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist');
|
||||
|
||||
/**
|
||||
* Copy the language server worker main file to the output directory.
|
||||
*/
|
||||
async function copyServerWorkerMain(outDir: string): Promise<void> {
|
||||
const srcPath = path.join(import.meta.dirname, 'node_modules', 'vscode-markdown-languageserver', 'dist', 'node', 'workerMain.js');
|
||||
const destPath = path.join(outDir, 'serverWorkerMain.js');
|
||||
await fs.promises.copyFile(srcPath, destPath);
|
||||
}
|
||||
|
||||
run({
|
||||
entryPoints: {
|
||||
'extension': path.join(srcDir, 'extension.ts'),
|
||||
},
|
||||
srcDir,
|
||||
outdir: outDir,
|
||||
}, process.argv, copyServerWorkerMain);
|
||||
@@ -1,27 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import CopyPlugin from 'copy-webpack-plugin';
|
||||
import { browser, browserPlugins } from '../shared.webpack.config.mjs';
|
||||
|
||||
export default browser({
|
||||
context: import.meta.dirname,
|
||||
entry: {
|
||||
extension: './src/extension.browser.ts'
|
||||
},
|
||||
plugins: [
|
||||
...browserPlugins(import.meta.dirname), // add plugins, don't replace inherited
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: './node_modules/vscode-markdown-languageserver/dist/browser/workerMain.js',
|
||||
to: 'serverWorkerMain.js',
|
||||
}
|
||||
],
|
||||
}),
|
||||
],
|
||||
}, {
|
||||
configFile: 'tsconfig.browser.json'
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import CopyPlugin from 'copy-webpack-plugin';
|
||||
import withDefaults, { nodePlugins } from '../shared.webpack.config.mjs';
|
||||
|
||||
export default withDefaults({
|
||||
context: import.meta.dirname,
|
||||
resolve: {
|
||||
mainFields: ['module', 'main']
|
||||
},
|
||||
entry: {
|
||||
extension: './src/extension.ts',
|
||||
},
|
||||
plugins: [
|
||||
...nodePlugins(import.meta.dirname), // add plugins, don't replace inherited
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: './node_modules/vscode-markdown-languageserver/dist/node/workerMain.js',
|
||||
to: 'serverWorkerMain.js',
|
||||
}
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
|
||||
|
||||
run({
|
||||
entryPoints: {
|
||||
'extension': path.join(srcDir, 'extension.ts'),
|
||||
},
|
||||
srcDir,
|
||||
outdir: outDir,
|
||||
additionalOptions: {
|
||||
platform: 'browser',
|
||||
format: 'cjs',
|
||||
define: {
|
||||
'process.platform': JSON.stringify('web'),
|
||||
'process.env': JSON.stringify({}),
|
||||
'process.env.BROWSER_ENV': JSON.stringify('true'),
|
||||
},
|
||||
},
|
||||
}, process.argv);
|
||||
+11
-7
@@ -2,12 +2,16 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import { browser as withBrowserDefaults } from '../shared.webpack.config.mjs';
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
|
||||
export default withBrowserDefaults({
|
||||
context: import.meta.dirname,
|
||||
entry: {
|
||||
extension: './src/extension.ts'
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist');
|
||||
|
||||
run({
|
||||
entryPoints: {
|
||||
'extension': path.join(srcDir, 'extension.ts'),
|
||||
},
|
||||
});
|
||||
srcDir,
|
||||
outdir: outDir,
|
||||
}, process.argv);
|
||||
@@ -1,13 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import { browser as withBrowserDefaults } from '../shared.webpack.config.mjs';
|
||||
|
||||
export default withBrowserDefaults({
|
||||
context: import.meta.dirname,
|
||||
entry: {
|
||||
extension: './src/extension.ts'
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
|
||||
|
||||
run({
|
||||
entryPoints: {
|
||||
'extension': path.join(srcDir, 'extension.ts'),
|
||||
},
|
||||
srcDir,
|
||||
outdir: outDir,
|
||||
additionalOptions: {
|
||||
platform: 'browser',
|
||||
format: 'cjs',
|
||||
define: {
|
||||
'process.platform': JSON.stringify('web'),
|
||||
'process.env': JSON.stringify({}),
|
||||
'process.env.BROWSER_ENV': JSON.stringify('true'),
|
||||
},
|
||||
},
|
||||
}, process.argv);
|
||||
+11
-10
@@ -2,15 +2,16 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import withDefaults from '../shared.webpack.config.mjs';
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
|
||||
export default withDefaults({
|
||||
context: import.meta.dirname,
|
||||
resolve: {
|
||||
mainFields: ['module', 'main']
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist');
|
||||
|
||||
run({
|
||||
entryPoints: {
|
||||
'extension': path.join(srcDir, 'extension.ts'),
|
||||
},
|
||||
entry: {
|
||||
extension: './src/extension.ts',
|
||||
}
|
||||
});
|
||||
srcDir,
|
||||
outdir: outDir,
|
||||
}, process.argv);
|
||||
@@ -0,0 +1,26 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
|
||||
|
||||
run({
|
||||
entryPoints: {
|
||||
'extension': path.join(srcDir, 'extension.ts'),
|
||||
},
|
||||
srcDir,
|
||||
outdir: outDir,
|
||||
additionalOptions: {
|
||||
platform: 'browser',
|
||||
format: 'cjs',
|
||||
define: {
|
||||
'process.platform': JSON.stringify('web'),
|
||||
'process.env': JSON.stringify({}),
|
||||
'process.env.BROWSER_ENV': JSON.stringify('true'),
|
||||
},
|
||||
},
|
||||
}, process.argv);
|
||||
+11
-10
@@ -2,15 +2,16 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import withDefaults from '../shared.webpack.config.mjs';
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
|
||||
export default withDefaults({
|
||||
context: import.meta.dirname,
|
||||
resolve: {
|
||||
mainFields: ['module', 'main']
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist');
|
||||
|
||||
run({
|
||||
entryPoints: {
|
||||
'extension': path.join(srcDir, 'extension.ts'),
|
||||
},
|
||||
entry: {
|
||||
extension: './src/extension.ts',
|
||||
}
|
||||
});
|
||||
srcDir,
|
||||
outdir: outDir,
|
||||
}, process.argv);
|
||||
@@ -1,13 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import { browser as withBrowserDefaults } from '../shared.webpack.config.mjs';
|
||||
|
||||
export default withBrowserDefaults({
|
||||
context: import.meta.dirname,
|
||||
entry: {
|
||||
extension: './src/extension.ts'
|
||||
}
|
||||
});
|
||||
@@ -34,6 +34,9 @@
|
||||
".instructions.md",
|
||||
"copilot-instructions.md"
|
||||
],
|
||||
"filenamePatterns": [
|
||||
"**/.claude/rules/**/*.md"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
},
|
||||
{
|
||||
@@ -47,7 +50,8 @@
|
||||
".chatmode.md"
|
||||
],
|
||||
"filenamePatterns": [
|
||||
"**/.github/agents/*.md"
|
||||
"**/.github/agents/*.md",
|
||||
"**/.claude/agents/*.md"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"type": "dark",
|
||||
"colors": {
|
||||
"foreground": "#bfbfbf",
|
||||
"disabledForeground": "#444444",
|
||||
"disabledForeground": "#666666",
|
||||
"errorForeground": "#f48771",
|
||||
"descriptionForeground": "#999999",
|
||||
"icon.foreground": "#888888",
|
||||
@@ -15,6 +15,7 @@
|
||||
"textCodeBlock.background": "#242526",
|
||||
"textLink.foreground": "#48A0C7",
|
||||
"textLink.activeForeground": "#53A5CA",
|
||||
"textPreformat.background": "#262626",
|
||||
"textPreformat.foreground": "#888888",
|
||||
"textSeparator.foreground": "#2a2a2aFF",
|
||||
"button.background": "#3994BCF2",
|
||||
@@ -93,7 +94,7 @@
|
||||
"menu.foreground": "#bfbfbf",
|
||||
"menu.selectionBackground": "#3994BC26",
|
||||
"menu.selectionForeground": "#bfbfbf",
|
||||
"menu.separatorBackground": "#838485",
|
||||
"menu.separatorBackground": "#2A2B2C",
|
||||
"menu.border": "#2A2B2CFF",
|
||||
"commandCenter.foreground": "#bfbfbf",
|
||||
"commandCenter.activeForeground": "#bfbfbf",
|
||||
@@ -102,6 +103,7 @@
|
||||
"commandCenter.border": "#2E3031",
|
||||
"editor.background": "#121314",
|
||||
"editor.foreground": "#BBBEBF",
|
||||
"editorStickyScroll.background": "#121314",
|
||||
"editorLineNumber.foreground": "#858889",
|
||||
"editorLineNumber.activeForeground": "#BBBEBF",
|
||||
"editorCursor.foreground": "#BBBEBF",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"button.border": "#F2F3F4FF",
|
||||
"button.secondaryBackground": "#EDEDED",
|
||||
"button.secondaryForeground": "#202020",
|
||||
"button.secondaryHoverBackground": "#E6E6E6",
|
||||
"button.secondaryHoverBackground": "#F3F3F3",
|
||||
"checkbox.background": "#EDEDED",
|
||||
"checkbox.border": "#D8D8D8",
|
||||
"checkbox.foreground": "#202020",
|
||||
@@ -49,7 +49,7 @@
|
||||
"inputValidation.errorForeground": "#202020",
|
||||
"scrollbar.shadow": "#00000000",
|
||||
"widget.shadow": "#00000000",
|
||||
"widget.border": "#F2F3F4FF",
|
||||
"widget.border": "#EEEEF1",
|
||||
"editorStickyScroll.shadow": "#00000000",
|
||||
"sideBarStickyScroll.shadow": "#00000000",
|
||||
"panelStickyScroll.shadow": "#00000000",
|
||||
@@ -64,7 +64,7 @@
|
||||
"list.activeSelectionForeground": "#202020",
|
||||
"list.inactiveSelectionBackground": "#E0E0E0",
|
||||
"list.inactiveSelectionForeground": "#202020",
|
||||
"list.hoverBackground": "#F7F7F7",
|
||||
"list.hoverBackground": "#F3F3F3",
|
||||
"list.hoverForeground": "#202020",
|
||||
"list.dropBackground": "#0069CC15",
|
||||
"list.focusBackground": "#0069CC1A",
|
||||
@@ -106,7 +106,7 @@
|
||||
"commandCenter.foreground": "#202020",
|
||||
"commandCenter.activeForeground": "#202020",
|
||||
"commandCenter.background": "#FAFAFD",
|
||||
"commandCenter.activeBackground": "#F0F0F3",
|
||||
"commandCenter.activeBackground": "#F3F3F3",
|
||||
"commandCenter.border": "#D8D8D8",
|
||||
"editor.background": "#FFFFFF",
|
||||
"editor.foreground": "#202020",
|
||||
@@ -127,21 +127,21 @@
|
||||
"editorLink.activeForeground": "#0069CC",
|
||||
"editorWhitespace.foreground": "#66666640",
|
||||
"editorIndentGuide.background": "#F7F7F740",
|
||||
"editorIndentGuide.activeBackground": "#F7F7F7",
|
||||
"editorIndentGuide.activeBackground": "#F3F3F3",
|
||||
"editorRuler.foreground": "#F7F7F7",
|
||||
"editorCodeLens.foreground": "#666666",
|
||||
"editorBracketMatch.background": "#0069CC40",
|
||||
"editorBracketMatch.border": "#F2F3F4FF",
|
||||
"editorWidget.background": "#F0F0F3",
|
||||
"editorWidget.border": "#F2F3F4FF",
|
||||
"editorWidget.border": "#EEEEF1",
|
||||
"editorWidget.foreground": "#202020",
|
||||
"editorSuggestWidget.background": "#F0F0F3",
|
||||
"editorSuggestWidget.border": "#F2F3F4FF",
|
||||
"editorSuggestWidget.border": "#EEEEF1",
|
||||
"editorSuggestWidget.foreground": "#202020",
|
||||
"editorSuggestWidget.highlightForeground": "#0069CC",
|
||||
"editorSuggestWidget.selectedBackground": "#0069CC26",
|
||||
"editorHoverWidget.background": "#F0F0F3",
|
||||
"editorHoverWidget.border": "#F2F3F4FF",
|
||||
"editorHoverWidget.border": "#EEEEF1",
|
||||
"peekView.border": "#0069CC",
|
||||
"peekViewEditor.background": "#F0F0F3",
|
||||
"peekViewEditor.matchHighlightBackground": "#0069CC33",
|
||||
@@ -179,13 +179,13 @@
|
||||
"statusBar.debuggingForeground": "#FFFFFF",
|
||||
"statusBar.noFolderBackground": "#F0F0F3",
|
||||
"statusBar.noFolderForeground": "#666666",
|
||||
"statusBarItem.activeBackground": "#E6E6E6",
|
||||
"statusBarItem.hoverBackground": "#F7F7F7",
|
||||
"statusBarItem.activeBackground": "#F3F3F3",
|
||||
"statusBarItem.hoverBackground": "#F3F3F3",
|
||||
"statusBarItem.focusBorder": "#0069CCFF",
|
||||
"statusBarItem.prominentBackground": "#0069CCDD",
|
||||
"statusBarItem.prominentForeground": "#FFFFFF",
|
||||
"statusBarItem.prominentHoverBackground": "#0069CC",
|
||||
"tab.activeBackground": "#FAFAFD",
|
||||
"tab.activeBackground": "#FFFFFF",
|
||||
"tab.activeForeground": "#202020",
|
||||
"tab.inactiveBackground": "#FAFAFD",
|
||||
"tab.inactiveForeground": "#666666",
|
||||
@@ -193,7 +193,7 @@
|
||||
"tab.lastPinnedBorder": "#F2F3F4FF",
|
||||
"tab.activeBorder": "#FAFAFD",
|
||||
"tab.activeBorderTop": "#000000",
|
||||
"tab.hoverBackground": "#F7F7F7",
|
||||
"tab.hoverBackground": "#F3F3F3",
|
||||
"tab.hoverForeground": "#202020",
|
||||
"tab.unfocusedActiveBackground": "#FAFAFD",
|
||||
"tab.unfocusedActiveForeground": "#666666",
|
||||
@@ -202,7 +202,7 @@
|
||||
"editorGroupHeader.tabsBackground": "#FAFAFD",
|
||||
"editorGroupHeader.tabsBorder": "#F2F3F4FF",
|
||||
"breadcrumb.foreground": "#666666",
|
||||
"breadcrumb.background": "#FAFAFD",
|
||||
"breadcrumb.background": "#FFFFFF",
|
||||
"breadcrumb.focusForeground": "#202020",
|
||||
"breadcrumb.activeSelectionForeground": "#202020",
|
||||
"breadcrumbPicker.background": "#F0F0F3",
|
||||
@@ -265,7 +265,8 @@
|
||||
"charts.yellow": "#667309",
|
||||
"charts.orange": "#d18616",
|
||||
"charts.green": "#388A34",
|
||||
"charts.purple": "#652D90"
|
||||
"charts.purple": "#652D90",
|
||||
"agentStatusIndicator.background": "#FFFFFF",
|
||||
},
|
||||
"tokenColors": [
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 6px;
|
||||
--radius-lg: 8px;
|
||||
--radius-xl: 12px;
|
||||
/* --radius-lg: 12px; */
|
||||
|
||||
--shadow-xs: 0 0 2px rgba(0, 0, 0, 0.06);
|
||||
--shadow-sm: 0 0 4px rgba(0, 0, 0, 0.08);
|
||||
@@ -131,15 +131,11 @@
|
||||
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active {
|
||||
box-shadow: inset var(--shadow-active-tab);
|
||||
/* background: var(--vs) */
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
border-radius: 0;
|
||||
border-top: none !important;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
color-mix(in srgb, var(--vscode-focusBorder) 10%, transparent) 0%,
|
||||
transparent 100%
|
||||
), var(--vscode-tab-activeBackground) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover:not(.active) {
|
||||
@@ -177,12 +173,16 @@
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-lg);
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .quick-input-widget {
|
||||
border: 1px solid var(--vscode-menu-border) !important;
|
||||
/* Remove backdrop-filter when quick chat is active, because it creates a new
|
||||
containing block that shifts position:fixed suggest widgets to the right. */
|
||||
.monaco-workbench .quick-input-widget:has(.interactive-session) {
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
background-color: var(--vscode-quickInput-background) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .quick-input-widget .monaco-list-rows {
|
||||
background-color: transparent !important;
|
||||
.monaco-workbench.vs-dark .quick-input-widget {
|
||||
border: 1px solid var(--vscode-menu-border) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .quick-input-widget .quick-input-header,
|
||||
@@ -199,6 +199,10 @@
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-editor .suggest-widget .monaco-list {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.monaco-workbench .quick-input-widget .monaco-inputbox {
|
||||
box-shadow: none !important;
|
||||
background: transparent !important;
|
||||
@@ -263,6 +267,8 @@
|
||||
backdrop-filter: var(--backdrop-blur-md);
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-md);
|
||||
background: color-mix(in srgb, var(--vscode-notifications-background) 60%, transparent) !important;
|
||||
border: 1px solid var(--vscode-editorWidget-border) !important;
|
||||
box-shadow: var(--shadow-lg) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .notifications-center {
|
||||
@@ -277,16 +283,18 @@
|
||||
|
||||
/* Context Menus */
|
||||
.monaco-workbench .monaco-menu .monaco-action-bar.vertical {
|
||||
box-shadow: var(--shadow-lg);
|
||||
border-radius: var(--radius-xl);
|
||||
backdrop-filter: var(--backdrop-blur-md);
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-md);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.monaco-workbench .context-view .monaco-menu {
|
||||
box-shadow: var(--shadow-lg);
|
||||
border: none;
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-menu-container > .monaco-scrollable-element {
|
||||
border-radius: var(--radius-lg) !important;
|
||||
box-shadow: var(--shadow-lg) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .action-widget {
|
||||
@@ -302,8 +310,7 @@
|
||||
/* Suggest Widget */
|
||||
.monaco-workbench .monaco-editor .suggest-widget {
|
||||
box-shadow: var(--shadow-lg);
|
||||
border: none;
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
backdrop-filter: var(--backdrop-blur-md);
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-md);
|
||||
background: color-mix(in srgb, var(--vscode-editorSuggestWidget-background) 60%, transparent) !important;
|
||||
@@ -323,10 +330,17 @@
|
||||
margin-top: 4px !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .inline-chat-gutter-menu {
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-lg);
|
||||
backdrop-filter: var(--backdrop-blur-md);
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-md);
|
||||
}
|
||||
|
||||
/* Dialog */
|
||||
.monaco-workbench .monaco-dialog-box {
|
||||
box-shadow: var(--shadow-2xl);
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
backdrop-filter: var(--backdrop-blur-lg);
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-lg);
|
||||
background: color-mix(in srgb, var(--vscode-editor-background) 60%, transparent) !important;
|
||||
@@ -429,6 +443,10 @@
|
||||
background: color-mix(in srgb, var(--vscode-breadcrumbPicker-background) 60%, transparent) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs .breadcrumbs-control {
|
||||
border-bottom: 1px solid var(--vscode-editorWidget-border);
|
||||
}
|
||||
|
||||
/* Input Boxes */
|
||||
.monaco-workbench .monaco-inputbox,
|
||||
.monaco-workbench .suggest-input-container {
|
||||
@@ -530,7 +548,7 @@
|
||||
/* Parameter Hints */
|
||||
.monaco-workbench .monaco-editor .parameter-hints-widget {
|
||||
box-shadow: var(--shadow-lg);
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
backdrop-filter: var(--backdrop-blur-md);
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-md);
|
||||
}
|
||||
@@ -561,10 +579,14 @@
|
||||
/* Sticky Scroll */
|
||||
.monaco-workbench .monaco-editor .sticky-widget {
|
||||
box-shadow: var(--shadow-md) !important;
|
||||
border-bottom: none !important;
|
||||
background: color-mix(in srgb, var(--vscode-editor-background) 60%, transparent) !important;
|
||||
backdrop-filter: var(--backdrop-blur-lg) !important;
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-lg) !important;
|
||||
border-bottom: var(--vscode-editorWidget-border) !important;
|
||||
background: transparent !important;
|
||||
backdrop-filter: var(--backdrop-blur-md) !important;
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-md) !important;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-editor .sticky-widget > * {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .monaco-editor .sticky-widget {
|
||||
@@ -572,31 +594,9 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-editor .sticky-widget .sticky-widget-lines {
|
||||
background-color: transparent !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .monaco-editor .sticky-widget,
|
||||
.monaco-workbench .monaco-editor .sticky-widget-focus-preview,
|
||||
.monaco-workbench .monaco-editor .sticky-scroll-focus-line,
|
||||
.monaco-workbench .monaco-editor .focused .sticky-widget,
|
||||
.monaco-workbench .monaco-editor:has(.sticky-widget:focus-within) .sticky-widget {
|
||||
background: color-mix(in srgb, var(--vscode-editor-background) 60%, transparent) !important;
|
||||
backdrop-filter: var(--backdrop-blur-lg) !important;
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-lg) !important;
|
||||
box-shadow: var(--shadow-hover) !important;
|
||||
}
|
||||
|
||||
.monaco-editor .sticky-widget .sticky-line-content,
|
||||
.monaco-workbench .monaco-editor .sticky-widget .sticky-line-number {
|
||||
backdrop-filter: var(--backdrop-blur-lg) !important;
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-lg) !important;
|
||||
background-color: color-mix(in srgb, var(--vscode-editor-background) 60%, transparent);
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .monaco-editor .sticky-widget .sticky-line-content,
|
||||
.monaco-workbench.vs-dark .monaco-editor .sticky-widget .sticky-line-number {
|
||||
background-color: color-mix(in srgb, var(--vscode-editor-background) 30%, transparent);
|
||||
background: color-mix(in srgb, var(--vscode-editor-background) 40%, transparent) !important;
|
||||
backdrop-filter: var(--backdrop-blur-md) !important;
|
||||
-webkit-backdrop-filter: var(--backdrop-blur-md) !important;
|
||||
}
|
||||
|
||||
.monaco-editor .rename-box.preview {
|
||||
@@ -608,9 +608,9 @@
|
||||
|
||||
/* Notebook */
|
||||
|
||||
/* .monaco-workbench .notebookOverlay.notebook-editor {
|
||||
.monaco-workbench .notebookOverlay.notebook-editor {
|
||||
z-index: 35 !important;
|
||||
} */
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .monaco-list-row .cell-editor-part:before {
|
||||
box-shadow: inset var(--shadow-sm);
|
||||
@@ -633,7 +633,7 @@
|
||||
.monaco-workbench .monaco-editor .inline-chat {
|
||||
box-shadow: var(--shadow-lg);
|
||||
border: none;
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
/* Command Center */
|
||||
@@ -652,10 +652,25 @@
|
||||
|
||||
.monaco-workbench .part.titlebar .command-center .agent-status-pill {
|
||||
box-shadow: inset var(--shadow-sm);
|
||||
border-color: var(--vscode-input-border);
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar .command-center .agent-status-badge {
|
||||
border-color: var(--vscode-input-border);
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .monaco-action-bar:not(.vertical) .agent-status-badge-section.sparkle .action-container:hover,
|
||||
.monaco-workbench.vs-dark .monaco-action-bar:not(.vertical) .agent-status-badge-section.sparkle .dropdown-action-container:hover
|
||||
{
|
||||
background-color: var(--vscode-toolbar-hoverBackground);
|
||||
}
|
||||
|
||||
.monaco-workbench.vs-dark .monaco-action-bar:not(.vertical) .agent-status-badge .monaco-dropdown-with-primary:not(.disabled):hover {
|
||||
background-color: var(--vscode-commandCenter-activeBackground);
|
||||
}
|
||||
|
||||
.monaco-dialog-modal-block .dialog-shadow {
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.monaco-workbench .unified-quick-access-tabs {
|
||||
|
||||
@@ -173,36 +173,6 @@
|
||||
"description": "%typescript.enablePromptUseWorkspaceTsdk%",
|
||||
"scope": "window"
|
||||
},
|
||||
"javascript.referencesCodeLens.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%javascript.referencesCodeLens.enabled%",
|
||||
"scope": "window"
|
||||
},
|
||||
"javascript.referencesCodeLens.showOnAllFunctions": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%javascript.referencesCodeLens.showOnAllFunctions%",
|
||||
"scope": "window"
|
||||
},
|
||||
"typescript.referencesCodeLens.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%typescript.referencesCodeLens.enabled%",
|
||||
"scope": "window"
|
||||
},
|
||||
"typescript.referencesCodeLens.showOnAllFunctions": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%typescript.referencesCodeLens.showOnAllFunctions%",
|
||||
"scope": "window"
|
||||
},
|
||||
"typescript.implementationsCodeLens.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%typescript.implementationsCodeLens.enabled%",
|
||||
"scope": "window"
|
||||
},
|
||||
"typescript.experimental.useTsgo": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
@@ -212,16 +182,103 @@
|
||||
"experimental"
|
||||
]
|
||||
},
|
||||
"js/ts.referencesCodeLens.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.referencesCodeLens.enabled%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"javascript.referencesCodeLens.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.referencesCodeLens.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.referencesCodeLens.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "window"
|
||||
},
|
||||
"typescript.referencesCodeLens.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.referencesCodeLens.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.referencesCodeLens.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "window"
|
||||
},
|
||||
"js/ts.referencesCodeLens.showOnAllFunctions": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.referencesCodeLens.showOnAllFunctions%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"javascript.referencesCodeLens.showOnAllFunctions": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.referencesCodeLens.showOnAllFunctions%",
|
||||
"markdownDeprecationMessage": "%configuration.referencesCodeLens.showOnAllFunctions.unifiedDeprecationMessage%",
|
||||
"scope": "window"
|
||||
},
|
||||
"typescript.referencesCodeLens.showOnAllFunctions": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.referencesCodeLens.showOnAllFunctions%",
|
||||
"markdownDeprecationMessage": "%configuration.referencesCodeLens.showOnAllFunctions.unifiedDeprecationMessage%",
|
||||
"scope": "window"
|
||||
},
|
||||
"js/ts.implementationsCodeLens.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.implementationsCodeLens.enabled%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"typescript.implementationsCodeLens.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.implementationsCodeLens.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.implementationsCodeLens.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "window"
|
||||
},
|
||||
"js/ts.implementationsCodeLens.showOnInterfaceMethods": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.implementationsCodeLens.showOnInterfaceMethods%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"typescript.implementationsCodeLens.showOnInterfaceMethods": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%typescript.implementationsCodeLens.showOnInterfaceMethods%",
|
||||
"description": "%configuration.implementationsCodeLens.showOnInterfaceMethods%",
|
||||
"markdownDeprecationMessage": "%configuration.implementationsCodeLens.showOnInterfaceMethods.unifiedDeprecationMessage%",
|
||||
"scope": "window"
|
||||
},
|
||||
"js/ts.implementationsCodeLens.showOnAllClassMethods": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%configuration.implementationsCodeLens.showOnAllClassMethods%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"typescript.implementationsCodeLens.showOnAllClassMethods": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%typescript.implementationsCodeLens.showOnAllClassMethods%",
|
||||
"description": "%configuration.implementationsCodeLens.showOnAllClassMethods%",
|
||||
"markdownDeprecationMessage": "%configuration.implementationsCodeLens.showOnAllClassMethods.unifiedDeprecationMessage%",
|
||||
"scope": "window"
|
||||
},
|
||||
"typescript.reportStyleChecksAsWarnings": {
|
||||
|
||||
@@ -49,13 +49,16 @@
|
||||
"javascript.validate.enable": "Enable/disable JavaScript validation.",
|
||||
"javascript.goToProjectConfig.title": "Go to Project Configuration (jsconfig / tsconfig)",
|
||||
"typescript.goToProjectConfig.title": "Go to Project Configuration (tsconfig)",
|
||||
"javascript.referencesCodeLens.enabled": "Enable/disable references CodeLens in JavaScript files.",
|
||||
"javascript.referencesCodeLens.showOnAllFunctions": "Enable/disable references CodeLens on all functions in JavaScript files.",
|
||||
"typescript.referencesCodeLens.enabled": "Enable/disable references CodeLens in TypeScript files.",
|
||||
"typescript.referencesCodeLens.showOnAllFunctions": "Enable/disable references CodeLens on all functions in TypeScript files.",
|
||||
"typescript.implementationsCodeLens.enabled": "Enable/disable implementations CodeLens. This CodeLens shows the implementers of an interface.",
|
||||
"typescript.implementationsCodeLens.showOnInterfaceMethods": "Enable/disable implementations CodeLens on interface methods.",
|
||||
"typescript.implementationsCodeLens.showOnAllClassMethods": "Enable/disable showing implementations CodeLens above all class methods instead of only on abstract methods.",
|
||||
"configuration.referencesCodeLens.enabled": "Enable/disable references CodeLens in JavaScript and TypeScript files. This CodeLens shows the number of references for classes and exported functions and allows you to peek or navigate to them.",
|
||||
"configuration.referencesCodeLens.enabled.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.referencesCodeLens.enabled#` instead.",
|
||||
"configuration.referencesCodeLens.showOnAllFunctions": "Enable/disable the references CodeLens on all functions in JavaScript and TypeScript files.",
|
||||
"configuration.referencesCodeLens.showOnAllFunctions.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.referencesCodeLens.showOnAllFunctions#` instead.",
|
||||
"configuration.implementationsCodeLens.enabled": "Enable/disable implementations CodeLens in TypeScript files. This CodeLens shows the implementers of a TypeScript interface.",
|
||||
"configuration.implementationsCodeLens.enabled.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.implementationsCodeLens.enabled#` instead.",
|
||||
"configuration.implementationsCodeLens.showOnInterfaceMethods": "Enable/disable implementations CodeLens on TypeScript interface methods.",
|
||||
"configuration.implementationsCodeLens.showOnInterfaceMethods.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.implementationsCodeLens.showOnInterfaceMethods#` instead.",
|
||||
"configuration.implementationsCodeLens.showOnAllClassMethods": "Enable/disable showing implementations CodeLens above all TypeScript class methods instead of only on abstract methods.",
|
||||
"configuration.implementationsCodeLens.showOnAllClassMethods.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.implementationsCodeLens.showOnAllClassMethods#` instead.",
|
||||
"typescript.openTsServerLog.title": "Open TS Server log",
|
||||
"typescript.restartTsServer": "Restart TS Server",
|
||||
"typescript.selectTypeScriptVersion.title": "Select TypeScript Version...",
|
||||
|
||||
+28
-8
@@ -11,10 +11,16 @@ import type * as Proto from '../../tsServer/protocol/protocol';
|
||||
import * as PConst from '../../tsServer/protocol/protocol.const';
|
||||
import * as typeConverters from '../../typeConverters';
|
||||
import { ClientCapability, ITypeScriptServiceClient } from '../../typescriptService';
|
||||
import { conditionalRegistration, requireGlobalConfiguration, requireSomeCapability } from '../util/dependentRegistration';
|
||||
import { readUnifiedConfig, unifiedConfigSection } from '../../utils/configuration';
|
||||
import { conditionalRegistration, requireHasModifiedUnifiedConfig, requireSomeCapability } from '../util/dependentRegistration';
|
||||
import { ReferencesCodeLens, TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider';
|
||||
import { ExecutionTarget } from '../../tsServer/server';
|
||||
|
||||
const Config = Object.freeze({
|
||||
enabled: 'implementationsCodeLens.enabled',
|
||||
showOnInterfaceMethods: 'implementationsCodeLens.showOnInterfaceMethods',
|
||||
showOnAllClassMethods: 'implementationsCodeLens.showOnAllClassMethods',
|
||||
});
|
||||
|
||||
export default class TypeScriptImplementationsCodeLensProvider extends TypeScriptBaseCodeLensProvider {
|
||||
public constructor(
|
||||
@@ -25,14 +31,30 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
|
||||
super(client, _cachedResponse);
|
||||
this._register(
|
||||
vscode.workspace.onDidChangeConfiguration(evt => {
|
||||
if (evt.affectsConfiguration(`${language.id}.implementationsCodeLens.showOnInterfaceMethods`) ||
|
||||
evt.affectsConfiguration(`${language.id}.implementationsCodeLens.showOnAllClassMethods`)) {
|
||||
if (
|
||||
evt.affectsConfiguration(`${unifiedConfigSection}.${Config.enabled}`) ||
|
||||
evt.affectsConfiguration(`${language.id}.${Config.enabled}`) ||
|
||||
evt.affectsConfiguration(`${unifiedConfigSection}.${Config.showOnInterfaceMethods}`) ||
|
||||
evt.affectsConfiguration(`${language.id}.${Config.showOnInterfaceMethods}`) ||
|
||||
evt.affectsConfiguration(`${unifiedConfigSection}.${Config.showOnAllClassMethods}`) ||
|
||||
evt.affectsConfiguration(`${language.id}.${Config.showOnAllClassMethods}`)
|
||||
) {
|
||||
this.changeEmitter.fire();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
override async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<ReferencesCodeLens[]> {
|
||||
const enabled = readUnifiedConfig<boolean>(Config.enabled, false, { scope: document, fallbackSection: this.language.id });
|
||||
if (!enabled) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return super.provideCodeLenses(document, token);
|
||||
}
|
||||
|
||||
public async resolveCodeLens(
|
||||
codeLens: ReferencesCodeLens,
|
||||
token: vscode.CancellationToken,
|
||||
@@ -88,8 +110,6 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
|
||||
item: Proto.NavigationTree,
|
||||
parent: Proto.NavigationTree | undefined
|
||||
): vscode.Range | undefined {
|
||||
const cfg = vscode.workspace.getConfiguration(this.language.id);
|
||||
|
||||
// Always show on interfaces
|
||||
if (item.kind === PConst.Kind.interface) {
|
||||
return getSymbolRange(document, item);
|
||||
@@ -111,7 +131,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
|
||||
if (
|
||||
item.kind === PConst.Kind.method &&
|
||||
parent?.kind === PConst.Kind.interface &&
|
||||
cfg.get<boolean>('implementationsCodeLens.showOnInterfaceMethods', false)
|
||||
readUnifiedConfig<boolean>('implementationsCodeLens.showOnInterfaceMethods', false, { scope: document, fallbackSection: this.language.id })
|
||||
) {
|
||||
return getSymbolRange(document, item);
|
||||
}
|
||||
@@ -121,7 +141,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
|
||||
if (
|
||||
item.kind === PConst.Kind.method &&
|
||||
parent?.kind === PConst.Kind.class &&
|
||||
cfg.get<boolean>('implementationsCodeLens.showOnAllClassMethods', false)
|
||||
readUnifiedConfig<boolean>('implementationsCodeLens.showOnAllClassMethods', false, { scope: document, fallbackSection: this.language.id })
|
||||
) {
|
||||
// But not private ones as these can never be overridden
|
||||
if (/\bprivate\b/.test(item.kindModifiers ?? '')) {
|
||||
@@ -141,7 +161,7 @@ export function register(
|
||||
cachedResponse: CachedResponse<Proto.NavTreeResponse>,
|
||||
) {
|
||||
return conditionalRegistration([
|
||||
requireGlobalConfiguration(language.id, 'implementationsCodeLens.enabled'),
|
||||
requireHasModifiedUnifiedConfig(Config.enabled, language.id),
|
||||
requireSomeCapability(client, ClientCapability.Semantic),
|
||||
], () => {
|
||||
return vscode.languages.registerCodeLensProvider(selector.semantic,
|
||||
|
||||
+23
-4
@@ -12,9 +12,14 @@ import * as PConst from '../../tsServer/protocol/protocol.const';
|
||||
import { ExecutionTarget } from '../../tsServer/server';
|
||||
import * as typeConverters from '../../typeConverters';
|
||||
import { ClientCapability, ITypeScriptServiceClient } from '../../typescriptService';
|
||||
import { conditionalRegistration, requireGlobalConfiguration, requireSomeCapability } from '../util/dependentRegistration';
|
||||
import { readUnifiedConfig, unifiedConfigSection } from '../../utils/configuration';
|
||||
import { conditionalRegistration, requireHasModifiedUnifiedConfig, requireSomeCapability } from '../util/dependentRegistration';
|
||||
import { ReferencesCodeLens, TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider';
|
||||
|
||||
const Config = Object.freeze({
|
||||
enabled: 'referencesCodeLens.enabled',
|
||||
showOnAllFunctions: 'referencesCodeLens.showOnAllFunctions',
|
||||
});
|
||||
|
||||
export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvider {
|
||||
public constructor(
|
||||
@@ -25,13 +30,27 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens
|
||||
super(client, _cachedResponse);
|
||||
this._register(
|
||||
vscode.workspace.onDidChangeConfiguration(evt => {
|
||||
if (evt.affectsConfiguration(`${language.id}.referencesCodeLens.showOnAllFunctions`)) {
|
||||
if (
|
||||
evt.affectsConfiguration(`${unifiedConfigSection}.${Config.enabled}`) ||
|
||||
evt.affectsConfiguration(`${language.id}.${Config.enabled}`) ||
|
||||
evt.affectsConfiguration(`${unifiedConfigSection}.${Config.showOnAllFunctions}`) ||
|
||||
evt.affectsConfiguration(`${language.id}.${Config.showOnAllFunctions}`)
|
||||
) {
|
||||
this.changeEmitter.fire();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<ReferencesCodeLens[]> {
|
||||
const enabled = readUnifiedConfig<boolean>(Config.enabled, false, { scope: document, fallbackSection: this.language.id });
|
||||
if (!enabled) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return super.provideCodeLenses(document, token);
|
||||
}
|
||||
|
||||
public async resolveCodeLens(codeLens: ReferencesCodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> {
|
||||
const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start);
|
||||
const response = await this.client.execute('references', args, token, {
|
||||
@@ -76,7 +95,7 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens
|
||||
|
||||
switch (item.kind) {
|
||||
case PConst.Kind.function: {
|
||||
const showOnAllFunctions = vscode.workspace.getConfiguration(this.language.id).get<boolean>('referencesCodeLens.showOnAllFunctions');
|
||||
const showOnAllFunctions = readUnifiedConfig<boolean>(Config.showOnAllFunctions, false, { scope: document, fallbackSection: this.language.id });
|
||||
if (showOnAllFunctions && item.nameSpan) {
|
||||
return getSymbolRange(document, item);
|
||||
}
|
||||
@@ -137,7 +156,7 @@ export function register(
|
||||
cachedResponse: CachedResponse<Proto.NavTreeResponse>,
|
||||
) {
|
||||
return conditionalRegistration([
|
||||
requireGlobalConfiguration(language.id, 'referencesCodeLens.enabled'),
|
||||
requireHasModifiedUnifiedConfig(Config.enabled, language.id),
|
||||
requireSomeCapability(client, ClientCapability.Semantic),
|
||||
], () => {
|
||||
return vscode.languages.registerCodeLensProvider(selector.semantic,
|
||||
|
||||
+16
@@ -6,6 +6,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { API } from '../../tsServer/api';
|
||||
import { ClientCapability, ITypeScriptServiceClient } from '../../typescriptService';
|
||||
import { hasModifiedUnifiedConfig } from '../../utils/configuration';
|
||||
import { Disposable } from '../../utils/dispose';
|
||||
|
||||
export class Condition extends Disposable {
|
||||
@@ -102,6 +103,21 @@ export function requireGlobalConfiguration(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires that a configuration value has been modified from its default value in either the global or workspace scope
|
||||
*
|
||||
* Does not check the value, only that it has been modified from the default.
|
||||
*/
|
||||
export function requireHasModifiedUnifiedConfig(
|
||||
configValue: string,
|
||||
fallbackSection: string,
|
||||
) {
|
||||
return new Condition(
|
||||
() => hasModifiedUnifiedConfig(configValue, { fallbackSection }),
|
||||
vscode.workspace.onDidChangeConfiguration
|
||||
);
|
||||
}
|
||||
|
||||
export function requireSomeCapability(
|
||||
client: ITypeScriptServiceClient,
|
||||
...capabilities: readonly ClientCapability[]
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
type ConfigurationScope = vscode.ConfigurationScope | null | undefined;
|
||||
|
||||
export const unifiedConfigSection = 'js/ts';
|
||||
|
||||
/**
|
||||
* Gets a configuration value, checking the unified `js/ts` setting first,
|
||||
* then falling back to the language-specific setting.
|
||||
*/
|
||||
export function readUnifiedConfig<T>(
|
||||
subSectionName: string,
|
||||
defaultValue: T,
|
||||
options: {
|
||||
readonly scope?: ConfigurationScope;
|
||||
readonly fallbackSection: string;
|
||||
}
|
||||
): T {
|
||||
// Check unified setting first
|
||||
const unifiedConfig = vscode.workspace.getConfiguration(unifiedConfigSection, options.scope);
|
||||
const unifiedInspect = unifiedConfig.inspect<T>(subSectionName);
|
||||
if (hasModifiedValue(unifiedInspect)) {
|
||||
return unifiedConfig.get<T>(subSectionName, defaultValue);
|
||||
}
|
||||
|
||||
// Fall back to language-specific setting
|
||||
const languageConfig = vscode.workspace.getConfiguration(options.fallbackSection, options.scope);
|
||||
return languageConfig.get<T>(subSectionName, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an inspected configuration value has any user-defined values set.
|
||||
*/
|
||||
function hasModifiedValue(inspect: ReturnType<vscode.WorkspaceConfiguration['inspect']>): boolean {
|
||||
if (!inspect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
typeof inspect.globalValue !== 'undefined'
|
||||
|| typeof inspect.workspaceValue !== 'undefined'
|
||||
|| typeof inspect.workspaceFolderValue !== 'undefined'
|
||||
|| typeof inspect.globalLanguageValue !== 'undefined'
|
||||
|| typeof inspect.workspaceLanguageValue !== 'undefined'
|
||||
|| typeof inspect.workspaceFolderLanguageValue !== 'undefined'
|
||||
|| ((inspect.languageIds?.length ?? 0) > 0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a unified configuration value has been modified from its default value.
|
||||
*/
|
||||
export function hasModifiedUnifiedConfig(
|
||||
subSectionName: string,
|
||||
options: {
|
||||
readonly scope?: ConfigurationScope;
|
||||
readonly fallbackSection: string;
|
||||
}
|
||||
): boolean {
|
||||
// Check unified setting
|
||||
const unifiedConfig = vscode.workspace.getConfiguration(unifiedConfigSection, options.scope);
|
||||
if (hasModifiedValue(unifiedConfig.inspect(subSectionName))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check language-specific setting
|
||||
const languageConfig = vscode.workspace.getConfiguration(options.fallbackSection, options.scope);
|
||||
return hasModifiedValue(languageConfig.inspect(subSectionName));
|
||||
}
|
||||
Generated
+52
-52
@@ -15,7 +15,7 @@
|
||||
"@microsoft/1ds-post-js": "^3.2.13",
|
||||
"@parcel/watcher": "^2.5.6",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@vscode/codicons": "^0.0.45-5",
|
||||
"@vscode/codicons": "^0.0.45-6",
|
||||
"@vscode/deviceid": "^0.1.1",
|
||||
"@vscode/iconv-lite-umd": "0.7.1",
|
||||
"@vscode/native-watchdog": "^1.4.6",
|
||||
@@ -30,16 +30,16 @@
|
||||
"@vscode/windows-mutex": "^0.5.0",
|
||||
"@vscode/windows-process-tree": "^0.6.0",
|
||||
"@vscode/windows-registry": "^1.1.0",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.144",
|
||||
"@xterm/addon-image": "^0.10.0-beta.144",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.144",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.144",
|
||||
"@xterm/addon-search": "^0.17.0-beta.144",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.144",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.144",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.143",
|
||||
"@xterm/headless": "^6.1.0-beta.144",
|
||||
"@xterm/xterm": "^6.1.0-beta.144",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.152",
|
||||
"@xterm/addon-image": "^0.10.0-beta.152",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.152",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.152",
|
||||
"@xterm/addon-search": "^0.17.0-beta.152",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.152",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.152",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.151",
|
||||
"@xterm/headless": "^6.1.0-beta.152",
|
||||
"@xterm/xterm": "^6.1.0-beta.152",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
"jschardet": "3.1.4",
|
||||
@@ -2947,9 +2947,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@vscode/codicons": {
|
||||
"version": "0.0.45-5",
|
||||
"resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.45-5.tgz",
|
||||
"integrity": "sha512-tvRtMrVAe+CnlePF1Z3uhRfu0mLhAVgrSiY9zuEaGyXyBlN1/06bnpXno8XYb4O3ggOFx2phximqje4dhoLnkQ==",
|
||||
"version": "0.0.45-6",
|
||||
"resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.45-6.tgz",
|
||||
"integrity": "sha512-HjJmIxw6anUPk/yiQTyF60ERjARNfc/A11kKoiO7jg2bzNeaCexunu4oUo/W8lHGr/dvHxYcruM1V3ZoGxyFNQ==",
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/@vscode/deviceid": {
|
||||
@@ -3895,30 +3895,30 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-clipboard": {
|
||||
"version": "0.3.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.3.0-beta.144.tgz",
|
||||
"integrity": "sha512-yH7eL8Gd9SxjNCe7WIRCHhKBfo7hggjLw6CznCY39HoUdF87xfCuk3mBj6itvZLNkSx8uvB8IXfYmXasLarwEg==",
|
||||
"version": "0.3.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.3.0-beta.152.tgz",
|
||||
"integrity": "sha512-D+wFHTTNj1qzlSL1h15tgFh6JgK/SSaotkohtaKykkKFmkdGrtJq8PpINaFipRDrZXX0d9eOD+wrMfz6IG+5Yw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-base64": "^3.7.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-image": {
|
||||
"version": "0.10.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.10.0-beta.144.tgz",
|
||||
"integrity": "sha512-PvLt7MokuHItiYnHqtX8qTTWS73vQgPcpuBKVapozM4yp65Y4kwSt0nOrohKqiyCTWPyMWW0NcaStoUJLHXyvg==",
|
||||
"version": "0.10.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.10.0-beta.152.tgz",
|
||||
"integrity": "sha512-pyQ/hQr3O0gY1La+6ZXdh0tI/+6MmNo2eFPNyWzB21J02xMu6nc30+B/H9VlPSR3AXHno5U67AWra5Y4FrE+5A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-ligatures": {
|
||||
"version": "0.11.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.144.tgz",
|
||||
"integrity": "sha512-JhfkL4HtMhO1XdXugAGpYwwIzp9mE90G+J1M00FsLVmccuI53BAI16+SLUZ4w3AOonwWg/vlVxGSxKSkSY+D6A==",
|
||||
"version": "0.11.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.152.tgz",
|
||||
"integrity": "sha512-DglTaxmWHolTfryequU/7+Q4bjpDywt7UDsE3SdbC7O/9fa1qaOZMVlxKtRBtMBBzX5PXa+Ha4qAaMS2psr3UQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0",
|
||||
@@ -3928,7 +3928,7 @@
|
||||
"node": ">8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-ligatures/node_modules/lru-cache": {
|
||||
@@ -3950,63 +3950,63 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@xterm/addon-progress": {
|
||||
"version": "0.3.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.3.0-beta.144.tgz",
|
||||
"integrity": "sha512-VbQu1y40UHeLKinJgEb8FwSq4Czx5h4o/TSvAVkXegdH8FAWx8YiOpNvFLEIAgboQBqgV2lgn2Azec70Ielqzg==",
|
||||
"version": "0.3.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.3.0-beta.152.tgz",
|
||||
"integrity": "sha512-H3qNwUaTNDRm51s8IzcYRinnQBSf7QDXWkcyAuDlprDJlR5BFhmGr9hpMV/KlCo2s6nhWrFjiwkd642DJ7McMg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-search": {
|
||||
"version": "0.17.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.144.tgz",
|
||||
"integrity": "sha512-+gjbQZBWqZiq8JciliUz+B6M5YPSIzT30pOi0IfC5GZHeDeDF82H02UwD2PDGweMJaWdPNtwSxrXTYr9SvtwjA==",
|
||||
"version": "0.17.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.152.tgz",
|
||||
"integrity": "sha512-0T/xDg0yh3PlS9HWOioIrNGP0OfUp4MtBJ7M2sfR+h23KKa4gl1ec7S1TsGU4gsvEMBKG1TB6jReX4vlKGYc4A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-serialize": {
|
||||
"version": "0.15.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.15.0-beta.144.tgz",
|
||||
"integrity": "sha512-OqXJf6OYOpsM4kBRHXeRVwEisWzHbnMH1ROBwmEspDnXP2NiOfoKW6E0C4Cla5qN91AYZTXcGwbyp7D+DfH9aQ==",
|
||||
"version": "0.15.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.15.0-beta.152.tgz",
|
||||
"integrity": "sha512-GnhwKg0dkpAI1gZmm9L69Xjseal5pKwXFaMUxzm+Viajcp/PdqK1pEBJX5RndToNF0Ti3xu4e6BFO7dqY/J9TA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-unicode11": {
|
||||
"version": "0.10.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.144.tgz",
|
||||
"integrity": "sha512-5NILSGbDh0t6jB5YEDzQQGAFo8zUmp7JGEBNkgCG6GHEAC8C4o8L68+RNrN+PcaAi6ucClyB4eqHQt3ZwQJ//A==",
|
||||
"version": "0.10.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.152.tgz",
|
||||
"integrity": "sha512-2HSpMjbckGAmU/56CTGuEbCZJZHxUlfNJ2uzR4akZyVVLEmavC4thHVSGT7Ei1zzpHZsAg0y4WMbcp4wzpPv3g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-webgl": {
|
||||
"version": "0.20.0-beta.143",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.20.0-beta.143.tgz",
|
||||
"integrity": "sha512-wXWwg043EsLWZvbJoxdvS+xyp4KC2f9Mhxgv8i4Kby6zYXOIKuhvh/s+VpR0dzbeHECzKO0wUJT5SKDPxFv1hA==",
|
||||
"version": "0.20.0-beta.151",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.20.0-beta.151.tgz",
|
||||
"integrity": "sha512-3ogsmZKPKc8n9Mjik4jTmNYT2Nbboe/zqcjDNG7RONO3w/tUyoKQshYCMBxxGMNLDwvh3BQ/D9/6JvdNWA1ShA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/headless": {
|
||||
"version": "6.1.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-6.1.0-beta.144.tgz",
|
||||
"integrity": "sha512-hHVZas1eJNq5t3g6EkljriMK6IeUQKyaB+88p7B5KfuDUC53RFSxNj9lQxE37iSdzOC17qE+lzfdk5rGu1+WBg==",
|
||||
"version": "6.1.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-6.1.0-beta.152.tgz",
|
||||
"integrity": "sha512-Hkt+KPuifM8kqDKtbHq1uIhqdZMQazKTl9zaqjcWY3Vogx7+JVr6F+eN89KHnrvhUUOmhAM0JQAIRv1O+upfUw==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"addons/*"
|
||||
]
|
||||
},
|
||||
"node_modules/@xterm/xterm": {
|
||||
"version": "6.1.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.1.0-beta.144.tgz",
|
||||
"integrity": "sha512-sV4pvPEYhJJ4crzgggmGwyoQgXoxXAK4fo1VqW2XxTpWpnhG7hdkZ7a25yEqF7Oq7GKXdr5jyNzl6QVv8Cm/pg==",
|
||||
"version": "6.1.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.1.0-beta.152.tgz",
|
||||
"integrity": "sha512-XHJ5ab19V6tmcHmBE7k9IYjXSwTxUd0c7oKLa5J+ZO0+aiXE8UKh9OEDw1oyl5ZQhw9gn71cGEo4TpB58KhfoQ==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"addons/*"
|
||||
|
||||
+15
-12
@@ -20,7 +20,7 @@
|
||||
"postinstall": "node build/npm/postinstall.ts",
|
||||
"compile": "npm run gulp compile",
|
||||
"compile-check-ts-native": "tsgo --project ./src/tsconfig.json --noEmit --skipLibCheck",
|
||||
"watch": "npm-run-all2 -lp watch-client watch-extensions",
|
||||
"watch": "npm-run-all2 -lp watch-client-transpile watch-client watch-extensions",
|
||||
"watchd": "deemon npm run watch",
|
||||
"watch-webd": "deemon npm run watch-web",
|
||||
"kill-watchd": "deemon --kill npm run watch",
|
||||
@@ -30,6 +30,9 @@
|
||||
"watch-client": "npm run gulp watch-client",
|
||||
"watch-clientd": "deemon npm run watch-client",
|
||||
"kill-watch-clientd": "deemon --kill npm run watch-client",
|
||||
"watch-client-transpile": "npx tsx build/next/index.ts transpile --watch",
|
||||
"watch-client-transpiled": "deemon npm run watch-client-transpile",
|
||||
"kill-watch-client-transpiled": "deemon --kill npm run watch-client-transpile",
|
||||
"watch-extensions": "npm run gulp watch-extensions watch-extension-media",
|
||||
"watch-extensionsd": "deemon npm run watch-extensions",
|
||||
"kill-watch-extensionsd": "deemon --kill npm run watch-extensions",
|
||||
@@ -77,7 +80,7 @@
|
||||
"@microsoft/1ds-post-js": "^3.2.13",
|
||||
"@parcel/watcher": "^2.5.6",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@vscode/codicons": "^0.0.45-5",
|
||||
"@vscode/codicons": "^0.0.45-6",
|
||||
"@vscode/deviceid": "^0.1.1",
|
||||
"@vscode/iconv-lite-umd": "0.7.1",
|
||||
"@vscode/native-watchdog": "^1.4.6",
|
||||
@@ -92,16 +95,16 @@
|
||||
"@vscode/windows-mutex": "^0.5.0",
|
||||
"@vscode/windows-process-tree": "^0.6.0",
|
||||
"@vscode/windows-registry": "^1.1.0",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.144",
|
||||
"@xterm/addon-image": "^0.10.0-beta.144",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.144",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.144",
|
||||
"@xterm/addon-search": "^0.17.0-beta.144",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.144",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.144",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.143",
|
||||
"@xterm/headless": "^6.1.0-beta.144",
|
||||
"@xterm/xterm": "^6.1.0-beta.144",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.152",
|
||||
"@xterm/addon-image": "^0.10.0-beta.152",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.152",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.152",
|
||||
"@xterm/addon-search": "^0.17.0-beta.152",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.152",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.152",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.151",
|
||||
"@xterm/headless": "^6.1.0-beta.152",
|
||||
"@xterm/xterm": "^6.1.0-beta.152",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
"jschardet": "3.1.4",
|
||||
|
||||
Generated
+48
-48
@@ -22,16 +22,16 @@
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"@vscode/windows-process-tree": "^0.6.0",
|
||||
"@vscode/windows-registry": "^1.1.0",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.144",
|
||||
"@xterm/addon-image": "^0.10.0-beta.144",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.144",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.144",
|
||||
"@xterm/addon-search": "^0.17.0-beta.144",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.144",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.144",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.143",
|
||||
"@xterm/headless": "^6.1.0-beta.144",
|
||||
"@xterm/xterm": "^6.1.0-beta.144",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.152",
|
||||
"@xterm/addon-image": "^0.10.0-beta.152",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.152",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.152",
|
||||
"@xterm/addon-search": "^0.17.0-beta.152",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.152",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.152",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.151",
|
||||
"@xterm/headless": "^6.1.0-beta.152",
|
||||
"@xterm/xterm": "^6.1.0-beta.152",
|
||||
"cookie": "^0.7.0",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
@@ -577,30 +577,30 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@xterm/addon-clipboard": {
|
||||
"version": "0.3.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.3.0-beta.144.tgz",
|
||||
"integrity": "sha512-yH7eL8Gd9SxjNCe7WIRCHhKBfo7hggjLw6CznCY39HoUdF87xfCuk3mBj6itvZLNkSx8uvB8IXfYmXasLarwEg==",
|
||||
"version": "0.3.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.3.0-beta.152.tgz",
|
||||
"integrity": "sha512-D+wFHTTNj1qzlSL1h15tgFh6JgK/SSaotkohtaKykkKFmkdGrtJq8PpINaFipRDrZXX0d9eOD+wrMfz6IG+5Yw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-base64": "^3.7.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-image": {
|
||||
"version": "0.10.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.10.0-beta.144.tgz",
|
||||
"integrity": "sha512-PvLt7MokuHItiYnHqtX8qTTWS73vQgPcpuBKVapozM4yp65Y4kwSt0nOrohKqiyCTWPyMWW0NcaStoUJLHXyvg==",
|
||||
"version": "0.10.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.10.0-beta.152.tgz",
|
||||
"integrity": "sha512-pyQ/hQr3O0gY1La+6ZXdh0tI/+6MmNo2eFPNyWzB21J02xMu6nc30+B/H9VlPSR3AXHno5U67AWra5Y4FrE+5A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-ligatures": {
|
||||
"version": "0.11.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.144.tgz",
|
||||
"integrity": "sha512-JhfkL4HtMhO1XdXugAGpYwwIzp9mE90G+J1M00FsLVmccuI53BAI16+SLUZ4w3AOonwWg/vlVxGSxKSkSY+D6A==",
|
||||
"version": "0.11.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.152.tgz",
|
||||
"integrity": "sha512-DglTaxmWHolTfryequU/7+Q4bjpDywt7UDsE3SdbC7O/9fa1qaOZMVlxKtRBtMBBzX5PXa+Ha4qAaMS2psr3UQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0",
|
||||
@@ -610,67 +610,67 @@
|
||||
"node": ">8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-progress": {
|
||||
"version": "0.3.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.3.0-beta.144.tgz",
|
||||
"integrity": "sha512-VbQu1y40UHeLKinJgEb8FwSq4Czx5h4o/TSvAVkXegdH8FAWx8YiOpNvFLEIAgboQBqgV2lgn2Azec70Ielqzg==",
|
||||
"version": "0.3.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.3.0-beta.152.tgz",
|
||||
"integrity": "sha512-H3qNwUaTNDRm51s8IzcYRinnQBSf7QDXWkcyAuDlprDJlR5BFhmGr9hpMV/KlCo2s6nhWrFjiwkd642DJ7McMg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-search": {
|
||||
"version": "0.17.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.144.tgz",
|
||||
"integrity": "sha512-+gjbQZBWqZiq8JciliUz+B6M5YPSIzT30pOi0IfC5GZHeDeDF82H02UwD2PDGweMJaWdPNtwSxrXTYr9SvtwjA==",
|
||||
"version": "0.17.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.152.tgz",
|
||||
"integrity": "sha512-0T/xDg0yh3PlS9HWOioIrNGP0OfUp4MtBJ7M2sfR+h23KKa4gl1ec7S1TsGU4gsvEMBKG1TB6jReX4vlKGYc4A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-serialize": {
|
||||
"version": "0.15.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.15.0-beta.144.tgz",
|
||||
"integrity": "sha512-OqXJf6OYOpsM4kBRHXeRVwEisWzHbnMH1ROBwmEspDnXP2NiOfoKW6E0C4Cla5qN91AYZTXcGwbyp7D+DfH9aQ==",
|
||||
"version": "0.15.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.15.0-beta.152.tgz",
|
||||
"integrity": "sha512-GnhwKg0dkpAI1gZmm9L69Xjseal5pKwXFaMUxzm+Viajcp/PdqK1pEBJX5RndToNF0Ti3xu4e6BFO7dqY/J9TA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-unicode11": {
|
||||
"version": "0.10.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.144.tgz",
|
||||
"integrity": "sha512-5NILSGbDh0t6jB5YEDzQQGAFo8zUmp7JGEBNkgCG6GHEAC8C4o8L68+RNrN+PcaAi6ucClyB4eqHQt3ZwQJ//A==",
|
||||
"version": "0.10.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.152.tgz",
|
||||
"integrity": "sha512-2HSpMjbckGAmU/56CTGuEbCZJZHxUlfNJ2uzR4akZyVVLEmavC4thHVSGT7Ei1zzpHZsAg0y4WMbcp4wzpPv3g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-webgl": {
|
||||
"version": "0.20.0-beta.143",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.20.0-beta.143.tgz",
|
||||
"integrity": "sha512-wXWwg043EsLWZvbJoxdvS+xyp4KC2f9Mhxgv8i4Kby6zYXOIKuhvh/s+VpR0dzbeHECzKO0wUJT5SKDPxFv1hA==",
|
||||
"version": "0.20.0-beta.151",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.20.0-beta.151.tgz",
|
||||
"integrity": "sha512-3ogsmZKPKc8n9Mjik4jTmNYT2Nbboe/zqcjDNG7RONO3w/tUyoKQshYCMBxxGMNLDwvh3BQ/D9/6JvdNWA1ShA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/headless": {
|
||||
"version": "6.1.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-6.1.0-beta.144.tgz",
|
||||
"integrity": "sha512-hHVZas1eJNq5t3g6EkljriMK6IeUQKyaB+88p7B5KfuDUC53RFSxNj9lQxE37iSdzOC17qE+lzfdk5rGu1+WBg==",
|
||||
"version": "6.1.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-6.1.0-beta.152.tgz",
|
||||
"integrity": "sha512-Hkt+KPuifM8kqDKtbHq1uIhqdZMQazKTl9zaqjcWY3Vogx7+JVr6F+eN89KHnrvhUUOmhAM0JQAIRv1O+upfUw==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"addons/*"
|
||||
]
|
||||
},
|
||||
"node_modules/@xterm/xterm": {
|
||||
"version": "6.1.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.1.0-beta.144.tgz",
|
||||
"integrity": "sha512-sV4pvPEYhJJ4crzgggmGwyoQgXoxXAK4fo1VqW2XxTpWpnhG7hdkZ7a25yEqF7Oq7GKXdr5jyNzl6QVv8Cm/pg==",
|
||||
"version": "6.1.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.1.0-beta.152.tgz",
|
||||
"integrity": "sha512-XHJ5ab19V6tmcHmBE7k9IYjXSwTxUd0c7oKLa5J+ZO0+aiXE8UKh9OEDw1oyl5ZQhw9gn71cGEo4TpB58KhfoQ==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"addons/*"
|
||||
|
||||
+10
-10
@@ -17,16 +17,16 @@
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"@vscode/windows-process-tree": "^0.6.0",
|
||||
"@vscode/windows-registry": "^1.1.0",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.144",
|
||||
"@xterm/addon-image": "^0.10.0-beta.144",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.144",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.144",
|
||||
"@xterm/addon-search": "^0.17.0-beta.144",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.144",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.144",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.143",
|
||||
"@xterm/headless": "^6.1.0-beta.144",
|
||||
"@xterm/xterm": "^6.1.0-beta.144",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.152",
|
||||
"@xterm/addon-image": "^0.10.0-beta.152",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.152",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.152",
|
||||
"@xterm/addon-search": "^0.17.0-beta.152",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.152",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.152",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.151",
|
||||
"@xterm/headless": "^6.1.0-beta.152",
|
||||
"@xterm/xterm": "^6.1.0-beta.152",
|
||||
"cookie": "^0.7.0",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.2",
|
||||
|
||||
Generated
+48
-48
@@ -10,19 +10,19 @@
|
||||
"dependencies": {
|
||||
"@microsoft/1ds-core-js": "^3.2.13",
|
||||
"@microsoft/1ds-post-js": "^3.2.13",
|
||||
"@vscode/codicons": "^0.0.45-5",
|
||||
"@vscode/codicons": "^0.0.45-6",
|
||||
"@vscode/iconv-lite-umd": "0.7.1",
|
||||
"@vscode/tree-sitter-wasm": "^0.3.0",
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.144",
|
||||
"@xterm/addon-image": "^0.10.0-beta.144",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.144",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.144",
|
||||
"@xterm/addon-search": "^0.17.0-beta.144",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.144",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.144",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.143",
|
||||
"@xterm/xterm": "^6.1.0-beta.144",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.152",
|
||||
"@xterm/addon-image": "^0.10.0-beta.152",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.152",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.152",
|
||||
"@xterm/addon-search": "^0.17.0-beta.152",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.152",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.152",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.151",
|
||||
"@xterm/xterm": "^6.1.0-beta.152",
|
||||
"jschardet": "3.1.4",
|
||||
"katex": "^0.16.22",
|
||||
"tas-client": "0.3.1",
|
||||
@@ -73,9 +73,9 @@
|
||||
"integrity": "sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ=="
|
||||
},
|
||||
"node_modules/@vscode/codicons": {
|
||||
"version": "0.0.45-5",
|
||||
"resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.45-5.tgz",
|
||||
"integrity": "sha512-tvRtMrVAe+CnlePF1Z3uhRfu0mLhAVgrSiY9zuEaGyXyBlN1/06bnpXno8XYb4O3ggOFx2phximqje4dhoLnkQ==",
|
||||
"version": "0.0.45-6",
|
||||
"resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.45-6.tgz",
|
||||
"integrity": "sha512-HjJmIxw6anUPk/yiQTyF60ERjARNfc/A11kKoiO7jg2bzNeaCexunu4oUo/W8lHGr/dvHxYcruM1V3ZoGxyFNQ==",
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/@vscode/iconv-lite-umd": {
|
||||
@@ -99,30 +99,30 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-clipboard": {
|
||||
"version": "0.3.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.3.0-beta.144.tgz",
|
||||
"integrity": "sha512-yH7eL8Gd9SxjNCe7WIRCHhKBfo7hggjLw6CznCY39HoUdF87xfCuk3mBj6itvZLNkSx8uvB8IXfYmXasLarwEg==",
|
||||
"version": "0.3.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.3.0-beta.152.tgz",
|
||||
"integrity": "sha512-D+wFHTTNj1qzlSL1h15tgFh6JgK/SSaotkohtaKykkKFmkdGrtJq8PpINaFipRDrZXX0d9eOD+wrMfz6IG+5Yw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-base64": "^3.7.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-image": {
|
||||
"version": "0.10.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.10.0-beta.144.tgz",
|
||||
"integrity": "sha512-PvLt7MokuHItiYnHqtX8qTTWS73vQgPcpuBKVapozM4yp65Y4kwSt0nOrohKqiyCTWPyMWW0NcaStoUJLHXyvg==",
|
||||
"version": "0.10.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.10.0-beta.152.tgz",
|
||||
"integrity": "sha512-pyQ/hQr3O0gY1La+6ZXdh0tI/+6MmNo2eFPNyWzB21J02xMu6nc30+B/H9VlPSR3AXHno5U67AWra5Y4FrE+5A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-ligatures": {
|
||||
"version": "0.11.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.144.tgz",
|
||||
"integrity": "sha512-JhfkL4HtMhO1XdXugAGpYwwIzp9mE90G+J1M00FsLVmccuI53BAI16+SLUZ4w3AOonwWg/vlVxGSxKSkSY+D6A==",
|
||||
"version": "0.11.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.152.tgz",
|
||||
"integrity": "sha512-DglTaxmWHolTfryequU/7+Q4bjpDywt7UDsE3SdbC7O/9fa1qaOZMVlxKtRBtMBBzX5PXa+Ha4qAaMS2psr3UQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0",
|
||||
@@ -132,58 +132,58 @@
|
||||
"node": ">8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-progress": {
|
||||
"version": "0.3.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.3.0-beta.144.tgz",
|
||||
"integrity": "sha512-VbQu1y40UHeLKinJgEb8FwSq4Czx5h4o/TSvAVkXegdH8FAWx8YiOpNvFLEIAgboQBqgV2lgn2Azec70Ielqzg==",
|
||||
"version": "0.3.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.3.0-beta.152.tgz",
|
||||
"integrity": "sha512-H3qNwUaTNDRm51s8IzcYRinnQBSf7QDXWkcyAuDlprDJlR5BFhmGr9hpMV/KlCo2s6nhWrFjiwkd642DJ7McMg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-search": {
|
||||
"version": "0.17.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.144.tgz",
|
||||
"integrity": "sha512-+gjbQZBWqZiq8JciliUz+B6M5YPSIzT30pOi0IfC5GZHeDeDF82H02UwD2PDGweMJaWdPNtwSxrXTYr9SvtwjA==",
|
||||
"version": "0.17.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.152.tgz",
|
||||
"integrity": "sha512-0T/xDg0yh3PlS9HWOioIrNGP0OfUp4MtBJ7M2sfR+h23KKa4gl1ec7S1TsGU4gsvEMBKG1TB6jReX4vlKGYc4A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-serialize": {
|
||||
"version": "0.15.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.15.0-beta.144.tgz",
|
||||
"integrity": "sha512-OqXJf6OYOpsM4kBRHXeRVwEisWzHbnMH1ROBwmEspDnXP2NiOfoKW6E0C4Cla5qN91AYZTXcGwbyp7D+DfH9aQ==",
|
||||
"version": "0.15.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.15.0-beta.152.tgz",
|
||||
"integrity": "sha512-GnhwKg0dkpAI1gZmm9L69Xjseal5pKwXFaMUxzm+Viajcp/PdqK1pEBJX5RndToNF0Ti3xu4e6BFO7dqY/J9TA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-unicode11": {
|
||||
"version": "0.10.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.144.tgz",
|
||||
"integrity": "sha512-5NILSGbDh0t6jB5YEDzQQGAFo8zUmp7JGEBNkgCG6GHEAC8C4o8L68+RNrN+PcaAi6ucClyB4eqHQt3ZwQJ//A==",
|
||||
"version": "0.10.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.152.tgz",
|
||||
"integrity": "sha512-2HSpMjbckGAmU/56CTGuEbCZJZHxUlfNJ2uzR4akZyVVLEmavC4thHVSGT7Ei1zzpHZsAg0y4WMbcp4wzpPv3g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-webgl": {
|
||||
"version": "0.20.0-beta.143",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.20.0-beta.143.tgz",
|
||||
"integrity": "sha512-wXWwg043EsLWZvbJoxdvS+xyp4KC2f9Mhxgv8i4Kby6zYXOIKuhvh/s+VpR0dzbeHECzKO0wUJT5SKDPxFv1hA==",
|
||||
"version": "0.20.0-beta.151",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.20.0-beta.151.tgz",
|
||||
"integrity": "sha512-3ogsmZKPKc8n9Mjik4jTmNYT2Nbboe/zqcjDNG7RONO3w/tUyoKQshYCMBxxGMNLDwvh3BQ/D9/6JvdNWA1ShA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@xterm/xterm": "^6.1.0-beta.144"
|
||||
"@xterm/xterm": "^6.1.0-beta.152"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/xterm": {
|
||||
"version": "6.1.0-beta.144",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.1.0-beta.144.tgz",
|
||||
"integrity": "sha512-sV4pvPEYhJJ4crzgggmGwyoQgXoxXAK4fo1VqW2XxTpWpnhG7hdkZ7a25yEqF7Oq7GKXdr5jyNzl6QVv8Cm/pg==",
|
||||
"version": "6.1.0-beta.152",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.1.0-beta.152.tgz",
|
||||
"integrity": "sha512-XHJ5ab19V6tmcHmBE7k9IYjXSwTxUd0c7oKLa5J+ZO0+aiXE8UKh9OEDw1oyl5ZQhw9gn71cGEo4TpB58KhfoQ==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"addons/*"
|
||||
|
||||
+10
-10
@@ -5,19 +5,19 @@
|
||||
"dependencies": {
|
||||
"@microsoft/1ds-core-js": "^3.2.13",
|
||||
"@microsoft/1ds-post-js": "^3.2.13",
|
||||
"@vscode/codicons": "^0.0.45-5",
|
||||
"@vscode/codicons": "^0.0.45-6",
|
||||
"@vscode/iconv-lite-umd": "0.7.1",
|
||||
"@vscode/tree-sitter-wasm": "^0.3.0",
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.144",
|
||||
"@xterm/addon-image": "^0.10.0-beta.144",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.144",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.144",
|
||||
"@xterm/addon-search": "^0.17.0-beta.144",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.144",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.144",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.143",
|
||||
"@xterm/xterm": "^6.1.0-beta.144",
|
||||
"@xterm/addon-clipboard": "^0.3.0-beta.152",
|
||||
"@xterm/addon-image": "^0.10.0-beta.152",
|
||||
"@xterm/addon-ligatures": "^0.11.0-beta.152",
|
||||
"@xterm/addon-progress": "^0.3.0-beta.152",
|
||||
"@xterm/addon-search": "^0.17.0-beta.152",
|
||||
"@xterm/addon-serialize": "^0.15.0-beta.152",
|
||||
"@xterm/addon-unicode11": "^0.10.0-beta.152",
|
||||
"@xterm/addon-webgl": "^0.20.0-beta.151",
|
||||
"@xterm/xterm": "^6.1.0-beta.152",
|
||||
"jschardet": "3.1.4",
|
||||
"katex": "^0.16.22",
|
||||
"tas-client": "0.3.1",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { getWindowId, onDidUnregisterWindow } from './dom.js';
|
||||
import { Emitter, Event } from '../common/event.js';
|
||||
import { Disposable, markAsSingleton } from '../common/lifecycle.js';
|
||||
import { Disposable, markAsSingleton, toDisposable } from '../common/lifecycle.js';
|
||||
|
||||
type BackingStoreContext = CanvasRenderingContext2D & {
|
||||
webkitBackingStorePixelRatio?: number;
|
||||
@@ -32,6 +32,8 @@ class DevicePixelRatioMonitor extends Disposable {
|
||||
this._listener = () => this._handleChange(targetWindow, true);
|
||||
this._mediaQueryList = null;
|
||||
this._handleChange(targetWindow, false);
|
||||
|
||||
this._register(toDisposable(() => this._mediaQueryList?.removeEventListener('change', this._listener)));
|
||||
}
|
||||
|
||||
private _handleChange(targetWindow: Window, fireEvent: boolean): void {
|
||||
|
||||
@@ -436,12 +436,12 @@ export class ActionViewItem extends BaseActionViewItem {
|
||||
if (this.options.isTabList) {
|
||||
this.label.setAttribute('aria-selected', this.action.checked ? 'true' : 'false');
|
||||
} else {
|
||||
this.label.setAttribute('aria-checked', this.action.checked ? 'true' : 'false');
|
||||
this.label.setAttribute('role', 'checkbox');
|
||||
this.label.setAttribute('aria-pressed', this.action.checked ? 'true' : 'false');
|
||||
this.label.setAttribute('role', 'button');
|
||||
}
|
||||
} else {
|
||||
this.label.classList.remove('checked');
|
||||
this.label.removeAttribute(this.options.isTabList ? 'aria-selected' : 'aria-checked');
|
||||
this.label.removeAttribute(this.options.isTabList ? 'aria-selected' : 'aria-pressed');
|
||||
this.label.setAttribute('role', this.getDefaultAriaRole());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
@font-face {
|
||||
font-family: "codicon";
|
||||
font-display: block;
|
||||
src: url("./codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype");
|
||||
src: url("./codicon.ttf") format("truetype");
|
||||
}
|
||||
|
||||
.codicon[class*='codicon-'] {
|
||||
|
||||
Binary file not shown.
@@ -7,11 +7,13 @@ import { BrowserFeatures } from '../../canIUse.js';
|
||||
import * as DOM from '../../dom.js';
|
||||
import { StandardMouseEvent } from '../../mouseEvent.js';
|
||||
import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../common/lifecycle.js';
|
||||
import { AnchorAlignment, AnchorAxisAlignment, AnchorPosition, IRect, layout2d } from '../../../common/layout.js';
|
||||
import * as platform from '../../../common/platform.js';
|
||||
import { Range } from '../../../common/range.js';
|
||||
import { OmitOptional } from '../../../common/types.js';
|
||||
import './contextview.css';
|
||||
|
||||
export { AnchorAlignment, AnchorAxisAlignment, AnchorPosition } from '../../../common/layout.js';
|
||||
|
||||
export const enum ContextViewDOMPosition {
|
||||
ABSOLUTE = 1,
|
||||
FIXED,
|
||||
@@ -31,18 +33,6 @@ export function isAnchor(obj: unknown): obj is IAnchor | OmitOptional<IAnchor> {
|
||||
return !!anchor && typeof anchor.x === 'number' && typeof anchor.y === 'number';
|
||||
}
|
||||
|
||||
export const enum AnchorAlignment {
|
||||
LEFT, RIGHT
|
||||
}
|
||||
|
||||
export const enum AnchorPosition {
|
||||
BELOW, ABOVE
|
||||
}
|
||||
|
||||
export const enum AnchorAxisAlignment {
|
||||
VERTICAL, HORIZONTAL
|
||||
}
|
||||
|
||||
export interface IDelegate {
|
||||
/**
|
||||
* The anchor where to position the context view.
|
||||
@@ -73,66 +63,40 @@ export interface IContextViewProvider {
|
||||
layout(): void;
|
||||
}
|
||||
|
||||
export interface IPosition {
|
||||
top: number;
|
||||
left: number;
|
||||
}
|
||||
export function getAnchorRect(anchor: HTMLElement | StandardMouseEvent | IAnchor): IRect {
|
||||
// Get the element's position and size (to anchor the view)
|
||||
if (DOM.isHTMLElement(anchor)) {
|
||||
const elementPosition = DOM.getDomNodePagePosition(anchor);
|
||||
|
||||
export interface ISize {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
// In areas where zoom is applied to the element or its ancestors, we need to adjust the size of the element
|
||||
// e.g. The title bar has counter zoom behavior meaning it applies the inverse of zoom level.
|
||||
// Window Zoom Level: 1.5, Title Bar Zoom: 1/1.5, Size Multiplier: 1.5
|
||||
const zoom = DOM.getDomNodeZoomLevel(anchor);
|
||||
|
||||
export interface IView extends IPosition, ISize { }
|
||||
|
||||
export const enum LayoutAnchorPosition {
|
||||
Before,
|
||||
After
|
||||
}
|
||||
|
||||
export enum LayoutAnchorMode {
|
||||
AVOID,
|
||||
ALIGN
|
||||
}
|
||||
|
||||
export interface ILayoutAnchor {
|
||||
offset: number;
|
||||
size: number;
|
||||
mode?: LayoutAnchorMode; // default: AVOID
|
||||
position: LayoutAnchorPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lays out a one dimensional view next to an anchor in a viewport.
|
||||
*
|
||||
* @returns The view offset within the viewport.
|
||||
*/
|
||||
export function layout(viewportSize: number, viewSize: number, anchor: ILayoutAnchor): number {
|
||||
const layoutAfterAnchorBoundary = anchor.mode === LayoutAnchorMode.ALIGN ? anchor.offset : anchor.offset + anchor.size;
|
||||
const layoutBeforeAnchorBoundary = anchor.mode === LayoutAnchorMode.ALIGN ? anchor.offset + anchor.size : anchor.offset;
|
||||
|
||||
if (anchor.position === LayoutAnchorPosition.Before) {
|
||||
if (viewSize <= viewportSize - layoutAfterAnchorBoundary) {
|
||||
return layoutAfterAnchorBoundary; // happy case, lay it out after the anchor
|
||||
}
|
||||
|
||||
if (viewSize <= layoutBeforeAnchorBoundary) {
|
||||
return layoutBeforeAnchorBoundary - viewSize; // ok case, lay it out before the anchor
|
||||
}
|
||||
|
||||
return Math.max(viewportSize - viewSize, 0); // sad case, lay it over the anchor
|
||||
return {
|
||||
top: elementPosition.top * zoom,
|
||||
left: elementPosition.left * zoom,
|
||||
width: elementPosition.width * zoom,
|
||||
height: elementPosition.height * zoom
|
||||
};
|
||||
} else if (isAnchor(anchor)) {
|
||||
return {
|
||||
top: anchor.y,
|
||||
left: anchor.x,
|
||||
width: anchor.width || 1,
|
||||
height: anchor.height || 2
|
||||
};
|
||||
} else {
|
||||
if (viewSize <= layoutBeforeAnchorBoundary) {
|
||||
return layoutBeforeAnchorBoundary - viewSize; // happy case, lay it out before the anchor
|
||||
}
|
||||
|
||||
|
||||
if (viewSize <= viewportSize - layoutAfterAnchorBoundary && layoutBeforeAnchorBoundary < viewSize / 2) {
|
||||
return layoutAfterAnchorBoundary; // ok case, lay it out after the anchor
|
||||
}
|
||||
|
||||
|
||||
return 0; // sad case, lay it over the anchor
|
||||
return {
|
||||
top: anchor.posy,
|
||||
left: anchor.posx,
|
||||
// We are about to position the context view where the mouse
|
||||
// cursor is. To prevent the view being exactly under the mouse
|
||||
// when showing and thus potentially triggering an action within,
|
||||
// we treat the mouse location like a small sized block element.
|
||||
width: 2,
|
||||
height: 2
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,82 +234,14 @@ export class ContextView extends Disposable {
|
||||
}
|
||||
|
||||
// Get anchor
|
||||
const anchor = this.delegate!.getAnchor();
|
||||
|
||||
// Compute around
|
||||
let around: IView;
|
||||
|
||||
// Get the element's position and size (to anchor the view)
|
||||
if (DOM.isHTMLElement(anchor)) {
|
||||
const elementPosition = DOM.getDomNodePagePosition(anchor);
|
||||
|
||||
// In areas where zoom is applied to the element or its ancestors, we need to adjust the size of the element
|
||||
// e.g. The title bar has counter zoom behavior meaning it applies the inverse of zoom level.
|
||||
// Window Zoom Level: 1.5, Title Bar Zoom: 1/1.5, Size Multiplier: 1.5
|
||||
const zoom = DOM.getDomNodeZoomLevel(anchor);
|
||||
|
||||
around = {
|
||||
top: elementPosition.top * zoom,
|
||||
left: elementPosition.left * zoom,
|
||||
width: elementPosition.width * zoom,
|
||||
height: elementPosition.height * zoom
|
||||
};
|
||||
} else if (isAnchor(anchor)) {
|
||||
around = {
|
||||
top: anchor.y,
|
||||
left: anchor.x,
|
||||
width: anchor.width || 1,
|
||||
height: anchor.height || 2
|
||||
};
|
||||
} else {
|
||||
around = {
|
||||
top: anchor.posy,
|
||||
left: anchor.posx,
|
||||
// We are about to position the context view where the mouse
|
||||
// cursor is. To prevent the view being exactly under the mouse
|
||||
// when showing and thus potentially triggering an action within,
|
||||
// we treat the mouse location like a small sized block element.
|
||||
width: 2,
|
||||
height: 2
|
||||
};
|
||||
}
|
||||
|
||||
const viewSizeWidth = DOM.getTotalWidth(this.view);
|
||||
const viewSizeHeight = DOM.getTotalHeight(this.view);
|
||||
|
||||
const anchorPosition = this.delegate!.anchorPosition ?? AnchorPosition.BELOW;
|
||||
const anchorAlignment = this.delegate!.anchorAlignment ?? AnchorAlignment.LEFT;
|
||||
const anchorAxisAlignment = this.delegate!.anchorAxisAlignment ?? AnchorAxisAlignment.VERTICAL;
|
||||
|
||||
let top: number;
|
||||
let left: number;
|
||||
|
||||
const anchor = getAnchorRect(this.delegate!.getAnchor());
|
||||
const activeWindow = DOM.getActiveWindow();
|
||||
if (anchorAxisAlignment === AnchorAxisAlignment.VERTICAL) {
|
||||
const verticalAnchor: ILayoutAnchor = { offset: around.top - activeWindow.pageYOffset, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After };
|
||||
const horizontalAnchor: ILayoutAnchor = { offset: around.left, size: around.width, position: anchorAlignment === AnchorAlignment.LEFT ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, mode: LayoutAnchorMode.ALIGN };
|
||||
|
||||
top = layout(activeWindow.innerHeight, viewSizeHeight, verticalAnchor) + activeWindow.pageYOffset;
|
||||
|
||||
// if view intersects vertically with anchor, we must avoid the anchor
|
||||
if (Range.intersects({ start: top, end: top + viewSizeHeight }, { start: verticalAnchor.offset, end: verticalAnchor.offset + verticalAnchor.size })) {
|
||||
horizontalAnchor.mode = LayoutAnchorMode.AVOID;
|
||||
}
|
||||
|
||||
left = layout(activeWindow.innerWidth, viewSizeWidth, horizontalAnchor);
|
||||
} else {
|
||||
const horizontalAnchor: ILayoutAnchor = { offset: around.left, size: around.width, position: anchorAlignment === AnchorAlignment.LEFT ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After };
|
||||
const verticalAnchor: ILayoutAnchor = { offset: around.top, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, mode: LayoutAnchorMode.ALIGN };
|
||||
|
||||
left = layout(activeWindow.innerWidth, viewSizeWidth, horizontalAnchor);
|
||||
|
||||
// if view intersects horizontally with anchor, we must avoid the anchor
|
||||
if (Range.intersects({ start: left, end: left + viewSizeWidth }, { start: horizontalAnchor.offset, end: horizontalAnchor.offset + horizontalAnchor.size })) {
|
||||
verticalAnchor.mode = LayoutAnchorMode.AVOID;
|
||||
}
|
||||
|
||||
top = layout(activeWindow.innerHeight, viewSizeHeight, verticalAnchor) + activeWindow.pageYOffset;
|
||||
}
|
||||
const viewport = { top: activeWindow.pageYOffset, left: activeWindow.pageXOffset, width: activeWindow.innerWidth, height: activeWindow.innerHeight };
|
||||
const view = { width: DOM.getTotalWidth(this.view), height: DOM.getTotalHeight(this.view) };
|
||||
const anchorPosition = this.delegate!.anchorPosition;
|
||||
const anchorAlignment = this.delegate!.anchorAlignment;
|
||||
const anchorAxisAlignment = this.delegate!.anchorAxisAlignment;
|
||||
const { top, left } = layout2d(viewport, view, anchor, { anchorAlignment, anchorPosition, anchorAxisAlignment });
|
||||
|
||||
this.view.classList.remove('top', 'bottom', 'left', 'right');
|
||||
this.view.classList.add(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top');
|
||||
|
||||
@@ -11,7 +11,6 @@ import { StandardKeyboardEvent } from '../../keyboardEvent.js';
|
||||
import { StandardMouseEvent } from '../../mouseEvent.js';
|
||||
import { ActionBar, ActionsOrientation, IActionViewItemProvider } from '../actionbar/actionbar.js';
|
||||
import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions } from '../actionbar/actionViewItems.js';
|
||||
import { AnchorAlignment, layout, LayoutAnchorPosition } from '../contextview/contextview.js';
|
||||
import { DomScrollableElement } from '../scrollbar/scrollableElement.js';
|
||||
import { EmptySubmenuAction, IAction, IActionRunner, Separator, SubmenuAction } from '../../../common/actions.js';
|
||||
import { RunOnceScheduler } from '../../../common/async.js';
|
||||
@@ -26,6 +25,7 @@ import { DisposableStore } from '../../../common/lifecycle.js';
|
||||
import { isLinux, isMacintosh } from '../../../common/platform.js';
|
||||
import { ScrollbarVisibility, ScrollEvent } from '../../../common/scrollable.js';
|
||||
import * as strings from '../../../common/strings.js';
|
||||
import { AnchorAlignment, layout, LayoutAnchorPosition } from '../../../common/layout.js';
|
||||
|
||||
export const MENU_MNEMONIC_REGEX = /\(&([^\s&])\)|(^|[^&])&([^\s&])/;
|
||||
export const MENU_ESCAPED_MNEMONIC_REGEX = /(&)?(&)([^\s&])/g;
|
||||
@@ -859,7 +859,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
|
||||
const ret = { top: 0, left: 0 };
|
||||
|
||||
// Start with horizontal
|
||||
ret.left = layout(windowDimensions.width, submenu.width, { position: expandDirection.horizontal === HorizontalDirection.Right ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, offset: entry.left, size: entry.width });
|
||||
ret.left = layout(windowDimensions.width, submenu.width, { position: expandDirection.horizontal === HorizontalDirection.Right ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, offset: entry.left, size: entry.width }).position;
|
||||
|
||||
// We don't have enough room to layout the menu fully, so we are overlapping the menu
|
||||
if (ret.left >= entry.left && ret.left < entry.left + entry.width) {
|
||||
@@ -872,7 +872,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
|
||||
}
|
||||
|
||||
// Now that we have a horizontal position, try layout vertically
|
||||
ret.top = layout(windowDimensions.height, submenu.height, { position: LayoutAnchorPosition.Before, offset: entry.top, size: 0 });
|
||||
ret.top = layout(windowDimensions.height, submenu.height, { position: LayoutAnchorPosition.Before, offset: entry.top, size: 0 }).position;
|
||||
|
||||
// We didn't have enough room below, but we did above, so we shift down to align the menu
|
||||
if (ret.top + submenu.height === entry.top && ret.top + entry.height + submenu.height <= windowDimensions.height) {
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Range } from './range.js';
|
||||
|
||||
export interface IAnchor {
|
||||
x: number;
|
||||
y: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export const enum AnchorAlignment {
|
||||
LEFT, RIGHT
|
||||
}
|
||||
|
||||
export const enum AnchorPosition {
|
||||
BELOW, ABOVE
|
||||
}
|
||||
|
||||
export const enum AnchorAxisAlignment {
|
||||
VERTICAL, HORIZONTAL
|
||||
}
|
||||
|
||||
interface IPosition {
|
||||
readonly top: number;
|
||||
readonly left: number;
|
||||
}
|
||||
|
||||
interface ISize {
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
}
|
||||
|
||||
export interface IRect extends IPosition, ISize { }
|
||||
|
||||
export const enum LayoutAnchorPosition {
|
||||
Before,
|
||||
After
|
||||
}
|
||||
|
||||
export enum LayoutAnchorMode {
|
||||
AVOID,
|
||||
ALIGN
|
||||
}
|
||||
|
||||
export interface ILayoutAnchor {
|
||||
offset: number;
|
||||
size: number;
|
||||
mode?: LayoutAnchorMode; // default: AVOID
|
||||
position: LayoutAnchorPosition;
|
||||
}
|
||||
|
||||
export interface ILayoutResult {
|
||||
position: number;
|
||||
result: 'ok' | 'flipped' | 'overlap';
|
||||
}
|
||||
|
||||
/**
|
||||
* Lays out a one dimensional view next to an anchor in a viewport.
|
||||
*
|
||||
* @returns The view offset within the viewport.
|
||||
*/
|
||||
export function layout(viewportSize: number, viewSize: number, anchor: ILayoutAnchor): ILayoutResult {
|
||||
const layoutAfterAnchorBoundary = anchor.mode === LayoutAnchorMode.ALIGN ? anchor.offset : anchor.offset + anchor.size;
|
||||
const layoutBeforeAnchorBoundary = anchor.mode === LayoutAnchorMode.ALIGN ? anchor.offset + anchor.size : anchor.offset;
|
||||
|
||||
if (anchor.position === LayoutAnchorPosition.Before) {
|
||||
if (viewSize <= viewportSize - layoutAfterAnchorBoundary) {
|
||||
return { position: layoutAfterAnchorBoundary, result: 'ok' }; // happy case, lay it out after the anchor
|
||||
}
|
||||
|
||||
if (viewSize <= layoutBeforeAnchorBoundary) {
|
||||
return { position: layoutBeforeAnchorBoundary - viewSize, result: 'flipped' }; // ok case, lay it out before the anchor
|
||||
}
|
||||
|
||||
return { position: Math.max(viewportSize - viewSize, 0), result: 'overlap' }; // sad case, lay it over the anchor
|
||||
} else {
|
||||
if (viewSize <= layoutBeforeAnchorBoundary) {
|
||||
return { position: layoutBeforeAnchorBoundary - viewSize, result: 'ok' }; // happy case, lay it out before the anchor
|
||||
}
|
||||
|
||||
if (viewSize <= viewportSize - layoutAfterAnchorBoundary && layoutBeforeAnchorBoundary < viewSize / 2) {
|
||||
return { position: layoutAfterAnchorBoundary, result: 'flipped' }; // ok case, lay it out after the anchor
|
||||
}
|
||||
|
||||
return { position: 0, result: 'overlap' }; // sad case, lay it over the anchor
|
||||
}
|
||||
}
|
||||
|
||||
interface ILayout2DOptions {
|
||||
readonly anchorAlignment?: AnchorAlignment; // default: left
|
||||
readonly anchorPosition?: AnchorPosition; // default: above
|
||||
readonly anchorAxisAlignment?: AnchorAxisAlignment; // default: vertical
|
||||
}
|
||||
|
||||
export interface ILayout2DResult {
|
||||
top: number;
|
||||
left: number;
|
||||
bottom: number;
|
||||
right: number;
|
||||
anchorAlignment: AnchorAlignment;
|
||||
anchorPosition: AnchorPosition;
|
||||
}
|
||||
|
||||
export function layout2d(viewport: IRect, view: ISize, anchor: IRect, options?: ILayout2DOptions): ILayout2DResult {
|
||||
let anchorAlignment = options?.anchorAlignment ?? AnchorAlignment.LEFT;
|
||||
let anchorPosition = options?.anchorPosition ?? AnchorPosition.BELOW;
|
||||
const anchorAxisAlignment = options?.anchorAxisAlignment ?? AnchorAxisAlignment.VERTICAL;
|
||||
|
||||
let top: number;
|
||||
let left: number;
|
||||
|
||||
if (anchorAxisAlignment === AnchorAxisAlignment.VERTICAL) {
|
||||
const verticalAnchor: ILayoutAnchor = { offset: anchor.top - viewport.top, size: anchor.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After };
|
||||
const horizontalAnchor: ILayoutAnchor = { offset: anchor.left, size: anchor.width, position: anchorAlignment === AnchorAlignment.LEFT ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, mode: LayoutAnchorMode.ALIGN };
|
||||
|
||||
const verticalLayoutResult = layout(viewport.height, view.height, verticalAnchor);
|
||||
top = verticalLayoutResult.position + viewport.top;
|
||||
|
||||
if (verticalLayoutResult.result === 'flipped') {
|
||||
anchorPosition = anchorPosition === AnchorPosition.BELOW ? AnchorPosition.ABOVE : AnchorPosition.BELOW;
|
||||
}
|
||||
|
||||
// if view intersects vertically with anchor, we must avoid the anchor
|
||||
if (Range.intersects({ start: top, end: top + view.height }, { start: verticalAnchor.offset, end: verticalAnchor.offset + verticalAnchor.size })) {
|
||||
horizontalAnchor.mode = LayoutAnchorMode.AVOID;
|
||||
}
|
||||
|
||||
const horizontalLayoutResult = layout(viewport.width, view.width, horizontalAnchor);
|
||||
left = horizontalLayoutResult.position;
|
||||
|
||||
if (horizontalLayoutResult.result === 'flipped') {
|
||||
anchorAlignment = anchorAlignment === AnchorAlignment.LEFT ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT;
|
||||
}
|
||||
} else {
|
||||
const horizontalAnchor: ILayoutAnchor = { offset: anchor.left, size: anchor.width, position: anchorAlignment === AnchorAlignment.LEFT ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After };
|
||||
const verticalAnchor: ILayoutAnchor = { offset: anchor.top, size: anchor.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, mode: LayoutAnchorMode.ALIGN };
|
||||
|
||||
const horizontalLayoutResult = layout(viewport.width, view.width, horizontalAnchor);
|
||||
left = horizontalLayoutResult.position;
|
||||
|
||||
if (horizontalLayoutResult.result === 'flipped') {
|
||||
anchorAlignment = anchorAlignment === AnchorAlignment.LEFT ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT;
|
||||
}
|
||||
|
||||
// if view intersects horizontally with anchor, we must avoid the anchor
|
||||
if (Range.intersects({ start: left, end: left + view.width }, { start: horizontalAnchor.offset, end: horizontalAnchor.offset + horizontalAnchor.size })) {
|
||||
verticalAnchor.mode = LayoutAnchorMode.AVOID;
|
||||
}
|
||||
|
||||
const verticalLayoutResult = layout(viewport.height, view.height, verticalAnchor);
|
||||
top = verticalLayoutResult.position + viewport.top;
|
||||
|
||||
if (verticalLayoutResult.result === 'flipped') {
|
||||
anchorPosition = anchorPosition === AnchorPosition.BELOW ? AnchorPosition.ABOVE : AnchorPosition.BELOW;
|
||||
}
|
||||
}
|
||||
|
||||
const right = viewport.width - (left + view.width);
|
||||
const bottom = viewport.height - (top + view.height);
|
||||
|
||||
return { top, left, bottom, right, anchorAlignment, anchorPosition };
|
||||
}
|
||||
@@ -22,7 +22,11 @@ function _validateUri(ret: URI, _strict?: boolean): void {
|
||||
// scheme, https://tools.ietf.org/html/rfc3986#section-3.1
|
||||
// ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
if (ret.scheme && !_schemePattern.test(ret.scheme)) {
|
||||
throw new Error('[UriError]: Scheme contains illegal characters.');
|
||||
const matches = [...ret.scheme.matchAll(/[^\w\d+.-]/gu)];
|
||||
const detail = matches.length > 0
|
||||
? ` Found '${matches[0][0]}' at index ${matches[0].index} (${matches.length} total)`
|
||||
: '';
|
||||
throw new Error(`[UriError]: Scheme contains illegal characters.${detail} (len:${ret.scheme.length})`);
|
||||
}
|
||||
|
||||
// path, http://tools.ietf.org/html/rfc3986#section-3.3
|
||||
|
||||
+15
-16
@@ -4,27 +4,26 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import assert from 'assert';
|
||||
import { layout, LayoutAnchorPosition } from '../../../../browser/ui/contextview/contextview.js';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../common/utils.js';
|
||||
import { layout, LayoutAnchorPosition } from '../../common/layout.js';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js';
|
||||
|
||||
suite('Contextview', function () {
|
||||
suite('Layout', function () {
|
||||
|
||||
test('layout', () => {
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.Before }), 0);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.Before }), 50);
|
||||
assert.strictEqual(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.Before }), 180);
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.Before }).position, 0);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.Before }).position, 50);
|
||||
assert.strictEqual(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.Before }).position, 180);
|
||||
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.After }), 0);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.After }), 30);
|
||||
assert.strictEqual(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.After }), 180);
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.After }).position, 0);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.After }).position, 30);
|
||||
assert.strictEqual(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.After }).position, 180);
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.Before }).position, 50);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.Before }).position, 100);
|
||||
assert.strictEqual(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.Before }).position, 130);
|
||||
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.Before }), 50);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.Before }), 100);
|
||||
assert.strictEqual(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.Before }), 130);
|
||||
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.After }), 50);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.After }), 30);
|
||||
assert.strictEqual(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.After }), 130);
|
||||
assert.strictEqual(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.After }).position, 50);
|
||||
assert.strictEqual(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.After }).position, 30);
|
||||
assert.strictEqual(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.After }).position, 130);
|
||||
});
|
||||
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
@@ -16,6 +16,7 @@ import { EditorOption } from '../../common/config/editorOptions.js';
|
||||
import * as platform from '../../../base/common/platform.js';
|
||||
import { StandardTokenType } from '../../common/encodedTokenAttributes.js';
|
||||
import { ITextModel } from '../../common/model.js';
|
||||
import { containsRTL } from '../../../base/common/strings.js';
|
||||
|
||||
export interface IMouseDispatchData {
|
||||
position: Position;
|
||||
@@ -181,15 +182,30 @@ export class ViewController {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Get 1-based boundaries of the string content (excluding quotes).
|
||||
const start = lineTokens.getStartOffset(index) + 2;
|
||||
const end = lineTokens.getEndOffset(index);
|
||||
|
||||
if (column !== start && column !== end) {
|
||||
// Verify the click is after starting or before closing quote.
|
||||
const tokenStart = lineTokens.getStartOffset(index);
|
||||
const tokenEnd = lineTokens.getEndOffset(index);
|
||||
if (column !== tokenStart + 2 && column !== tokenEnd) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return new Selection(lineNumber, start, lineNumber, end);
|
||||
// Verify the token looks like a complete quoted string (quote ... quote).
|
||||
const lineContent = model.getLineContent(lineNumber);
|
||||
const firstChar = lineContent.charAt(tokenStart);
|
||||
if (firstChar !== '"' && firstChar !== '\'' && firstChar !== '`') {
|
||||
return undefined;
|
||||
}
|
||||
if (lineContent.charAt(tokenEnd - 1) !== firstChar) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Skip if string contains RTL characters.
|
||||
const content = lineContent.substring(tokenStart + 1, tokenEnd - 1);
|
||||
if (containsRTL(content)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return new Selection(lineNumber, tokenStart + 2, lineNumber, tokenEnd);
|
||||
}
|
||||
|
||||
public dispatchMouse(data: IMouseDispatchData): void {
|
||||
|
||||
@@ -60,6 +60,7 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I
|
||||
|
||||
public dispose(): void {
|
||||
this.diffAlgorithmOnDidChangeSubscription?.dispose();
|
||||
this.onDidChangeEventEmitter.dispose();
|
||||
}
|
||||
|
||||
async computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions, cancellationToken: CancellationToken): Promise<IDocumentDiff> {
|
||||
|
||||
@@ -496,7 +496,7 @@ export interface IEditorOptions {
|
||||
* Enable quick suggestions (shadow suggestions)
|
||||
* Defaults to true.
|
||||
*/
|
||||
quickSuggestions?: boolean | IQuickSuggestionsOptions;
|
||||
quickSuggestions?: boolean | QuickSuggestionsValue | IQuickSuggestionsOptions;
|
||||
/**
|
||||
* Quick suggestions show delay (in ms)
|
||||
* Defaults to 10 (ms)
|
||||
@@ -3712,7 +3712,7 @@ class PlaceholderOption extends BaseEditorOption<EditorOption.placeholder, strin
|
||||
|
||||
//#region quickSuggestions
|
||||
|
||||
export type QuickSuggestionsValue = 'on' | 'inline' | 'off';
|
||||
export type QuickSuggestionsValue = 'on' | 'inline' | 'off' | 'offWhenInlineCompletions';
|
||||
|
||||
/**
|
||||
* Configuration options for quick suggestions
|
||||
@@ -3729,7 +3729,7 @@ export interface InternalQuickSuggestionsOptions {
|
||||
readonly strings: QuickSuggestionsValue;
|
||||
}
|
||||
|
||||
class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggestions, boolean | IQuickSuggestionsOptions, InternalQuickSuggestionsOptions> {
|
||||
class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggestions, boolean | QuickSuggestionsValue | IQuickSuggestionsOptions, InternalQuickSuggestionsOptions> {
|
||||
|
||||
public override readonly defaultValue: InternalQuickSuggestionsOptions;
|
||||
|
||||
@@ -3743,32 +3743,45 @@ class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggesti
|
||||
{ type: 'boolean' },
|
||||
{
|
||||
type: 'string',
|
||||
enum: ['on', 'inline', 'off'],
|
||||
enumDescriptions: [nls.localize('on', "Quick suggestions show inside the suggest widget"), nls.localize('inline', "Quick suggestions show as ghost text"), nls.localize('off', "Quick suggestions are disabled")]
|
||||
enum: ['on', 'inline', 'off', 'offWhenInlineCompletions'],
|
||||
enumDescriptions: [nls.localize('on', "Quick suggestions show inside the suggest widget"), nls.localize('inline', "Quick suggestions show as ghost text"), nls.localize('off', "Quick suggestions are disabled"), nls.localize('offWhenInlineCompletions', "Quick suggestions are disabled when an inline completion provider is available")]
|
||||
}
|
||||
];
|
||||
super(EditorOption.quickSuggestions, 'quickSuggestions', defaults, {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
strings: {
|
||||
anyOf: types,
|
||||
default: defaults.strings,
|
||||
description: nls.localize('quickSuggestions.strings', "Enable quick suggestions inside strings.")
|
||||
anyOf: [
|
||||
{ type: 'boolean' },
|
||||
{
|
||||
type: 'string',
|
||||
enum: ['on', 'inline', 'off', 'offWhenInlineCompletions'],
|
||||
enumDescriptions: [nls.localize('quickSuggestions.topLevel.on', "Quick suggestions are enabled for all token types"), nls.localize('quickSuggestions.topLevel.inline', "Quick suggestions show as ghost text for all token types"), nls.localize('quickSuggestions.topLevel.off', "Quick suggestions are disabled for all token types"), nls.localize('quickSuggestions.topLevel.offWhenInlineCompletions', "Quick suggestions are disabled for all token types when an inline completion provider is available")]
|
||||
},
|
||||
comments: {
|
||||
anyOf: types,
|
||||
default: defaults.comments,
|
||||
description: nls.localize('quickSuggestions.comments', "Enable quick suggestions inside comments.")
|
||||
},
|
||||
other: {
|
||||
anyOf: types,
|
||||
default: defaults.other,
|
||||
description: nls.localize('quickSuggestions.other', "Enable quick suggestions outside of strings and comments.")
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
strings: {
|
||||
anyOf: types,
|
||||
default: defaults.strings,
|
||||
description: nls.localize('quickSuggestions.strings', "Enable quick suggestions inside strings.")
|
||||
},
|
||||
comments: {
|
||||
anyOf: types,
|
||||
default: defaults.comments,
|
||||
description: nls.localize('quickSuggestions.comments', "Enable quick suggestions inside comments.")
|
||||
},
|
||||
other: {
|
||||
anyOf: types,
|
||||
default: defaults.other,
|
||||
description: nls.localize('quickSuggestions.other', "Enable quick suggestions outside of strings and comments.")
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
default: defaults,
|
||||
markdownDescription: nls.localize('quickSuggestions', "Controls whether suggestions should automatically show up while typing. This can be controlled for typing in comments, strings, and other code. Quick suggestion can be configured to show as ghost text or with the suggest widget. Also be aware of the {0}-setting which controls if suggestions are triggered by special characters.", '`#editor.suggestOnTriggerCharacters#`')
|
||||
markdownDescription: nls.localize('quickSuggestions', "Controls whether suggestions should automatically show up while typing. This can be controlled for typing in comments, strings, and other code. Quick suggestion can be configured to show as ghost text or with the suggest widget. Also be aware of the {0}-setting which controls if suggestions are triggered by special characters.", '`#editor.suggestOnTriggerCharacters#`'),
|
||||
experiment: {
|
||||
mode: 'auto'
|
||||
}
|
||||
});
|
||||
this.defaultValue = defaults;
|
||||
}
|
||||
@@ -3779,13 +3792,19 @@ class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggesti
|
||||
const value = input ? 'on' : 'off';
|
||||
return { comments: value, strings: value, other: value };
|
||||
}
|
||||
if (typeof input === 'string') {
|
||||
// string shorthand -> apply same value to all token types
|
||||
const allowedValues: QuickSuggestionsValue[] = ['on', 'inline', 'off', 'offWhenInlineCompletions'];
|
||||
const validated = stringSet<QuickSuggestionsValue>(input as QuickSuggestionsValue, this.defaultValue.other, allowedValues);
|
||||
return { comments: validated, strings: validated, other: validated };
|
||||
}
|
||||
if (!input || typeof input !== 'object') {
|
||||
// invalid object
|
||||
// invalid input
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
const { other, comments, strings } = (<IQuickSuggestionsOptions>input);
|
||||
const allowedValues: QuickSuggestionsValue[] = ['on', 'inline', 'off'];
|
||||
const allowedValues: QuickSuggestionsValue[] = ['on', 'inline', 'off', 'offWhenInlineCompletions'];
|
||||
let validatedOther: QuickSuggestionsValue;
|
||||
let validatedComments: QuickSuggestionsValue;
|
||||
let validatedStrings: QuickSuggestionsValue;
|
||||
|
||||
@@ -1182,6 +1182,13 @@ export interface ITextModel {
|
||||
*/
|
||||
getCustomLineHeightsDecorations(ownerId?: number): IModelDecoration[];
|
||||
|
||||
/**
|
||||
* Gets all the decorations that contain custom line heights.
|
||||
* @param range The range to search in
|
||||
* @param ownerId If set, it will ignore decorations belonging to other owners.
|
||||
*/
|
||||
getCustomLineHeightsDecorationsInRange(range: Range, ownerId?: number): IModelDecoration[];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,7 @@ import { LineTokens } from '../../tokens/lineTokens.js';
|
||||
export class BracketPairsTextModelPart extends Disposable implements IBracketPairsTextModelPart {
|
||||
private readonly bracketPairsTree = this._register(new MutableDisposable<IReference<BracketPairsTree>>());
|
||||
|
||||
private readonly onDidChangeEmitter = new Emitter<void>();
|
||||
private readonly onDidChangeEmitter = this._register(new Emitter<void>());
|
||||
public readonly onDidChange = this.onDidChangeEmitter.event;
|
||||
|
||||
private get canBuildAST() {
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ export class BracketPairsTree extends Disposable {
|
||||
private readonly getLanguageConfiguration: (languageId: string) => ResolvedLanguageConfiguration
|
||||
) {
|
||||
super();
|
||||
this.didChangeEmitter = new Emitter<void>();
|
||||
this.didChangeEmitter = this._register(new Emitter<void>());
|
||||
this.denseKeyProvider = new DenseKeyProvider<string>();
|
||||
this.brackets = new LanguageAgnosticBracketTokens(this.denseKeyProvider, this.getLanguageConfiguration);
|
||||
this.onDidChange = this.didChangeEmitter.event;
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ export class ColorizedBracketPairsDecorationProvider extends Disposable implemen
|
||||
private colorizationOptions: BracketPairColorizationOptions;
|
||||
private readonly colorProvider = new ColorProvider();
|
||||
|
||||
private readonly onDidChangeEmitter = new Emitter<void>();
|
||||
private readonly onDidChangeEmitter = this._register(new Emitter<void>());
|
||||
public readonly onDidChange = this.onDidChangeEmitter.event;
|
||||
|
||||
constructor(private readonly textModel: TextModel) {
|
||||
|
||||
@@ -299,7 +299,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
|
||||
private readonly _guidesTextModelPart: GuidesTextModelPart;
|
||||
public get guides(): IGuidesTextModelPart { return this._guidesTextModelPart; }
|
||||
|
||||
private readonly _attachedViews = new AttachedViews();
|
||||
private readonly _attachedViews = this._register(new AttachedViews());
|
||||
private readonly _viewModels = new Set<IViewModel>();
|
||||
|
||||
constructor(
|
||||
@@ -1563,6 +1563,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
|
||||
rawContentChanges.push(
|
||||
new ModelRawLineChanged(
|
||||
editLineNumber,
|
||||
currentEditLineNumber,
|
||||
this.getLineContent(currentEditLineNumber),
|
||||
decorationsInCurrentLine
|
||||
));
|
||||
@@ -1593,7 +1594,8 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
|
||||
rawContentChanges.push(
|
||||
new ModelRawLinesInserted(
|
||||
spliceLineNumber + 1,
|
||||
startLineNumber + insertingLinesCnt,
|
||||
fromLineNumber,
|
||||
cnt,
|
||||
newLines,
|
||||
injectedTexts
|
||||
)
|
||||
@@ -1653,7 +1655,7 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
|
||||
|
||||
if (affectedInjectedTextLines && affectedInjectedTextLines.size > 0) {
|
||||
const affectedLines = Array.from(affectedInjectedTextLines);
|
||||
const lineChangeEvents = affectedLines.map(lineNumber => new ModelRawLineChanged(lineNumber, this.getLineContent(lineNumber), this._getInjectedTextInLine(lineNumber)));
|
||||
const lineChangeEvents = affectedLines.map(lineNumber => new ModelRawLineChanged(lineNumber, lineNumber, this.getLineContent(lineNumber), this._getInjectedTextInLine(lineNumber)));
|
||||
this._onDidChangeContentOrInjectedText(new ModelInjectedTextChangedEvent(lineChangeEvents));
|
||||
}
|
||||
this._fireOnDidChangeLineHeight(affectedLineHeights);
|
||||
@@ -1865,6 +1867,12 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
|
||||
return decs;
|
||||
}
|
||||
|
||||
public getCustomLineHeightsDecorationsInRange(range: Range, ownerId: number = 0): model.IModelDecoration[] {
|
||||
const decs = this._decorationsTree.getCustomLineHeightsInInterval(this, this.getOffsetAt(range.getStartPosition()), this.getOffsetAt(range.getEndPosition()), ownerId);
|
||||
pushMany(decs, this._fontTokenDecorationsProvider.getDecorationsInRange(range, ownerId));
|
||||
return decs;
|
||||
}
|
||||
|
||||
private _getInjectedTextInLine(lineNumber: number): LineInjectedText[] {
|
||||
const startOffset = this._buffer.getOffsetAt(lineNumber, 1);
|
||||
const endOffset = startOffset + this._buffer.getLineLength(lineNumber);
|
||||
@@ -2249,6 +2257,12 @@ class DecorationsTrees {
|
||||
return this._ensureNodesHaveRanges(host, result).filter((i) => typeof i.options.lineHeight === 'number');
|
||||
}
|
||||
|
||||
public getCustomLineHeightsInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number): model.IModelDecoration[] {
|
||||
const versionId = host.getVersionId();
|
||||
const result = this._intervalSearch(start, end, filterOwnerId, false, false, versionId, false);
|
||||
return this._ensureNodesHaveRanges(host, result).filter((i) => typeof i.options.lineHeight === 'number');
|
||||
}
|
||||
|
||||
public getAll(host: IDecorationsTreesHost, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, overviewRulerOnly: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] {
|
||||
const versionId = host.getVersionId();
|
||||
const result = this._search(filterOwnerId, filterOutValidation, filterFontDecorations, overviewRulerOnly, versionId, onlyMarginDecorations);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { equals } from '../../../../base/common/arrays.js';
|
||||
import { RunOnceScheduler } from '../../../../base/common/async.js';
|
||||
import { Emitter, Event } from '../../../../base/common/event.js';
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js';
|
||||
import { LineRange } from '../../core/ranges/lineRange.js';
|
||||
import { StandardTokenType } from '../../encodedTokenAttributes.js';
|
||||
import { ILanguageIdCodec } from '../../languages.js';
|
||||
@@ -21,7 +21,7 @@ import { equalsIfDefinedC, thisEqualsC, arrayEqualsC } from '../../../../base/co
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class AttachedViews {
|
||||
export class AttachedViews implements IDisposable {
|
||||
private readonly _onDidChangeVisibleRanges = new Emitter<{ view: IAttachedView; state: AttachedViewState | undefined }>();
|
||||
public readonly onDidChangeVisibleRanges = this._onDidChangeVisibleRanges.event;
|
||||
|
||||
@@ -57,6 +57,10 @@ export class AttachedViews {
|
||||
this._onDidChangeVisibleRanges.fire({ view, state: undefined });
|
||||
this._viewsChanged.trigger(undefined);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._onDidChangeVisibleRanges.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,10 +25,10 @@ export class TokenizationFontDecorationProvider extends Disposable implements De
|
||||
|
||||
private static DECORATION_COUNT = 0;
|
||||
|
||||
private readonly _onDidChangeLineHeight = new Emitter<Set<LineHeightChangingDecoration>>();
|
||||
private readonly _onDidChangeLineHeight = this._register(new Emitter<Set<LineHeightChangingDecoration>>());
|
||||
public readonly onDidChangeLineHeight = this._onDidChangeLineHeight.event;
|
||||
|
||||
private readonly _onDidChangeFont = new Emitter<Set<LineFontChangingDecoration>>();
|
||||
private readonly _onDidChangeFont = this._register(new Emitter<Set<LineFontChangingDecoration>>());
|
||||
public readonly onDidChangeFont = this._onDidChangeFont.event;
|
||||
|
||||
private _fontAnnotatedString: IAnnotatedString<IFontTokenAnnotation> = new AnnotatedString<IFontTokenAnnotation>();
|
||||
|
||||
@@ -308,9 +308,13 @@ export class LineInjectedText {
|
||||
export class ModelRawLineChanged {
|
||||
public readonly changeType = RawContentChangedType.LineChanged;
|
||||
/**
|
||||
* The line that has changed.
|
||||
* The line number that has changed (before the change was applied).
|
||||
*/
|
||||
public readonly lineNumber: number;
|
||||
/**
|
||||
* The new line number the old one is mapped to (after the change was applied).
|
||||
*/
|
||||
public readonly lineNumberPostEdit: number;
|
||||
/**
|
||||
* The new value of the line.
|
||||
*/
|
||||
@@ -320,8 +324,9 @@ export class ModelRawLineChanged {
|
||||
*/
|
||||
public readonly injectedText: LineInjectedText[] | null;
|
||||
|
||||
constructor(lineNumber: number, detail: string, injectedText: LineInjectedText[] | null) {
|
||||
constructor(lineNumber: number, lineNumberPostEdit: number, detail: string, injectedText: LineInjectedText[] | null) {
|
||||
this.lineNumber = lineNumber;
|
||||
this.lineNumberPostEdit = lineNumberPostEdit;
|
||||
this.detail = detail;
|
||||
this.injectedText = injectedText;
|
||||
}
|
||||
@@ -409,10 +414,26 @@ export class ModelRawLinesInserted {
|
||||
* Before what line did the insertion begin
|
||||
*/
|
||||
public readonly fromLineNumber: number;
|
||||
/**
|
||||
* The actual start line number in the updated buffer where the newly inserted content can be found.
|
||||
*/
|
||||
public readonly fromLineNumberPostEdit: number;
|
||||
/**
|
||||
* The count of inserted lines.
|
||||
*/
|
||||
public readonly count: number;
|
||||
/**
|
||||
* `toLineNumber` - `fromLineNumber` + 1 denotes the number of lines that were inserted
|
||||
*/
|
||||
public readonly toLineNumber: number;
|
||||
public get toLineNumber(): number {
|
||||
return this.fromLineNumber + this.count - 1;
|
||||
}
|
||||
/**
|
||||
* The actual end line number of the insertion in the updated buffer.
|
||||
*/
|
||||
public get toLineNumberPostEdit(): number {
|
||||
return this.fromLineNumberPostEdit + this.count - 1;
|
||||
}
|
||||
/**
|
||||
* The text that was inserted
|
||||
*/
|
||||
@@ -422,10 +443,11 @@ export class ModelRawLinesInserted {
|
||||
*/
|
||||
public readonly injectedTexts: (LineInjectedText[] | null)[];
|
||||
|
||||
constructor(fromLineNumber: number, toLineNumber: number, detail: string[], injectedTexts: (LineInjectedText[] | null)[]) {
|
||||
constructor(fromLineNumber: number, fromLineNumberPostEdit: number, count: number, detail: string[], injectedTexts: (LineInjectedText[] | null)[]) {
|
||||
this.injectedTexts = injectedTexts;
|
||||
this.fromLineNumber = fromLineNumber;
|
||||
this.toLineNumber = toLineNumber;
|
||||
this.fromLineNumberPostEdit = fromLineNumberPostEdit;
|
||||
this.count = count;
|
||||
this.detail = detail;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
import { binarySearch2 } from '../../../base/common/arrays.js';
|
||||
import { intersection } from '../../../base/common/collections.js';
|
||||
import { IEditorConfiguration } from '../config/editorConfiguration.js';
|
||||
import { EditorOption } from '../config/editorOptions.js';
|
||||
import { ICoordinatesConverter } from '../coordinatesConverter.js';
|
||||
import { IModelDecoration } from '../model.js';
|
||||
|
||||
export class CustomLine {
|
||||
|
||||
@@ -62,7 +66,7 @@ export class LineHeightsManager {
|
||||
private _defaultLineHeight: number;
|
||||
private _hasPending: boolean = false;
|
||||
|
||||
constructor(defaultLineHeight: number, customLineHeightData: ICustomLineHeightData[]) {
|
||||
constructor(defaultLineHeight: number, customLineHeightData: CustomLineHeightData[]) {
|
||||
this._defaultLineHeight = defaultLineHeight;
|
||||
if (customLineHeightData.length > 0) {
|
||||
for (const data of customLineHeightData) {
|
||||
@@ -227,7 +231,7 @@ export class LineHeightsManager {
|
||||
}
|
||||
}
|
||||
|
||||
public onLinesInserted(fromLineNumber: number, toLineNumber: number): void {
|
||||
public onLinesInserted(fromLineNumber: number, toLineNumber: number, lineHeightsAdded: CustomLineHeightData[]): void {
|
||||
const insertCount = toLineNumber - fromLineNumber + 1;
|
||||
const candidateStartIndexOfInsertion = this._binarySearchOverOrderedCustomLinesArray(fromLineNumber);
|
||||
let startIndexOfInsertion: number;
|
||||
@@ -243,7 +247,22 @@ export class LineHeightsManager {
|
||||
} else {
|
||||
startIndexOfInsertion = -(candidateStartIndexOfInsertion + 1);
|
||||
}
|
||||
const toReAdd: ICustomLineHeightData[] = [];
|
||||
const maxLineHeightPerLine = new Map<number, number>();
|
||||
for (const lineHeightAdded of lineHeightsAdded) {
|
||||
for (let lineNumber = lineHeightAdded.startLineNumber; lineNumber <= lineHeightAdded.endLineNumber; lineNumber++) {
|
||||
if (lineNumber >= fromLineNumber && lineNumber <= toLineNumber) {
|
||||
const currentMax = maxLineHeightPerLine.get(lineNumber) ?? this._defaultLineHeight;
|
||||
maxLineHeightPerLine.set(lineNumber, Math.max(currentMax, lineHeightAdded.lineHeight));
|
||||
}
|
||||
}
|
||||
this.insertOrChangeCustomLineHeight(
|
||||
lineHeightAdded.decorationId,
|
||||
lineHeightAdded.startLineNumber,
|
||||
lineHeightAdded.endLineNumber,
|
||||
lineHeightAdded.lineHeight
|
||||
);
|
||||
}
|
||||
const toReAdd: CustomLineHeightData[] = [];
|
||||
const decorationsImmediatelyAfter = new Set<string>();
|
||||
for (let i = startIndexOfInsertion; i < this._orderedCustomLines.length; i++) {
|
||||
if (this._orderedCustomLines[i].lineNumber === fromLineNumber) {
|
||||
@@ -257,9 +276,12 @@ export class LineHeightsManager {
|
||||
}
|
||||
}
|
||||
const decorationsWithGaps = intersection(decorationsImmediatelyBefore, decorationsImmediatelyAfter);
|
||||
const specialHeightToAdd = Array.from(maxLineHeightPerLine.values()).reduce((acc, height) => acc + height, 0);
|
||||
const defaultHeightToAdd = (insertCount - maxLineHeightPerLine.size) * this._defaultLineHeight;
|
||||
const prefixSumToAdd = specialHeightToAdd + defaultHeightToAdd;
|
||||
for (let i = startIndexOfInsertion; i < this._orderedCustomLines.length; i++) {
|
||||
this._orderedCustomLines[i].lineNumber += insertCount;
|
||||
this._orderedCustomLines[i].prefixSum += this._defaultLineHeight * insertCount;
|
||||
this._orderedCustomLines[i].prefixSum += prefixSumToAdd;
|
||||
}
|
||||
|
||||
if (decorationsWithGaps.size > 0) {
|
||||
@@ -281,8 +303,8 @@ export class LineHeightsManager {
|
||||
for (const dec of toReAdd) {
|
||||
this.insertOrChangeCustomLineHeight(dec.decorationId, dec.startLineNumber, dec.endLineNumber, dec.lineHeight);
|
||||
}
|
||||
this.commit();
|
||||
}
|
||||
this.commit();
|
||||
}
|
||||
|
||||
public commit(): void {
|
||||
@@ -363,11 +385,27 @@ export class LineHeightsManager {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ICustomLineHeightData {
|
||||
readonly decorationId: string;
|
||||
readonly startLineNumber: number;
|
||||
readonly endLineNumber: number;
|
||||
readonly lineHeight: number;
|
||||
export class CustomLineHeightData {
|
||||
|
||||
constructor(
|
||||
readonly decorationId: string,
|
||||
readonly startLineNumber: number,
|
||||
readonly endLineNumber: number,
|
||||
readonly lineHeight: number
|
||||
) { }
|
||||
|
||||
public static fromDecorations(decorations: IModelDecoration[], coordinatesConverter: ICoordinatesConverter, configuration: IEditorConfiguration): CustomLineHeightData[] {
|
||||
const defaultLineHeight = configuration.options.get(EditorOption.lineHeight);
|
||||
return decorations.map((d) => {
|
||||
const viewRange = coordinatesConverter.convertModelRangeToViewRange(d.range);
|
||||
return new CustomLineHeightData(
|
||||
d.id,
|
||||
viewRange.startLineNumber,
|
||||
viewRange.endLineNumber,
|
||||
d.options.lineHeight ? d.options.lineHeight * defaultLineHeight : 0
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayMap<K, T> {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { IEditorWhitespace, IPartialViewLinesViewportData, ILineHeightChangeAccessor, IViewWhitespaceViewportData, IWhitespaceChangeAccessor } from '../viewModel.js';
|
||||
import * as strings from '../../../base/common/strings.js';
|
||||
import { ICustomLineHeightData, LineHeightsManager } from './lineHeights.js';
|
||||
import { CustomLineHeightData, LineHeightsManager } from './lineHeights.js';
|
||||
|
||||
interface IPendingChange { id: string; newAfterLineNumber: number; newHeight: number }
|
||||
interface IPendingRemove { id: string }
|
||||
@@ -95,7 +95,7 @@ export class LinesLayout {
|
||||
private _paddingBottom: number;
|
||||
private _lineHeightsManager: LineHeightsManager;
|
||||
|
||||
constructor(lineCount: number, defaultLineHeight: number, paddingTop: number, paddingBottom: number, customLineHeightData: ICustomLineHeightData[]) {
|
||||
constructor(lineCount: number, defaultLineHeight: number, paddingTop: number, paddingBottom: number, customLineHeightData: CustomLineHeightData[]) {
|
||||
this._instanceId = strings.singleLetterHash(++LinesLayout.INSTANCE_COUNT);
|
||||
this._pendingChanges = new PendingChanges();
|
||||
this._lastWhitespaceId = 0;
|
||||
@@ -155,7 +155,7 @@ export class LinesLayout {
|
||||
*
|
||||
* @param lineCount New number of lines.
|
||||
*/
|
||||
public onFlushed(lineCount: number, customLineHeightData: ICustomLineHeightData[]): void {
|
||||
public onFlushed(lineCount: number, customLineHeightData: CustomLineHeightData[]): void {
|
||||
this._lineCount = lineCount;
|
||||
this._lineHeightsManager = new LineHeightsManager(this._lineHeightsManager.defaultLineHeight, customLineHeightData);
|
||||
}
|
||||
@@ -353,8 +353,9 @@ export class LinesLayout {
|
||||
*
|
||||
* @param fromLineNumber The line number at which the insertion started, inclusive
|
||||
* @param toLineNumber The line number at which the insertion ended, inclusive.
|
||||
* @param lineHeightsAdded The custom line height data for the inserted lines.
|
||||
*/
|
||||
public onLinesInserted(fromLineNumber: number, toLineNumber: number): void {
|
||||
public onLinesInserted(fromLineNumber: number, toLineNumber: number, lineHeightsAdded: CustomLineHeightData[]): void {
|
||||
fromLineNumber = fromLineNumber | 0;
|
||||
toLineNumber = toLineNumber | 0;
|
||||
|
||||
@@ -366,7 +367,7 @@ export class LinesLayout {
|
||||
this._arr[i].afterLineNumber += (toLineNumber - fromLineNumber + 1);
|
||||
}
|
||||
}
|
||||
this._lineHeightsManager.onLinesInserted(fromLineNumber, toLineNumber);
|
||||
this._lineHeightsManager.onLinesInserted(fromLineNumber, toLineNumber, lineHeightsAdded);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IEditorConfiguration } from '../config/editorConfiguration.js';
|
||||
import { LinesLayout } from './linesLayout.js';
|
||||
import { IEditorWhitespace, IPartialViewLinesViewportData, ILineHeightChangeAccessor, IViewLayout, IViewWhitespaceViewportData, IWhitespaceChangeAccessor, Viewport } from '../viewModel.js';
|
||||
import { ContentSizeChangedEvent } from '../viewModelEventDispatcher.js';
|
||||
import { ICustomLineHeightData } from './lineHeights.js';
|
||||
import { CustomLineHeightData } from './lineHeights.js';
|
||||
|
||||
const SMOOTH_SCROLLING_TIME = 125;
|
||||
|
||||
@@ -164,7 +164,7 @@ export class ViewLayout extends Disposable implements IViewLayout {
|
||||
public readonly onDidScroll: Event<ScrollEvent>;
|
||||
public readonly onDidContentSizeChange: Event<ContentSizeChangedEvent>;
|
||||
|
||||
constructor(configuration: IEditorConfiguration, lineCount: number, customLineHeightData: ICustomLineHeightData[], scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) {
|
||||
constructor(configuration: IEditorConfiguration, lineCount: number, customLineHeightData: CustomLineHeightData[], scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) {
|
||||
super();
|
||||
|
||||
this._configuration = configuration;
|
||||
@@ -237,14 +237,14 @@ export class ViewLayout extends Disposable implements IViewLayout {
|
||||
this._configureSmoothScrollDuration();
|
||||
}
|
||||
}
|
||||
public onFlushed(lineCount: number, customLineHeightData: ICustomLineHeightData[]): void {
|
||||
public onFlushed(lineCount: number, customLineHeightData: CustomLineHeightData[]): void {
|
||||
this._linesLayout.onFlushed(lineCount, customLineHeightData);
|
||||
}
|
||||
public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void {
|
||||
this._linesLayout.onLinesDeleted(fromLineNumber, toLineNumber);
|
||||
}
|
||||
public onLinesInserted(fromLineNumber: number, toLineNumber: number): void {
|
||||
this._linesLayout.onLinesInserted(fromLineNumber, toLineNumber);
|
||||
public onLinesInserted(fromLineNumber: number, toLineNumber: number, lineHeightsAdded: CustomLineHeightData[]): void {
|
||||
this._linesLayout.onLinesInserted(fromLineNumber, toLineNumber, lineHeightsAdded);
|
||||
}
|
||||
|
||||
// ---- end view event handlers
|
||||
|
||||
@@ -21,7 +21,7 @@ export class MinimapTokensColorTracker extends Disposable {
|
||||
private _colors!: RGBA8[];
|
||||
private _backgroundIsLight!: boolean;
|
||||
|
||||
private readonly _onDidChange = new Emitter<void>();
|
||||
private readonly _onDidChange = this._register(new Emitter<void>());
|
||||
public readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
private constructor() {
|
||||
|
||||
@@ -41,7 +41,7 @@ import { FocusChangedEvent, HiddenAreasChangedEvent, ModelContentChangedEvent, M
|
||||
import { IViewModelLines, ViewModelLinesFromModelAsIs, ViewModelLinesFromProjectedModel } from './viewModelLines.js';
|
||||
import { IThemeService } from '../../../platform/theme/common/themeService.js';
|
||||
import { GlyphMarginLanesModel } from './glyphLanesModel.js';
|
||||
import { ICustomLineHeightData } from '../viewLayout/lineHeights.js';
|
||||
import { CustomLineHeightData } from '../viewLayout/lineHeights.js';
|
||||
import { TextModelEditSource } from '../textModelEditSource.js';
|
||||
import { InlineDecoration } from './inlineDecorations.js';
|
||||
import { ICoordinatesConverter } from '../coordinatesConverter.js';
|
||||
@@ -196,23 +196,23 @@ export class ViewModel extends Disposable implements IViewModel {
|
||||
this._eventDispatcher.removeViewEventHandler(eventHandler);
|
||||
}
|
||||
|
||||
private _getCustomLineHeights(): ICustomLineHeightData[] {
|
||||
private _getCustomLineHeights(): CustomLineHeightData[] {
|
||||
const allowVariableLineHeights = this._configuration.options.get(EditorOption.allowVariableLineHeights);
|
||||
if (!allowVariableLineHeights) {
|
||||
return [];
|
||||
}
|
||||
const defaultLineHeight = this._configuration.options.get(EditorOption.lineHeight);
|
||||
const decorations = this.model.getCustomLineHeightsDecorations(this._editorId);
|
||||
return decorations.map((d) => {
|
||||
const lineNumber = d.range.startLineNumber;
|
||||
const viewRange = this.coordinatesConverter.convertModelRangeToViewRange(new Range(lineNumber, 1, lineNumber, this.model.getLineMaxColumn(lineNumber)));
|
||||
return {
|
||||
decorationId: d.id,
|
||||
startLineNumber: viewRange.startLineNumber,
|
||||
endLineNumber: viewRange.endLineNumber,
|
||||
lineHeight: d.options.lineHeight ? d.options.lineHeight * defaultLineHeight : 0
|
||||
};
|
||||
});
|
||||
return CustomLineHeightData.fromDecorations(decorations, this.coordinatesConverter, this._configuration);
|
||||
}
|
||||
|
||||
private _getCustomLineHeightsForLines(fromLineNumber: number, toLineNumber: number): CustomLineHeightData[] {
|
||||
const allowVariableLineHeights = this._configuration.options.get(EditorOption.allowVariableLineHeights);
|
||||
if (!allowVariableLineHeights) {
|
||||
return [];
|
||||
}
|
||||
const modelRange = new Range(fromLineNumber, 1, toLineNumber, this.model.getLineMaxColumn(toLineNumber));
|
||||
const decorations = this.model.getCustomLineHeightsDecorationsInRange(modelRange, this._editorId);
|
||||
return CustomLineHeightData.fromDecorations(decorations, this.coordinatesConverter, this._configuration);
|
||||
}
|
||||
|
||||
private _updateConfigurationViewLineCountNow(): void {
|
||||
@@ -379,7 +379,7 @@ export class ViewModel extends Disposable implements IViewModel {
|
||||
const linesInsertedEvent = this._lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks);
|
||||
if (linesInsertedEvent !== null) {
|
||||
eventsCollector.emitViewEvent(linesInsertedEvent);
|
||||
this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
|
||||
this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber, this._getCustomLineHeightsForLines(change.fromLineNumberPostEdit, change.toLineNumberPostEdit));
|
||||
}
|
||||
hadOtherModelChange = true;
|
||||
break;
|
||||
@@ -394,7 +394,7 @@ export class ViewModel extends Disposable implements IViewModel {
|
||||
}
|
||||
if (linesInsertedEvent) {
|
||||
eventsCollector.emitViewEvent(linesInsertedEvent);
|
||||
this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
|
||||
this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber, this._getCustomLineHeightsForLines(change.lineNumberPostEdit, change.lineNumberPostEdit));
|
||||
}
|
||||
if (linesDeletedEvent) {
|
||||
eventsCollector.emitViewEvent(linesDeletedEvent);
|
||||
|
||||
@@ -76,6 +76,7 @@ export class CodeLensContribution implements IEditorContribution {
|
||||
this._localDispose();
|
||||
this._localToDispose.dispose();
|
||||
this._disposables.dispose();
|
||||
this._resolveCodeLensesScheduler.dispose();
|
||||
this._oldCodeLensModels.dispose();
|
||||
this._currentCodeLensModel?.dispose();
|
||||
}
|
||||
|
||||
+2
-2
@@ -21,10 +21,10 @@ export class SaturationBox extends Disposable {
|
||||
private height!: number;
|
||||
|
||||
private monitor: GlobalPointerMoveMonitor | null;
|
||||
private readonly _onDidChange = new Emitter<{ s: number; v: number }>();
|
||||
private readonly _onDidChange = this._register(new Emitter<{ s: number; v: number }>());
|
||||
readonly onDidChange: Event<{ s: number; v: number }> = this._onDidChange.event;
|
||||
|
||||
private readonly _onColorFlushed = new Emitter<void>();
|
||||
private readonly _onColorFlushed = this._register(new Emitter<void>());
|
||||
readonly onColorFlushed: Event<void> = this._onColorFlushed.event;
|
||||
|
||||
constructor(container: HTMLElement, private readonly model: ColorPickerModel, private pixelRatio: number) {
|
||||
|
||||
@@ -20,10 +20,10 @@ export abstract class Strip extends Disposable {
|
||||
protected slider: HTMLElement;
|
||||
private height!: number;
|
||||
|
||||
private readonly _onDidChange = new Emitter<number>();
|
||||
private readonly _onDidChange = this._register(new Emitter<number>());
|
||||
readonly onDidChange: Event<number> = this._onDidChange.event;
|
||||
|
||||
private readonly _onColorFlushed = new Emitter<void>();
|
||||
private readonly _onColorFlushed = this._register(new Emitter<void>());
|
||||
readonly onColorFlushed: Event<void> = this._onColorFlushed.event;
|
||||
|
||||
constructor(container: HTMLElement, protected model: ColorPickerModel, type: ColorPickerWidgetType) {
|
||||
|
||||
@@ -131,7 +131,7 @@ export class CommonFindController extends Disposable implements IEditorContribut
|
||||
this._notificationService = notificationService;
|
||||
this._hoverService = hoverService;
|
||||
|
||||
this._updateHistoryDelayer = new Delayer<void>(500);
|
||||
this._updateHistoryDelayer = this._register(new Delayer<void>(500));
|
||||
this._state = this._register(new FindReplaceState());
|
||||
this.loadQueryState();
|
||||
this._register(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e)));
|
||||
|
||||
@@ -8,6 +8,7 @@ import { IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDe
|
||||
import { FoldingRegion, FoldingRegions, ILineRange, FoldRange, FoldSource } from './foldingRanges.js';
|
||||
import { hash } from '../../../../base/common/hash.js';
|
||||
import { SelectedLines } from './folding.js';
|
||||
import { IDisposable } from '../../../../base/common/lifecycle.js';
|
||||
|
||||
export interface IDecorationProvider {
|
||||
getDecorationOption(isCollapsed: boolean, isHidden: boolean, isManual: boolean): IModelDecorationOptions;
|
||||
@@ -28,7 +29,7 @@ interface ILineMemento extends ILineRange {
|
||||
|
||||
export type CollapseMemento = ILineMemento[];
|
||||
|
||||
export class FoldingModel {
|
||||
export class FoldingModel implements IDisposable {
|
||||
private readonly _textModel: ITextModel;
|
||||
private readonly _decorationProvider: IDecorationProvider;
|
||||
|
||||
@@ -229,6 +230,7 @@ export class FoldingModel {
|
||||
|
||||
public dispose() {
|
||||
this._decorationProvider.removeDecorations(this._editorDecorationIds);
|
||||
this._updateEventEmitter.dispose();
|
||||
}
|
||||
|
||||
getAllRegionsAtLine(lineNumber: number, filter?: (r: FoldingRegion, level: number) => boolean): FoldingRegion[] {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IModelContentChangedEvent } from '../../../common/textModelEvents.js';
|
||||
import { countEOL } from '../../../common/core/misc/eolCounter.js';
|
||||
import { FoldingModel } from './foldingModel.js';
|
||||
|
||||
export class HiddenRangeModel {
|
||||
export class HiddenRangeModel implements IDisposable {
|
||||
|
||||
private readonly _foldingModel: FoldingModel;
|
||||
private _hiddenRanges: IRange[];
|
||||
@@ -134,6 +134,7 @@ export class HiddenRangeModel {
|
||||
this._foldingModelListener.dispose();
|
||||
this._foldingModelListener = null;
|
||||
}
|
||||
this._updateEventEmitter.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -294,6 +294,7 @@ export class MarkerNavigationWidget extends PeekViewWidget {
|
||||
|
||||
override dispose(): void {
|
||||
this._callOnDispose.dispose();
|
||||
this._onDidSelectRelatedInformation.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
|
||||
private readonly _disposeOnNewModel = new DisposableStore();
|
||||
private readonly _callOnDispose = new DisposableStore();
|
||||
|
||||
private readonly _onDidSelectReference = new Emitter<SelectionEvent>();
|
||||
private readonly _onDidSelectReference = this._callOnDispose.add(new Emitter<SelectionEvent>());
|
||||
readonly onDidSelectReference = this._onDidSelectReference.event;
|
||||
|
||||
private _tree!: ReferencesTree;
|
||||
|
||||
@@ -6,12 +6,16 @@
|
||||
import * as dom from '../../../../base/browser/dom.js';
|
||||
import { IEditorMouseEvent } from '../../../browser/editorBrowser.js';
|
||||
|
||||
const enum PADDING {
|
||||
VALUE = 3
|
||||
}
|
||||
|
||||
export function isMousePositionWithinElement(element: HTMLElement, posx: number, posy: number): boolean {
|
||||
const elementRect = dom.getDomNodePagePosition(element);
|
||||
if (posx < elementRect.left
|
||||
|| posx > elementRect.left + elementRect.width
|
||||
|| posy < elementRect.top
|
||||
|| posy > elementRect.top + elementRect.height) {
|
||||
if (posx < elementRect.left + PADDING.VALUE
|
||||
|| posx > elementRect.left + elementRect.width - PADDING.VALUE
|
||||
|| posy < elementRect.top + PADDING.VALUE
|
||||
|| posy > elementRect.top + elementRect.height - PADDING.VALUE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -36,8 +36,8 @@ import { HoverStartSource } from './hoverOperation.js';
|
||||
import { ScrollEvent } from '../../../../base/common/scrollable.js';
|
||||
|
||||
const $ = dom.$;
|
||||
const increaseHoverVerbosityIcon = registerIcon('hover-increase-verbosity', Codicon.add, nls.localize('increaseHoverVerbosity', 'Icon for increaseing hover verbosity.'));
|
||||
const decreaseHoverVerbosityIcon = registerIcon('hover-decrease-verbosity', Codicon.remove, nls.localize('decreaseHoverVerbosity', 'Icon for decreasing hover verbosity.'));
|
||||
const increaseHoverVerbosityIcon = registerIcon('hover-increase-verbosity', Codicon.addSmall, nls.localize('increaseHoverVerbosity', 'Icon for increaseing hover verbosity.'));
|
||||
const decreaseHoverVerbosityIcon = registerIcon('hover-decrease-verbosity', Codicon.removeSmall, nls.localize('decreaseHoverVerbosity', 'Icon for decreasing hover verbosity.'));
|
||||
|
||||
export class MarkdownHover implements IHoverPart {
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user