Merge remote-tracking branch 'origin/main' into tyriar/116467_2
This commit is contained in:
commit
6d01ad952a
|
@ -985,7 +985,8 @@
|
|||
"CustomEditorProvider",
|
||||
"CustomReadonlyEditorProvider",
|
||||
"TerminalLinkProvider",
|
||||
"AuthenticationProvider"
|
||||
"AuthenticationProvider",
|
||||
"NotebookContentProvider"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
41
SECURITY.md
Normal file
41
SECURITY.md
Normal 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 -->
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
// });
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.55.0",
|
||||
"distro": "17365b1560dc5051788f2ec8b388b44b67ec1365",
|
||||
"distro": "0a442d49008152919d1ca15e5d21c03a1536c80d",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@ description: |
|
|||
architectures:
|
||||
- build-on: amd64
|
||||
run-on: @@ARCHITECTURE@@
|
||||
compression: lzo
|
||||
|
||||
grade: stable
|
||||
confinement: classic
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
))
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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'));
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
28
src/vs/vscode.proposed.d.ts
vendored
28
src/vs/vscode.proposed.d.ts
vendored
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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 ?? []
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -276,7 +276,6 @@ export class StatefulMarkdownCell extends Disposable {
|
|||
}
|
||||
|
||||
dispose() {
|
||||
this.notebookEditor.removeMarkdownPreview(this.viewCell);
|
||||
this.localDisposables.dispose();
|
||||
this.viewCell.detachTextEditor();
|
||||
super.dispose();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -255,6 +255,7 @@ export class ExcludePatternInputWidget extends PatternInputWidget {
|
|||
|
||||
setUseExcludesAndIgnoreFiles(value: boolean) {
|
||||
this.useExcludesAndIgnoreFilesBox.checked = value;
|
||||
this._onChangeIgnoreBoxEmitter.fire();
|
||||
}
|
||||
|
||||
protected getSubcontrolsWidth(): number {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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`);
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
|
|
Loading…
Reference in a new issue