Merge remote-tracking branch 'origin/main' into tyriar/116467_2

This commit is contained in:
Daniel Imms 2021-03-09 05:35:42 -08:00
commit 6d01ad952a
53 changed files with 456 additions and 271 deletions

View file

@ -985,7 +985,8 @@
"CustomEditorProvider",
"CustomReadonlyEditorProvider",
"TerminalLinkProvider",
"AuthenticationProvider"
"AuthenticationProvider",
"NotebookContentProvider"
]
}
],

41
SECURITY.md Normal file
View file

@ -0,0 +1,41 @@
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->

View file

@ -77,9 +77,9 @@ jsonc-parser@^2.3.0:
integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==
vscode-emmet-helper@^2.3.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.4.0.tgz#12f82fd05fb1df11ef7e78f85df63724de54a05b"
integrity sha512-aMbUL3oHBWM/Ux/C035a6KKLgIV8+GNGuu1b7ztuHXM6VmrdFFLNI5+tfFjfYQ3GBpD+Q7ZQU7CX/JaGlBbBiA==
version "2.4.1"
resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-2.4.1.tgz#7e020f66cbd72cf8e107b0ac7235cb806a728126"
integrity sha512-Sz3QbEgD1h05+sK3ltVLOUl2KKqwFVGUFSLS7ZlVqZoKPCWiFnd0dgf3miyipt6lGOadrsqvn7mCRbiSXJAMqA==
dependencies:
emmet "^2.3.0"
jsonc-parser "^2.3.0"

View file

@ -1240,6 +1240,12 @@ suite('Notebook API tests', function () {
assert.strictEqual(document.cells[0].metadata.executionOrder, executionOrder);
assert.strictEqual(document.cells[0].metadata.runState, vscode.NotebookCellRunState.Success);
});
test('Opening a notebook should fire activeNotebook event changed only once', async function () {
const openedEditor = asPromise(vscode.window.onDidChangeActiveNotebookEditor);
const resource = await createRandomFile('', undefined, '.vsctestnb');
await vscode.notebook.openNotebookDocument(resource);
assert.ok(await openedEditor);
});
// });

View file

@ -13,6 +13,22 @@
},
"contributes": {
"languages": [
{
"id": "dockercompose",
"aliases": [
"Compose",
"compose"
],
"filenamePatterns": [
"compose.yml",
"compose.yaml",
"compose.*.yml",
"compose.*.yaml",
"*docker*compose*.yml",
"*docker*compose*.yaml"
],
"configuration": "./language-configuration.json"
},
{
"id": "yaml",
"aliases": [
@ -30,6 +46,11 @@
}
],
"grammars": [
{
"language": "dockercompose",
"scopeName": "source.yaml",
"path": "./syntaxes/yaml.tmLanguage.json"
},
{
"language": "yaml",
"scopeName": "source.yaml",

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.55.0",
"distro": "17365b1560dc5051788f2ec8b388b44b67ec1365",
"distro": "0a442d49008152919d1ca15e5d21c03a1536c80d",
"author": {
"name": "Microsoft Corporation"
},

View file

@ -9,6 +9,7 @@ description: |
architectures:
- build-on: amd64
run-on: @@ARCHITECTURE@@
compression: lzo
grade: stable
confinement: classic

View file

@ -73,6 +73,7 @@
align-items: center;
justify-content: center;
color: inherit;
outline-offset: -2px;
}
.monaco-pane-view .pane > .pane-header .monaco-action-bar .action-item.select-container {

View file

@ -13,7 +13,7 @@ import * as processes from 'vs/base/node/processes';
* shell that the terminal uses by default.
* @param p The platform to detect the shell of.
*/
export async function getSystemShell(p: platform.Platform, env = process.env as platform.IProcessEnvironment): Promise<string> {
export async function getSystemShell(p: platform.Platform, env: platform.IProcessEnvironment): Promise<string> {
if (p === platform.Platform.Windows) {
if (platform.isWindows) {
return getSystemShellWindows();
@ -25,7 +25,7 @@ export async function getSystemShell(p: platform.Platform, env = process.env as
return getSystemShellUnixLike(p, env);
}
export function getSystemShellSync(p: platform.Platform, env = process.env as platform.IProcessEnvironment): string {
export function getSystemShellSync(p: platform.Platform, env: platform.IProcessEnvironment): string {
if (p === platform.Platform.Windows) {
if (platform.isWindows) {
return getSystemShellWindowsSync(env);

View file

@ -53,6 +53,7 @@ export interface IssueReporterConfiguration extends IWindowConfiguration {
commit: string | undefined;
date: string | undefined;
reportIssueUrl: string | undefined;
reportMarketplaceIssueUrl: string | undefined;
}
}
@ -83,6 +84,7 @@ export class IssueReporter extends Disposable {
super();
this.initServices(configuration);
console.log(configuration);
const targetExtension = configuration.data.extensionId ? configuration.data.enabledExtensions.find(extension => extension.id === configuration.data.extensionId) : undefined;
this.issueReporterModel = new IssueReporterModel({
@ -353,7 +355,8 @@ export class IssueReporter extends Disposable {
this.addEventListener('issue-title', 'input', (e: Event) => {
const title = (<HTMLInputElement>e.target).value;
const lengthValidationMessage = this.getElementById('issue-title-length-validation-error');
if (title && this.getIssueUrlWithTitle(title).length > MAX_URL_LENGTH) {
const issueUrl = this.getIssueUrl();
if (title && this.getIssueUrlWithTitle(title, issueUrl).length > MAX_URL_LENGTH) {
show(lengthValidationMessage);
} else {
hide(lengthValidationMessage);
@ -468,6 +471,10 @@ export class IssueReporter extends Disposable {
return true;
}
if (issueType === IssueType.Marketplace) {
return true;
}
return false;
}
@ -633,11 +640,18 @@ export class IssueReporter extends Disposable {
const typeSelect = this.getElementById('issue-type')! as HTMLSelectElement;
const { issueType } = this.issueReporterModel.getData();
reset(typeSelect,
makeOption(IssueType.Bug, localize('bugReporter', "Bug Report")),
makeOption(IssueType.FeatureRequest, localize('featureRequest', "Feature Request")),
makeOption(IssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue"))
);
this.configuration.product.reportMarketplaceIssueUrl
? reset(typeSelect,
makeOption(IssueType.Bug, localize('bugReporter', "Bug Report")),
makeOption(IssueType.FeatureRequest, localize('featureRequest', "Feature Request")),
makeOption(IssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue")),
makeOption(IssueType.Marketplace, localize('marketplaceIssue', "Extensions Marketplace Issue"))
)
: reset(typeSelect,
makeOption(IssueType.Bug, localize('bugReporter', "Bug Report")),
makeOption(IssueType.FeatureRequest, localize('featureRequest', "Feature Request")),
makeOption(IssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue")),
);
typeSelect.value = issueType.toString();
@ -751,6 +765,9 @@ export class IssueReporter extends Disposable {
if (fileOnExtension) {
show(extensionSelector);
}
} else if (issueType === IssueType.Marketplace) {
reset(descriptionTitle, localize('description', "Description"), $('span.required-input', undefined, '*'));
reset(descriptionSubtitle, localize('marketplaceDescription', "Please describe the feature you would like added to the marketplace, or steps to reliably reproduce the problem if you are reporting a bug. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub."));
}
}
@ -770,10 +787,14 @@ export class IssueReporter extends Disposable {
private validateInputs(): boolean {
let isValid = true;
['issue-title', 'description', 'issue-source'].forEach(elementId => {
['issue-title', 'description'].forEach(elementId => {
isValid = this.validateInput(elementId) && isValid;
});
if (this.issueReporterModel.getData().issueType !== IssueType.Marketplace) {
isValid = this.validateInput('issue-source') && isValid;
}
if (this.issueReporterModel.fileOnExtension()) {
isValid = this.validateInput('extension-selector') && isValid;
}
@ -845,13 +866,13 @@ export class IssueReporter extends Disposable {
const issueTitle = (<HTMLInputElement>this.getElementById('issue-title')).value;
const issueBody = this.issueReporterModel.serialize();
const issueUrl = this.issueReporterModel.fileOnExtension() ? this.getExtensionGitHubUrl() : this.configuration.product.reportIssueUrl!;
const issueUrl = this.getIssueUrl();
const gitHubDetails = this.parseGitHubUrl(issueUrl);
if (this.configuration.data.githubAccessToken && gitHubDetails) {
return this.submitToGitHub(issueTitle, issueBody, gitHubDetails);
}
const baseUrl = this.getIssueUrlWithTitle((<HTMLInputElement>this.getElementById('issue-title')).value);
const baseUrl = this.getIssueUrlWithTitle((<HTMLInputElement>this.getElementById('issue-title')).value, issueUrl);
let url = baseUrl + `&body=${encodeURIComponent(issueBody)}`;
if (url.length > MAX_URL_LENGTH) {
@ -881,6 +902,14 @@ export class IssueReporter extends Disposable {
});
}
private getIssueUrl(): string {
return this.issueReporterModel.fileOnExtension()
? this.getExtensionGitHubUrl()
: this.issueReporterModel.getData().issueType === IssueType.Marketplace
? this.configuration.product.reportMarketplaceIssueUrl!
: this.configuration.product.reportIssueUrl!;
}
private parseGitHubUrl(url: string): undefined | { repositoryName: string, owner: string } {
// Assumes a GitHub url to a particular repo, https://github.com/repositoryName/owner.
// Repository name and owner cannot contain '/'
@ -909,16 +938,12 @@ export class IssueReporter extends Disposable {
return repositoryUrl;
}
private getIssueUrlWithTitle(issueTitle: string): string {
let repositoryUrl = this.configuration.product.reportIssueUrl;
private getIssueUrlWithTitle(issueTitle: string, repositoryUrl: string): string {
if (this.issueReporterModel.fileOnExtension()) {
const extensionGitHubUrl = this.getExtensionGitHubUrl();
if (extensionGitHubUrl) {
repositoryUrl = extensionGitHubUrl + '/issues/new';
}
repositoryUrl = repositoryUrl + '/issues/new';
}
const queryStringPrefix = this.configuration.product.reportIssueUrl && this.configuration.product.reportIssueUrl.indexOf('?') === -1 ? '?' : '&';
const queryStringPrefix = repositoryUrl.indexOf('?') === -1 ? '?' : '&';
return `${repositoryUrl}${queryStringPrefix}title=${encodeURIComponent(issueTitle)}`;
}
@ -1156,7 +1181,7 @@ export class IssueReporter extends Disposable {
),
...extensions.map(extension => $('tr', undefined,
$('td', undefined, extension.name),
$('td', undefined, extension.publisher.substr(0, 3)),
$('td', undefined, extension.publisher?.substr(0, 3) ?? 'N/A'),
$('td', undefined, extension.version),
))
);

View file

@ -238,7 +238,7 @@ ${this._data.experimentInfo}
const tableHeader = `Extension|Author (truncated)|Version
---|---|---`;
const table = this._data.enabledNonThemeExtesions.map(e => {
return `${e.name}|${e.publisher.substr(0, 3)}|${e.version}`;
return `${e.name}|${e.publisher?.substr(0, 3) ?? 'N/A'}|${e.version}`;
}).join('\n');
return `<details><summary>Extensions (${this._data.enabledNonThemeExtesions.length})</summary>

View file

@ -121,9 +121,9 @@ export class RangeUtil {
startChildIndex = Math.min(max, Math.max(min, startChildIndex));
endChildIndex = Math.min(max, Math.max(min, endChildIndex));
if (startChildIndex === endChildIndex && startOffset === endOffset && startOffset === 0) {
if (startChildIndex === endChildIndex && startOffset === endOffset && startOffset === 0 && !domNode.children[startChildIndex].firstChild) {
// We must find the position at the beginning of a <span>
// To cover cases of empty <span>s, aboid using a range and use the <span>'s bounding box
// To cover cases of empty <span>s, avoid using a range and use the <span>'s bounding box
const clientRects = domNode.children[startChildIndex].getClientRects();
return this._createHorizontalRangesFromClientRects(clientRects, clientRectDeltaLeft);
}

View file

@ -90,10 +90,10 @@ class CodeLensContentWidget implements IContentWidget {
if (lens.command) {
const title = renderLabelWithIcons(lens.command.title.trim());
if (lens.command.id) {
children.push(dom.$('a', { id: String(i) }, ...title));
children.push(dom.$('a', { id: String(i), title: lens.command.tooltip }, ...title));
this._commands.set(String(i), lens.command);
} else {
children.push(dom.$('span', undefined, ...title));
children.push(dom.$('span', { title: lens.command.tooltip }, ...title));
}
if (i + 1 < lenses.length) {
children.push(dom.$('span', undefined, '\u00a0|\u00a0'));

View file

@ -79,7 +79,7 @@ async function doResolveUnixShellEnv(logService: ILogService): Promise<typeof pr
logService.trace('getUnixShellEnvironment#env', env);
logService.trace('getUnixShellEnvironment#spawn', command);
const systemShellUnix = await getSystemShell(platform);
const systemShellUnix = await getSystemShell(platform, env);
const child = spawn(systemShellUnix, ['-ilc', command], {
detached: true,
stdio: ['ignore', 'pipe', process.stderr],

View file

@ -17,7 +17,8 @@ export interface WindowData {
export const enum IssueType {
Bug,
PerformanceIssue,
FeatureRequest
FeatureRequest,
Marketplace
}
export interface IssueReporterStyles extends WindowStyles {
@ -40,7 +41,7 @@ export interface IssueReporterStyles extends WindowStyles {
export interface IssueReporterExtensionData {
name: string;
publisher: string;
publisher: string | undefined;
version: string;
id: string;
isTheme: boolean;

View file

@ -414,7 +414,8 @@ export class IssueMainService implements ICommonIssueService {
version: !!product.darwinUniversalAssetId ? `${product.version} (Universal)` : product.version,
commit: product.commit,
date: product.date,
reportIssueUrl: product.reportIssueUrl
reportIssueUrl: product.reportIssueUrl,
reportMarketplaceIssueUrl: product.reportMarketplaceIssueUrl
}
};

View file

@ -107,6 +107,7 @@ export interface IProductConfiguration {
readonly twitterUrl?: string;
readonly requestFeatureUrl?: string;
readonly reportIssueUrl?: string;
readonly reportMarketplaceIssueUrl?: string;
readonly licenseUrl?: string;
readonly privacyStatementUrl?: string;
readonly telemetryOptOutUrl?: string;

View file

@ -27,7 +27,7 @@ const WRITE_INTERVAL_MS = 5;
const enum ShutdownConstants {
/**
* The amount of time that must pass between data events after exit is queued before the actual
* The amount of ms that must pass between data events after exit is queued before the actual
* kill call is triggered. This data flush mechanism works around an [issue in node-pty][1]
* where not all data is flushed which causes problems for task problem matchers. Additionally
* on Windows under conpty, killing a process while data is being output will cause the [conhost
@ -39,7 +39,7 @@ const enum ShutdownConstants {
*/
DataFlushTimeout = 250,
/**
* The maximum time to allow after dispose is called because forcefully killing the process.
* The maximum ms to allow after dispose is called because forcefully killing the process.
*/
MaximumShutdownTime = 5000
}

View file

@ -700,7 +700,7 @@ declare module 'vscode' {
export interface InlineValueContext {
/**
* Debug Adapter Protocol ID of the the stack frame.
* The stack frame (as a DAP Id) where the execution has stopped.
*/
readonly frameId: number;
@ -1555,27 +1555,27 @@ declare module 'vscode' {
readonly backupId?: string;
}
// todo@API use openNotebookDOCUMENT to align with openCustomDocument etc?
export interface NotebookContentProvider {
readonly options?: NotebookDocumentContentOptions;
readonly onDidChangeNotebookContentOptions?: Event<NotebookDocumentContentOptions>;
// todo@API remove! against separation of data provider and renderer
// eslint-disable-next-line vscode-dts-cancellation
resolveNotebook(document: NotebookDocument, webview: NotebookCommunication): Thenable<void>;
/**
* Content providers should always use [file system providers](#FileSystemProvider) to
* resolve the raw content for `uri` as the resouce is not necessarily a file on disk.
*/
// eslint-disable-next-line vscode-dts-provider-naming
openNotebook(uri: Uri, openContext: NotebookDocumentOpenContext): NotebookData | Thenable<NotebookData>;
// eslint-disable-next-line vscode-dts-provider-naming
// eslint-disable-next-line vscode-dts-cancellation
resolveNotebook(document: NotebookDocument, webview: NotebookCommunication): Thenable<void>;
// eslint-disable-next-line vscode-dts-provider-naming
saveNotebook(document: NotebookDocument, cancellation: CancellationToken): Thenable<void>;
// eslint-disable-next-line vscode-dts-provider-naming
saveNotebookAs(targetResource: Uri, document: NotebookDocument, cancellation: CancellationToken): Thenable<void>;
// eslint-disable-next-line vscode-dts-provider-naming
backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, cancellation: CancellationToken): Thenable<NotebookDocumentBackup>;
openNotebook(uri: Uri, openContext: NotebookDocumentOpenContext, token: CancellationToken): NotebookData | Thenable<NotebookData>;
// ???
// provideKernels(document: NotebookDocument, token: CancellationToken): ProviderResult<T[]>;
saveNotebook(document: NotebookDocument, token: CancellationToken): Thenable<void>;
saveNotebookAs(targetResource: Uri, document: NotebookDocument, token: CancellationToken): Thenable<void>;
backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, token: CancellationToken): Thenable<NotebookDocumentBackup>;
}
export namespace notebook {

View file

@ -403,8 +403,8 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
contentOptions.transientOutputs = newOptions.transientOutputs;
},
viewOptions: options.viewOptions,
openNotebook: async (viewType: string, uri: URI, backupId?: string) => {
const data = await this._proxy.$openNotebook(viewType, uri, backupId);
openNotebook: async (viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken) => {
const data = await this._proxy.$openNotebook(viewType, uri, backupId, token);
return {
data,
transientOptions: contentOptions

View file

@ -1863,7 +1863,7 @@ export interface ExtHostNotebookShape {
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
$cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
$openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto>;
$openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, token: CancellationToken): Promise<NotebookDataDto>;
$saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean>;
$saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean>;
$backupNotebook(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string>;

View file

@ -114,7 +114,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
this._proxy.$sendDidChangeSessions(id, {
added: e.added ?? [],
changed: e.changed ?? [],
removed: e.changed ?? []
removed: e.removed ?? []
});
});

View file

@ -497,9 +497,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
// --- open, save, saveAs, backup
async $openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto> {
async $openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, token: CancellationToken): Promise<NotebookDataDto> {
const { provider } = this._getProviderData(viewType);
const data = await provider.openNotebook(URI.revive(uri), { backupId });
const data = await provider.openNotebook(URI.revive(uri), { backupId }, token);
return {
metadata: {
...notebookDocumentMetadataDefaults,
@ -776,7 +776,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
} else if (delta.newActiveEditor) {
this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor;
}
this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor?.editor);
if (delta.newActiveEditor !== undefined) {
this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor?.editor);
}
}
createNotebookCellStatusBarItemInternal(cell: vscode.NotebookCell, alignment: extHostTypes.NotebookCellStatusBarAlignment | undefined, priority: number | undefined) {

View file

@ -41,7 +41,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
// Getting the SystemShell is an async operation, however, the ExtHost terminal service is mostly synchronous
// and the API `vscode.env.shell` is also synchronous. The default shell _should_ be set when extensions are
// starting up but if not, we run getSystemShellSync below which gets a sane default.
getSystemShell(platform.platform).then(s => this._defaultShell = s);
getSystemShell(platform.platform, process.env as platform.IProcessEnvironment).then(s => this._defaultShell = s);
this._updateLastActiveWorkspace();
this._updateVariableResolver();
@ -83,7 +83,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
return terminalEnvironment.getDefaultShell(
fetchSetting,
this._isWorkspaceShellAllowed,
this._defaultShell ?? getSystemShellSync(platform.platform),
this._defaultShell ?? getSystemShellSync(platform.platform, process.env as platform.IProcessEnvironment),
process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'),
process.env.windir,
terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, this._variableResolver),

View file

@ -384,12 +384,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyCode.F5,
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing))),
handler: async (accessor: ServicesAccessor, debugStartOptions?: { noDebug: boolean }) => {
handler: async (accessor: ServicesAccessor, debugStartOptions: { config?: Partial<IConfig>; noDebug?: boolean } = {}) => {
const debugService = accessor.get(IDebugService);
let { launch, name, getConfig } = debugService.getConfigurationManager().selectedConfiguration;
const config = await getConfig();
const clonedConfig = deepClone(config);
await debugService.startDebugging(launch, clonedConfig || name, { noDebug: debugStartOptions && debugStartOptions.noDebug });
const configOrName = config ? Object.assign(deepClone(config), debugStartOptions.config) : name;
await debugService.startDebugging(launch, configOrName, { noDebug: debugStartOptions.noDebug });
}
});

View file

@ -374,6 +374,10 @@ export class ConfigurationManager implements IConfigurationManager {
let type = config?.type;
if (name && names.indexOf(name) >= 0) {
this.setSelectedLaunchName(name);
if (!config && name && launch) {
config = launch.getConfiguration(name);
type = config?.type;
}
} else if (dynamicConfig && dynamicConfig.type) {
// We could not find the previously used name and config is not passed. We should get all dynamic configurations from providers
// And potentially auto select the previously used dynamic configuration #96293

View file

@ -852,14 +852,12 @@ registerAction2(class extends ViewAction<Repl> {
const stopppedChildSession = debugService.getModel().getSessions().find(s => s.parentSession === session && s.state === State.Stopped);
if (stopppedChildSession) {
session = stopppedChildSession;
} else {
await view.selectSession(session);
}
}
await debugService.focusStackFrame(undefined, undefined, session, true);
} else {
await view.selectSession(session);
}
// Need to select the session in the view since the focussed session might not have changed
await view.selectSession(session);
}
});

View file

@ -272,7 +272,7 @@ export class ExtensionsTree extends WorkbenchAsyncDataTree<IExtensionData, IExte
accessibilityProvider: <IListAccessibilityProvider<IExtensionData>>{
getAriaLabel(extensionData: IExtensionData): string {
const extension = extensionData.extension;
return localize('extension-arialabel', "{0}, {1}, {2}, {3}", extension.displayName, extension.version, extension.publisherDisplayName, extension.description);
return localize('extension.arialabel', "{0}, {1}, {2}, {3}", extension.displayName, extension.version, extension.publisherDisplayName, extension.description);
},
getWidgetAriaLabel(): string {
return localize('extensions', "Extensions");

View file

@ -165,7 +165,7 @@ export class ExtensionsListView extends ViewPane {
horizontalScrolling: false,
accessibilityProvider: <IListAccessibilityProvider<IExtension | null>>{
getAriaLabel(extension: IExtension | null): string {
return extension ? localize('extension-arialabel', "{0}, {1}, {2}, {3}", extension.displayName, extension.version, extension.publisherDisplayName, extension.description) : '';
return extension ? localize('extension.arialabel', "{0}, {1}, {2}, {3}", extension.displayName, extension.version, extension.publisherDisplayName, extension.description) : '';
},
getWidgetAriaLabel(): string {
return localize('extensions', "Extensions");

View file

@ -109,7 +109,7 @@ export class OutputElement extends Disposable {
if (result.type !== RenderOutputType.Mainframe) {
// this.viewCell.selfSizeMonitoring = true;
this._notebookEditor.createInset(
this._notebookEditor.createOutput(
this._diffElementViewModel,
this._nestedCell,
result,

View file

@ -33,7 +33,7 @@ export interface INotebookTextDiffEditor extends ICommonNotebookEditor {
getLayoutInfo(): NotebookLayoutInfo;
layoutNotebookCell(cell: DiffElementViewModelBase, height: number): void;
getOutputRenderer(): OutputRenderer;
createInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: IInsetRenderOutput, getOffset: () => number, diffSide: DiffSide): void;
createOutput(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: IInsetRenderOutput, getOffset: () => number, diffSide: DiffSide): void;
showInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, displayOutput: ICellOutputViewModel, diffSide: DiffSide): void;
removeInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: ICellOutputViewModel, diffSide: DiffSide): void;
hideInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: ICellOutputViewModel): void;

View file

@ -590,7 +590,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
this._list.triggerScrollFromMouseWheelEvent(event);
}
createInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, output: IInsetRenderOutput, getOffset: () => number, diffSide: DiffSide): void {
createOutput(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, output: IInsetRenderOutput, getOffset: () => number, diffSide: DiffSide): void {
this._insetModifyQueueByOutputId.queue(output.source.model.outputId + (diffSide === DiffSide.Modified ? '-right' : 'left'), async () => {
const activeWebview = diffSide === DiffSide.Modified ? this._modifiedWebview : this._originalWebview;
if (!activeWebview) {
@ -599,7 +599,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
if (!activeWebview.insetMapping.has(output.source)) {
const cellTop = this._list.getAbsoluteTopOfElement(cellDiffViewModel);
await activeWebview.createInset({ diffElement: cellDiffViewModel, cellHandle: cellViewModel.handle, cellId: cellViewModel.id, cellUri: cellViewModel.uri }, output, cellTop, getOffset());
await activeWebview.createOutput({ diffElement: cellDiffViewModel, cellHandle: cellViewModel.handle, cellId: cellViewModel.id, cellUri: cellViewModel.uri }, output, cellTop, getOffset());
} else {
const cellTop = this._list.getAbsoluteTopOfElement(cellDiffViewModel);
const scrollTop = this._list.scrollTop;

View file

@ -479,7 +479,7 @@ export interface INotebookEditor extends ICommonNotebookEditor {
/**
* Render the output in webview layer
*/
createInset(cell: ICellViewModel, output: IInsetRenderOutput, offset: number): Promise<void>;
createOutput(cell: ICellViewModel, output: IInsetRenderOutput, offset: number): Promise<void>;
/**
* Remove the output from the webview layer
@ -748,7 +748,8 @@ export enum CellRevealType {
export enum CellRevealPosition {
Top,
Center
Center,
Bottom
}
export enum CellEditState {

View file

@ -1693,7 +1693,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
await this._webview?.updateMarkdownPreviewSelectionState(cell.id, isSelected);
}
async createInset(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number): Promise<void> {
async createOutput(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number): Promise<void> {
this._insetModifyQueueByOutputId.queue(output.source.model.outputId, async () => {
if (!this._webview) {
return;
@ -1703,7 +1703,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
if (!this._webview!.insetMapping.has(output.source)) {
const cellTop = this._list.getAbsoluteTopOfElement(cell);
await this._webview!.createInset({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri }, output, cellTop, offset);
await this._webview!.createOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri }, output, cellTop, offset);
} else {
const cellTop = this._list.getAbsoluteTopOfElement(cell);
const scrollTop = this._list.scrollTop;

View file

@ -512,12 +512,12 @@ export class NotebookService extends Disposable implements INotebookService, IEd
return result;
}
async fetchNotebookRawData(viewType: string, uri: URI, backupId?: string): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions }> {
async fetchNotebookRawData(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions }> {
if (!await this.canResolve(viewType)) {
throw new Error(`CANNOT fetch notebook data, there is NO provider for '${viewType}'`);
}
const provider = this._withProvider(viewType)!;
return await provider.controller.openNotebook(viewType, uri, backupId);
return await provider.controller.openNotebook(viewType, uri, backupId, token);
}
async save(viewType: string, resource: URI, token: CancellationToken): Promise<boolean> {

View file

@ -24,6 +24,7 @@ import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/
import { diff, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, ICellRange, NOTEBOOK_EDITOR_CURSOR_BEGIN_END, cellRangesToIndexes, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { clamp } from 'vs/base/common/numbers';
import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants';
import { ISplice } from 'vs/base/common/sequence';
export interface IFocusNextPreviousDelegate {
onFocusNext(applyFocusNext: () => void): void;
@ -313,53 +314,14 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
});
if (e.synchronous) {
viewDiffs.reverse().forEach((diff) => {
// remove output in the webview
const hideOutputs: ICellOutputViewModel[] = [];
const deletedOutputs: ICellOutputViewModel[] = [];
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
const cell = this.element(i);
if (cell.cellKind === CellKind.Code) {
if (this._viewModel!.hasCell(cell.handle)) {
hideOutputs.push(...cell?.outputsViewModels);
} else {
deletedOutputs.push(...cell?.outputsViewModels);
}
}
}
this.splice2(diff.start, diff.deleteCount, diff.toInsert);
hideOutputs.forEach(output => this._onDidHideOutput.fire(output));
deletedOutputs.forEach(output => this._onDidRemoveOutput.fire(output));
});
this._updateElementsInWebview(viewDiffs);
} else {
this._viewModelStore.add(DOM.scheduleAtNextAnimationFrame(() => {
if (this._isDisposed) {
return;
}
viewDiffs.reverse().forEach((diff) => {
const hideOutputs: ICellOutputViewModel[] = [];
const deletedOutputs: ICellOutputViewModel[] = [];
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
const cell = this.element(i);
if (cell.cellKind === CellKind.Code) {
if (this._viewModel!.hasCell(cell.handle)) {
hideOutputs.push(...cell?.outputsViewModels);
} else {
deletedOutputs.push(...cell?.outputsViewModels);
}
}
}
this.splice2(diff.start, diff.deleteCount, diff.toInsert);
hideOutputs.forEach(output => this._onDidHideOutput.fire(output));
deletedOutputs.forEach(output => this._onDidRemoveOutput.fire(output));
});
this._updateElementsInWebview(viewDiffs);
}));
}
}));
@ -393,6 +355,33 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
this.splice2(0, 0, viewCells);
}
private _updateElementsInWebview(viewDiffs: ISplice<CellViewModel>[]) {
viewDiffs.reverse().forEach((diff) => {
const hideOutputs: ICellOutputViewModel[] = [];
const deletedOutputs: ICellOutputViewModel[] = [];
const removedMarkdownCells: ICellViewModel[] = [];
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
const cell = this.element(i);
if (cell.cellKind === CellKind.Code) {
if (this._viewModel!.hasCell(cell.handle)) {
hideOutputs.push(...cell?.outputsViewModels);
} else {
deletedOutputs.push(...cell?.outputsViewModels);
}
} else {
removedMarkdownCells.push(cell);
}
}
this.splice2(diff.start, diff.deleteCount, diff.toInsert);
hideOutputs.forEach(output => this._onDidHideOutput.fire(output));
deletedOutputs.forEach(output => this._onDidRemoveOutput.fire(output));
removedMarkdownCells.forEach(cell => this._onDidRemoveCellFromView.fire(cell));
});
}
clear() {
super.splice(0, this.length);
}
@ -473,31 +462,7 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
return oldViewCellMapping.has(a.uri.toString());
});
viewDiffs.reverse().forEach((diff) => {
// remove output in the webview
const hideOutputs: ICellOutputViewModel[] = [];
const deletedOutputs: ICellOutputViewModel[] = [];
const removedMarkdownCells: ICellViewModel[] = [];
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
const cell = this.element(i);
if (cell.cellKind === CellKind.Code) {
if (this._viewModel!.hasCell(cell.handle)) {
hideOutputs.push(...cell?.outputsViewModels);
} else {
deletedOutputs.push(...cell?.outputsViewModels);
}
} else {
removedMarkdownCells.push(cell);
}
}
this.splice2(diff.start, diff.deleteCount, diff.toInsert);
hideOutputs.forEach(output => this._onDidHideOutput.fire(output));
deletedOutputs.forEach(output => this._onDidRemoveOutput.fire(output));
removedMarkdownCells.forEach(cell => this._onDidRemoveCellFromView.fire(cell));
});
this._updateElementsInWebview(viewDiffs);
}
splice2(start: number, deleteCount: number, elements: CellViewModel[] = []): void {
@ -698,7 +663,7 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
return;
}
const endIndex = this._getViewIndexUpperBound2(range.end);
const endIndex = this._getViewIndexUpperBound2(range.end - 1);
const scrollTop = this.getViewScrollTop();
const wrapperBottom = this.getViewScrollBottom();
@ -1074,18 +1039,31 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
}
}
// first render
const viewItemOffset = revealPosition === CellRevealPosition.Top ? elementTop : (elementTop - this.view.renderHeight / 2);
this.view.setScrollTop(viewItemOffset);
// second scroll as markdown cell is dynamic
const newElementTop = this.view.elementTop(viewIndex);
const newViewItemOffset = revealPosition === CellRevealPosition.Top ? newElementTop : (newElementTop - this.view.renderHeight / 2);
this.view.setScrollTop(newViewItemOffset);
switch (revealPosition) {
case CellRevealPosition.Top:
this.view.setScrollTop(elementTop);
this.view.setScrollTop(this.view.elementTop(viewIndex));
break;
case CellRevealPosition.Center:
this.view.setScrollTop(elementTop - this.view.renderHeight / 2);
this.view.setScrollTop(this.view.elementTop(viewIndex) - this.view.renderHeight / 2);
break;
case CellRevealPosition.Bottom:
this.view.setScrollTop(elementBottom - this.view.renderHeight);
this.view.setScrollTop(this.view.elementTop(viewIndex) + this.view.elementHeight(viewIndex) - this.view.renderHeight);
break;
default:
break;
}
}
private _revealInView(viewIndex: number) {
this._revealInternal(viewIndex, true, CellRevealPosition.Top);
const firstIndex = this.view.firstVisibleIndex;
if (viewIndex < firstIndex) {
this._revealInternal(viewIndex, true, CellRevealPosition.Top);
} else {
this._revealInternal(viewIndex, true, CellRevealPosition.Bottom);
}
}
private _revealInCenter(viewIndex: number) {

View file

@ -1163,7 +1163,7 @@ var requirejs = (function() {
await p;
}
async createInset(cellInfo: T, content: IInsetRenderOutput, cellTop: number, offset: number) {
async createOutput(cellInfo: T, content: IInsetRenderOutput, cellTop: number, offset: number) {
if (this._disposed) {
return;
}

View file

@ -150,7 +150,7 @@ export class CellOutputElement extends Disposable {
}
if (renderResult.type !== RenderOutputType.Mainframe) {
this.notebookEditor.createInset(this.viewCell, renderResult, this.viewCell.getOutputOffset(index));
this.notebookEditor.createOutput(this.viewCell, renderResult, this.viewCell.getOutputOffset(index));
this.domNode.classList.add('background');
} else {
this.domNode.classList.add('foreground', 'output-element');
@ -388,7 +388,7 @@ export class CellOutputContainer extends Disposable {
const renderedOutput = this.outputEntries.get(currOutput);
if (renderedOutput && renderedOutput.renderResult) {
if (renderedOutput.renderResult.type !== RenderOutputType.Mainframe) {
this.notebookEditor.createInset(this.viewCell, renderedOutput.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index));
this.notebookEditor.createOutput(this.viewCell, renderedOutput.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index));
} else {
this.viewCell.updateOutputHeight(index, renderedOutput.domClientHeight);
}

View file

@ -276,7 +276,6 @@ export class StatefulMarkdownCell extends Disposable {
}
dispose() {
this.notebookEditor.removeMarkdownPreview(this.viewCell);
this.localDisposables.dispose();
this.viewCell.detachTextEditor();
super.dispose();

View file

@ -170,7 +170,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM
private async _loadFromProvider(backupId: string | undefined): Promise<void> {
const data = await this._notebookService.fetchNotebookRawData(this.viewType, this.resource, backupId);
const data = await this._notebookService.fetchNotebookRawData(this.viewType, this.resource, backupId, CancellationToken.None);
this._lastResolvedFileStat = await this._resolveStats(this.resource);
if (this.isDisposed()) {

View file

@ -22,9 +22,10 @@ export const INotebookService = createDecorator<INotebookService>('notebookServi
export interface IMainNotebookController {
viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; };
options: { transientOutputs: boolean; transientMetadata: TransientMetadata; };
openNotebook(viewType: string, uri: URI, backupId?: string): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>;
resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise<void>;
onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: any): void;
openNotebook(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken): Promise<{ data: NotebookDataDto, transientOptions: TransientOptions; }>;
save(uri: URI, token: CancellationToken): Promise<boolean>;
saveAs(uri: URI, target: URI, token: CancellationToken): Promise<boolean>;
backup(uri: URI, token: CancellationToken): Promise<string>;
@ -66,7 +67,7 @@ export interface INotebookService {
getNotebookProviderResourceRoots(): URI[];
destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void;
fetchNotebookRawData(viewType: string, uri: URI, backupId?: string): Promise<INotebookRawData>;
fetchNotebookRawData(viewType: string, uri: URI, backupId: string | undefined, token: CancellationToken): Promise<INotebookRawData>;
save(viewType: string, resource: URI, token: CancellationToken): Promise<boolean>;
saveAs(viewType: string, resource: URI, target: URI, token: CancellationToken): Promise<boolean>;
backup(viewType: string, uri: URI, token: CancellationToken): Promise<string | undefined>;

View file

@ -309,7 +309,7 @@ export class TestNotebookEditor implements INotebookEditor {
// throw new Error('Method not implemented.');
return;
}
createInset(cell: CellViewModel, output: IInsetRenderOutput, offset: number): Promise<void> {
createOutput(cell: CellViewModel, output: IInsetRenderOutput, offset: number): Promise<void> {
return Promise.resolve();
}
createMarkdownPreview(cell: ICellViewModel): Promise<void> {

View file

@ -303,10 +303,6 @@
box-sizing: border-box;
}
.monaco-workbench .search-view a.prominent {
text-decoration: underline;
}
/* Theming */
.monaco-workbench.vs .search-view .search-widget .toggle-replace-button:hover {

View file

@ -255,6 +255,7 @@ export class ExcludePatternInputWidget extends PatternInputWidget {
setUseExcludesAndIgnoreFiles(value: boolean) {
this.useExcludesAndIgnoreFilesBox.checked = value;
this._onChangeIgnoreBoxEmitter.fire();
}
protected getSubcontrolsWidth(): number {

View file

@ -17,7 +17,7 @@ import * as errors from 'vs/base/common/errors';
import { Event } from 'vs/base/common/event';
import { Iterable } from 'vs/base/common/iterator';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
import * as env from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
@ -45,7 +45,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IProgress, IProgressService, IProgressStep } from 'vs/platform/progress/common/progress';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, editorFindMatchHighlight, editorFindMatchHighlightBorder, foreground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry';
import { diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, editorFindMatchHighlight, editorFindMatchHighlightBorder, foreground, listActiveSelectionForeground, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
@ -118,7 +118,7 @@ export class SearchView extends ViewPane {
private treeLabels!: ResourceLabels;
private viewletState: MementoObject;
private messagesElement!: HTMLElement;
private messageDisposables: IDisposable[] = [];
private readonly messageDisposables: DisposableStore = new DisposableStore();
private searchWidgetsContainerElement!: HTMLElement;
private searchWidget!: SearchWidget;
private size!: dom.Dimension;
@ -680,8 +680,7 @@ export class SearchView extends ViewPane {
const wasHidden = this.messagesElement.style.display === 'none';
dom.clearNode(this.messagesElement);
dom.show(this.messagesElement);
dispose(this.messageDisposables);
this.messageDisposables = [];
this.messageDisposables.clear();
const newMessage = dom.append(this.messagesElement, $('.message'));
if (wasHidden) {
@ -1478,32 +1477,23 @@ export class SearchView extends ViewPane {
const p = dom.append(messageEl, $('p', undefined, message));
if (!completed) {
const searchAgainLink = dom.append(p, $('a.pointer.prominent', undefined, nls.localize('rerunSearch.message', "Search again")));
this.messageDisposables.push(dom.addDisposableListener(searchAgainLink, dom.EventType.CLICK, (e: MouseEvent) => {
dom.EventHelper.stop(e, false);
this.triggerQueryChange({ preserveFocus: false });
}));
const searchAgainButton = this.messageDisposables.add(new SearchLinkButton(
nls.localize('rerunSearch.message', "Search again"),
() => this.triggerQueryChange({ preserveFocus: false })));
dom.append(p, searchAgainButton.element);
} else if (hasIncludes || hasExcludes) {
const searchAgainLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('rerunSearchInAll.message', "Search again in all files")));
this.messageDisposables.push(dom.addDisposableListener(searchAgainLink, dom.EventType.CLICK, (e: MouseEvent) => {
dom.EventHelper.stop(e, false);
this.inputPatternExcludes.setValue('');
this.inputPatternIncludes.setValue('');
this.inputPatternIncludes.setOnlySearchInOpenEditors(false);
this.triggerQueryChange({ preserveFocus: false });
}));
const searchAgainButton = this.messageDisposables.add(new SearchLinkButton(nls.localize('rerunSearchInAll.message', "Search again in all files"), this.onSearchAgain.bind(this)));
dom.append(p, searchAgainButton.element);
} else {
const openSettingsLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('openSettings.message', "Open Settings")));
this.addClickEvents(openSettingsLink, this.onOpenSettings);
const openSettingsButton = this.messageDisposables.add(new SearchLinkButton(nls.localize('openSettings.message', "Open Settings"), this.onOpenSettings.bind(this)));
dom.append(p, openSettingsButton.element);
}
if (completed) {
dom.append(p, $('span', undefined, ' - '));
const learnMoreLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('openSettings.learnMore', "Learn More")));
this.addClickEvents(learnMoreLink, this.onLearnMore);
const learnMoreButton = this.messageDisposables.add(new SearchLinkButton(nls.localize('openSettings.learnMore', "Learn More"), this.onLearnMore.bind(this)));
dom.append(p, learnMoreButton.element);
}
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
@ -1555,30 +1545,10 @@ export class SearchView extends ViewPane {
.then(onComplete, onError);
}
private addClickEvents = (element: HTMLElement, handler: (event: any) => void): void => {
this.messageDisposables.push(dom.addDisposableListener(element, dom.EventType.CLICK, handler));
this.messageDisposables.push(dom.addDisposableListener(element, dom.EventType.KEY_DOWN, e => {
const event = new StandardKeyboardEvent(e);
let eventHandled = true;
if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) {
handler(e);
} else {
eventHandled = false;
}
if (eventHandled) {
event.preventDefault();
event.stopPropagation();
}
}));
};
private onOpenSettings = (e: dom.EventLike): void => {
private onOpenSettings(e: dom.EventLike): void {
dom.EventHelper.stop(e, false);
this.openSettings('.exclude');
};
this.openSettings('@id:files.exclude,search.exclude,search.useGlobalIgnoreFiles,search.useIgnoreFiles');
}
private openSettings(query: string): Promise<IEditorPane | undefined> {
const options: ISettingsEditorOptions = { query };
@ -1587,11 +1557,22 @@ export class SearchView extends ViewPane {
this.preferencesService.openGlobalSettings(undefined, options);
}
private onLearnMore = (e: MouseEvent): void => {
dom.EventHelper.stop(e, false);
private onLearnMore(): void {
this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=853977'));
};
}
private onSearchAgain(): void {
this.inputPatternExcludes.setValue('');
this.inputPatternIncludes.setValue('');
this.inputPatternIncludes.setOnlySearchInOpenEditors(false);
this.triggerQueryChange({ preserveFocus: false });
}
private onEnableExcludes(): void {
this.toggleQueryDetails(false, true);
this.searchExcludePattern.setUseExcludesAndIgnoreFiles(true);
}
private updateSearchResultCount(disregardExcludesAndIgnores?: boolean): void {
const fileCount = this.viewModel.searchResult.fileCount();
@ -1600,26 +1581,27 @@ export class SearchView extends ViewPane {
const msgWasHidden = this.messagesElement.style.display === 'none';
const messageEl = this.clearMessage();
let resultMsg = this.buildResultCountMessage(this.viewModel.searchResult.count(), fileCount);
const resultMsg = this.buildResultCountMessage(this.viewModel.searchResult.count(), fileCount);
this.tree.ariaLabel = resultMsg + nls.localize('forTerm', " - Search: {0}", this.searchResult.query?.contentPattern.pattern ?? '');
dom.append(messageEl, resultMsg);
if (fileCount > 0) {
if (disregardExcludesAndIgnores) {
resultMsg += nls.localize('useIgnoresAndExcludesDisabled', " - exclude settings and ignore files are disabled");
const excludesDisabledMessage = ' - ' + nls.localize('useIgnoresAndExcludesDisabled', "exclude settings and ignore files are disabled") + ' ';
const enableExcludesButton = this.messageDisposables.add(new SearchLinkButton(nls.localize('excludes.enable', "enable"), this.onEnableExcludes.bind(this), nls.localize('useExcludesAndIgnoreFilesDescription', "Use Exclude Settings and Ignore Files")));
dom.append(messageEl, $('span', undefined, excludesDisabledMessage, '(', enableExcludesButton.element, ')'));
}
dom.append(messageEl, $('span', undefined, resultMsg + ' - '));
const span = dom.append(messageEl, $('span'));
const openInEditorLink = dom.append(span, $('a.pointer.prominent', undefined, nls.localize('openInEditor.message', "Open in editor")));
dom.append(messageEl, ' - ');
openInEditorLink.title = appendKeyBindingLabel(
const openInEditorTooltip = appendKeyBindingLabel(
nls.localize('openInEditor.tooltip', "Copy current search results to an editor"),
this.keybindingService.lookupKeybinding(Constants.OpenInEditorCommandId), this.keybindingService);
this.messageDisposables.push(dom.addDisposableListener(openInEditorLink, dom.EventType.CLICK, (e: MouseEvent) => {
dom.EventHelper.stop(e, false);
this.instantiationService.invokeFunction(createEditorFromSearchResult, this.searchResult, this.searchIncludePattern.getValue(), this.searchExcludePattern.getValue(), this.searchIncludePattern.onlySearchInOpenEditors());
}));
const openInEditorButton = this.messageDisposables.add(new SearchLinkButton(
nls.localize('openInEditor.message', "Open in editor"),
() => this.instantiationService.invokeFunction(createEditorFromSearchResult, this.searchResult, this.searchIncludePattern.getValue(), this.searchExcludePattern.getValue(), this.searchIncludePattern.onlySearchInOpenEditors()),
openInEditorTooltip));
dom.append(messageEl, openInEditorButton.element);
this.reLayout();
} else if (!msgWasHidden) {
@ -1645,24 +1627,22 @@ export class SearchView extends ViewPane {
const textEl = dom.append(this.searchWithoutFolderMessageElement,
$('p', undefined, nls.localize('searchWithoutFolder', "You have not opened or specified a folder. Only open files are currently searched - ")));
const openFolderLink = dom.append(textEl,
$('a.pointer.prominent', { tabindex: 0 }, nls.localize('openFolder', "Open Folder")));
const actionRunner = this.messageDisposables.add(new ActionRunner());
const openFolderButton = this.messageDisposables.add(new SearchLinkButton(
nls.localize('openFolder', "Open Folder"),
() => {
const action = env.isMacintosh ?
this.instantiationService.createInstance(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL) :
this.instantiationService.createInstance(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL);
const actionRunner = new ActionRunner();
this.messageDisposables.push(dom.addDisposableListener(openFolderLink, dom.EventType.CLICK, (e: MouseEvent) => {
dom.EventHelper.stop(e, false);
const action = env.isMacintosh ?
this.instantiationService.createInstance(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL) :
this.instantiationService.createInstance(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL);
actionRunner.run(action).then(() => {
action.dispose();
}, err => {
action.dispose();
errors.onUnexpectedError(err);
});
}));
actionRunner.run(action).then(() => {
action.dispose();
}, err => {
action.dispose();
errors.onUnexpectedError(err);
});
}));
dom.append(textEl, openFolderButton.element);
}
private showEmptyStage(forceHideMessages = false): void {
@ -1930,4 +1910,42 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
collector.addRule(`.search-view .message { color: ${fgWithOpacity}; }`);
}
}
const link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(`.monaco-workbench .search-view .message a { color: ${link}; }`);
}
const activeLink = theme.getColor(textLinkActiveForeground);
if (activeLink) {
collector.addRule(`.monaco-workbench .search-view .message a:hover,
.monaco-workbench .search-view .message a:active { color: ${activeLink}; }`);
}
});
class SearchLinkButton extends Disposable {
public readonly element: HTMLElement;
constructor(label: string, handler: (e: dom.EventLike) => unknown, tooltip?: string) {
super();
this.element = $('a.pointer', { tabindex: 0, title: tooltip }, label);
this.addEventHandlers(handler);
}
private addEventHandlers(handler: (e: dom.EventLike) => unknown): void {
const wrappedHandler = (e: dom.EventLike) => {
dom.EventHelper.stop(e, false);
handler(e);
};
this._register(dom.addDisposableListener(this.element, dom.EventType.CLICK, wrappedHandler));
this._register(dom.addDisposableListener(this.element, dom.EventType.KEY_DOWN, e => {
const event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) {
wrappedHandler(e);
event.preventDefault();
event.stopPropagation();
}
}));
}
}

View file

@ -364,6 +364,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
}
private _setupPtyHostListeners(offProcessTerminalService: IOffProcessTerminalService) {
// Mark the process as disconnected is the pty host is unresponsive, the responsive event
// will fire only when the pty host was already unresponsive
this._register(offProcessTerminalService.onPtyHostUnresponsive(() => {
this.isDisconnected = true;
this._onPtyDisconnect.fire();
@ -373,6 +375,9 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
this._onPtyReconnect.fire();
});
this._register(toDisposable(() => this._ptyResponsiveListener?.dispose()));
// When the pty host restarts, reconnect is no longer possible so dispose the responsive
// listener
this._register(offProcessTerminalService.onPtyHostRestart(() => {
// When the pty host restarts, reconnect is no longer possible
if (!this.isDisconnected) {

View file

@ -13,6 +13,8 @@ import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/co
import { getTerminalShellConfiguration } from 'vs/workbench/contrib/terminal/common/terminalConfiguration';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { getSystemShell } from 'vs/base/node/shell';
import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { Platform } from 'vs/base/common/platform';
// This file contains additional desktop-only contributions on top of those in browser/
@ -25,4 +27,5 @@ workbenchRegistry.registerWorkbenchContribution(TerminalNativeContribution, Life
// Register configurations
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
getTerminalShellConfiguration(getSystemShell).then(config => configurationRegistry.registerConfiguration(config));
const systemShell = async (p: Platform) => getSystemShell(p, await process.getShellEnv());
getTerminalShellConfiguration(systemShell).then(config => configurationRegistry.registerConfiguration(config));

View file

@ -13,6 +13,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IShellEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import type { Terminal as XTermTerminal } from 'xterm';
import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search';
@ -34,7 +35,8 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
@IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService,
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
@IHistoryService private readonly _historyService: IHistoryService,
@ILogService private readonly _logService: ILogService
@ILogService private readonly _logService: ILogService,
@IShellEnvironmentService private readonly _shellEnvironmentService: IShellEnvironmentService
) {
super();
}
@ -76,10 +78,11 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot();
let lastActiveWorkspace = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : undefined;
lastActiveWorkspace = lastActiveWorkspace === null ? undefined : lastActiveWorkspace;
const shell = getDefaultShell(
(key) => this._configurationService.inspect(key),
isWorkspaceShellAllowed,
await getSystemShell(platformOverride),
await getSystemShell(platformOverride, await this._shellEnvironmentService.getShellEnv()),
process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'),
process.env.windir,
createVariableResolver(lastActiveWorkspace, this._configurationResolverService),
@ -87,6 +90,7 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
useAutomationShell,
platformOverride
);
const args = getDefaultShellArgs(
(key) => this._configurationService.inspect(key),
isWorkspaceShellAllowed,
@ -95,6 +99,7 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
this._logService,
platformOverride
);
return Promise.resolve({ shell, args });
}

View file

@ -5,11 +5,16 @@
import { localize } from 'vs/nls';
import { registerAction2 } from 'vs/platform/actions/common/actions';
import { EditorOverride, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor';
import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { Extensions as EditorInputExtensions, IEditorInput, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetAction, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand } from './webviewCommands';
import { WebviewEditor } from './webviewEditor';
import { WebviewInput } from './webviewEditorInput';
@ -22,6 +27,53 @@ import { IWebviewWorkbenchService, WebviewEditorService } from './webviewWorkben
localize('webview.editor.label', "webview editor")),
[new SyncDescriptor(WebviewInput)]);
class WebviewPanelContribution implements IWorkbenchContribution {
constructor(
@IEditorService private readonly editorService: IEditorService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
) {
this.editorService.overrideOpenEditor({
open: (editor, options, group) => this.onEditorOpening(editor, options, group)
});
}
private onEditorOpening(
editor: IEditorInput,
options: ITextEditorOptions | undefined,
group: IEditorGroup
): IOpenEditorOverride | undefined {
if (!(editor instanceof WebviewInput) || editor.getTypeId() !== WebviewInput.typeId) {
return undefined;
}
if (group.isOpened(editor)) {
return undefined;
}
let previousGroup: IEditorGroup | undefined;
const groups = this.editorGroupService.groups;
for (const group of groups) {
if (group.isOpened(editor)) {
previousGroup = group;
break;
}
}
if (!previousGroup) {
return undefined;
}
previousGroup.closeEditor(editor);
return {
override: this.editorService.openEditor(editor, { ...options, override: EditorOverride.DISABLED }, group)
};
}
}
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchContributionsRegistry.registerWorkbenchContribution(WebviewPanelContribution, LifecyclePhase.Starting);
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(
WebviewEditorInputFactory.ID,
WebviewEditorInputFactory);

View file

@ -353,4 +353,40 @@ suite('NotebookCell#Document', function () {
assert.strictEqual(event.changes[0].items[0].document.isClosed, false);
assert.strictEqual(event.changes[0].items[1].document.isClosed, false);
});
test('Opening a notebook results in VS Code firing the event onDidChangeActiveNotebookEditor twice #118470', function () {
let count = 0;
extHostNotebooks.onDidChangeActiveNotebookEditor(() => count += 1);
extHostNotebooks.$acceptDocumentAndEditorsDelta({
addedEditors: [{
documentUri: notebookUri,
id: '_notebook_editor_2',
selections: [{ start: 0, end: 1 }],
visibleRanges: []
}]
});
extHostNotebooks.$acceptDocumentAndEditorsDelta({
newActiveEditor: '_notebook_editor_2'
});
assert.strictEqual(count, 1);
});
test('unset active notebook editor', function () {
const editor = extHostNotebooks.activeNotebookEditor;
assert.ok(editor !== undefined);
extHostNotebooks.$acceptDocumentAndEditorsDelta({ newActiveEditor: undefined });
assert.ok(extHostNotebooks.activeNotebookEditor === editor);
extHostNotebooks.$acceptDocumentAndEditorsDelta({});
assert.ok(extHostNotebooks.activeNotebookEditor === editor);
extHostNotebooks.$acceptDocumentAndEditorsDelta({ newActiveEditor: null });
assert.ok(extHostNotebooks.activeNotebookEditor === undefined);
});
});

View file

@ -24,10 +24,6 @@ export class Extensions extends Viewlet {
await this.code.waitForActiveElement(SEARCH_BOX);
}
async waitForExtensionsViewlet(): Promise<any> {
await this.code.waitForElement(SEARCH_BOX);
}
async searchForExtension(id: string): Promise<any> {
await this.code.waitAndClick(SEARCH_BOX);
await this.code.waitForActiveElement(SEARCH_BOX);
@ -40,6 +36,10 @@ export class Extensions extends Viewlet {
await this.code.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[data-extension-id="${id}"]`);
}
async closeExtension(title: string): Promise<any> {
await this.code.waitAndClick(`.tabs-container div.tab[title="Extension: ${title}"] div.tab-actions a.action-label.codicon.codicon-close`);
}
async installExtension(id: string, waitUntilEnabled: boolean): Promise<void> {
await this.searchForExtension(id);
await this.code.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[data-extension-id="${id}"] .extension-list-item .monaco-action-bar .action-item:not(.disabled) .extension-action.install`);

View file

@ -7,35 +7,22 @@ import { Application, Quality } from '../../../../automation';
export function setup() {
describe('Extensions', () => {
it(`install and activate vscode-smoketest-check extension`, async function () {
it(`install and enable vscode-smoketest-check extension`, async function () {
const app = this.app as Application;
if (app.quality === Quality.Dev || app.web /* https://github.com/microsoft/vscode/issues/118443 */) {
if (app.quality === Quality.Dev) {
this.skip();
return;
}
if (!app.web) {
await app.workbench.settingsEditor.addUserSetting('webview.experimental.useIframes', 'true');
}
await app.workbench.extensions.openExtensionsViewlet();
await app.workbench.extensions.installExtension('michelkaporin.vscode-smoketest-check', true);
await app.workbench.extensions.waitForExtensionsViewlet();
// Close extension editor because keybindings dispatch is not working when web views are opened and focused
// https://github.com/microsoft/vscode/issues/110276
await app.workbench.extensions.closeExtension('vscode-smoketest-check');
await app.workbench.quickaccess.runCommand('Smoke Test Check');
await app.workbench.statusbar.waitForStatusbarText('smoke test', 'VS Code Smoke Test Check');
});
after(async function () {
const app = this.app as Application;
if (app.web) {
return;
}
await app.workbench.settingsEditor.clearUserSettings();
});
});

View file

@ -35,7 +35,7 @@ export function setup() {
});
// https://github.com/microsoft/vscode/issues/115244
it('dismisses result & checks for correct result number', async function () {
it.skip('dismisses result & checks for correct result number', async function () {
const app = this.app as Application;
await app.workbench.search.searchFor('body');
await app.workbench.search.removeFileMatch('app.js');