Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8bfcb33
Initial plan
Copilot Feb 25, 2026
dfe6844
Fix markdown syntax showing in deprecated setting diagnostic messages
Copilot Feb 25, 2026
d2345ae
Use renderAsPlaintext and rewriteSettingLinks for deprecation message…
Copilot Feb 25, 2026
8ea2556
Reuse fixSettingLinks from preferencesModels.ts instead of custom rew…
Copilot Feb 25, 2026
8665c72
Merge branch 'main' into copilot/fix-tooltip-markdown-syntax
mjbvz Mar 4, 2026
34cee22
Merge branch 'main' into copilot/fix-tooltip-markdown-syntax
mjbvz Mar 10, 2026
f0df384
Reduce `....args: any[]` usage
mjbvz Mar 10, 2026
3ef1baf
Consolidate MockChatService
mjbvz Mar 10, 2026
9f946ce
fix: prevent local cwd evaluation in sessions window
benibenj Mar 10, 2026
3f2a0f6
Merge pull request #300472 from mjbvz/dev/mjbvz/stable-lark
mjbvz Mar 10, 2026
c2da267
[MCP_Sandboxing]Updating regex for extracting paths from sandbox erro…
dileepyavan Mar 10, 2026
7daf926
Add telemetry for xterm imageAddon (#299017)
anthonykim1 Mar 10, 2026
0cd1e59
Disable api-version-check for now
mjbvz Mar 10, 2026
34bfd71
Revert "Revert "Debug Panel: oTel data source support and Import/expo…
vijayupadya Mar 10, 2026
2eefd9e
Merge pull request #300495 from mjbvz/dev/mjbvz/fierce-hawk
mjbvz Mar 10, 2026
0a8edf7
plugins: allow updating agent plugins (#300344)
connor4312 Mar 10, 2026
cb61e0d
Fix source path duplication in NLS plugin source maps and add corresp…
jrieken Mar 10, 2026
929482d
Merge branch 'main' into dev/mjbvz/faithful-crow
mjbvz Mar 10, 2026
01eb53f
Bump xterm to take IME overflow fix (#300322)
anthonykim1 Mar 10, 2026
d42b123
Merge pull request #297570 from microsoft/copilot/fix-tooltip-markdow…
mjbvz Mar 10, 2026
82ee6eb
Bump the component-explorer group with 2 updates (#300168)
dependabot[bot] Mar 10, 2026
13262e3
fix `cannot read properties of undefined (reading 'getCell')` error t…
meganrogge Mar 10, 2026
97c94c2
Sessions - update context key calculation (#300514)
lszomoru Mar 10, 2026
db2c7ee
Update Fig spec for Azure Developer CLI (azd) (#299892)
JeffreyCA Mar 10, 2026
b50d56f
do not send freeform input for autoreply, just return (#300515)
meganrogge Mar 10, 2026
cfe3b32
Update action for the title bar (#300453)
dmitrivMS Mar 10, 2026
3aa9077
Merge pull request #300358 from mjbvz/dev/mjbvz/faithful-crow
mjbvz Mar 10, 2026
bcc83ce
Fix `cannot read properties of undefined (reading 'filter')` (#300518)
meganrogge Mar 10, 2026
ea9e938
chat: simplify symbol reference cache to an array (#300489)
connor4312 Mar 10, 2026
9a1fe57
Fix registerToolDefinition tools from default chat agent being filter…
bhavyaus Mar 10, 2026
f01d41c
Keep Chat customizations section selected while active (#300528)
joshspicer Mar 10, 2026
03968c2
Re-remove webpack
mjbvz Mar 10, 2026
d71bb75
Remove `await` in discovery phase (#300537)
pwang347 Mar 10, 2026
4863c50
Sessions - add prompt for the draft pull request (#300543)
lszomoru Mar 10, 2026
b971893
Log when `untitled` sessions cross the ext host boundary
mjbvz Mar 10, 2026
f6a8182
Remove one log
mjbvz Mar 10, 2026
a200e8b
ai-customizations: improve list visual scannability (#300551)
joshspicer Mar 10, 2026
ec2b57c
Merge pull request #300547 from mjbvz/dev/mjbvz/glorious-sparrow
mjbvz Mar 10, 2026
6097683
Merge pull request #300475 from microsoft/benibenj/northern-tahr
benibenj Mar 10, 2026
3140ee0
Merge pull request #300562 from mjbvz/dev/mjbvz/tragic-tyrannosaurus
mjbvz Mar 10, 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
10 changes: 5 additions & 5 deletions .github/workflows/api-proposal-version-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ jobs:
check-version-changes:
name: Check API Proposal Version Changes
# Run on PR events, or on issue_comment if it's on a PR and contains the override command
if: |
github.event_name == 'pull_request' ||
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(github.event.comment.body, '/api-proposal-change-required'))
if: false # temporarily disabled
# github.event_name == 'pull_request' ||
# (github.event_name == 'issue_comment' &&
# github.event.issue.pull_request &&
# contains(github.event.comment.body, '/api-proposal-change-required'))
runs-on: ubuntu-latest
steps:
- name: Get PR info
Expand Down
12 changes: 0 additions & 12 deletions build/gulpfile.extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,6 @@ async function buildWebExtensions(isWatch: boolean): Promise<void> {
{ ignore: ['**/node_modules'] }
);

// 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
Expand All @@ -330,10 +323,5 @@ async function buildWebExtensions(isWatch: boolean): Promise<void> {
);
}

// 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);
}
208 changes: 4 additions & 204 deletions build/lib/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ import fancyLog from 'fancy-log';
import ansiColors from 'ansi-colors';
import buffer from 'gulp-buffer';
import * as jsoncParser from 'jsonc-parser';
import webpack from 'webpack';
import { getProductionDependencies } from './dependencies.ts';
import { type IExtensionDefinition, getExtensionStream } from './builtInExtensions.ts';
import { getVersion } from './getVersion.ts';
import { fetchUrls, fetchGithub } from './fetch.ts';
import { createTsgoStream, spawnTsgo } from './tsgo.ts';
import vzip from 'gulp-vinyl-zip';
Expand All @@ -32,8 +30,8 @@ import { createRequire } from 'module';
const require = createRequire(import.meta.url);

const root = path.dirname(path.dirname(import.meta.dirname));
const commit = getVersion(root);
const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`;
// const commit = getVersion(root);
// const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`;

function minifyExtensionResources(input: Stream): Stream {
const jsonFilter = filter(['**/*.json', '**/*.code-snippets'], { restore: true });
Expand Down Expand Up @@ -65,32 +63,24 @@ function updateExtensionPackageJSON(input: Stream, update: (data: any) => any):
.pipe(packageJsonFilter.restore);
}

function fromLocal(extensionPath: string, forWeb: boolean, disableMangle: boolean): Stream {
function fromLocal(extensionPath: string, forWeb: boolean, _disableMangle: boolean): Stream {

const esbuildConfigFileName = forWeb
? 'esbuild.browser.mts'
: 'esbuild.mts';

const webpackConfigFileName = forWeb
? `extension-browser.webpack.config.js`
: `extension.webpack.config.js`;

const hasEsbuild = fs.existsSync(path.join(extensionPath, esbuildConfigFileName));
const hasWebpack = fs.existsSync(path.join(extensionPath, webpackConfigFileName));

let input: Stream;
let isBundled = false;

if (hasEsbuild) {
// Unlike webpack, esbuild only does bundling so we still want to run a separate type check step
// Esbuild only does bundling so we still want to run a separate type check step
input = es.merge(
fromLocalEsbuild(extensionPath, esbuildConfigFileName),
...getBuildRootsForExtension(extensionPath).map(root => typeCheckExtensionStream(root, forWeb)),
);
isBundled = true;
} else if (hasWebpack) {
input = fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle);
isBundled = true;
} else {
input = fromLocalNormal(extensionPath);
}
Expand Down Expand Up @@ -122,132 +112,6 @@ export function typeCheckExtensionStream(extensionPath: string, forWeb: boolean)
return createTsgoStream(tsconfigPath, { taskName: 'typechecking extension (tsgo)', noEmit: true });
}

function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string, disableMangle: boolean): Stream {
const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce');
const webpack = require('webpack');
const webpackGulp = require('webpack-stream');
const result = es.through();

const packagedDependencies: string[] = [];
const stripOutSourceMaps: string[] = [];
const packageJsonConfig = require(path.join(extensionPath, 'package.json'));
if (packageJsonConfig.dependencies) {
const webpackConfig = require(path.join(extensionPath, webpackConfigFileName));
const webpackRootConfig = webpackConfig.default;
for (const key in webpackRootConfig.externals) {
if (key in packageJsonConfig.dependencies) {
packagedDependencies.push(key);
}
}

if (webpackConfig.StripOutSourceMaps) {
for (const filePath of webpackConfig.StripOutSourceMaps) {
stripOutSourceMaps.push(filePath);
}
}
}

// TODO: add prune support based on packagedDependencies to vsce.PackageManager.Npm similar
// to vsce.PackageManager.Yarn.
// A static analysis showed there are no webpack externals that are dependencies of the current
// local extensions so we can use the vsce.PackageManager.None config to ignore dependencies list
// as a temporary workaround.
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.None, packagedDependencies }).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)
}));

// check for a webpack configuration files, then invoke webpack
// and merge its output with the files stream.
const webpackConfigLocations = (glob.sync(
path.join(extensionPath, '**', webpackConfigFileName),
{ ignore: ['**/node_modules'] }
) as string[]);
const webpackStreams = webpackConfigLocations.flatMap(webpackConfigPath => {

const webpackDone = (err: Error | undefined, stats: any) => {
fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`);
if (err) {
result.emit('error', err);
}
const { compilation } = stats;
if (compilation.errors.length > 0) {
result.emit('error', compilation.errors.join('\n'));
}
if (compilation.warnings.length > 0) {
result.emit('error', compilation.warnings.join('\n'));
}
};

const exportedConfig = require(webpackConfigPath).default;
return (Array.isArray(exportedConfig) ? exportedConfig : [exportedConfig]).map(config => {
const webpackConfig = {
...config,
...{ mode: 'production' }
};
if (disableMangle) {
if (Array.isArray(config.module.rules)) {
for (const rule of config.module.rules) {
if (Array.isArray(rule.use)) {
for (const use of rule.use) {
if (String(use.loader).endsWith('mangle-loader.js')) {
use.options.disabled = true;
}
}
}
}
}
}
const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path);

return webpackGulp(webpackConfig, webpack, webpackDone)
.pipe(es.through(function (data) {
data.stat = data.stat || {};
data.base = extensionPath;
this.emit('data', data);
}))
.pipe(es.through(function (data: File) {
// source map handling:
// * rewrite sourceMappingURL
// * save to disk so that upload-task picks this up
if (path.extname(data.basename) === '.js') {
if (stripOutSourceMaps.indexOf(data.relative) >= 0) { // remove source map
const contents = (data.contents as Buffer).toString('utf8');
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8');
} else {
const contents = (data.contents as Buffer).toString('utf8');
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) {
return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`;
}), 'utf8');
}
}

this.emit('data', data);
}));
});
});

es.merge(...webpackStreams, es.readArray(files))
// .pipe(es.through(function (data) {
// // debug
// console.log('out', data.path, data.contents.length);
// this.emit('data', data);
// }))
.pipe(result);

}).catch(err => {
console.error(extensionPath);
console.error(packagedDependencies);
result.emit('error', err);
});

return result.pipe(createStatsStream(path.basename(extensionPath)));
}

function fromLocalNormal(extensionPath: string): Stream {
const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce');
Expand Down Expand Up @@ -649,70 +513,6 @@ export function translatePackageJSON(packageJSON: string, packageNLSPath: string

const extensionsPath = path.join(root, 'extensions');

export async function webpackExtensions(taskName: string, isWatch: boolean, webpackConfigLocations: { configPath: string; outputRoot?: string }[]) {
const webpack = require('webpack') as typeof import('webpack');

const webpackConfigs: webpack.Configuration[] = [];

for (const { configPath, outputRoot } of webpackConfigLocations) {
const configOrFnOrArray = require(configPath).default;
function addConfig(configOrFnOrArray: webpack.Configuration | ((env: unknown, args: unknown) => webpack.Configuration) | webpack.Configuration[]) {
for (const configOrFn of Array.isArray(configOrFnOrArray) ? configOrFnOrArray : [configOrFnOrArray]) {
const config = typeof configOrFn === 'function' ? configOrFn({}, {}) : configOrFn;
if (outputRoot) {
config.output!.path = path.join(outputRoot, path.relative(path.dirname(configPath), config.output!.path!));
}
webpackConfigs.push(config);
}
}
addConfig(configOrFnOrArray);
}

function reporter(fullStats: any) {
if (Array.isArray(fullStats.children)) {
for (const stats of fullStats.children) {
const outputPath = stats.outputPath;
if (outputPath) {
const relativePath = path.relative(extensionsPath, outputPath).replace(/\\/g, '/');
const match = relativePath.match(/[^\/]+(\/server|\/client)?/);
fancyLog(`Finished ${ansiColors.green(taskName)} ${ansiColors.cyan(match![0])} with ${stats.errors.length} errors.`);
}
if (Array.isArray(stats.errors)) {
stats.errors.forEach((error: any) => {
fancyLog.error(error);
});
}
if (Array.isArray(stats.warnings)) {
stats.warnings.forEach((warning: any) => {
fancyLog.warn(warning);
});
}
}
}
}
return new Promise<void>((resolve, reject) => {
if (isWatch) {
webpack(webpackConfigs).watch({}, (err, stats) => {
if (err) {
reject();
} else {
reporter(stats?.toJson());
}
});
} else {
webpack(webpackConfigs).run((err, stats) => {
if (err) {
fancyLog.error(err);
reject();
} else {
reporter(stats?.toJson());
resolve();
}
});
}
});
}

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);
Expand Down
1 change: 1 addition & 0 deletions build/lib/stylelint/vscode-known-variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,7 @@
"--testMessageDecorationFontSize",
"--title-border-bottom-color",
"--title-wco-width",
"--update-progress",
"--reveal-button-size",
"--part-background",
"--part-border-color",
Expand Down
6 changes: 5 additions & 1 deletion build/next/nls-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,11 @@ export function nlsPlugin(options: NLSPluginOptions): esbuild.Plugin {
// back to the original. Embed it inline so esbuild composes it
// with its own bundle source map, making the final map point to
// the original TS source.
const sourceName = relativePath.replace(/\\/g, '/');
// This inline source map is resolved relative to esbuild's sourcefile
// for args.path. Using the full repo-relative path here makes esbuild
// resolve it against the file's own directory, which duplicates the
// directory segments in the final bundled source map.
const sourceName = path.basename(args.path);
const sourcemap = generateNLSSourceMap(source, sourceName, edits);
const encodedMap = Buffer.from(sourcemap).toString('base64');
const contentsWithMap = code + `\n//# sourceMappingURL=data:application/json;base64,${encodedMap}\n`;
Expand Down
22 changes: 22 additions & 0 deletions build/next/test/nls-sourcemap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,28 @@ suite('NLS plugin source maps', () => {
}
});

test('NLS-affected nested file keeps a non-duplicated source path', async () => {
const source = [
'import { localize } from "../../vs/nls";',
'export const msg = localize("myKey", "Hello World");',
].join('\n');

const { mapJson, cleanup } = await bundleWithNLS(
{ 'nested/deep/file.ts': source },
'nested/deep/file.ts',
);

try {
const sources: string[] = mapJson.sources ?? [];
const nestedSource = sources.find((s: string) => s.endsWith('/nested/deep/file.ts'));
assert.ok(nestedSource, 'Should find nested/deep/file.ts in sources');
assert.ok(!nestedSource.includes('/nested/deep/nested/deep/file.ts'),
`Source path should not duplicate directory segments. Actual: ${nestedSource}`);
} finally {
cleanup();
}
});

test('line mapping correct for code after localize calls', async () => {
const source = [
'import { localize } from "../vs/nls";', // 1
Expand Down
27 changes: 25 additions & 2 deletions extensions/git/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1064,10 +1064,33 @@ export class CommandCenter {
}

@command('_git.pull')
async pullRepository(repositoryPath: string): Promise<void> {
async pullRepository(repositoryPath: string): Promise<boolean> {
const dotGit = await this.git.getRepositoryDotGit(repositoryPath);
const repo = new GitRepository(this.git, repositoryPath, undefined, dotGit, this.logger);
await repo.pull();
return repo.pull();
}

@command('_git.fetchRepository')
async fetchRepository(repositoryPath: string): Promise<void> {
const dotGit = await this.git.getRepositoryDotGit(repositoryPath);
const repo = new GitRepository(this.git, repositoryPath, undefined, dotGit, this.logger);
await repo.fetch();
}

@command('_git.revParse')
async revParse(repositoryPath: string, ref: string): Promise<string> {
const dotGit = await this.git.getRepositoryDotGit(repositoryPath);
const repo = new GitRepository(this.git, repositoryPath, undefined, dotGit, this.logger);
const result = await repo.exec(['rev-parse', ref]);
return result.stdout.trim();
}

@command('_git.revListCount')
async revListCount(repositoryPath: string, fromRef: string, toRef: string): Promise<number> {
const dotGit = await this.git.getRepositoryDotGit(repositoryPath);
const repo = new GitRepository(this.git, repositoryPath, undefined, dotGit, this.logger);
const result = await repo.exec(['rev-list', '--count', `${fromRef}..${toRef}`]);
return Number(result.stdout.trim()) || 0;
}

@command('_git.revParseAbbrevRef')
Expand Down
Loading
Loading