Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cca17c1
Use ts-go for building our extensions
mjbvz Feb 3, 2026
1b46833
Use npx
mjbvz Feb 3, 2026
9e3e805
Fix for windows
mjbvz Feb 3, 2026
5a76a42
Merge branch 'main' into dev/mjbvz/ext-ts-go
mjbvz Feb 3, 2026
1cf3239
sessions: prevent transferring a disposed editing session
connor4312 Feb 3, 2026
a809270
Switch to standard import in build file
mjbvz Feb 3, 2026
d0e5166
fix: rename product executable on macOS (#291948)
deepak1556 Feb 3, 2026
e2e8686
Fix double focus (#292691)
lramos15 Feb 3, 2026
4f19550
Merge pull request #292667 from microsoft/connor4312/292174
connor4312 Feb 3, 2026
2c03cf9
Merge pull request #292461 from microsoft/dev/mjbvz/ext-ts-go
mjbvz Feb 3, 2026
eeed26e
fix uninstalling deprecated extensions in all profiles (#292694)
sandy081 Feb 3, 2026
dba5f79
Merge pull request #292704 from mjbvz/dev/mjbvz/isolated-quokka
mjbvz Feb 3, 2026
68e1b20
Fix quick chat layout (#292705)
roblourens Feb 3, 2026
ce96513
fix selection, accessibility, and keyboard bug (#292709)
justschen Feb 4, 2026
d158e4e
Update status bar item with hovers (#292136)
dmitrivMS Feb 4, 2026
02df3fb
Revert "Bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 in /extensio…
rzhao271 Feb 4, 2026
f0a58da
Revert "Bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 in /extensio…
rzhao271 Feb 4, 2026
f4ce567
Revert "Bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 in /extensio…
rzhao271 Feb 4, 2026
dd69bca
Add initial hooks support (#292699)
pwang347 Feb 4, 2026
3cd367d
Fix source maps for extensions after tsgo
mjbvz Feb 4, 2026
9140aa3
Fix redundant "Checkbox for Step" in walkthrough step aria-labels (#2…
Copilot Feb 4, 2026
bc72d94
Merge pull request #292757 from mjbvz/dev/mjbvz/lonely-felidae
mjbvz Feb 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,10 @@ export class DarwinTestRunner extends PosixTestRunner {

/** @override */
protected override async binaryPath() {
const { nameLong } = await this.readProductJson();
const { nameLong, nameShort } = await this.readProductJson();
return path.join(
this.repoLocation.uri.fsPath,
`.build/electron/${nameLong}.app/Contents/MacOS/Electron`
`.build/electron/${nameLong}.app/Contents/MacOS/${nameShort}`
);
}
}
Expand Down
6 changes: 3 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@
"request": "launch",
"name": "Run Unit Tests",
"program": "${workspaceFolder}/test/unit/electron/index.js",
"runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron",
"runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Code - OSS",
"windows": {
"runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.exe"
},
Expand Down Expand Up @@ -535,7 +535,7 @@
"request": "launch",
"name": "Run Unit Tests For Current File",
"program": "${workspaceFolder}/test/unit/electron/index.js",
"runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron",
"runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Code - OSS",
"windows": {
"runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.exe"
},
Expand Down Expand Up @@ -571,7 +571,7 @@
"timeout": 240000,
"args": [
"-l",
"${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron"
"${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Code - OSS"
],
"outFiles": [
"${cwd}/out/**/*.js"
Expand Down
14 changes: 14 additions & 0 deletions build/azure-pipelines/darwin/product-build-darwin-universal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,20 @@ jobs:
DEBUG=* node build/darwin/create-universal-app.ts $(agent.builddirectory)
displayName: Create Universal App

- script: |
set -e
APP_ROOT="$(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH)"
APP_NAME="`ls $APP_ROOT | head -n 1`"
APP_PATH="$APP_ROOT/$APP_NAME"
EXEC_NAME=$(node -p "require(\"$APP_PATH/Contents/Resources/app/product.json\").nameShort")
# Create a symlink from 'Electron' to the actual executable for backward compatibility
# This ensures apps that relied on the hardcoded path 'Contents/MacOS/Electron' continue to work
# Remove this step once main branch is on 1.112 release.
if [ "$EXEC_NAME" != "Electron" ] && [ ! -L "$APP_PATH/Contents/MacOS/Electron" ]; then
ln -s "$EXEC_NAME" "$APP_PATH/Contents/MacOS/Electron"
fi
displayName: Create Electron symlink for backward compatibility

- script: |
set -e
APP_ROOT="$(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,21 @@ steps:
chmod +x "$APP_PATH/Contents/Resources/app/bin/$CLI_APP_NAME"
displayName: Make CLI executable

- script: |
set -e
APP_ROOT="$(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH)"
APP_NAME="`ls $APP_ROOT | head -n 1`"
APP_PATH="$APP_ROOT/$APP_NAME"
EXEC_NAME=$(node -p "require(\"$APP_PATH/Contents/Resources/app/product.json\").nameShort")
# Create a symlink from 'Electron' to the actual executable for backward compatibility
# This ensures apps that relied on the hardcoded path 'Contents/MacOS/Electron' continue to work
# Remove this step once main branch is on 1.112 release.
if [ "$EXEC_NAME" != "Electron" ] && [ ! -L "$APP_PATH/Contents/MacOS/Electron" ]; then
ln -s "$EXEC_NAME" "$APP_PATH/Contents/MacOS/Electron"
fi
condition: eq(variables['BUILT_CLIENT'], 'true')
displayName: Create Electron symlink for backward compatibility

- script: |
set -e
APP_ROOT="$(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ steps:
set -e
APP_ROOT="$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)"
APP_NAME="`ls $APP_ROOT | head -n 1`"
INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \
ProductJsonPath=$(find "$APP_ROOT" -name "product.json" -type f | head -n 1)
BINARY_NAME=$(jq -r '.nameShort' "$ProductJsonPath")
INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/$BINARY_NAME" \
./scripts/test-integration.sh --build --tfs "Integration Tests"
env:
VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)
Expand All @@ -77,7 +79,9 @@ steps:
set -e
APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)
APP_NAME="`ls $APP_ROOT | head -n 1`"
INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \
ProductJsonPath=$(find "$APP_ROOT" -name "product.json" -type f | head -n 1)
BINARY_NAME=$(jq -r '.nameShort' "$ProductJsonPath")
INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/$BINARY_NAME" \
./scripts/test-remote-integration.sh
env:
VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)
Expand Down
54 changes: 32 additions & 22 deletions build/gulpfile.extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@
import { EventEmitter } from 'events';
EventEmitter.defaultMaxListeners = 100;

import es from 'event-stream';
import glob from 'glob';
import gulp from 'gulp';
import filter from 'gulp-filter';
import plumber from 'gulp-plumber';
import sourcemaps from 'gulp-sourcemaps';
import * as path from 'path';
import * as nodeUtil from 'util';
import es from 'event-stream';
import filter from 'gulp-filter';
import * as util from './lib/util.ts';
import * as ext from './lib/extensions.ts';
import { getVersion } from './lib/getVersion.ts';
import * as task from './lib/task.ts';
import watcher from './lib/watch/index.ts';
import { createReporter } from './lib/reporter.ts';
import glob from 'glob';
import plumber from 'gulp-plumber';
import * as ext from './lib/extensions.ts';
import * as task from './lib/task.ts';
import * as tsb from './lib/tsb/index.ts';
import sourcemaps from 'gulp-sourcemaps';
import { createTsgoStream, spawnTsgo } from './lib/tsgo.ts';
import * as util from './lib/util.ts';
import watcher from './lib/watch/index.ts';

const root = path.dirname(import.meta.dirname);
const commit = getVersion(root);
Expand Down Expand Up @@ -78,6 +79,18 @@ const compilations = [

const getBaseUrl = (out: string) => `https://main.vscode-cdn.net/sourcemaps/${commit}/${out}`;

function rewriteTsgoSourceMappingUrlsIfNeeded(build: boolean, out: string, baseUrl: string): Promise<void> {
if (!build) {
return Promise.resolve();
}

return util.streamToPromise(
gulp.src(path.join(out, '**', '*.js'), { base: out })
.pipe(util.rewriteSourceMappingURL(baseUrl))
.pipe(gulp.dest(out))
);
}

const tasks = compilations.map(function (tsconfigFile) {
const absolutePath = path.join(root, tsconfigFile);
const relativeDirname = path.dirname(tsconfigFile.replace(/^(.*\/)?extensions\//i, ''));
Expand Down Expand Up @@ -150,25 +163,22 @@ const tasks = compilations.map(function (tsconfigFile) {
.pipe(gulp.dest(out));
}));

const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, () => {
const pipeline = createPipeline(false, true);
const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts']));
const input = es.merge(nonts, pipeline.tsProjectSrc());
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));

return input
.pipe(pipeline())
.pipe(gulp.dest(out));
await Promise.all([copyNonTs, tsgo]);
}));

const watchTask = task.define(`watch-extension:${name}`, task.series(cleanTask, () => {
const pipeline = createPipeline(false);
const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts']));
const input = es.merge(nonts, pipeline.tsProjectSrc());
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 watchStream = es.merge(nonts.pipe(gulp.dest(out)), watchNonTs, tsgoStream);

return watchInput
.pipe(util.incremental(pipeline, input))
.pipe(gulp.dest(out));
return watchStream;
}));

// Tasks
Expand Down
18 changes: 8 additions & 10 deletions build/gulpfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { EventEmitter } from 'events';
EventEmitter.defaultMaxListeners = 100;

import glob from 'glob';
import gulp from 'gulp';
import { createRequire } from 'node:module';
Expand All @@ -12,29 +14,25 @@ import * as compilation from './lib/compilation.ts';
import * as task from './lib/task.ts';
import * as util from './lib/util.ts';

EventEmitter.defaultMaxListeners = 100;

const require = createRequire(import.meta.url);

const { transpileTask, compileTask, watchTask, compileApiProposalNamesTask, watchApiProposalNamesTask } = compilation;

// API proposal names
gulp.task(compileApiProposalNamesTask);
gulp.task(watchApiProposalNamesTask);
gulp.task(compilation.compileApiProposalNamesTask);
gulp.task(compilation.watchApiProposalNamesTask);

// SWC Client Transpile
const transpileClientSWCTask = task.define('transpile-client-esbuild', task.series(util.rimraf('out'), transpileTask('src', 'out', true)));
const transpileClientSWCTask = task.define('transpile-client-esbuild', task.series(util.rimraf('out'), compilation.transpileTask('src', 'out', true)));
gulp.task(transpileClientSWCTask);

// Transpile only
const transpileClientTask = task.define('transpile-client', task.series(util.rimraf('out'), transpileTask('src', 'out')));
const transpileClientTask = task.define('transpile-client', task.series(util.rimraf('out'), compilation.transpileTask('src', 'out')));
gulp.task(transpileClientTask);

// Fast compile for development time
const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), compilation.copyCodiconsTask, compileApiProposalNamesTask, compileTask('src', 'out', false)));
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(watchTask('out', false), watchApiProposalNamesTask, compilation.watchCodiconsTask)));
const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), task.parallel(compilation.watchTask('out', false), compilation.watchApiProposalNamesTask, compilation.watchCodiconsTask)));
gulp.task(watchClientTask);

// All
Expand Down
1 change: 1 addition & 0 deletions build/gulpfile.vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d
} else if (platform === 'darwin') {
const shortcut = gulp.src('resources/darwin/bin/code.sh')
.pipe(replace('@@APPNAME@@', product.applicationName))
.pipe(replace('@@NAME@@', product.nameShort))
.pipe(rename('bin/code'));
const policyDest = gulp.src('.build/policies/darwin/**', { base: '.build/policies/darwin' })
.pipe(rename(f => f.dirname = `policies/${f.dirname}`));
Expand Down
1 change: 1 addition & 0 deletions build/lib/electron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export const config = {
productAppName: product.nameLong,
companyName: 'Microsoft Corporation',
copyright: 'Copyright (C) 2026 Microsoft. All rights reserved',
darwinExecutable: product.nameShort,
darwinIcon: 'resources/darwin/code.icns',
darwinBundleIdentifier: product.darwinBundleIdentifier,
darwinApplicationCategoryType: 'public.app-category.developer-tools',
Expand Down
116 changes: 116 additions & 0 deletions build/lib/tsgo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as cp from 'child_process';
import es from 'event-stream';
import * as path from 'path';
import { createReporter } from './reporter.ts';

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');
let report: NodeJS.ReadWriteStream | undefined;

const beginReport = (emitError: boolean) => {
if (report) {
report.end();
}
report = reporter.end(emitError);
};

const endReport = () => {
if (!report) {
return;
}
report.end();
report = undefined;
};

const args = ['tsgo', '--project', projectPath, '--pretty', 'false', '--sourceMap', '--inlineSources'];

beginReport(false);

const child = cp.spawn(npx, args, {
cwd: root,
stdio: ['ignore', 'pipe', 'pipe'],
shell: true
});

let buffer = '';
const handleLine = (line: string) => {
const trimmed = line.replace(ansiRegex, '').trim();
if (!trimmed) {
return;
}
if (/Starting compilation|File change detected/i.test(trimmed)) {
beginReport(false);
return;
}
if (/Compilation complete/i.test(trimmed)) {
endReport();
return;
}

const match = /(.*\(\d+,\d+\): )(.*: )(.*)/.exec(trimmed);

if (match) {
const fullpath = path.isAbsolute(match[1]) ? match[1] : path.join(root, match[1]);
const message = match[3];
reporter(fullpath + message);
} else {
reporter(trimmed);
}
};

const handleData = (data: Buffer) => {
buffer += data.toString('utf8');
const lines = buffer.split(/\r?\n/);
buffer = lines.pop() ?? '';
for (const line of lines) {
handleLine(line);
}
};

child.stdout?.on('data', handleData);
child.stderr?.on('data', handleData);

const done = new Promise<void>((resolve, reject) => {
child.on('exit', code => {
if (buffer.trim()) {
handleLine(buffer);
buffer = '';
}
endReport();
if (code === 0) {
Promise.resolve(onComplete?.()).then(() => resolve(), reject);
return;
}
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 {
const stream = es.through();

spawnTsgo(projectPath, onComplete).then(() => {
stream.emit('end');
}).catch(() => {
// Errors are already reported by spawnTsgo via the reporter.
// Don't emit 'error' on the stream as that would exit the watch process.
stream.emit('end');
});

return stream;
}
6 changes: 3 additions & 3 deletions extensions/css-language-features/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions extensions/git-base/src/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ function _throttle<T>(fn: Function, key: string): Function {
return trigger;
}

function decorate(decorator: (fn: Function, key: string) => Function): Function {
return function (original: any, context: ClassMethodDecoratorContext) {
if (context.kind !== 'method') {
function decorate(decorator: (fn: Function, key: string) => Function): MethodDecorator {
return (_target: any, key: string | symbol, descriptor: PropertyDescriptor): void => {
if (typeof descriptor.value !== 'function') {
throw new Error('not supported');
}
return decorator(original, context.name.toString());
descriptor.value = decorator(descriptor.value, String(key));
};
}
Loading
Loading