diff --git a/.gitignore b/.gitignore index 11a7486bf53..93e14b79193 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ Thumbs.db node_modules/ .build/ extensions/**/dist/ +extensions/notebook-markdown-extensions/notebook-out/** /out*/ /extensions/**/out/ src/vs/server diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index b48798c9e9e..95ae002374e 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -29,550 +29,6 @@ { "mime": "x-application/github-issues", "value": [ - { - "url": "https://api.github.com/repos/microsoft/vscode/issues/115631", - "repository_url": "https://api.github.com/repos/microsoft/vscode", - "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/labels{/name}", - "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/comments", - "events_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/events", - "html_url": "https://github.com/microsoft/vscode/issues/115631", - "id": 799606785, - "node_id": "MDU6SXNzdWU3OTk2MDY3ODU=", - "number": 115631, - "title": "Provide a way for custom editors to process untitled files without relying on textDocument", - "user": { - "login": "lramos15", - "id": 4544166, - "node_id": "MDQ6VXNlcjQ1NDQxNjY=", - "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/lramos15", - "html_url": "https://github.com/lramos15", - "followers_url": "https://api.github.com/users/lramos15/followers", - "following_url": "https://api.github.com/users/lramos15/following{/other_user}", - "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", - "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", - "organizations_url": "https://api.github.com/users/lramos15/orgs", - "repos_url": "https://api.github.com/users/lramos15/repos", - "events_url": "https://api.github.com/users/lramos15/events{/privacy}", - "received_events_url": "https://api.github.com/users/lramos15/received_events", - "type": "User", - "site_admin": false - }, - "labels": [ - { - "id": 974714207, - "node_id": "MDU6TGFiZWw5NzQ3MTQyMDc=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/api-finalization", - "name": "api-finalization", - "color": "c5def5", - "default": false, - "description": "" - }, - { - "id": 869332220, - "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", - "name": "api-proposal", - "color": "c5def5", - "default": false, - "description": "" - }, - { - "id": 1713330180, - "node_id": "MDU6TGFiZWwxNzEzMzMwMTgw", - "url": "https://api.github.com/repos/microsoft/vscode/labels/custom-editors", - "name": "custom-editors", - "color": "c5def5", - "default": false, - "description": "Custom editor API (webview based editors)" - }, - { - "id": 272689392, - "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", - "name": "feature-request", - "color": "dcdcdc", - "default": false, - "description": "Request for new features or functionality" - }, - { - "id": 1839857516, - "node_id": "MDU6TGFiZWwxODM5ODU3NTE2", - "url": "https://api.github.com/repos/microsoft/vscode/labels/notebook", - "name": "notebook", - "color": "c5def5", - "default": false, - "description": "" - } - ], - "state": "open", - "locked": false, - "assignee": { - "login": "lramos15", - "id": 4544166, - "node_id": "MDQ6VXNlcjQ1NDQxNjY=", - "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/lramos15", - "html_url": "https://github.com/lramos15", - "followers_url": "https://api.github.com/users/lramos15/followers", - "following_url": "https://api.github.com/users/lramos15/following{/other_user}", - "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", - "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", - "organizations_url": "https://api.github.com/users/lramos15/orgs", - "repos_url": "https://api.github.com/users/lramos15/repos", - "events_url": "https://api.github.com/users/lramos15/events{/privacy}", - "received_events_url": "https://api.github.com/users/lramos15/received_events", - "type": "User", - "site_admin": false - }, - "assignees": [ - { - "login": "lramos15", - "id": 4544166, - "node_id": "MDQ6VXNlcjQ1NDQxNjY=", - "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/lramos15", - "html_url": "https://github.com/lramos15", - "followers_url": "https://api.github.com/users/lramos15/followers", - "following_url": "https://api.github.com/users/lramos15/following{/other_user}", - "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", - "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", - "organizations_url": "https://api.github.com/users/lramos15/orgs", - "repos_url": "https://api.github.com/users/lramos15/repos", - "events_url": "https://api.github.com/users/lramos15/events{/privacy}", - "received_events_url": "https://api.github.com/users/lramos15/received_events", - "type": "User", - "site_admin": false - }, - { - "login": "mjbvz", - "id": 12821956, - "node_id": "MDQ6VXNlcjEyODIxOTU2", - "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/mjbvz", - "html_url": "https://github.com/mjbvz", - "followers_url": "https://api.github.com/users/mjbvz/followers", - "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", - "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", - "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", - "organizations_url": "https://api.github.com/users/mjbvz/orgs", - "repos_url": "https://api.github.com/users/mjbvz/repos", - "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", - "received_events_url": "https://api.github.com/users/mjbvz/received_events", - "type": "User", - "site_admin": false - } - ], - "milestone": { - "url": "https://api.github.com/repos/microsoft/vscode/milestones/144", - "html_url": "https://github.com/microsoft/vscode/milestone/144", - "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/144/labels", - "id": 6407294, - "node_id": "MDk6TWlsZXN0b25lNjQwNzI5NA==", - "number": 144, - "title": "March 2021", - "description": "", - "creator": { - "login": "isidorn", - "id": 1926584, - "node_id": "MDQ6VXNlcjE5MjY1ODQ=", - "avatar_url": "https://avatars.githubusercontent.com/u/1926584?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/isidorn", - "html_url": "https://github.com/isidorn", - "followers_url": "https://api.github.com/users/isidorn/followers", - "following_url": "https://api.github.com/users/isidorn/following{/other_user}", - "gists_url": "https://api.github.com/users/isidorn/gists{/gist_id}", - "starred_url": "https://api.github.com/users/isidorn/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/isidorn/subscriptions", - "organizations_url": "https://api.github.com/users/isidorn/orgs", - "repos_url": "https://api.github.com/users/isidorn/repos", - "events_url": "https://api.github.com/users/isidorn/events{/privacy}", - "received_events_url": "https://api.github.com/users/isidorn/received_events", - "type": "User", - "site_admin": false - }, - "open_issues": 252, - "closed_issues": 190, - "state": "open", - "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", - "due_on": null, - "closed_at": null - }, - "comments": 1, - "created_at": "2021-02-02T19:29:05Z", - "updated_at": "2021-03-09T16:12:17Z", - "closed_at": null, - "author_association": "MEMBER", - "active_lock_reason": null, - "body": "Currently the \"Reopen with\" experience for untitled files and custom binary editors needs better support. See #114711. After discussion in the API call the best proposal seems to be placing the untitled file data in the OpenEditor / OpenNotebook context. There interface would be modified as shown:\r\n```ts\r\n\t/**\r\n\t * Additional information about the opening custom document.\r\n\t */\r\n\tinterface CustomDocumentOpenContext {\r\n\t\t/**\r\n\t\t * The id of the backup to restore the document from or `undefined` if there is no backup.\r\n\t\t *\r\n\t\t * If this is provided, your extension should restore the editor from the backup instead of reading the file\r\n\t\t * from the user's workspace.\r\n\t\t */\r\n\t\treadonly backupId?: string;\r\n\t\t/**\r\n\t\t * If the URI is an untitled file, this will be populated with the byte data of that file\r\n\t\t *\r\n\t\t * If this is provided, your extension should utilize this byte data rather than executing fs APIs on the URI passed in\r\n\t\t */\r\n\t\treadonly untitledDocumentData?: Uint8Array;\r\n\t}\r\n\r\n\tinterface NotebookDocumentOpenContext {\r\n\t\treadonly backupId?: string;\r\n\t\treadonly untitledDocumentData?: Uint8Array;\r\n\t}\r\n```\r\nThe extension other would then not be required to resolve the URI to a text document (which would have been disposed of). ", - "performed_via_github_app": null, - "score": 1 - } - ] - }, - { - "mime": "text/markdown", - "value": "- [#115631](https://github.com/microsoft/vscode/issues/115631 \"Provide a way for custom editors to process untitled files without relying on textDocument\") Provide a way for custom editors to process untitled files without relying on textDocument [api-finalization, api-proposal, custom-editors, feature-request, notebook]- [@lramos15](https://github.com/lramos15 \"Issue 115631 is assigned to lramos15\")\n\n" - } - ] - }, - { - "kind": 1, - "language": "markdown", - "value": "### Proposals", - "editable": true, - "outputs": [] - }, - { - "kind": 2, - "language": "github-issues", - "value": "$repo $milestone is:open label:api-proposal ", - "editable": true, - "outputs": [ - { - "mime": "x-application/github-issues", - "value": [ - { - "url": "https://api.github.com/repos/microsoft/vscode/issues/118084", - "repository_url": "https://api.github.com/repos/microsoft/vscode", - "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/118084/labels{/name}", - "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/118084/comments", - "events_url": "https://api.github.com/repos/microsoft/vscode/issues/118084/events", - "html_url": "https://github.com/microsoft/vscode/issues/118084", - "id": 821570132, - "node_id": "MDU6SXNzdWU4MjE1NzAxMzI=", - "number": 118084, - "title": "Add trigger reason to code actions", - "user": { - "login": "mjbvz", - "id": 12821956, - "node_id": "MDQ6VXNlcjEyODIxOTU2", - "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/mjbvz", - "html_url": "https://github.com/mjbvz", - "followers_url": "https://api.github.com/users/mjbvz/followers", - "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", - "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", - "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", - "organizations_url": "https://api.github.com/users/mjbvz/orgs", - "repos_url": "https://api.github.com/users/mjbvz/repos", - "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", - "received_events_url": "https://api.github.com/users/mjbvz/received_events", - "type": "User", - "site_admin": false - }, - "labels": [ - { - "id": 290465400, - "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/api", - "name": "api", - "color": "1d76db", - "default": false, - "description": "" - }, - { - "id": 869332220, - "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", - "name": "api-proposal", - "color": "c5def5", - "default": false, - "description": "" - }, - { - "id": 431507554, - "node_id": "MDU6TGFiZWw0MzE1MDc1NTQ=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/editor-code-actions", - "name": "editor-code-actions", - "color": "c5def5", - "default": false, - "description": "Editor inplace actions (Ctrl + .)" - } - ], - "state": "open", - "locked": false, - "assignee": { - "login": "mjbvz", - "id": 12821956, - "node_id": "MDQ6VXNlcjEyODIxOTU2", - "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/mjbvz", - "html_url": "https://github.com/mjbvz", - "followers_url": "https://api.github.com/users/mjbvz/followers", - "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", - "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", - "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", - "organizations_url": "https://api.github.com/users/mjbvz/orgs", - "repos_url": "https://api.github.com/users/mjbvz/repos", - "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", - "received_events_url": "https://api.github.com/users/mjbvz/received_events", - "type": "User", - "site_admin": false - }, - "assignees": [ - { - "login": "mjbvz", - "id": 12821956, - "node_id": "MDQ6VXNlcjEyODIxOTU2", - "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/mjbvz", - "html_url": "https://github.com/mjbvz", - "followers_url": "https://api.github.com/users/mjbvz/followers", - "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", - "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", - "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", - "organizations_url": "https://api.github.com/users/mjbvz/orgs", - "repos_url": "https://api.github.com/users/mjbvz/repos", - "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", - "received_events_url": "https://api.github.com/users/mjbvz/received_events", - "type": "User", - "site_admin": false - } - ], - "milestone": { - "url": "https://api.github.com/repos/microsoft/vscode/milestones/144", - "html_url": "https://github.com/microsoft/vscode/milestone/144", - "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/144/labels", - "id": 6407294, - "node_id": "MDk6TWlsZXN0b25lNjQwNzI5NA==", - "number": 144, - "title": "March 2021", - "description": "", - "creator": { - "login": "isidorn", - "id": 1926584, - "node_id": "MDQ6VXNlcjE5MjY1ODQ=", - "avatar_url": "https://avatars.githubusercontent.com/u/1926584?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/isidorn", - "html_url": "https://github.com/isidorn", - "followers_url": "https://api.github.com/users/isidorn/followers", - "following_url": "https://api.github.com/users/isidorn/following{/other_user}", - "gists_url": "https://api.github.com/users/isidorn/gists{/gist_id}", - "starred_url": "https://api.github.com/users/isidorn/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/isidorn/subscriptions", - "organizations_url": "https://api.github.com/users/isidorn/orgs", - "repos_url": "https://api.github.com/users/isidorn/repos", - "events_url": "https://api.github.com/users/isidorn/events{/privacy}", - "received_events_url": "https://api.github.com/users/isidorn/received_events", - "type": "User", - "site_admin": false - }, - "open_issues": 252, - "closed_issues": 190, - "state": "open", - "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", - "due_on": null, - "closed_at": null - }, - "comments": 3, - "created_at": "2021-03-03T23:04:03Z", - "updated_at": "2021-03-04T01:38:44Z", - "closed_at": null, - "author_association": "MEMBER", - "active_lock_reason": null, - "body": "## Problem\r\n\r\nFor JS/TS, if user makes a imprecise selection (such as only selecting part of an identifier name) and then manually requests code actions, we'd like to return refactorings as if the selection were expanded. However we don't want to do this automatically for implicitly requested code actions (which cause the light bulb to show up)\r\n\r\n## Proposal\r\nAdd a trigger reason to the code action context. This would let you know if the code actions have been manually requested or were triggered automatically", - "performed_via_github_app": null, - "score": 1 - }, - { - "url": "https://api.github.com/repos/microsoft/vscode/issues/117506", - "repository_url": "https://api.github.com/repos/microsoft/vscode", - "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/117506/labels{/name}", - "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/117506/comments", - "events_url": "https://api.github.com/repos/microsoft/vscode/issues/117506/events", - "html_url": "https://github.com/microsoft/vscode/issues/117506", - "id": 814923063, - "node_id": "MDU6SXNzdWU4MTQ5MjMwNjM=", - "number": 117506, - "title": "Add an API for a notebook kernel to modify its list of supported languages at runtime.", - "user": { - "login": "brettfo", - "id": 926281, - "node_id": "MDQ6VXNlcjkyNjI4MQ==", - "avatar_url": "https://avatars.githubusercontent.com/u/926281?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/brettfo", - "html_url": "https://github.com/brettfo", - "followers_url": "https://api.github.com/users/brettfo/followers", - "following_url": "https://api.github.com/users/brettfo/following{/other_user}", - "gists_url": "https://api.github.com/users/brettfo/gists{/gist_id}", - "starred_url": "https://api.github.com/users/brettfo/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/brettfo/subscriptions", - "organizations_url": "https://api.github.com/users/brettfo/orgs", - "repos_url": "https://api.github.com/users/brettfo/repos", - "events_url": "https://api.github.com/users/brettfo/events{/privacy}", - "received_events_url": "https://api.github.com/users/brettfo/received_events", - "type": "User", - "site_admin": false - }, - "labels": [ - { - "id": 290465400, - "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/api", - "name": "api", - "color": "1d76db", - "default": false, - "description": "" - }, - { - "id": 869332220, - "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", - "name": "api-proposal", - "color": "c5def5", - "default": false, - "description": "" - }, - { - "id": 272689392, - "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", - "name": "feature-request", - "color": "dcdcdc", - "default": false, - "description": "Request for new features or functionality" - }, - { - "id": 1839857516, - "node_id": "MDU6TGFiZWwxODM5ODU3NTE2", - "url": "https://api.github.com/repos/microsoft/vscode/labels/notebook", - "name": "notebook", - "color": "c5def5", - "default": false, - "description": "" - } - ], - "state": "open", - "locked": false, - "assignee": { - "login": "rebornix", - "id": 876920, - "node_id": "MDQ6VXNlcjg3NjkyMA==", - "avatar_url": "https://avatars.githubusercontent.com/u/876920?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/rebornix", - "html_url": "https://github.com/rebornix", - "followers_url": "https://api.github.com/users/rebornix/followers", - "following_url": "https://api.github.com/users/rebornix/following{/other_user}", - "gists_url": "https://api.github.com/users/rebornix/gists{/gist_id}", - "starred_url": "https://api.github.com/users/rebornix/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/rebornix/subscriptions", - "organizations_url": "https://api.github.com/users/rebornix/orgs", - "repos_url": "https://api.github.com/users/rebornix/repos", - "events_url": "https://api.github.com/users/rebornix/events{/privacy}", - "received_events_url": "https://api.github.com/users/rebornix/received_events", - "type": "User", - "site_admin": false - }, - "assignees": [ - { - "login": "rebornix", - "id": 876920, - "node_id": "MDQ6VXNlcjg3NjkyMA==", - "avatar_url": "https://avatars.githubusercontent.com/u/876920?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/rebornix", - "html_url": "https://github.com/rebornix", - "followers_url": "https://api.github.com/users/rebornix/followers", - "following_url": "https://api.github.com/users/rebornix/following{/other_user}", - "gists_url": "https://api.github.com/users/rebornix/gists{/gist_id}", - "starred_url": "https://api.github.com/users/rebornix/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/rebornix/subscriptions", - "organizations_url": "https://api.github.com/users/rebornix/orgs", - "repos_url": "https://api.github.com/users/rebornix/repos", - "events_url": "https://api.github.com/users/rebornix/events{/privacy}", - "received_events_url": "https://api.github.com/users/rebornix/received_events", - "type": "User", - "site_admin": false - }, - { - "login": "jrieken", - "id": 1794099, - "node_id": "MDQ6VXNlcjE3OTQwOTk=", - "avatar_url": "https://avatars.githubusercontent.com/u/1794099?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/jrieken", - "html_url": "https://github.com/jrieken", - "followers_url": "https://api.github.com/users/jrieken/followers", - "following_url": "https://api.github.com/users/jrieken/following{/other_user}", - "gists_url": "https://api.github.com/users/jrieken/gists{/gist_id}", - "starred_url": "https://api.github.com/users/jrieken/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/jrieken/subscriptions", - "organizations_url": "https://api.github.com/users/jrieken/orgs", - "repos_url": "https://api.github.com/users/jrieken/repos", - "events_url": "https://api.github.com/users/jrieken/events{/privacy}", - "received_events_url": "https://api.github.com/users/jrieken/received_events", - "type": "User", - "site_admin": false - } - ], - "milestone": { - "url": "https://api.github.com/repos/microsoft/vscode/milestones/144", - "html_url": "https://github.com/microsoft/vscode/milestone/144", - "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/144/labels", - "id": 6407294, - "node_id": "MDk6TWlsZXN0b25lNjQwNzI5NA==", - "number": 144, - "title": "March 2021", - "description": "", - "creator": { - "login": "isidorn", - "id": 1926584, - "node_id": "MDQ6VXNlcjE5MjY1ODQ=", - "avatar_url": "https://avatars.githubusercontent.com/u/1926584?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/isidorn", - "html_url": "https://github.com/isidorn", - "followers_url": "https://api.github.com/users/isidorn/followers", - "following_url": "https://api.github.com/users/isidorn/following{/other_user}", - "gists_url": "https://api.github.com/users/isidorn/gists{/gist_id}", - "starred_url": "https://api.github.com/users/isidorn/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/isidorn/subscriptions", - "organizations_url": "https://api.github.com/users/isidorn/orgs", - "repos_url": "https://api.github.com/users/isidorn/repos", - "events_url": "https://api.github.com/users/isidorn/events{/privacy}", - "received_events_url": "https://api.github.com/users/isidorn/received_events", - "type": "User", - "site_admin": false - }, - "open_issues": 252, - "closed_issues": 190, - "state": "open", - "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", - "due_on": null, - "closed_at": null - }, - "comments": 1, - "created_at": "2021-02-23T23:32:14Z", - "updated_at": "2021-02-25T14:56:35Z", - "closed_at": null, - "author_association": "MEMBER", - "active_lock_reason": null, - "body": "\r\n\r\n\r\n\r\n\r\nThe `NotebookKernel` interfaces exposes a `supportedLanguages: string[]` field, but for kernels that support a dynamic list of languages, there's currently no way for that list to be updated in a meaningful way at runtime.\r\n\r\nE.g., the .NET Interactive notebook extension supports doing something like this: `#!connect -type=MySQL -name=sql -connectionString=\"abcd\"` and from that point forward, a cell language of `SQL` should be supported, but not necessarily before.\r\n\r\nOne way to accomplish this would be to add an event on `NotebookKernel` called `LanguagesChanged` or similar that either contained a new string array of the supported languages _or_ instructed any listeners that a new string array was present in the `supportedLanguages` field; I don't have a strong preference either way.", - "performed_via_github_app": null, - "score": 1 - }, { "url": "https://api.github.com/repos/microsoft/vscode/issues/117058", "repository_url": "https://api.github.com/repos/microsoft/vscode", @@ -606,16 +62,16 @@ }, "labels": [ { - "id": 869332220, - "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", - "name": "api-proposal", + "id": 974714207, + "node_id": "MDU6TGFiZWw5NzQ3MTQyMDc=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-finalization", + "name": "api-finalization", "color": "c5def5", "default": false, "description": "" } ], - "state": "open", + "state": "closed", "locked": false, "assignee": { "login": "sbatten", @@ -688,18 +144,18 @@ "type": "User", "site_admin": false }, - "open_issues": 252, - "closed_issues": 190, + "open_issues": 249, + "closed_issues": 280, "state": "open", "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", + "updated_at": "2021-03-16T15:51:00Z", "due_on": null, "closed_at": null }, "comments": 1, "created_at": "2021-02-19T16:33:09Z", - "updated_at": "2021-02-22T18:49:22Z", - "closed_at": null, + "updated_at": "2021-03-09T18:24:07Z", + "closed_at": "2021-03-09T18:24:07Z", "author_association": "MEMBER", "active_lock_reason": null, "body": "```ts\r\nexport interface ExtensionContext {\r\n\r\n/**\r\n * Indicates that this is a fresh install of VS Code.\r\n */\r\nreadonly isNewInstall: boolean;\r\n}\r\n```", @@ -747,6 +203,15 @@ "default": false, "description": "" }, + { + "id": 974714207, + "node_id": "MDU6TGFiZWw5NzQ3MTQyMDc=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-finalization", + "name": "api-finalization", + "color": "c5def5", + "default": false, + "description": "" + }, { "id": 869332220, "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", @@ -839,17 +304,17 @@ "type": "User", "site_admin": false }, - "open_issues": 252, - "closed_issues": 190, + "open_issues": 249, + "closed_issues": 280, "state": "open", "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", + "updated_at": "2021-03-16T15:51:00Z", "due_on": null, "closed_at": null }, - "comments": 10, + "comments": 11, "created_at": "2021-02-18T01:20:24Z", - "updated_at": "2021-02-24T08:13:19Z", + "updated_at": "2021-03-15T15:26:39Z", "closed_at": null, "author_association": "MEMBER", "active_lock_reason": null, @@ -1028,11 +493,846 @@ "type": "User", "site_admin": false }, - "open_issues": 252, - "closed_issues": 190, + "open_issues": 249, + "closed_issues": 280, "state": "open", "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", + "updated_at": "2021-03-16T15:51:00Z", + "due_on": null, + "closed_at": null + }, + "comments": 1, + "created_at": "2021-02-02T19:29:05Z", + "updated_at": "2021-03-09T16:12:17Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "Currently the \"Reopen with\" experience for untitled files and custom binary editors needs better support. See #114711. After discussion in the API call the best proposal seems to be placing the untitled file data in the OpenEditor / OpenNotebook context. There interface would be modified as shown:\r\n```ts\r\n\t/**\r\n\t * Additional information about the opening custom document.\r\n\t */\r\n\tinterface CustomDocumentOpenContext {\r\n\t\t/**\r\n\t\t * The id of the backup to restore the document from or `undefined` if there is no backup.\r\n\t\t *\r\n\t\t * If this is provided, your extension should restore the editor from the backup instead of reading the file\r\n\t\t * from the user's workspace.\r\n\t\t */\r\n\t\treadonly backupId?: string;\r\n\t\t/**\r\n\t\t * If the URI is an untitled file, this will be populated with the byte data of that file\r\n\t\t *\r\n\t\t * If this is provided, your extension should utilize this byte data rather than executing fs APIs on the URI passed in\r\n\t\t */\r\n\t\treadonly untitledDocumentData?: Uint8Array;\r\n\t}\r\n\r\n\tinterface NotebookDocumentOpenContext {\r\n\t\treadonly backupId?: string;\r\n\t\treadonly untitledDocumentData?: Uint8Array;\r\n\t}\r\n```\r\nThe extension other would then not be required to resolve the URI to a text document (which would have been disposed of). ", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/110267", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/110267/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/110267/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/110267/events", + "html_url": "https://github.com/microsoft/vscode/issues/110267", + "id": 739391278, + "node_id": "MDU6SXNzdWU3MzkzOTEyNzg=", + "number": 110267, + "title": "Pass telemetry enablement to extensions", + "user": { + "login": "sbatten", + "id": 6561887, + "node_id": "MDQ6VXNlcjY1NjE4ODc=", + "avatar_url": "https://avatars.githubusercontent.com/u/6561887?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sbatten", + "html_url": "https://github.com/sbatten", + "followers_url": "https://api.github.com/users/sbatten/followers", + "following_url": "https://api.github.com/users/sbatten/following{/other_user}", + "gists_url": "https://api.github.com/users/sbatten/gists{/gist_id}", + "starred_url": "https://api.github.com/users/sbatten/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/sbatten/subscriptions", + "organizations_url": "https://api.github.com/users/sbatten/orgs", + "repos_url": "https://api.github.com/users/sbatten/repos", + "events_url": "https://api.github.com/users/sbatten/events{/privacy}", + "received_events_url": "https://api.github.com/users/sbatten/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 974714207, + "node_id": "MDU6TGFiZWw5NzQ3MTQyMDc=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-finalization", + "name": "api-finalization", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + }, + { + "id": 414580097, + "node_id": "MDU6TGFiZWw0MTQ1ODAwOTc=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/telemetry", + "name": "telemetry", + "color": "c5def5", + "default": false, + "description": "Telemetry system issues" + } + ], + "state": "closed", + "locked": false, + "assignee": { + "login": "sbatten", + "id": 6561887, + "node_id": "MDQ6VXNlcjY1NjE4ODc=", + "avatar_url": "https://avatars.githubusercontent.com/u/6561887?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sbatten", + "html_url": "https://github.com/sbatten", + "followers_url": "https://api.github.com/users/sbatten/followers", + "following_url": "https://api.github.com/users/sbatten/following{/other_user}", + "gists_url": "https://api.github.com/users/sbatten/gists{/gist_id}", + "starred_url": "https://api.github.com/users/sbatten/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/sbatten/subscriptions", + "organizations_url": "https://api.github.com/users/sbatten/orgs", + "repos_url": "https://api.github.com/users/sbatten/repos", + "events_url": "https://api.github.com/users/sbatten/events{/privacy}", + "received_events_url": "https://api.github.com/users/sbatten/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "sbatten", + "id": 6561887, + "node_id": "MDQ6VXNlcjY1NjE4ODc=", + "avatar_url": "https://avatars.githubusercontent.com/u/6561887?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sbatten", + "html_url": "https://github.com/sbatten", + "followers_url": "https://api.github.com/users/sbatten/followers", + "following_url": "https://api.github.com/users/sbatten/following{/other_user}", + "gists_url": "https://api.github.com/users/sbatten/gists{/gist_id}", + "starred_url": "https://api.github.com/users/sbatten/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/sbatten/subscriptions", + "organizations_url": "https://api.github.com/users/sbatten/orgs", + "repos_url": "https://api.github.com/users/sbatten/repos", + "events_url": "https://api.github.com/users/sbatten/events{/privacy}", + "received_events_url": "https://api.github.com/users/sbatten/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/144", + "html_url": "https://github.com/microsoft/vscode/milestone/144", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/144/labels", + "id": 6407294, + "node_id": "MDk6TWlsZXN0b25lNjQwNzI5NA==", + "number": 144, + "title": "March 2021", + "description": "", + "creator": { + "login": "isidorn", + "id": 1926584, + "node_id": "MDQ6VXNlcjE5MjY1ODQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/1926584?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/isidorn", + "html_url": "https://github.com/isidorn", + "followers_url": "https://api.github.com/users/isidorn/followers", + "following_url": "https://api.github.com/users/isidorn/following{/other_user}", + "gists_url": "https://api.github.com/users/isidorn/gists{/gist_id}", + "starred_url": "https://api.github.com/users/isidorn/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/isidorn/subscriptions", + "organizations_url": "https://api.github.com/users/isidorn/orgs", + "repos_url": "https://api.github.com/users/isidorn/repos", + "events_url": "https://api.github.com/users/isidorn/events{/privacy}", + "received_events_url": "https://api.github.com/users/isidorn/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 249, + "closed_issues": 280, + "state": "open", + "created_at": "2021-02-08T17:30:20Z", + "updated_at": "2021-03-16T15:51:00Z", + "due_on": null, + "closed_at": null + }, + "comments": 2, + "created_at": "2020-11-09T21:56:01Z", + "updated_at": "2021-03-09T18:26:17Z", + "closed_at": "2021-03-09T18:26:17Z", + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "Right now extensions use the configuration to determine if they should send telemetry; however, the cli flag is not passed to the extension host.\r\n\r\n```ts\r\nexport namespace env {\r\n export const enableTelemetry: boolean;\r\n\r\n export const onDidChangeEnableTelemetry: Event;\r\n}\r\n```\r\n", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/105690", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/events", + "html_url": "https://github.com/microsoft/vscode/issues/105690", + "id": 688793797, + "node_id": "MDU6SXNzdWU2ODg3OTM3OTc=", + "number": 105690, + "title": "Extension API for Inline Values", + "user": { + "login": "weinand", + "id": 1898161, + "node_id": "MDQ6VXNlcjE4OTgxNjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/weinand", + "html_url": "https://github.com/weinand", + "followers_url": "https://api.github.com/users/weinand/followers", + "following_url": "https://api.github.com/users/weinand/following{/other_user}", + "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", + "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", + "organizations_url": "https://api.github.com/users/weinand/orgs", + "repos_url": "https://api.github.com/users/weinand/repos", + "events_url": "https://api.github.com/users/weinand/events{/privacy}", + "received_events_url": "https://api.github.com/users/weinand/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 290465400, + "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api", + "name": "api", + "color": "1d76db", + "default": false, + "description": "" + }, + { + "id": 974714207, + "node_id": "MDU6TGFiZWw5NzQ3MTQyMDc=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-finalization", + "name": "api-finalization", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 291054922, + "node_id": "MDU6TGFiZWwyOTEwNTQ5MjI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/debug", + "name": "debug", + "color": "1d76db", + "default": false, + "description": "Debug viewlet, configurations, breakpoints, adapter issues" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "weinand", + "id": 1898161, + "node_id": "MDQ6VXNlcjE4OTgxNjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/weinand", + "html_url": "https://github.com/weinand", + "followers_url": "https://api.github.com/users/weinand/followers", + "following_url": "https://api.github.com/users/weinand/following{/other_user}", + "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", + "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", + "organizations_url": "https://api.github.com/users/weinand/orgs", + "repos_url": "https://api.github.com/users/weinand/repos", + "events_url": "https://api.github.com/users/weinand/events{/privacy}", + "received_events_url": "https://api.github.com/users/weinand/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "weinand", + "id": 1898161, + "node_id": "MDQ6VXNlcjE4OTgxNjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/weinand", + "html_url": "https://github.com/weinand", + "followers_url": "https://api.github.com/users/weinand/followers", + "following_url": "https://api.github.com/users/weinand/following{/other_user}", + "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", + "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", + "organizations_url": "https://api.github.com/users/weinand/orgs", + "repos_url": "https://api.github.com/users/weinand/repos", + "events_url": "https://api.github.com/users/weinand/events{/privacy}", + "received_events_url": "https://api.github.com/users/weinand/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/144", + "html_url": "https://github.com/microsoft/vscode/milestone/144", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/144/labels", + "id": 6407294, + "node_id": "MDk6TWlsZXN0b25lNjQwNzI5NA==", + "number": 144, + "title": "March 2021", + "description": "", + "creator": { + "login": "isidorn", + "id": 1926584, + "node_id": "MDQ6VXNlcjE5MjY1ODQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/1926584?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/isidorn", + "html_url": "https://github.com/isidorn", + "followers_url": "https://api.github.com/users/isidorn/followers", + "following_url": "https://api.github.com/users/isidorn/following{/other_user}", + "gists_url": "https://api.github.com/users/isidorn/gists{/gist_id}", + "starred_url": "https://api.github.com/users/isidorn/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/isidorn/subscriptions", + "organizations_url": "https://api.github.com/users/isidorn/orgs", + "repos_url": "https://api.github.com/users/isidorn/repos", + "events_url": "https://api.github.com/users/isidorn/events{/privacy}", + "received_events_url": "https://api.github.com/users/isidorn/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 249, + "closed_issues": 280, + "state": "open", + "created_at": "2021-02-08T17:30:20Z", + "updated_at": "2021-03-16T15:51:00Z", + "due_on": null, + "closed_at": null + }, + "comments": 22, + "created_at": "2020-08-30T21:21:23Z", + "updated_at": "2021-03-16T15:47:54Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "Today the \"Show Inline Values\" feature of VS Code's debugger is based on a generic implementation in the VS Code core and provides neither customisability through settings, nor extensibility via extensions.\r\n\r\nAs a consequence, it is not a perfect fit for all languages (e.g. #101797) and sometimes even shows incorrect values because it doesn't understand the scope regions of the underlying language. \r\n\r\nThis features asks for an extension API that either replaces the built-in implementation completely or allows to replace parts of the implementation with custom code.\r\n", + "performed_via_github_app": null, + "score": 1 + } + ] + }, + { + "mime": "text/markdown", + "value": "- [#117058](https://github.com/microsoft/vscode/issues/117058 \"add property to extension api for new install\") add property to extension api for new install [api-finalization]- [@sbatten](https://github.com/sbatten \"Issue 117058 is assigned to sbatten\")\n\n- [#116906](https://github.com/microsoft/vscode/issues/116906 \"Add extension ID and version to ExtensionContext\") Add extension ID and version to ExtensionContext [api, api-finalization, api-proposal, feature-request]- [@eamodio](https://github.com/eamodio \"Issue 116906 is assigned to eamodio\")\n\n- [#115631](https://github.com/microsoft/vscode/issues/115631 \"Provide a way for custom editors to process untitled files without relying on textDocument\") Provide a way for custom editors to process untitled files without relying on textDocument [api-finalization, api-proposal, custom-editors, feature-request, notebook]- [@lramos15](https://github.com/lramos15 \"Issue 115631 is assigned to lramos15\")\n\n- [#110267](https://github.com/microsoft/vscode/issues/110267 \"Pass telemetry enablement to extensions\") Pass telemetry enablement to extensions [api-finalization, feature-request, telemetry]- [@sbatten](https://github.com/sbatten \"Issue 110267 is assigned to sbatten\")\n\n- [#105690](https://github.com/microsoft/vscode/issues/105690 \"Extension API for Inline Values\") Extension API for Inline Values [api, api-finalization, debug, feature-request]- [@weinand](https://github.com/weinand \"Issue 105690 is assigned to weinand\")\n\n" + } + ] + }, + { + "kind": 1, + "language": "markdown", + "value": "### Proposals", + "editable": true, + "outputs": [] + }, + { + "kind": 2, + "language": "github-issues", + "value": "$repo $milestone is:open label:api-proposal ", + "editable": true, + "outputs": [ + { + "mime": "x-application/github-issues", + "value": [ + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/119097", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/119097/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/119097/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/119097/events", + "html_url": "https://github.com/microsoft/vscode/issues/119097", + "id": 832954890, + "node_id": "MDU6SXNzdWU4MzI5NTQ4OTA=", + "number": 119097, + "title": "Allow extensions to contribute getting started content", + "user": { + "login": "JacksonKearl", + "id": 8586769, + "node_id": "MDQ6VXNlcjg1ODY3Njk=", + "avatar_url": "https://avatars.githubusercontent.com/u/8586769?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/JacksonKearl", + "html_url": "https://github.com/JacksonKearl", + "followers_url": "https://api.github.com/users/JacksonKearl/followers", + "following_url": "https://api.github.com/users/JacksonKearl/following{/other_user}", + "gists_url": "https://api.github.com/users/JacksonKearl/gists{/gist_id}", + "starred_url": "https://api.github.com/users/JacksonKearl/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/JacksonKearl/subscriptions", + "organizations_url": "https://api.github.com/users/JacksonKearl/orgs", + "repos_url": "https://api.github.com/users/JacksonKearl/repos", + "events_url": "https://api.github.com/users/JacksonKearl/events{/privacy}", + "received_events_url": "https://api.github.com/users/JacksonKearl/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 290465400, + "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api", + "name": "api", + "color": "1d76db", + "default": false, + "description": "" + }, + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + }, + { + "id": 2552283302, + "node_id": "MDU6TGFiZWwyNTUyMjgzMzAy", + "url": "https://api.github.com/repos/microsoft/vscode/labels/getting-started", + "name": "getting-started", + "color": "c5def5", + "default": false, + "description": "" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "JacksonKearl", + "id": 8586769, + "node_id": "MDQ6VXNlcjg1ODY3Njk=", + "avatar_url": "https://avatars.githubusercontent.com/u/8586769?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/JacksonKearl", + "html_url": "https://github.com/JacksonKearl", + "followers_url": "https://api.github.com/users/JacksonKearl/followers", + "following_url": "https://api.github.com/users/JacksonKearl/following{/other_user}", + "gists_url": "https://api.github.com/users/JacksonKearl/gists{/gist_id}", + "starred_url": "https://api.github.com/users/JacksonKearl/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/JacksonKearl/subscriptions", + "organizations_url": "https://api.github.com/users/JacksonKearl/orgs", + "repos_url": "https://api.github.com/users/JacksonKearl/repos", + "events_url": "https://api.github.com/users/JacksonKearl/events{/privacy}", + "received_events_url": "https://api.github.com/users/JacksonKearl/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "JacksonKearl", + "id": 8586769, + "node_id": "MDQ6VXNlcjg1ODY3Njk=", + "avatar_url": "https://avatars.githubusercontent.com/u/8586769?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/JacksonKearl", + "html_url": "https://github.com/JacksonKearl", + "followers_url": "https://api.github.com/users/JacksonKearl/followers", + "following_url": "https://api.github.com/users/JacksonKearl/following{/other_user}", + "gists_url": "https://api.github.com/users/JacksonKearl/gists{/gist_id}", + "starred_url": "https://api.github.com/users/JacksonKearl/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/JacksonKearl/subscriptions", + "organizations_url": "https://api.github.com/users/JacksonKearl/orgs", + "repos_url": "https://api.github.com/users/JacksonKearl/repos", + "events_url": "https://api.github.com/users/JacksonKearl/events{/privacy}", + "received_events_url": "https://api.github.com/users/JacksonKearl/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/144", + "html_url": "https://github.com/microsoft/vscode/milestone/144", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/144/labels", + "id": 6407294, + "node_id": "MDk6TWlsZXN0b25lNjQwNzI5NA==", + "number": 144, + "title": "March 2021", + "description": "", + "creator": { + "login": "isidorn", + "id": 1926584, + "node_id": "MDQ6VXNlcjE5MjY1ODQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/1926584?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/isidorn", + "html_url": "https://github.com/isidorn", + "followers_url": "https://api.github.com/users/isidorn/followers", + "following_url": "https://api.github.com/users/isidorn/following{/other_user}", + "gists_url": "https://api.github.com/users/isidorn/gists{/gist_id}", + "starred_url": "https://api.github.com/users/isidorn/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/isidorn/subscriptions", + "organizations_url": "https://api.github.com/users/isidorn/orgs", + "repos_url": "https://api.github.com/users/isidorn/repos", + "events_url": "https://api.github.com/users/isidorn/events{/privacy}", + "received_events_url": "https://api.github.com/users/isidorn/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 250, + "closed_issues": 281, + "state": "open", + "created_at": "2021-02-08T17:30:20Z", + "updated_at": "2021-03-16T16:43:27Z", + "due_on": null, + "closed_at": null + }, + "comments": 0, + "created_at": "2021-03-16T16:02:03Z", + "updated_at": "2021-03-16T16:09:09Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "We want to allow extensions to contribute items to the welcome page's getting started section:\r\n![image](https://user-images.githubusercontent.com/8586769/111341118-74bf9280-8636-11eb-9589-56e6c5ce926d.png)\r\n![image](https://user-images.githubusercontent.com/8586769/111341319-a33d6d80-8636-11eb-97d2-35bb563b7c9e.png)\r\n\r\nUsing a package.json like this:\r\n```json\r\n\t\t\"welcomeCategories\": [\r\n\t\t\t{\r\n\t\t\t\t\"id\": \"exampleProject\",\r\n\t\t\t\t\"title\": \"Turn Markdown into HTML\",\r\n\t\t\t\t\"description\": \"Use this sample project to learn how to convert Markdown to HTML!\"\r\n\t\t\t}\r\n\t\t],\r\n\t\t\"welcomeItems\": {\r\n\t\t\t\"exampleProject\": [\r\n\t\t\t\t{\r\n\t\t\t\t\t\"id\": \"md-to-html.openExample\",\r\n\t\t\t\t\t\"title\": \"Open an example folder\",\r\n\t\t\t\t\t\"description\": \"To start, try opening an example folder that has been preconfigured for this tutorial. This is optional, but helps for following along!\",\r\n\t\t\t\t\t\"button\": {\r\n\t\t\t\t\t\t\"title\": \"Open Example\",\r\n\t\t\t\t\t\t\"command\": \"md-to-html.openExample\"\r\n\t\t\t\t\t},\r\n\t\t\t\t\t\"media\": {\r\n\t\t\t\t\t\t\"path\": \"media/example-project.png\",\r\n\t\t\t\t\t\t\"altText\": \"example project\"\r\n\t\t\t\t\t}\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\t\"id\": \"md-to-html.showPreview\",\r\n\t\t\t\t\t\"title\": \"Preview your Markdown\",\r\n\t\t\t\t\t\"description\": \"Open a markdown file and click the \\\"Open Preview\\\" button at the top of the screen to see a preview of your file. This is technically optional, but it updates live and is helpful to check when creating your content!\",\r\n\t\t\t\t\t\"button\": {\r\n\t\t\t\t\t\t\"title\": \"Open Markdown File\",\r\n\t\t\t\t\t\t\"command\": \"md-to-html.openMarkdown\"\r\n\t\t\t\t\t},\r\n\t\t\t\t\t\"media\": {\r\n\t\t\t\t\t\t\"path\": \"media/preview.png\",\r\n\t\t\t\t\t\t\"altText\": \"preview\"\r\n\t\t\t\t\t},\r\n\t\t\t\t\t\"doneOn\": {\"command\": \"markdown.showPreviewToSide\" } \r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\t\"id\": \"md-to-html.convertToHTML\",\r\n\t\t\t\t\t\"title\": \"Create your .html file\",\r\n\t\t\t\t\t\"description\": \"To create the html file, run \\\"Convert Document to HTML\\\" from the editor actions context menu, behind the three dots top of the screen. This will create an .html file along side the markdown file.\",\r\n\t\t\t\t\t\"button\": {\r\n\t\t\t\t\t\t\"title\": \"Open Markdown File\",\r\n\t\t\t\t\t\t\"command\": \"md-to-html.openMarkdown\"\r\n\t\t\t\t\t},\r\n\t\t\t\t\t\"media\": {\r\n\t\t\t\t\t\t\"path\": \"media/convert-option.png\",\r\n\t\t\t\t\t\t\"altText\": \"showing editor actions context menu\"\r\n\t\t\t\t\t},\r\n\t\t\t\t\t\"doneOn\": {\"command\": \"md-to-html.convertToHTML\" }\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\t\"id\": \"md-to-html.openInBrowser\",\r\n\t\t\t\t\t\"title\": \"Open your .html file in the browser\",\r\n\t\t\t\t\t\"description\": \"Test that everything worked by opening the .html file in your browser. First open the html file in vscode, then right click in the editor and choose \\\"Open in Default Browser\\\".\",\r\n\t\t\t\t\t\"button\": {\r\n\t\t\t\t\t\t\"title\": \"Open HTML File\",\r\n\t\t\t\t\t\t\"command\": \"md-to-html.openHTML\"\r\n\t\t\t\t\t},\r\n\t\t\t\t\t\"media\": {\r\n\t\t\t\t\t\t\"path\": \"media/open-in-browser.png\",\r\n\t\t\t\t\t\t\"altText\": \"use editor context menu to open an .html file in your browser\"\r\n\t\t\t\t\t},\r\n\t\t\t\t\t\"doneOn\": {\"command\": \"extension.openInDefaultBrowser\" }\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\t\"id\": \"md-to-html.addKeybinding\",\r\n\t\t\t\t\t\"title\": \"Create a keyboard shortcut\",\r\n\t\t\t\t\t\"description\": \"That's all! You can share that single .html file with anyone without needing to bundle the images. To make this even easier in the future, consider adding a keybinding for the \\\"Convert Document to HTML\\\" command.\",\r\n\t\t\t\t\t\"button\": {\r\n\t\t\t\t\t\t\"title\": \"Add a keybinding\",\r\n\t\t\t\t\t\t\"command\": \"md-to-html.addKeybinding\"\r\n\t\t\t\t\t},\r\n\t\t\t\t\t\"media\": {\r\n\t\t\t\t\t\t\"path\": \"media/add-keybinding.png\",\r\n\t\t\t\t\t\t\"altText\": \"use the keybindings editor to add a keybinding for this command\"\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t]\r\n\t\t},\r\n```\r\n\r\nThis is the change to the extension contributions interface:\r\n```ts\r\nexport interface IExtensionContributions {\r\n .....\r\n\twelcomeItems?: { [category: string]: IWelcomeItem[] };\r\n\twelcomeCategories?: IWelcomeCategory[];\r\n .....\r\n}\r\n```\r\n\r\n```ts\r\nexport interface IWelcomeItem {\r\n\treadonly id: string;\r\n\treadonly title: string;\r\n\treadonly description: string;\r\n\treadonly button: { title: string } & ({ command?: never, link: string } | { command: string, link?: never }),\r\n\treadonly media: { path: string | { hc: string, light: string, dark: string }, altText: string },\r\n\treadonly doneOn?:\r\n\t| { event: string; command?: never }\r\n\t| { event?: never; command: string };\r\n\treadonly when?: string;\r\n}\r\n\r\nexport interface IWelcomeCategory {\r\n\treadonly id: string,\r\n\treadonly title: string;\r\n\treadonly description: string;\r\n\treadonly when?: string;\r\n}\r\n```\r\n\r\n\r\n\r\n", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/118084", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/118084/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/118084/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/118084/events", + "html_url": "https://github.com/microsoft/vscode/issues/118084", + "id": 821570132, + "node_id": "MDU6SXNzdWU4MjE1NzAxMzI=", + "number": 118084, + "title": "Add trigger reason to code actions", + "user": { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 290465400, + "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api", + "name": "api", + "color": "1d76db", + "default": false, + "description": "" + }, + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 431507554, + "node_id": "MDU6TGFiZWw0MzE1MDc1NTQ=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/editor-code-actions", + "name": "editor-code-actions", + "color": "c5def5", + "default": false, + "description": "Editor inplace actions (Ctrl + .)" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/144", + "html_url": "https://github.com/microsoft/vscode/milestone/144", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/144/labels", + "id": 6407294, + "node_id": "MDk6TWlsZXN0b25lNjQwNzI5NA==", + "number": 144, + "title": "March 2021", + "description": "", + "creator": { + "login": "isidorn", + "id": 1926584, + "node_id": "MDQ6VXNlcjE5MjY1ODQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/1926584?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/isidorn", + "html_url": "https://github.com/isidorn", + "followers_url": "https://api.github.com/users/isidorn/followers", + "following_url": "https://api.github.com/users/isidorn/following{/other_user}", + "gists_url": "https://api.github.com/users/isidorn/gists{/gist_id}", + "starred_url": "https://api.github.com/users/isidorn/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/isidorn/subscriptions", + "organizations_url": "https://api.github.com/users/isidorn/orgs", + "repos_url": "https://api.github.com/users/isidorn/repos", + "events_url": "https://api.github.com/users/isidorn/events{/privacy}", + "received_events_url": "https://api.github.com/users/isidorn/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 250, + "closed_issues": 281, + "state": "open", + "created_at": "2021-02-08T17:30:20Z", + "updated_at": "2021-03-16T16:43:27Z", + "due_on": null, + "closed_at": null + }, + "comments": 4, + "created_at": "2021-03-03T23:04:03Z", + "updated_at": "2021-03-16T16:08:22Z", + "closed_at": null, + "author_association": "MEMBER", + "active_lock_reason": null, + "body": "## Problem\r\n\r\nFor JS/TS, if user makes a imprecise selection (such as only selecting part of an identifier name) and then manually requests code actions, we'd like to return refactorings as if the selection were expanded. However we don't want to do this automatically for implicitly requested code actions (which cause the light bulb to show up)\r\n\r\n## Proposal\r\nAdd a trigger reason to the code action context. This would let you know if the code actions have been manually requested or were triggered automatically", + "performed_via_github_app": null, + "score": 1 + }, + { + "url": "https://api.github.com/repos/microsoft/vscode/issues/115631", + "repository_url": "https://api.github.com/repos/microsoft/vscode", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/115631/events", + "html_url": "https://github.com/microsoft/vscode/issues/115631", + "id": 799606785, + "node_id": "MDU6SXNzdWU3OTk2MDY3ODU=", + "number": 115631, + "title": "Provide a way for custom editors to process untitled files without relying on textDocument", + "user": { + "login": "lramos15", + "id": 4544166, + "node_id": "MDQ6VXNlcjQ1NDQxNjY=", + "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lramos15", + "html_url": "https://github.com/lramos15", + "followers_url": "https://api.github.com/users/lramos15/followers", + "following_url": "https://api.github.com/users/lramos15/following{/other_user}", + "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", + "organizations_url": "https://api.github.com/users/lramos15/orgs", + "repos_url": "https://api.github.com/users/lramos15/repos", + "events_url": "https://api.github.com/users/lramos15/events{/privacy}", + "received_events_url": "https://api.github.com/users/lramos15/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "id": 974714207, + "node_id": "MDU6TGFiZWw5NzQ3MTQyMDc=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-finalization", + "name": "api-finalization", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 869332220, + "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", + "name": "api-proposal", + "color": "c5def5", + "default": false, + "description": "" + }, + { + "id": 1713330180, + "node_id": "MDU6TGFiZWwxNzEzMzMwMTgw", + "url": "https://api.github.com/repos/microsoft/vscode/labels/custom-editors", + "name": "custom-editors", + "color": "c5def5", + "default": false, + "description": "Custom editor API (webview based editors)" + }, + { + "id": 272689392, + "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", + "name": "feature-request", + "color": "dcdcdc", + "default": false, + "description": "Request for new features or functionality" + }, + { + "id": 1839857516, + "node_id": "MDU6TGFiZWwxODM5ODU3NTE2", + "url": "https://api.github.com/repos/microsoft/vscode/labels/notebook", + "name": "notebook", + "color": "c5def5", + "default": false, + "description": "" + } + ], + "state": "open", + "locked": false, + "assignee": { + "login": "lramos15", + "id": 4544166, + "node_id": "MDQ6VXNlcjQ1NDQxNjY=", + "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lramos15", + "html_url": "https://github.com/lramos15", + "followers_url": "https://api.github.com/users/lramos15/followers", + "following_url": "https://api.github.com/users/lramos15/following{/other_user}", + "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", + "organizations_url": "https://api.github.com/users/lramos15/orgs", + "repos_url": "https://api.github.com/users/lramos15/repos", + "events_url": "https://api.github.com/users/lramos15/events{/privacy}", + "received_events_url": "https://api.github.com/users/lramos15/received_events", + "type": "User", + "site_admin": false + }, + "assignees": [ + { + "login": "lramos15", + "id": 4544166, + "node_id": "MDQ6VXNlcjQ1NDQxNjY=", + "avatar_url": "https://avatars.githubusercontent.com/u/4544166?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/lramos15", + "html_url": "https://github.com/lramos15", + "followers_url": "https://api.github.com/users/lramos15/followers", + "following_url": "https://api.github.com/users/lramos15/following{/other_user}", + "gists_url": "https://api.github.com/users/lramos15/gists{/gist_id}", + "starred_url": "https://api.github.com/users/lramos15/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/lramos15/subscriptions", + "organizations_url": "https://api.github.com/users/lramos15/orgs", + "repos_url": "https://api.github.com/users/lramos15/repos", + "events_url": "https://api.github.com/users/lramos15/events{/privacy}", + "received_events_url": "https://api.github.com/users/lramos15/received_events", + "type": "User", + "site_admin": false + }, + { + "login": "mjbvz", + "id": 12821956, + "node_id": "MDQ6VXNlcjEyODIxOTU2", + "avatar_url": "https://avatars.githubusercontent.com/u/12821956?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mjbvz", + "html_url": "https://github.com/mjbvz", + "followers_url": "https://api.github.com/users/mjbvz/followers", + "following_url": "https://api.github.com/users/mjbvz/following{/other_user}", + "gists_url": "https://api.github.com/users/mjbvz/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mjbvz/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mjbvz/subscriptions", + "organizations_url": "https://api.github.com/users/mjbvz/orgs", + "repos_url": "https://api.github.com/users/mjbvz/repos", + "events_url": "https://api.github.com/users/mjbvz/events{/privacy}", + "received_events_url": "https://api.github.com/users/mjbvz/received_events", + "type": "User", + "site_admin": false + } + ], + "milestone": { + "url": "https://api.github.com/repos/microsoft/vscode/milestones/144", + "html_url": "https://github.com/microsoft/vscode/milestone/144", + "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/144/labels", + "id": 6407294, + "node_id": "MDk6TWlsZXN0b25lNjQwNzI5NA==", + "number": 144, + "title": "March 2021", + "description": "", + "creator": { + "login": "isidorn", + "id": 1926584, + "node_id": "MDQ6VXNlcjE5MjY1ODQ=", + "avatar_url": "https://avatars.githubusercontent.com/u/1926584?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/isidorn", + "html_url": "https://github.com/isidorn", + "followers_url": "https://api.github.com/users/isidorn/followers", + "following_url": "https://api.github.com/users/isidorn/following{/other_user}", + "gists_url": "https://api.github.com/users/isidorn/gists{/gist_id}", + "starred_url": "https://api.github.com/users/isidorn/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/isidorn/subscriptions", + "organizations_url": "https://api.github.com/users/isidorn/orgs", + "repos_url": "https://api.github.com/users/isidorn/repos", + "events_url": "https://api.github.com/users/isidorn/events{/privacy}", + "received_events_url": "https://api.github.com/users/isidorn/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 250, + "closed_issues": 281, + "state": "open", + "created_at": "2021-02-08T17:30:20Z", + "updated_at": "2021-03-16T16:43:27Z", "due_on": null, "closed_at": null }, @@ -1190,11 +1490,11 @@ "type": "User", "site_admin": false }, - "open_issues": 252, - "closed_issues": 190, + "open_issues": 250, + "closed_issues": 281, "state": "open", "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", + "updated_at": "2021-03-16T16:43:27Z", "due_on": null, "closed_at": null }, @@ -1340,17 +1640,17 @@ "type": "User", "site_admin": false }, - "open_issues": 252, - "closed_issues": 190, + "open_issues": 250, + "closed_issues": 281, "state": "open", "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", + "updated_at": "2021-03-16T16:43:27Z", "due_on": null, "closed_at": null }, - "comments": 16, + "comments": 17, "created_at": "2021-02-02T15:37:45Z", - "updated_at": "2021-03-08T14:51:56Z", + "updated_at": "2021-03-16T00:33:17Z", "closed_at": null, "author_association": "MEMBER", "active_lock_reason": null, @@ -1500,11 +1800,11 @@ "type": "User", "site_admin": false }, - "open_issues": 252, - "closed_issues": 190, + "open_issues": 250, + "closed_issues": 281, "state": "open", "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", + "updated_at": "2021-03-16T16:43:27Z", "due_on": null, "closed_at": null }, @@ -1678,11 +1978,11 @@ "type": "User", "site_admin": false }, - "open_issues": 252, - "closed_issues": 190, + "open_issues": 250, + "closed_issues": 281, "state": "open", "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", + "updated_at": "2021-03-16T16:43:27Z", "due_on": null, "closed_at": null }, @@ -1696,157 +1996,6 @@ "performed_via_github_app": null, "score": 1 }, - { - "url": "https://api.github.com/repos/microsoft/vscode/issues/110267", - "repository_url": "https://api.github.com/repos/microsoft/vscode", - "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/110267/labels{/name}", - "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/110267/comments", - "events_url": "https://api.github.com/repos/microsoft/vscode/issues/110267/events", - "html_url": "https://github.com/microsoft/vscode/issues/110267", - "id": 739391278, - "node_id": "MDU6SXNzdWU3MzkzOTEyNzg=", - "number": 110267, - "title": "Pass telemetry enablement to extensions", - "user": { - "login": "sbatten", - "id": 6561887, - "node_id": "MDQ6VXNlcjY1NjE4ODc=", - "avatar_url": "https://avatars.githubusercontent.com/u/6561887?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/sbatten", - "html_url": "https://github.com/sbatten", - "followers_url": "https://api.github.com/users/sbatten/followers", - "following_url": "https://api.github.com/users/sbatten/following{/other_user}", - "gists_url": "https://api.github.com/users/sbatten/gists{/gist_id}", - "starred_url": "https://api.github.com/users/sbatten/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/sbatten/subscriptions", - "organizations_url": "https://api.github.com/users/sbatten/orgs", - "repos_url": "https://api.github.com/users/sbatten/repos", - "events_url": "https://api.github.com/users/sbatten/events{/privacy}", - "received_events_url": "https://api.github.com/users/sbatten/received_events", - "type": "User", - "site_admin": false - }, - "labels": [ - { - "id": 869332220, - "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/api-proposal", - "name": "api-proposal", - "color": "c5def5", - "default": false, - "description": "" - }, - { - "id": 272689392, - "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/feature-request", - "name": "feature-request", - "color": "dcdcdc", - "default": false, - "description": "Request for new features or functionality" - }, - { - "id": 414580097, - "node_id": "MDU6TGFiZWw0MTQ1ODAwOTc=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/telemetry", - "name": "telemetry", - "color": "c5def5", - "default": false, - "description": "Telemetry system issues" - } - ], - "state": "open", - "locked": false, - "assignee": { - "login": "sbatten", - "id": 6561887, - "node_id": "MDQ6VXNlcjY1NjE4ODc=", - "avatar_url": "https://avatars.githubusercontent.com/u/6561887?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/sbatten", - "html_url": "https://github.com/sbatten", - "followers_url": "https://api.github.com/users/sbatten/followers", - "following_url": "https://api.github.com/users/sbatten/following{/other_user}", - "gists_url": "https://api.github.com/users/sbatten/gists{/gist_id}", - "starred_url": "https://api.github.com/users/sbatten/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/sbatten/subscriptions", - "organizations_url": "https://api.github.com/users/sbatten/orgs", - "repos_url": "https://api.github.com/users/sbatten/repos", - "events_url": "https://api.github.com/users/sbatten/events{/privacy}", - "received_events_url": "https://api.github.com/users/sbatten/received_events", - "type": "User", - "site_admin": false - }, - "assignees": [ - { - "login": "sbatten", - "id": 6561887, - "node_id": "MDQ6VXNlcjY1NjE4ODc=", - "avatar_url": "https://avatars.githubusercontent.com/u/6561887?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/sbatten", - "html_url": "https://github.com/sbatten", - "followers_url": "https://api.github.com/users/sbatten/followers", - "following_url": "https://api.github.com/users/sbatten/following{/other_user}", - "gists_url": "https://api.github.com/users/sbatten/gists{/gist_id}", - "starred_url": "https://api.github.com/users/sbatten/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/sbatten/subscriptions", - "organizations_url": "https://api.github.com/users/sbatten/orgs", - "repos_url": "https://api.github.com/users/sbatten/repos", - "events_url": "https://api.github.com/users/sbatten/events{/privacy}", - "received_events_url": "https://api.github.com/users/sbatten/received_events", - "type": "User", - "site_admin": false - } - ], - "milestone": { - "url": "https://api.github.com/repos/microsoft/vscode/milestones/144", - "html_url": "https://github.com/microsoft/vscode/milestone/144", - "labels_url": "https://api.github.com/repos/microsoft/vscode/milestones/144/labels", - "id": 6407294, - "node_id": "MDk6TWlsZXN0b25lNjQwNzI5NA==", - "number": 144, - "title": "March 2021", - "description": "", - "creator": { - "login": "isidorn", - "id": 1926584, - "node_id": "MDQ6VXNlcjE5MjY1ODQ=", - "avatar_url": "https://avatars.githubusercontent.com/u/1926584?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/isidorn", - "html_url": "https://github.com/isidorn", - "followers_url": "https://api.github.com/users/isidorn/followers", - "following_url": "https://api.github.com/users/isidorn/following{/other_user}", - "gists_url": "https://api.github.com/users/isidorn/gists{/gist_id}", - "starred_url": "https://api.github.com/users/isidorn/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/isidorn/subscriptions", - "organizations_url": "https://api.github.com/users/isidorn/orgs", - "repos_url": "https://api.github.com/users/isidorn/repos", - "events_url": "https://api.github.com/users/isidorn/events{/privacy}", - "received_events_url": "https://api.github.com/users/isidorn/received_events", - "type": "User", - "site_admin": false - }, - "open_issues": 252, - "closed_issues": 190, - "state": "open", - "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", - "due_on": null, - "closed_at": null - }, - "comments": 2, - "created_at": "2020-11-09T21:56:01Z", - "updated_at": "2021-03-08T19:03:37Z", - "closed_at": null, - "author_association": "MEMBER", - "active_lock_reason": null, - "body": "Right now extensions use the configuration to determine if they should send telemetry; however, the cli flag is not passed to the extension host.\r\n\r\n```ts\r\nexport namespace env {\r\n export const enableTelemetry: boolean;\r\n\r\n export const onDidChangeEnableTelemetry: Event;\r\n}\r\n```\r\n", - "performed_via_github_app": null, - "score": 1 - }, { "url": "https://api.github.com/repos/microsoft/vscode/issues/109277", "repository_url": "https://api.github.com/repos/microsoft/vscode", @@ -1980,11 +2129,11 @@ "type": "User", "site_admin": false }, - "open_issues": 252, - "closed_issues": 190, + "open_issues": 250, + "closed_issues": 281, "state": "open", "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", + "updated_at": "2021-03-16T16:43:27Z", "due_on": null, "closed_at": null }, @@ -2160,17 +2309,17 @@ "type": "User", "site_admin": false }, - "open_issues": 252, - "closed_issues": 190, + "open_issues": 250, + "closed_issues": 281, "state": "open", "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", + "updated_at": "2021-03-16T16:43:27Z", "due_on": null, "closed_at": null }, - "comments": 61, + "comments": 62, "created_at": "2020-09-25T17:19:53Z", - "updated_at": "2021-02-22T23:40:30Z", + "updated_at": "2021-03-16T08:33:01Z", "closed_at": null, "author_association": "MEMBER", "active_lock_reason": null, @@ -2179,46 +2328,37 @@ "score": 1 }, { - "url": "https://api.github.com/repos/microsoft/vscode/issues/105690", + "url": "https://api.github.com/repos/microsoft/vscode/issues/77423", "repository_url": "https://api.github.com/repos/microsoft/vscode", - "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/labels{/name}", - "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/comments", - "events_url": "https://api.github.com/repos/microsoft/vscode/issues/105690/events", - "html_url": "https://github.com/microsoft/vscode/issues/105690", - "id": 688793797, - "node_id": "MDU6SXNzdWU2ODg3OTM3OTc=", - "number": 105690, - "title": "Extension API for Inline Values", + "labels_url": "https://api.github.com/repos/microsoft/vscode/issues/77423/labels{/name}", + "comments_url": "https://api.github.com/repos/microsoft/vscode/issues/77423/comments", + "events_url": "https://api.github.com/repos/microsoft/vscode/issues/77423/events", + "html_url": "https://github.com/microsoft/vscode/issues/77423", + "id": 468297758, + "node_id": "MDU6SXNzdWU0NjgyOTc3NTg=", + "number": 77423, + "title": "Add title property to QuickPickOptions (showQuickPick)", "user": { - "login": "weinand", - "id": 1898161, - "node_id": "MDQ6VXNlcjE4OTgxNjE=", - "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "login": "letmaik", + "id": 530988, + "node_id": "MDQ6VXNlcjUzMDk4OA==", + "avatar_url": "https://avatars.githubusercontent.com/u/530988?v=4", "gravatar_id": "", - "url": "https://api.github.com/users/weinand", - "html_url": "https://github.com/weinand", - "followers_url": "https://api.github.com/users/weinand/followers", - "following_url": "https://api.github.com/users/weinand/following{/other_user}", - "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", - "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", - "organizations_url": "https://api.github.com/users/weinand/orgs", - "repos_url": "https://api.github.com/users/weinand/repos", - "events_url": "https://api.github.com/users/weinand/events{/privacy}", - "received_events_url": "https://api.github.com/users/weinand/received_events", + "url": "https://api.github.com/users/letmaik", + "html_url": "https://github.com/letmaik", + "followers_url": "https://api.github.com/users/letmaik/followers", + "following_url": "https://api.github.com/users/letmaik/following{/other_user}", + "gists_url": "https://api.github.com/users/letmaik/gists{/gist_id}", + "starred_url": "https://api.github.com/users/letmaik/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/letmaik/subscriptions", + "organizations_url": "https://api.github.com/users/letmaik/orgs", + "repos_url": "https://api.github.com/users/letmaik/repos", + "events_url": "https://api.github.com/users/letmaik/events{/privacy}", + "received_events_url": "https://api.github.com/users/letmaik/received_events", "type": "User", "site_admin": false }, "labels": [ - { - "id": 290465400, - "node_id": "MDU6TGFiZWwyOTA0NjU0MDA=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/api", - "name": "api", - "color": "1d76db", - "default": false, - "description": "" - }, { "id": 869332220, "node_id": "MDU6TGFiZWw4NjkzMzIyMjA=", @@ -2228,15 +2368,6 @@ "default": false, "description": "" }, - { - "id": 291054922, - "node_id": "MDU6TGFiZWwyOTEwNTQ5MjI=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/debug", - "name": "debug", - "color": "1d76db", - "default": false, - "description": "Debug viewlet, configurations, breakpoints, adapter issues" - }, { "id": 272689392, "node_id": "MDU6TGFiZWwyNzI2ODkzOTI=", @@ -2247,55 +2378,75 @@ "description": "Request for new features or functionality" }, { - "id": 635068821, - "node_id": "MDU6TGFiZWw2MzUwNjg4MjE=", - "url": "https://api.github.com/repos/microsoft/vscode/labels/on-testplan", - "name": "on-testplan", - "color": "E2A1C2", + "id": 527005453, + "node_id": "MDU6TGFiZWw1MjcwMDU0NTM=", + "url": "https://api.github.com/repos/microsoft/vscode/labels/quick-pick", + "name": "quick-pick", + "color": "c5def5", "default": false, - "description": "VS Code - Issue added to test plan" + "description": "Quick-pick widget issues" } ], "state": "open", "locked": false, "assignee": { - "login": "weinand", - "id": 1898161, - "node_id": "MDQ6VXNlcjE4OTgxNjE=", - "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "login": "TylerLeonhardt", + "id": 2644648, + "node_id": "MDQ6VXNlcjI2NDQ2NDg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2644648?v=4", "gravatar_id": "", - "url": "https://api.github.com/users/weinand", - "html_url": "https://github.com/weinand", - "followers_url": "https://api.github.com/users/weinand/followers", - "following_url": "https://api.github.com/users/weinand/following{/other_user}", - "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", - "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", - "organizations_url": "https://api.github.com/users/weinand/orgs", - "repos_url": "https://api.github.com/users/weinand/repos", - "events_url": "https://api.github.com/users/weinand/events{/privacy}", - "received_events_url": "https://api.github.com/users/weinand/received_events", + "url": "https://api.github.com/users/TylerLeonhardt", + "html_url": "https://github.com/TylerLeonhardt", + "followers_url": "https://api.github.com/users/TylerLeonhardt/followers", + "following_url": "https://api.github.com/users/TylerLeonhardt/following{/other_user}", + "gists_url": "https://api.github.com/users/TylerLeonhardt/gists{/gist_id}", + "starred_url": "https://api.github.com/users/TylerLeonhardt/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/TylerLeonhardt/subscriptions", + "organizations_url": "https://api.github.com/users/TylerLeonhardt/orgs", + "repos_url": "https://api.github.com/users/TylerLeonhardt/repos", + "events_url": "https://api.github.com/users/TylerLeonhardt/events{/privacy}", + "received_events_url": "https://api.github.com/users/TylerLeonhardt/received_events", "type": "User", "site_admin": false }, "assignees": [ { - "login": "weinand", - "id": 1898161, - "node_id": "MDQ6VXNlcjE4OTgxNjE=", - "avatar_url": "https://avatars.githubusercontent.com/u/1898161?v=4", + "login": "TylerLeonhardt", + "id": 2644648, + "node_id": "MDQ6VXNlcjI2NDQ2NDg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2644648?v=4", "gravatar_id": "", - "url": "https://api.github.com/users/weinand", - "html_url": "https://github.com/weinand", - "followers_url": "https://api.github.com/users/weinand/followers", - "following_url": "https://api.github.com/users/weinand/following{/other_user}", - "gists_url": "https://api.github.com/users/weinand/gists{/gist_id}", - "starred_url": "https://api.github.com/users/weinand/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/weinand/subscriptions", - "organizations_url": "https://api.github.com/users/weinand/orgs", - "repos_url": "https://api.github.com/users/weinand/repos", - "events_url": "https://api.github.com/users/weinand/events{/privacy}", - "received_events_url": "https://api.github.com/users/weinand/received_events", + "url": "https://api.github.com/users/TylerLeonhardt", + "html_url": "https://github.com/TylerLeonhardt", + "followers_url": "https://api.github.com/users/TylerLeonhardt/followers", + "following_url": "https://api.github.com/users/TylerLeonhardt/following{/other_user}", + "gists_url": "https://api.github.com/users/TylerLeonhardt/gists{/gist_id}", + "starred_url": "https://api.github.com/users/TylerLeonhardt/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/TylerLeonhardt/subscriptions", + "organizations_url": "https://api.github.com/users/TylerLeonhardt/orgs", + "repos_url": "https://api.github.com/users/TylerLeonhardt/repos", + "events_url": "https://api.github.com/users/TylerLeonhardt/events{/privacy}", + "received_events_url": "https://api.github.com/users/TylerLeonhardt/received_events", + "type": "User", + "site_admin": false + }, + { + "login": "chrmarti", + "id": 9205389, + "node_id": "MDQ6VXNlcjkyMDUzODk=", + "avatar_url": "https://avatars.githubusercontent.com/u/9205389?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/chrmarti", + "html_url": "https://github.com/chrmarti", + "followers_url": "https://api.github.com/users/chrmarti/followers", + "following_url": "https://api.github.com/users/chrmarti/following{/other_user}", + "gists_url": "https://api.github.com/users/chrmarti/gists{/gist_id}", + "starred_url": "https://api.github.com/users/chrmarti/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/chrmarti/subscriptions", + "organizations_url": "https://api.github.com/users/chrmarti/orgs", + "repos_url": "https://api.github.com/users/chrmarti/repos", + "events_url": "https://api.github.com/users/chrmarti/events{/privacy}", + "received_events_url": "https://api.github.com/users/chrmarti/received_events", "type": "User", "site_admin": false } @@ -2329,21 +2480,21 @@ "type": "User", "site_admin": false }, - "open_issues": 252, - "closed_issues": 190, + "open_issues": 250, + "closed_issues": 281, "state": "open", "created_at": "2021-02-08T17:30:20Z", - "updated_at": "2021-03-09T16:43:04Z", + "updated_at": "2021-03-16T16:43:27Z", "due_on": null, "closed_at": null }, - "comments": 18, - "created_at": "2020-08-30T21:21:23Z", - "updated_at": "2021-03-08T08:55:19Z", + "comments": 2, + "created_at": "2019-07-15T19:31:48Z", + "updated_at": "2021-03-16T16:43:27Z", "closed_at": null, "author_association": "MEMBER", "active_lock_reason": null, - "body": "Today the \"Show Inline Values\" feature of VS Code's debugger is based on a generic implementation in the VS Code core and provides neither customisability through settings, nor extensibility via extensions.\r\n\r\nAs a consequence, it is not a perfect fit for all languages (e.g. #101797) and sometimes even shows incorrect values because it doesn't understand the scope regions of the underlying language. \r\n\r\nThis features asks for an extension API that either replaces the built-in implementation completely or allows to replace parts of the implementation with custom code.\r\n", + "body": "Currently, `title` can only be set when going through the more complex `createQuickPick` API. It would be great if the title could be set via `QuickPickOptions` when using the simpler `showQuickPick`.", "performed_via_github_app": null, "score": 1 } @@ -2351,7 +2502,7 @@ }, { "mime": "text/markdown", - "value": "- [#118084](https://github.com/microsoft/vscode/issues/118084 \"Add trigger reason to code actions\") Add trigger reason to code actions [api, api-proposal, editor-code-actions]- [@mjbvz](https://github.com/mjbvz \"Issue 118084 is assigned to mjbvz\")\n\n- [#117506](https://github.com/microsoft/vscode/issues/117506 \"Add an API for a notebook kernel to modify its list of supported languages at runtime.\") Add an API for a notebook kernel to modify its list of supported languages at runtime. [api, api-proposal, feature-request, notebook]- [@rebornix](https://github.com/rebornix \"Issue 117506 is assigned to rebornix\")\n\n- [#117058](https://github.com/microsoft/vscode/issues/117058 \"add property to extension api for new install\") add property to extension api for new install [api-proposal]- [@sbatten](https://github.com/sbatten \"Issue 117058 is assigned to sbatten\")\n\n- [#116906](https://github.com/microsoft/vscode/issues/116906 \"Add extension ID and version to ExtensionContext\") Add extension ID and version to ExtensionContext [api, api-proposal, feature-request]- [@eamodio](https://github.com/eamodio \"Issue 116906 is assigned to eamodio\")\n\n- [#115631](https://github.com/microsoft/vscode/issues/115631 \"Provide a way for custom editors to process untitled files without relying on textDocument\") Provide a way for custom editors to process untitled files without relying on textDocument [api-finalization, api-proposal, custom-editors, feature-request, notebook]- [@lramos15](https://github.com/lramos15 \"Issue 115631 is assigned to lramos15\")\n\n- [#115626](https://github.com/microsoft/vscode/issues/115626 \"Microsoft Auth Provider should support overriding client id and tenant id\") Microsoft Auth Provider should support overriding client id and tenant id [api-proposal, authentication]- [@TylerLeonhardt](https://github.com/TylerLeonhardt \"Issue 115626 is assigned to TylerLeonhardt\")\n\n- [#115616](https://github.com/microsoft/vscode/issues/115616 \"Provide extension API to exclude ports from forwarding\") Provide extension API to exclude ports from forwarding [api, api-proposal, feature-request, ghcs-in-progress, remote-explorer]\n- [#114898](https://github.com/microsoft/vscode/issues/114898 \"[ext-api] provide Pseudoterminal.onDidChangeName event\") [ext-api] provide Pseudoterminal.onDidChangeName event [api, api-proposal, feature-request, help wanted]- [@Tyriar](https://github.com/Tyriar \"Issue 114898 is assigned to Tyriar\")\n\n- [#111521](https://github.com/microsoft/vscode/issues/111521 \"Support a way to have nightly/insiders versions of extensions not activate if \"main\" extension is installed/enabled\") Support a way to have nightly/insiders versions of extensions not activate if \"main\" extension is installed/enabled [api, api-proposal, extensions, extensions-development, feature-request, marketplace]- [@sandy081](https://github.com/sandy081 \"Issue 111521 is assigned to sandy081\")\n\n- [#110267](https://github.com/microsoft/vscode/issues/110267 \"Pass telemetry enablement to extensions\") Pass telemetry enablement to extensions [api-proposal, feature-request, telemetry]- [@sbatten](https://github.com/sbatten \"Issue 110267 is assigned to sbatten\")\n\n- [#109277](https://github.com/microsoft/vscode/issues/109277 \"Let extensions hook into url opening\") Let extensions hook into url opening [api, api-proposal, under-discussion]- [@mjbvz](https://github.com/mjbvz \"Issue 109277 is assigned to mjbvz\")\n\n- [#107467](https://github.com/microsoft/vscode/issues/107467 \"Testing in VS Code\") Testing in VS Code [api-proposal, plan-item, testing, under-discussion]- [@connor4312](https://github.com/connor4312 \"Issue 107467 is assigned to connor4312\")\n\n- [#105690](https://github.com/microsoft/vscode/issues/105690 \"Extension API for Inline Values\") Extension API for Inline Values [api, api-proposal, debug, feature-request, on-testplan]- [@weinand](https://github.com/weinand \"Issue 105690 is assigned to weinand\")\n\n" + "value": "- [#119097](https://github.com/microsoft/vscode/issues/119097 \"Allow extensions to contribute getting started content\") Allow extensions to contribute getting started content [api, api-proposal, feature-request, getting-started]- [@JacksonKearl](https://github.com/JacksonKearl \"Issue 119097 is assigned to JacksonKearl\")\n\n- [#118084](https://github.com/microsoft/vscode/issues/118084 \"Add trigger reason to code actions\") Add trigger reason to code actions [api, api-proposal, editor-code-actions]- [@mjbvz](https://github.com/mjbvz \"Issue 118084 is assigned to mjbvz\")\n\n- [#115631](https://github.com/microsoft/vscode/issues/115631 \"Provide a way for custom editors to process untitled files without relying on textDocument\") Provide a way for custom editors to process untitled files without relying on textDocument [api-finalization, api-proposal, custom-editors, feature-request, notebook]- [@lramos15](https://github.com/lramos15 \"Issue 115631 is assigned to lramos15\")\n\n- [#115626](https://github.com/microsoft/vscode/issues/115626 \"Microsoft Auth Provider should support overriding client id and tenant id\") Microsoft Auth Provider should support overriding client id and tenant id [api-proposal, authentication]- [@TylerLeonhardt](https://github.com/TylerLeonhardt \"Issue 115626 is assigned to TylerLeonhardt\")\n\n- [#115616](https://github.com/microsoft/vscode/issues/115616 \"Provide extension API to exclude ports from forwarding\") Provide extension API to exclude ports from forwarding [api, api-proposal, feature-request, ghcs-in-progress, remote-explorer]\n- [#114898](https://github.com/microsoft/vscode/issues/114898 \"[ext-api] provide Pseudoterminal.onDidChangeName event\") [ext-api] provide Pseudoterminal.onDidChangeName event [api, api-proposal, feature-request, help wanted]- [@Tyriar](https://github.com/Tyriar \"Issue 114898 is assigned to Tyriar\")\n\n- [#111521](https://github.com/microsoft/vscode/issues/111521 \"Support a way to have nightly/insiders versions of extensions not activate if \"main\" extension is installed/enabled\") Support a way to have nightly/insiders versions of extensions not activate if \"main\" extension is installed/enabled [api, api-proposal, extensions, extensions-development, feature-request, marketplace]- [@sandy081](https://github.com/sandy081 \"Issue 111521 is assigned to sandy081\")\n\n- [#109277](https://github.com/microsoft/vscode/issues/109277 \"Let extensions hook into url opening\") Let extensions hook into url opening [api, api-proposal, under-discussion]- [@mjbvz](https://github.com/mjbvz \"Issue 109277 is assigned to mjbvz\")\n\n- [#107467](https://github.com/microsoft/vscode/issues/107467 \"Testing in VS Code\") Testing in VS Code [api-proposal, plan-item, testing, under-discussion]- [@connor4312](https://github.com/connor4312 \"Issue 107467 is assigned to connor4312\")\n\n- [#77423](https://github.com/microsoft/vscode/issues/77423 \"Add title property to QuickPickOptions (showQuickPick)\") Add title property to QuickPickOptions (showQuickPick) [api-proposal, feature-request, quick-pick]- [@TylerLeonhardt](https://github.com/TylerLeonhardt \"Issue 77423 is assigned to TylerLeonhardt\")\n\n" } ] } diff --git a/README.md b/README.md index eb792510e1c..0a9a62d9b7f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## The Repository -This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Studio Code](https://code.visualstudio.com) product. Not only do we work on code and issues here, we also publish our [roadmap](https://github.com/microsoft/vscode/wiki/Roadmap), [monthly iteration plans](https://github.com/microsoft/vscode/wiki/Iteration-Plans), and our [endgame plans](https://github.com/microsoft/vscode/wiki/Running-the-Endgame). This source code is available to everyone under the standard [MIT license](https://github.com/microsoft/vscode/blob/main/LICENSE.txt). +This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Studio Code](https://code.visualstudio.com) product together with the community. Not only do we work on code and issues here, we also publish our [roadmap](https://github.com/microsoft/vscode/wiki/Roadmap), [monthly iteration plans](https://github.com/microsoft/vscode/wiki/Iteration-Plans), and our [endgame plans](https://github.com/microsoft/vscode/wiki/Running-the-Endgame). This source code is available to everyone under the standard [MIT license](https://github.com/microsoft/vscode/blob/main/LICENSE.txt). ## Visual Studio Code diff --git a/build/filters.js b/build/filters.js index f0270742e0f..8c0b94dc3f8 100644 --- a/build/filters.js +++ b/build/filters.js @@ -51,6 +51,7 @@ module.exports.indentationFilter = [ '!test/monaco/out/**', '!test/smoke/out/**', '!extensions/typescript-language-features/test-workspace/**', + '!extensions/notebook-markdown-extensions/notebook-out/**', '!extensions/vscode-api-tests/testWorkspace/**', '!extensions/vscode-api-tests/testWorkspace2/**', '!extensions/vscode-custom-editor-tests/test-workspace/**', @@ -118,6 +119,7 @@ module.exports.copyrightFilter = [ '!resources/completions/**', '!extensions/configuration-editing/build/inline-allOf.ts', '!extensions/markdown-language-features/media/highlight.css', + '!extensions/notebook-markdown-extensions/notebook-out/**', '!extensions/html-language-features/server/src/modes/typescript/*', '!extensions/*/server/bin/*', '!src/vs/editor/test/node/classification/typescript-test.ts', diff --git a/extensions/configuration-editing/schemas/attachContainer.schema.json b/extensions/configuration-editing/schemas/attachContainer.schema.json index d7846aad40e..e52d8a12c8e 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -22,9 +22,9 @@ "portsAttributes": { "type": "object", "patternProperties": { - "^\\d+(\\-\\d+)?$": { + "(^\\d+(\\-\\d+)?$)|(.+)": { "type": "object", - "description": "A port, or range of ports (ex. \"40000-55000\") that the attributes should apply to", + "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { "onAutoForward": { "type": "string", @@ -53,27 +53,57 @@ "label": { "type": "string", "description": "Label that will be shown in the UI for this port.", - "default": "Labeled Port" + "default": "Application" } }, "default": { - "label": "Labeled Port", + "label": "Application", "onAutoForward": "notify" } } }, - "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Labeled Port\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n}\n```", + "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```", "defaultSnippets": [ { "body": { "${1:3000}": { - "label": "${2:My Port}", + "label": "${2:Application}", "onAutoForward": "notify" } } } ], - "errorMessage": "Must be a port number or a range of port numbers", + "additionalProperties": false + }, + "unconfiguredPortsAttributes": { + "type": "object", + "properties": { + "onAutoForward": { + "type": "string", + "enum": ["notify", "openBrowser", "openPreview", "silent", "ignore"], + "enumDescriptions": [ + "Shows a notification when a port is automatically forwarded.", + "Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.", + "Opens a preview in the same window when the port is automatically forwarded.", + "Shows no notification and takes no action when this port is automatically forwarded.", + "This port will not be automatically forwarded." + ], + "description": "Defines the action that occurs when the port is discovered for automatic forwarding", + "default": "notify" + }, + "elevateIfNeeded": { + "type": "boolean", + "description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.", + "default": false + }, + "label": { + "type": "string", + "description": "Label that will be shown in the UI for this port.", + "default": "Application" + } + }, + "defaultSnippets": [{ "body": { "onAutoForward": "ignore" } }], + "markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```", "additionalProperties": false }, "settings": { diff --git a/extensions/configuration-editing/schemas/devContainer.schema.generated.json b/extensions/configuration-editing/schemas/devContainer.schema.generated.json index 91f9249af80..5115e778e79 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.generated.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.generated.json @@ -130,9 +130,9 @@ "portsAttributes": { "type": "object", "patternProperties": { - "^\\d+(\\-\\d+)?$": { + "(^\\d+(\\-\\d+)?$)|(.+)": { "type": "object", - "description": "A port, or range of ports (ex. \"40000-55000\") that the attributes should apply to", + "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { "onAutoForward": { "type": "string", @@ -161,28 +161,70 @@ "label": { "type": "string", "description": "Label that will be shown in the UI for this port.", - "default": "Labeled Port" + "default": "Application" } }, "default": { - "label": "Labeled Port", + "label": "Application", "onAutoForward": "notify" }, "additionalProperties": false } }, - "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Labeled Port\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n}\n```", + "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```", "defaultSnippets": [ { "body": { "${1:3000}": { - "label": "${2:My Port}", + "label": "${2:Application}", "onAutoForward": "notify" } } } ], - "errorMessage": "Must be a port number or a range of port numbers", + "additionalProperties": false + }, + "unconfiguredPortsAttributes": { + "type": "object", + "properties": { + "onAutoForward": { + "type": "string", + "enum": [ + "notify", + "openBrowser", + "openPreview", + "silent", + "ignore" + ], + "enumDescriptions": [ + "Shows a notification when a port is automatically forwarded.", + "Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.", + "Opens a preview in the same window when the port is automatically forwarded.", + "Shows no notification and takes no action when this port is automatically forwarded.", + "This port will not be automatically forwarded." + ], + "description": "Defines the action that occurs when the port is discovered for automatic forwarding", + "default": "notify" + }, + "elevateIfNeeded": { + "type": "boolean", + "description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.", + "default": false + }, + "label": { + "type": "string", + "description": "Label that will be shown in the UI for this port.", + "default": "Application" + } + }, + "defaultSnippets": [ + { + "body": { + "onAutoForward": "ignore" + } + } + ], + "markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```", "additionalProperties": false }, "remoteEnv": { @@ -387,9 +429,9 @@ "portsAttributes": { "type": "object", "patternProperties": { - "^\\d+(\\-\\d+)?$": { + "(^\\d+(\\-\\d+)?$)|(.+)": { "type": "object", - "description": "A port, or range of ports (ex. \"40000-55000\") that the attributes should apply to", + "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { "onAutoForward": { "type": "string", @@ -418,28 +460,70 @@ "label": { "type": "string", "description": "Label that will be shown in the UI for this port.", - "default": "Labeled Port" + "default": "Application" } }, "default": { - "label": "Labeled Port", + "label": "Application", "onAutoForward": "notify" }, "additionalProperties": false } }, - "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Labeled Port\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n}\n```", + "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```", "defaultSnippets": [ { "body": { "${1:3000}": { - "label": "${2:My Port}", + "label": "${2:Application}", "onAutoForward": "notify" } } } ], - "errorMessage": "Must be a port number or a range of port numbers", + "additionalProperties": false + }, + "unconfiguredPortsAttributes": { + "type": "object", + "properties": { + "onAutoForward": { + "type": "string", + "enum": [ + "notify", + "openBrowser", + "openPreview", + "silent", + "ignore" + ], + "enumDescriptions": [ + "Shows a notification when a port is automatically forwarded.", + "Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.", + "Opens a preview in the same window when the port is automatically forwarded.", + "Shows no notification and takes no action when this port is automatically forwarded.", + "This port will not be automatically forwarded." + ], + "description": "Defines the action that occurs when the port is discovered for automatic forwarding", + "default": "notify" + }, + "elevateIfNeeded": { + "type": "boolean", + "description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.", + "default": false + }, + "label": { + "type": "string", + "description": "Label that will be shown in the UI for this port.", + "default": "Application" + } + }, + "defaultSnippets": [ + { + "body": { + "onAutoForward": "ignore" + } + } + ], + "markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```", "additionalProperties": false }, "remoteEnv": { @@ -620,9 +704,9 @@ "portsAttributes": { "type": "object", "patternProperties": { - "^\\d+(\\-\\d+)?$": { + "(^\\d+(\\-\\d+)?$)|(.+)": { "type": "object", - "description": "A port, or range of ports (ex. \"40000-55000\") that the attributes should apply to", + "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { "onAutoForward": { "type": "string", @@ -651,28 +735,70 @@ "label": { "type": "string", "description": "Label that will be shown in the UI for this port.", - "default": "Labeled Port" + "default": "Application" } }, "default": { - "label": "Labeled Port", + "label": "Application", "onAutoForward": "notify" }, "additionalProperties": false } }, - "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Labeled Port\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n}\n```", + "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```", "defaultSnippets": [ { "body": { "${1:3000}": { - "label": "${2:My Port}", + "label": "${2:Application}", "onAutoForward": "notify" } } } ], - "errorMessage": "Must be a port number or a range of port numbers", + "additionalProperties": false + }, + "unconfiguredPortsAttributes": { + "type": "object", + "properties": { + "onAutoForward": { + "type": "string", + "enum": [ + "notify", + "openBrowser", + "openPreview", + "silent", + "ignore" + ], + "enumDescriptions": [ + "Shows a notification when a port is automatically forwarded.", + "Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.", + "Opens a preview in the same window when the port is automatically forwarded.", + "Shows no notification and takes no action when this port is automatically forwarded.", + "This port will not be automatically forwarded." + ], + "description": "Defines the action that occurs when the port is discovered for automatic forwarding", + "default": "notify" + }, + "elevateIfNeeded": { + "type": "boolean", + "description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.", + "default": false + }, + "label": { + "type": "string", + "description": "Label that will be shown in the UI for this port.", + "default": "Application" + } + }, + "defaultSnippets": [ + { + "body": { + "onAutoForward": "ignore" + } + } + ], + "markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```", "additionalProperties": false }, "remoteEnv": { @@ -819,9 +945,9 @@ "portsAttributes": { "type": "object", "patternProperties": { - "^\\d+(\\-\\d+)?$": { + "(^\\d+(\\-\\d+)?$)|(.+)": { "type": "object", - "description": "A port, or range of ports (ex. \"40000-55000\") that the attributes should apply to", + "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { "onAutoForward": { "type": "string", @@ -850,28 +976,70 @@ "label": { "type": "string", "description": "Label that will be shown in the UI for this port.", - "default": "Labeled Port" + "default": "Application" } }, "default": { - "label": "Labeled Port", + "label": "Application", "onAutoForward": "notify" }, "additionalProperties": false } }, - "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Labeled Port\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n}\n```", + "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```", "defaultSnippets": [ { "body": { "${1:3000}": { - "label": "${2:My Port}", + "label": "${2:Application}", "onAutoForward": "notify" } } } ], - "errorMessage": "Must be a port number or a range of port numbers", + "additionalProperties": false + }, + "unconfiguredPortsAttributes": { + "type": "object", + "properties": { + "onAutoForward": { + "type": "string", + "enum": [ + "notify", + "openBrowser", + "openPreview", + "silent", + "ignore" + ], + "enumDescriptions": [ + "Shows a notification when a port is automatically forwarded.", + "Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.", + "Opens a preview in the same window when the port is automatically forwarded.", + "Shows no notification and takes no action when this port is automatically forwarded.", + "This port will not be automatically forwarded." + ], + "description": "Defines the action that occurs when the port is discovered for automatic forwarding", + "default": "notify" + }, + "elevateIfNeeded": { + "type": "boolean", + "description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.", + "default": false + }, + "label": { + "type": "string", + "description": "Label that will be shown in the UI for this port.", + "default": "Application" + } + }, + "defaultSnippets": [ + { + "body": { + "onAutoForward": "ignore" + } + } + ], + "markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```", "additionalProperties": false }, "remoteEnv": { @@ -987,9 +1155,9 @@ "portsAttributes": { "type": "object", "patternProperties": { - "^\\d+(\\-\\d+)?$": { + "(^\\d+(\\-\\d+)?$)|(.+)": { "type": "object", - "description": "A port, or range of ports (ex. \"40000-55000\") that the attributes should apply to", + "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { "onAutoForward": { "type": "string", @@ -1018,28 +1186,70 @@ "label": { "type": "string", "description": "Label that will be shown in the UI for this port.", - "default": "Labeled Port" + "default": "Application" } }, "default": { - "label": "Labeled Port", + "label": "Application", "onAutoForward": "notify" }, "additionalProperties": false } }, - "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Labeled Port\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n}\n```", + "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```", "defaultSnippets": [ { "body": { "${1:3000}": { - "label": "${2:My Port}", + "label": "${2:Application}", "onAutoForward": "notify" } } } ], - "errorMessage": "Must be a port number or a range of port numbers", + "additionalProperties": false + }, + "unconfiguredPortsAttributes": { + "type": "object", + "properties": { + "onAutoForward": { + "type": "string", + "enum": [ + "notify", + "openBrowser", + "openPreview", + "silent", + "ignore" + ], + "enumDescriptions": [ + "Shows a notification when a port is automatically forwarded.", + "Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.", + "Opens a preview in the same window when the port is automatically forwarded.", + "Shows no notification and takes no action when this port is automatically forwarded.", + "This port will not be automatically forwarded." + ], + "description": "Defines the action that occurs when the port is discovered for automatic forwarding", + "default": "notify" + }, + "elevateIfNeeded": { + "type": "boolean", + "description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.", + "default": false + }, + "label": { + "type": "string", + "description": "Label that will be shown in the UI for this port.", + "default": "Application" + } + }, + "defaultSnippets": [ + { + "body": { + "onAutoForward": "ignore" + } + } + ], + "markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```", "additionalProperties": false }, "remoteEnv": { diff --git a/extensions/configuration-editing/schemas/devContainer.schema.src.json b/extensions/configuration-editing/schemas/devContainer.schema.src.json index bac86dc8af5..baef1756a64 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.src.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.src.json @@ -36,9 +36,9 @@ "portsAttributes": { "type": "object", "patternProperties": { - "^\\d+(\\-\\d+)?$": { + "(^\\d+(\\-\\d+)?$)|(.+)": { "type": "object", - "description": "A port, or range of ports (ex. \"40000-55000\") that the attributes should apply to", + "description": "A port, range of ports (ex. \"40000-55000\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "properties": { "onAutoForward": { "type": "string", @@ -67,27 +67,69 @@ "label": { "type": "string", "description": "Label that will be shown in the UI for this port.", - "default": "Labeled Port" + "default": "Application" } }, "default": { - "label": "Labeled Port", + "label": "Application", "onAutoForward": "notify" } } }, - "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Labeled Port\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n}\n```", + "markdownDescription": "Set default properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```", "defaultSnippets": [ { "body": { "${1:3000}": { - "label": "${2:My Port}", + "label": "${2:Application}", "onAutoForward": "notify" } } } ], - "errorMessage": "Must be a port number or a range of port numbers", + "additionalProperties": false + }, + "unconfiguredPortsAttributes": { + "type": "object", + "properties": { + "onAutoForward": { + "type": "string", + "enum": [ + "notify", + "openBrowser", + "openPreview", + "silent", + "ignore" + ], + "enumDescriptions": [ + "Shows a notification when a port is automatically forwarded.", + "Opens the browser when the port is automatically forwarded. Depending on your settings, this could open an embedded browser.", + "Opens a preview in the same window when the port is automatically forwarded.", + "Shows no notification and takes no action when this port is automatically forwarded.", + "This port will not be automatically forwarded." + ], + "description": "Defines the action that occurs when the port is discovered for automatic forwarding", + "default": "notify" + }, + "elevateIfNeeded": { + "type": "boolean", + "description": "Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port.", + "default": false + }, + "label": { + "type": "string", + "description": "Label that will be shown in the UI for this port.", + "default": "Application" + } + }, + "defaultSnippets": [ + { + "body": { + "onAutoForward": "ignore" + } + } + ], + "markdownDescription": "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```", "additionalProperties": false }, "remoteEnv": { diff --git a/extensions/markdown-language-features/notebook-out/index.js b/extensions/markdown-language-features/notebook-out/index.js index ef0a3821273..66df08f4026 100644 --- a/extensions/markdown-language-features/notebook-out/index.js +++ b/extensions/markdown-language-features/notebook-out/index.js @@ -1 +1 @@ -!function(e){var t={};function r(n){if(t[n])return t[n].exports;var s=t[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,r),s.l=!0,s.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)r.d(n,s,function(t){return e[t]}.bind(null,s));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=9)}([function(e,t,r){"use strict";var n=Object.prototype.hasOwnProperty;function s(e,t){return n.call(e,t)}function o(e){return!(e>=55296&&e<=57343)&&(!(e>=64976&&e<=65007)&&(65535!=(65535&e)&&65534!=(65535&e)&&(!(e>=0&&e<=8)&&(11!==e&&(!(e>=14&&e<=31)&&(!(e>=127&&e<=159)&&!(e>1114111)))))))}function i(e){if(e>65535){var t=55296+((e-=65536)>>10),r=56320+(1023&e);return String.fromCharCode(t,r)}return String.fromCharCode(e)}var a=/\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g,u=new RegExp(a.source+"|"+/&([a-z#][a-z0-9]{1,31});/gi.source,"gi"),c=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i,l=r(3);var p=/[&<>"]/,h=/[&<>"]/g,f={"&":"&","<":"<",">":">",'"':"""};function d(e){return f[e]}var m=/[.?*+^$[\]\\(){}|-]/g;var g=r(4);t.lib={},t.lib.mdurl=r(5),t.lib.ucmicro=r(17),t.assign=function(e){var t=Array.prototype.slice.call(arguments,1);return t.forEach((function(t){if(t){if("object"!=typeof t)throw new TypeError(t+"must be object");Object.keys(t).forEach((function(r){e[r]=t[r]}))}})),e},t.isString=function(e){return"[object String]"===function(e){return Object.prototype.toString.call(e)}(e)},t.has=s,t.unescapeMd=function(e){return e.indexOf("\\")<0?e:e.replace(a,"$1")},t.unescapeAll=function(e){return e.indexOf("\\")<0&&e.indexOf("&")<0?e:e.replace(u,(function(e,t,r){return t||function(e,t){var r=0;return s(l,t)?l[t]:35===t.charCodeAt(0)&&c.test(t)&&o(r="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10))?i(r):e}(e,r)}))},t.isValidEntityCode=o,t.fromCodePoint=i,t.escapeHtml=function(e){return p.test(e)?e.replace(h,d):e},t.arrayReplaceAt=function(e,t,r){return[].concat(e.slice(0,t),r,e.slice(t+1))},t.isSpace=function(e){switch(e){case 9:case 32:return!0}return!1},t.isWhiteSpace=function(e){if(e>=8192&&e<=8202)return!0;switch(e){case 9:case 10:case 11:case 12:case 13:case 32:case 160:case 5760:case 8239:case 8287:case 12288:return!0}return!1},t.isMdAsciiPunct=function(e){switch(e){case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:case 58:case 59:case 60:case 61:case 62:case 63:case 64:case 91:case 92:case 93:case 94:case 95:case 96:case 123:case 124:case 125:case 126:return!0;default:return!1}},t.isPunctChar=function(e){return g.test(e)},t.escapeRE=function(e){return e.replace(m,"\\$&")},t.normalizeReference=function(e){return e=e.trim().replace(/\s+/g," "),"Ṿ"==="ẞ".toLowerCase()&&(e=e.replace(/ẞ/g,"ß")),e.toLowerCase().toUpperCase()}},function(e,t,r){"use strict";function n(){this.__rules__=[],this.__cache__=null}n.prototype.__find__=function(e){for(var t=0;t=0&&(r=this.attrs[t][1]),r},n.prototype.attrJoin=function(e,t){var r=this.attrIndex(e);r<0?this.attrPush([e,t]):this.attrs[r][1]=this.attrs[r][1]+" "+t},e.exports=n},function(e,t,r){"use strict";e.exports=r(12)},function(e,t){e.exports=/[!-#%-\*,-/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E49\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD806[\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2]|\uD807[\uDC41-\uDC45\uDC70\uDC71]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/},function(e,t,r){"use strict";e.exports.encode=r(13),e.exports.decode=r(14),e.exports.format=r(15),e.exports.parse=r(16)},function(e,t,r){"use strict";var n="<[A-Za-z][A-Za-z0-9\\-]*(?:\\s+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:\\s*=\\s*(?:[^\"'=<>`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*\\/?>",s="<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>",o=new RegExp("^(?:"+n+"|"+s+"|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|<[?][\\s\\S]*?[?]>|]*>|)"),i=new RegExp("^(?:"+n+"|"+s+")");e.exports.HTML_TAG_RE=o,e.exports.HTML_OPEN_CLOSE_TAG_RE=i},function(e,t,r){"use strict";function n(e,t){var r,n,s,o,i,a=[],u=t.length;for(r=0;r=0;r--)95!==(n=t[r]).marker&&42!==n.marker||-1!==n.end&&(s=t[n.end],a=r>0&&t[r-1].end===n.end+1&&t[r-1].token===n.token-1&&t[n.end+1].token===s.token+1&&t[r-1].marker===n.marker,i=String.fromCharCode(n.marker),(o=e.tokens[n.token]).type=a?"strong_open":"em_open",o.tag=a?"strong":"em",o.nesting=1,o.markup=a?i+i:i,o.content="",(o=e.tokens[s.token]).type=a?"strong_close":"em_close",o.tag=a?"strong":"em",o.nesting=-1,o.markup=a?i+i:i,o.content="",a&&(e.tokens[t[r-1].token].content="",e.tokens[t[n.end+1].token].content="",r--))}e.exports.tokenize=function(e,t){var r,n,s=e.pos,o=e.src.charCodeAt(s);if(t)return!1;if(95!==o&&42!==o)return!1;for(n=e.scanDelims(e.pos,42===o),r=0;r{t(e)};acquireNotebookRendererApi("notebookCoreTestRenderer").onDidCreateMarkdown(({element:t,content:r})=>{const n=e.render(r);t.innerHTML=n});const t=new Date;console.log(`${t.getSeconds()}:${t.getMilliseconds().toString().padStart(3,"0")}`,"loaded markdown it")}()},function(e,t,r){"use strict";e.exports=r(11)},function(e,t,r){"use strict";var n=r(0),s=r(22),o=r(26),i=r(27),a=r(35),u=r(49),c=r(62),l=r(5),p=r(68),h={default:r(71),zero:r(72),commonmark:r(73)},f=/^(vbscript|javascript|file|data):/,d=/^data:image\/(gif|png|jpeg|webp);/;function m(e){var t=e.trim().toLowerCase();return!f.test(t)||!!d.test(t)}var g=["http:","https:","mailto:"];function _(e){var t=l.parse(e,!0);if(t.hostname&&(!t.protocol||g.indexOf(t.protocol)>=0))try{t.hostname=p.toASCII(t.hostname)}catch(e){}return l.encode(l.format(t))}function b(e){var t=l.parse(e,!0);if(t.hostname&&(!t.protocol||g.indexOf(t.protocol)>=0))try{t.hostname=p.toUnicode(t.hostname)}catch(e){}return l.decode(l.format(t),l.decode.defaultChars+"%")}function k(e,t){if(!(this instanceof k))return new k(e,t);t||n.isString(e)||(t=e||{},e="default"),this.inline=new u,this.block=new a,this.core=new i,this.renderer=new o,this.linkify=new c,this.validateLink=m,this.normalizeLink=_,this.normalizeLinkText=b,this.utils=n,this.helpers=n.assign({},s),this.options={},this.configure(e),t&&this.set(t)}k.prototype.set=function(e){return n.assign(this.options,e),this},k.prototype.configure=function(e){var t,r=this;if(n.isString(e)&&!(e=h[t=e]))throw new Error('Wrong `markdown-it` preset "'+t+'", check name');if(!e)throw new Error("Wrong `markdown-it` preset, can't be empty");return e.options&&r.set(e.options),e.components&&Object.keys(e.components).forEach((function(t){e.components[t].rules&&r[t].ruler.enableOnly(e.components[t].rules),e.components[t].rules2&&r[t].ruler2.enableOnly(e.components[t].rules2)})),this},k.prototype.enable=function(e,t){var r=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(t){r=r.concat(this[t].ruler.enable(e,!0))}),this),r=r.concat(this.inline.ruler2.enable(e,!0));var n=e.filter((function(e){return r.indexOf(e)<0}));if(n.length&&!t)throw new Error("MarkdownIt. Failed to enable unknown rule(s): "+n);return this},k.prototype.disable=function(e,t){var r=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(t){r=r.concat(this[t].ruler.disable(e,!0))}),this),r=r.concat(this.inline.ruler2.disable(e,!0));var n=e.filter((function(e){return r.indexOf(e)<0}));if(n.length&&!t)throw new Error("MarkdownIt. Failed to disable unknown rule(s): "+n);return this},k.prototype.use=function(e){var t=[this].concat(Array.prototype.slice.call(arguments,1));return e.apply(e,t),this},k.prototype.parse=function(e,t){if("string"!=typeof e)throw new Error("Input data should be a String");var r=new this.core.State(e,this,t);return this.core.process(r),r.tokens},k.prototype.render=function(e,t){return t=t||{},this.renderer.render(this.parse(e,t),this.options,t)},k.prototype.parseInline=function(e,t){var r=new this.core.State(e,this,t);return r.inlineMode=!0,this.core.process(r),r.tokens},k.prototype.renderInline=function(e,t){return t=t||{},this.renderer.render(this.parseInline(e,t),this.options,t)},e.exports=k},function(e){e.exports=JSON.parse('{"Aacute":"Á","aacute":"á","Abreve":"Ă","abreve":"ă","ac":"∾","acd":"∿","acE":"∾̳","Acirc":"Â","acirc":"â","acute":"´","Acy":"А","acy":"а","AElig":"Æ","aelig":"æ","af":"⁡","Afr":"𝔄","afr":"𝔞","Agrave":"À","agrave":"à","alefsym":"ℵ","aleph":"ℵ","Alpha":"Α","alpha":"α","Amacr":"Ā","amacr":"ā","amalg":"⨿","amp":"&","AMP":"&","andand":"⩕","And":"⩓","and":"∧","andd":"⩜","andslope":"⩘","andv":"⩚","ang":"∠","ange":"⦤","angle":"∠","angmsdaa":"⦨","angmsdab":"⦩","angmsdac":"⦪","angmsdad":"⦫","angmsdae":"⦬","angmsdaf":"⦭","angmsdag":"⦮","angmsdah":"⦯","angmsd":"∡","angrt":"∟","angrtvb":"⊾","angrtvbd":"⦝","angsph":"∢","angst":"Å","angzarr":"⍼","Aogon":"Ą","aogon":"ą","Aopf":"𝔸","aopf":"𝕒","apacir":"⩯","ap":"≈","apE":"⩰","ape":"≊","apid":"≋","apos":"\'","ApplyFunction":"⁡","approx":"≈","approxeq":"≊","Aring":"Å","aring":"å","Ascr":"𝒜","ascr":"𝒶","Assign":"≔","ast":"*","asymp":"≈","asympeq":"≍","Atilde":"Ã","atilde":"ã","Auml":"Ä","auml":"ä","awconint":"∳","awint":"⨑","backcong":"≌","backepsilon":"϶","backprime":"‵","backsim":"∽","backsimeq":"⋍","Backslash":"∖","Barv":"⫧","barvee":"⊽","barwed":"⌅","Barwed":"⌆","barwedge":"⌅","bbrk":"⎵","bbrktbrk":"⎶","bcong":"≌","Bcy":"Б","bcy":"б","bdquo":"„","becaus":"∵","because":"∵","Because":"∵","bemptyv":"⦰","bepsi":"϶","bernou":"ℬ","Bernoullis":"ℬ","Beta":"Β","beta":"β","beth":"ℶ","between":"≬","Bfr":"𝔅","bfr":"𝔟","bigcap":"⋂","bigcirc":"◯","bigcup":"⋃","bigodot":"⨀","bigoplus":"⨁","bigotimes":"⨂","bigsqcup":"⨆","bigstar":"★","bigtriangledown":"▽","bigtriangleup":"△","biguplus":"⨄","bigvee":"⋁","bigwedge":"⋀","bkarow":"⤍","blacklozenge":"⧫","blacksquare":"▪","blacktriangle":"▴","blacktriangledown":"▾","blacktriangleleft":"◂","blacktriangleright":"▸","blank":"␣","blk12":"▒","blk14":"░","blk34":"▓","block":"█","bne":"=⃥","bnequiv":"≡⃥","bNot":"⫭","bnot":"⌐","Bopf":"𝔹","bopf":"𝕓","bot":"⊥","bottom":"⊥","bowtie":"⋈","boxbox":"⧉","boxdl":"┐","boxdL":"╕","boxDl":"╖","boxDL":"╗","boxdr":"┌","boxdR":"╒","boxDr":"╓","boxDR":"╔","boxh":"─","boxH":"═","boxhd":"┬","boxHd":"╤","boxhD":"╥","boxHD":"╦","boxhu":"┴","boxHu":"╧","boxhU":"╨","boxHU":"╩","boxminus":"⊟","boxplus":"⊞","boxtimes":"⊠","boxul":"┘","boxuL":"╛","boxUl":"╜","boxUL":"╝","boxur":"└","boxuR":"╘","boxUr":"╙","boxUR":"╚","boxv":"│","boxV":"║","boxvh":"┼","boxvH":"╪","boxVh":"╫","boxVH":"╬","boxvl":"┤","boxvL":"╡","boxVl":"╢","boxVL":"╣","boxvr":"├","boxvR":"╞","boxVr":"╟","boxVR":"╠","bprime":"‵","breve":"˘","Breve":"˘","brvbar":"¦","bscr":"𝒷","Bscr":"ℬ","bsemi":"⁏","bsim":"∽","bsime":"⋍","bsolb":"⧅","bsol":"\\\\","bsolhsub":"⟈","bull":"•","bullet":"•","bump":"≎","bumpE":"⪮","bumpe":"≏","Bumpeq":"≎","bumpeq":"≏","Cacute":"Ć","cacute":"ć","capand":"⩄","capbrcup":"⩉","capcap":"⩋","cap":"∩","Cap":"⋒","capcup":"⩇","capdot":"⩀","CapitalDifferentialD":"ⅅ","caps":"∩︀","caret":"⁁","caron":"ˇ","Cayleys":"ℭ","ccaps":"⩍","Ccaron":"Č","ccaron":"č","Ccedil":"Ç","ccedil":"ç","Ccirc":"Ĉ","ccirc":"ĉ","Cconint":"∰","ccups":"⩌","ccupssm":"⩐","Cdot":"Ċ","cdot":"ċ","cedil":"¸","Cedilla":"¸","cemptyv":"⦲","cent":"¢","centerdot":"·","CenterDot":"·","cfr":"𝔠","Cfr":"ℭ","CHcy":"Ч","chcy":"ч","check":"✓","checkmark":"✓","Chi":"Χ","chi":"χ","circ":"ˆ","circeq":"≗","circlearrowleft":"↺","circlearrowright":"↻","circledast":"⊛","circledcirc":"⊚","circleddash":"⊝","CircleDot":"⊙","circledR":"®","circledS":"Ⓢ","CircleMinus":"⊖","CirclePlus":"⊕","CircleTimes":"⊗","cir":"○","cirE":"⧃","cire":"≗","cirfnint":"⨐","cirmid":"⫯","cirscir":"⧂","ClockwiseContourIntegral":"∲","CloseCurlyDoubleQuote":"”","CloseCurlyQuote":"’","clubs":"♣","clubsuit":"♣","colon":":","Colon":"∷","Colone":"⩴","colone":"≔","coloneq":"≔","comma":",","commat":"@","comp":"∁","compfn":"∘","complement":"∁","complexes":"ℂ","cong":"≅","congdot":"⩭","Congruent":"≡","conint":"∮","Conint":"∯","ContourIntegral":"∮","copf":"𝕔","Copf":"ℂ","coprod":"∐","Coproduct":"∐","copy":"©","COPY":"©","copysr":"℗","CounterClockwiseContourIntegral":"∳","crarr":"↵","cross":"✗","Cross":"⨯","Cscr":"𝒞","cscr":"𝒸","csub":"⫏","csube":"⫑","csup":"⫐","csupe":"⫒","ctdot":"⋯","cudarrl":"⤸","cudarrr":"⤵","cuepr":"⋞","cuesc":"⋟","cularr":"↶","cularrp":"⤽","cupbrcap":"⩈","cupcap":"⩆","CupCap":"≍","cup":"∪","Cup":"⋓","cupcup":"⩊","cupdot":"⊍","cupor":"⩅","cups":"∪︀","curarr":"↷","curarrm":"⤼","curlyeqprec":"⋞","curlyeqsucc":"⋟","curlyvee":"⋎","curlywedge":"⋏","curren":"¤","curvearrowleft":"↶","curvearrowright":"↷","cuvee":"⋎","cuwed":"⋏","cwconint":"∲","cwint":"∱","cylcty":"⌭","dagger":"†","Dagger":"‡","daleth":"ℸ","darr":"↓","Darr":"↡","dArr":"⇓","dash":"‐","Dashv":"⫤","dashv":"⊣","dbkarow":"⤏","dblac":"˝","Dcaron":"Ď","dcaron":"ď","Dcy":"Д","dcy":"д","ddagger":"‡","ddarr":"⇊","DD":"ⅅ","dd":"ⅆ","DDotrahd":"⤑","ddotseq":"⩷","deg":"°","Del":"∇","Delta":"Δ","delta":"δ","demptyv":"⦱","dfisht":"⥿","Dfr":"𝔇","dfr":"𝔡","dHar":"⥥","dharl":"⇃","dharr":"⇂","DiacriticalAcute":"´","DiacriticalDot":"˙","DiacriticalDoubleAcute":"˝","DiacriticalGrave":"`","DiacriticalTilde":"˜","diam":"⋄","diamond":"⋄","Diamond":"⋄","diamondsuit":"♦","diams":"♦","die":"¨","DifferentialD":"ⅆ","digamma":"ϝ","disin":"⋲","div":"÷","divide":"÷","divideontimes":"⋇","divonx":"⋇","DJcy":"Ђ","djcy":"ђ","dlcorn":"⌞","dlcrop":"⌍","dollar":"$","Dopf":"𝔻","dopf":"𝕕","Dot":"¨","dot":"˙","DotDot":"⃜","doteq":"≐","doteqdot":"≑","DotEqual":"≐","dotminus":"∸","dotplus":"∔","dotsquare":"⊡","doublebarwedge":"⌆","DoubleContourIntegral":"∯","DoubleDot":"¨","DoubleDownArrow":"⇓","DoubleLeftArrow":"⇐","DoubleLeftRightArrow":"⇔","DoubleLeftTee":"⫤","DoubleLongLeftArrow":"⟸","DoubleLongLeftRightArrow":"⟺","DoubleLongRightArrow":"⟹","DoubleRightArrow":"⇒","DoubleRightTee":"⊨","DoubleUpArrow":"⇑","DoubleUpDownArrow":"⇕","DoubleVerticalBar":"∥","DownArrowBar":"⤓","downarrow":"↓","DownArrow":"↓","Downarrow":"⇓","DownArrowUpArrow":"⇵","DownBreve":"̑","downdownarrows":"⇊","downharpoonleft":"⇃","downharpoonright":"⇂","DownLeftRightVector":"⥐","DownLeftTeeVector":"⥞","DownLeftVectorBar":"⥖","DownLeftVector":"↽","DownRightTeeVector":"⥟","DownRightVectorBar":"⥗","DownRightVector":"⇁","DownTeeArrow":"↧","DownTee":"⊤","drbkarow":"⤐","drcorn":"⌟","drcrop":"⌌","Dscr":"𝒟","dscr":"𝒹","DScy":"Ѕ","dscy":"ѕ","dsol":"⧶","Dstrok":"Đ","dstrok":"đ","dtdot":"⋱","dtri":"▿","dtrif":"▾","duarr":"⇵","duhar":"⥯","dwangle":"⦦","DZcy":"Џ","dzcy":"џ","dzigrarr":"⟿","Eacute":"É","eacute":"é","easter":"⩮","Ecaron":"Ě","ecaron":"ě","Ecirc":"Ê","ecirc":"ê","ecir":"≖","ecolon":"≕","Ecy":"Э","ecy":"э","eDDot":"⩷","Edot":"Ė","edot":"ė","eDot":"≑","ee":"ⅇ","efDot":"≒","Efr":"𝔈","efr":"𝔢","eg":"⪚","Egrave":"È","egrave":"è","egs":"⪖","egsdot":"⪘","el":"⪙","Element":"∈","elinters":"⏧","ell":"ℓ","els":"⪕","elsdot":"⪗","Emacr":"Ē","emacr":"ē","empty":"∅","emptyset":"∅","EmptySmallSquare":"◻","emptyv":"∅","EmptyVerySmallSquare":"▫","emsp13":" ","emsp14":" ","emsp":" ","ENG":"Ŋ","eng":"ŋ","ensp":" ","Eogon":"Ę","eogon":"ę","Eopf":"𝔼","eopf":"𝕖","epar":"⋕","eparsl":"⧣","eplus":"⩱","epsi":"ε","Epsilon":"Ε","epsilon":"ε","epsiv":"ϵ","eqcirc":"≖","eqcolon":"≕","eqsim":"≂","eqslantgtr":"⪖","eqslantless":"⪕","Equal":"⩵","equals":"=","EqualTilde":"≂","equest":"≟","Equilibrium":"⇌","equiv":"≡","equivDD":"⩸","eqvparsl":"⧥","erarr":"⥱","erDot":"≓","escr":"ℯ","Escr":"ℰ","esdot":"≐","Esim":"⩳","esim":"≂","Eta":"Η","eta":"η","ETH":"Ð","eth":"ð","Euml":"Ë","euml":"ë","euro":"€","excl":"!","exist":"∃","Exists":"∃","expectation":"ℰ","exponentiale":"ⅇ","ExponentialE":"ⅇ","fallingdotseq":"≒","Fcy":"Ф","fcy":"ф","female":"♀","ffilig":"ffi","fflig":"ff","ffllig":"ffl","Ffr":"𝔉","ffr":"𝔣","filig":"fi","FilledSmallSquare":"◼","FilledVerySmallSquare":"▪","fjlig":"fj","flat":"♭","fllig":"fl","fltns":"▱","fnof":"ƒ","Fopf":"𝔽","fopf":"𝕗","forall":"∀","ForAll":"∀","fork":"⋔","forkv":"⫙","Fouriertrf":"ℱ","fpartint":"⨍","frac12":"½","frac13":"⅓","frac14":"¼","frac15":"⅕","frac16":"⅙","frac18":"⅛","frac23":"⅔","frac25":"⅖","frac34":"¾","frac35":"⅗","frac38":"⅜","frac45":"⅘","frac56":"⅚","frac58":"⅝","frac78":"⅞","frasl":"⁄","frown":"⌢","fscr":"𝒻","Fscr":"ℱ","gacute":"ǵ","Gamma":"Γ","gamma":"γ","Gammad":"Ϝ","gammad":"ϝ","gap":"⪆","Gbreve":"Ğ","gbreve":"ğ","Gcedil":"Ģ","Gcirc":"Ĝ","gcirc":"ĝ","Gcy":"Г","gcy":"г","Gdot":"Ġ","gdot":"ġ","ge":"≥","gE":"≧","gEl":"⪌","gel":"⋛","geq":"≥","geqq":"≧","geqslant":"⩾","gescc":"⪩","ges":"⩾","gesdot":"⪀","gesdoto":"⪂","gesdotol":"⪄","gesl":"⋛︀","gesles":"⪔","Gfr":"𝔊","gfr":"𝔤","gg":"≫","Gg":"⋙","ggg":"⋙","gimel":"ℷ","GJcy":"Ѓ","gjcy":"ѓ","gla":"⪥","gl":"≷","glE":"⪒","glj":"⪤","gnap":"⪊","gnapprox":"⪊","gne":"⪈","gnE":"≩","gneq":"⪈","gneqq":"≩","gnsim":"⋧","Gopf":"𝔾","gopf":"𝕘","grave":"`","GreaterEqual":"≥","GreaterEqualLess":"⋛","GreaterFullEqual":"≧","GreaterGreater":"⪢","GreaterLess":"≷","GreaterSlantEqual":"⩾","GreaterTilde":"≳","Gscr":"𝒢","gscr":"ℊ","gsim":"≳","gsime":"⪎","gsiml":"⪐","gtcc":"⪧","gtcir":"⩺","gt":">","GT":">","Gt":"≫","gtdot":"⋗","gtlPar":"⦕","gtquest":"⩼","gtrapprox":"⪆","gtrarr":"⥸","gtrdot":"⋗","gtreqless":"⋛","gtreqqless":"⪌","gtrless":"≷","gtrsim":"≳","gvertneqq":"≩︀","gvnE":"≩︀","Hacek":"ˇ","hairsp":" ","half":"½","hamilt":"ℋ","HARDcy":"Ъ","hardcy":"ъ","harrcir":"⥈","harr":"↔","hArr":"⇔","harrw":"↭","Hat":"^","hbar":"ℏ","Hcirc":"Ĥ","hcirc":"ĥ","hearts":"♥","heartsuit":"♥","hellip":"…","hercon":"⊹","hfr":"𝔥","Hfr":"ℌ","HilbertSpace":"ℋ","hksearow":"⤥","hkswarow":"⤦","hoarr":"⇿","homtht":"∻","hookleftarrow":"↩","hookrightarrow":"↪","hopf":"𝕙","Hopf":"ℍ","horbar":"―","HorizontalLine":"─","hscr":"𝒽","Hscr":"ℋ","hslash":"ℏ","Hstrok":"Ħ","hstrok":"ħ","HumpDownHump":"≎","HumpEqual":"≏","hybull":"⁃","hyphen":"‐","Iacute":"Í","iacute":"í","ic":"⁣","Icirc":"Î","icirc":"î","Icy":"И","icy":"и","Idot":"İ","IEcy":"Е","iecy":"е","iexcl":"¡","iff":"⇔","ifr":"𝔦","Ifr":"ℑ","Igrave":"Ì","igrave":"ì","ii":"ⅈ","iiiint":"⨌","iiint":"∭","iinfin":"⧜","iiota":"℩","IJlig":"IJ","ijlig":"ij","Imacr":"Ī","imacr":"ī","image":"ℑ","ImaginaryI":"ⅈ","imagline":"ℐ","imagpart":"ℑ","imath":"ı","Im":"ℑ","imof":"⊷","imped":"Ƶ","Implies":"⇒","incare":"℅","in":"∈","infin":"∞","infintie":"⧝","inodot":"ı","intcal":"⊺","int":"∫","Int":"∬","integers":"ℤ","Integral":"∫","intercal":"⊺","Intersection":"⋂","intlarhk":"⨗","intprod":"⨼","InvisibleComma":"⁣","InvisibleTimes":"⁢","IOcy":"Ё","iocy":"ё","Iogon":"Į","iogon":"į","Iopf":"𝕀","iopf":"𝕚","Iota":"Ι","iota":"ι","iprod":"⨼","iquest":"¿","iscr":"𝒾","Iscr":"ℐ","isin":"∈","isindot":"⋵","isinE":"⋹","isins":"⋴","isinsv":"⋳","isinv":"∈","it":"⁢","Itilde":"Ĩ","itilde":"ĩ","Iukcy":"І","iukcy":"і","Iuml":"Ï","iuml":"ï","Jcirc":"Ĵ","jcirc":"ĵ","Jcy":"Й","jcy":"й","Jfr":"𝔍","jfr":"𝔧","jmath":"ȷ","Jopf":"𝕁","jopf":"𝕛","Jscr":"𝒥","jscr":"𝒿","Jsercy":"Ј","jsercy":"ј","Jukcy":"Є","jukcy":"є","Kappa":"Κ","kappa":"κ","kappav":"ϰ","Kcedil":"Ķ","kcedil":"ķ","Kcy":"К","kcy":"к","Kfr":"𝔎","kfr":"𝔨","kgreen":"ĸ","KHcy":"Х","khcy":"х","KJcy":"Ќ","kjcy":"ќ","Kopf":"𝕂","kopf":"𝕜","Kscr":"𝒦","kscr":"𝓀","lAarr":"⇚","Lacute":"Ĺ","lacute":"ĺ","laemptyv":"⦴","lagran":"ℒ","Lambda":"Λ","lambda":"λ","lang":"⟨","Lang":"⟪","langd":"⦑","langle":"⟨","lap":"⪅","Laplacetrf":"ℒ","laquo":"«","larrb":"⇤","larrbfs":"⤟","larr":"←","Larr":"↞","lArr":"⇐","larrfs":"⤝","larrhk":"↩","larrlp":"↫","larrpl":"⤹","larrsim":"⥳","larrtl":"↢","latail":"⤙","lAtail":"⤛","lat":"⪫","late":"⪭","lates":"⪭︀","lbarr":"⤌","lBarr":"⤎","lbbrk":"❲","lbrace":"{","lbrack":"[","lbrke":"⦋","lbrksld":"⦏","lbrkslu":"⦍","Lcaron":"Ľ","lcaron":"ľ","Lcedil":"Ļ","lcedil":"ļ","lceil":"⌈","lcub":"{","Lcy":"Л","lcy":"л","ldca":"⤶","ldquo":"“","ldquor":"„","ldrdhar":"⥧","ldrushar":"⥋","ldsh":"↲","le":"≤","lE":"≦","LeftAngleBracket":"⟨","LeftArrowBar":"⇤","leftarrow":"←","LeftArrow":"←","Leftarrow":"⇐","LeftArrowRightArrow":"⇆","leftarrowtail":"↢","LeftCeiling":"⌈","LeftDoubleBracket":"⟦","LeftDownTeeVector":"⥡","LeftDownVectorBar":"⥙","LeftDownVector":"⇃","LeftFloor":"⌊","leftharpoondown":"↽","leftharpoonup":"↼","leftleftarrows":"⇇","leftrightarrow":"↔","LeftRightArrow":"↔","Leftrightarrow":"⇔","leftrightarrows":"⇆","leftrightharpoons":"⇋","leftrightsquigarrow":"↭","LeftRightVector":"⥎","LeftTeeArrow":"↤","LeftTee":"⊣","LeftTeeVector":"⥚","leftthreetimes":"⋋","LeftTriangleBar":"⧏","LeftTriangle":"⊲","LeftTriangleEqual":"⊴","LeftUpDownVector":"⥑","LeftUpTeeVector":"⥠","LeftUpVectorBar":"⥘","LeftUpVector":"↿","LeftVectorBar":"⥒","LeftVector":"↼","lEg":"⪋","leg":"⋚","leq":"≤","leqq":"≦","leqslant":"⩽","lescc":"⪨","les":"⩽","lesdot":"⩿","lesdoto":"⪁","lesdotor":"⪃","lesg":"⋚︀","lesges":"⪓","lessapprox":"⪅","lessdot":"⋖","lesseqgtr":"⋚","lesseqqgtr":"⪋","LessEqualGreater":"⋚","LessFullEqual":"≦","LessGreater":"≶","lessgtr":"≶","LessLess":"⪡","lesssim":"≲","LessSlantEqual":"⩽","LessTilde":"≲","lfisht":"⥼","lfloor":"⌊","Lfr":"𝔏","lfr":"𝔩","lg":"≶","lgE":"⪑","lHar":"⥢","lhard":"↽","lharu":"↼","lharul":"⥪","lhblk":"▄","LJcy":"Љ","ljcy":"љ","llarr":"⇇","ll":"≪","Ll":"⋘","llcorner":"⌞","Lleftarrow":"⇚","llhard":"⥫","lltri":"◺","Lmidot":"Ŀ","lmidot":"ŀ","lmoustache":"⎰","lmoust":"⎰","lnap":"⪉","lnapprox":"⪉","lne":"⪇","lnE":"≨","lneq":"⪇","lneqq":"≨","lnsim":"⋦","loang":"⟬","loarr":"⇽","lobrk":"⟦","longleftarrow":"⟵","LongLeftArrow":"⟵","Longleftarrow":"⟸","longleftrightarrow":"⟷","LongLeftRightArrow":"⟷","Longleftrightarrow":"⟺","longmapsto":"⟼","longrightarrow":"⟶","LongRightArrow":"⟶","Longrightarrow":"⟹","looparrowleft":"↫","looparrowright":"↬","lopar":"⦅","Lopf":"𝕃","lopf":"𝕝","loplus":"⨭","lotimes":"⨴","lowast":"∗","lowbar":"_","LowerLeftArrow":"↙","LowerRightArrow":"↘","loz":"◊","lozenge":"◊","lozf":"⧫","lpar":"(","lparlt":"⦓","lrarr":"⇆","lrcorner":"⌟","lrhar":"⇋","lrhard":"⥭","lrm":"‎","lrtri":"⊿","lsaquo":"‹","lscr":"𝓁","Lscr":"ℒ","lsh":"↰","Lsh":"↰","lsim":"≲","lsime":"⪍","lsimg":"⪏","lsqb":"[","lsquo":"‘","lsquor":"‚","Lstrok":"Ł","lstrok":"ł","ltcc":"⪦","ltcir":"⩹","lt":"<","LT":"<","Lt":"≪","ltdot":"⋖","lthree":"⋋","ltimes":"⋉","ltlarr":"⥶","ltquest":"⩻","ltri":"◃","ltrie":"⊴","ltrif":"◂","ltrPar":"⦖","lurdshar":"⥊","luruhar":"⥦","lvertneqq":"≨︀","lvnE":"≨︀","macr":"¯","male":"♂","malt":"✠","maltese":"✠","Map":"⤅","map":"↦","mapsto":"↦","mapstodown":"↧","mapstoleft":"↤","mapstoup":"↥","marker":"▮","mcomma":"⨩","Mcy":"М","mcy":"м","mdash":"—","mDDot":"∺","measuredangle":"∡","MediumSpace":" ","Mellintrf":"ℳ","Mfr":"𝔐","mfr":"𝔪","mho":"℧","micro":"µ","midast":"*","midcir":"⫰","mid":"∣","middot":"·","minusb":"⊟","minus":"−","minusd":"∸","minusdu":"⨪","MinusPlus":"∓","mlcp":"⫛","mldr":"…","mnplus":"∓","models":"⊧","Mopf":"𝕄","mopf":"𝕞","mp":"∓","mscr":"𝓂","Mscr":"ℳ","mstpos":"∾","Mu":"Μ","mu":"μ","multimap":"⊸","mumap":"⊸","nabla":"∇","Nacute":"Ń","nacute":"ń","nang":"∠⃒","nap":"≉","napE":"⩰̸","napid":"≋̸","napos":"ʼn","napprox":"≉","natural":"♮","naturals":"ℕ","natur":"♮","nbsp":" ","nbump":"≎̸","nbumpe":"≏̸","ncap":"⩃","Ncaron":"Ň","ncaron":"ň","Ncedil":"Ņ","ncedil":"ņ","ncong":"≇","ncongdot":"⩭̸","ncup":"⩂","Ncy":"Н","ncy":"н","ndash":"–","nearhk":"⤤","nearr":"↗","neArr":"⇗","nearrow":"↗","ne":"≠","nedot":"≐̸","NegativeMediumSpace":"​","NegativeThickSpace":"​","NegativeThinSpace":"​","NegativeVeryThinSpace":"​","nequiv":"≢","nesear":"⤨","nesim":"≂̸","NestedGreaterGreater":"≫","NestedLessLess":"≪","NewLine":"\\n","nexist":"∄","nexists":"∄","Nfr":"𝔑","nfr":"𝔫","ngE":"≧̸","nge":"≱","ngeq":"≱","ngeqq":"≧̸","ngeqslant":"⩾̸","nges":"⩾̸","nGg":"⋙̸","ngsim":"≵","nGt":"≫⃒","ngt":"≯","ngtr":"≯","nGtv":"≫̸","nharr":"↮","nhArr":"⇎","nhpar":"⫲","ni":"∋","nis":"⋼","nisd":"⋺","niv":"∋","NJcy":"Њ","njcy":"њ","nlarr":"↚","nlArr":"⇍","nldr":"‥","nlE":"≦̸","nle":"≰","nleftarrow":"↚","nLeftarrow":"⇍","nleftrightarrow":"↮","nLeftrightarrow":"⇎","nleq":"≰","nleqq":"≦̸","nleqslant":"⩽̸","nles":"⩽̸","nless":"≮","nLl":"⋘̸","nlsim":"≴","nLt":"≪⃒","nlt":"≮","nltri":"⋪","nltrie":"⋬","nLtv":"≪̸","nmid":"∤","NoBreak":"⁠","NonBreakingSpace":" ","nopf":"𝕟","Nopf":"ℕ","Not":"⫬","not":"¬","NotCongruent":"≢","NotCupCap":"≭","NotDoubleVerticalBar":"∦","NotElement":"∉","NotEqual":"≠","NotEqualTilde":"≂̸","NotExists":"∄","NotGreater":"≯","NotGreaterEqual":"≱","NotGreaterFullEqual":"≧̸","NotGreaterGreater":"≫̸","NotGreaterLess":"≹","NotGreaterSlantEqual":"⩾̸","NotGreaterTilde":"≵","NotHumpDownHump":"≎̸","NotHumpEqual":"≏̸","notin":"∉","notindot":"⋵̸","notinE":"⋹̸","notinva":"∉","notinvb":"⋷","notinvc":"⋶","NotLeftTriangleBar":"⧏̸","NotLeftTriangle":"⋪","NotLeftTriangleEqual":"⋬","NotLess":"≮","NotLessEqual":"≰","NotLessGreater":"≸","NotLessLess":"≪̸","NotLessSlantEqual":"⩽̸","NotLessTilde":"≴","NotNestedGreaterGreater":"⪢̸","NotNestedLessLess":"⪡̸","notni":"∌","notniva":"∌","notnivb":"⋾","notnivc":"⋽","NotPrecedes":"⊀","NotPrecedesEqual":"⪯̸","NotPrecedesSlantEqual":"⋠","NotReverseElement":"∌","NotRightTriangleBar":"⧐̸","NotRightTriangle":"⋫","NotRightTriangleEqual":"⋭","NotSquareSubset":"⊏̸","NotSquareSubsetEqual":"⋢","NotSquareSuperset":"⊐̸","NotSquareSupersetEqual":"⋣","NotSubset":"⊂⃒","NotSubsetEqual":"⊈","NotSucceeds":"⊁","NotSucceedsEqual":"⪰̸","NotSucceedsSlantEqual":"⋡","NotSucceedsTilde":"≿̸","NotSuperset":"⊃⃒","NotSupersetEqual":"⊉","NotTilde":"≁","NotTildeEqual":"≄","NotTildeFullEqual":"≇","NotTildeTilde":"≉","NotVerticalBar":"∤","nparallel":"∦","npar":"∦","nparsl":"⫽⃥","npart":"∂̸","npolint":"⨔","npr":"⊀","nprcue":"⋠","nprec":"⊀","npreceq":"⪯̸","npre":"⪯̸","nrarrc":"⤳̸","nrarr":"↛","nrArr":"⇏","nrarrw":"↝̸","nrightarrow":"↛","nRightarrow":"⇏","nrtri":"⋫","nrtrie":"⋭","nsc":"⊁","nsccue":"⋡","nsce":"⪰̸","Nscr":"𝒩","nscr":"𝓃","nshortmid":"∤","nshortparallel":"∦","nsim":"≁","nsime":"≄","nsimeq":"≄","nsmid":"∤","nspar":"∦","nsqsube":"⋢","nsqsupe":"⋣","nsub":"⊄","nsubE":"⫅̸","nsube":"⊈","nsubset":"⊂⃒","nsubseteq":"⊈","nsubseteqq":"⫅̸","nsucc":"⊁","nsucceq":"⪰̸","nsup":"⊅","nsupE":"⫆̸","nsupe":"⊉","nsupset":"⊃⃒","nsupseteq":"⊉","nsupseteqq":"⫆̸","ntgl":"≹","Ntilde":"Ñ","ntilde":"ñ","ntlg":"≸","ntriangleleft":"⋪","ntrianglelefteq":"⋬","ntriangleright":"⋫","ntrianglerighteq":"⋭","Nu":"Ν","nu":"ν","num":"#","numero":"№","numsp":" ","nvap":"≍⃒","nvdash":"⊬","nvDash":"⊭","nVdash":"⊮","nVDash":"⊯","nvge":"≥⃒","nvgt":">⃒","nvHarr":"⤄","nvinfin":"⧞","nvlArr":"⤂","nvle":"≤⃒","nvlt":"<⃒","nvltrie":"⊴⃒","nvrArr":"⤃","nvrtrie":"⊵⃒","nvsim":"∼⃒","nwarhk":"⤣","nwarr":"↖","nwArr":"⇖","nwarrow":"↖","nwnear":"⤧","Oacute":"Ó","oacute":"ó","oast":"⊛","Ocirc":"Ô","ocirc":"ô","ocir":"⊚","Ocy":"О","ocy":"о","odash":"⊝","Odblac":"Ő","odblac":"ő","odiv":"⨸","odot":"⊙","odsold":"⦼","OElig":"Œ","oelig":"œ","ofcir":"⦿","Ofr":"𝔒","ofr":"𝔬","ogon":"˛","Ograve":"Ò","ograve":"ò","ogt":"⧁","ohbar":"⦵","ohm":"Ω","oint":"∮","olarr":"↺","olcir":"⦾","olcross":"⦻","oline":"‾","olt":"⧀","Omacr":"Ō","omacr":"ō","Omega":"Ω","omega":"ω","Omicron":"Ο","omicron":"ο","omid":"⦶","ominus":"⊖","Oopf":"𝕆","oopf":"𝕠","opar":"⦷","OpenCurlyDoubleQuote":"“","OpenCurlyQuote":"‘","operp":"⦹","oplus":"⊕","orarr":"↻","Or":"⩔","or":"∨","ord":"⩝","order":"ℴ","orderof":"ℴ","ordf":"ª","ordm":"º","origof":"⊶","oror":"⩖","orslope":"⩗","orv":"⩛","oS":"Ⓢ","Oscr":"𝒪","oscr":"ℴ","Oslash":"Ø","oslash":"ø","osol":"⊘","Otilde":"Õ","otilde":"õ","otimesas":"⨶","Otimes":"⨷","otimes":"⊗","Ouml":"Ö","ouml":"ö","ovbar":"⌽","OverBar":"‾","OverBrace":"⏞","OverBracket":"⎴","OverParenthesis":"⏜","para":"¶","parallel":"∥","par":"∥","parsim":"⫳","parsl":"⫽","part":"∂","PartialD":"∂","Pcy":"П","pcy":"п","percnt":"%","period":".","permil":"‰","perp":"⊥","pertenk":"‱","Pfr":"𝔓","pfr":"𝔭","Phi":"Φ","phi":"φ","phiv":"ϕ","phmmat":"ℳ","phone":"☎","Pi":"Π","pi":"π","pitchfork":"⋔","piv":"ϖ","planck":"ℏ","planckh":"ℎ","plankv":"ℏ","plusacir":"⨣","plusb":"⊞","pluscir":"⨢","plus":"+","plusdo":"∔","plusdu":"⨥","pluse":"⩲","PlusMinus":"±","plusmn":"±","plussim":"⨦","plustwo":"⨧","pm":"±","Poincareplane":"ℌ","pointint":"⨕","popf":"𝕡","Popf":"ℙ","pound":"£","prap":"⪷","Pr":"⪻","pr":"≺","prcue":"≼","precapprox":"⪷","prec":"≺","preccurlyeq":"≼","Precedes":"≺","PrecedesEqual":"⪯","PrecedesSlantEqual":"≼","PrecedesTilde":"≾","preceq":"⪯","precnapprox":"⪹","precneqq":"⪵","precnsim":"⋨","pre":"⪯","prE":"⪳","precsim":"≾","prime":"′","Prime":"″","primes":"ℙ","prnap":"⪹","prnE":"⪵","prnsim":"⋨","prod":"∏","Product":"∏","profalar":"⌮","profline":"⌒","profsurf":"⌓","prop":"∝","Proportional":"∝","Proportion":"∷","propto":"∝","prsim":"≾","prurel":"⊰","Pscr":"𝒫","pscr":"𝓅","Psi":"Ψ","psi":"ψ","puncsp":" ","Qfr":"𝔔","qfr":"𝔮","qint":"⨌","qopf":"𝕢","Qopf":"ℚ","qprime":"⁗","Qscr":"𝒬","qscr":"𝓆","quaternions":"ℍ","quatint":"⨖","quest":"?","questeq":"≟","quot":"\\"","QUOT":"\\"","rAarr":"⇛","race":"∽̱","Racute":"Ŕ","racute":"ŕ","radic":"√","raemptyv":"⦳","rang":"⟩","Rang":"⟫","rangd":"⦒","range":"⦥","rangle":"⟩","raquo":"»","rarrap":"⥵","rarrb":"⇥","rarrbfs":"⤠","rarrc":"⤳","rarr":"→","Rarr":"↠","rArr":"⇒","rarrfs":"⤞","rarrhk":"↪","rarrlp":"↬","rarrpl":"⥅","rarrsim":"⥴","Rarrtl":"⤖","rarrtl":"↣","rarrw":"↝","ratail":"⤚","rAtail":"⤜","ratio":"∶","rationals":"ℚ","rbarr":"⤍","rBarr":"⤏","RBarr":"⤐","rbbrk":"❳","rbrace":"}","rbrack":"]","rbrke":"⦌","rbrksld":"⦎","rbrkslu":"⦐","Rcaron":"Ř","rcaron":"ř","Rcedil":"Ŗ","rcedil":"ŗ","rceil":"⌉","rcub":"}","Rcy":"Р","rcy":"р","rdca":"⤷","rdldhar":"⥩","rdquo":"”","rdquor":"”","rdsh":"↳","real":"ℜ","realine":"ℛ","realpart":"ℜ","reals":"ℝ","Re":"ℜ","rect":"▭","reg":"®","REG":"®","ReverseElement":"∋","ReverseEquilibrium":"⇋","ReverseUpEquilibrium":"⥯","rfisht":"⥽","rfloor":"⌋","rfr":"𝔯","Rfr":"ℜ","rHar":"⥤","rhard":"⇁","rharu":"⇀","rharul":"⥬","Rho":"Ρ","rho":"ρ","rhov":"ϱ","RightAngleBracket":"⟩","RightArrowBar":"⇥","rightarrow":"→","RightArrow":"→","Rightarrow":"⇒","RightArrowLeftArrow":"⇄","rightarrowtail":"↣","RightCeiling":"⌉","RightDoubleBracket":"⟧","RightDownTeeVector":"⥝","RightDownVectorBar":"⥕","RightDownVector":"⇂","RightFloor":"⌋","rightharpoondown":"⇁","rightharpoonup":"⇀","rightleftarrows":"⇄","rightleftharpoons":"⇌","rightrightarrows":"⇉","rightsquigarrow":"↝","RightTeeArrow":"↦","RightTee":"⊢","RightTeeVector":"⥛","rightthreetimes":"⋌","RightTriangleBar":"⧐","RightTriangle":"⊳","RightTriangleEqual":"⊵","RightUpDownVector":"⥏","RightUpTeeVector":"⥜","RightUpVectorBar":"⥔","RightUpVector":"↾","RightVectorBar":"⥓","RightVector":"⇀","ring":"˚","risingdotseq":"≓","rlarr":"⇄","rlhar":"⇌","rlm":"‏","rmoustache":"⎱","rmoust":"⎱","rnmid":"⫮","roang":"⟭","roarr":"⇾","robrk":"⟧","ropar":"⦆","ropf":"𝕣","Ropf":"ℝ","roplus":"⨮","rotimes":"⨵","RoundImplies":"⥰","rpar":")","rpargt":"⦔","rppolint":"⨒","rrarr":"⇉","Rrightarrow":"⇛","rsaquo":"›","rscr":"𝓇","Rscr":"ℛ","rsh":"↱","Rsh":"↱","rsqb":"]","rsquo":"’","rsquor":"’","rthree":"⋌","rtimes":"⋊","rtri":"▹","rtrie":"⊵","rtrif":"▸","rtriltri":"⧎","RuleDelayed":"⧴","ruluhar":"⥨","rx":"℞","Sacute":"Ś","sacute":"ś","sbquo":"‚","scap":"⪸","Scaron":"Š","scaron":"š","Sc":"⪼","sc":"≻","sccue":"≽","sce":"⪰","scE":"⪴","Scedil":"Ş","scedil":"ş","Scirc":"Ŝ","scirc":"ŝ","scnap":"⪺","scnE":"⪶","scnsim":"⋩","scpolint":"⨓","scsim":"≿","Scy":"С","scy":"с","sdotb":"⊡","sdot":"⋅","sdote":"⩦","searhk":"⤥","searr":"↘","seArr":"⇘","searrow":"↘","sect":"§","semi":";","seswar":"⤩","setminus":"∖","setmn":"∖","sext":"✶","Sfr":"𝔖","sfr":"𝔰","sfrown":"⌢","sharp":"♯","SHCHcy":"Щ","shchcy":"щ","SHcy":"Ш","shcy":"ш","ShortDownArrow":"↓","ShortLeftArrow":"←","shortmid":"∣","shortparallel":"∥","ShortRightArrow":"→","ShortUpArrow":"↑","shy":"­","Sigma":"Σ","sigma":"σ","sigmaf":"ς","sigmav":"ς","sim":"∼","simdot":"⩪","sime":"≃","simeq":"≃","simg":"⪞","simgE":"⪠","siml":"⪝","simlE":"⪟","simne":"≆","simplus":"⨤","simrarr":"⥲","slarr":"←","SmallCircle":"∘","smallsetminus":"∖","smashp":"⨳","smeparsl":"⧤","smid":"∣","smile":"⌣","smt":"⪪","smte":"⪬","smtes":"⪬︀","SOFTcy":"Ь","softcy":"ь","solbar":"⌿","solb":"⧄","sol":"/","Sopf":"𝕊","sopf":"𝕤","spades":"♠","spadesuit":"♠","spar":"∥","sqcap":"⊓","sqcaps":"⊓︀","sqcup":"⊔","sqcups":"⊔︀","Sqrt":"√","sqsub":"⊏","sqsube":"⊑","sqsubset":"⊏","sqsubseteq":"⊑","sqsup":"⊐","sqsupe":"⊒","sqsupset":"⊐","sqsupseteq":"⊒","square":"□","Square":"□","SquareIntersection":"⊓","SquareSubset":"⊏","SquareSubsetEqual":"⊑","SquareSuperset":"⊐","SquareSupersetEqual":"⊒","SquareUnion":"⊔","squarf":"▪","squ":"□","squf":"▪","srarr":"→","Sscr":"𝒮","sscr":"𝓈","ssetmn":"∖","ssmile":"⌣","sstarf":"⋆","Star":"⋆","star":"☆","starf":"★","straightepsilon":"ϵ","straightphi":"ϕ","strns":"¯","sub":"⊂","Sub":"⋐","subdot":"⪽","subE":"⫅","sube":"⊆","subedot":"⫃","submult":"⫁","subnE":"⫋","subne":"⊊","subplus":"⪿","subrarr":"⥹","subset":"⊂","Subset":"⋐","subseteq":"⊆","subseteqq":"⫅","SubsetEqual":"⊆","subsetneq":"⊊","subsetneqq":"⫋","subsim":"⫇","subsub":"⫕","subsup":"⫓","succapprox":"⪸","succ":"≻","succcurlyeq":"≽","Succeeds":"≻","SucceedsEqual":"⪰","SucceedsSlantEqual":"≽","SucceedsTilde":"≿","succeq":"⪰","succnapprox":"⪺","succneqq":"⪶","succnsim":"⋩","succsim":"≿","SuchThat":"∋","sum":"∑","Sum":"∑","sung":"♪","sup1":"¹","sup2":"²","sup3":"³","sup":"⊃","Sup":"⋑","supdot":"⪾","supdsub":"⫘","supE":"⫆","supe":"⊇","supedot":"⫄","Superset":"⊃","SupersetEqual":"⊇","suphsol":"⟉","suphsub":"⫗","suplarr":"⥻","supmult":"⫂","supnE":"⫌","supne":"⊋","supplus":"⫀","supset":"⊃","Supset":"⋑","supseteq":"⊇","supseteqq":"⫆","supsetneq":"⊋","supsetneqq":"⫌","supsim":"⫈","supsub":"⫔","supsup":"⫖","swarhk":"⤦","swarr":"↙","swArr":"⇙","swarrow":"↙","swnwar":"⤪","szlig":"ß","Tab":"\\t","target":"⌖","Tau":"Τ","tau":"τ","tbrk":"⎴","Tcaron":"Ť","tcaron":"ť","Tcedil":"Ţ","tcedil":"ţ","Tcy":"Т","tcy":"т","tdot":"⃛","telrec":"⌕","Tfr":"𝔗","tfr":"𝔱","there4":"∴","therefore":"∴","Therefore":"∴","Theta":"Θ","theta":"θ","thetasym":"ϑ","thetav":"ϑ","thickapprox":"≈","thicksim":"∼","ThickSpace":"  ","ThinSpace":" ","thinsp":" ","thkap":"≈","thksim":"∼","THORN":"Þ","thorn":"þ","tilde":"˜","Tilde":"∼","TildeEqual":"≃","TildeFullEqual":"≅","TildeTilde":"≈","timesbar":"⨱","timesb":"⊠","times":"×","timesd":"⨰","tint":"∭","toea":"⤨","topbot":"⌶","topcir":"⫱","top":"⊤","Topf":"𝕋","topf":"𝕥","topfork":"⫚","tosa":"⤩","tprime":"‴","trade":"™","TRADE":"™","triangle":"▵","triangledown":"▿","triangleleft":"◃","trianglelefteq":"⊴","triangleq":"≜","triangleright":"▹","trianglerighteq":"⊵","tridot":"◬","trie":"≜","triminus":"⨺","TripleDot":"⃛","triplus":"⨹","trisb":"⧍","tritime":"⨻","trpezium":"⏢","Tscr":"𝒯","tscr":"𝓉","TScy":"Ц","tscy":"ц","TSHcy":"Ћ","tshcy":"ћ","Tstrok":"Ŧ","tstrok":"ŧ","twixt":"≬","twoheadleftarrow":"↞","twoheadrightarrow":"↠","Uacute":"Ú","uacute":"ú","uarr":"↑","Uarr":"↟","uArr":"⇑","Uarrocir":"⥉","Ubrcy":"Ў","ubrcy":"ў","Ubreve":"Ŭ","ubreve":"ŭ","Ucirc":"Û","ucirc":"û","Ucy":"У","ucy":"у","udarr":"⇅","Udblac":"Ű","udblac":"ű","udhar":"⥮","ufisht":"⥾","Ufr":"𝔘","ufr":"𝔲","Ugrave":"Ù","ugrave":"ù","uHar":"⥣","uharl":"↿","uharr":"↾","uhblk":"▀","ulcorn":"⌜","ulcorner":"⌜","ulcrop":"⌏","ultri":"◸","Umacr":"Ū","umacr":"ū","uml":"¨","UnderBar":"_","UnderBrace":"⏟","UnderBracket":"⎵","UnderParenthesis":"⏝","Union":"⋃","UnionPlus":"⊎","Uogon":"Ų","uogon":"ų","Uopf":"𝕌","uopf":"𝕦","UpArrowBar":"⤒","uparrow":"↑","UpArrow":"↑","Uparrow":"⇑","UpArrowDownArrow":"⇅","updownarrow":"↕","UpDownArrow":"↕","Updownarrow":"⇕","UpEquilibrium":"⥮","upharpoonleft":"↿","upharpoonright":"↾","uplus":"⊎","UpperLeftArrow":"↖","UpperRightArrow":"↗","upsi":"υ","Upsi":"ϒ","upsih":"ϒ","Upsilon":"Υ","upsilon":"υ","UpTeeArrow":"↥","UpTee":"⊥","upuparrows":"⇈","urcorn":"⌝","urcorner":"⌝","urcrop":"⌎","Uring":"Ů","uring":"ů","urtri":"◹","Uscr":"𝒰","uscr":"𝓊","utdot":"⋰","Utilde":"Ũ","utilde":"ũ","utri":"▵","utrif":"▴","uuarr":"⇈","Uuml":"Ü","uuml":"ü","uwangle":"⦧","vangrt":"⦜","varepsilon":"ϵ","varkappa":"ϰ","varnothing":"∅","varphi":"ϕ","varpi":"ϖ","varpropto":"∝","varr":"↕","vArr":"⇕","varrho":"ϱ","varsigma":"ς","varsubsetneq":"⊊︀","varsubsetneqq":"⫋︀","varsupsetneq":"⊋︀","varsupsetneqq":"⫌︀","vartheta":"ϑ","vartriangleleft":"⊲","vartriangleright":"⊳","vBar":"⫨","Vbar":"⫫","vBarv":"⫩","Vcy":"В","vcy":"в","vdash":"⊢","vDash":"⊨","Vdash":"⊩","VDash":"⊫","Vdashl":"⫦","veebar":"⊻","vee":"∨","Vee":"⋁","veeeq":"≚","vellip":"⋮","verbar":"|","Verbar":"‖","vert":"|","Vert":"‖","VerticalBar":"∣","VerticalLine":"|","VerticalSeparator":"❘","VerticalTilde":"≀","VeryThinSpace":" ","Vfr":"𝔙","vfr":"𝔳","vltri":"⊲","vnsub":"⊂⃒","vnsup":"⊃⃒","Vopf":"𝕍","vopf":"𝕧","vprop":"∝","vrtri":"⊳","Vscr":"𝒱","vscr":"𝓋","vsubnE":"⫋︀","vsubne":"⊊︀","vsupnE":"⫌︀","vsupne":"⊋︀","Vvdash":"⊪","vzigzag":"⦚","Wcirc":"Ŵ","wcirc":"ŵ","wedbar":"⩟","wedge":"∧","Wedge":"⋀","wedgeq":"≙","weierp":"℘","Wfr":"𝔚","wfr":"𝔴","Wopf":"𝕎","wopf":"𝕨","wp":"℘","wr":"≀","wreath":"≀","Wscr":"𝒲","wscr":"𝓌","xcap":"⋂","xcirc":"◯","xcup":"⋃","xdtri":"▽","Xfr":"𝔛","xfr":"𝔵","xharr":"⟷","xhArr":"⟺","Xi":"Ξ","xi":"ξ","xlarr":"⟵","xlArr":"⟸","xmap":"⟼","xnis":"⋻","xodot":"⨀","Xopf":"𝕏","xopf":"𝕩","xoplus":"⨁","xotime":"⨂","xrarr":"⟶","xrArr":"⟹","Xscr":"𝒳","xscr":"𝓍","xsqcup":"⨆","xuplus":"⨄","xutri":"△","xvee":"⋁","xwedge":"⋀","Yacute":"Ý","yacute":"ý","YAcy":"Я","yacy":"я","Ycirc":"Ŷ","ycirc":"ŷ","Ycy":"Ы","ycy":"ы","yen":"¥","Yfr":"𝔜","yfr":"𝔶","YIcy":"Ї","yicy":"ї","Yopf":"𝕐","yopf":"𝕪","Yscr":"𝒴","yscr":"𝓎","YUcy":"Ю","yucy":"ю","yuml":"ÿ","Yuml":"Ÿ","Zacute":"Ź","zacute":"ź","Zcaron":"Ž","zcaron":"ž","Zcy":"З","zcy":"з","Zdot":"Ż","zdot":"ż","zeetrf":"ℨ","ZeroWidthSpace":"​","Zeta":"Ζ","zeta":"ζ","zfr":"𝔷","Zfr":"ℨ","ZHcy":"Ж","zhcy":"ж","zigrarr":"⇝","zopf":"𝕫","Zopf":"ℤ","Zscr":"𝒵","zscr":"𝓏","zwj":"‍","zwnj":"‌"}')},function(e,t,r){"use strict";var n={};function s(e,t,r){var o,i,a,u,c,l="";for("string"!=typeof t&&(r=t,t=s.defaultChars),void 0===r&&(r=!0),c=function(e){var t,r,s=n[e];if(s)return s;for(s=n[e]=[],t=0;t<128;t++)r=String.fromCharCode(t),/^[0-9a-z]$/i.test(r)?s.push(r):s.push("%"+("0"+t.toString(16).toUpperCase()).slice(-2));for(t=0;t=55296&&a<=57343){if(a>=55296&&a<=56319&&o+1=56320&&u<=57343){l+=encodeURIComponent(e[o]+e[o+1]),o++;continue}l+="%EF%BF%BD"}else l+=encodeURIComponent(e[o]);return l}s.defaultChars=";/?:@&=+$,-_.!~*'()#",s.componentChars="-_.!~*'()",e.exports=s},function(e,t,r){"use strict";var n={};function s(e,t){var r;return"string"!=typeof t&&(t=s.defaultChars),r=function(e){var t,r,s=n[e];if(s)return s;for(s=n[e]=[],t=0;t<128;t++)r=String.fromCharCode(t),s.push(r);for(t=0;t=55296&&u<=57343?"���":String.fromCharCode(u),t+=6):240==(248&s)&&t+91114111?c+="����":(u-=65536,c+=String.fromCharCode(55296+(u>>10),56320+(1023&u))),t+=9):c+="�";return c}))}s.defaultChars=";/?:@&=+$,#",s.componentChars="",e.exports=s},function(e,t,r){"use strict";e.exports=function(e){var t="";return t+=e.protocol||"",t+=e.slashes?"//":"",t+=e.auth?e.auth+"@":"",e.hostname&&-1!==e.hostname.indexOf(":")?t+="["+e.hostname+"]":t+=e.hostname||"",t+=e.port?":"+e.port:"",t+=e.pathname||"",t+=e.search||"",t+=e.hash||""}},function(e,t,r){"use strict";function n(){this.protocol=null,this.slashes=null,this.auth=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.pathname=null}var s=/^([a-z0-9.+-]+:)/i,o=/:[0-9]*$/,i=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,a=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),u=["'"].concat(a),c=["%","/","?",";","#"].concat(u),l=["/","?","#"],p=/^[+a-z0-9A-Z_-]{0,63}$/,h=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,f={javascript:!0,"javascript:":!0},d={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0};n.prototype.parse=function(e,t){var r,n,o,a,u,m=e;if(m=m.trim(),!t&&1===e.split("#").length){var g=i.exec(m);if(g)return this.pathname=g[1],g[2]&&(this.search=g[2]),this}var _=s.exec(m);if(_&&(o=(_=_[0]).toLowerCase(),this.protocol=_,m=m.substr(_.length)),(t||_||m.match(/^\/\/[^@\/]+@[^@\/]+/))&&(!(u="//"===m.substr(0,2))||_&&f[_]||(m=m.substr(2),this.slashes=!0)),!f[_]&&(u||_&&!d[_])){var b,k,C=-1;for(r=0;r127?x+="x":x+=y[F];if(!x.match(p)){var E=A.slice(0,r),q=A.slice(r+1),S=y.match(h);S&&(E.push(S[1]),q.unshift(S[2])),q.length&&(m=q.join(".")+m),this.hostname=E.join(".");break}}}}this.hostname.length>255&&(this.hostname=""),D&&(this.hostname=this.hostname.substr(1,this.hostname.length-2))}var L=m.indexOf("#");-1!==L&&(this.hash=m.substr(L),m=m.slice(0,L));var z=m.indexOf("?");return-1!==z&&(this.search=m.substr(z),m=m.slice(0,z)),m&&(this.pathname=m),d[o]&&this.hostname&&!this.pathname&&(this.pathname=""),this},n.prototype.parseHost=function(e){var t=o.exec(e);t&&(":"!==(t=t[0])&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)},e.exports=function(e,t){if(e&&e instanceof n)return e;var r=new n;return r.parse(e,t),r}},function(e,t,r){"use strict";t.Any=r(18),t.Cc=r(19),t.Cf=r(20),t.P=r(4),t.Z=r(21)},function(e,t){e.exports=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/},function(e,t){e.exports=/[\0-\x1F\x7F-\x9F]/},function(e,t){e.exports=/[\xAD\u0600-\u0605\u061C\u06DD\u070F\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804\uDCBD|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/},function(e,t){e.exports=/[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/},function(e,t,r){"use strict";t.parseLinkLabel=r(23),t.parseLinkDestination=r(24),t.parseLinkTitle=r(25)},function(e,t,r){"use strict";e.exports=function(e,t,r){var n,s,o,i,a=-1,u=e.posMax,c=e.pos;for(e.pos=t+1,n=1;e.pos32)return a;if(41===s){if(0===o)break;o--}t++}return i===t||0!==o||(a.str=n(e.slice(i,t)),a.lines=0,a.pos=t,a.ok=!0),a}},function(e,t,r){"use strict";var n=r(0).unescapeAll;e.exports=function(e,t,r){var s,o,i=0,a=t,u={ok:!1,pos:0,lines:0,str:""};if(t>=r)return u;if(34!==(o=e.charCodeAt(t))&&39!==o&&40!==o)return u;for(t++,40===o&&(o=41);t"+o(e[t].content)+""},i.code_block=function(e,t,r,n,s){var i=e[t];return""+o(e[t].content)+"\n"},i.fence=function(e,t,r,n,i){var a,u,c,l,p,h=e[t],f=h.info?s(h.info).trim():"",d="",m="";return f&&(d=(c=f.split(/(\s+)/g))[0],m=c.slice(2).join("")),0===(a=r.highlight&&r.highlight(h.content,d,m)||o(h.content)).indexOf(""+a+"\n"):"
"+a+"
\n"},i.image=function(e,t,r,n,s){var o=e[t];return o.attrs[o.attrIndex("alt")][1]=s.renderInlineAsText(o.children,r,n),s.renderToken(e,t,r)},i.hardbreak=function(e,t,r){return r.xhtmlOut?"
\n":"
\n"},i.softbreak=function(e,t,r){return r.breaks?r.xhtmlOut?"
\n":"
\n":"\n"},i.text=function(e,t){return o(e[t].content)},i.html_block=function(e,t){return e[t].content},i.html_inline=function(e,t){return e[t].content},a.prototype.renderAttrs=function(e){var t,r,n;if(!e.attrs)return"";for(n="",t=0,r=e.attrs.length;t\n":">")},a.prototype.renderInline=function(e,t,r){for(var n,s="",o=this.rules,i=0,a=e.length;i/i.test(e)}e.exports=function(e){var t,r,o,i,a,u,c,l,p,h,f,d,m,g,_,b,k,C,v=e.tokens;if(e.md.options.linkify)for(r=0,o=v.length;r=0;t--)if("link_close"!==(u=i[t]).type){if("html_inline"===u.type&&(C=u.content,/^\s]/i.test(C)&&m>0&&m--,s(u.content)&&m++),!(m>0)&&"text"===u.type&&e.md.linkify.test(u.content)){for(p=u.content,k=e.md.linkify.match(p),c=[],d=u.level,f=0,l=0;lf&&((a=new e.Token("text","",0)).content=p.slice(f,h),a.level=d,c.push(a)),(a=new e.Token("link_open","a",1)).attrs=[["href",_]],a.level=d++,a.markup="linkify",a.info="auto",c.push(a),(a=new e.Token("text","",0)).content=b,a.level=d,c.push(a),(a=new e.Token("link_close","a",-1)).level=--d,a.markup="linkify",a.info="auto",c.push(a),f=k[l].lastIndex);f=0;t--)"text"!==(r=e[t]).type||n||(r.content=r.content.replace(o,a)),"link_open"===r.type&&"auto"===r.info&&n--,"link_close"===r.type&&"auto"===r.info&&n++}function c(e){var t,r,s=0;for(t=e.length-1;t>=0;t--)"text"!==(r=e[t]).type||s||n.test(r.content)&&(r.content=r.content.replace(/\+-/g,"±").replace(/\.{2,}/g,"…").replace(/([?!])…/g,"$1..").replace(/([?!]){4,}/g,"$1$1$1").replace(/,{2,}/g,",").replace(/(^|[^-])---(?=[^-]|$)/gm,"$1—").replace(/(^|\s)--(?=\s|$)/gm,"$1–").replace(/(^|[^-\s])--(?=[^-\s]|$)/gm,"$1–")),"link_open"===r.type&&"auto"===r.info&&s--,"link_close"===r.type&&"auto"===r.info&&s++}e.exports=function(e){var t;if(e.md.options.typographer)for(t=e.tokens.length-1;t>=0;t--)"inline"===e.tokens[t].type&&(s.test(e.tokens[t].content)&&u(e.tokens[t].children),n.test(e.tokens[t].content)&&c(e.tokens[t].children))}},function(e,t,r){"use strict";var n=r(0).isWhiteSpace,s=r(0).isPunctChar,o=r(0).isMdAsciiPunct,i=/['"]/,a=/['"]/g;function u(e,t,r){return e.substr(0,t)+r+e.substr(t+1)}function c(e,t){var r,i,c,l,p,h,f,d,m,g,_,b,k,C,v,D,A,y,x,F,w;for(x=[],r=0;r=0&&!(x[A].level<=f);A--);if(x.length=A+1,"text"===i.type){p=0,h=(c=i.content).length;e:for(;p=0)m=c.charCodeAt(l.index-1);else for(A=r-1;A>=0&&("softbreak"!==e[A].type&&"hardbreak"!==e[A].type);A--)if(e[A].content){m=e[A].content.charCodeAt(e[A].content.length-1);break}if(g=32,p=48&&m<=57&&(D=v=!1),v&&D&&(v=_,D=b),v||D){if(D)for(A=x.length-1;A>=0&&(d=x[A],!(x[A].level=0;t--)"inline"===e.tokens[t].type&&i.test(e.tokens[t].content)&&c(e.tokens[t].children,e)}},function(e,t,r){"use strict";var n=r(2);function s(e,t,r){this.src=e,this.env=r,this.tokens=[],this.inlineMode=!1,this.md=t}s.prototype.Token=n,e.exports=s},function(e,t,r){"use strict";var n=r(1),s=[["table",r(36),["paragraph","reference"]],["code",r(37)],["fence",r(38),["paragraph","reference","blockquote","list"]],["blockquote",r(39),["paragraph","reference","blockquote","list"]],["hr",r(40),["paragraph","reference","blockquote","list"]],["list",r(41),["paragraph","reference","blockquote"]],["reference",r(42)],["heading",r(43),["paragraph","reference","blockquote"]],["lheading",r(44)],["html_block",r(45),["paragraph","reference","blockquote"]],["paragraph",r(47)]];function o(){this.ruler=new n;for(var e=0;e=r))&&!(e.sCount[i]=u){e.line=r;break}for(n=0;nr)return!1;if(h=t+1,e.sCount[h]=4)return!1;if((c=e.bMarks[h]+e.tShift[h])>=e.eMarks[h])return!1;if(124!==(a=e.src.charCodeAt(c++))&&45!==a&&58!==a)return!1;for(;c=4)return!1;if((f=o(u)).length&&""===f[0]&&f.shift(),f.length&&""===f[f.length-1]&&f.pop(),0===(d=f.length)||d!==g.length)return!1;if(i)return!0;for(C=e.parentType,e.parentType="table",D=e.md.block.ruler.getRules("blockquote"),(m=e.push("table_open","table",1)).map=b=[t,0],(m=e.push("thead_open","thead",1)).map=[t,t+1],(m=e.push("tr_open","tr",1)).map=[t,t+1],l=0;l=4)break;for((f=o(u)).length&&""===f[0]&&f.shift(),f.length&&""===f[f.length-1]&&f.pop(),h===t+2&&((m=e.push("tbody_open","tbody",1)).map=k=[t+2,0]),(m=e.push("tr_open","tr",1)).map=[h,h+1],l=0;l=4))break;s=++n}return e.line=s,(o=e.push("code_block","code",0)).content=e.getLines(t,s,4+e.blkIndent,!0),o.map=[t,e.line],!0}},function(e,t,r){"use strict";e.exports=function(e,t,r,n){var s,o,i,a,u,c,l,p=!1,h=e.bMarks[t]+e.tShift[t],f=e.eMarks[t];if(e.sCount[t]-e.blkIndent>=4)return!1;if(h+3>f)return!1;if(126!==(s=e.src.charCodeAt(h))&&96!==s)return!1;if(u=h,(o=(h=e.skipChars(h,s))-u)<3)return!1;if(l=e.src.slice(u,h),i=e.src.slice(h,f),96===s&&i.indexOf(String.fromCharCode(s))>=0)return!1;if(n)return!0;for(a=t;!(++a>=r)&&!((h=u=e.bMarks[a]+e.tShift[a])<(f=e.eMarks[a])&&e.sCount[a]=4||(h=e.skipChars(h,s))-u=4)return!1;if(62!==e.src.charCodeAt(F++))return!1;if(s)return!0;for(u=f=e.sCount[t]+1,32===e.src.charCodeAt(F)?(F++,u++,f++,o=!1,C=!0):9===e.src.charCodeAt(F)?(C=!0,(e.bsCount[t]+f)%4==3?(F++,u++,f++,o=!1):o=!0):C=!1,d=[e.bMarks[t]],e.bMarks[t]=F;F=w,b=[e.sCount[t]],e.sCount[t]=f-u,k=[e.tShift[t]],e.tShift[t]=F-e.bMarks[t],D=e.md.block.ruler.getRules("blockquote"),_=e.parentType,e.parentType="blockquote",h=t+1;h=(w=e.eMarks[h])));h++)if(62!==e.src.charCodeAt(F++)||y){if(l)break;for(v=!1,a=0,c=D.length;a=w,m.push(e.bsCount[h]),e.bsCount[h]=e.sCount[h]+1+(C?1:0),b.push(e.sCount[h]),e.sCount[h]=f-u,k.push(e.tShift[h]),e.tShift[h]=F-e.bMarks[h]}for(g=e.blkIndent,e.blkIndent=0,(A=e.push("blockquote_open","blockquote",1)).markup=">",A.map=p=[t,0],e.md.block.tokenize(e,t,h),(A=e.push("blockquote_close","blockquote",-1)).markup=">",e.lineMax=x,e.parentType=_,p[1]=e.line,a=0;a=4)return!1;if(42!==(o=e.src.charCodeAt(c++))&&45!==o&&95!==o)return!1;for(i=1;c=i)return-1;if((r=e.src.charCodeAt(o++))<48||r>57)return-1;for(;;){if(o>=i)return-1;if(!((r=e.src.charCodeAt(o++))>=48&&r<=57)){if(41===r||46===r)break;return-1}if(o-s>=10)return-1}return o=4)return!1;if(e.listIndent>=0&&e.sCount[t]-e.listIndent>=4&&e.sCount[t]=e.blkIndent&&(B=!0),(E=o(e,t))>=0){if(h=!0,S=e.bMarks[t]+e.tShift[t],b=Number(e.src.substr(S,E-S-1)),B&&1!==b)return!1}else{if(!((E=s(e,t))>=0))return!1;h=!1}if(B&&e.skipSpaces(E)>=e.eMarks[t])return!1;if(_=e.src.charCodeAt(E-1),n)return!0;for(g=e.tokens.length,h?(T=e.push("ordered_list_open","ol",1),1!==b&&(T.attrs=[["start",b]])):T=e.push("bullet_list_open","ul",1),T.map=m=[t,0],T.markup=String.fromCharCode(_),C=t,q=!1,z=e.md.block.ruler.getRules("list"),A=e.parentType,e.parentType="list";C=k?1:v-p)>4&&(l=1),c=p+l,(T=e.push("list_item_open","li",1)).markup=String.fromCharCode(_),T.map=f=[t,0],F=e.tight,x=e.tShift[t],y=e.sCount[t],D=e.listIndent,e.listIndent=e.blkIndent,e.blkIndent=c,e.tight=!0,e.tShift[t]=a-e.bMarks[t],e.sCount[t]=v,a>=k&&e.isEmpty(t+1)?e.line=Math.min(e.line+2,r):e.md.block.tokenize(e,t,r,!0),e.tight&&!q||(I=!1),q=e.line-t>1&&e.isEmpty(e.line-1),e.blkIndent=e.listIndent,e.listIndent=D,e.tShift[t]=x,e.sCount[t]=y,e.tight=F,(T=e.push("list_item_close","li",-1)).markup=String.fromCharCode(_),C=t=e.line,f[1]=C,a=e.bMarks[t],C>=r)break;if(e.sCount[C]=4)break;for(L=!1,u=0,d=z.length;u=4)return!1;if(91!==e.src.charCodeAt(A))return!1;for(;++A3||e.sCount[x]<0)){for(k=!1,p=0,h=C.length;p=4)return!1;if(35!==(o=e.src.charCodeAt(c))||c>=l)return!1;for(i=1,o=e.src.charCodeAt(++c);35===o&&c6||cc&&n(e.src.charCodeAt(a-1))&&(l=a),e.line=t+1,(u=e.push("heading_open","h"+String(i),1)).markup="########".slice(0,i),u.map=[t,e.line],(u=e.push("inline","",0)).content=e.src.slice(c,l).trim(),u.map=[t,e.line],u.children=[],(u=e.push("heading_close","h"+String(i),-1)).markup="########".slice(0,i)),!0)}},function(e,t,r){"use strict";e.exports=function(e,t,r){var n,s,o,i,a,u,c,l,p,h,f=t+1,d=e.md.block.ruler.getRules("paragraph");if(e.sCount[t]-e.blkIndent>=4)return!1;for(h=e.parentType,e.parentType="paragraph";f3)){if(e.sCount[f]>=e.blkIndent&&(u=e.bMarks[f]+e.tShift[f])<(c=e.eMarks[f])&&(45===(p=e.src.charCodeAt(u))||61===p)&&(u=e.skipChars(u,p),(u=e.skipSpaces(u))>=c)){l=61===p?1:2;break}if(!(e.sCount[f]<0)){for(s=!1,o=0,i=d.length;o|$))/i,/<\/(script|pre|style)>/i,!0],[/^/,!0],[/^<\?/,/\?>/,!0],[/^/,!0],[/^/,!0],[new RegExp("^|$))","i"),/^$/,!0],[new RegExp(s.source+"\\s*$"),/^$/,!1]];e.exports=function(e,t,r,n){var s,i,a,u,c=e.bMarks[t]+e.tShift[t],l=e.eMarks[t];if(e.sCount[t]-e.blkIndent>=4)return!1;if(!e.md.options.html)return!1;if(60!==e.src.charCodeAt(c))return!1;for(u=e.src.slice(c,l),s=0;s3||e.sCount[u]<0)){for(n=!1,s=0,o=c.length;s0&&this.level++,this.tokens.push(s),s},o.prototype.isEmpty=function(e){return this.bMarks[e]+this.tShift[e]>=this.eMarks[e]},o.prototype.skipEmptyLines=function(e){for(var t=this.lineMax;et;)if(!s(this.src.charCodeAt(--e)))return e+1;return e},o.prototype.skipChars=function(e,t){for(var r=this.src.length;er;)if(t!==this.src.charCodeAt(--e))return e+1;return e},o.prototype.getLines=function(e,t,r,n){var o,i,a,u,c,l,p,h=e;if(e>=t)return"";for(l=new Array(t-e),o=0;hr?new Array(i-r+1).join(" ")+this.src.slice(u,c):this.src.slice(u,c)}return l.join("")},o.prototype.Token=n,e.exports=o},function(e,t,r){"use strict";var n=r(1),s=[["text",r(50)],["newline",r(51)],["escape",r(52)],["backticks",r(53)],["strikethrough",r(7).tokenize],["emphasis",r(8).tokenize],["link",r(54)],["image",r(55)],["autolink",r(56)],["html_inline",r(57)],["entity",r(58)]],o=[["balance_pairs",r(59)],["strikethrough",r(7).postProcess],["emphasis",r(8).postProcess],["text_collapse",r(60)]];function i(){var e;for(this.ruler=new n,e=0;e=o)break}else e.pending+=e.src[e.pos++]}e.pending&&e.pushPending()},i.prototype.parse=function(e,t,r,n){var s,o,i,a=new this.State(e,t,r,n);for(this.tokenize(a),i=(o=this.ruler2.getRules("")).length,s=0;s=0&&32===e.pending.charCodeAt(r)?r>=1&&32===e.pending.charCodeAt(r-1)?(e.pending=e.pending.replace(/ +$/,""),e.push("hardbreak","br",0)):(e.pending=e.pending.slice(0,-1),e.push("softbreak","br",0)):e.push("softbreak","br",0)),o++;o?@[]^_`{|}~-".split("").forEach((function(e){s[e.charCodeAt(0)]=1})),e.exports=function(e,t){var r,o=e.pos,i=e.posMax;if(92!==e.src.charCodeAt(o))return!1;if(++o=m)return!1;if(g=c,(l=e.md.helpers.parseLinkDestination(e.src,c,e.posMax)).ok){for(h=e.md.normalizeLink(l.str),e.md.validateLink(h)?c=l.pos:h="",g=c;c=m||41!==e.src.charCodeAt(c))&&(_=!0),c++}if(_){if(void 0===e.env.references)return!1;if(c=0?i=e.src.slice(g,c++):c=a+1):c=a+1,i||(i=e.src.slice(u,a)),!(p=e.env.references[n(i)]))return e.pos=d,!1;h=p.href,f=p.title}return t||(e.pos=u,e.posMax=a,e.push("link_open","a",1).attrs=r=[["href",h]],f&&r.push(["title",f]),e.md.inline.tokenize(e),e.push("link_close","a",-1)),e.pos=c,e.posMax=m,!0}},function(e,t,r){"use strict";var n=r(0).normalizeReference,s=r(0).isSpace;e.exports=function(e,t){var r,o,i,a,u,c,l,p,h,f,d,m,g,_="",b=e.pos,k=e.posMax;if(33!==e.src.charCodeAt(e.pos))return!1;if(91!==e.src.charCodeAt(e.pos+1))return!1;if(c=e.pos+2,(u=e.md.helpers.parseLinkLabel(e,e.pos+1,!1))<0)return!1;if((l=u+1)=k)return!1;for(g=l,(h=e.md.helpers.parseLinkDestination(e.src,l,e.posMax)).ok&&(_=e.md.normalizeLink(h.str),e.md.validateLink(_)?l=h.pos:_=""),g=l;l=k||41!==e.src.charCodeAt(l))return e.pos=b,!1;l++}else{if(void 0===e.env.references)return!1;if(l=0?a=e.src.slice(g,l++):l=u+1):l=u+1,a||(a=e.src.slice(c,u)),!(p=e.env.references[n(a)]))return e.pos=b,!1;_=p.href,f=p.title}return t||(i=e.src.slice(c,u),e.md.inline.parse(i,e.md,e.env,m=[]),(d=e.push("image","img",0)).attrs=r=[["src",_],["alt",""]],d.children=m,d.content=i,f&&r.push(["title",f])),e.pos=l,e.posMax=k,!0}},function(e,t,r){"use strict";var n=/^([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/,s=/^([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)$/;e.exports=function(e,t){var r,o,i,a,u,c,l=e.pos;if(60!==e.src.charCodeAt(l))return!1;for(u=e.pos,c=e.posMax;;){if(++l>=c)return!1;if(60===(a=e.src.charCodeAt(l)))return!1;if(62===a)break}return r=e.src.slice(u+1,l),s.test(r)?(o=e.md.normalizeLink(r),!!e.md.validateLink(o)&&(t||((i=e.push("link_open","a",1)).attrs=[["href",o]],i.markup="autolink",i.info="auto",(i=e.push("text","",0)).content=e.md.normalizeLinkText(r),(i=e.push("link_close","a",-1)).markup="autolink",i.info="auto"),e.pos+=r.length+2,!0)):!!n.test(r)&&(o=e.md.normalizeLink("mailto:"+r),!!e.md.validateLink(o)&&(t||((i=e.push("link_open","a",1)).attrs=[["href",o]],i.markup="autolink",i.info="auto",(i=e.push("text","",0)).content=e.md.normalizeLinkText(r),(i=e.push("link_close","a",-1)).markup="autolink",i.info="auto"),e.pos+=r.length+2,!0))}},function(e,t,r){"use strict";var n=r(6).HTML_TAG_RE;e.exports=function(e,t){var r,s,o,i=e.pos;return!!e.md.options.html&&(o=e.posMax,!(60!==e.src.charCodeAt(i)||i+2>=o)&&(!(33!==(r=e.src.charCodeAt(i+1))&&63!==r&&47!==r&&!function(e){var t=32|e;return t>=97&&t<=122}(r))&&(!!(s=e.src.slice(i).match(n))&&(t||(e.push("html_inline","",0).content=e.src.slice(i,i+s[0].length)),e.pos+=s[0].length,!0))))}},function(e,t,r){"use strict";var n=r(3),s=r(0).has,o=r(0).isValidEntityCode,i=r(0).fromCodePoint,a=/^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i,u=/^&([a-z][a-z0-9]{1,31});/i;e.exports=function(e,t){var r,c,l=e.pos,p=e.posMax;if(38!==e.src.charCodeAt(l))return!1;if(l+1i;n-=o.jump+1)if((o=t[n]).marker===s.marker&&o.open&&o.end<0&&(u=!1,(o.close||s.open)&&(o.length+s.length)%3==0&&(o.length%3==0&&s.length%3==0||(u=!0)),!u)){c=n>0&&!t[n-1].open?t[n-1].jump+1:0,s.jump=r-n+c,s.open=!1,o.end=r,o.jump=c,o.close=!1,a=-1;break}-1!==a&&(l[s.marker][(s.length||0)%3]=a)}}e.exports=function(e){var t,r=e.tokens_meta,s=e.tokens_meta.length;for(n(0,e.delimiters),t=0;t0&&n++,"text"===s[t].type&&t+10&&(this.level++,this._prev_delimiters.push(this.delimiters),this.delimiters=[],o={delimiters:this.delimiters}),this.pendingLevel=this.level,this.tokens.push(s),this.tokens_meta.push(o),s},a.prototype.scanDelims=function(e,t){var r,n,a,u,c,l,p,h,f,d=e,m=!0,g=!0,_=this.posMax,b=this.src.charCodeAt(e);for(r=e>0?this.src.charCodeAt(e-1):32;d<_&&this.src.charCodeAt(d)===b;)d++;return a=d-e,n=d<_?this.src.charCodeAt(d):32,p=i(r)||o(String.fromCharCode(r)),f=i(n)||o(String.fromCharCode(n)),l=s(r),(h=s(n))?m=!1:f&&(l||p||(m=!1)),l?g=!1:p&&(h||f||(g=!1)),t?(u=m,c=g):(u=m&&(!g||p),c=g&&(!m||f)),{can_open:u,can_close:c,length:a}},a.prototype.Token=n,e.exports=a},function(e,t,r){"use strict";function n(e){var t=Array.prototype.slice.call(arguments,1);return t.forEach((function(t){t&&Object.keys(t).forEach((function(r){e[r]=t[r]}))})),e}function s(e){return Object.prototype.toString.call(e)}function o(e){return"[object Function]"===s(e)}function i(e){return e.replace(/[.?*+^$[\]\\(){}|-]/g,"\\$&")}var a={fuzzyLink:!0,fuzzyEmail:!0,fuzzyIP:!1};var u={"http:":{validate:function(e,t,r){var n=e.slice(t);return r.re.http||(r.re.http=new RegExp("^\\/\\/"+r.re.src_auth+r.re.src_host_port_strict+r.re.src_path,"i")),r.re.http.test(n)?n.match(r.re.http)[0].length:0}},"https:":"http:","ftp:":"http:","//":{validate:function(e,t,r){var n=e.slice(t);return r.re.no_http||(r.re.no_http=new RegExp("^"+r.re.src_auth+"(?:localhost|(?:(?:"+r.re.src_domain+")\\.)+"+r.re.src_domain_root+")"+r.re.src_port+r.re.src_host_terminator+r.re.src_path,"i")),r.re.no_http.test(n)?t>=3&&":"===e[t-3]||t>=3&&"/"===e[t-3]?0:n.match(r.re.no_http)[0].length:0}},"mailto:":{validate:function(e,t,r){var n=e.slice(t);return r.re.mailto||(r.re.mailto=new RegExp("^"+r.re.src_email_name+"@"+r.re.src_host_strict,"i")),r.re.mailto.test(n)?n.match(r.re.mailto)[0].length:0}}},c="biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф".split("|");function l(e){var t=e.re=r(63)(e.__opts__),n=e.__tlds__.slice();function a(e){return e.replace("%TLDS%",t.src_tlds)}e.onCompile(),e.__tlds_replaced__||n.push("a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]"),n.push(t.src_xn),t.src_tlds=n.join("|"),t.email_fuzzy=RegExp(a(t.tpl_email_fuzzy),"i"),t.link_fuzzy=RegExp(a(t.tpl_link_fuzzy),"i"),t.link_no_ip_fuzzy=RegExp(a(t.tpl_link_no_ip_fuzzy),"i"),t.host_fuzzy_test=RegExp(a(t.tpl_host_fuzzy_test),"i");var u=[];function c(e,t){throw new Error('(LinkifyIt) Invalid schema "'+e+'": '+t)}e.__compiled__={},Object.keys(e.__schemas__).forEach((function(t){var r=e.__schemas__[t];if(null!==r){var n={validate:null,link:null};if(e.__compiled__[t]=n,"[object Object]"===s(r))return!function(e){return"[object RegExp]"===s(e)}(r.validate)?o(r.validate)?n.validate=r.validate:c(t,r):n.validate=function(e){return function(t,r){var n=t.slice(r);return e.test(n)?n.match(e)[0].length:0}}(r.validate),void(o(r.normalize)?n.normalize=r.normalize:r.normalize?c(t,r):n.normalize=function(e,t){t.normalize(e)});!function(e){return"[object String]"===s(e)}(r)?c(t,r):u.push(t)}})),u.forEach((function(t){e.__compiled__[e.__schemas__[t]]&&(e.__compiled__[t].validate=e.__compiled__[e.__schemas__[t]].validate,e.__compiled__[t].normalize=e.__compiled__[e.__schemas__[t]].normalize)})),e.__compiled__[""]={validate:null,normalize:function(e,t){t.normalize(e)}};var l=Object.keys(e.__compiled__).filter((function(t){return t.length>0&&e.__compiled__[t]})).map(i).join("|");e.re.schema_test=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+l+")","i"),e.re.schema_search=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+l+")","ig"),e.re.pretest=RegExp("("+e.re.schema_test.source+")|("+e.re.host_fuzzy_test.source+")|@","i"),function(e){e.__index__=-1,e.__text_cache__=""}(e)}function p(e,t){var r=e.__index__,n=e.__last_index__,s=e.__text_cache__.slice(r,n);this.schema=e.__schema__.toLowerCase(),this.index=r+t,this.lastIndex=n+t,this.raw=s,this.text=s,this.url=s}function h(e,t){var r=new p(e,t);return e.__compiled__[r.schema].normalize(r,e),r}function f(e,t){if(!(this instanceof f))return new f(e,t);var r;t||(r=e,Object.keys(r||{}).reduce((function(e,t){return e||a.hasOwnProperty(t)}),!1)&&(t=e,e={})),this.__opts__=n({},a,t),this.__index__=-1,this.__last_index__=-1,this.__schema__="",this.__text_cache__="",this.__schemas__=n({},u,e),this.__compiled__={},this.__tlds__=c,this.__tlds_replaced__=!1,this.re={},l(this)}f.prototype.add=function(e,t){return this.__schemas__[e]=t,l(this),this},f.prototype.set=function(e){return this.__opts__=n(this.__opts__,e),this},f.prototype.test=function(e){if(this.__text_cache__=e,this.__index__=-1,!e.length)return!1;var t,r,n,s,o,i,a,u;if(this.re.schema_test.test(e))for((a=this.re.schema_search).lastIndex=0;null!==(t=a.exec(e));)if(s=this.testSchemaAt(e,t[2],a.lastIndex)){this.__schema__=t[2],this.__index__=t.index+t[1].length,this.__last_index__=t.index+t[0].length+s;break}return this.__opts__.fuzzyLink&&this.__compiled__["http:"]&&(u=e.search(this.re.host_fuzzy_test))>=0&&(this.__index__<0||u=0&&null!==(n=e.match(this.re.email_fuzzy))&&(o=n.index+n[1].length,i=n.index+n[0].length,(this.__index__<0||othis.__last_index__)&&(this.__schema__="mailto:",this.__index__=o,this.__last_index__=i)),this.__index__>=0},f.prototype.pretest=function(e){return this.re.pretest.test(e)},f.prototype.testSchemaAt=function(e,t,r){return this.__compiled__[t.toLowerCase()]?this.__compiled__[t.toLowerCase()].validate(e,r,this):0},f.prototype.match=function(e){var t=0,r=[];this.__index__>=0&&this.__text_cache__===e&&(r.push(h(this,t)),t=this.__last_index__);for(var n=t?e.slice(t):e;this.test(n);)r.push(h(this,t)),n=n.slice(this.__last_index__),t+=this.__last_index__;return r.length?r:null},f.prototype.tlds=function(e,t){return e=Array.isArray(e)?e:[e],t?(this.__tlds__=this.__tlds__.concat(e).sort().filter((function(e,t,r){return e!==r[t-1]})).reverse(),l(this),this):(this.__tlds__=e.slice(),this.__tlds_replaced__=!0,l(this),this)},f.prototype.normalize=function(e){e.schema||(e.url="http://"+e.url),"mailto:"!==e.schema||/^mailto:/i.test(e.url)||(e.url="mailto:"+e.url)},f.prototype.onCompile=function(){},e.exports=f},function(e,t,r){"use strict";e.exports=function(e){var t={};t.src_Any=r(64).source,t.src_Cc=r(65).source,t.src_Z=r(66).source,t.src_P=r(67).source,t.src_ZPCc=[t.src_Z,t.src_P,t.src_Cc].join("|"),t.src_ZCc=[t.src_Z,t.src_Cc].join("|");return t.src_pseudo_letter="(?:(?![><|]|"+t.src_ZPCc+")"+t.src_Any+")",t.src_ip4="(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",t.src_auth="(?:(?:(?!"+t.src_ZCc+"|[@/\\[\\]()]).)+@)?",t.src_port="(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?",t.src_host_terminator="(?=$|[><|]|"+t.src_ZPCc+")(?!-|_|:\\d|\\.-|\\.(?!$|"+t.src_ZPCc+"))",t.src_path="(?:[/?#](?:(?!"+t.src_ZCc+"|[><|]|[()[\\]{}.,\"'?!\\-]).|\\[(?:(?!"+t.src_ZCc+"|\\]).)*\\]|\\((?:(?!"+t.src_ZCc+"|[)]).)*\\)|\\{(?:(?!"+t.src_ZCc+'|[}]).)*\\}|\\"(?:(?!'+t.src_ZCc+'|["]).)+\\"|\\\'(?:(?!'+t.src_ZCc+"|[']).)+\\'|\\'(?="+t.src_pseudo_letter+"|[-]).|\\.{2,}[a-zA-Z0-9%/&]|\\.(?!"+t.src_ZCc+"|[.]).|"+(e&&e["---"]?"\\-(?!--(?:[^-]|$))(?:-*)|":"\\-+|")+"\\,(?!"+t.src_ZCc+").|\\!+(?!"+t.src_ZCc+"|[!]).|\\?(?!"+t.src_ZCc+"|[?]).)+|\\/)?",t.src_email_name='[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*',t.src_xn="xn--[a-z0-9\\-]{1,59}",t.src_domain_root="(?:"+t.src_xn+"|"+t.src_pseudo_letter+"{1,63})",t.src_domain="(?:"+t.src_xn+"|(?:"+t.src_pseudo_letter+")|(?:"+t.src_pseudo_letter+"(?:-|"+t.src_pseudo_letter+"){0,61}"+t.src_pseudo_letter+"))",t.src_host="(?:(?:(?:(?:"+t.src_domain+")\\.)*"+t.src_domain+"))",t.tpl_host_fuzzy="(?:"+t.src_ip4+"|(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%)))",t.tpl_host_no_ip_fuzzy="(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%))",t.src_host_strict=t.src_host+t.src_host_terminator,t.tpl_host_fuzzy_strict=t.tpl_host_fuzzy+t.src_host_terminator,t.src_host_port_strict=t.src_host+t.src_port+t.src_host_terminator,t.tpl_host_port_fuzzy_strict=t.tpl_host_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_port_no_ip_fuzzy_strict=t.tpl_host_no_ip_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_fuzzy_test="localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:"+t.src_ZPCc+"|>|$))",t.tpl_email_fuzzy='(^|[><|]|"|\\(|'+t.src_ZCc+")("+t.src_email_name+"@"+t.tpl_host_fuzzy_strict+")",t.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_fuzzy_strict+t.src_path+")",t.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_no_ip_fuzzy_strict+t.src_path+")",t}},function(e,t){e.exports=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/},function(e,t){e.exports=/[\0-\x1F\x7F-\x9F]/},function(e,t){e.exports=/[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/},function(e,t){e.exports=/[!-#%-\*,-/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E44\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD807[\uDC41-\uDC45\uDC70\uDC71]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/},function(e,t,r){(function(e,n){var s;/*! https://mths.be/punycode v1.4.1 by @mathias */!function(o){t&&t.nodeType,e&&e.nodeType;var i="object"==typeof n&&n;i.global!==i&&i.window!==i&&i.self;var a,u=2147483647,c=/^xn--/,l=/[^\x20-\x7E]/,p=/[\x2E\u3002\uFF0E\uFF61]/g,h={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},f=Math.floor,d=String.fromCharCode;function m(e){throw new RangeError(h[e])}function g(e,t){for(var r=e.length,n=[];r--;)n[r]=t(e[r]);return n}function _(e,t){var r=e.split("@"),n="";return r.length>1&&(n=r[0]+"@",e=r[1]),n+g((e=e.replace(p,".")).split("."),t).join(".")}function b(e){for(var t,r,n=[],s=0,o=e.length;s=55296&&t<=56319&&s65535&&(t+=d((e-=65536)>>>10&1023|55296),e=56320|1023&e),t+=d(e)})).join("")}function C(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function v(e,t,r){var n=0;for(e=r?f(e/700):e>>1,e+=f(e/t);e>455;n+=36)e=f(e/35);return f(n+36*e/(e+38))}function D(e){var t,r,n,s,o,i,a,c,l,p,h,d=[],g=e.length,_=0,b=128,C=72;for((r=e.lastIndexOf("-"))<0&&(r=0),n=0;n=128&&m("not-basic"),d.push(e.charCodeAt(n));for(s=r>0?r+1:0;s=g&&m("invalid-input"),((c=(h=e.charCodeAt(s++))-48<10?h-22:h-65<26?h-65:h-97<26?h-97:36)>=36||c>f((u-_)/i))&&m("overflow"),_+=c*i,!(c<(l=a<=C?1:a>=C+26?26:a-C));a+=36)i>f(u/(p=36-l))&&m("overflow"),i*=p;C=v(_-o,t=d.length+1,0==o),f(_/t)>u-b&&m("overflow"),b+=f(_/t),_%=t,d.splice(_++,0,b)}return k(d)}function A(e){var t,r,n,s,o,i,a,c,l,p,h,g,_,k,D,A=[];for(g=(e=b(e)).length,t=128,r=0,o=72,i=0;i=t&&hf((u-r)/(_=n+1))&&m("overflow"),r+=(a-t)*_,t=a,i=0;iu&&m("overflow"),h==t){for(c=r,l=36;!(c<(p=l<=o?1:l>=o+26?26:l-o));l+=36)D=c-p,k=36-p,A.push(d(C(p+D%k,0))),c=f(D/k);A.push(d(C(c,0))),o=v(r,_,n==s),r=0,++n}++r,++t}return A.join("")}a={version:"1.4.1",ucs2:{decode:b,encode:k},decode:D,encode:A,toASCII:function(e){return _(e,(function(e){return l.test(e)?"xn--"+A(e):e}))},toUnicode:function(e){return _(e,(function(e){return c.test(e)?D(e.slice(4).toLowerCase()):e}))}},void 0===(s=function(){return a}.call(t,r,t,e))||(e.exports=s)}()}).call(this,r(69)(e),r(70))},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r},function(e,t,r){"use strict";e.exports={options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:100},components:{core:{},block:{},inline:{}}}},function(e,t,r){"use strict";e.exports={options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{rules:["normalize","block","inline"]},block:{rules:["paragraph"]},inline:{rules:["text"],rules2:["balance_pairs","text_collapse"]}}}},function(e,t,r){"use strict";e.exports={options:{html:!0,xhtmlOut:!0,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{rules:["normalize","block","inline"]},block:{rules:["blockquote","code","fence","heading","hr","html_block","lheading","list","reference","paragraph"]},inline:{rules:["autolink","backticks","emphasis","entity","escape","html_inline","image","link","newline","text"],rules2:["balance_pairs","emphasis","text_collapse"]}}}}]); \ No newline at end of file +!function(e){var t={};function r(n){if(t[n])return t[n].exports;var s=t[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,r),s.l=!0,s.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)r.d(n,s,function(t){return e[t]}.bind(null,s));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=9)}([function(e,t,r){"use strict";var n=Object.prototype.hasOwnProperty;function s(e,t){return n.call(e,t)}function o(e){return!(e>=55296&&e<=57343)&&(!(e>=64976&&e<=65007)&&(65535!=(65535&e)&&65534!=(65535&e)&&(!(e>=0&&e<=8)&&(11!==e&&(!(e>=14&&e<=31)&&(!(e>=127&&e<=159)&&!(e>1114111)))))))}function i(e){if(e>65535){var t=55296+((e-=65536)>>10),r=56320+(1023&e);return String.fromCharCode(t,r)}return String.fromCharCode(e)}var a=/\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g,u=new RegExp(a.source+"|"+/&([a-z#][a-z0-9]{1,31});/gi.source,"gi"),c=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i,l=r(3);var p=/[&<>"]/,h=/[&<>"]/g,f={"&":"&","<":"<",">":">",'"':"""};function d(e){return f[e]}var m=/[.?*+^$[\]\\(){}|-]/g;var g=r(4);t.lib={},t.lib.mdurl=r(5),t.lib.ucmicro=r(17),t.assign=function(e){var t=Array.prototype.slice.call(arguments,1);return t.forEach((function(t){if(t){if("object"!=typeof t)throw new TypeError(t+"must be object");Object.keys(t).forEach((function(r){e[r]=t[r]}))}})),e},t.isString=function(e){return"[object String]"===function(e){return Object.prototype.toString.call(e)}(e)},t.has=s,t.unescapeMd=function(e){return e.indexOf("\\")<0?e:e.replace(a,"$1")},t.unescapeAll=function(e){return e.indexOf("\\")<0&&e.indexOf("&")<0?e:e.replace(u,(function(e,t,r){return t||function(e,t){var r=0;return s(l,t)?l[t]:35===t.charCodeAt(0)&&c.test(t)&&o(r="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10))?i(r):e}(e,r)}))},t.isValidEntityCode=o,t.fromCodePoint=i,t.escapeHtml=function(e){return p.test(e)?e.replace(h,d):e},t.arrayReplaceAt=function(e,t,r){return[].concat(e.slice(0,t),r,e.slice(t+1))},t.isSpace=function(e){switch(e){case 9:case 32:return!0}return!1},t.isWhiteSpace=function(e){if(e>=8192&&e<=8202)return!0;switch(e){case 9:case 10:case 11:case 12:case 13:case 32:case 160:case 5760:case 8239:case 8287:case 12288:return!0}return!1},t.isMdAsciiPunct=function(e){switch(e){case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:case 58:case 59:case 60:case 61:case 62:case 63:case 64:case 91:case 92:case 93:case 94:case 95:case 96:case 123:case 124:case 125:case 126:return!0;default:return!1}},t.isPunctChar=function(e){return g.test(e)},t.escapeRE=function(e){return e.replace(m,"\\$&")},t.normalizeReference=function(e){return e=e.trim().replace(/\s+/g," "),"Ṿ"==="ẞ".toLowerCase()&&(e=e.replace(/ẞ/g,"ß")),e.toLowerCase().toUpperCase()}},function(e,t,r){"use strict";function n(){this.__rules__=[],this.__cache__=null}n.prototype.__find__=function(e){for(var t=0;t=0&&(r=this.attrs[t][1]),r},n.prototype.attrJoin=function(e,t){var r=this.attrIndex(e);r<0?this.attrPush([e,t]):this.attrs[r][1]=this.attrs[r][1]+" "+t},e.exports=n},function(e,t,r){"use strict";e.exports=r(12)},function(e,t){e.exports=/[!-#%-\*,-/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E49\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD806[\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2]|\uD807[\uDC41-\uDC45\uDC70\uDC71]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/},function(e,t,r){"use strict";e.exports.encode=r(13),e.exports.decode=r(14),e.exports.format=r(15),e.exports.parse=r(16)},function(e,t,r){"use strict";var n="<[A-Za-z][A-Za-z0-9\\-]*(?:\\s+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:\\s*=\\s*(?:[^\"'=<>`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*\\/?>",s="<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>",o=new RegExp("^(?:"+n+"|"+s+"|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|<[?][\\s\\S]*?[?]>|]*>|)"),i=new RegExp("^(?:"+n+"|"+s+")");e.exports.HTML_TAG_RE=o,e.exports.HTML_OPEN_CLOSE_TAG_RE=i},function(e,t,r){"use strict";function n(e,t){var r,n,s,o,i,a=[],u=t.length;for(r=0;r=0;r--)95!==(n=t[r]).marker&&42!==n.marker||-1!==n.end&&(s=t[n.end],a=r>0&&t[r-1].end===n.end+1&&t[r-1].token===n.token-1&&t[n.end+1].token===s.token+1&&t[r-1].marker===n.marker,i=String.fromCharCode(n.marker),(o=e.tokens[n.token]).type=a?"strong_open":"em_open",o.tag=a?"strong":"em",o.nesting=1,o.markup=a?i+i:i,o.content="",(o=e.tokens[s.token]).type=a?"strong_close":"em_close",o.tag=a?"strong":"em",o.nesting=-1,o.markup=a?i+i:i,o.content="",a&&(e.tokens[t[r-1].token].content="",e.tokens[t[n.end+1].token].content="",r--))}e.exports.tokenize=function(e,t){var r,n,s=e.pos,o=e.src.charCodeAt(s);if(t)return!1;if(95!==o&&42!==o)return!1;for(n=e.scanDelims(e.pos,42===o),r=0;r{t(e)};acquireNotebookRendererApi("notebookCoreTestRenderer").onDidCreateMarkdown(({element:t,content:r})=>{const n=e.render(r);t.innerHTML=n})}()},function(e,t,r){"use strict";e.exports=r(11)},function(e,t,r){"use strict";var n=r(0),s=r(22),o=r(26),i=r(27),a=r(35),u=r(49),c=r(62),l=r(5),p=r(68),h={default:r(71),zero:r(72),commonmark:r(73)},f=/^(vbscript|javascript|file|data):/,d=/^data:image\/(gif|png|jpeg|webp);/;function m(e){var t=e.trim().toLowerCase();return!f.test(t)||!!d.test(t)}var g=["http:","https:","mailto:"];function _(e){var t=l.parse(e,!0);if(t.hostname&&(!t.protocol||g.indexOf(t.protocol)>=0))try{t.hostname=p.toASCII(t.hostname)}catch(e){}return l.encode(l.format(t))}function b(e){var t=l.parse(e,!0);if(t.hostname&&(!t.protocol||g.indexOf(t.protocol)>=0))try{t.hostname=p.toUnicode(t.hostname)}catch(e){}return l.decode(l.format(t),l.decode.defaultChars+"%")}function k(e,t){if(!(this instanceof k))return new k(e,t);t||n.isString(e)||(t=e||{},e="default"),this.inline=new u,this.block=new a,this.core=new i,this.renderer=new o,this.linkify=new c,this.validateLink=m,this.normalizeLink=_,this.normalizeLinkText=b,this.utils=n,this.helpers=n.assign({},s),this.options={},this.configure(e),t&&this.set(t)}k.prototype.set=function(e){return n.assign(this.options,e),this},k.prototype.configure=function(e){var t,r=this;if(n.isString(e)&&!(e=h[t=e]))throw new Error('Wrong `markdown-it` preset "'+t+'", check name');if(!e)throw new Error("Wrong `markdown-it` preset, can't be empty");return e.options&&r.set(e.options),e.components&&Object.keys(e.components).forEach((function(t){e.components[t].rules&&r[t].ruler.enableOnly(e.components[t].rules),e.components[t].rules2&&r[t].ruler2.enableOnly(e.components[t].rules2)})),this},k.prototype.enable=function(e,t){var r=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(t){r=r.concat(this[t].ruler.enable(e,!0))}),this),r=r.concat(this.inline.ruler2.enable(e,!0));var n=e.filter((function(e){return r.indexOf(e)<0}));if(n.length&&!t)throw new Error("MarkdownIt. Failed to enable unknown rule(s): "+n);return this},k.prototype.disable=function(e,t){var r=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(t){r=r.concat(this[t].ruler.disable(e,!0))}),this),r=r.concat(this.inline.ruler2.disable(e,!0));var n=e.filter((function(e){return r.indexOf(e)<0}));if(n.length&&!t)throw new Error("MarkdownIt. Failed to disable unknown rule(s): "+n);return this},k.prototype.use=function(e){var t=[this].concat(Array.prototype.slice.call(arguments,1));return e.apply(e,t),this},k.prototype.parse=function(e,t){if("string"!=typeof e)throw new Error("Input data should be a String");var r=new this.core.State(e,this,t);return this.core.process(r),r.tokens},k.prototype.render=function(e,t){return t=t||{},this.renderer.render(this.parse(e,t),this.options,t)},k.prototype.parseInline=function(e,t){var r=new this.core.State(e,this,t);return r.inlineMode=!0,this.core.process(r),r.tokens},k.prototype.renderInline=function(e,t){return t=t||{},this.renderer.render(this.parseInline(e,t),this.options,t)},e.exports=k},function(e){e.exports=JSON.parse('{"Aacute":"Á","aacute":"á","Abreve":"Ă","abreve":"ă","ac":"∾","acd":"∿","acE":"∾̳","Acirc":"Â","acirc":"â","acute":"´","Acy":"А","acy":"а","AElig":"Æ","aelig":"æ","af":"⁡","Afr":"𝔄","afr":"𝔞","Agrave":"À","agrave":"à","alefsym":"ℵ","aleph":"ℵ","Alpha":"Α","alpha":"α","Amacr":"Ā","amacr":"ā","amalg":"⨿","amp":"&","AMP":"&","andand":"⩕","And":"⩓","and":"∧","andd":"⩜","andslope":"⩘","andv":"⩚","ang":"∠","ange":"⦤","angle":"∠","angmsdaa":"⦨","angmsdab":"⦩","angmsdac":"⦪","angmsdad":"⦫","angmsdae":"⦬","angmsdaf":"⦭","angmsdag":"⦮","angmsdah":"⦯","angmsd":"∡","angrt":"∟","angrtvb":"⊾","angrtvbd":"⦝","angsph":"∢","angst":"Å","angzarr":"⍼","Aogon":"Ą","aogon":"ą","Aopf":"𝔸","aopf":"𝕒","apacir":"⩯","ap":"≈","apE":"⩰","ape":"≊","apid":"≋","apos":"\'","ApplyFunction":"⁡","approx":"≈","approxeq":"≊","Aring":"Å","aring":"å","Ascr":"𝒜","ascr":"𝒶","Assign":"≔","ast":"*","asymp":"≈","asympeq":"≍","Atilde":"Ã","atilde":"ã","Auml":"Ä","auml":"ä","awconint":"∳","awint":"⨑","backcong":"≌","backepsilon":"϶","backprime":"‵","backsim":"∽","backsimeq":"⋍","Backslash":"∖","Barv":"⫧","barvee":"⊽","barwed":"⌅","Barwed":"⌆","barwedge":"⌅","bbrk":"⎵","bbrktbrk":"⎶","bcong":"≌","Bcy":"Б","bcy":"б","bdquo":"„","becaus":"∵","because":"∵","Because":"∵","bemptyv":"⦰","bepsi":"϶","bernou":"ℬ","Bernoullis":"ℬ","Beta":"Β","beta":"β","beth":"ℶ","between":"≬","Bfr":"𝔅","bfr":"𝔟","bigcap":"⋂","bigcirc":"◯","bigcup":"⋃","bigodot":"⨀","bigoplus":"⨁","bigotimes":"⨂","bigsqcup":"⨆","bigstar":"★","bigtriangledown":"▽","bigtriangleup":"△","biguplus":"⨄","bigvee":"⋁","bigwedge":"⋀","bkarow":"⤍","blacklozenge":"⧫","blacksquare":"▪","blacktriangle":"▴","blacktriangledown":"▾","blacktriangleleft":"◂","blacktriangleright":"▸","blank":"␣","blk12":"▒","blk14":"░","blk34":"▓","block":"█","bne":"=⃥","bnequiv":"≡⃥","bNot":"⫭","bnot":"⌐","Bopf":"𝔹","bopf":"𝕓","bot":"⊥","bottom":"⊥","bowtie":"⋈","boxbox":"⧉","boxdl":"┐","boxdL":"╕","boxDl":"╖","boxDL":"╗","boxdr":"┌","boxdR":"╒","boxDr":"╓","boxDR":"╔","boxh":"─","boxH":"═","boxhd":"┬","boxHd":"╤","boxhD":"╥","boxHD":"╦","boxhu":"┴","boxHu":"╧","boxhU":"╨","boxHU":"╩","boxminus":"⊟","boxplus":"⊞","boxtimes":"⊠","boxul":"┘","boxuL":"╛","boxUl":"╜","boxUL":"╝","boxur":"└","boxuR":"╘","boxUr":"╙","boxUR":"╚","boxv":"│","boxV":"║","boxvh":"┼","boxvH":"╪","boxVh":"╫","boxVH":"╬","boxvl":"┤","boxvL":"╡","boxVl":"╢","boxVL":"╣","boxvr":"├","boxvR":"╞","boxVr":"╟","boxVR":"╠","bprime":"‵","breve":"˘","Breve":"˘","brvbar":"¦","bscr":"𝒷","Bscr":"ℬ","bsemi":"⁏","bsim":"∽","bsime":"⋍","bsolb":"⧅","bsol":"\\\\","bsolhsub":"⟈","bull":"•","bullet":"•","bump":"≎","bumpE":"⪮","bumpe":"≏","Bumpeq":"≎","bumpeq":"≏","Cacute":"Ć","cacute":"ć","capand":"⩄","capbrcup":"⩉","capcap":"⩋","cap":"∩","Cap":"⋒","capcup":"⩇","capdot":"⩀","CapitalDifferentialD":"ⅅ","caps":"∩︀","caret":"⁁","caron":"ˇ","Cayleys":"ℭ","ccaps":"⩍","Ccaron":"Č","ccaron":"č","Ccedil":"Ç","ccedil":"ç","Ccirc":"Ĉ","ccirc":"ĉ","Cconint":"∰","ccups":"⩌","ccupssm":"⩐","Cdot":"Ċ","cdot":"ċ","cedil":"¸","Cedilla":"¸","cemptyv":"⦲","cent":"¢","centerdot":"·","CenterDot":"·","cfr":"𝔠","Cfr":"ℭ","CHcy":"Ч","chcy":"ч","check":"✓","checkmark":"✓","Chi":"Χ","chi":"χ","circ":"ˆ","circeq":"≗","circlearrowleft":"↺","circlearrowright":"↻","circledast":"⊛","circledcirc":"⊚","circleddash":"⊝","CircleDot":"⊙","circledR":"®","circledS":"Ⓢ","CircleMinus":"⊖","CirclePlus":"⊕","CircleTimes":"⊗","cir":"○","cirE":"⧃","cire":"≗","cirfnint":"⨐","cirmid":"⫯","cirscir":"⧂","ClockwiseContourIntegral":"∲","CloseCurlyDoubleQuote":"”","CloseCurlyQuote":"’","clubs":"♣","clubsuit":"♣","colon":":","Colon":"∷","Colone":"⩴","colone":"≔","coloneq":"≔","comma":",","commat":"@","comp":"∁","compfn":"∘","complement":"∁","complexes":"ℂ","cong":"≅","congdot":"⩭","Congruent":"≡","conint":"∮","Conint":"∯","ContourIntegral":"∮","copf":"𝕔","Copf":"ℂ","coprod":"∐","Coproduct":"∐","copy":"©","COPY":"©","copysr":"℗","CounterClockwiseContourIntegral":"∳","crarr":"↵","cross":"✗","Cross":"⨯","Cscr":"𝒞","cscr":"𝒸","csub":"⫏","csube":"⫑","csup":"⫐","csupe":"⫒","ctdot":"⋯","cudarrl":"⤸","cudarrr":"⤵","cuepr":"⋞","cuesc":"⋟","cularr":"↶","cularrp":"⤽","cupbrcap":"⩈","cupcap":"⩆","CupCap":"≍","cup":"∪","Cup":"⋓","cupcup":"⩊","cupdot":"⊍","cupor":"⩅","cups":"∪︀","curarr":"↷","curarrm":"⤼","curlyeqprec":"⋞","curlyeqsucc":"⋟","curlyvee":"⋎","curlywedge":"⋏","curren":"¤","curvearrowleft":"↶","curvearrowright":"↷","cuvee":"⋎","cuwed":"⋏","cwconint":"∲","cwint":"∱","cylcty":"⌭","dagger":"†","Dagger":"‡","daleth":"ℸ","darr":"↓","Darr":"↡","dArr":"⇓","dash":"‐","Dashv":"⫤","dashv":"⊣","dbkarow":"⤏","dblac":"˝","Dcaron":"Ď","dcaron":"ď","Dcy":"Д","dcy":"д","ddagger":"‡","ddarr":"⇊","DD":"ⅅ","dd":"ⅆ","DDotrahd":"⤑","ddotseq":"⩷","deg":"°","Del":"∇","Delta":"Δ","delta":"δ","demptyv":"⦱","dfisht":"⥿","Dfr":"𝔇","dfr":"𝔡","dHar":"⥥","dharl":"⇃","dharr":"⇂","DiacriticalAcute":"´","DiacriticalDot":"˙","DiacriticalDoubleAcute":"˝","DiacriticalGrave":"`","DiacriticalTilde":"˜","diam":"⋄","diamond":"⋄","Diamond":"⋄","diamondsuit":"♦","diams":"♦","die":"¨","DifferentialD":"ⅆ","digamma":"ϝ","disin":"⋲","div":"÷","divide":"÷","divideontimes":"⋇","divonx":"⋇","DJcy":"Ђ","djcy":"ђ","dlcorn":"⌞","dlcrop":"⌍","dollar":"$","Dopf":"𝔻","dopf":"𝕕","Dot":"¨","dot":"˙","DotDot":"⃜","doteq":"≐","doteqdot":"≑","DotEqual":"≐","dotminus":"∸","dotplus":"∔","dotsquare":"⊡","doublebarwedge":"⌆","DoubleContourIntegral":"∯","DoubleDot":"¨","DoubleDownArrow":"⇓","DoubleLeftArrow":"⇐","DoubleLeftRightArrow":"⇔","DoubleLeftTee":"⫤","DoubleLongLeftArrow":"⟸","DoubleLongLeftRightArrow":"⟺","DoubleLongRightArrow":"⟹","DoubleRightArrow":"⇒","DoubleRightTee":"⊨","DoubleUpArrow":"⇑","DoubleUpDownArrow":"⇕","DoubleVerticalBar":"∥","DownArrowBar":"⤓","downarrow":"↓","DownArrow":"↓","Downarrow":"⇓","DownArrowUpArrow":"⇵","DownBreve":"̑","downdownarrows":"⇊","downharpoonleft":"⇃","downharpoonright":"⇂","DownLeftRightVector":"⥐","DownLeftTeeVector":"⥞","DownLeftVectorBar":"⥖","DownLeftVector":"↽","DownRightTeeVector":"⥟","DownRightVectorBar":"⥗","DownRightVector":"⇁","DownTeeArrow":"↧","DownTee":"⊤","drbkarow":"⤐","drcorn":"⌟","drcrop":"⌌","Dscr":"𝒟","dscr":"𝒹","DScy":"Ѕ","dscy":"ѕ","dsol":"⧶","Dstrok":"Đ","dstrok":"đ","dtdot":"⋱","dtri":"▿","dtrif":"▾","duarr":"⇵","duhar":"⥯","dwangle":"⦦","DZcy":"Џ","dzcy":"џ","dzigrarr":"⟿","Eacute":"É","eacute":"é","easter":"⩮","Ecaron":"Ě","ecaron":"ě","Ecirc":"Ê","ecirc":"ê","ecir":"≖","ecolon":"≕","Ecy":"Э","ecy":"э","eDDot":"⩷","Edot":"Ė","edot":"ė","eDot":"≑","ee":"ⅇ","efDot":"≒","Efr":"𝔈","efr":"𝔢","eg":"⪚","Egrave":"È","egrave":"è","egs":"⪖","egsdot":"⪘","el":"⪙","Element":"∈","elinters":"⏧","ell":"ℓ","els":"⪕","elsdot":"⪗","Emacr":"Ē","emacr":"ē","empty":"∅","emptyset":"∅","EmptySmallSquare":"◻","emptyv":"∅","EmptyVerySmallSquare":"▫","emsp13":" ","emsp14":" ","emsp":" ","ENG":"Ŋ","eng":"ŋ","ensp":" ","Eogon":"Ę","eogon":"ę","Eopf":"𝔼","eopf":"𝕖","epar":"⋕","eparsl":"⧣","eplus":"⩱","epsi":"ε","Epsilon":"Ε","epsilon":"ε","epsiv":"ϵ","eqcirc":"≖","eqcolon":"≕","eqsim":"≂","eqslantgtr":"⪖","eqslantless":"⪕","Equal":"⩵","equals":"=","EqualTilde":"≂","equest":"≟","Equilibrium":"⇌","equiv":"≡","equivDD":"⩸","eqvparsl":"⧥","erarr":"⥱","erDot":"≓","escr":"ℯ","Escr":"ℰ","esdot":"≐","Esim":"⩳","esim":"≂","Eta":"Η","eta":"η","ETH":"Ð","eth":"ð","Euml":"Ë","euml":"ë","euro":"€","excl":"!","exist":"∃","Exists":"∃","expectation":"ℰ","exponentiale":"ⅇ","ExponentialE":"ⅇ","fallingdotseq":"≒","Fcy":"Ф","fcy":"ф","female":"♀","ffilig":"ffi","fflig":"ff","ffllig":"ffl","Ffr":"𝔉","ffr":"𝔣","filig":"fi","FilledSmallSquare":"◼","FilledVerySmallSquare":"▪","fjlig":"fj","flat":"♭","fllig":"fl","fltns":"▱","fnof":"ƒ","Fopf":"𝔽","fopf":"𝕗","forall":"∀","ForAll":"∀","fork":"⋔","forkv":"⫙","Fouriertrf":"ℱ","fpartint":"⨍","frac12":"½","frac13":"⅓","frac14":"¼","frac15":"⅕","frac16":"⅙","frac18":"⅛","frac23":"⅔","frac25":"⅖","frac34":"¾","frac35":"⅗","frac38":"⅜","frac45":"⅘","frac56":"⅚","frac58":"⅝","frac78":"⅞","frasl":"⁄","frown":"⌢","fscr":"𝒻","Fscr":"ℱ","gacute":"ǵ","Gamma":"Γ","gamma":"γ","Gammad":"Ϝ","gammad":"ϝ","gap":"⪆","Gbreve":"Ğ","gbreve":"ğ","Gcedil":"Ģ","Gcirc":"Ĝ","gcirc":"ĝ","Gcy":"Г","gcy":"г","Gdot":"Ġ","gdot":"ġ","ge":"≥","gE":"≧","gEl":"⪌","gel":"⋛","geq":"≥","geqq":"≧","geqslant":"⩾","gescc":"⪩","ges":"⩾","gesdot":"⪀","gesdoto":"⪂","gesdotol":"⪄","gesl":"⋛︀","gesles":"⪔","Gfr":"𝔊","gfr":"𝔤","gg":"≫","Gg":"⋙","ggg":"⋙","gimel":"ℷ","GJcy":"Ѓ","gjcy":"ѓ","gla":"⪥","gl":"≷","glE":"⪒","glj":"⪤","gnap":"⪊","gnapprox":"⪊","gne":"⪈","gnE":"≩","gneq":"⪈","gneqq":"≩","gnsim":"⋧","Gopf":"𝔾","gopf":"𝕘","grave":"`","GreaterEqual":"≥","GreaterEqualLess":"⋛","GreaterFullEqual":"≧","GreaterGreater":"⪢","GreaterLess":"≷","GreaterSlantEqual":"⩾","GreaterTilde":"≳","Gscr":"𝒢","gscr":"ℊ","gsim":"≳","gsime":"⪎","gsiml":"⪐","gtcc":"⪧","gtcir":"⩺","gt":">","GT":">","Gt":"≫","gtdot":"⋗","gtlPar":"⦕","gtquest":"⩼","gtrapprox":"⪆","gtrarr":"⥸","gtrdot":"⋗","gtreqless":"⋛","gtreqqless":"⪌","gtrless":"≷","gtrsim":"≳","gvertneqq":"≩︀","gvnE":"≩︀","Hacek":"ˇ","hairsp":" ","half":"½","hamilt":"ℋ","HARDcy":"Ъ","hardcy":"ъ","harrcir":"⥈","harr":"↔","hArr":"⇔","harrw":"↭","Hat":"^","hbar":"ℏ","Hcirc":"Ĥ","hcirc":"ĥ","hearts":"♥","heartsuit":"♥","hellip":"…","hercon":"⊹","hfr":"𝔥","Hfr":"ℌ","HilbertSpace":"ℋ","hksearow":"⤥","hkswarow":"⤦","hoarr":"⇿","homtht":"∻","hookleftarrow":"↩","hookrightarrow":"↪","hopf":"𝕙","Hopf":"ℍ","horbar":"―","HorizontalLine":"─","hscr":"𝒽","Hscr":"ℋ","hslash":"ℏ","Hstrok":"Ħ","hstrok":"ħ","HumpDownHump":"≎","HumpEqual":"≏","hybull":"⁃","hyphen":"‐","Iacute":"Í","iacute":"í","ic":"⁣","Icirc":"Î","icirc":"î","Icy":"И","icy":"и","Idot":"İ","IEcy":"Е","iecy":"е","iexcl":"¡","iff":"⇔","ifr":"𝔦","Ifr":"ℑ","Igrave":"Ì","igrave":"ì","ii":"ⅈ","iiiint":"⨌","iiint":"∭","iinfin":"⧜","iiota":"℩","IJlig":"IJ","ijlig":"ij","Imacr":"Ī","imacr":"ī","image":"ℑ","ImaginaryI":"ⅈ","imagline":"ℐ","imagpart":"ℑ","imath":"ı","Im":"ℑ","imof":"⊷","imped":"Ƶ","Implies":"⇒","incare":"℅","in":"∈","infin":"∞","infintie":"⧝","inodot":"ı","intcal":"⊺","int":"∫","Int":"∬","integers":"ℤ","Integral":"∫","intercal":"⊺","Intersection":"⋂","intlarhk":"⨗","intprod":"⨼","InvisibleComma":"⁣","InvisibleTimes":"⁢","IOcy":"Ё","iocy":"ё","Iogon":"Į","iogon":"į","Iopf":"𝕀","iopf":"𝕚","Iota":"Ι","iota":"ι","iprod":"⨼","iquest":"¿","iscr":"𝒾","Iscr":"ℐ","isin":"∈","isindot":"⋵","isinE":"⋹","isins":"⋴","isinsv":"⋳","isinv":"∈","it":"⁢","Itilde":"Ĩ","itilde":"ĩ","Iukcy":"І","iukcy":"і","Iuml":"Ï","iuml":"ï","Jcirc":"Ĵ","jcirc":"ĵ","Jcy":"Й","jcy":"й","Jfr":"𝔍","jfr":"𝔧","jmath":"ȷ","Jopf":"𝕁","jopf":"𝕛","Jscr":"𝒥","jscr":"𝒿","Jsercy":"Ј","jsercy":"ј","Jukcy":"Є","jukcy":"є","Kappa":"Κ","kappa":"κ","kappav":"ϰ","Kcedil":"Ķ","kcedil":"ķ","Kcy":"К","kcy":"к","Kfr":"𝔎","kfr":"𝔨","kgreen":"ĸ","KHcy":"Х","khcy":"х","KJcy":"Ќ","kjcy":"ќ","Kopf":"𝕂","kopf":"𝕜","Kscr":"𝒦","kscr":"𝓀","lAarr":"⇚","Lacute":"Ĺ","lacute":"ĺ","laemptyv":"⦴","lagran":"ℒ","Lambda":"Λ","lambda":"λ","lang":"⟨","Lang":"⟪","langd":"⦑","langle":"⟨","lap":"⪅","Laplacetrf":"ℒ","laquo":"«","larrb":"⇤","larrbfs":"⤟","larr":"←","Larr":"↞","lArr":"⇐","larrfs":"⤝","larrhk":"↩","larrlp":"↫","larrpl":"⤹","larrsim":"⥳","larrtl":"↢","latail":"⤙","lAtail":"⤛","lat":"⪫","late":"⪭","lates":"⪭︀","lbarr":"⤌","lBarr":"⤎","lbbrk":"❲","lbrace":"{","lbrack":"[","lbrke":"⦋","lbrksld":"⦏","lbrkslu":"⦍","Lcaron":"Ľ","lcaron":"ľ","Lcedil":"Ļ","lcedil":"ļ","lceil":"⌈","lcub":"{","Lcy":"Л","lcy":"л","ldca":"⤶","ldquo":"“","ldquor":"„","ldrdhar":"⥧","ldrushar":"⥋","ldsh":"↲","le":"≤","lE":"≦","LeftAngleBracket":"⟨","LeftArrowBar":"⇤","leftarrow":"←","LeftArrow":"←","Leftarrow":"⇐","LeftArrowRightArrow":"⇆","leftarrowtail":"↢","LeftCeiling":"⌈","LeftDoubleBracket":"⟦","LeftDownTeeVector":"⥡","LeftDownVectorBar":"⥙","LeftDownVector":"⇃","LeftFloor":"⌊","leftharpoondown":"↽","leftharpoonup":"↼","leftleftarrows":"⇇","leftrightarrow":"↔","LeftRightArrow":"↔","Leftrightarrow":"⇔","leftrightarrows":"⇆","leftrightharpoons":"⇋","leftrightsquigarrow":"↭","LeftRightVector":"⥎","LeftTeeArrow":"↤","LeftTee":"⊣","LeftTeeVector":"⥚","leftthreetimes":"⋋","LeftTriangleBar":"⧏","LeftTriangle":"⊲","LeftTriangleEqual":"⊴","LeftUpDownVector":"⥑","LeftUpTeeVector":"⥠","LeftUpVectorBar":"⥘","LeftUpVector":"↿","LeftVectorBar":"⥒","LeftVector":"↼","lEg":"⪋","leg":"⋚","leq":"≤","leqq":"≦","leqslant":"⩽","lescc":"⪨","les":"⩽","lesdot":"⩿","lesdoto":"⪁","lesdotor":"⪃","lesg":"⋚︀","lesges":"⪓","lessapprox":"⪅","lessdot":"⋖","lesseqgtr":"⋚","lesseqqgtr":"⪋","LessEqualGreater":"⋚","LessFullEqual":"≦","LessGreater":"≶","lessgtr":"≶","LessLess":"⪡","lesssim":"≲","LessSlantEqual":"⩽","LessTilde":"≲","lfisht":"⥼","lfloor":"⌊","Lfr":"𝔏","lfr":"𝔩","lg":"≶","lgE":"⪑","lHar":"⥢","lhard":"↽","lharu":"↼","lharul":"⥪","lhblk":"▄","LJcy":"Љ","ljcy":"љ","llarr":"⇇","ll":"≪","Ll":"⋘","llcorner":"⌞","Lleftarrow":"⇚","llhard":"⥫","lltri":"◺","Lmidot":"Ŀ","lmidot":"ŀ","lmoustache":"⎰","lmoust":"⎰","lnap":"⪉","lnapprox":"⪉","lne":"⪇","lnE":"≨","lneq":"⪇","lneqq":"≨","lnsim":"⋦","loang":"⟬","loarr":"⇽","lobrk":"⟦","longleftarrow":"⟵","LongLeftArrow":"⟵","Longleftarrow":"⟸","longleftrightarrow":"⟷","LongLeftRightArrow":"⟷","Longleftrightarrow":"⟺","longmapsto":"⟼","longrightarrow":"⟶","LongRightArrow":"⟶","Longrightarrow":"⟹","looparrowleft":"↫","looparrowright":"↬","lopar":"⦅","Lopf":"𝕃","lopf":"𝕝","loplus":"⨭","lotimes":"⨴","lowast":"∗","lowbar":"_","LowerLeftArrow":"↙","LowerRightArrow":"↘","loz":"◊","lozenge":"◊","lozf":"⧫","lpar":"(","lparlt":"⦓","lrarr":"⇆","lrcorner":"⌟","lrhar":"⇋","lrhard":"⥭","lrm":"‎","lrtri":"⊿","lsaquo":"‹","lscr":"𝓁","Lscr":"ℒ","lsh":"↰","Lsh":"↰","lsim":"≲","lsime":"⪍","lsimg":"⪏","lsqb":"[","lsquo":"‘","lsquor":"‚","Lstrok":"Ł","lstrok":"ł","ltcc":"⪦","ltcir":"⩹","lt":"<","LT":"<","Lt":"≪","ltdot":"⋖","lthree":"⋋","ltimes":"⋉","ltlarr":"⥶","ltquest":"⩻","ltri":"◃","ltrie":"⊴","ltrif":"◂","ltrPar":"⦖","lurdshar":"⥊","luruhar":"⥦","lvertneqq":"≨︀","lvnE":"≨︀","macr":"¯","male":"♂","malt":"✠","maltese":"✠","Map":"⤅","map":"↦","mapsto":"↦","mapstodown":"↧","mapstoleft":"↤","mapstoup":"↥","marker":"▮","mcomma":"⨩","Mcy":"М","mcy":"м","mdash":"—","mDDot":"∺","measuredangle":"∡","MediumSpace":" ","Mellintrf":"ℳ","Mfr":"𝔐","mfr":"𝔪","mho":"℧","micro":"µ","midast":"*","midcir":"⫰","mid":"∣","middot":"·","minusb":"⊟","minus":"−","minusd":"∸","minusdu":"⨪","MinusPlus":"∓","mlcp":"⫛","mldr":"…","mnplus":"∓","models":"⊧","Mopf":"𝕄","mopf":"𝕞","mp":"∓","mscr":"𝓂","Mscr":"ℳ","mstpos":"∾","Mu":"Μ","mu":"μ","multimap":"⊸","mumap":"⊸","nabla":"∇","Nacute":"Ń","nacute":"ń","nang":"∠⃒","nap":"≉","napE":"⩰̸","napid":"≋̸","napos":"ʼn","napprox":"≉","natural":"♮","naturals":"ℕ","natur":"♮","nbsp":" ","nbump":"≎̸","nbumpe":"≏̸","ncap":"⩃","Ncaron":"Ň","ncaron":"ň","Ncedil":"Ņ","ncedil":"ņ","ncong":"≇","ncongdot":"⩭̸","ncup":"⩂","Ncy":"Н","ncy":"н","ndash":"–","nearhk":"⤤","nearr":"↗","neArr":"⇗","nearrow":"↗","ne":"≠","nedot":"≐̸","NegativeMediumSpace":"​","NegativeThickSpace":"​","NegativeThinSpace":"​","NegativeVeryThinSpace":"​","nequiv":"≢","nesear":"⤨","nesim":"≂̸","NestedGreaterGreater":"≫","NestedLessLess":"≪","NewLine":"\\n","nexist":"∄","nexists":"∄","Nfr":"𝔑","nfr":"𝔫","ngE":"≧̸","nge":"≱","ngeq":"≱","ngeqq":"≧̸","ngeqslant":"⩾̸","nges":"⩾̸","nGg":"⋙̸","ngsim":"≵","nGt":"≫⃒","ngt":"≯","ngtr":"≯","nGtv":"≫̸","nharr":"↮","nhArr":"⇎","nhpar":"⫲","ni":"∋","nis":"⋼","nisd":"⋺","niv":"∋","NJcy":"Њ","njcy":"њ","nlarr":"↚","nlArr":"⇍","nldr":"‥","nlE":"≦̸","nle":"≰","nleftarrow":"↚","nLeftarrow":"⇍","nleftrightarrow":"↮","nLeftrightarrow":"⇎","nleq":"≰","nleqq":"≦̸","nleqslant":"⩽̸","nles":"⩽̸","nless":"≮","nLl":"⋘̸","nlsim":"≴","nLt":"≪⃒","nlt":"≮","nltri":"⋪","nltrie":"⋬","nLtv":"≪̸","nmid":"∤","NoBreak":"⁠","NonBreakingSpace":" ","nopf":"𝕟","Nopf":"ℕ","Not":"⫬","not":"¬","NotCongruent":"≢","NotCupCap":"≭","NotDoubleVerticalBar":"∦","NotElement":"∉","NotEqual":"≠","NotEqualTilde":"≂̸","NotExists":"∄","NotGreater":"≯","NotGreaterEqual":"≱","NotGreaterFullEqual":"≧̸","NotGreaterGreater":"≫̸","NotGreaterLess":"≹","NotGreaterSlantEqual":"⩾̸","NotGreaterTilde":"≵","NotHumpDownHump":"≎̸","NotHumpEqual":"≏̸","notin":"∉","notindot":"⋵̸","notinE":"⋹̸","notinva":"∉","notinvb":"⋷","notinvc":"⋶","NotLeftTriangleBar":"⧏̸","NotLeftTriangle":"⋪","NotLeftTriangleEqual":"⋬","NotLess":"≮","NotLessEqual":"≰","NotLessGreater":"≸","NotLessLess":"≪̸","NotLessSlantEqual":"⩽̸","NotLessTilde":"≴","NotNestedGreaterGreater":"⪢̸","NotNestedLessLess":"⪡̸","notni":"∌","notniva":"∌","notnivb":"⋾","notnivc":"⋽","NotPrecedes":"⊀","NotPrecedesEqual":"⪯̸","NotPrecedesSlantEqual":"⋠","NotReverseElement":"∌","NotRightTriangleBar":"⧐̸","NotRightTriangle":"⋫","NotRightTriangleEqual":"⋭","NotSquareSubset":"⊏̸","NotSquareSubsetEqual":"⋢","NotSquareSuperset":"⊐̸","NotSquareSupersetEqual":"⋣","NotSubset":"⊂⃒","NotSubsetEqual":"⊈","NotSucceeds":"⊁","NotSucceedsEqual":"⪰̸","NotSucceedsSlantEqual":"⋡","NotSucceedsTilde":"≿̸","NotSuperset":"⊃⃒","NotSupersetEqual":"⊉","NotTilde":"≁","NotTildeEqual":"≄","NotTildeFullEqual":"≇","NotTildeTilde":"≉","NotVerticalBar":"∤","nparallel":"∦","npar":"∦","nparsl":"⫽⃥","npart":"∂̸","npolint":"⨔","npr":"⊀","nprcue":"⋠","nprec":"⊀","npreceq":"⪯̸","npre":"⪯̸","nrarrc":"⤳̸","nrarr":"↛","nrArr":"⇏","nrarrw":"↝̸","nrightarrow":"↛","nRightarrow":"⇏","nrtri":"⋫","nrtrie":"⋭","nsc":"⊁","nsccue":"⋡","nsce":"⪰̸","Nscr":"𝒩","nscr":"𝓃","nshortmid":"∤","nshortparallel":"∦","nsim":"≁","nsime":"≄","nsimeq":"≄","nsmid":"∤","nspar":"∦","nsqsube":"⋢","nsqsupe":"⋣","nsub":"⊄","nsubE":"⫅̸","nsube":"⊈","nsubset":"⊂⃒","nsubseteq":"⊈","nsubseteqq":"⫅̸","nsucc":"⊁","nsucceq":"⪰̸","nsup":"⊅","nsupE":"⫆̸","nsupe":"⊉","nsupset":"⊃⃒","nsupseteq":"⊉","nsupseteqq":"⫆̸","ntgl":"≹","Ntilde":"Ñ","ntilde":"ñ","ntlg":"≸","ntriangleleft":"⋪","ntrianglelefteq":"⋬","ntriangleright":"⋫","ntrianglerighteq":"⋭","Nu":"Ν","nu":"ν","num":"#","numero":"№","numsp":" ","nvap":"≍⃒","nvdash":"⊬","nvDash":"⊭","nVdash":"⊮","nVDash":"⊯","nvge":"≥⃒","nvgt":">⃒","nvHarr":"⤄","nvinfin":"⧞","nvlArr":"⤂","nvle":"≤⃒","nvlt":"<⃒","nvltrie":"⊴⃒","nvrArr":"⤃","nvrtrie":"⊵⃒","nvsim":"∼⃒","nwarhk":"⤣","nwarr":"↖","nwArr":"⇖","nwarrow":"↖","nwnear":"⤧","Oacute":"Ó","oacute":"ó","oast":"⊛","Ocirc":"Ô","ocirc":"ô","ocir":"⊚","Ocy":"О","ocy":"о","odash":"⊝","Odblac":"Ő","odblac":"ő","odiv":"⨸","odot":"⊙","odsold":"⦼","OElig":"Œ","oelig":"œ","ofcir":"⦿","Ofr":"𝔒","ofr":"𝔬","ogon":"˛","Ograve":"Ò","ograve":"ò","ogt":"⧁","ohbar":"⦵","ohm":"Ω","oint":"∮","olarr":"↺","olcir":"⦾","olcross":"⦻","oline":"‾","olt":"⧀","Omacr":"Ō","omacr":"ō","Omega":"Ω","omega":"ω","Omicron":"Ο","omicron":"ο","omid":"⦶","ominus":"⊖","Oopf":"𝕆","oopf":"𝕠","opar":"⦷","OpenCurlyDoubleQuote":"“","OpenCurlyQuote":"‘","operp":"⦹","oplus":"⊕","orarr":"↻","Or":"⩔","or":"∨","ord":"⩝","order":"ℴ","orderof":"ℴ","ordf":"ª","ordm":"º","origof":"⊶","oror":"⩖","orslope":"⩗","orv":"⩛","oS":"Ⓢ","Oscr":"𝒪","oscr":"ℴ","Oslash":"Ø","oslash":"ø","osol":"⊘","Otilde":"Õ","otilde":"õ","otimesas":"⨶","Otimes":"⨷","otimes":"⊗","Ouml":"Ö","ouml":"ö","ovbar":"⌽","OverBar":"‾","OverBrace":"⏞","OverBracket":"⎴","OverParenthesis":"⏜","para":"¶","parallel":"∥","par":"∥","parsim":"⫳","parsl":"⫽","part":"∂","PartialD":"∂","Pcy":"П","pcy":"п","percnt":"%","period":".","permil":"‰","perp":"⊥","pertenk":"‱","Pfr":"𝔓","pfr":"𝔭","Phi":"Φ","phi":"φ","phiv":"ϕ","phmmat":"ℳ","phone":"☎","Pi":"Π","pi":"π","pitchfork":"⋔","piv":"ϖ","planck":"ℏ","planckh":"ℎ","plankv":"ℏ","plusacir":"⨣","plusb":"⊞","pluscir":"⨢","plus":"+","plusdo":"∔","plusdu":"⨥","pluse":"⩲","PlusMinus":"±","plusmn":"±","plussim":"⨦","plustwo":"⨧","pm":"±","Poincareplane":"ℌ","pointint":"⨕","popf":"𝕡","Popf":"ℙ","pound":"£","prap":"⪷","Pr":"⪻","pr":"≺","prcue":"≼","precapprox":"⪷","prec":"≺","preccurlyeq":"≼","Precedes":"≺","PrecedesEqual":"⪯","PrecedesSlantEqual":"≼","PrecedesTilde":"≾","preceq":"⪯","precnapprox":"⪹","precneqq":"⪵","precnsim":"⋨","pre":"⪯","prE":"⪳","precsim":"≾","prime":"′","Prime":"″","primes":"ℙ","prnap":"⪹","prnE":"⪵","prnsim":"⋨","prod":"∏","Product":"∏","profalar":"⌮","profline":"⌒","profsurf":"⌓","prop":"∝","Proportional":"∝","Proportion":"∷","propto":"∝","prsim":"≾","prurel":"⊰","Pscr":"𝒫","pscr":"𝓅","Psi":"Ψ","psi":"ψ","puncsp":" ","Qfr":"𝔔","qfr":"𝔮","qint":"⨌","qopf":"𝕢","Qopf":"ℚ","qprime":"⁗","Qscr":"𝒬","qscr":"𝓆","quaternions":"ℍ","quatint":"⨖","quest":"?","questeq":"≟","quot":"\\"","QUOT":"\\"","rAarr":"⇛","race":"∽̱","Racute":"Ŕ","racute":"ŕ","radic":"√","raemptyv":"⦳","rang":"⟩","Rang":"⟫","rangd":"⦒","range":"⦥","rangle":"⟩","raquo":"»","rarrap":"⥵","rarrb":"⇥","rarrbfs":"⤠","rarrc":"⤳","rarr":"→","Rarr":"↠","rArr":"⇒","rarrfs":"⤞","rarrhk":"↪","rarrlp":"↬","rarrpl":"⥅","rarrsim":"⥴","Rarrtl":"⤖","rarrtl":"↣","rarrw":"↝","ratail":"⤚","rAtail":"⤜","ratio":"∶","rationals":"ℚ","rbarr":"⤍","rBarr":"⤏","RBarr":"⤐","rbbrk":"❳","rbrace":"}","rbrack":"]","rbrke":"⦌","rbrksld":"⦎","rbrkslu":"⦐","Rcaron":"Ř","rcaron":"ř","Rcedil":"Ŗ","rcedil":"ŗ","rceil":"⌉","rcub":"}","Rcy":"Р","rcy":"р","rdca":"⤷","rdldhar":"⥩","rdquo":"”","rdquor":"”","rdsh":"↳","real":"ℜ","realine":"ℛ","realpart":"ℜ","reals":"ℝ","Re":"ℜ","rect":"▭","reg":"®","REG":"®","ReverseElement":"∋","ReverseEquilibrium":"⇋","ReverseUpEquilibrium":"⥯","rfisht":"⥽","rfloor":"⌋","rfr":"𝔯","Rfr":"ℜ","rHar":"⥤","rhard":"⇁","rharu":"⇀","rharul":"⥬","Rho":"Ρ","rho":"ρ","rhov":"ϱ","RightAngleBracket":"⟩","RightArrowBar":"⇥","rightarrow":"→","RightArrow":"→","Rightarrow":"⇒","RightArrowLeftArrow":"⇄","rightarrowtail":"↣","RightCeiling":"⌉","RightDoubleBracket":"⟧","RightDownTeeVector":"⥝","RightDownVectorBar":"⥕","RightDownVector":"⇂","RightFloor":"⌋","rightharpoondown":"⇁","rightharpoonup":"⇀","rightleftarrows":"⇄","rightleftharpoons":"⇌","rightrightarrows":"⇉","rightsquigarrow":"↝","RightTeeArrow":"↦","RightTee":"⊢","RightTeeVector":"⥛","rightthreetimes":"⋌","RightTriangleBar":"⧐","RightTriangle":"⊳","RightTriangleEqual":"⊵","RightUpDownVector":"⥏","RightUpTeeVector":"⥜","RightUpVectorBar":"⥔","RightUpVector":"↾","RightVectorBar":"⥓","RightVector":"⇀","ring":"˚","risingdotseq":"≓","rlarr":"⇄","rlhar":"⇌","rlm":"‏","rmoustache":"⎱","rmoust":"⎱","rnmid":"⫮","roang":"⟭","roarr":"⇾","robrk":"⟧","ropar":"⦆","ropf":"𝕣","Ropf":"ℝ","roplus":"⨮","rotimes":"⨵","RoundImplies":"⥰","rpar":")","rpargt":"⦔","rppolint":"⨒","rrarr":"⇉","Rrightarrow":"⇛","rsaquo":"›","rscr":"𝓇","Rscr":"ℛ","rsh":"↱","Rsh":"↱","rsqb":"]","rsquo":"’","rsquor":"’","rthree":"⋌","rtimes":"⋊","rtri":"▹","rtrie":"⊵","rtrif":"▸","rtriltri":"⧎","RuleDelayed":"⧴","ruluhar":"⥨","rx":"℞","Sacute":"Ś","sacute":"ś","sbquo":"‚","scap":"⪸","Scaron":"Š","scaron":"š","Sc":"⪼","sc":"≻","sccue":"≽","sce":"⪰","scE":"⪴","Scedil":"Ş","scedil":"ş","Scirc":"Ŝ","scirc":"ŝ","scnap":"⪺","scnE":"⪶","scnsim":"⋩","scpolint":"⨓","scsim":"≿","Scy":"С","scy":"с","sdotb":"⊡","sdot":"⋅","sdote":"⩦","searhk":"⤥","searr":"↘","seArr":"⇘","searrow":"↘","sect":"§","semi":";","seswar":"⤩","setminus":"∖","setmn":"∖","sext":"✶","Sfr":"𝔖","sfr":"𝔰","sfrown":"⌢","sharp":"♯","SHCHcy":"Щ","shchcy":"щ","SHcy":"Ш","shcy":"ш","ShortDownArrow":"↓","ShortLeftArrow":"←","shortmid":"∣","shortparallel":"∥","ShortRightArrow":"→","ShortUpArrow":"↑","shy":"­","Sigma":"Σ","sigma":"σ","sigmaf":"ς","sigmav":"ς","sim":"∼","simdot":"⩪","sime":"≃","simeq":"≃","simg":"⪞","simgE":"⪠","siml":"⪝","simlE":"⪟","simne":"≆","simplus":"⨤","simrarr":"⥲","slarr":"←","SmallCircle":"∘","smallsetminus":"∖","smashp":"⨳","smeparsl":"⧤","smid":"∣","smile":"⌣","smt":"⪪","smte":"⪬","smtes":"⪬︀","SOFTcy":"Ь","softcy":"ь","solbar":"⌿","solb":"⧄","sol":"/","Sopf":"𝕊","sopf":"𝕤","spades":"♠","spadesuit":"♠","spar":"∥","sqcap":"⊓","sqcaps":"⊓︀","sqcup":"⊔","sqcups":"⊔︀","Sqrt":"√","sqsub":"⊏","sqsube":"⊑","sqsubset":"⊏","sqsubseteq":"⊑","sqsup":"⊐","sqsupe":"⊒","sqsupset":"⊐","sqsupseteq":"⊒","square":"□","Square":"□","SquareIntersection":"⊓","SquareSubset":"⊏","SquareSubsetEqual":"⊑","SquareSuperset":"⊐","SquareSupersetEqual":"⊒","SquareUnion":"⊔","squarf":"▪","squ":"□","squf":"▪","srarr":"→","Sscr":"𝒮","sscr":"𝓈","ssetmn":"∖","ssmile":"⌣","sstarf":"⋆","Star":"⋆","star":"☆","starf":"★","straightepsilon":"ϵ","straightphi":"ϕ","strns":"¯","sub":"⊂","Sub":"⋐","subdot":"⪽","subE":"⫅","sube":"⊆","subedot":"⫃","submult":"⫁","subnE":"⫋","subne":"⊊","subplus":"⪿","subrarr":"⥹","subset":"⊂","Subset":"⋐","subseteq":"⊆","subseteqq":"⫅","SubsetEqual":"⊆","subsetneq":"⊊","subsetneqq":"⫋","subsim":"⫇","subsub":"⫕","subsup":"⫓","succapprox":"⪸","succ":"≻","succcurlyeq":"≽","Succeeds":"≻","SucceedsEqual":"⪰","SucceedsSlantEqual":"≽","SucceedsTilde":"≿","succeq":"⪰","succnapprox":"⪺","succneqq":"⪶","succnsim":"⋩","succsim":"≿","SuchThat":"∋","sum":"∑","Sum":"∑","sung":"♪","sup1":"¹","sup2":"²","sup3":"³","sup":"⊃","Sup":"⋑","supdot":"⪾","supdsub":"⫘","supE":"⫆","supe":"⊇","supedot":"⫄","Superset":"⊃","SupersetEqual":"⊇","suphsol":"⟉","suphsub":"⫗","suplarr":"⥻","supmult":"⫂","supnE":"⫌","supne":"⊋","supplus":"⫀","supset":"⊃","Supset":"⋑","supseteq":"⊇","supseteqq":"⫆","supsetneq":"⊋","supsetneqq":"⫌","supsim":"⫈","supsub":"⫔","supsup":"⫖","swarhk":"⤦","swarr":"↙","swArr":"⇙","swarrow":"↙","swnwar":"⤪","szlig":"ß","Tab":"\\t","target":"⌖","Tau":"Τ","tau":"τ","tbrk":"⎴","Tcaron":"Ť","tcaron":"ť","Tcedil":"Ţ","tcedil":"ţ","Tcy":"Т","tcy":"т","tdot":"⃛","telrec":"⌕","Tfr":"𝔗","tfr":"𝔱","there4":"∴","therefore":"∴","Therefore":"∴","Theta":"Θ","theta":"θ","thetasym":"ϑ","thetav":"ϑ","thickapprox":"≈","thicksim":"∼","ThickSpace":"  ","ThinSpace":" ","thinsp":" ","thkap":"≈","thksim":"∼","THORN":"Þ","thorn":"þ","tilde":"˜","Tilde":"∼","TildeEqual":"≃","TildeFullEqual":"≅","TildeTilde":"≈","timesbar":"⨱","timesb":"⊠","times":"×","timesd":"⨰","tint":"∭","toea":"⤨","topbot":"⌶","topcir":"⫱","top":"⊤","Topf":"𝕋","topf":"𝕥","topfork":"⫚","tosa":"⤩","tprime":"‴","trade":"™","TRADE":"™","triangle":"▵","triangledown":"▿","triangleleft":"◃","trianglelefteq":"⊴","triangleq":"≜","triangleright":"▹","trianglerighteq":"⊵","tridot":"◬","trie":"≜","triminus":"⨺","TripleDot":"⃛","triplus":"⨹","trisb":"⧍","tritime":"⨻","trpezium":"⏢","Tscr":"𝒯","tscr":"𝓉","TScy":"Ц","tscy":"ц","TSHcy":"Ћ","tshcy":"ћ","Tstrok":"Ŧ","tstrok":"ŧ","twixt":"≬","twoheadleftarrow":"↞","twoheadrightarrow":"↠","Uacute":"Ú","uacute":"ú","uarr":"↑","Uarr":"↟","uArr":"⇑","Uarrocir":"⥉","Ubrcy":"Ў","ubrcy":"ў","Ubreve":"Ŭ","ubreve":"ŭ","Ucirc":"Û","ucirc":"û","Ucy":"У","ucy":"у","udarr":"⇅","Udblac":"Ű","udblac":"ű","udhar":"⥮","ufisht":"⥾","Ufr":"𝔘","ufr":"𝔲","Ugrave":"Ù","ugrave":"ù","uHar":"⥣","uharl":"↿","uharr":"↾","uhblk":"▀","ulcorn":"⌜","ulcorner":"⌜","ulcrop":"⌏","ultri":"◸","Umacr":"Ū","umacr":"ū","uml":"¨","UnderBar":"_","UnderBrace":"⏟","UnderBracket":"⎵","UnderParenthesis":"⏝","Union":"⋃","UnionPlus":"⊎","Uogon":"Ų","uogon":"ų","Uopf":"𝕌","uopf":"𝕦","UpArrowBar":"⤒","uparrow":"↑","UpArrow":"↑","Uparrow":"⇑","UpArrowDownArrow":"⇅","updownarrow":"↕","UpDownArrow":"↕","Updownarrow":"⇕","UpEquilibrium":"⥮","upharpoonleft":"↿","upharpoonright":"↾","uplus":"⊎","UpperLeftArrow":"↖","UpperRightArrow":"↗","upsi":"υ","Upsi":"ϒ","upsih":"ϒ","Upsilon":"Υ","upsilon":"υ","UpTeeArrow":"↥","UpTee":"⊥","upuparrows":"⇈","urcorn":"⌝","urcorner":"⌝","urcrop":"⌎","Uring":"Ů","uring":"ů","urtri":"◹","Uscr":"𝒰","uscr":"𝓊","utdot":"⋰","Utilde":"Ũ","utilde":"ũ","utri":"▵","utrif":"▴","uuarr":"⇈","Uuml":"Ü","uuml":"ü","uwangle":"⦧","vangrt":"⦜","varepsilon":"ϵ","varkappa":"ϰ","varnothing":"∅","varphi":"ϕ","varpi":"ϖ","varpropto":"∝","varr":"↕","vArr":"⇕","varrho":"ϱ","varsigma":"ς","varsubsetneq":"⊊︀","varsubsetneqq":"⫋︀","varsupsetneq":"⊋︀","varsupsetneqq":"⫌︀","vartheta":"ϑ","vartriangleleft":"⊲","vartriangleright":"⊳","vBar":"⫨","Vbar":"⫫","vBarv":"⫩","Vcy":"В","vcy":"в","vdash":"⊢","vDash":"⊨","Vdash":"⊩","VDash":"⊫","Vdashl":"⫦","veebar":"⊻","vee":"∨","Vee":"⋁","veeeq":"≚","vellip":"⋮","verbar":"|","Verbar":"‖","vert":"|","Vert":"‖","VerticalBar":"∣","VerticalLine":"|","VerticalSeparator":"❘","VerticalTilde":"≀","VeryThinSpace":" ","Vfr":"𝔙","vfr":"𝔳","vltri":"⊲","vnsub":"⊂⃒","vnsup":"⊃⃒","Vopf":"𝕍","vopf":"𝕧","vprop":"∝","vrtri":"⊳","Vscr":"𝒱","vscr":"𝓋","vsubnE":"⫋︀","vsubne":"⊊︀","vsupnE":"⫌︀","vsupne":"⊋︀","Vvdash":"⊪","vzigzag":"⦚","Wcirc":"Ŵ","wcirc":"ŵ","wedbar":"⩟","wedge":"∧","Wedge":"⋀","wedgeq":"≙","weierp":"℘","Wfr":"𝔚","wfr":"𝔴","Wopf":"𝕎","wopf":"𝕨","wp":"℘","wr":"≀","wreath":"≀","Wscr":"𝒲","wscr":"𝓌","xcap":"⋂","xcirc":"◯","xcup":"⋃","xdtri":"▽","Xfr":"𝔛","xfr":"𝔵","xharr":"⟷","xhArr":"⟺","Xi":"Ξ","xi":"ξ","xlarr":"⟵","xlArr":"⟸","xmap":"⟼","xnis":"⋻","xodot":"⨀","Xopf":"𝕏","xopf":"𝕩","xoplus":"⨁","xotime":"⨂","xrarr":"⟶","xrArr":"⟹","Xscr":"𝒳","xscr":"𝓍","xsqcup":"⨆","xuplus":"⨄","xutri":"△","xvee":"⋁","xwedge":"⋀","Yacute":"Ý","yacute":"ý","YAcy":"Я","yacy":"я","Ycirc":"Ŷ","ycirc":"ŷ","Ycy":"Ы","ycy":"ы","yen":"¥","Yfr":"𝔜","yfr":"𝔶","YIcy":"Ї","yicy":"ї","Yopf":"𝕐","yopf":"𝕪","Yscr":"𝒴","yscr":"𝓎","YUcy":"Ю","yucy":"ю","yuml":"ÿ","Yuml":"Ÿ","Zacute":"Ź","zacute":"ź","Zcaron":"Ž","zcaron":"ž","Zcy":"З","zcy":"з","Zdot":"Ż","zdot":"ż","zeetrf":"ℨ","ZeroWidthSpace":"​","Zeta":"Ζ","zeta":"ζ","zfr":"𝔷","Zfr":"ℨ","ZHcy":"Ж","zhcy":"ж","zigrarr":"⇝","zopf":"𝕫","Zopf":"ℤ","Zscr":"𝒵","zscr":"𝓏","zwj":"‍","zwnj":"‌"}')},function(e,t,r){"use strict";var n={};function s(e,t,r){var o,i,a,u,c,l="";for("string"!=typeof t&&(r=t,t=s.defaultChars),void 0===r&&(r=!0),c=function(e){var t,r,s=n[e];if(s)return s;for(s=n[e]=[],t=0;t<128;t++)r=String.fromCharCode(t),/^[0-9a-z]$/i.test(r)?s.push(r):s.push("%"+("0"+t.toString(16).toUpperCase()).slice(-2));for(t=0;t=55296&&a<=57343){if(a>=55296&&a<=56319&&o+1=56320&&u<=57343){l+=encodeURIComponent(e[o]+e[o+1]),o++;continue}l+="%EF%BF%BD"}else l+=encodeURIComponent(e[o]);return l}s.defaultChars=";/?:@&=+$,-_.!~*'()#",s.componentChars="-_.!~*'()",e.exports=s},function(e,t,r){"use strict";var n={};function s(e,t){var r;return"string"!=typeof t&&(t=s.defaultChars),r=function(e){var t,r,s=n[e];if(s)return s;for(s=n[e]=[],t=0;t<128;t++)r=String.fromCharCode(t),s.push(r);for(t=0;t=55296&&u<=57343?"���":String.fromCharCode(u),t+=6):240==(248&s)&&t+91114111?c+="����":(u-=65536,c+=String.fromCharCode(55296+(u>>10),56320+(1023&u))),t+=9):c+="�";return c}))}s.defaultChars=";/?:@&=+$,#",s.componentChars="",e.exports=s},function(e,t,r){"use strict";e.exports=function(e){var t="";return t+=e.protocol||"",t+=e.slashes?"//":"",t+=e.auth?e.auth+"@":"",e.hostname&&-1!==e.hostname.indexOf(":")?t+="["+e.hostname+"]":t+=e.hostname||"",t+=e.port?":"+e.port:"",t+=e.pathname||"",t+=e.search||"",t+=e.hash||""}},function(e,t,r){"use strict";function n(){this.protocol=null,this.slashes=null,this.auth=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.pathname=null}var s=/^([a-z0-9.+-]+:)/i,o=/:[0-9]*$/,i=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,a=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),u=["'"].concat(a),c=["%","/","?",";","#"].concat(u),l=["/","?","#"],p=/^[+a-z0-9A-Z_-]{0,63}$/,h=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,f={javascript:!0,"javascript:":!0},d={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0};n.prototype.parse=function(e,t){var r,n,o,a,u,m=e;if(m=m.trim(),!t&&1===e.split("#").length){var g=i.exec(m);if(g)return this.pathname=g[1],g[2]&&(this.search=g[2]),this}var _=s.exec(m);if(_&&(o=(_=_[0]).toLowerCase(),this.protocol=_,m=m.substr(_.length)),(t||_||m.match(/^\/\/[^@\/]+@[^@\/]+/))&&(!(u="//"===m.substr(0,2))||_&&f[_]||(m=m.substr(2),this.slashes=!0)),!f[_]&&(u||_&&!d[_])){var b,k,C=-1;for(r=0;r127?x+="x":x+=y[F];if(!x.match(p)){var w=A.slice(0,r),q=A.slice(r+1),S=y.match(h);S&&(w.push(S[1]),q.unshift(S[2])),q.length&&(m=q.join(".")+m),this.hostname=w.join(".");break}}}}this.hostname.length>255&&(this.hostname=""),D&&(this.hostname=this.hostname.substr(1,this.hostname.length-2))}var L=m.indexOf("#");-1!==L&&(this.hash=m.substr(L),m=m.slice(0,L));var z=m.indexOf("?");return-1!==z&&(this.search=m.substr(z),m=m.slice(0,z)),m&&(this.pathname=m),d[o]&&this.hostname&&!this.pathname&&(this.pathname=""),this},n.prototype.parseHost=function(e){var t=o.exec(e);t&&(":"!==(t=t[0])&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)},e.exports=function(e,t){if(e&&e instanceof n)return e;var r=new n;return r.parse(e,t),r}},function(e,t,r){"use strict";t.Any=r(18),t.Cc=r(19),t.Cf=r(20),t.P=r(4),t.Z=r(21)},function(e,t){e.exports=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/},function(e,t){e.exports=/[\0-\x1F\x7F-\x9F]/},function(e,t){e.exports=/[\xAD\u0600-\u0605\u061C\u06DD\u070F\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804\uDCBD|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/},function(e,t){e.exports=/[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/},function(e,t,r){"use strict";t.parseLinkLabel=r(23),t.parseLinkDestination=r(24),t.parseLinkTitle=r(25)},function(e,t,r){"use strict";e.exports=function(e,t,r){var n,s,o,i,a=-1,u=e.posMax,c=e.pos;for(e.pos=t+1,n=1;e.pos32)return a;if(41===s){if(0===o)break;o--}t++}return i===t||0!==o||(a.str=n(e.slice(i,t)),a.lines=0,a.pos=t,a.ok=!0),a}},function(e,t,r){"use strict";var n=r(0).unescapeAll;e.exports=function(e,t,r){var s,o,i=0,a=t,u={ok:!1,pos:0,lines:0,str:""};if(t>=r)return u;if(34!==(o=e.charCodeAt(t))&&39!==o&&40!==o)return u;for(t++,40===o&&(o=41);t"+o(e[t].content)+""},i.code_block=function(e,t,r,n,s){var i=e[t];return""+o(e[t].content)+"\n"},i.fence=function(e,t,r,n,i){var a,u,c,l,p,h=e[t],f=h.info?s(h.info).trim():"",d="",m="";return f&&(d=(c=f.split(/(\s+)/g))[0],m=c.slice(2).join("")),0===(a=r.highlight&&r.highlight(h.content,d,m)||o(h.content)).indexOf(""+a+"\n"):"
"+a+"
\n"},i.image=function(e,t,r,n,s){var o=e[t];return o.attrs[o.attrIndex("alt")][1]=s.renderInlineAsText(o.children,r,n),s.renderToken(e,t,r)},i.hardbreak=function(e,t,r){return r.xhtmlOut?"
\n":"
\n"},i.softbreak=function(e,t,r){return r.breaks?r.xhtmlOut?"
\n":"
\n":"\n"},i.text=function(e,t){return o(e[t].content)},i.html_block=function(e,t){return e[t].content},i.html_inline=function(e,t){return e[t].content},a.prototype.renderAttrs=function(e){var t,r,n;if(!e.attrs)return"";for(n="",t=0,r=e.attrs.length;t\n":">")},a.prototype.renderInline=function(e,t,r){for(var n,s="",o=this.rules,i=0,a=e.length;i/i.test(e)}e.exports=function(e){var t,r,o,i,a,u,c,l,p,h,f,d,m,g,_,b,k,C,v=e.tokens;if(e.md.options.linkify)for(r=0,o=v.length;r=0;t--)if("link_close"!==(u=i[t]).type){if("html_inline"===u.type&&(C=u.content,/^\s]/i.test(C)&&m>0&&m--,s(u.content)&&m++),!(m>0)&&"text"===u.type&&e.md.linkify.test(u.content)){for(p=u.content,k=e.md.linkify.match(p),c=[],d=u.level,f=0,l=0;lf&&((a=new e.Token("text","",0)).content=p.slice(f,h),a.level=d,c.push(a)),(a=new e.Token("link_open","a",1)).attrs=[["href",_]],a.level=d++,a.markup="linkify",a.info="auto",c.push(a),(a=new e.Token("text","",0)).content=b,a.level=d,c.push(a),(a=new e.Token("link_close","a",-1)).level=--d,a.markup="linkify",a.info="auto",c.push(a),f=k[l].lastIndex);f=0;t--)"text"!==(r=e[t]).type||n||(r.content=r.content.replace(o,a)),"link_open"===r.type&&"auto"===r.info&&n--,"link_close"===r.type&&"auto"===r.info&&n++}function c(e){var t,r,s=0;for(t=e.length-1;t>=0;t--)"text"!==(r=e[t]).type||s||n.test(r.content)&&(r.content=r.content.replace(/\+-/g,"±").replace(/\.{2,}/g,"…").replace(/([?!])…/g,"$1..").replace(/([?!]){4,}/g,"$1$1$1").replace(/,{2,}/g,",").replace(/(^|[^-])---(?=[^-]|$)/gm,"$1—").replace(/(^|\s)--(?=\s|$)/gm,"$1–").replace(/(^|[^-\s])--(?=[^-\s]|$)/gm,"$1–")),"link_open"===r.type&&"auto"===r.info&&s--,"link_close"===r.type&&"auto"===r.info&&s++}e.exports=function(e){var t;if(e.md.options.typographer)for(t=e.tokens.length-1;t>=0;t--)"inline"===e.tokens[t].type&&(s.test(e.tokens[t].content)&&u(e.tokens[t].children),n.test(e.tokens[t].content)&&c(e.tokens[t].children))}},function(e,t,r){"use strict";var n=r(0).isWhiteSpace,s=r(0).isPunctChar,o=r(0).isMdAsciiPunct,i=/['"]/,a=/['"]/g;function u(e,t,r){return e.substr(0,t)+r+e.substr(t+1)}function c(e,t){var r,i,c,l,p,h,f,d,m,g,_,b,k,C,v,D,A,y,x,F,E;for(x=[],r=0;r=0&&!(x[A].level<=f);A--);if(x.length=A+1,"text"===i.type){p=0,h=(c=i.content).length;e:for(;p=0)m=c.charCodeAt(l.index-1);else for(A=r-1;A>=0&&("softbreak"!==e[A].type&&"hardbreak"!==e[A].type);A--)if(e[A].content){m=e[A].content.charCodeAt(e[A].content.length-1);break}if(g=32,p=48&&m<=57&&(D=v=!1),v&&D&&(v=_,D=b),v||D){if(D)for(A=x.length-1;A>=0&&(d=x[A],!(x[A].level=0;t--)"inline"===e.tokens[t].type&&i.test(e.tokens[t].content)&&c(e.tokens[t].children,e)}},function(e,t,r){"use strict";var n=r(2);function s(e,t,r){this.src=e,this.env=r,this.tokens=[],this.inlineMode=!1,this.md=t}s.prototype.Token=n,e.exports=s},function(e,t,r){"use strict";var n=r(1),s=[["table",r(36),["paragraph","reference"]],["code",r(37)],["fence",r(38),["paragraph","reference","blockquote","list"]],["blockquote",r(39),["paragraph","reference","blockquote","list"]],["hr",r(40),["paragraph","reference","blockquote","list"]],["list",r(41),["paragraph","reference","blockquote"]],["reference",r(42)],["heading",r(43),["paragraph","reference","blockquote"]],["lheading",r(44)],["html_block",r(45),["paragraph","reference","blockquote"]],["paragraph",r(47)]];function o(){this.ruler=new n;for(var e=0;e=r))&&!(e.sCount[i]=u){e.line=r;break}for(n=0;nr)return!1;if(h=t+1,e.sCount[h]=4)return!1;if((c=e.bMarks[h]+e.tShift[h])>=e.eMarks[h])return!1;if(124!==(a=e.src.charCodeAt(c++))&&45!==a&&58!==a)return!1;for(;c=4)return!1;if((f=o(u)).length&&""===f[0]&&f.shift(),f.length&&""===f[f.length-1]&&f.pop(),0===(d=f.length)||d!==g.length)return!1;if(i)return!0;for(C=e.parentType,e.parentType="table",D=e.md.block.ruler.getRules("blockquote"),(m=e.push("table_open","table",1)).map=b=[t,0],(m=e.push("thead_open","thead",1)).map=[t,t+1],(m=e.push("tr_open","tr",1)).map=[t,t+1],l=0;l=4)break;for((f=o(u)).length&&""===f[0]&&f.shift(),f.length&&""===f[f.length-1]&&f.pop(),h===t+2&&((m=e.push("tbody_open","tbody",1)).map=k=[t+2,0]),(m=e.push("tr_open","tr",1)).map=[h,h+1],l=0;l=4))break;s=++n}return e.line=s,(o=e.push("code_block","code",0)).content=e.getLines(t,s,4+e.blkIndent,!0),o.map=[t,e.line],!0}},function(e,t,r){"use strict";e.exports=function(e,t,r,n){var s,o,i,a,u,c,l,p=!1,h=e.bMarks[t]+e.tShift[t],f=e.eMarks[t];if(e.sCount[t]-e.blkIndent>=4)return!1;if(h+3>f)return!1;if(126!==(s=e.src.charCodeAt(h))&&96!==s)return!1;if(u=h,(o=(h=e.skipChars(h,s))-u)<3)return!1;if(l=e.src.slice(u,h),i=e.src.slice(h,f),96===s&&i.indexOf(String.fromCharCode(s))>=0)return!1;if(n)return!0;for(a=t;!(++a>=r)&&!((h=u=e.bMarks[a]+e.tShift[a])<(f=e.eMarks[a])&&e.sCount[a]=4||(h=e.skipChars(h,s))-u=4)return!1;if(62!==e.src.charCodeAt(F++))return!1;if(s)return!0;for(u=f=e.sCount[t]+1,32===e.src.charCodeAt(F)?(F++,u++,f++,o=!1,C=!0):9===e.src.charCodeAt(F)?(C=!0,(e.bsCount[t]+f)%4==3?(F++,u++,f++,o=!1):o=!0):C=!1,d=[e.bMarks[t]],e.bMarks[t]=F;F=E,b=[e.sCount[t]],e.sCount[t]=f-u,k=[e.tShift[t]],e.tShift[t]=F-e.bMarks[t],D=e.md.block.ruler.getRules("blockquote"),_=e.parentType,e.parentType="blockquote",h=t+1;h=(E=e.eMarks[h])));h++)if(62!==e.src.charCodeAt(F++)||y){if(l)break;for(v=!1,a=0,c=D.length;a=E,m.push(e.bsCount[h]),e.bsCount[h]=e.sCount[h]+1+(C?1:0),b.push(e.sCount[h]),e.sCount[h]=f-u,k.push(e.tShift[h]),e.tShift[h]=F-e.bMarks[h]}for(g=e.blkIndent,e.blkIndent=0,(A=e.push("blockquote_open","blockquote",1)).markup=">",A.map=p=[t,0],e.md.block.tokenize(e,t,h),(A=e.push("blockquote_close","blockquote",-1)).markup=">",e.lineMax=x,e.parentType=_,p[1]=e.line,a=0;a=4)return!1;if(42!==(o=e.src.charCodeAt(c++))&&45!==o&&95!==o)return!1;for(i=1;c=i)return-1;if((r=e.src.charCodeAt(o++))<48||r>57)return-1;for(;;){if(o>=i)return-1;if(!((r=e.src.charCodeAt(o++))>=48&&r<=57)){if(41===r||46===r)break;return-1}if(o-s>=10)return-1}return o=4)return!1;if(e.listIndent>=0&&e.sCount[t]-e.listIndent>=4&&e.sCount[t]=e.blkIndent&&(B=!0),(w=o(e,t))>=0){if(h=!0,S=e.bMarks[t]+e.tShift[t],b=Number(e.src.substr(S,w-S-1)),B&&1!==b)return!1}else{if(!((w=s(e,t))>=0))return!1;h=!1}if(B&&e.skipSpaces(w)>=e.eMarks[t])return!1;if(_=e.src.charCodeAt(w-1),n)return!0;for(g=e.tokens.length,h?(T=e.push("ordered_list_open","ol",1),1!==b&&(T.attrs=[["start",b]])):T=e.push("bullet_list_open","ul",1),T.map=m=[t,0],T.markup=String.fromCharCode(_),C=t,q=!1,z=e.md.block.ruler.getRules("list"),A=e.parentType,e.parentType="list";C=k?1:v-p)>4&&(l=1),c=p+l,(T=e.push("list_item_open","li",1)).markup=String.fromCharCode(_),T.map=f=[t,0],F=e.tight,x=e.tShift[t],y=e.sCount[t],D=e.listIndent,e.listIndent=e.blkIndent,e.blkIndent=c,e.tight=!0,e.tShift[t]=a-e.bMarks[t],e.sCount[t]=v,a>=k&&e.isEmpty(t+1)?e.line=Math.min(e.line+2,r):e.md.block.tokenize(e,t,r,!0),e.tight&&!q||(I=!1),q=e.line-t>1&&e.isEmpty(e.line-1),e.blkIndent=e.listIndent,e.listIndent=D,e.tShift[t]=x,e.sCount[t]=y,e.tight=F,(T=e.push("list_item_close","li",-1)).markup=String.fromCharCode(_),C=t=e.line,f[1]=C,a=e.bMarks[t],C>=r)break;if(e.sCount[C]=4)break;for(L=!1,u=0,d=z.length;u=4)return!1;if(91!==e.src.charCodeAt(A))return!1;for(;++A3||e.sCount[x]<0)){for(k=!1,p=0,h=C.length;p=4)return!1;if(35!==(o=e.src.charCodeAt(c))||c>=l)return!1;for(i=1,o=e.src.charCodeAt(++c);35===o&&c6||cc&&n(e.src.charCodeAt(a-1))&&(l=a),e.line=t+1,(u=e.push("heading_open","h"+String(i),1)).markup="########".slice(0,i),u.map=[t,e.line],(u=e.push("inline","",0)).content=e.src.slice(c,l).trim(),u.map=[t,e.line],u.children=[],(u=e.push("heading_close","h"+String(i),-1)).markup="########".slice(0,i)),!0)}},function(e,t,r){"use strict";e.exports=function(e,t,r){var n,s,o,i,a,u,c,l,p,h,f=t+1,d=e.md.block.ruler.getRules("paragraph");if(e.sCount[t]-e.blkIndent>=4)return!1;for(h=e.parentType,e.parentType="paragraph";f3)){if(e.sCount[f]>=e.blkIndent&&(u=e.bMarks[f]+e.tShift[f])<(c=e.eMarks[f])&&(45===(p=e.src.charCodeAt(u))||61===p)&&(u=e.skipChars(u,p),(u=e.skipSpaces(u))>=c)){l=61===p?1:2;break}if(!(e.sCount[f]<0)){for(s=!1,o=0,i=d.length;o|$))/i,/<\/(script|pre|style)>/i,!0],[/^/,!0],[/^<\?/,/\?>/,!0],[/^/,!0],[/^/,!0],[new RegExp("^|$))","i"),/^$/,!0],[new RegExp(s.source+"\\s*$"),/^$/,!1]];e.exports=function(e,t,r,n){var s,i,a,u,c=e.bMarks[t]+e.tShift[t],l=e.eMarks[t];if(e.sCount[t]-e.blkIndent>=4)return!1;if(!e.md.options.html)return!1;if(60!==e.src.charCodeAt(c))return!1;for(u=e.src.slice(c,l),s=0;s3||e.sCount[u]<0)){for(n=!1,s=0,o=c.length;s0&&this.level++,this.tokens.push(s),s},o.prototype.isEmpty=function(e){return this.bMarks[e]+this.tShift[e]>=this.eMarks[e]},o.prototype.skipEmptyLines=function(e){for(var t=this.lineMax;et;)if(!s(this.src.charCodeAt(--e)))return e+1;return e},o.prototype.skipChars=function(e,t){for(var r=this.src.length;er;)if(t!==this.src.charCodeAt(--e))return e+1;return e},o.prototype.getLines=function(e,t,r,n){var o,i,a,u,c,l,p,h=e;if(e>=t)return"";for(l=new Array(t-e),o=0;hr?new Array(i-r+1).join(" ")+this.src.slice(u,c):this.src.slice(u,c)}return l.join("")},o.prototype.Token=n,e.exports=o},function(e,t,r){"use strict";var n=r(1),s=[["text",r(50)],["newline",r(51)],["escape",r(52)],["backticks",r(53)],["strikethrough",r(7).tokenize],["emphasis",r(8).tokenize],["link",r(54)],["image",r(55)],["autolink",r(56)],["html_inline",r(57)],["entity",r(58)]],o=[["balance_pairs",r(59)],["strikethrough",r(7).postProcess],["emphasis",r(8).postProcess],["text_collapse",r(60)]];function i(){var e;for(this.ruler=new n,e=0;e=o)break}else e.pending+=e.src[e.pos++]}e.pending&&e.pushPending()},i.prototype.parse=function(e,t,r,n){var s,o,i,a=new this.State(e,t,r,n);for(this.tokenize(a),i=(o=this.ruler2.getRules("")).length,s=0;s=0&&32===e.pending.charCodeAt(r)?r>=1&&32===e.pending.charCodeAt(r-1)?(e.pending=e.pending.replace(/ +$/,""),e.push("hardbreak","br",0)):(e.pending=e.pending.slice(0,-1),e.push("softbreak","br",0)):e.push("softbreak","br",0)),o++;o?@[]^_`{|}~-".split("").forEach((function(e){s[e.charCodeAt(0)]=1})),e.exports=function(e,t){var r,o=e.pos,i=e.posMax;if(92!==e.src.charCodeAt(o))return!1;if(++o=m)return!1;if(g=c,(l=e.md.helpers.parseLinkDestination(e.src,c,e.posMax)).ok){for(h=e.md.normalizeLink(l.str),e.md.validateLink(h)?c=l.pos:h="",g=c;c=m||41!==e.src.charCodeAt(c))&&(_=!0),c++}if(_){if(void 0===e.env.references)return!1;if(c=0?i=e.src.slice(g,c++):c=a+1):c=a+1,i||(i=e.src.slice(u,a)),!(p=e.env.references[n(i)]))return e.pos=d,!1;h=p.href,f=p.title}return t||(e.pos=u,e.posMax=a,e.push("link_open","a",1).attrs=r=[["href",h]],f&&r.push(["title",f]),e.md.inline.tokenize(e),e.push("link_close","a",-1)),e.pos=c,e.posMax=m,!0}},function(e,t,r){"use strict";var n=r(0).normalizeReference,s=r(0).isSpace;e.exports=function(e,t){var r,o,i,a,u,c,l,p,h,f,d,m,g,_="",b=e.pos,k=e.posMax;if(33!==e.src.charCodeAt(e.pos))return!1;if(91!==e.src.charCodeAt(e.pos+1))return!1;if(c=e.pos+2,(u=e.md.helpers.parseLinkLabel(e,e.pos+1,!1))<0)return!1;if((l=u+1)=k)return!1;for(g=l,(h=e.md.helpers.parseLinkDestination(e.src,l,e.posMax)).ok&&(_=e.md.normalizeLink(h.str),e.md.validateLink(_)?l=h.pos:_=""),g=l;l=k||41!==e.src.charCodeAt(l))return e.pos=b,!1;l++}else{if(void 0===e.env.references)return!1;if(l=0?a=e.src.slice(g,l++):l=u+1):l=u+1,a||(a=e.src.slice(c,u)),!(p=e.env.references[n(a)]))return e.pos=b,!1;_=p.href,f=p.title}return t||(i=e.src.slice(c,u),e.md.inline.parse(i,e.md,e.env,m=[]),(d=e.push("image","img",0)).attrs=r=[["src",_],["alt",""]],d.children=m,d.content=i,f&&r.push(["title",f])),e.pos=l,e.posMax=k,!0}},function(e,t,r){"use strict";var n=/^([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/,s=/^([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)$/;e.exports=function(e,t){var r,o,i,a,u,c,l=e.pos;if(60!==e.src.charCodeAt(l))return!1;for(u=e.pos,c=e.posMax;;){if(++l>=c)return!1;if(60===(a=e.src.charCodeAt(l)))return!1;if(62===a)break}return r=e.src.slice(u+1,l),s.test(r)?(o=e.md.normalizeLink(r),!!e.md.validateLink(o)&&(t||((i=e.push("link_open","a",1)).attrs=[["href",o]],i.markup="autolink",i.info="auto",(i=e.push("text","",0)).content=e.md.normalizeLinkText(r),(i=e.push("link_close","a",-1)).markup="autolink",i.info="auto"),e.pos+=r.length+2,!0)):!!n.test(r)&&(o=e.md.normalizeLink("mailto:"+r),!!e.md.validateLink(o)&&(t||((i=e.push("link_open","a",1)).attrs=[["href",o]],i.markup="autolink",i.info="auto",(i=e.push("text","",0)).content=e.md.normalizeLinkText(r),(i=e.push("link_close","a",-1)).markup="autolink",i.info="auto"),e.pos+=r.length+2,!0))}},function(e,t,r){"use strict";var n=r(6).HTML_TAG_RE;e.exports=function(e,t){var r,s,o,i=e.pos;return!!e.md.options.html&&(o=e.posMax,!(60!==e.src.charCodeAt(i)||i+2>=o)&&(!(33!==(r=e.src.charCodeAt(i+1))&&63!==r&&47!==r&&!function(e){var t=32|e;return t>=97&&t<=122}(r))&&(!!(s=e.src.slice(i).match(n))&&(t||(e.push("html_inline","",0).content=e.src.slice(i,i+s[0].length)),e.pos+=s[0].length,!0))))}},function(e,t,r){"use strict";var n=r(3),s=r(0).has,o=r(0).isValidEntityCode,i=r(0).fromCodePoint,a=/^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i,u=/^&([a-z][a-z0-9]{1,31});/i;e.exports=function(e,t){var r,c,l=e.pos,p=e.posMax;if(38!==e.src.charCodeAt(l))return!1;if(l+1i;n-=o.jump+1)if((o=t[n]).marker===s.marker&&o.open&&o.end<0&&(u=!1,(o.close||s.open)&&(o.length+s.length)%3==0&&(o.length%3==0&&s.length%3==0||(u=!0)),!u)){c=n>0&&!t[n-1].open?t[n-1].jump+1:0,s.jump=r-n+c,s.open=!1,o.end=r,o.jump=c,o.close=!1,a=-1;break}-1!==a&&(l[s.marker][(s.length||0)%3]=a)}}e.exports=function(e){var t,r=e.tokens_meta,s=e.tokens_meta.length;for(n(0,e.delimiters),t=0;t0&&n++,"text"===s[t].type&&t+10&&(this.level++,this._prev_delimiters.push(this.delimiters),this.delimiters=[],o={delimiters:this.delimiters}),this.pendingLevel=this.level,this.tokens.push(s),this.tokens_meta.push(o),s},a.prototype.scanDelims=function(e,t){var r,n,a,u,c,l,p,h,f,d=e,m=!0,g=!0,_=this.posMax,b=this.src.charCodeAt(e);for(r=e>0?this.src.charCodeAt(e-1):32;d<_&&this.src.charCodeAt(d)===b;)d++;return a=d-e,n=d<_?this.src.charCodeAt(d):32,p=i(r)||o(String.fromCharCode(r)),f=i(n)||o(String.fromCharCode(n)),l=s(r),(h=s(n))?m=!1:f&&(l||p||(m=!1)),l?g=!1:p&&(h||f||(g=!1)),t?(u=m,c=g):(u=m&&(!g||p),c=g&&(!m||f)),{can_open:u,can_close:c,length:a}},a.prototype.Token=n,e.exports=a},function(e,t,r){"use strict";function n(e){var t=Array.prototype.slice.call(arguments,1);return t.forEach((function(t){t&&Object.keys(t).forEach((function(r){e[r]=t[r]}))})),e}function s(e){return Object.prototype.toString.call(e)}function o(e){return"[object Function]"===s(e)}function i(e){return e.replace(/[.?*+^$[\]\\(){}|-]/g,"\\$&")}var a={fuzzyLink:!0,fuzzyEmail:!0,fuzzyIP:!1};var u={"http:":{validate:function(e,t,r){var n=e.slice(t);return r.re.http||(r.re.http=new RegExp("^\\/\\/"+r.re.src_auth+r.re.src_host_port_strict+r.re.src_path,"i")),r.re.http.test(n)?n.match(r.re.http)[0].length:0}},"https:":"http:","ftp:":"http:","//":{validate:function(e,t,r){var n=e.slice(t);return r.re.no_http||(r.re.no_http=new RegExp("^"+r.re.src_auth+"(?:localhost|(?:(?:"+r.re.src_domain+")\\.)+"+r.re.src_domain_root+")"+r.re.src_port+r.re.src_host_terminator+r.re.src_path,"i")),r.re.no_http.test(n)?t>=3&&":"===e[t-3]||t>=3&&"/"===e[t-3]?0:n.match(r.re.no_http)[0].length:0}},"mailto:":{validate:function(e,t,r){var n=e.slice(t);return r.re.mailto||(r.re.mailto=new RegExp("^"+r.re.src_email_name+"@"+r.re.src_host_strict,"i")),r.re.mailto.test(n)?n.match(r.re.mailto)[0].length:0}}},c="biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф".split("|");function l(e){var t=e.re=r(63)(e.__opts__),n=e.__tlds__.slice();function a(e){return e.replace("%TLDS%",t.src_tlds)}e.onCompile(),e.__tlds_replaced__||n.push("a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]"),n.push(t.src_xn),t.src_tlds=n.join("|"),t.email_fuzzy=RegExp(a(t.tpl_email_fuzzy),"i"),t.link_fuzzy=RegExp(a(t.tpl_link_fuzzy),"i"),t.link_no_ip_fuzzy=RegExp(a(t.tpl_link_no_ip_fuzzy),"i"),t.host_fuzzy_test=RegExp(a(t.tpl_host_fuzzy_test),"i");var u=[];function c(e,t){throw new Error('(LinkifyIt) Invalid schema "'+e+'": '+t)}e.__compiled__={},Object.keys(e.__schemas__).forEach((function(t){var r=e.__schemas__[t];if(null!==r){var n={validate:null,link:null};if(e.__compiled__[t]=n,"[object Object]"===s(r))return!function(e){return"[object RegExp]"===s(e)}(r.validate)?o(r.validate)?n.validate=r.validate:c(t,r):n.validate=function(e){return function(t,r){var n=t.slice(r);return e.test(n)?n.match(e)[0].length:0}}(r.validate),void(o(r.normalize)?n.normalize=r.normalize:r.normalize?c(t,r):n.normalize=function(e,t){t.normalize(e)});!function(e){return"[object String]"===s(e)}(r)?c(t,r):u.push(t)}})),u.forEach((function(t){e.__compiled__[e.__schemas__[t]]&&(e.__compiled__[t].validate=e.__compiled__[e.__schemas__[t]].validate,e.__compiled__[t].normalize=e.__compiled__[e.__schemas__[t]].normalize)})),e.__compiled__[""]={validate:null,normalize:function(e,t){t.normalize(e)}};var l=Object.keys(e.__compiled__).filter((function(t){return t.length>0&&e.__compiled__[t]})).map(i).join("|");e.re.schema_test=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+l+")","i"),e.re.schema_search=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+l+")","ig"),e.re.pretest=RegExp("("+e.re.schema_test.source+")|("+e.re.host_fuzzy_test.source+")|@","i"),function(e){e.__index__=-1,e.__text_cache__=""}(e)}function p(e,t){var r=e.__index__,n=e.__last_index__,s=e.__text_cache__.slice(r,n);this.schema=e.__schema__.toLowerCase(),this.index=r+t,this.lastIndex=n+t,this.raw=s,this.text=s,this.url=s}function h(e,t){var r=new p(e,t);return e.__compiled__[r.schema].normalize(r,e),r}function f(e,t){if(!(this instanceof f))return new f(e,t);var r;t||(r=e,Object.keys(r||{}).reduce((function(e,t){return e||a.hasOwnProperty(t)}),!1)&&(t=e,e={})),this.__opts__=n({},a,t),this.__index__=-1,this.__last_index__=-1,this.__schema__="",this.__text_cache__="",this.__schemas__=n({},u,e),this.__compiled__={},this.__tlds__=c,this.__tlds_replaced__=!1,this.re={},l(this)}f.prototype.add=function(e,t){return this.__schemas__[e]=t,l(this),this},f.prototype.set=function(e){return this.__opts__=n(this.__opts__,e),this},f.prototype.test=function(e){if(this.__text_cache__=e,this.__index__=-1,!e.length)return!1;var t,r,n,s,o,i,a,u;if(this.re.schema_test.test(e))for((a=this.re.schema_search).lastIndex=0;null!==(t=a.exec(e));)if(s=this.testSchemaAt(e,t[2],a.lastIndex)){this.__schema__=t[2],this.__index__=t.index+t[1].length,this.__last_index__=t.index+t[0].length+s;break}return this.__opts__.fuzzyLink&&this.__compiled__["http:"]&&(u=e.search(this.re.host_fuzzy_test))>=0&&(this.__index__<0||u=0&&null!==(n=e.match(this.re.email_fuzzy))&&(o=n.index+n[1].length,i=n.index+n[0].length,(this.__index__<0||othis.__last_index__)&&(this.__schema__="mailto:",this.__index__=o,this.__last_index__=i)),this.__index__>=0},f.prototype.pretest=function(e){return this.re.pretest.test(e)},f.prototype.testSchemaAt=function(e,t,r){return this.__compiled__[t.toLowerCase()]?this.__compiled__[t.toLowerCase()].validate(e,r,this):0},f.prototype.match=function(e){var t=0,r=[];this.__index__>=0&&this.__text_cache__===e&&(r.push(h(this,t)),t=this.__last_index__);for(var n=t?e.slice(t):e;this.test(n);)r.push(h(this,t)),n=n.slice(this.__last_index__),t+=this.__last_index__;return r.length?r:null},f.prototype.tlds=function(e,t){return e=Array.isArray(e)?e:[e],t?(this.__tlds__=this.__tlds__.concat(e).sort().filter((function(e,t,r){return e!==r[t-1]})).reverse(),l(this),this):(this.__tlds__=e.slice(),this.__tlds_replaced__=!0,l(this),this)},f.prototype.normalize=function(e){e.schema||(e.url="http://"+e.url),"mailto:"!==e.schema||/^mailto:/i.test(e.url)||(e.url="mailto:"+e.url)},f.prototype.onCompile=function(){},e.exports=f},function(e,t,r){"use strict";e.exports=function(e){var t={};t.src_Any=r(64).source,t.src_Cc=r(65).source,t.src_Z=r(66).source,t.src_P=r(67).source,t.src_ZPCc=[t.src_Z,t.src_P,t.src_Cc].join("|"),t.src_ZCc=[t.src_Z,t.src_Cc].join("|");return t.src_pseudo_letter="(?:(?![><|]|"+t.src_ZPCc+")"+t.src_Any+")",t.src_ip4="(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",t.src_auth="(?:(?:(?!"+t.src_ZCc+"|[@/\\[\\]()]).)+@)?",t.src_port="(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?",t.src_host_terminator="(?=$|[><|]|"+t.src_ZPCc+")(?!-|_|:\\d|\\.-|\\.(?!$|"+t.src_ZPCc+"))",t.src_path="(?:[/?#](?:(?!"+t.src_ZCc+"|[><|]|[()[\\]{}.,\"'?!\\-]).|\\[(?:(?!"+t.src_ZCc+"|\\]).)*\\]|\\((?:(?!"+t.src_ZCc+"|[)]).)*\\)|\\{(?:(?!"+t.src_ZCc+'|[}]).)*\\}|\\"(?:(?!'+t.src_ZCc+'|["]).)+\\"|\\\'(?:(?!'+t.src_ZCc+"|[']).)+\\'|\\'(?="+t.src_pseudo_letter+"|[-]).|\\.{2,}[a-zA-Z0-9%/&]|\\.(?!"+t.src_ZCc+"|[.]).|"+(e&&e["---"]?"\\-(?!--(?:[^-]|$))(?:-*)|":"\\-+|")+"\\,(?!"+t.src_ZCc+").|\\!+(?!"+t.src_ZCc+"|[!]).|\\?(?!"+t.src_ZCc+"|[?]).)+|\\/)?",t.src_email_name='[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*',t.src_xn="xn--[a-z0-9\\-]{1,59}",t.src_domain_root="(?:"+t.src_xn+"|"+t.src_pseudo_letter+"{1,63})",t.src_domain="(?:"+t.src_xn+"|(?:"+t.src_pseudo_letter+")|(?:"+t.src_pseudo_letter+"(?:-|"+t.src_pseudo_letter+"){0,61}"+t.src_pseudo_letter+"))",t.src_host="(?:(?:(?:(?:"+t.src_domain+")\\.)*"+t.src_domain+"))",t.tpl_host_fuzzy="(?:"+t.src_ip4+"|(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%)))",t.tpl_host_no_ip_fuzzy="(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%))",t.src_host_strict=t.src_host+t.src_host_terminator,t.tpl_host_fuzzy_strict=t.tpl_host_fuzzy+t.src_host_terminator,t.src_host_port_strict=t.src_host+t.src_port+t.src_host_terminator,t.tpl_host_port_fuzzy_strict=t.tpl_host_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_port_no_ip_fuzzy_strict=t.tpl_host_no_ip_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_fuzzy_test="localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:"+t.src_ZPCc+"|>|$))",t.tpl_email_fuzzy='(^|[><|]|"|\\(|'+t.src_ZCc+")("+t.src_email_name+"@"+t.tpl_host_fuzzy_strict+")",t.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_fuzzy_strict+t.src_path+")",t.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_no_ip_fuzzy_strict+t.src_path+")",t}},function(e,t){e.exports=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/},function(e,t){e.exports=/[\0-\x1F\x7F-\x9F]/},function(e,t){e.exports=/[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/},function(e,t){e.exports=/[!-#%-\*,-/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E44\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD807[\uDC41-\uDC45\uDC70\uDC71]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/},function(e,t,r){(function(e,n){var s;/*! https://mths.be/punycode v1.4.1 by @mathias */!function(o){t&&t.nodeType,e&&e.nodeType;var i="object"==typeof n&&n;i.global!==i&&i.window!==i&&i.self;var a,u=2147483647,c=/^xn--/,l=/[^\x20-\x7E]/,p=/[\x2E\u3002\uFF0E\uFF61]/g,h={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},f=Math.floor,d=String.fromCharCode;function m(e){throw new RangeError(h[e])}function g(e,t){for(var r=e.length,n=[];r--;)n[r]=t(e[r]);return n}function _(e,t){var r=e.split("@"),n="";return r.length>1&&(n=r[0]+"@",e=r[1]),n+g((e=e.replace(p,".")).split("."),t).join(".")}function b(e){for(var t,r,n=[],s=0,o=e.length;s=55296&&t<=56319&&s65535&&(t+=d((e-=65536)>>>10&1023|55296),e=56320|1023&e),t+=d(e)})).join("")}function C(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function v(e,t,r){var n=0;for(e=r?f(e/700):e>>1,e+=f(e/t);e>455;n+=36)e=f(e/35);return f(n+36*e/(e+38))}function D(e){var t,r,n,s,o,i,a,c,l,p,h,d=[],g=e.length,_=0,b=128,C=72;for((r=e.lastIndexOf("-"))<0&&(r=0),n=0;n=128&&m("not-basic"),d.push(e.charCodeAt(n));for(s=r>0?r+1:0;s=g&&m("invalid-input"),((c=(h=e.charCodeAt(s++))-48<10?h-22:h-65<26?h-65:h-97<26?h-97:36)>=36||c>f((u-_)/i))&&m("overflow"),_+=c*i,!(c<(l=a<=C?1:a>=C+26?26:a-C));a+=36)i>f(u/(p=36-l))&&m("overflow"),i*=p;C=v(_-o,t=d.length+1,0==o),f(_/t)>u-b&&m("overflow"),b+=f(_/t),_%=t,d.splice(_++,0,b)}return k(d)}function A(e){var t,r,n,s,o,i,a,c,l,p,h,g,_,k,D,A=[];for(g=(e=b(e)).length,t=128,r=0,o=72,i=0;i=t&&hf((u-r)/(_=n+1))&&m("overflow"),r+=(a-t)*_,t=a,i=0;iu&&m("overflow"),h==t){for(c=r,l=36;!(c<(p=l<=o?1:l>=o+26?26:l-o));l+=36)D=c-p,k=36-p,A.push(d(C(p+D%k,0))),c=f(D/k);A.push(d(C(c,0))),o=v(r,_,n==s),r=0,++n}++r,++t}return A.join("")}a={version:"1.4.1",ucs2:{decode:b,encode:k},decode:D,encode:A,toASCII:function(e){return _(e,(function(e){return l.test(e)?"xn--"+A(e):e}))},toUnicode:function(e){return _(e,(function(e){return c.test(e)?D(e.slice(4).toLowerCase()):e}))}},void 0===(s=function(){return a}.call(t,r,t,e))||(e.exports=s)}()}).call(this,r(69)(e),r(70))},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r},function(e,t,r){"use strict";e.exports={options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:100},components:{core:{},block:{},inline:{}}}},function(e,t,r){"use strict";e.exports={options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{rules:["normalize","block","inline"]},block:{rules:["paragraph"]},inline:{rules:["text"],rules2:["balance_pairs","text_collapse"]}}}},function(e,t,r){"use strict";e.exports={options:{html:!0,xhtmlOut:!0,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{rules:["normalize","block","inline"]},block:{rules:["blockquote","code","fence","heading","hr","html_block","lheading","list","reference","paragraph"]},inline:{rules:["autolink","backticks","emphasis","entity","escape","html_inline","image","link","newline","text"],rules2:["balance_pairs","emphasis","text_collapse"]}}}}]); \ No newline at end of file diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 40b0f925446..2414055c282 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -657,7 +657,8 @@ export class AzureActiveDirectoryService { this._refreshTimeouts.set(sessionId, setTimeout(async () => { try { - await this.refreshToken(refreshToken, scope, sessionId); + const refreshedToken = await this.refreshToken(refreshToken, scope, sessionId); + onDidChangeSessions.fire({ added: [], removed: [], changed: [this.convertToSessionSync(refreshedToken)] }); } catch (e) { this.pollForReconnect(sessionId, refreshToken, scope); } @@ -671,21 +672,14 @@ export class AzureActiveDirectoryService { return resolve(false); } - if (attempts === 1) { - const token = this._tokens.find(token => token.sessionId === sessionId); - if (token) { - token.accessToken = undefined; - onDidChangeSessions.fire({ added: [], removed: [], changed: [this.convertToSessionSync(token)] }); - } - } - const delayBeforeRetry = 5 * attempts * attempts; this.clearSessionTimeout(sessionId); this._refreshTimeouts.set(sessionId, setTimeout(async () => { try { - await this.refreshToken(refreshToken, scope, sessionId); + const refreshedToken = await this.refreshToken(refreshToken, scope, sessionId); + onDidChangeSessions.fire({ added: [], removed: [], changed: [this.convertToSessionSync(refreshedToken)] }); return resolve(true); } catch (e) { return resolve(await this.handleRefreshNetworkError(sessionId, refreshToken, scope, attempts + 1)); diff --git a/extensions/notebook-markdown-extensions/.vscodeignore b/extensions/notebook-markdown-extensions/.vscodeignore index 9f1e0620775..fd41868757d 100644 --- a/extensions/notebook-markdown-extensions/.vscodeignore +++ b/extensions/notebook-markdown-extensions/.vscodeignore @@ -1,12 +1,5 @@ -test/** -test-workspace/** -src/** -tsconfig.json -out/test/** -out/** -extension.webpack.config.js +notebook/** extension-browser.webpack.config.js cgmanifest.json yarn.lock -preview-src/** webpack.config.js diff --git a/extensions/notebook-markdown-extensions/notebook-out/emoji.js b/extensions/notebook-markdown-extensions/notebook-out/emoji.js deleted file mode 100644 index 23e7d31e654..00000000000 --- a/extensions/notebook-markdown-extensions/notebook-out/emoji.js +++ /dev/null @@ -1 +0,0 @@ -!function(a){var e={};function n(o){if(e[o])return e[o].exports;var i=e[o]={i:o,l:!1,exports:{}};return a[o].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=a,n.c=e,n.d=function(a,e,o){n.o(a,e)||Object.defineProperty(a,e,{enumerable:!0,get:o})},n.r=function(a){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(a,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(a,"__esModule",{value:!0})},n.t=function(a,e){if(1&e&&(a=n(a)),8&e)return a;if(4&e&&"object"==typeof a&&a&&a.__esModule)return a;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:a}),2&e&&"string"!=typeof a)for(var i in a)n.d(o,i,function(e){return a[e]}.bind(null,i));return o},n.n=function(a){var e=a&&a.__esModule?function(){return a.default}:function(){return a};return n.d(e,"a",e),e},n.o=function(a,e){return Object.prototype.hasOwnProperty.call(a,e)},n.p="",n(n.s=3)}([,,,function(a,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),function(){if("undefined"!=typeof extendMarkdownIt){const a=n(4);extendMarkdownIt(e=>{e.use(a)})}}()},function(a,e,n){"use strict";var o=n(5),i=n(6),r=n(7);a.exports=function(a,e){var n={defs:o,shortcuts:i,enabled:[]},_=a.utils.assign({},n,e||{});r(a,_)}},function(a){a.exports=JSON.parse('{"100":"💯","1234":"🔢","grinning":"😀","smiley":"😃","smile":"😄","grin":"😁","laughing":"😆","satisfied":"😆","sweat_smile":"😅","rofl":"🤣","joy":"😂","slightly_smiling_face":"🙂","upside_down_face":"🙃","wink":"😉","blush":"😊","innocent":"😇","smiling_face_with_three_hearts":"🥰","heart_eyes":"😍","star_struck":"🤩","kissing_heart":"😘","kissing":"😗","relaxed":"☺️","kissing_closed_eyes":"😚","kissing_smiling_eyes":"😙","smiling_face_with_tear":"🥲","yum":"😋","stuck_out_tongue":"😛","stuck_out_tongue_winking_eye":"😜","zany_face":"🤪","stuck_out_tongue_closed_eyes":"😝","money_mouth_face":"🤑","hugs":"🤗","hand_over_mouth":"🤭","shushing_face":"🤫","thinking":"🤔","zipper_mouth_face":"🤐","raised_eyebrow":"🤨","neutral_face":"😐","expressionless":"😑","no_mouth":"😶","smirk":"😏","unamused":"😒","roll_eyes":"🙄","grimacing":"😬","lying_face":"🤥","relieved":"😌","pensive":"😔","sleepy":"😪","drooling_face":"🤤","sleeping":"😴","mask":"😷","face_with_thermometer":"🤒","face_with_head_bandage":"🤕","nauseated_face":"🤢","vomiting_face":"🤮","sneezing_face":"🤧","hot_face":"🥵","cold_face":"🥶","woozy_face":"🥴","dizzy_face":"😵","exploding_head":"🤯","cowboy_hat_face":"🤠","partying_face":"🥳","disguised_face":"🥸","sunglasses":"😎","nerd_face":"🤓","monocle_face":"🧐","confused":"😕","worried":"😟","slightly_frowning_face":"🙁","frowning_face":"☹️","open_mouth":"😮","hushed":"😯","astonished":"😲","flushed":"😳","pleading_face":"🥺","frowning":"😦","anguished":"😧","fearful":"😨","cold_sweat":"😰","disappointed_relieved":"😥","cry":"😢","sob":"😭","scream":"😱","confounded":"😖","persevere":"😣","disappointed":"😞","sweat":"😓","weary":"😩","tired_face":"😫","yawning_face":"🥱","triumph":"😤","rage":"😡","pout":"😡","angry":"😠","cursing_face":"🤬","smiling_imp":"😈","imp":"👿","skull":"💀","skull_and_crossbones":"☠️","hankey":"💩","poop":"💩","shit":"💩","clown_face":"🤡","japanese_ogre":"👹","japanese_goblin":"👺","ghost":"👻","alien":"👽","space_invader":"👾","robot":"🤖","smiley_cat":"😺","smile_cat":"😸","joy_cat":"😹","heart_eyes_cat":"😻","smirk_cat":"😼","kissing_cat":"😽","scream_cat":"🙀","crying_cat_face":"😿","pouting_cat":"😾","see_no_evil":"🙈","hear_no_evil":"🙉","speak_no_evil":"🙊","kiss":"💋","love_letter":"💌","cupid":"💘","gift_heart":"💝","sparkling_heart":"💖","heartpulse":"💗","heartbeat":"💓","revolving_hearts":"💞","two_hearts":"💕","heart_decoration":"💟","heavy_heart_exclamation":"❣️","broken_heart":"💔","heart":"❤️","orange_heart":"🧡","yellow_heart":"💛","green_heart":"💚","blue_heart":"💙","purple_heart":"💜","brown_heart":"🤎","black_heart":"🖤","white_heart":"🤍","anger":"💢","boom":"💥","collision":"💥","dizzy":"💫","sweat_drops":"💦","dash":"💨","hole":"🕳️","bomb":"💣","speech_balloon":"💬","eye_speech_bubble":"👁️‍🗨️","left_speech_bubble":"🗨️","right_anger_bubble":"🗯️","thought_balloon":"💭","zzz":"💤","wave":"👋","raised_back_of_hand":"🤚","raised_hand_with_fingers_splayed":"🖐️","hand":"✋","raised_hand":"✋","vulcan_salute":"🖖","ok_hand":"👌","pinched_fingers":"🤌","pinching_hand":"🤏","v":"✌️","crossed_fingers":"🤞","love_you_gesture":"🤟","metal":"🤘","call_me_hand":"🤙","point_left":"👈","point_right":"👉","point_up_2":"👆","middle_finger":"🖕","fu":"🖕","point_down":"👇","point_up":"☝️","+1":"👍","thumbsup":"👍","-1":"👎","thumbsdown":"👎","fist_raised":"✊","fist":"✊","fist_oncoming":"👊","facepunch":"👊","punch":"👊","fist_left":"🤛","fist_right":"🤜","clap":"👏","raised_hands":"🙌","open_hands":"👐","palms_up_together":"🤲","handshake":"🤝","pray":"🙏","writing_hand":"✍️","nail_care":"💅","selfie":"🤳","muscle":"💪","mechanical_arm":"🦾","mechanical_leg":"🦿","leg":"🦵","foot":"🦶","ear":"👂","ear_with_hearing_aid":"🦻","nose":"👃","brain":"🧠","anatomical_heart":"🫀","lungs":"🫁","tooth":"🦷","bone":"🦴","eyes":"👀","eye":"👁️","tongue":"👅","lips":"👄","baby":"👶","child":"🧒","boy":"👦","girl":"👧","adult":"🧑","blond_haired_person":"👱","man":"👨","bearded_person":"🧔","red_haired_man":"👨‍🦰","curly_haired_man":"👨‍🦱","white_haired_man":"👨‍🦳","bald_man":"👨‍🦲","woman":"👩","red_haired_woman":"👩‍🦰","person_red_hair":"🧑‍🦰","curly_haired_woman":"👩‍🦱","person_curly_hair":"🧑‍🦱","white_haired_woman":"👩‍🦳","person_white_hair":"🧑‍🦳","bald_woman":"👩‍🦲","person_bald":"🧑‍🦲","blond_haired_woman":"👱‍♀️","blonde_woman":"👱‍♀️","blond_haired_man":"👱‍♂️","older_adult":"🧓","older_man":"👴","older_woman":"👵","frowning_person":"🙍","frowning_man":"🙍‍♂️","frowning_woman":"🙍‍♀️","pouting_face":"🙎","pouting_man":"🙎‍♂️","pouting_woman":"🙎‍♀️","no_good":"🙅","no_good_man":"🙅‍♂️","ng_man":"🙅‍♂️","no_good_woman":"🙅‍♀️","ng_woman":"🙅‍♀️","ok_person":"🙆","ok_man":"🙆‍♂️","ok_woman":"🙆‍♀️","tipping_hand_person":"💁","information_desk_person":"💁","tipping_hand_man":"💁‍♂️","sassy_man":"💁‍♂️","tipping_hand_woman":"💁‍♀️","sassy_woman":"💁‍♀️","raising_hand":"🙋","raising_hand_man":"🙋‍♂️","raising_hand_woman":"🙋‍♀️","deaf_person":"🧏","deaf_man":"🧏‍♂️","deaf_woman":"🧏‍♀️","bow":"🙇","bowing_man":"🙇‍♂️","bowing_woman":"🙇‍♀️","facepalm":"🤦","man_facepalming":"🤦‍♂️","woman_facepalming":"🤦‍♀️","shrug":"🤷","man_shrugging":"🤷‍♂️","woman_shrugging":"🤷‍♀️","health_worker":"🧑‍⚕️","man_health_worker":"👨‍⚕️","woman_health_worker":"👩‍⚕️","student":"🧑‍🎓","man_student":"👨‍🎓","woman_student":"👩‍🎓","teacher":"🧑‍🏫","man_teacher":"👨‍🏫","woman_teacher":"👩‍🏫","judge":"🧑‍⚖️","man_judge":"👨‍⚖️","woman_judge":"👩‍⚖️","farmer":"🧑‍🌾","man_farmer":"👨‍🌾","woman_farmer":"👩‍🌾","cook":"🧑‍🍳","man_cook":"👨‍🍳","woman_cook":"👩‍🍳","mechanic":"🧑‍🔧","man_mechanic":"👨‍🔧","woman_mechanic":"👩‍🔧","factory_worker":"🧑‍🏭","man_factory_worker":"👨‍🏭","woman_factory_worker":"👩‍🏭","office_worker":"🧑‍💼","man_office_worker":"👨‍💼","woman_office_worker":"👩‍💼","scientist":"🧑‍🔬","man_scientist":"👨‍🔬","woman_scientist":"👩‍🔬","technologist":"🧑‍💻","man_technologist":"👨‍💻","woman_technologist":"👩‍💻","singer":"🧑‍🎤","man_singer":"👨‍🎤","woman_singer":"👩‍🎤","artist":"🧑‍🎨","man_artist":"👨‍🎨","woman_artist":"👩‍🎨","pilot":"🧑‍✈️","man_pilot":"👨‍✈️","woman_pilot":"👩‍✈️","astronaut":"🧑‍🚀","man_astronaut":"👨‍🚀","woman_astronaut":"👩‍🚀","firefighter":"🧑‍🚒","man_firefighter":"👨‍🚒","woman_firefighter":"👩‍🚒","police_officer":"👮","cop":"👮","policeman":"👮‍♂️","policewoman":"👮‍♀️","detective":"🕵️","male_detective":"🕵️‍♂️","female_detective":"🕵️‍♀️","guard":"💂","guardsman":"💂‍♂️","guardswoman":"💂‍♀️","ninja":"🥷","construction_worker":"👷","construction_worker_man":"👷‍♂️","construction_worker_woman":"👷‍♀️","prince":"🤴","princess":"👸","person_with_turban":"👳","man_with_turban":"👳‍♂️","woman_with_turban":"👳‍♀️","man_with_gua_pi_mao":"👲","woman_with_headscarf":"🧕","person_in_tuxedo":"🤵","man_in_tuxedo":"🤵‍♂️","woman_in_tuxedo":"🤵‍♀️","person_with_veil":"👰","man_with_veil":"👰‍♂️","woman_with_veil":"👰‍♀️","bride_with_veil":"👰‍♀️","pregnant_woman":"🤰","breast_feeding":"🤱","woman_feeding_baby":"👩‍🍼","man_feeding_baby":"👨‍🍼","person_feeding_baby":"🧑‍🍼","angel":"👼","santa":"🎅","mrs_claus":"🤶","mx_claus":"🧑‍🎄","superhero":"🦸","superhero_man":"🦸‍♂️","superhero_woman":"🦸‍♀️","supervillain":"🦹","supervillain_man":"🦹‍♂️","supervillain_woman":"🦹‍♀️","mage":"🧙","mage_man":"🧙‍♂️","mage_woman":"🧙‍♀️","fairy":"🧚","fairy_man":"🧚‍♂️","fairy_woman":"🧚‍♀️","vampire":"🧛","vampire_man":"🧛‍♂️","vampire_woman":"🧛‍♀️","merperson":"🧜","merman":"🧜‍♂️","mermaid":"🧜‍♀️","elf":"🧝","elf_man":"🧝‍♂️","elf_woman":"🧝‍♀️","genie":"🧞","genie_man":"🧞‍♂️","genie_woman":"🧞‍♀️","zombie":"🧟","zombie_man":"🧟‍♂️","zombie_woman":"🧟‍♀️","massage":"💆","massage_man":"💆‍♂️","massage_woman":"💆‍♀️","haircut":"💇","haircut_man":"💇‍♂️","haircut_woman":"💇‍♀️","walking":"🚶","walking_man":"🚶‍♂️","walking_woman":"🚶‍♀️","standing_person":"🧍","standing_man":"🧍‍♂️","standing_woman":"🧍‍♀️","kneeling_person":"🧎","kneeling_man":"🧎‍♂️","kneeling_woman":"🧎‍♀️","person_with_probing_cane":"🧑‍🦯","man_with_probing_cane":"👨‍🦯","woman_with_probing_cane":"👩‍🦯","person_in_motorized_wheelchair":"🧑‍🦼","man_in_motorized_wheelchair":"👨‍🦼","woman_in_motorized_wheelchair":"👩‍🦼","person_in_manual_wheelchair":"🧑‍🦽","man_in_manual_wheelchair":"👨‍🦽","woman_in_manual_wheelchair":"👩‍🦽","runner":"🏃","running":"🏃","running_man":"🏃‍♂️","running_woman":"🏃‍♀️","woman_dancing":"💃","dancer":"💃","man_dancing":"🕺","business_suit_levitating":"🕴️","dancers":"👯","dancing_men":"👯‍♂️","dancing_women":"👯‍♀️","sauna_person":"🧖","sauna_man":"🧖‍♂️","sauna_woman":"🧖‍♀️","climbing":"🧗","climbing_man":"🧗‍♂️","climbing_woman":"🧗‍♀️","person_fencing":"🤺","horse_racing":"🏇","skier":"⛷️","snowboarder":"🏂","golfing":"🏌️","golfing_man":"🏌️‍♂️","golfing_woman":"🏌️‍♀️","surfer":"🏄","surfing_man":"🏄‍♂️","surfing_woman":"🏄‍♀️","rowboat":"🚣","rowing_man":"🚣‍♂️","rowing_woman":"🚣‍♀️","swimmer":"🏊","swimming_man":"🏊‍♂️","swimming_woman":"🏊‍♀️","bouncing_ball_person":"⛹️","bouncing_ball_man":"⛹️‍♂️","basketball_man":"⛹️‍♂️","bouncing_ball_woman":"⛹️‍♀️","basketball_woman":"⛹️‍♀️","weight_lifting":"🏋️","weight_lifting_man":"🏋️‍♂️","weight_lifting_woman":"🏋️‍♀️","bicyclist":"🚴","biking_man":"🚴‍♂️","biking_woman":"🚴‍♀️","mountain_bicyclist":"🚵","mountain_biking_man":"🚵‍♂️","mountain_biking_woman":"🚵‍♀️","cartwheeling":"🤸","man_cartwheeling":"🤸‍♂️","woman_cartwheeling":"🤸‍♀️","wrestling":"🤼","men_wrestling":"🤼‍♂️","women_wrestling":"🤼‍♀️","water_polo":"🤽","man_playing_water_polo":"🤽‍♂️","woman_playing_water_polo":"🤽‍♀️","handball_person":"🤾","man_playing_handball":"🤾‍♂️","woman_playing_handball":"🤾‍♀️","juggling_person":"🤹","man_juggling":"🤹‍♂️","woman_juggling":"🤹‍♀️","lotus_position":"🧘","lotus_position_man":"🧘‍♂️","lotus_position_woman":"🧘‍♀️","bath":"🛀","sleeping_bed":"🛌","people_holding_hands":"🧑‍🤝‍🧑","two_women_holding_hands":"👭","couple":"👫","two_men_holding_hands":"👬","couplekiss":"💏","couplekiss_man_woman":"👩‍❤️‍💋‍👨","couplekiss_man_man":"👨‍❤️‍💋‍👨","couplekiss_woman_woman":"👩‍❤️‍💋‍👩","couple_with_heart":"💑","couple_with_heart_woman_man":"👩‍❤️‍👨","couple_with_heart_man_man":"👨‍❤️‍👨","couple_with_heart_woman_woman":"👩‍❤️‍👩","family":"👪","family_man_woman_boy":"👨‍👩‍👦","family_man_woman_girl":"👨‍👩‍👧","family_man_woman_girl_boy":"👨‍👩‍👧‍👦","family_man_woman_boy_boy":"👨‍👩‍👦‍👦","family_man_woman_girl_girl":"👨‍👩‍👧‍👧","family_man_man_boy":"👨‍👨‍👦","family_man_man_girl":"👨‍👨‍👧","family_man_man_girl_boy":"👨‍👨‍👧‍👦","family_man_man_boy_boy":"👨‍👨‍👦‍👦","family_man_man_girl_girl":"👨‍👨‍👧‍👧","family_woman_woman_boy":"👩‍👩‍👦","family_woman_woman_girl":"👩‍👩‍👧","family_woman_woman_girl_boy":"👩‍👩‍👧‍👦","family_woman_woman_boy_boy":"👩‍👩‍👦‍👦","family_woman_woman_girl_girl":"👩‍👩‍👧‍👧","family_man_boy":"👨‍👦","family_man_boy_boy":"👨‍👦‍👦","family_man_girl":"👨‍👧","family_man_girl_boy":"👨‍👧‍👦","family_man_girl_girl":"👨‍👧‍👧","family_woman_boy":"👩‍👦","family_woman_boy_boy":"👩‍👦‍👦","family_woman_girl":"👩‍👧","family_woman_girl_boy":"👩‍👧‍👦","family_woman_girl_girl":"👩‍👧‍👧","speaking_head":"🗣️","bust_in_silhouette":"👤","busts_in_silhouette":"👥","people_hugging":"🫂","footprints":"👣","monkey_face":"🐵","monkey":"🐒","gorilla":"🦍","orangutan":"🦧","dog":"🐶","dog2":"🐕","guide_dog":"🦮","service_dog":"🐕‍🦺","poodle":"🐩","wolf":"🐺","fox_face":"🦊","raccoon":"🦝","cat":"🐱","cat2":"🐈","black_cat":"🐈‍⬛","lion":"🦁","tiger":"🐯","tiger2":"🐅","leopard":"🐆","horse":"🐴","racehorse":"🐎","unicorn":"🦄","zebra":"🦓","deer":"🦌","bison":"🦬","cow":"🐮","ox":"🐂","water_buffalo":"🐃","cow2":"🐄","pig":"🐷","pig2":"🐖","boar":"🐗","pig_nose":"🐽","ram":"🐏","sheep":"🐑","goat":"🐐","dromedary_camel":"🐪","camel":"🐫","llama":"🦙","giraffe":"🦒","elephant":"🐘","mammoth":"🦣","rhinoceros":"🦏","hippopotamus":"🦛","mouse":"🐭","mouse2":"🐁","rat":"🐀","hamster":"🐹","rabbit":"🐰","rabbit2":"🐇","chipmunk":"🐿️","beaver":"🦫","hedgehog":"🦔","bat":"🦇","bear":"🐻","polar_bear":"🐻‍❄️","koala":"🐨","panda_face":"🐼","sloth":"🦥","otter":"🦦","skunk":"🦨","kangaroo":"🦘","badger":"🦡","feet":"🐾","paw_prints":"🐾","turkey":"🦃","chicken":"🐔","rooster":"🐓","hatching_chick":"🐣","baby_chick":"🐤","hatched_chick":"🐥","bird":"🐦","penguin":"🐧","dove":"🕊️","eagle":"🦅","duck":"🦆","swan":"🦢","owl":"🦉","dodo":"🦤","feather":"🪶","flamingo":"🦩","peacock":"🦚","parrot":"🦜","frog":"🐸","crocodile":"🐊","turtle":"🐢","lizard":"🦎","snake":"🐍","dragon_face":"🐲","dragon":"🐉","sauropod":"🦕","t-rex":"🦖","whale":"🐳","whale2":"🐋","dolphin":"🐬","flipper":"🐬","seal":"🦭","fish":"🐟","tropical_fish":"🐠","blowfish":"🐡","shark":"🦈","octopus":"🐙","shell":"🐚","snail":"🐌","butterfly":"🦋","bug":"🐛","ant":"🐜","bee":"🐝","honeybee":"🐝","beetle":"🪲","lady_beetle":"🐞","cricket":"🦗","cockroach":"🪳","spider":"🕷️","spider_web":"🕸️","scorpion":"🦂","mosquito":"🦟","fly":"🪰","worm":"🪱","microbe":"🦠","bouquet":"💐","cherry_blossom":"🌸","white_flower":"💮","rosette":"🏵️","rose":"🌹","wilted_flower":"🥀","hibiscus":"🌺","sunflower":"🌻","blossom":"🌼","tulip":"🌷","seedling":"🌱","potted_plant":"🪴","evergreen_tree":"🌲","deciduous_tree":"🌳","palm_tree":"🌴","cactus":"🌵","ear_of_rice":"🌾","herb":"🌿","shamrock":"☘️","four_leaf_clover":"🍀","maple_leaf":"🍁","fallen_leaf":"🍂","leaves":"🍃","grapes":"🍇","melon":"🍈","watermelon":"🍉","tangerine":"🍊","orange":"🍊","mandarin":"🍊","lemon":"🍋","banana":"🍌","pineapple":"🍍","mango":"🥭","apple":"🍎","green_apple":"🍏","pear":"🍐","peach":"🍑","cherries":"🍒","strawberry":"🍓","blueberries":"🫐","kiwi_fruit":"🥝","tomato":"🍅","olive":"🫒","coconut":"🥥","avocado":"🥑","eggplant":"🍆","potato":"🥔","carrot":"🥕","corn":"🌽","hot_pepper":"🌶️","bell_pepper":"🫑","cucumber":"🥒","leafy_green":"🥬","broccoli":"🥦","garlic":"🧄","onion":"🧅","mushroom":"🍄","peanuts":"🥜","chestnut":"🌰","bread":"🍞","croissant":"🥐","baguette_bread":"🥖","flatbread":"🫓","pretzel":"🥨","bagel":"🥯","pancakes":"🥞","waffle":"🧇","cheese":"🧀","meat_on_bone":"🍖","poultry_leg":"🍗","cut_of_meat":"🥩","bacon":"🥓","hamburger":"🍔","fries":"🍟","pizza":"🍕","hotdog":"🌭","sandwich":"🥪","taco":"🌮","burrito":"🌯","tamale":"🫔","stuffed_flatbread":"🥙","falafel":"🧆","egg":"🥚","fried_egg":"🍳","shallow_pan_of_food":"🥘","stew":"🍲","fondue":"🫕","bowl_with_spoon":"🥣","green_salad":"🥗","popcorn":"🍿","butter":"🧈","salt":"🧂","canned_food":"🥫","bento":"🍱","rice_cracker":"🍘","rice_ball":"🍙","rice":"🍚","curry":"🍛","ramen":"🍜","spaghetti":"🍝","sweet_potato":"🍠","oden":"🍢","sushi":"🍣","fried_shrimp":"🍤","fish_cake":"🍥","moon_cake":"🥮","dango":"🍡","dumpling":"🥟","fortune_cookie":"🥠","takeout_box":"🥡","crab":"🦀","lobster":"🦞","shrimp":"🦐","squid":"🦑","oyster":"🦪","icecream":"🍦","shaved_ice":"🍧","ice_cream":"🍨","doughnut":"🍩","cookie":"🍪","birthday":"🎂","cake":"🍰","cupcake":"🧁","pie":"🥧","chocolate_bar":"🍫","candy":"🍬","lollipop":"🍭","custard":"🍮","honey_pot":"🍯","baby_bottle":"🍼","milk_glass":"🥛","coffee":"☕","teapot":"🫖","tea":"🍵","sake":"🍶","champagne":"🍾","wine_glass":"🍷","cocktail":"🍸","tropical_drink":"🍹","beer":"🍺","beers":"🍻","clinking_glasses":"🥂","tumbler_glass":"🥃","cup_with_straw":"🥤","bubble_tea":"🧋","beverage_box":"🧃","mate":"🧉","ice_cube":"🧊","chopsticks":"🥢","plate_with_cutlery":"🍽️","fork_and_knife":"🍴","spoon":"🥄","hocho":"🔪","knife":"🔪","amphora":"🏺","earth_africa":"🌍","earth_americas":"🌎","earth_asia":"🌏","globe_with_meridians":"🌐","world_map":"🗺️","japan":"🗾","compass":"🧭","mountain_snow":"🏔️","mountain":"⛰️","volcano":"🌋","mount_fuji":"🗻","camping":"🏕️","beach_umbrella":"🏖️","desert":"🏜️","desert_island":"🏝️","national_park":"🏞️","stadium":"🏟️","classical_building":"🏛️","building_construction":"🏗️","bricks":"🧱","rock":"🪨","wood":"🪵","hut":"🛖","houses":"🏘️","derelict_house":"🏚️","house":"🏠","house_with_garden":"🏡","office":"🏢","post_office":"🏣","european_post_office":"🏤","hospital":"🏥","bank":"🏦","hotel":"🏨","love_hotel":"🏩","convenience_store":"🏪","school":"🏫","department_store":"🏬","factory":"🏭","japanese_castle":"🏯","european_castle":"🏰","wedding":"💒","tokyo_tower":"🗼","statue_of_liberty":"🗽","church":"⛪","mosque":"🕌","hindu_temple":"🛕","synagogue":"🕍","shinto_shrine":"⛩️","kaaba":"🕋","fountain":"⛲","tent":"⛺","foggy":"🌁","night_with_stars":"🌃","cityscape":"🏙️","sunrise_over_mountains":"🌄","sunrise":"🌅","city_sunset":"🌆","city_sunrise":"🌇","bridge_at_night":"🌉","hotsprings":"♨️","carousel_horse":"🎠","ferris_wheel":"🎡","roller_coaster":"🎢","barber":"💈","circus_tent":"🎪","steam_locomotive":"🚂","railway_car":"🚃","bullettrain_side":"🚄","bullettrain_front":"🚅","train2":"🚆","metro":"🚇","light_rail":"🚈","station":"🚉","tram":"🚊","monorail":"🚝","mountain_railway":"🚞","train":"🚋","bus":"🚌","oncoming_bus":"🚍","trolleybus":"🚎","minibus":"🚐","ambulance":"🚑","fire_engine":"🚒","police_car":"🚓","oncoming_police_car":"🚔","taxi":"🚕","oncoming_taxi":"🚖","car":"🚗","red_car":"🚗","oncoming_automobile":"🚘","blue_car":"🚙","pickup_truck":"🛻","truck":"🚚","articulated_lorry":"🚛","tractor":"🚜","racing_car":"🏎️","motorcycle":"🏍️","motor_scooter":"🛵","manual_wheelchair":"🦽","motorized_wheelchair":"🦼","auto_rickshaw":"🛺","bike":"🚲","kick_scooter":"🛴","skateboard":"🛹","roller_skate":"🛼","busstop":"🚏","motorway":"🛣️","railway_track":"🛤️","oil_drum":"🛢️","fuelpump":"⛽","rotating_light":"🚨","traffic_light":"🚥","vertical_traffic_light":"🚦","stop_sign":"🛑","construction":"🚧","anchor":"⚓","boat":"⛵","sailboat":"⛵","canoe":"🛶","speedboat":"🚤","passenger_ship":"🛳️","ferry":"⛴️","motor_boat":"🛥️","ship":"🚢","airplane":"✈️","small_airplane":"🛩️","flight_departure":"🛫","flight_arrival":"🛬","parachute":"🪂","seat":"💺","helicopter":"🚁","suspension_railway":"🚟","mountain_cableway":"🚠","aerial_tramway":"🚡","artificial_satellite":"🛰️","rocket":"🚀","flying_saucer":"🛸","bellhop_bell":"🛎️","luggage":"🧳","hourglass":"⌛","hourglass_flowing_sand":"⏳","watch":"⌚","alarm_clock":"⏰","stopwatch":"⏱️","timer_clock":"⏲️","mantelpiece_clock":"🕰️","clock12":"🕛","clock1230":"🕧","clock1":"🕐","clock130":"🕜","clock2":"🕑","clock230":"🕝","clock3":"🕒","clock330":"🕞","clock4":"🕓","clock430":"🕟","clock5":"🕔","clock530":"🕠","clock6":"🕕","clock630":"🕡","clock7":"🕖","clock730":"🕢","clock8":"🕗","clock830":"🕣","clock9":"🕘","clock930":"🕤","clock10":"🕙","clock1030":"🕥","clock11":"🕚","clock1130":"🕦","new_moon":"🌑","waxing_crescent_moon":"🌒","first_quarter_moon":"🌓","moon":"🌔","waxing_gibbous_moon":"🌔","full_moon":"🌕","waning_gibbous_moon":"🌖","last_quarter_moon":"🌗","waning_crescent_moon":"🌘","crescent_moon":"🌙","new_moon_with_face":"🌚","first_quarter_moon_with_face":"🌛","last_quarter_moon_with_face":"🌜","thermometer":"🌡️","sunny":"☀️","full_moon_with_face":"🌝","sun_with_face":"🌞","ringed_planet":"🪐","star":"⭐","star2":"🌟","stars":"🌠","milky_way":"🌌","cloud":"☁️","partly_sunny":"⛅","cloud_with_lightning_and_rain":"⛈️","sun_behind_small_cloud":"🌤️","sun_behind_large_cloud":"🌥️","sun_behind_rain_cloud":"🌦️","cloud_with_rain":"🌧️","cloud_with_snow":"🌨️","cloud_with_lightning":"🌩️","tornado":"🌪️","fog":"🌫️","wind_face":"🌬️","cyclone":"🌀","rainbow":"🌈","closed_umbrella":"🌂","open_umbrella":"☂️","umbrella":"☔","parasol_on_ground":"⛱️","zap":"⚡","snowflake":"❄️","snowman_with_snow":"☃️","snowman":"⛄","comet":"☄️","fire":"🔥","droplet":"💧","ocean":"🌊","jack_o_lantern":"🎃","christmas_tree":"🎄","fireworks":"🎆","sparkler":"🎇","firecracker":"🧨","sparkles":"✨","balloon":"🎈","tada":"🎉","confetti_ball":"🎊","tanabata_tree":"🎋","bamboo":"🎍","dolls":"🎎","flags":"🎏","wind_chime":"🎐","rice_scene":"🎑","red_envelope":"🧧","ribbon":"🎀","gift":"🎁","reminder_ribbon":"🎗️","tickets":"🎟️","ticket":"🎫","medal_military":"🎖️","trophy":"🏆","medal_sports":"🏅","1st_place_medal":"🥇","2nd_place_medal":"🥈","3rd_place_medal":"🥉","soccer":"⚽","baseball":"⚾","softball":"🥎","basketball":"🏀","volleyball":"🏐","football":"🏈","rugby_football":"🏉","tennis":"🎾","flying_disc":"🥏","bowling":"🎳","cricket_game":"🏏","field_hockey":"🏑","ice_hockey":"🏒","lacrosse":"🥍","ping_pong":"🏓","badminton":"🏸","boxing_glove":"🥊","martial_arts_uniform":"🥋","goal_net":"🥅","golf":"⛳","ice_skate":"⛸️","fishing_pole_and_fish":"🎣","diving_mask":"🤿","running_shirt_with_sash":"🎽","ski":"🎿","sled":"🛷","curling_stone":"🥌","dart":"🎯","yo_yo":"🪀","kite":"🪁","8ball":"🎱","crystal_ball":"🔮","magic_wand":"🪄","nazar_amulet":"🧿","video_game":"🎮","joystick":"🕹️","slot_machine":"🎰","game_die":"🎲","jigsaw":"🧩","teddy_bear":"🧸","pinata":"🪅","nesting_dolls":"🪆","spades":"♠️","hearts":"♥️","diamonds":"♦️","clubs":"♣️","chess_pawn":"♟️","black_joker":"🃏","mahjong":"🀄","flower_playing_cards":"🎴","performing_arts":"🎭","framed_picture":"🖼️","art":"🎨","thread":"🧵","sewing_needle":"🪡","yarn":"🧶","knot":"🪢","eyeglasses":"👓","dark_sunglasses":"🕶️","goggles":"🥽","lab_coat":"🥼","safety_vest":"🦺","necktie":"👔","shirt":"👕","tshirt":"👕","jeans":"👖","scarf":"🧣","gloves":"🧤","coat":"🧥","socks":"🧦","dress":"👗","kimono":"👘","sari":"🥻","one_piece_swimsuit":"🩱","swim_brief":"🩲","shorts":"🩳","bikini":"👙","womans_clothes":"👚","purse":"👛","handbag":"👜","pouch":"👝","shopping":"🛍️","school_satchel":"🎒","thong_sandal":"🩴","mans_shoe":"👞","shoe":"👞","athletic_shoe":"👟","hiking_boot":"🥾","flat_shoe":"🥿","high_heel":"👠","sandal":"👡","ballet_shoes":"🩰","boot":"👢","crown":"👑","womans_hat":"👒","tophat":"🎩","mortar_board":"🎓","billed_cap":"🧢","military_helmet":"🪖","rescue_worker_helmet":"⛑️","prayer_beads":"📿","lipstick":"💄","ring":"💍","gem":"💎","mute":"🔇","speaker":"🔈","sound":"🔉","loud_sound":"🔊","loudspeaker":"📢","mega":"📣","postal_horn":"📯","bell":"🔔","no_bell":"🔕","musical_score":"🎼","musical_note":"🎵","notes":"🎶","studio_microphone":"🎙️","level_slider":"🎚️","control_knobs":"🎛️","microphone":"🎤","headphones":"🎧","radio":"📻","saxophone":"🎷","accordion":"🪗","guitar":"🎸","musical_keyboard":"🎹","trumpet":"🎺","violin":"🎻","banjo":"🪕","drum":"🥁","long_drum":"🪘","iphone":"📱","calling":"📲","phone":"☎️","telephone":"☎️","telephone_receiver":"📞","pager":"📟","fax":"📠","battery":"🔋","electric_plug":"🔌","computer":"💻","desktop_computer":"🖥️","printer":"🖨️","keyboard":"⌨️","computer_mouse":"🖱️","trackball":"🖲️","minidisc":"💽","floppy_disk":"💾","cd":"💿","dvd":"📀","abacus":"🧮","movie_camera":"🎥","film_strip":"🎞️","film_projector":"📽️","clapper":"🎬","tv":"📺","camera":"📷","camera_flash":"📸","video_camera":"📹","vhs":"📼","mag":"🔍","mag_right":"🔎","candle":"🕯️","bulb":"💡","flashlight":"🔦","izakaya_lantern":"🏮","lantern":"🏮","diya_lamp":"🪔","notebook_with_decorative_cover":"📔","closed_book":"📕","book":"📖","open_book":"📖","green_book":"📗","blue_book":"📘","orange_book":"📙","books":"📚","notebook":"📓","ledger":"📒","page_with_curl":"📃","scroll":"📜","page_facing_up":"📄","newspaper":"📰","newspaper_roll":"🗞️","bookmark_tabs":"📑","bookmark":"🔖","label":"🏷️","moneybag":"💰","coin":"🪙","yen":"💴","dollar":"💵","euro":"💶","pound":"💷","money_with_wings":"💸","credit_card":"💳","receipt":"🧾","chart":"💹","envelope":"✉️","email":"📧","e-mail":"📧","incoming_envelope":"📨","envelope_with_arrow":"📩","outbox_tray":"📤","inbox_tray":"📥","package":"📦","mailbox":"📫","mailbox_closed":"📪","mailbox_with_mail":"📬","mailbox_with_no_mail":"📭","postbox":"📮","ballot_box":"🗳️","pencil2":"✏️","black_nib":"✒️","fountain_pen":"🖋️","pen":"🖊️","paintbrush":"🖌️","crayon":"🖍️","memo":"📝","pencil":"📝","briefcase":"💼","file_folder":"📁","open_file_folder":"📂","card_index_dividers":"🗂️","date":"📅","calendar":"📆","spiral_notepad":"🗒️","spiral_calendar":"🗓️","card_index":"📇","chart_with_upwards_trend":"📈","chart_with_downwards_trend":"📉","bar_chart":"📊","clipboard":"📋","pushpin":"📌","round_pushpin":"📍","paperclip":"📎","paperclips":"🖇️","straight_ruler":"📏","triangular_ruler":"📐","scissors":"✂️","card_file_box":"🗃️","file_cabinet":"🗄️","wastebasket":"🗑️","lock":"🔒","unlock":"🔓","lock_with_ink_pen":"🔏","closed_lock_with_key":"🔐","key":"🔑","old_key":"🗝️","hammer":"🔨","axe":"🪓","pick":"⛏️","hammer_and_pick":"⚒️","hammer_and_wrench":"🛠️","dagger":"🗡️","crossed_swords":"⚔️","gun":"🔫","boomerang":"🪃","bow_and_arrow":"🏹","shield":"🛡️","carpentry_saw":"🪚","wrench":"🔧","screwdriver":"🪛","nut_and_bolt":"🔩","gear":"⚙️","clamp":"🗜️","balance_scale":"⚖️","probing_cane":"🦯","link":"🔗","chains":"⛓️","hook":"🪝","toolbox":"🧰","magnet":"🧲","ladder":"🪜","alembic":"⚗️","test_tube":"🧪","petri_dish":"🧫","dna":"🧬","microscope":"🔬","telescope":"🔭","satellite":"📡","syringe":"💉","drop_of_blood":"🩸","pill":"💊","adhesive_bandage":"🩹","stethoscope":"🩺","door":"🚪","elevator":"🛗","mirror":"🪞","window":"🪟","bed":"🛏️","couch_and_lamp":"🛋️","chair":"🪑","toilet":"🚽","plunger":"🪠","shower":"🚿","bathtub":"🛁","mouse_trap":"🪤","razor":"🪒","lotion_bottle":"🧴","safety_pin":"🧷","broom":"🧹","basket":"🧺","roll_of_paper":"🧻","bucket":"🪣","soap":"🧼","toothbrush":"🪥","sponge":"🧽","fire_extinguisher":"🧯","shopping_cart":"🛒","smoking":"🚬","coffin":"⚰️","headstone":"🪦","funeral_urn":"⚱️","moyai":"🗿","placard":"🪧","atm":"🏧","put_litter_in_its_place":"🚮","potable_water":"🚰","wheelchair":"♿","mens":"🚹","womens":"🚺","restroom":"🚻","baby_symbol":"🚼","wc":"🚾","passport_control":"🛂","customs":"🛃","baggage_claim":"🛄","left_luggage":"🛅","warning":"⚠️","children_crossing":"🚸","no_entry":"⛔","no_entry_sign":"🚫","no_bicycles":"🚳","no_smoking":"🚭","do_not_litter":"🚯","non-potable_water":"🚱","no_pedestrians":"🚷","no_mobile_phones":"📵","underage":"🔞","radioactive":"☢️","biohazard":"☣️","arrow_up":"⬆️","arrow_upper_right":"↗️","arrow_right":"➡️","arrow_lower_right":"↘️","arrow_down":"⬇️","arrow_lower_left":"↙️","arrow_left":"⬅️","arrow_upper_left":"↖️","arrow_up_down":"↕️","left_right_arrow":"↔️","leftwards_arrow_with_hook":"↩️","arrow_right_hook":"↪️","arrow_heading_up":"⤴️","arrow_heading_down":"⤵️","arrows_clockwise":"🔃","arrows_counterclockwise":"🔄","back":"🔙","end":"🔚","on":"🔛","soon":"🔜","top":"🔝","place_of_worship":"🛐","atom_symbol":"⚛️","om":"🕉️","star_of_david":"✡️","wheel_of_dharma":"☸️","yin_yang":"☯️","latin_cross":"✝️","orthodox_cross":"☦️","star_and_crescent":"☪️","peace_symbol":"☮️","menorah":"🕎","six_pointed_star":"🔯","aries":"♈","taurus":"♉","gemini":"♊","cancer":"♋","leo":"♌","virgo":"♍","libra":"♎","scorpius":"♏","sagittarius":"♐","capricorn":"♑","aquarius":"♒","pisces":"♓","ophiuchus":"⛎","twisted_rightwards_arrows":"🔀","repeat":"🔁","repeat_one":"🔂","arrow_forward":"▶️","fast_forward":"⏩","next_track_button":"⏭️","play_or_pause_button":"⏯️","arrow_backward":"◀️","rewind":"⏪","previous_track_button":"⏮️","arrow_up_small":"🔼","arrow_double_up":"⏫","arrow_down_small":"🔽","arrow_double_down":"⏬","pause_button":"⏸️","stop_button":"⏹️","record_button":"⏺️","eject_button":"⏏️","cinema":"🎦","low_brightness":"🔅","high_brightness":"🔆","signal_strength":"📶","vibration_mode":"📳","mobile_phone_off":"📴","female_sign":"♀️","male_sign":"♂️","transgender_symbol":"⚧️","heavy_multiplication_x":"✖️","heavy_plus_sign":"➕","heavy_minus_sign":"➖","heavy_division_sign":"➗","infinity":"♾️","bangbang":"‼️","interrobang":"⁉️","question":"❓","grey_question":"❔","grey_exclamation":"❕","exclamation":"❗","heavy_exclamation_mark":"❗","wavy_dash":"〰️","currency_exchange":"💱","heavy_dollar_sign":"💲","medical_symbol":"⚕️","recycle":"♻️","fleur_de_lis":"⚜️","trident":"🔱","name_badge":"📛","beginner":"🔰","o":"⭕","white_check_mark":"✅","ballot_box_with_check":"☑️","heavy_check_mark":"✔️","x":"❌","negative_squared_cross_mark":"❎","curly_loop":"➰","loop":"➿","part_alternation_mark":"〽️","eight_spoked_asterisk":"✳️","eight_pointed_black_star":"✴️","sparkle":"❇️","copyright":"©️","registered":"®️","tm":"™️","hash":"#️⃣","asterisk":"*️⃣","zero":"0️⃣","one":"1️⃣","two":"2️⃣","three":"3️⃣","four":"4️⃣","five":"5️⃣","six":"6️⃣","seven":"7️⃣","eight":"8️⃣","nine":"9️⃣","keycap_ten":"🔟","capital_abcd":"🔠","abcd":"🔡","symbols":"🔣","abc":"🔤","a":"🅰️","ab":"🆎","b":"🅱️","cl":"🆑","cool":"🆒","free":"🆓","information_source":"ℹ️","id":"🆔","m":"Ⓜ️","new":"🆕","ng":"🆖","o2":"🅾️","ok":"🆗","parking":"🅿️","sos":"🆘","up":"🆙","vs":"🆚","koko":"🈁","sa":"🈂️","ideograph_advantage":"🉐","accept":"🉑","congratulations":"㊗️","secret":"㊙️","u6e80":"🈵","red_circle":"🔴","orange_circle":"🟠","yellow_circle":"🟡","green_circle":"🟢","large_blue_circle":"🔵","purple_circle":"🟣","brown_circle":"🟤","black_circle":"⚫","white_circle":"⚪","red_square":"🟥","orange_square":"🟧","yellow_square":"🟨","green_square":"🟩","blue_square":"🟦","purple_square":"🟪","brown_square":"🟫","black_large_square":"⬛","white_large_square":"⬜","black_medium_square":"◼️","white_medium_square":"◻️","black_medium_small_square":"◾","white_medium_small_square":"◽","black_small_square":"▪️","white_small_square":"▫️","large_orange_diamond":"🔶","large_blue_diamond":"🔷","small_orange_diamond":"🔸","small_blue_diamond":"🔹","small_red_triangle":"🔺","small_red_triangle_down":"🔻","diamond_shape_with_a_dot_inside":"💠","radio_button":"🔘","white_square_button":"🔳","black_square_button":"🔲","checkered_flag":"🏁","triangular_flag_on_post":"🚩","crossed_flags":"🎌","black_flag":"🏴","white_flag":"🏳️","rainbow_flag":"🏳️‍🌈","transgender_flag":"🏳️‍⚧️","pirate_flag":"🏴‍☠️","ascension_island":"🇦🇨","andorra":"🇦🇩","united_arab_emirates":"🇦🇪","afghanistan":"🇦🇫","antigua_barbuda":"🇦🇬","anguilla":"🇦🇮","albania":"🇦🇱","armenia":"🇦🇲","angola":"🇦🇴","antarctica":"🇦🇶","argentina":"🇦🇷","american_samoa":"🇦🇸","austria":"🇦🇹","australia":"🇦🇺","aruba":"🇦🇼","aland_islands":"🇦🇽","azerbaijan":"🇦🇿","bosnia_herzegovina":"🇧🇦","barbados":"🇧🇧","bangladesh":"🇧🇩","belgium":"🇧🇪","burkina_faso":"🇧🇫","bulgaria":"🇧🇬","bahrain":"🇧🇭","burundi":"🇧🇮","benin":"🇧🇯","st_barthelemy":"🇧🇱","bermuda":"🇧🇲","brunei":"🇧🇳","bolivia":"🇧🇴","caribbean_netherlands":"🇧🇶","brazil":"🇧🇷","bahamas":"🇧🇸","bhutan":"🇧🇹","bouvet_island":"🇧🇻","botswana":"🇧🇼","belarus":"🇧🇾","belize":"🇧🇿","canada":"🇨🇦","cocos_islands":"🇨🇨","congo_kinshasa":"🇨🇩","central_african_republic":"🇨🇫","congo_brazzaville":"🇨🇬","switzerland":"🇨🇭","cote_divoire":"🇨🇮","cook_islands":"🇨🇰","chile":"🇨🇱","cameroon":"🇨🇲","cn":"🇨🇳","colombia":"🇨🇴","clipperton_island":"🇨🇵","costa_rica":"🇨🇷","cuba":"🇨🇺","cape_verde":"🇨🇻","curacao":"🇨🇼","christmas_island":"🇨🇽","cyprus":"🇨🇾","czech_republic":"🇨🇿","de":"🇩🇪","diego_garcia":"🇩🇬","djibouti":"🇩🇯","denmark":"🇩🇰","dominica":"🇩🇲","dominican_republic":"🇩🇴","algeria":"🇩🇿","ceuta_melilla":"🇪🇦","ecuador":"🇪🇨","estonia":"🇪🇪","egypt":"🇪🇬","western_sahara":"🇪🇭","eritrea":"🇪🇷","es":"🇪🇸","ethiopia":"🇪🇹","eu":"🇪🇺","european_union":"🇪🇺","finland":"🇫🇮","fiji":"🇫🇯","falkland_islands":"🇫🇰","micronesia":"🇫🇲","faroe_islands":"🇫🇴","fr":"🇫🇷","gabon":"🇬🇦","gb":"🇬🇧","uk":"🇬🇧","grenada":"🇬🇩","georgia":"🇬🇪","french_guiana":"🇬🇫","guernsey":"🇬🇬","ghana":"🇬🇭","gibraltar":"🇬🇮","greenland":"🇬🇱","gambia":"🇬🇲","guinea":"🇬🇳","guadeloupe":"🇬🇵","equatorial_guinea":"🇬🇶","greece":"🇬🇷","south_georgia_south_sandwich_islands":"🇬🇸","guatemala":"🇬🇹","guam":"🇬🇺","guinea_bissau":"🇬🇼","guyana":"🇬🇾","hong_kong":"🇭🇰","heard_mcdonald_islands":"🇭🇲","honduras":"🇭🇳","croatia":"🇭🇷","haiti":"🇭🇹","hungary":"🇭🇺","canary_islands":"🇮🇨","indonesia":"🇮🇩","ireland":"🇮🇪","israel":"🇮🇱","isle_of_man":"🇮🇲","india":"🇮🇳","british_indian_ocean_territory":"🇮🇴","iraq":"🇮🇶","iran":"🇮🇷","iceland":"🇮🇸","it":"🇮🇹","jersey":"🇯🇪","jamaica":"🇯🇲","jordan":"🇯🇴","jp":"🇯🇵","kenya":"🇰🇪","kyrgyzstan":"🇰🇬","cambodia":"🇰🇭","kiribati":"🇰🇮","comoros":"🇰🇲","st_kitts_nevis":"🇰🇳","north_korea":"🇰🇵","kr":"🇰🇷","kuwait":"🇰🇼","cayman_islands":"🇰🇾","kazakhstan":"🇰🇿","laos":"🇱🇦","lebanon":"🇱🇧","st_lucia":"🇱🇨","liechtenstein":"🇱🇮","sri_lanka":"🇱🇰","liberia":"🇱🇷","lesotho":"🇱🇸","lithuania":"🇱🇹","luxembourg":"🇱🇺","latvia":"🇱🇻","libya":"🇱🇾","morocco":"🇲🇦","monaco":"🇲🇨","moldova":"🇲🇩","montenegro":"🇲🇪","st_martin":"🇲🇫","madagascar":"🇲🇬","marshall_islands":"🇲🇭","macedonia":"🇲🇰","mali":"🇲🇱","myanmar":"🇲🇲","mongolia":"🇲🇳","macau":"🇲🇴","northern_mariana_islands":"🇲🇵","martinique":"🇲🇶","mauritania":"🇲🇷","montserrat":"🇲🇸","malta":"🇲🇹","mauritius":"🇲🇺","maldives":"🇲🇻","malawi":"🇲🇼","mexico":"🇲🇽","malaysia":"🇲🇾","mozambique":"🇲🇿","namibia":"🇳🇦","new_caledonia":"🇳🇨","niger":"🇳🇪","norfolk_island":"🇳🇫","nigeria":"🇳🇬","nicaragua":"🇳🇮","netherlands":"🇳🇱","norway":"🇳🇴","nepal":"🇳🇵","nauru":"🇳🇷","niue":"🇳🇺","new_zealand":"🇳🇿","oman":"🇴🇲","panama":"🇵🇦","peru":"🇵🇪","french_polynesia":"🇵🇫","papua_new_guinea":"🇵🇬","philippines":"🇵🇭","pakistan":"🇵🇰","poland":"🇵🇱","st_pierre_miquelon":"🇵🇲","pitcairn_islands":"🇵🇳","puerto_rico":"🇵🇷","palestinian_territories":"🇵🇸","portugal":"🇵🇹","palau":"🇵🇼","paraguay":"🇵🇾","qatar":"🇶🇦","reunion":"🇷🇪","romania":"🇷🇴","serbia":"🇷🇸","ru":"🇷🇺","rwanda":"🇷🇼","saudi_arabia":"🇸🇦","solomon_islands":"🇸🇧","seychelles":"🇸🇨","sudan":"🇸🇩","sweden":"🇸🇪","singapore":"🇸🇬","st_helena":"🇸🇭","slovenia":"🇸🇮","svalbard_jan_mayen":"🇸🇯","slovakia":"🇸🇰","sierra_leone":"🇸🇱","san_marino":"🇸🇲","senegal":"🇸🇳","somalia":"🇸🇴","suriname":"🇸🇷","south_sudan":"🇸🇸","sao_tome_principe":"🇸🇹","el_salvador":"🇸🇻","sint_maarten":"🇸🇽","syria":"🇸🇾","swaziland":"🇸🇿","tristan_da_cunha":"🇹🇦","turks_caicos_islands":"🇹🇨","chad":"🇹🇩","french_southern_territories":"🇹🇫","togo":"🇹🇬","thailand":"🇹🇭","tajikistan":"🇹🇯","tokelau":"🇹🇰","timor_leste":"🇹🇱","turkmenistan":"🇹🇲","tunisia":"🇹🇳","tonga":"🇹🇴","tr":"🇹🇷","trinidad_tobago":"🇹🇹","tuvalu":"🇹🇻","taiwan":"🇹🇼","tanzania":"🇹🇿","ukraine":"🇺🇦","uganda":"🇺🇬","us_outlying_islands":"🇺🇲","united_nations":"🇺🇳","us":"🇺🇸","uruguay":"🇺🇾","uzbekistan":"🇺🇿","vatican_city":"🇻🇦","st_vincent_grenadines":"🇻🇨","venezuela":"🇻🇪","british_virgin_islands":"🇻🇬","us_virgin_islands":"🇻🇮","vietnam":"🇻🇳","vanuatu":"🇻🇺","wallis_futuna":"🇼🇫","samoa":"🇼🇸","kosovo":"🇽🇰","yemen":"🇾🇪","mayotte":"🇾🇹","south_africa":"🇿🇦","zambia":"🇿🇲","zimbabwe":"🇿🇼","england":"🏴󠁧󠁢󠁥󠁮󠁧󠁿","scotland":"🏴󠁧󠁢󠁳󠁣󠁴󠁿","wales":"🏴󠁧󠁢󠁷󠁬󠁳󠁿"}')},function(a,e,n){"use strict";a.exports={angry:[">:(",">:-("],blush:[':")',':-")'],broken_heart:["0&&!t.test(c[i-1]))return;if(i+o.lengths&&((_=new r("text","",0)).content=a.slice(s,i),l.push(_)),(_=new r("emoji","",0)).markup=m,_.content=e[m],l.push(_),s=i+o.length})),s=0;e--)"link_open"!==(t=_[e]).type&&"link_close"!==t.type||"auto"===t.info&&(c-=t.nesting),"text"===t.type&&0===c&&o.test(t.content)&&(l[n].children=_=r(_,e,s(t.content,t.level,a.Token)))}}},function(a,e,n){"use strict";a.exports=function(a){var e,n=a.defs;a.enabled.length&&(n=Object.keys(n).reduce((function(e,o){return a.enabled.indexOf(o)>=0&&(e[o]=n[o]),e}),{})),e=Object.keys(a.shortcuts).reduce((function(e,o){return n[o]?Array.isArray(a.shortcuts[o])?(a.shortcuts[o].forEach((function(a){e[a]=o})),e):(e[a.shortcuts[o]]=o,e):e}),{});var o,i=Object.keys(n);o=0===i.length?"^$":i.map((function(a){return":"+a+":"})).concat(Object.keys(e)).sort().reverse().map((function(a){return a.replace(/[.?*+^$[\]\\(){}|-]/g,"\\$&")})).join("|");var r=RegExp(o),_=RegExp(o,"g");return{defs:n,shortcuts:e,scanRE:r,replaceRE:_}}}]); \ No newline at end of file diff --git a/extensions/notebook-markdown-extensions/notebook-out/katex.js b/extensions/notebook-markdown-extensions/notebook-out/katex.js deleted file mode 100644 index 117a960f9bf..00000000000 --- a/extensions/notebook-markdown-extensions/notebook-out/katex.js +++ /dev/null @@ -1 +0,0 @@ -!function(t){var e={};function r(n){if(e[n])return e[n].exports;var a=e[n]={i:n,l:!1,exports:{}};return t[n].call(a.exports,a,a.exports,r),a.l=!0,a.exports}r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)r.d(n,a,function(e){return t[e]}.bind(null,a));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=document.currentScript.src.replace("notebook-out/katex.js","")+"node_modules/katex/dist/katex.min.css",a=document.createElement("link");a.rel="stylesheet",a.href=n,document.head.append(a),function(){const t=r(1);"undefined"!=typeof extendMarkdownIt&&extendMarkdownIt(e=>{e.use(t)})}()},function(t,e,r){"use strict";var n=r(2);function a(t,e){var r,n,a=t.posMax,i=!0,o=!0;return r=e>0?t.src.charCodeAt(e-1):-1,n=e+1<=a?t.src.charCodeAt(e+1):-1,(32===r||9===r||n>=48&&n<=57)&&(o=!1),32!==n&&9!==n||(i=!1),{can_open:i,can_close:o}}function i(t,e){var r,n,i,o;if("$"!==t.src[t.pos])return!1;if(!a(t,t.pos).can_open)return e||(t.pending+="$"),t.pos+=1,!0;for(n=r=t.pos+1;-1!==(n=t.src.indexOf("$",n));){for(o=n-1;"\\"===t.src[o];)o-=1;if((n-o)%2==1)break;n+=1}return-1===n?(e||(t.pending+="$"),t.pos=r,!0):n-r==0?(e||(t.pending+="$$"),t.pos=r+1,!0):a(t,n).can_close?(e||((i=t.push("math_inline","math",0)).markup="$",i.content=t.src.slice(r,n)),t.pos=n+1,!0):(e||(t.pending+="$"),t.pos=r,!0)}function o(t,e,r,n){var a,i,o,s,l,h=!1,m=t.bMarks[e]+t.tShift[e],c=t.eMarks[e];if(m+2>c)return!1;if("$$"!==t.src.slice(m,m+2))return!1;if(m+=2,a=t.src.slice(m,c),n)return!0;for("$$"===a.trim().slice(-2)&&(a=a.trim().slice(0,-2),h=!0),o=e;!h&&!(++o>=r)&&!((m=t.bMarks[o]+t.tShift[o])<(c=t.eMarks[o])&&t.tShift[o]/g,">").replace(/"/g,""").replace(/'/g,"'")}t.exports=function(t,e){e=e||{};t.inline.ruler.after("escape","math_inline",i),t.block.ruler.after("blockquote","math_block",o,{alt:["paragraph","reference","blockquote","list"]}),t.renderer.rules.math_inline=function(t,r){return function(t){e.displayMode=!1;try{return n.renderToString(t,e)}catch(r){return e.throwOnError&&console.log(r),`${s(t)}`}}(t[r].content)},t.renderer.rules.math_block=function(t,r){return function(t){e.displayMode=!0;try{return"

"+n.renderToString(t,e)+"

"}catch(r){return e.throwOnError&&console.log(r),`

${s(t)}

`}}(t[r].content)+"\n"}}},function(t,e,r){var n;"undefined"!=typeof self&&self,n=function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var a=e[n]={i:n,l:!1,exports:{}};return t[n].call(a.exports,a,a.exports,r),a.l=!0,a.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)r.d(n,a,function(e){return t[e]}.bind(null,a));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=1)}([function(t,e,r){},function(t,e,r){"use strict";r.r(e),r(0);var n=function(){function t(t,e,r){this.lexer=void 0,this.start=void 0,this.end=void 0,this.lexer=t,this.start=e,this.end=r}return t.range=function(e,r){return r?e&&e.loc&&r.loc&&e.loc.lexer===r.loc.lexer?new t(e.loc.lexer,e.loc.start,r.loc.end):null:e&&e.loc},t}(),a=function(){function t(t,e){this.text=void 0,this.loc=void 0,this.noexpand=void 0,this.treatAsRelax=void 0,this.text=t,this.loc=e}return t.prototype.range=function(e,r){return new t(r,n.range(this,e))},t}(),i=function t(e,r){this.position=void 0;var n,a="KaTeX parse error: "+e,i=r&&r.loc;if(i&&i.start<=i.end){var o=i.lexer.input;n=i.start;var s=i.end;n===o.length?a+=" at end of input: ":a+=" at position "+(n+1)+": ";var l=o.slice(n,s).replace(/[^]/g,"$&̲");a+=(n>15?"…"+o.slice(n-15,n):o.slice(0,n))+l+(s+15":">","<":"<",'"':""","'":"'"},h=/[&><"']/g,m=function t(e){return"ordgroup"===e.type||"color"===e.type?1===e.body.length?t(e.body[0]):e:"font"===e.type?t(e.body):e},c={contains:function(t,e){return-1!==t.indexOf(e)},deflt:function(t,e){return void 0===t?e:t},escape:function(t){return String(t).replace(h,(function(t){return l[t]}))},hyphenate:function(t){return t.replace(s,"-$1").toLowerCase()},getBaseElem:m,isCharacterBox:function(t){var e=m(t);return"mathord"===e.type||"textord"===e.type||"atom"===e.type},protocolFromUrl:function(t){var e=/^\s*([^\\/#]*?)(?::|�*58|�*3a)/i.exec(t);return null!=e?e[1]:"_relative"}},u=function(){function t(t){this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,t=t||{},this.displayMode=c.deflt(t.displayMode,!1),this.output=c.deflt(t.output,"htmlAndMathml"),this.leqno=c.deflt(t.leqno,!1),this.fleqn=c.deflt(t.fleqn,!1),this.throwOnError=c.deflt(t.throwOnError,!0),this.errorColor=c.deflt(t.errorColor,"#cc0000"),this.macros=t.macros||{},this.minRuleThickness=Math.max(0,c.deflt(t.minRuleThickness,0)),this.colorIsTextColor=c.deflt(t.colorIsTextColor,!1),this.strict=c.deflt(t.strict,"warn"),this.trust=c.deflt(t.trust,!1),this.maxSize=Math.max(0,c.deflt(t.maxSize,1/0)),this.maxExpand=Math.max(0,c.deflt(t.maxExpand,1e3)),this.globalGroup=c.deflt(t.globalGroup,!1)}var e=t.prototype;return e.reportNonstrict=function(t,e,r){var n=this.strict;if("function"==typeof n&&(n=n(t,e,r)),n&&"ignore"!==n){if(!0===n||"error"===n)throw new o("LaTeX-incompatible input and strict mode is set to 'error': "+e+" ["+t+"]",r);"warn"===n?"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+e+" ["+t+"]"):"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+n+"': "+e+" ["+t+"]")}},e.useStrictBehavior=function(t,e,r){var n=this.strict;if("function"==typeof n)try{n=n(t,e,r)}catch(t){n="error"}return!(!n||"ignore"===n||!0!==n&&"error"!==n&&("warn"===n?("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+e+" ["+t+"]"),1):("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+n+"': "+e+" ["+t+"]"),1)))},e.isTrusted=function(t){t.url&&!t.protocol&&(t.protocol=c.protocolFromUrl(t.url));var e="function"==typeof this.trust?this.trust(t):this.trust;return Boolean(e)},t}(),p=function(){function t(t,e,r){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=t,this.size=e,this.cramped=r}var e=t.prototype;return e.sup=function(){return d[f[this.id]]},e.sub=function(){return d[g[this.id]]},e.fracNum=function(){return d[x[this.id]]},e.fracDen=function(){return d[v[this.id]]},e.cramp=function(){return d[b[this.id]]},e.text=function(){return d[y[this.id]]},e.isTight=function(){return this.size>=2},t}(),d=[new p(0,0,!1),new p(1,0,!0),new p(2,1,!1),new p(3,1,!0),new p(4,2,!1),new p(5,2,!0),new p(6,3,!1),new p(7,3,!0)],f=[4,5,4,5,6,7,6,7],g=[5,5,5,5,7,7,7,7],x=[2,3,4,5,6,7,6,7],v=[3,3,5,5,7,7,7,7],b=[1,1,3,3,5,5,7,7],y=[0,1,2,3,2,3,2,3],w={DISPLAY:d[0],TEXT:d[2],SCRIPT:d[4],SCRIPTSCRIPT:d[6]},k=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}],S=[];function M(t){for(var e=0;e=S[e]&&t<=S[e+1])return!0;return!1}k.forEach((function(t){return t.blocks.forEach((function(t){return S.push.apply(S,t)}))}));var z={leftParenInner:"M291 0 H417 V300 H291 z",rightParenInner:"M457 0 H583 V300 H457 z",doubleleftarrow:"M262 157\nl10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3\n 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28\n 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5\nc2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5\n 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87\n-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7\n-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z\nm8 0v40h399730v-40zm0 194v40h399730v-40z",doublerightarrow:"M399738 392l\n-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5\n 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88\n-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68\n-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18\n-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782\nc-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3\n-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z",leftarrow:"M400000 241H110l3-3c68.7-52.7 113.7-120\n 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8\n-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247\nc-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208\n 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3\n 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202\n l-3-3h399890zM100 241v40h399900v-40z",leftbrace:"M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117\n-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7\n 5-6 9-10 13-.7 1-7.3 1-20 1H6z",leftbraceunder:"M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13\n 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688\n 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7\n-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z",leftgroup:"M400000 80\nH435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0\n 435 0h399565z",leftgroupunder:"M400000 262\nH435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219\n 435 219h399565z",leftharpoon:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3\n-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5\n-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7\n-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z",leftharpoonplus:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5\n 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3\n-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7\n-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z\nm0 0v40h400000v-40z",leftharpoondown:"M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333\n 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5\n 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667\n-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z",leftharpoondownplus:"M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12\n 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7\n-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0\nv40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z",lefthook:"M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5\n-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3\n-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21\n 71.5 23h399859zM103 281v-40h399897v40z",leftlinesegment:"M40 281 V428 H0 V94 H40 V241 H400000 v40z\nM40 281 V428 H0 V94 H40 V241 H400000 v40z",leftmapsto:"M40 281 V448H0V74H40V241H400000v40z\nM40 281 V448H0V74H40V241H400000v40z",leftToFrom:"M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23\n-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8\nc28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3\n 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z",longequal:"M0 50 h400000 v40H0z m0 194h40000v40H0z\nM0 50 h400000 v40H0z m0 194h40000v40H0z",midbrace:"M200428 334\nc-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14\n-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7\n 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11\n 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z",midbraceunder:"M199572 214\nc100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14\n 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3\n 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0\n-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z",oiintSize1:"M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6\n-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z\nm368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8\n60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z",oiintSize2:"M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8\n-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z\nm502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2\nc0 110 84 276 504 276s502.4-166 502.4-276z",oiiintSize1:"M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6\n-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z\nm525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0\n85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z",oiiintSize2:"M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8\n-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z\nm770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1\nc0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z",rightarrow:"M0 241v40h399891c-47.3 35.3-84 78-110 128\n-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20\n 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7\n 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85\n-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n 151.7 139 205zm0 0v40h399900v-40z",rightbrace:"M400000 542l\n-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5\ns-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1\nc124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z",rightbraceunder:"M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3\n 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237\n-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z",rightgroup:"M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0\n 3-1 3-3v-38c-76-158-257-219-435-219H0z",rightgroupunder:"M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18\n 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z",rightharpoon:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3\n-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2\n-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58\n 69.2 92 94.5zm0 0v40h399900v-40z",rightharpoonplus:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11\n-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7\n 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z\nm0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z",rightharpoondown:"M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8\n 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5\n-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95\n-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z",rightharpoondownplus:"M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8\n 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3\n 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3\n-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z\nm0-194v40h400000v-40zm0 0v40h400000v-40z",righthook:"M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3\n 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0\n-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21\n 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z",rightlinesegment:"M399960 241 V94 h40 V428 h-40 V281 H0 v-40z\nM399960 241 V94 h40 V428 h-40 V281 H0 v-40z",rightToFrom:"M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23\n 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32\n-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142\n-167z M100 147v40h399900v-40zM0 341v40h399900v-40z",twoheadleftarrow:"M0 167c68 40\n 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69\n-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3\n-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19\n-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101\n 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z",twoheadrightarrow:"M400000 167\nc-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3\n 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42\n 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333\n-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70\n 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z",tilde1:"M200 55.538c-77 0-168 73.953-177 73.953-3 0-7\n-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0\n 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0\n 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128\n-68.267.847-113-73.952-191-73.952z",tilde2:"M344 55.266c-142 0-300.638 81.316-311.5 86.418\n-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9\n 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114\nc1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751\n 181.476 676 181.476c-149 0-189-126.21-332-126.21z",tilde3:"M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457\n-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0\n 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697\n 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696\n -338 0-409-156.573-744-156.573z",tilde4:"M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345\n-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409\n 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9\n 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409\n -175.236-744-175.236z",vec:"M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5\n3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11\n10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63\n-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1\n-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59\nH213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359\nc-16-25.333-24-45-24-59z",widehat1:"M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22\nc-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z",widehat2:"M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat3:"M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat4:"M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widecheck1:"M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1,\n-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z",widecheck2:"M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck3:"M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck4:"M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",baraboveleftarrow:"M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202\nc4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5\nc-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130\ns-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47\n121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6\ns2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11\nc0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z\nM100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z",rightarrowabovebar:"M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32\n-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0\n13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39\n-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5\n-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z",baraboveshortleftharpoon:"M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17\nc2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21\nc-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40\nc-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z\nM0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z",rightharpoonaboveshortbar:"M0,241 l0,40c399126,0,399993,0,399993,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z",shortbaraboveleftharpoon:"M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9,\n1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7,\n-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z\nM93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z",shortrightharpoonabovebar:"M53,241l0,40c398570,0,399437,0,399437,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z"},A=function(){function t(t){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=t,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}var e=t.prototype;return e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){for(var t=document.createDocumentFragment(),e=0;e"},N=function(){function t(t,e,r,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,B.call(this,t,r,n),this.children=e||[]}var e=t.prototype;return e.setAttribute=function(t,e){this.attributes[t]=e},e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){return C.call(this,"span")},e.toMarkup=function(){return q.call(this,"span")},t}(),I=function(){function t(t,e,r,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,B.call(this,e,n),this.children=r||[],this.setAttribute("href",t)}var e=t.prototype;return e.setAttribute=function(t,e){this.attributes[t]=e},e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){return C.call(this,"a")},e.toMarkup=function(){return q.call(this,"a")},t}(),O=function(){function t(t,e,r){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=e,this.src=t,this.classes=["mord"],this.style=r}var e=t.prototype;return e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){var t=document.createElement("img");for(var e in t.src=this.src,t.alt=this.alt,t.className="mord",this.style)this.style.hasOwnProperty(e)&&(t.style[e]=this.style[e]);return t},e.toMarkup=function(){var t=""+this.alt+"=a[0]&&t<=a[1])return r.name}return null}(this.text.charCodeAt(0));l&&this.classes.push(l+"_fallback"),/[îïíì]/.test(this.text)&&(this.text=R[this.text])}var e=t.prototype;return e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){var t=document.createTextNode(this.text),e=null;for(var r in this.italic>0&&((e=document.createElement("span")).style.marginRight=this.italic+"em"),this.classes.length>0&&((e=e||document.createElement("span")).className=T(this.classes)),this.style)this.style.hasOwnProperty(r)&&((e=e||document.createElement("span")).style[r]=this.style[r]);return e?(e.appendChild(t),e):t},e.toMarkup=function(){var t=!1,e="0&&(r+="margin-right:"+this.italic+"em;"),this.style)this.style.hasOwnProperty(n)&&(r+=c.hyphenate(n)+":"+this.style[n]+";");r&&(t=!0,e+=' style="'+c.escape(r)+'"');var a=c.escape(this.text);return t?(e+=">",e+=a,e+=""):a},t}(),L=function(){function t(t,e){this.children=void 0,this.attributes=void 0,this.children=t||[],this.attributes=e||{}}var e=t.prototype;return e.toNode=function(){var t=document.createElementNS("http://www.w3.org/2000/svg","svg");for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&t.setAttribute(e,this.attributes[e]);for(var r=0;r":""},t}(),D=function(){function t(t){this.attributes=void 0,this.attributes=t||{}}var e=t.prototype;return e.toNode=function(){var t=document.createElementNS("http://www.w3.org/2000/svg","line");for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&t.setAttribute(e,this.attributes[e]);return t},e.toMarkup=function(){var t="","\\gt",!0),j("math",Z,et,"∈","\\in",!0),j("math",Z,et,"","\\@not"),j("math",Z,et,"⊂","\\subset",!0),j("math",Z,et,"⊃","\\supset",!0),j("math",Z,et,"⊆","\\subseteq",!0),j("math",Z,et,"⊇","\\supseteq",!0),j("math",K,et,"⊈","\\nsubseteq",!0),j("math",K,et,"⊉","\\nsupseteq",!0),j("math",Z,et,"⊨","\\models"),j("math",Z,et,"←","\\leftarrow",!0),j("math",Z,et,"≤","\\le"),j("math",Z,et,"≤","\\leq",!0),j("math",Z,et,"<","\\lt",!0),j("math",Z,et,"→","\\rightarrow",!0),j("math",Z,et,"→","\\to"),j("math",K,et,"≱","\\ngeq",!0),j("math",K,et,"≰","\\nleq",!0),j("math",Z,"spacing"," ","\\ "),j("math",Z,"spacing"," ","~"),j("math",Z,"spacing"," ","\\space"),j("math",Z,"spacing"," ","\\nobreakspace"),j("text",Z,"spacing"," ","\\ "),j("text",Z,"spacing"," "," "),j("text",Z,"spacing"," ","~"),j("text",Z,"spacing"," ","\\space"),j("text",Z,"spacing"," ","\\nobreakspace"),j("math",Z,"spacing",null,"\\nobreak"),j("math",Z,"spacing",null,"\\allowbreak"),j("math",Z,"punct",",",","),j("math",Z,"punct",";",";"),j("math",K,J,"⊼","\\barwedge",!0),j("math",K,J,"⊻","\\veebar",!0),j("math",Z,J,"⊙","\\odot",!0),j("math",Z,J,"⊕","\\oplus",!0),j("math",Z,J,"⊗","\\otimes",!0),j("math",Z,"textord","∂","\\partial",!0),j("math",Z,J,"⊘","\\oslash",!0),j("math",K,J,"⊚","\\circledcirc",!0),j("math",K,J,"⊡","\\boxdot",!0),j("math",Z,J,"△","\\bigtriangleup"),j("math",Z,J,"▽","\\bigtriangledown"),j("math",Z,J,"†","\\dagger"),j("math",Z,J,"⋄","\\diamond"),j("math",Z,J,"⋆","\\star"),j("math",Z,J,"◃","\\triangleleft"),j("math",Z,J,"▹","\\triangleright"),j("math",Z,"open","{","\\{"),j("text",Z,"textord","{","\\{"),j("text",Z,"textord","{","\\textbraceleft"),j("math",Z,"close","}","\\}"),j("text",Z,"textord","}","\\}"),j("text",Z,"textord","}","\\textbraceright"),j("math",Z,"open","{","\\lbrace"),j("math",Z,"close","}","\\rbrace"),j("math",Z,"open","[","\\lbrack",!0),j("text",Z,"textord","[","\\lbrack",!0),j("math",Z,"close","]","\\rbrack",!0),j("text",Z,"textord","]","\\rbrack",!0),j("math",Z,"open","(","\\lparen",!0),j("math",Z,"close",")","\\rparen",!0),j("text",Z,"textord","<","\\textless",!0),j("text",Z,"textord",">","\\textgreater",!0),j("math",Z,"open","⌊","\\lfloor",!0),j("math",Z,"close","⌋","\\rfloor",!0),j("math",Z,"open","⌈","\\lceil",!0),j("math",Z,"close","⌉","\\rceil",!0),j("math",Z,"textord","\\","\\backslash"),j("math",Z,"textord","∣","|"),j("math",Z,"textord","∣","\\vert"),j("text",Z,"textord","|","\\textbar",!0),j("math",Z,"textord","∥","\\|"),j("math",Z,"textord","∥","\\Vert"),j("text",Z,"textord","∥","\\textbardbl"),j("text",Z,"textord","~","\\textasciitilde"),j("text",Z,"textord","\\","\\textbackslash"),j("text",Z,"textord","^","\\textasciicircum"),j("math",Z,et,"↑","\\uparrow",!0),j("math",Z,et,"⇑","\\Uparrow",!0),j("math",Z,et,"↓","\\downarrow",!0),j("math",Z,et,"⇓","\\Downarrow",!0),j("math",Z,et,"↕","\\updownarrow",!0),j("math",Z,et,"⇕","\\Updownarrow",!0),j("math",Z,tt,"∐","\\coprod"),j("math",Z,tt,"⋁","\\bigvee"),j("math",Z,tt,"⋀","\\bigwedge"),j("math",Z,tt,"⨄","\\biguplus"),j("math",Z,tt,"⋂","\\bigcap"),j("math",Z,tt,"⋃","\\bigcup"),j("math",Z,tt,"∫","\\int"),j("math",Z,tt,"∫","\\intop"),j("math",Z,tt,"∬","\\iint"),j("math",Z,tt,"∭","\\iiint"),j("math",Z,tt,"∏","\\prod"),j("math",Z,tt,"∑","\\sum"),j("math",Z,tt,"⨂","\\bigotimes"),j("math",Z,tt,"⨁","\\bigoplus"),j("math",Z,tt,"⨀","\\bigodot"),j("math",Z,tt,"∮","\\oint"),j("math",Z,tt,"⨆","\\bigsqcup"),j("math",Z,tt,"∫","\\smallint"),j("text",Z,"inner","…","\\textellipsis"),j("math",Z,"inner","…","\\mathellipsis"),j("text",Z,"inner","…","\\ldots",!0),j("math",Z,"inner","…","\\ldots",!0),j("math",Z,"inner","⋯","\\@cdots",!0),j("math",Z,"inner","⋱","\\ddots",!0),j("math",Z,"textord","⋮","\\varvdots"),j("math",Z,"accent-token","ˊ","\\acute"),j("math",Z,"accent-token","ˋ","\\grave"),j("math",Z,"accent-token","¨","\\ddot"),j("math",Z,"accent-token","~","\\tilde"),j("math",Z,"accent-token","ˉ","\\bar"),j("math",Z,"accent-token","˘","\\breve"),j("math",Z,"accent-token","ˇ","\\check"),j("math",Z,"accent-token","^","\\hat"),j("math",Z,"accent-token","⃗","\\vec"),j("math",Z,"accent-token","˙","\\dot"),j("math",Z,"accent-token","˚","\\mathring"),j("math",Z,Q,"","\\@imath"),j("math",Z,Q,"","\\@jmath"),j("math",Z,"textord","ı","ı"),j("math",Z,"textord","ȷ","ȷ"),j("text",Z,"textord","ı","\\i",!0),j("text",Z,"textord","ȷ","\\j",!0),j("text",Z,"textord","ß","\\ss",!0),j("text",Z,"textord","æ","\\ae",!0),j("text",Z,"textord","œ","\\oe",!0),j("text",Z,"textord","ø","\\o",!0),j("text",Z,"textord","Æ","\\AE",!0),j("text",Z,"textord","Œ","\\OE",!0),j("text",Z,"textord","Ø","\\O",!0),j("text",Z,"accent-token","ˊ","\\'"),j("text",Z,"accent-token","ˋ","\\`"),j("text",Z,"accent-token","ˆ","\\^"),j("text",Z,"accent-token","˜","\\~"),j("text",Z,"accent-token","ˉ","\\="),j("text",Z,"accent-token","˘","\\u"),j("text",Z,"accent-token","˙","\\."),j("text",Z,"accent-token","˚","\\r"),j("text",Z,"accent-token","ˇ","\\v"),j("text",Z,"accent-token","¨",'\\"'),j("text",Z,"accent-token","˝","\\H"),j("text",Z,"accent-token","◯","\\textcircled");var rt={"--":!0,"---":!0,"``":!0,"''":!0};j("text",Z,"textord","–","--",!0),j("text",Z,"textord","–","\\textendash"),j("text",Z,"textord","—","---",!0),j("text",Z,"textord","—","\\textemdash"),j("text",Z,"textord","‘","`",!0),j("text",Z,"textord","‘","\\textquoteleft"),j("text",Z,"textord","’","'",!0),j("text",Z,"textord","’","\\textquoteright"),j("text",Z,"textord","“","``",!0),j("text",Z,"textord","“","\\textquotedblleft"),j("text",Z,"textord","”","''",!0),j("text",Z,"textord","”","\\textquotedblright"),j("math",Z,"textord","°","\\degree",!0),j("text",Z,"textord","°","\\degree"),j("text",Z,"textord","°","\\textdegree",!0),j("math",Z,"textord","£","\\pounds"),j("math",Z,"textord","£","\\mathsterling",!0),j("text",Z,"textord","£","\\pounds"),j("text",Z,"textord","£","\\textsterling",!0),j("math",K,"textord","✠","\\maltese"),j("text",K,"textord","✠","\\maltese");for(var nt=0;nt<'0123456789/@."'.length;nt++){var at='0123456789/@."'.charAt(nt);j("math",Z,"textord",at,at)}for(var it=0;it<'0123456789!@*()-=+";:?/.,'.length;it++){var ot='0123456789!@*()-=+";:?/.,'.charAt(it);j("text",Z,"textord",ot,ot)}for(var st="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",lt=0;lt=5?0:t>=3?1:2]){var r=_[e]={cssEmPerMu:V.quad[e]/18};for(var n in V)V.hasOwnProperty(n)&&(r[n]=V[n][e])}return _[e]}(this.size)),this._fontMetrics},e.getColor=function(){return this.phantom?"transparent":this.color},t}();kt.BASESIZE=6;var St=kt,Mt={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:1.00375,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:1.00375},zt={ex:!0,em:!0,mu:!0},At=function(t){return"string"!=typeof t&&(t=t.unit),t in Mt||t in zt||"ex"===t},Tt=function(t,e){var r;if(t.unit in Mt)r=Mt[t.unit]/e.fontMetrics().ptPerEm/e.sizeMultiplier;else if("mu"===t.unit)r=e.fontMetrics().cssEmPerMu;else{var n;if(n=e.style.isTight()?e.havingStyle(e.style.text()):e,"ex"===t.unit)r=n.fontMetrics().xHeight;else{if("em"!==t.unit)throw new o("Invalid unit: '"+t.unit+"'");r=n.fontMetrics().quad}n!==e&&(r*=n.sizeMultiplier/e.sizeMultiplier)}return Math.min(t.number*r,e.maxSize)},Bt=function(t,e,r){return X[r][t]&&X[r][t].replace&&(t=X[r][t].replace),{value:t,metrics:G(t,e,r)}},Ct=function(t,e,r,n,a){var i,o=Bt(t,e,r),s=o.metrics;if(t=o.value,s){var l=s.italic;("text"===r||n&&"mathit"===n.font)&&(l=0),i=new E(t,s.height,s.depth,l,s.skew,s.width,a)}else"undefined"!=typeof console&&console.warn("No character metrics for '"+t+"' in style '"+e+"' and mode '"+r+"'"),i=new E(t,0,0,0,0,0,a);if(n){i.maxFontSize=n.sizeMultiplier,n.style.isTight()&&i.classes.push("mtight");var h=n.getColor();h&&(i.style.color=h)}return i},qt=function(t,e){if(T(t.classes)!==T(e.classes)||t.skew!==e.skew||t.maxFontSize!==e.maxFontSize)return!1;for(var r in t.style)if(t.style.hasOwnProperty(r)&&t.style[r]!==e.style[r])return!1;for(var n in e.style)if(e.style.hasOwnProperty(n)&&t.style[n]!==e.style[n])return!1;return!0},Nt=function(t){for(var e=0,r=0,n=0,a=0;ae&&(e=i.height),i.depth>r&&(r=i.depth),i.maxFontSize>n&&(n=i.maxFontSize)}t.height=e,t.depth=r,t.maxFontSize=n},It=function(t,e,r,n){var a=new N(t,e,r,n);return Nt(a),a},Ot=function(t,e,r,n){return new N(t,e,r,n)},Rt=function(t){var e=new A(t);return Nt(e),e},Et=function(t,e,r){var n="";switch(t){case"amsrm":n="AMS";break;case"textrm":n="Main";break;case"textsf":n="SansSerif";break;case"texttt":n="Typewriter";break;default:n=t}return n+"-"+("textbf"===e&&"textit"===r?"BoldItalic":"textbf"===e?"Bold":"textit"===e?"Italic":"Regular")},Lt={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathnormal:{variant:"italic",fontName:"Math-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},Pt={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659],leftParenInner:["leftParenInner",.875,.3],rightParenInner:["rightParenInner",.875,.3]},Dt={fontMap:Lt,makeSymbol:Ct,mathsym:function(t,e,r,n){return void 0===n&&(n=[]),"boldsymbol"===r.font&&Bt(t,"Main-Bold",e).metrics?Ct(t,"Main-Bold",e,r,n.concat(["mathbf"])):"\\"===t||"main"===X[e][t].font?Ct(t,"Main-Regular",e,r,n):Ct(t,"AMS-Regular",e,r,n.concat(["amsrm"]))},makeSpan:It,makeSvgSpan:Ot,makeLineSpan:function(t,e,r){var n=It([t],[],e);return n.height=Math.max(r||e.fontMetrics().defaultRuleThickness,e.minRuleThickness),n.style.borderBottomWidth=n.height+"em",n.maxFontSize=1,n},makeAnchor:function(t,e,r,n){var a=new I(t,e,r,n);return Nt(a),a},makeFragment:Rt,wrapFragment:function(t,e){return t instanceof A?It([],[t],e):t},makeVList:function(t,e){for(var r=function(t){if("individualShift"===t.positionType){for(var e=t.children,r=[e[0]],n=-e[0].shift-e[0].elem.depth,a=n,i=1;i0&&(i.push(se(o,e)),o=[]),i.push(a[s]));o.length>0&&i.push(se(o,e)),r&&((n=se(ee(r,e,!0))).classes=["tag"],i.push(n));var h=Zt(["katex-html"],i);if(h.setAttribute("aria-hidden","true"),n){var m=n.children[0];m.style.height=h.height+h.depth+"em",m.style.verticalAlign=-h.depth+"em"}return h}function he(t){return new A(t)}var me=function(){function t(t,e){this.type=void 0,this.attributes=void 0,this.children=void 0,this.type=t,this.attributes={},this.children=e||[]}var e=t.prototype;return e.setAttribute=function(t,e){this.attributes[t]=e},e.getAttribute=function(t){return this.attributes[t]},e.toNode=function(){var t=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&t.setAttribute(e,this.attributes[e]);for(var r=0;r"},e.toText=function(){return this.children.map((function(t){return t.toText()})).join("")},t}(),ce=function(){function t(t){this.text=void 0,this.text=t}var e=t.prototype;return e.toNode=function(){return document.createTextNode(this.text)},e.toMarkup=function(){return c.escape(this.toText())},e.toText=function(){return this.text},t}(),ue={MathNode:me,TextNode:ce,SpaceNode:function(){function t(t){this.width=void 0,this.character=void 0,this.width=t,this.character=t>=.05555&&t<=.05556?" ":t>=.1666&&t<=.1667?" ":t>=.2222&&t<=.2223?" ":t>=.2777&&t<=.2778?"  ":t>=-.05556&&t<=-.05555?" ⁣":t>=-.1667&&t<=-.1666?" ⁣":t>=-.2223&&t<=-.2222?" ⁣":t>=-.2778&&t<=-.2777?" ⁣":null}var e=t.prototype;return e.toNode=function(){if(this.character)return document.createTextNode(this.character);var t=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return t.setAttribute("width",this.width+"em"),t},e.toMarkup=function(){return this.character?""+this.character+"":''},e.toText=function(){return this.character?this.character:" "},t}(),newDocumentFragment:he},pe=function(t,e,r){return!X[e][t]||!X[e][t].replace||55349===t.charCodeAt(0)||rt.hasOwnProperty(t)&&r&&(r.fontFamily&&"tt"===r.fontFamily.substr(4,2)||r.font&&"tt"===r.font.substr(4,2))||(t=X[e][t].replace),new ue.TextNode(t)},de=function(t){return 1===t.length?t[0]:new ue.MathNode("mrow",t)},fe=function(t,e){if("texttt"===e.fontFamily)return"monospace";if("textsf"===e.fontFamily)return"textit"===e.fontShape&&"textbf"===e.fontWeight?"sans-serif-bold-italic":"textit"===e.fontShape?"sans-serif-italic":"textbf"===e.fontWeight?"bold-sans-serif":"sans-serif";if("textit"===e.fontShape&&"textbf"===e.fontWeight)return"bold-italic";if("textit"===e.fontShape)return"italic";if("textbf"===e.fontWeight)return"bold";var r=e.font;if(!r||"mathnormal"===r)return null;var n=t.mode;if("mathit"===r)return"italic";if("boldsymbol"===r)return"textord"===t.type?"bold":"bold-italic";if("mathbf"===r)return"bold";if("mathbb"===r)return"double-struck";if("mathfrak"===r)return"fraktur";if("mathscr"===r||"mathcal"===r)return"script";if("mathsf"===r)return"sans-serif";if("mathtt"===r)return"monospace";var a=t.text;return c.contains(["\\imath","\\jmath"],a)?null:(X[n][a]&&X[n][a].replace&&(a=X[n][a].replace),G(a,Dt.fontMap[r].fontName,n)?Dt.fontMap[r].variant:null)},ge=function(t,e,r){if(1===t.length){var n=ve(t[0],e);return r&&n instanceof me&&"mo"===n.type&&(n.setAttribute("lspace","0em"),n.setAttribute("rspace","0em")),[n]}for(var a,i=[],o=0;o0&&(p.text=p.text.slice(0,1)+"̸"+p.text.slice(1),i.pop())}}}i.push(s),a=s}return i},xe=function(t,e,r){return de(ge(t,e,r))},ve=function(t,e){if(!t)return new ue.MathNode("mrow");if($t[t.type])return $t[t.type](t,e);throw new o("Got group of unknown type: '"+t.type+"'")};function be(t,e,r,n,a){var i,o=ge(t,r);i=1===o.length&&o[0]instanceof me&&c.contains(["mrow","mtable"],o[0].type)?o[0]:new ue.MathNode("mrow",o);var s=new ue.MathNode("annotation",[new ue.TextNode(e)]);s.setAttribute("encoding","application/x-tex");var l=new ue.MathNode("semantics",[i,s]),h=new ue.MathNode("math",[l]);h.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),n&&h.setAttribute("display","block");var m=a?"katex":"katex-mathml";return Dt.makeSpan([m],[h])}var ye=function(t){return new St({style:t.displayMode?w.DISPLAY:w.TEXT,maxSize:t.maxSize,minRuleThickness:t.minRuleThickness})},we=function(t,e){if(e.displayMode){var r=["katex-display"];e.leqno&&r.push("leqno"),e.fleqn&&r.push("fleqn"),t=Dt.makeSpan(r,[t])}return t},ke=function(t,e,r){var n,a=ye(r);if("mathml"===r.output)return be(t,e,a,r.displayMode,!0);if("html"===r.output){var i=le(t,a);n=Dt.makeSpan(["katex"],[i])}else{var o=be(t,e,a,r.displayMode,!1),s=le(t,a);n=Dt.makeSpan(["katex"],[o,s])}return we(n,r)},Se={widehat:"^",widecheck:"ˇ",widetilde:"~",utilde:"~",overleftarrow:"←",underleftarrow:"←",xleftarrow:"←",overrightarrow:"→",underrightarrow:"→",xrightarrow:"→",underbrace:"⏟",overbrace:"⏞",overgroup:"⏠",undergroup:"⏡",overleftrightarrow:"↔",underleftrightarrow:"↔",xleftrightarrow:"↔",Overrightarrow:"⇒",xRightarrow:"⇒",overleftharpoon:"↼",xleftharpoonup:"↼",overrightharpoon:"⇀",xrightharpoonup:"⇀",xLeftarrow:"⇐",xLeftrightarrow:"⇔",xhookleftarrow:"↩",xhookrightarrow:"↪",xmapsto:"↦",xrightharpoondown:"⇁",xleftharpoondown:"↽",xrightleftharpoons:"⇌",xleftrightharpoons:"⇋",xtwoheadleftarrow:"↞",xtwoheadrightarrow:"↠",xlongequal:"=",xtofrom:"⇄",xrightleftarrows:"⇄",xrightequilibrium:"⇌",xleftequilibrium:"⇋"},Me={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]},ze=function(t,e,r,n){var a,i=t.height+t.depth+2*r;if(/fbox|color/.test(e)){if(a=Dt.makeSpan(["stretchy",e],[],n),"fbox"===e){var o=n.color&&n.getColor();o&&(a.style.borderColor=o)}}else{var s=[];/^[bx]cancel$/.test(e)&&s.push(new D({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(e)&&s.push(new D({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));var l=new L(s,{width:"100%",height:i+"em"});a=Dt.makeSvgSpan([],[l],n)}return a.height=i,a.style.height=i+"em",a},Ae=function(t){var e=new ue.MathNode("mo",[new ue.TextNode(Se[t.substr(1)])]);return e.setAttribute("stretchy","true"),e},Te=function(t,e){var r=function(){var r=4e5,n=t.label.substr(1);if(c.contains(["widehat","widecheck","widetilde","utilde"],n)){var a,i,o,s="ordgroup"===(d=t.base).type?d.body.length:1;if(s>5)"widehat"===n||"widecheck"===n?(a=420,r=2364,o=.42,i=n+"4"):(a=312,r=2340,o=.34,i="tilde4");else{var l=[1,1,2,2,3,3][s];"widehat"===n||"widecheck"===n?(r=[0,1062,2364,2364,2364][l],a=[0,239,300,360,420][l],o=[0,.24,.3,.3,.36,.42][l],i=n+l):(r=[0,600,1033,2339,2340][l],a=[0,260,286,306,312][l],o=[0,.26,.286,.3,.306,.34][l],i="tilde"+l)}var h=new P(i),m=new L([h],{width:"100%",height:o+"em",viewBox:"0 0 "+r+" "+a,preserveAspectRatio:"none"});return{span:Dt.makeSvgSpan([],[m],e),minWidth:0,height:o}}var u,p,d,f=[],g=Me[n],x=g[0],v=g[1],b=g[2],y=b/1e3,w=x.length;if(1===w)u=["hide-tail"],p=[g[3]];else if(2===w)u=["halfarrow-left","halfarrow-right"],p=["xMinYMin","xMaxYMin"];else{if(3!==w)throw new Error("Correct katexImagesData or update code here to support\n "+w+" children.");u=["brace-left","brace-center","brace-right"],p=["xMinYMin","xMidYMin","xMaxYMin"]}for(var k=0;k0&&(n.style.minWidth=a+"em"),n};function Be(t,e){if(!t||t.type!==e)throw new Error("Expected node of type "+e+", but got "+(t?"node of type "+t.type:String(t)));return t}function Ce(t){var e=qe(t);if(!e)throw new Error("Expected node of symbol group type, but got "+(t?"node of type "+t.type:String(t)));return e}function qe(t){return t&&("atom"===t.type||$.hasOwnProperty(t.type))?t:null}var Ne=function(t,e){var r,n,a;t&&"supsub"===t.type?(r=(n=Be(t.base,"accent")).base,t.base=r,a=function(t){if(t instanceof N)return t;throw new Error("Expected span but got "+String(t)+".")}(oe(t,e)),t.base=n):r=(n=Be(t,"accent")).base;var i=oe(r,e.havingCrampedStyle()),o=0;if(n.isShifty&&c.isCharacterBox(r)){var s=c.getBaseElem(r);o=H(oe(s,e.havingCrampedStyle())).skew}var l,h=Math.min(i.height,e.fontMetrics().xHeight);if(n.isStretchy)l=Te(n,e),l=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:i},{type:"elem",elem:l,wrapperClasses:["svg-align"],wrapperStyle:o>0?{width:"calc(100% - "+2*o+"em)",marginLeft:2*o+"em"}:void 0}]},e);else{var m,u;"\\vec"===n.label?(m=Dt.staticSvg("vec",e),u=Dt.svgData.vec[1]):((m=H(m=Dt.makeOrd({mode:n.mode,text:n.label},e,"textord"))).italic=0,u=m.width),l=Dt.makeSpan(["accent-body"],[m]);var p="\\textcircled"===n.label;p&&(l.classes.push("accent-full"),h=i.height);var d=o;p||(d-=u/2),l.style.left=d+"em","\\textcircled"===n.label&&(l.style.top=".2em"),l=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:i},{type:"kern",size:-h},{type:"elem",elem:l}]},e)}var f=Dt.makeSpan(["mord","accent"],[l],e);return a?(a.children[0]=f,a.height=Math.max(f.height,a.height),a.classes[0]="mord",a):f},Ie=function(t,e){var r=t.isStretchy?Ae(t.label):new ue.MathNode("mo",[pe(t.label,t.mode)]),n=new ue.MathNode("mover",[ve(t.base,e),r]);return n.setAttribute("accent","true"),n},Oe=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map((function(t){return"\\"+t})).join("|"));Wt({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:function(t,e){var r=e[0],n=!Oe.test(t.funcName),a=!n||"\\widehat"===t.funcName||"\\widetilde"===t.funcName||"\\widecheck"===t.funcName;return{type:"accent",mode:t.parser.mode,label:t.funcName,isStretchy:n,isShifty:a,base:r}},htmlBuilder:Ne,mathmlBuilder:Ie}),Wt({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!1},handler:function(t,e){var r=e[0];return{type:"accent",mode:t.parser.mode,label:t.funcName,isStretchy:!1,isShifty:!0,base:r}},htmlBuilder:Ne,mathmlBuilder:Ie}),Wt({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:function(t,e){var r=t.parser,n=t.funcName,a=e[0];return{type:"accentUnder",mode:r.mode,label:n,base:a}},htmlBuilder:function(t,e){var r=oe(t.base,e),n=Te(t,e),a="\\utilde"===t.label?.12:0,i=Dt.makeVList({positionType:"top",positionData:r.height,children:[{type:"elem",elem:n,wrapperClasses:["svg-align"]},{type:"kern",size:a},{type:"elem",elem:r}]},e);return Dt.makeSpan(["mord","accentunder"],[i],e)},mathmlBuilder:function(t,e){var r=Ae(t.label),n=new ue.MathNode("munder",[ve(t.base,e),r]);return n.setAttribute("accentunder","true"),n}});var Re=function(t){var e=new ue.MathNode("mpadded",t?[t]:[]);return e.setAttribute("width","+0.6em"),e.setAttribute("lspace","0.3em"),e};Wt({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium"],props:{numArgs:1,numOptionalArgs:1},handler:function(t,e,r){var n=t.parser,a=t.funcName;return{type:"xArrow",mode:n.mode,label:a,body:e[0],below:r[0]}},htmlBuilder:function(t,e){var r,n=e.style,a=e.havingStyle(n.sup()),i=Dt.wrapFragment(oe(t.body,a,e),e);i.classes.push("x-arrow-pad"),t.below&&(a=e.havingStyle(n.sub()),(r=Dt.wrapFragment(oe(t.below,a,e),e)).classes.push("x-arrow-pad"));var o,s=Te(t,e),l=-e.fontMetrics().axisHeight+.5*s.height,h=-e.fontMetrics().axisHeight-.5*s.height-.111;if((i.depth>.25||"\\xleftequilibrium"===t.label)&&(h-=i.depth),r){var m=-e.fontMetrics().axisHeight+r.height+.5*s.height+.111;o=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:i,shift:h},{type:"elem",elem:s,shift:l},{type:"elem",elem:r,shift:m}]},e)}else o=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:i,shift:h},{type:"elem",elem:s,shift:l}]},e);return o.children[0].children[0].children[1].classes.push("svg-align"),Dt.makeSpan(["mrel","x-arrow"],[o],e)},mathmlBuilder:function(t,e){var r,n=Ae(t.label);if(t.body){var a=Re(ve(t.body,e));if(t.below){var i=Re(ve(t.below,e));r=new ue.MathNode("munderover",[n,i,a])}else r=new ue.MathNode("mover",[n,a])}else if(t.below){var o=Re(ve(t.below,e));r=new ue.MathNode("munder",[n,o])}else r=Re(),r=new ue.MathNode("mover",[n,r]);return r}}),Wt({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){for(var r=t.parser,n=Be(e[0],"ordgroup").body,a="",i=0;i","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"],Ke=[0,1.2,1.8,2.4,3],Je=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],Qe=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"stack"}],tr=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],er=function(t){if("small"===t.type)return"Main-Regular";if("large"===t.type)return"Size"+t.size+"-Regular";if("stack"===t.type)return"Size4-Regular";throw new Error("Add support for delim type '"+t.type+"' here.")},rr=function(t,e,r,n){for(var a=Math.min(2,3-n.style.size);ae)return r[a]}return r[r.length-1]},nr=function(t,e,r,n,a,i){var o;"<"===t||"\\lt"===t||"⟨"===t?t="\\langle":">"!==t&&"\\gt"!==t&&"⟩"!==t||(t="\\rangle"),o=c.contains(Ze,t)?Je:c.contains(Xe,t)?tr:Qe;var s=rr(t,e,o,n);return"small"===s.type?function(t,e,r,n,a,i){var o=Dt.makeSymbol(t,"Main-Regular",a,n),s=Ve(o,e,n,i);return r&&Ue(s,n,e),s}(t,s.style,r,n,a,i):"large"===s.type?Ge(t,s.size,r,n,a,i):$e(t,e,r,n,a,i)},ar=function(t,e){var r,n,a=e.havingBaseSizing(),i=rr("\\surd",t*a.sizeMultiplier,tr,a),o=a.sizeMultiplier,s=Math.max(0,e.minRuleThickness-e.fontMetrics().sqrtRuleThickness),l=0,h=0,m=0;return"small"===i.type?(t<1?o=1:t<1.4&&(o=.7),h=(1+s)/o,(r=We("sqrtMain",l=(1+s+.08)/o,m=1e3+1e3*s+80,s,e)).style.minWidth="0.853em",n=.833/o):"large"===i.type?(m=1080*Ke[i.size],h=(Ke[i.size]+s)/o,l=(Ke[i.size]+s+.08)/o,(r=We("sqrtSize"+i.size,l,m,s,e)).style.minWidth="1.02em",n=1/o):(l=t+s+.08,h=t+s,m=Math.floor(1e3*t+s)+80,(r=We("sqrtTall",l,m,s,e)).style.minWidth="0.742em",n=1.056),r.height=h,r.style.height=l+"em",{span:r,advanceWidth:n,ruleWidth:(e.fontMetrics().sqrtRuleThickness+s)*o}},ir=function(t,e,r,n,a){if("<"===t||"\\lt"===t||"⟨"===t?t="\\langle":">"!==t&&"\\gt"!==t&&"⟩"!==t||(t="\\rangle"),c.contains(Xe,t)||c.contains(Ze,t))return Ge(t,e,!1,r,n,a);if(c.contains(je,t))return $e(t,Ke[e],!1,r,n,a);throw new o("Illegal delimiter: '"+t+"'")},or=nr,sr=function(t,e,r,n,a,i){var o=n.fontMetrics().axisHeight*n.sizeMultiplier,s=5/n.fontMetrics().ptPerEm,l=Math.max(e-o,r+o),h=Math.max(l/500*901,2*l-s);return nr(t,h,!0,n,a,i)},lr={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},hr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","⌊","⌋","\\lceil","\\rceil","⌈","⌉","<",">","\\langle","⟨","\\rangle","⟩","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","⟮","⟯","\\lmoustache","\\rmoustache","⎰","⎱","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."];function mr(t,e){var r=qe(t);if(r&&c.contains(hr,r.text))return r;throw new o(r?"Invalid delimiter '"+r.text+"' after '"+e.funcName+"'":"Invalid delimiter type '"+t.type+"'",t)}function cr(t){if(!t.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}Wt({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1},handler:function(t,e){var r=mr(e[0],t);return{type:"delimsizing",mode:t.parser.mode,size:lr[t.funcName].size,mclass:lr[t.funcName].mclass,delim:r.text}},htmlBuilder:function(t,e){return"."===t.delim?Dt.makeSpan([t.mclass]):ir(t.delim,t.size,e,t.mode,[t.mclass])},mathmlBuilder:function(t){var e=[];"."!==t.delim&&e.push(pe(t.delim,t.mode));var r=new ue.MathNode("mo",e);return"mopen"===t.mclass||"mclose"===t.mclass?r.setAttribute("fence","true"):r.setAttribute("fence","false"),r}}),Wt({type:"leftright-right",names:["\\right"],props:{numArgs:1},handler:function(t,e){var r=t.parser.gullet.macros.get("\\current@color");if(r&&"string"!=typeof r)throw new o("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:t.parser.mode,delim:mr(e[0],t).text,color:r}}}),Wt({type:"leftright",names:["\\left"],props:{numArgs:1},handler:function(t,e){var r=mr(e[0],t),n=t.parser;++n.leftrightDepth;var a=n.parseExpression(!1);--n.leftrightDepth,n.expect("\\right",!1);var i=Be(n.parseFunction(),"leftright-right");return{type:"leftright",mode:n.mode,body:a,left:r.text,right:i.delim,rightColor:i.color}},htmlBuilder:function(t,e){cr(t);for(var r,n,a=ee(t.body,e,!0,["mopen","mclose"]),i=0,o=0,s=!1,l=0;l-1?"mpadded":"menclose",[ve(t.body,e)]);switch(t.label){case"\\cancel":n.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":n.setAttribute("notation","downdiagonalstrike");break;case"\\sout":n.setAttribute("notation","horizontalstrike");break;case"\\fbox":n.setAttribute("notation","box");break;case"\\fcolorbox":case"\\colorbox":if(r=e.fontMetrics().fboxsep*e.fontMetrics().ptPerEm,n.setAttribute("width","+"+2*r+"pt"),n.setAttribute("height","+"+2*r+"pt"),n.setAttribute("lspace",r+"pt"),n.setAttribute("voffset",r+"pt"),"\\fcolorbox"===t.label){var a=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness);n.setAttribute("style","border: "+a+"em solid "+String(t.borderColor))}break;case"\\xcancel":n.setAttribute("notation","updiagonalstrike downdiagonalstrike")}return t.backgroundColor&&n.setAttribute("mathbackground",t.backgroundColor),n};Wt({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,greediness:3,argTypes:["color","text"]},handler:function(t,e,r){var n=t.parser,a=t.funcName,i=Be(e[0],"color-token").color,o=e[1];return{type:"enclose",mode:n.mode,label:a,backgroundColor:i,body:o}},htmlBuilder:ur,mathmlBuilder:pr}),Wt({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,greediness:3,argTypes:["color","color","text"]},handler:function(t,e,r){var n=t.parser,a=t.funcName,i=Be(e[0],"color-token").color,o=Be(e[1],"color-token").color,s=e[2];return{type:"enclose",mode:n.mode,label:a,backgroundColor:o,borderColor:i,body:s}},htmlBuilder:ur,mathmlBuilder:pr}),Wt({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler:function(t,e){return{type:"enclose",mode:t.parser.mode,label:"\\fbox",body:e[0]}}}),Wt({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\sout"],props:{numArgs:1},handler:function(t,e,r){var n=t.parser,a=t.funcName,i=e[0];return{type:"enclose",mode:n.mode,label:a,body:i}},htmlBuilder:ur,mathmlBuilder:pr});var dr={};function fr(t){for(var e=t.type,r=t.names,n=t.props,a=t.handler,i=t.htmlBuilder,o=t.mathmlBuilder,s={type:e,numArgs:n.numArgs||0,greediness:1,allowedInText:!1,numOptionalArgs:0,handler:a},l=0;l0&&(b+=.25),h.push({pos:b,isDashed:t[e]})}for(y(i[0]),r=0;r0&&(M<(B+=v)&&(M=B),B=0),t.addJot&&(M+=f),z.height=S,z.depth=M,b+=S,z.pos=b,b+=M+B,l[r]=z,y(i[r+1])}var C,q,N=b/2+e.fontMetrics().axisHeight,I=t.cols||[],O=[];for(n=0,q=0;n=s)){var D=void 0;(n>0||t.hskipBeforeAndAfter)&&0!==(D=c.deflt(R.pregap,p))&&((C=Dt.makeSpan(["arraycolsep"],[])).style.width=D+"em",O.push(C));var H=[];for(r=0;r0){for(var G=Dt.makeLineSpan("hline",e,m),_=Dt.makeLineSpan("hdashline",e,m),Y=[{type:"elem",elem:l,shift:0}];h.length>0;){var $=h.pop(),W=$.pos-N;$.isDashed?Y.push({type:"elem",elem:_,shift:W}):Y.push({type:"elem",elem:G,shift:W})}l=Dt.makeVList({positionType:"individualShift",children:Y},e)}return Dt.makeSpan(["mord"],[l],e)},yr={c:"center ",l:"left ",r:"right "},wr=function(t,e){var r=new ue.MathNode("mtable",t.body.map((function(t){return new ue.MathNode("mtr",t.map((function(t){return new ue.MathNode("mtd",[ve(t,e)])})))}))),n=.5===t.arraystretch?.1:.16+t.arraystretch-1+(t.addJot?.09:0);r.setAttribute("rowspacing",n+"em");var a="",i="";if(t.cols&&t.cols.length>0){var o=t.cols,s="",l=!1,h=0,m=o.length;"separator"===o[0].type&&(a+="top ",h=1),"separator"===o[o.length-1].type&&(a+="bottom ",m-=1);for(var c=h;c0?"left ":"",a+=g[g.length-1].length>0?"right ":"";for(var x=1;x0&&m&&(p=1),n[c]={type:"align",align:u,pregap:p,postgap:0}}return a.colSeparationType=m?"align":"alignat",a};fr({type:"array",names:["array","darray"],props:{numArgs:1},handler:function(t,e){var r={cols:(qe(e[0])?[e[0]]:Be(e[0],"ordgroup").body).map((function(t){var e=Ce(t).text;if(-1!=="lcr".indexOf(e))return{type:"align",align:e};if("|"===e)return{type:"separator",separator:"|"};if(":"===e)return{type:"separator",separator:":"};throw new o("Unknown column alignment: "+e,t)})),hskipBeforeAndAfter:!0};return xr(t.parser,r,vr(t.envName))},htmlBuilder:br,mathmlBuilder:wr}),fr({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix"],props:{numArgs:0},handler:function(t){var e={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[t.envName],r=xr(t.parser,{hskipBeforeAndAfter:!1},vr(t.envName));return e?{type:"leftright",mode:t.mode,body:[r],left:e[0],right:e[1],rightColor:void 0}:r},htmlBuilder:br,mathmlBuilder:wr}),fr({type:"array",names:["smallmatrix"],props:{numArgs:0},handler:function(t){var e=xr(t.parser,{arraystretch:.5},"script");return e.colSeparationType="small",e},htmlBuilder:br,mathmlBuilder:wr}),fr({type:"array",names:["subarray"],props:{numArgs:1},handler:function(t,e){var r=(qe(e[0])?[e[0]]:Be(e[0],"ordgroup").body).map((function(t){var e=Ce(t).text;if(-1!=="lc".indexOf(e))return{type:"align",align:e};throw new o("Unknown column alignment: "+e,t)}));if(r.length>1)throw new o("{subarray} can contain only one column");var n={cols:r,hskipBeforeAndAfter:!1,arraystretch:.5};if((n=xr(t.parser,n,"script")).body.length>0&&n.body[0].length>1)throw new o("{subarray} can contain only one column");return n},htmlBuilder:br,mathmlBuilder:wr}),fr({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler:function(t){var e=xr(t.parser,{arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},vr(t.envName));return{type:"leftright",mode:t.mode,body:[e],left:t.envName.indexOf("r")>-1?".":"\\{",right:t.envName.indexOf("r")>-1?"\\}":".",rightColor:void 0}},htmlBuilder:br,mathmlBuilder:wr}),fr({type:"array",names:["aligned"],props:{numArgs:0},handler:kr,htmlBuilder:br,mathmlBuilder:wr}),fr({type:"array",names:["gathered"],props:{numArgs:0},handler:function(t){return xr(t.parser,{cols:[{type:"align",align:"c"}],addJot:!0},"display")},htmlBuilder:br,mathmlBuilder:wr}),fr({type:"array",names:["alignedat"],props:{numArgs:1},handler:kr,htmlBuilder:br,mathmlBuilder:wr}),Wt({type:"text",names:["\\hline","\\hdashline"],props:{numArgs:0,allowedInText:!0,allowedInMath:!0},handler:function(t,e){throw new o(t.funcName+" valid only within array environment")}});var Sr=dr;Wt({type:"environment",names:["\\begin","\\end"],props:{numArgs:1,argTypes:["text"]},handler:function(t,e){var r=t.parser,n=t.funcName,a=e[0];if("ordgroup"!==a.type)throw new o("Invalid environment name",a);for(var i="",s=0;s=w.SCRIPT.id?r.text():w.DISPLAY:"text"===t&&r.size===w.DISPLAY.size?r=w.TEXT:"script"===t?r=w.SCRIPT:"scriptscript"===t&&(r=w.SCRIPTSCRIPT),r},Ir=function(t,e){var r,n=Nr(t.size,e.style),a=n.fracNum(),i=n.fracDen();r=e.havingStyle(a);var o=oe(t.numer,r,e);if(t.continued){var s=8.5/e.fontMetrics().ptPerEm,l=3.5/e.fontMetrics().ptPerEm;o.height=o.height0?3*c:7*c,d=e.fontMetrics().denom1):(m>0?(u=e.fontMetrics().num2,p=c):(u=e.fontMetrics().num3,p=3*c),d=e.fontMetrics().denom2),h){var y=e.fontMetrics().axisHeight;u-o.depth-(y+.5*m)0&&(e="."===(e=t)?null:e),e};Wt({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,greediness:6,argTypes:["math","math","size","text","math","math"]},handler:function(t,e){var r,n=t.parser,a=e[4],i=e[5],o="atom"===e[0].type&&"open"===e[0].family?Er(e[0].text):null,s="atom"===e[1].type&&"close"===e[1].family?Er(e[1].text):null,l=Be(e[2],"size"),h=null;r=!!l.isBlank||(h=l.value).number>0;var m="auto",c=e[3];if("ordgroup"===c.type){if(c.body.length>0){var u=Be(c.body[0],"textord");m=Rr[Number(u.text)]}}else c=Be(c,"textord"),m=Rr[Number(c.text)];return{type:"genfrac",mode:n.mode,numer:a,denom:i,continued:!1,hasBarLine:r,barSize:h,leftDelim:o,rightDelim:s,size:m}},htmlBuilder:Ir,mathmlBuilder:Or}),Wt({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler:function(t,e){var r=t.parser,n=(t.funcName,t.token);return{type:"infix",mode:r.mode,replaceWith:"\\\\abovefrac",size:Be(e[0],"size").value,token:n}}}),Wt({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:function(t,e){var r=t.parser,n=(t.funcName,e[0]),a=function(t){if(!t)throw new Error("Expected non-null, but got "+String(t));return t}(Be(e[1],"infix").size),i=e[2],o=a.number>0;return{type:"genfrac",mode:r.mode,numer:n,denom:i,continued:!1,hasBarLine:o,barSize:a,leftDelim:null,rightDelim:null,size:"auto"}},htmlBuilder:Ir,mathmlBuilder:Or});var Lr=function(t,e){var r,n,a=e.style;"supsub"===t.type?(r=t.sup?oe(t.sup,e.havingStyle(a.sup()),e):oe(t.sub,e.havingStyle(a.sub()),e),n=Be(t.base,"horizBrace")):n=Be(t,"horizBrace");var i,o=oe(n.base,e.havingBaseStyle(w.DISPLAY)),s=Te(n,e);if(n.isOver?(i=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:o},{type:"kern",size:.1},{type:"elem",elem:s}]},e)).children[0].children[0].children[1].classes.push("svg-align"):(i=Dt.makeVList({positionType:"bottom",positionData:o.depth+.1+s.height,children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:o}]},e)).children[0].children[0].children[0].classes.push("svg-align"),r){var l=Dt.makeSpan(["mord",n.isOver?"mover":"munder"],[i],e);i=n.isOver?Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:l},{type:"kern",size:.2},{type:"elem",elem:r}]},e):Dt.makeVList({positionType:"bottom",positionData:l.depth+.2+r.height+r.depth,children:[{type:"elem",elem:r},{type:"kern",size:.2},{type:"elem",elem:l}]},e)}return Dt.makeSpan(["mord",n.isOver?"mover":"munder"],[i],e)};Wt({type:"horizBrace",names:["\\overbrace","\\underbrace"],props:{numArgs:1},handler:function(t,e){var r=t.parser,n=t.funcName;return{type:"horizBrace",mode:r.mode,label:n,isOver:/^\\over/.test(n),base:e[0]}},htmlBuilder:Lr,mathmlBuilder:function(t,e){var r=Ae(t.label);return new ue.MathNode(t.isOver?"mover":"munder",[ve(t.base,e),r])}}),Wt({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:function(t,e){var r=t.parser,n=e[1],a=Be(e[0],"url").url;return r.settings.isTrusted({command:"\\href",url:a})?{type:"href",mode:r.mode,href:a,body:jt(n)}:r.formatUnsupportedCmd("\\href")},htmlBuilder:function(t,e){var r=ee(t.body,e,!1);return Dt.makeAnchor(t.href,[],r,e)},mathmlBuilder:function(t,e){var r=xe(t.body,e);return r instanceof me||(r=new me("mrow",[r])),r.setAttribute("href",t.href),r}}),Wt({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:function(t,e){var r=t.parser,n=Be(e[0],"url").url;if(!r.settings.isTrusted({command:"\\url",url:n}))return r.formatUnsupportedCmd("\\url");for(var a=[],i=0;i0&&(n=Tt(t.totalheight,e)-r,n=Number(n.toFixed(2)));var a=0;t.width.number>0&&(a=Tt(t.width,e));var i={height:r+n+"em"};a>0&&(i.width=a+"em"),n>0&&(i.verticalAlign=-n+"em");var o=new O(t.src,t.alt,i);return o.height=r,o.depth=n,o},mathmlBuilder:function(t,e){var r=new ue.MathNode("mglyph",[]);r.setAttribute("alt",t.alt);var n=Tt(t.height,e),a=0;if(t.totalheight.number>0&&(a=(a=Tt(t.totalheight,e)-n).toFixed(2),r.setAttribute("valign","-"+a+"em")),r.setAttribute("height",n+a+"em"),t.width.number>0){var i=Tt(t.width,e);r.setAttribute("width",i+"em")}return r.setAttribute("src",t.src),r}}),Wt({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],allowedInText:!0},handler:function(t,e){var r=t.parser,n=t.funcName,a=Be(e[0],"size");if(r.settings.strict){var i="m"===n[1],o="mu"===a.value.unit;i?(o||r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" supports only mu units, not "+a.value.unit+" units"),"math"!==r.mode&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" works only in math mode")):o&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" doesn't support mu units")}return{type:"kern",mode:r.mode,dimension:a.value}},htmlBuilder:function(t,e){return Dt.makeGlue(t.dimension,e)},mathmlBuilder:function(t,e){var r=Tt(t.dimension,e);return new ue.SpaceNode(r)}}),Wt({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){var r=t.parser,n=t.funcName,a=e[0];return{type:"lap",mode:r.mode,alignment:n.slice(5),body:a}},htmlBuilder:function(t,e){var r;"clap"===t.alignment?(r=Dt.makeSpan([],[oe(t.body,e)]),r=Dt.makeSpan(["inner"],[r],e)):r=Dt.makeSpan(["inner"],[oe(t.body,e)]);var n=Dt.makeSpan(["fix"],[]),a=Dt.makeSpan([t.alignment],[r,n],e),i=Dt.makeSpan(["strut"]);return i.style.height=a.height+a.depth+"em",i.style.verticalAlign=-a.depth+"em",a.children.unshift(i),a=Dt.makeSpan(["thinbox"],[a],e),Dt.makeSpan(["mord","vbox"],[a],e)},mathmlBuilder:function(t,e){var r=new ue.MathNode("mpadded",[ve(t.body,e)]);if("rlap"!==t.alignment){var n="llap"===t.alignment?"-1":"-0.5";r.setAttribute("lspace",n+"width")}return r.setAttribute("width","0px"),r}}),Wt({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler:function(t,e){var r=t.funcName,n=t.parser,a=n.mode;n.switchMode("math");var i="\\("===r?"\\)":"$",o=n.parseExpression(!1,i);return n.expect(i),n.switchMode(a),{type:"styling",mode:n.mode,style:"text",body:o}}}),Wt({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler:function(t,e){throw new o("Mismatched "+t.funcName)}});var Dr=function(t,e){switch(e.style.size){case w.DISPLAY.size:return t.display;case w.TEXT.size:return t.text;case w.SCRIPT.size:return t.script;case w.SCRIPTSCRIPT.size:return t.scriptscript;default:return t.text}};Wt({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4},handler:function(t,e){return{type:"mathchoice",mode:t.parser.mode,display:jt(e[0]),text:jt(e[1]),script:jt(e[2]),scriptscript:jt(e[3])}},htmlBuilder:function(t,e){var r=Dr(t,e),n=ee(r,e,!1);return Dt.makeFragment(n)},mathmlBuilder:function(t,e){var r=Dr(t,e);return xe(r,e)}});var Hr=function(t,e,r,n,a,i,o){var s,l,h;if(t=Dt.makeSpan([],[t]),e){var m=oe(e,n.havingStyle(a.sup()),n);l={elem:m,kern:Math.max(n.fontMetrics().bigOpSpacing1,n.fontMetrics().bigOpSpacing3-m.depth)}}if(r){var c=oe(r,n.havingStyle(a.sub()),n);s={elem:c,kern:Math.max(n.fontMetrics().bigOpSpacing2,n.fontMetrics().bigOpSpacing4-c.height)}}if(l&&s){var u=n.fontMetrics().bigOpSpacing5+s.elem.height+s.elem.depth+s.kern+t.depth+o;h=Dt.makeVList({positionType:"bottom",positionData:u,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:s.elem,marginLeft:-i+"em"},{type:"kern",size:s.kern},{type:"elem",elem:t},{type:"kern",size:l.kern},{type:"elem",elem:l.elem,marginLeft:i+"em"},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]},n)}else if(s){var p=t.height-o;h=Dt.makeVList({positionType:"top",positionData:p,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:s.elem,marginLeft:-i+"em"},{type:"kern",size:s.kern},{type:"elem",elem:t}]},n)}else{if(!l)return t;var d=t.depth+o;h=Dt.makeVList({positionType:"bottom",positionData:d,children:[{type:"elem",elem:t},{type:"kern",size:l.kern},{type:"elem",elem:l.elem,marginLeft:i+"em"},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]},n)}return Dt.makeSpan(["mop","op-limits"],[h],n)},Fr=["\\smallint"],Vr=function(t,e){var r,n,a,i=!1;"supsub"===t.type?(r=t.sup,n=t.sub,a=Be(t.base,"op"),i=!0):a=Be(t,"op");var o,s=e.style,l=!1;if(s.size===w.DISPLAY.size&&a.symbol&&!c.contains(Fr,a.name)&&(l=!0),a.symbol){var h=l?"Size2-Regular":"Size1-Regular",m="";if("\\oiint"!==a.name&&"\\oiiint"!==a.name||(m=a.name.substr(1),a.name="oiint"===m?"\\iint":"\\iiint"),o=Dt.makeSymbol(a.name,h,"math",e,["mop","op-symbol",l?"large-op":"small-op"]),m.length>0){var u=o.italic,p=Dt.staticSvg(m+"Size"+(l?"2":"1"),e);o=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:o,shift:0},{type:"elem",elem:p,shift:l?.08:0}]},e),a.name="\\"+m,o.classes.unshift("mop"),o.italic=u}}else if(a.body){var d=ee(a.body,e,!0);1===d.length&&d[0]instanceof E?(o=d[0]).classes[0]="mop":o=Dt.makeSpan(["mop"],Dt.tryCombineChars(d),e)}else{for(var f=[],g=1;g0){for(var s=a.body.map((function(t){var e=t.text;return"string"==typeof e?{type:"textord",mode:t.mode,text:e}:t})),l=ee(s,e.withFont("mathrm"),!0),h=0;h=0?s.setAttribute("height","+"+a+"em"):(s.setAttribute("height",a+"em"),s.setAttribute("depth","+"+-a+"em")),s.setAttribute("voffset",a+"em"),s}});var Wr=["\\tiny","\\sixptsize","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"];Wt({type:"sizing",names:Wr,props:{numArgs:0,allowedInText:!0},handler:function(t,e){var r=t.breakOnTokenText,n=t.funcName,a=t.parser,i=a.parseExpression(!1,r);return{type:"sizing",mode:a.mode,size:Wr.indexOf(n)+1,body:i}},htmlBuilder:function(t,e){var r=e.havingSize(t.size);return $r(t.body,r,e)},mathmlBuilder:function(t,e){var r=e.havingSize(t.size),n=ge(t.body,r),a=new ue.MathNode("mstyle",n);return a.setAttribute("mathsize",r.sizeMultiplier+"em"),a}}),Wt({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:function(t,e,r){var n=t.parser,a=!1,i=!1,o=r[0]&&Be(r[0],"ordgroup");if(o)for(var s="",l=0;lr.height+r.depth+i&&(i=(i+c-r.height-r.depth)/2);var u=l.height-r.height-i-h;r.style.paddingLeft=m+"em";var p=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r,wrapperClasses:["svg-align"]},{type:"kern",size:-(r.height+u)},{type:"elem",elem:l},{type:"kern",size:h}]},e);if(t.index){var d=e.havingStyle(w.SCRIPTSCRIPT),f=oe(t.index,d,e),g=.6*(p.height-p.depth),x=Dt.makeVList({positionType:"shift",positionData:-g,children:[{type:"elem",elem:f}]},e),v=Dt.makeSpan(["root"],[x]);return Dt.makeSpan(["mord","sqrt"],[v,p],e)}return Dt.makeSpan(["mord","sqrt"],[p],e)},mathmlBuilder:function(t,e){var r=t.body,n=t.index;return n?new ue.MathNode("mroot",[ve(r,e),ve(n,e)]):new ue.MathNode("msqrt",[ve(r,e)])}});var Xr={display:w.DISPLAY,text:w.TEXT,script:w.SCRIPT,scriptscript:w.SCRIPTSCRIPT};Wt({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0},handler:function(t,e){var r=t.breakOnTokenText,n=t.funcName,a=t.parser,i=a.parseExpression(!0,r),o=n.slice(1,n.length-5);return{type:"styling",mode:a.mode,style:o,body:i}},htmlBuilder:function(t,e){var r=Xr[t.style],n=e.havingStyle(r).withFont("");return $r(t.body,n,e)},mathmlBuilder:function(t,e){var r=Xr[t.style],n=e.havingStyle(r),a=ge(t.body,n),i=new ue.MathNode("mstyle",a),o={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]}[t.style];return i.setAttribute("scriptlevel",o[0]),i.setAttribute("displaystyle",o[1]),i}}),Xt({type:"supsub",htmlBuilder:function(t,e){var r=function(t,e){var r=t.base;return r?"op"===r.type?r.limits&&(e.style.size===w.DISPLAY.size||r.alwaysHandleSupSub)?Vr:null:"operatorname"===r.type?r.alwaysHandleSupSub&&(e.style.size===w.DISPLAY.size||r.limits)?Yr:null:"accent"===r.type?c.isCharacterBox(r.base)?Ne:null:"horizBrace"===r.type&&!t.sub===r.isOver?Lr:null:null}(t,e);if(r)return r(t,e);var n,a,i,o=t.base,s=t.sup,l=t.sub,h=oe(o,e),m=e.fontMetrics(),u=0,p=0,d=o&&c.isCharacterBox(o);if(s){var f=e.havingStyle(e.style.sup());n=oe(s,f,e),d||(u=h.height-f.fontMetrics().supDrop*f.sizeMultiplier/e.sizeMultiplier)}if(l){var g=e.havingStyle(e.style.sub());a=oe(l,g,e),d||(p=h.depth+g.fontMetrics().subDrop*g.sizeMultiplier/e.sizeMultiplier)}i=e.style===w.DISPLAY?m.sup1:e.style.cramped?m.sup3:m.sup2;var x,v=e.sizeMultiplier,b=.5/m.ptPerEm/v+"em",y=null;if(a){var k=t.base&&"op"===t.base.type&&t.base.name&&("\\oiint"===t.base.name||"\\oiiint"===t.base.name);(h instanceof E||k)&&(y=-h.italic+"em")}if(n&&a){u=Math.max(u,i,n.depth+.25*m.xHeight),p=Math.max(p,m.sub2);var S=4*m.defaultRuleThickness;if(u-n.depth-(a.height-p)0&&(u+=M,p-=M)}var z=[{type:"elem",elem:a,shift:p,marginRight:b,marginLeft:y},{type:"elem",elem:n,shift:-u,marginRight:b}];x=Dt.makeVList({positionType:"individualShift",children:z},e)}else if(a){p=Math.max(p,m.sub1,a.height-.8*m.xHeight);var A=[{type:"elem",elem:a,marginLeft:y,marginRight:b}];x=Dt.makeVList({positionType:"shift",positionData:p,children:A},e)}else{if(!n)throw new Error("supsub must have either sup or sub.");u=Math.max(u,i,n.depth+.25*m.xHeight),x=Dt.makeVList({positionType:"shift",positionData:-u,children:[{type:"elem",elem:n,marginRight:b}]},e)}var T=ae(h,"right")||"mord";return Dt.makeSpan([T],[h,Dt.makeSpan(["msupsub"],[x])],e)},mathmlBuilder:function(t,e){var r,n=!1;t.base&&"horizBrace"===t.base.type&&!!t.sup===t.base.isOver&&(n=!0,r=t.base.isOver),!t.base||"op"!==t.base.type&&"operatorname"!==t.base.type||(t.base.parentIsSupSub=!0);var a,i=[ve(t.base,e)];if(t.sub&&i.push(ve(t.sub,e)),t.sup&&i.push(ve(t.sup,e)),n)a=r?"mover":"munder";else if(t.sub)if(t.sup){var o=t.base;a=o&&"op"===o.type&&o.limits&&e.style===w.DISPLAY||o&&"operatorname"===o.type&&o.alwaysHandleSupSub&&(e.style===w.DISPLAY||o.limits)?"munderover":"msubsup"}else{var s=t.base;a=s&&"op"===s.type&&s.limits&&(e.style===w.DISPLAY||s.alwaysHandleSupSub)||s&&"operatorname"===s.type&&s.alwaysHandleSupSub&&(s.limits||e.style===w.DISPLAY)?"munder":"msub"}else{var l=t.base;a=l&&"op"===l.type&&l.limits&&(e.style===w.DISPLAY||l.alwaysHandleSupSub)||l&&"operatorname"===l.type&&l.alwaysHandleSupSub&&(l.limits||e.style===w.DISPLAY)?"mover":"msup"}return new ue.MathNode(a,i)}}),Xt({type:"atom",htmlBuilder:function(t,e){return Dt.mathsym(t.text,t.mode,e,["m"+t.family])},mathmlBuilder:function(t,e){var r=new ue.MathNode("mo",[pe(t.text,t.mode)]);if("bin"===t.family){var n=fe(t,e);"bold-italic"===n&&r.setAttribute("mathvariant",n)}else"punct"===t.family?r.setAttribute("separator","true"):"open"!==t.family&&"close"!==t.family||r.setAttribute("stretchy","false");return r}});var jr={mi:"italic",mn:"normal",mtext:"normal"};Xt({type:"mathord",htmlBuilder:function(t,e){return Dt.makeOrd(t,e,"mathord")},mathmlBuilder:function(t,e){var r=new ue.MathNode("mi",[pe(t.text,t.mode,e)]),n=fe(t,e)||"italic";return n!==jr[r.type]&&r.setAttribute("mathvariant",n),r}}),Xt({type:"textord",htmlBuilder:function(t,e){return Dt.makeOrd(t,e,"textord")},mathmlBuilder:function(t,e){var r,n=pe(t.text,t.mode,e),a=fe(t,e)||"normal";return r="text"===t.mode?new ue.MathNode("mtext",[n]):/[0-9]/.test(t.text)?new ue.MathNode("mn",[n]):"\\prime"===t.text?new ue.MathNode("mo",[n]):new ue.MathNode("mi",[n]),a!==jr[r.type]&&r.setAttribute("mathvariant",a),r}});var Zr={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},Kr={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};Xt({type:"spacing",htmlBuilder:function(t,e){if(Kr.hasOwnProperty(t.text)){var r=Kr[t.text].className||"";if("text"===t.mode){var n=Dt.makeOrd(t,e,"textord");return n.classes.push(r),n}return Dt.makeSpan(["mspace",r],[Dt.mathsym(t.text,t.mode,e)],e)}if(Zr.hasOwnProperty(t.text))return Dt.makeSpan(["mspace",Zr[t.text]],[],e);throw new o('Unknown type of space "'+t.text+'"')},mathmlBuilder:function(t,e){if(!Kr.hasOwnProperty(t.text)){if(Zr.hasOwnProperty(t.text))return new ue.MathNode("mspace");throw new o('Unknown type of space "'+t.text+'"')}return new ue.MathNode("mtext",[new ue.TextNode(" ")])}});var Jr=function(){var t=new ue.MathNode("mtd",[]);return t.setAttribute("width","50%"),t};Xt({type:"tag",mathmlBuilder:function(t,e){var r=new ue.MathNode("mtable",[new ue.MathNode("mtr",[Jr(),new ue.MathNode("mtd",[xe(t.body,e)]),Jr(),new ue.MathNode("mtd",[xe(t.tag,e)])])]);return r.setAttribute("width","100%"),r}});var Qr={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},tn={"\\textbf":"textbf","\\textmd":"textmd"},en={"\\textit":"textit","\\textup":"textup"},rn=function(t,e){var r=t.font;return r?Qr[r]?e.withTextFontFamily(Qr[r]):tn[r]?e.withTextFontWeight(tn[r]):e.withTextFontShape(en[r]):e};Wt({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup"],props:{numArgs:1,argTypes:["text"],greediness:2,allowedInText:!0},handler:function(t,e){var r=t.parser,n=t.funcName,a=e[0];return{type:"text",mode:r.mode,body:jt(a),font:n}},htmlBuilder:function(t,e){var r=rn(t,e),n=ee(t.body,r,!0);return Dt.makeSpan(["mord","text"],Dt.tryCombineChars(n),r)},mathmlBuilder:function(t,e){var r=rn(t,e);return xe(t.body,r)}}),Wt({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){return{type:"underline",mode:t.parser.mode,body:e[0]}},htmlBuilder:function(t,e){var r=oe(t.body,e),n=Dt.makeLineSpan("underline-line",e),a=e.fontMetrics().defaultRuleThickness,i=Dt.makeVList({positionType:"top",positionData:r.height,children:[{type:"kern",size:a},{type:"elem",elem:n},{type:"kern",size:3*a},{type:"elem",elem:r}]},e);return Dt.makeSpan(["mord","underline"],[i],e)},mathmlBuilder:function(t,e){var r=new ue.MathNode("mo",[new ue.TextNode("‾")]);r.setAttribute("stretchy","true");var n=new ue.MathNode("munder",[ve(t.body,e),r]);return n.setAttribute("accentunder","true"),n}}),Wt({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler:function(t,e,r){throw new o("\\verb ended by end of line instead of matching delimiter")},htmlBuilder:function(t,e){for(var r=nn(t),n=[],a=e.havingStyle(e.style.text()),i=0;i0&&(this.undefStack[this.undefStack.length-1][t]=e)}else{var a=this.undefStack[this.undefStack.length-1];a&&!a.hasOwnProperty(t)&&(a[t]=this.current[t])}this.current[t]=e},t}(),mn={},cn=mn;function un(t,e){mn[t]=e}un("\\noexpand",(function(t){var e=t.popToken();return t.isExpandable(e.text)&&(e.noexpand=!0,e.treatAsRelax=!0),{tokens:[e],numArgs:0}})),un("\\expandafter",(function(t){var e=t.popToken();return t.expandOnce(!0),{tokens:[e],numArgs:0}})),un("\\@firstoftwo",(function(t){return{tokens:t.consumeArgs(2)[0],numArgs:0}})),un("\\@secondoftwo",(function(t){return{tokens:t.consumeArgs(2)[1],numArgs:0}})),un("\\@ifnextchar",(function(t){var e=t.consumeArgs(3);t.consumeSpaces();var r=t.future();return 1===e[0].length&&e[0][0].text===r.text?{tokens:e[1],numArgs:0}:{tokens:e[2],numArgs:0}})),un("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),un("\\TextOrMath",(function(t){var e=t.consumeArgs(2);return"text"===t.mode?{tokens:e[0],numArgs:0}:{tokens:e[1],numArgs:0}}));var pn={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};un("\\char",(function(t){var e,r=t.popToken(),n="";if("'"===r.text)e=8,r=t.popToken();else if('"'===r.text)e=16,r=t.popToken();else if("`"===r.text)if("\\"===(r=t.popToken()).text[0])n=r.text.charCodeAt(1);else{if("EOF"===r.text)throw new o("\\char` missing argument");n=r.text.charCodeAt(0)}else e=10;if(e){if(null==(n=pn[r.text])||n>=e)throw new o("Invalid base-"+e+" digit "+r.text);for(var a;null!=(a=pn[t.future().text])&&a":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"};un("\\dots",(function(t){var e="\\dotso",r=t.expandAfterFuture().text;return r in fn?e=fn[r]:("\\not"===r.substr(0,4)||r in X.math&&c.contains(["bin","rel"],X.math[r].group))&&(e="\\dotsb"),e}));var gn={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};un("\\dotso",(function(t){return t.future().text in gn?"\\ldots\\,":"\\ldots"})),un("\\dotsc",(function(t){var e=t.future().text;return e in gn&&","!==e?"\\ldots\\,":"\\ldots"})),un("\\cdots",(function(t){return t.future().text in gn?"\\@cdots\\,":"\\@cdots"})),un("\\dotsb","\\cdots"),un("\\dotsm","\\cdots"),un("\\dotsi","\\!\\cdots"),un("\\dotsx","\\ldots\\,"),un("\\DOTSI","\\relax"),un("\\DOTSB","\\relax"),un("\\DOTSX","\\relax"),un("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),un("\\,","\\tmspace+{3mu}{.1667em}"),un("\\thinspace","\\,"),un("\\>","\\mskip{4mu}"),un("\\:","\\tmspace+{4mu}{.2222em}"),un("\\medspace","\\:"),un("\\;","\\tmspace+{5mu}{.2777em}"),un("\\thickspace","\\;"),un("\\!","\\tmspace-{3mu}{.1667em}"),un("\\negthinspace","\\!"),un("\\negmedspace","\\tmspace-{4mu}{.2222em}"),un("\\negthickspace","\\tmspace-{5mu}{.277em}"),un("\\enspace","\\kern.5em "),un("\\enskip","\\hskip.5em\\relax"),un("\\quad","\\hskip1em\\relax"),un("\\qquad","\\hskip2em\\relax"),un("\\tag","\\@ifstar\\tag@literal\\tag@paren"),un("\\tag@paren","\\tag@literal{({#1})}"),un("\\tag@literal",(function(t){if(t.macros.get("\\df@tag"))throw new o("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"})),un("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),un("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),un("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),un("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),un("\\pmb","\\html@mathml{\\@binrel{#1}{\\mathrlap{#1}\\kern0.5px#1}}{\\mathbf{#1}}"),un("\\\\","\\newline"),un("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");var xn=F["Main-Regular"]["T".charCodeAt(0)][1]-.7*F["Main-Regular"]["A".charCodeAt(0)][1]+"em";un("\\LaTeX","\\textrm{\\html@mathml{L\\kern-.36em\\raisebox{"+xn+"}{\\scriptstyle A}\\kern-.15em\\TeX}{LaTeX}}"),un("\\KaTeX","\\textrm{\\html@mathml{K\\kern-.17em\\raisebox{"+xn+"}{\\scriptstyle A}\\kern-.15em\\TeX}{KaTeX}}"),un("\\hspace","\\@ifstar\\@hspacer\\@hspace"),un("\\@hspace","\\hskip #1\\relax"),un("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),un("\\ordinarycolon",":"),un("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}"),un("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}'),un("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}'),un("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}'),un("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}'),un("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}'),un("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}'),un("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}'),un("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}'),un("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}'),un("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}'),un("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}'),un("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}'),un("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}'),un("∷","\\dblcolon"),un("∹","\\eqcolon"),un("≔","\\coloneqq"),un("≕","\\eqqcolon"),un("⩴","\\Coloneqq"),un("\\ratio","\\vcentcolon"),un("\\coloncolon","\\dblcolon"),un("\\colonequals","\\coloneqq"),un("\\coloncolonequals","\\Coloneqq"),un("\\equalscolon","\\eqqcolon"),un("\\equalscoloncolon","\\Eqqcolon"),un("\\colonminus","\\coloneq"),un("\\coloncolonminus","\\Coloneq"),un("\\minuscolon","\\eqcolon"),un("\\minuscoloncolon","\\Eqcolon"),un("\\coloncolonapprox","\\Colonapprox"),un("\\coloncolonsim","\\Colonsim"),un("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),un("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),un("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),un("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),un("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`∌}}"),un("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),un("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),un("\\gvertneqq","\\html@mathml{\\@gvertneqq}{≩}"),un("\\lvertneqq","\\html@mathml{\\@lvertneqq}{≨}"),un("\\ngeqq","\\html@mathml{\\@ngeqq}{≱}"),un("\\ngeqslant","\\html@mathml{\\@ngeqslant}{≱}"),un("\\nleqq","\\html@mathml{\\@nleqq}{≰}"),un("\\nleqslant","\\html@mathml{\\@nleqslant}{≰}"),un("\\nshortmid","\\html@mathml{\\@nshortmid}{∤}"),un("\\nshortparallel","\\html@mathml{\\@nshortparallel}{∦}"),un("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{⊈}"),un("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{⊉}"),un("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{⊊}"),un("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{⫋}"),un("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{⊋}"),un("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{⫌}"),un("\\imath","\\html@mathml{\\@imath}{ı}"),un("\\jmath","\\html@mathml{\\@jmath}{ȷ}"),un("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`⟦}}"),un("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`⟧}}"),un("⟦","\\llbracket"),un("⟧","\\rrbracket"),un("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`⦃}}"),un("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`⦄}}"),un("⦃","\\lBrace"),un("⦄","\\rBrace"),un("\\minuso","\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`⦵}}"),un("⦵","\\minuso"),un("\\darr","\\downarrow"),un("\\dArr","\\Downarrow"),un("\\Darr","\\Downarrow"),un("\\lang","\\langle"),un("\\rang","\\rangle"),un("\\uarr","\\uparrow"),un("\\uArr","\\Uparrow"),un("\\Uarr","\\Uparrow"),un("\\N","\\mathbb{N}"),un("\\R","\\mathbb{R}"),un("\\Z","\\mathbb{Z}"),un("\\alef","\\aleph"),un("\\alefsym","\\aleph"),un("\\Alpha","\\mathrm{A}"),un("\\Beta","\\mathrm{B}"),un("\\bull","\\bullet"),un("\\Chi","\\mathrm{X}"),un("\\clubs","\\clubsuit"),un("\\cnums","\\mathbb{C}"),un("\\Complex","\\mathbb{C}"),un("\\Dagger","\\ddagger"),un("\\diamonds","\\diamondsuit"),un("\\empty","\\emptyset"),un("\\Epsilon","\\mathrm{E}"),un("\\Eta","\\mathrm{H}"),un("\\exist","\\exists"),un("\\harr","\\leftrightarrow"),un("\\hArr","\\Leftrightarrow"),un("\\Harr","\\Leftrightarrow"),un("\\hearts","\\heartsuit"),un("\\image","\\Im"),un("\\infin","\\infty"),un("\\Iota","\\mathrm{I}"),un("\\isin","\\in"),un("\\Kappa","\\mathrm{K}"),un("\\larr","\\leftarrow"),un("\\lArr","\\Leftarrow"),un("\\Larr","\\Leftarrow"),un("\\lrarr","\\leftrightarrow"),un("\\lrArr","\\Leftrightarrow"),un("\\Lrarr","\\Leftrightarrow"),un("\\Mu","\\mathrm{M}"),un("\\natnums","\\mathbb{N}"),un("\\Nu","\\mathrm{N}"),un("\\Omicron","\\mathrm{O}"),un("\\plusmn","\\pm"),un("\\rarr","\\rightarrow"),un("\\rArr","\\Rightarrow"),un("\\Rarr","\\Rightarrow"),un("\\real","\\Re"),un("\\reals","\\mathbb{R}"),un("\\Reals","\\mathbb{R}"),un("\\Rho","\\mathrm{P}"),un("\\sdot","\\cdot"),un("\\sect","\\S"),un("\\spades","\\spadesuit"),un("\\sub","\\subset"),un("\\sube","\\subseteq"),un("\\supe","\\supseteq"),un("\\Tau","\\mathrm{T}"),un("\\thetasym","\\vartheta"),un("\\weierp","\\wp"),un("\\Zeta","\\mathrm{Z}"),un("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),un("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),un("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),un("\\bra","\\mathinner{\\langle{#1}|}"),un("\\ket","\\mathinner{|{#1}\\rangle}"),un("\\braket","\\mathinner{\\langle{#1}\\rangle}"),un("\\Bra","\\left\\langle#1\\right|"),un("\\Ket","\\left|#1\\right\\rangle"),un("\\blue","\\textcolor{##6495ed}{#1}"),un("\\orange","\\textcolor{##ffa500}{#1}"),un("\\pink","\\textcolor{##ff00af}{#1}"),un("\\red","\\textcolor{##df0030}{#1}"),un("\\green","\\textcolor{##28ae7b}{#1}"),un("\\gray","\\textcolor{gray}{#1}"),un("\\purple","\\textcolor{##9d38bd}{#1}"),un("\\blueA","\\textcolor{##ccfaff}{#1}"),un("\\blueB","\\textcolor{##80f6ff}{#1}"),un("\\blueC","\\textcolor{##63d9ea}{#1}"),un("\\blueD","\\textcolor{##11accd}{#1}"),un("\\blueE","\\textcolor{##0c7f99}{#1}"),un("\\tealA","\\textcolor{##94fff5}{#1}"),un("\\tealB","\\textcolor{##26edd5}{#1}"),un("\\tealC","\\textcolor{##01d1c1}{#1}"),un("\\tealD","\\textcolor{##01a995}{#1}"),un("\\tealE","\\textcolor{##208170}{#1}"),un("\\greenA","\\textcolor{##b6ffb0}{#1}"),un("\\greenB","\\textcolor{##8af281}{#1}"),un("\\greenC","\\textcolor{##74cf70}{#1}"),un("\\greenD","\\textcolor{##1fab54}{#1}"),un("\\greenE","\\textcolor{##0d923f}{#1}"),un("\\goldA","\\textcolor{##ffd0a9}{#1}"),un("\\goldB","\\textcolor{##ffbb71}{#1}"),un("\\goldC","\\textcolor{##ff9c39}{#1}"),un("\\goldD","\\textcolor{##e07d10}{#1}"),un("\\goldE","\\textcolor{##a75a05}{#1}"),un("\\redA","\\textcolor{##fca9a9}{#1}"),un("\\redB","\\textcolor{##ff8482}{#1}"),un("\\redC","\\textcolor{##f9685d}{#1}"),un("\\redD","\\textcolor{##e84d39}{#1}"),un("\\redE","\\textcolor{##bc2612}{#1}"),un("\\maroonA","\\textcolor{##ffbde0}{#1}"),un("\\maroonB","\\textcolor{##ff92c6}{#1}"),un("\\maroonC","\\textcolor{##ed5fa6}{#1}"),un("\\maroonD","\\textcolor{##ca337c}{#1}"),un("\\maroonE","\\textcolor{##9e034e}{#1}"),un("\\purpleA","\\textcolor{##ddd7ff}{#1}"),un("\\purpleB","\\textcolor{##c6b9fc}{#1}"),un("\\purpleC","\\textcolor{##aa87ff}{#1}"),un("\\purpleD","\\textcolor{##7854ab}{#1}"),un("\\purpleE","\\textcolor{##543b78}{#1}"),un("\\mintA","\\textcolor{##f5f9e8}{#1}"),un("\\mintB","\\textcolor{##edf2df}{#1}"),un("\\mintC","\\textcolor{##e0e5cc}{#1}"),un("\\grayA","\\textcolor{##f6f7f7}{#1}"),un("\\grayB","\\textcolor{##f0f1f2}{#1}"),un("\\grayC","\\textcolor{##e3e5e6}{#1}"),un("\\grayD","\\textcolor{##d6d8da}{#1}"),un("\\grayE","\\textcolor{##babec2}{#1}"),un("\\grayF","\\textcolor{##888d93}{#1}"),un("\\grayG","\\textcolor{##626569}{#1}"),un("\\grayH","\\textcolor{##3b3e40}{#1}"),un("\\grayI","\\textcolor{##21242c}{#1}"),un("\\kaBlue","\\textcolor{##314453}{#1}"),un("\\kaGreen","\\textcolor{##71B307}{#1}");var vn={"\\relax":!0,"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0},bn=function(){function t(t,e,r){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=e,this.expansionCount=0,this.feed(t),this.macros=new hn(cn,e.macros),this.mode=r,this.stack=[]}var e=t.prototype;return e.feed=function(t){this.lexer=new ln(t,this.settings)},e.switchMode=function(t){this.mode=t},e.beginGroup=function(){this.macros.beginGroup()},e.endGroup=function(){this.macros.endGroup()},e.future=function(){return 0===this.stack.length&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]},e.popToken=function(){return this.future(),this.stack.pop()},e.pushToken=function(t){this.stack.push(t)},e.pushTokens=function(t){var e;(e=this.stack).push.apply(e,t)},e.consumeSpaces=function(){for(;" "===this.future().text;)this.stack.pop()},e.consumeArgs=function(t){for(var e=[],r=0;rthis.settings.maxExpand)throw new o("Too many expansions: infinite loop or need to increase maxExpand setting");var a=n.tokens;if(n.numArgs)for(var i=this.consumeArgs(n.numArgs),s=(a=a.slice()).length-1;s>=0;--s){var l=a[s];if("#"===l.text){if(0===s)throw new o("Incomplete placeholder at end of macro body",l);if("#"===(l=a[--s]).text)a.splice(s+1,1);else{if(!/^[1-9]$/.test(l.text))throw new o("Not a valid argument number",l);var h;(h=a).splice.apply(h,[s,2].concat(i[+l.text-1]))}}}return this.pushTokens(a),a},e.expandAfterFuture=function(){return this.expandOnce(),this.future()},e.expandNextToken=function(){for(;;){var t=this.expandOnce();if(t instanceof a){if("\\relax"!==t.text&&!t.treatAsRelax)return this.stack.pop();this.stack.pop()}}throw new Error},e.expandMacro=function(t){return this.macros.has(t)?this.expandTokens([new a(t)]):void 0},e.expandTokens=function(t){var e=[],r=this.stack.length;for(this.pushTokens(t);this.stack.length>r;){var n=this.expandOnce(!0);n instanceof a&&(n.treatAsRelax&&(n.noexpand=!1,n.treatAsRelax=!1),e.push(this.stack.pop()))}return e},e.expandMacroAsText=function(t){var e=this.expandMacro(t);return e?e.map((function(t){return t.text})).join(""):e},e._getExpansion=function(t){var e=this.macros.get(t);if(null==e)return e;var r="function"==typeof e?e(this):e;if("string"==typeof r){var n=0;if(-1!==r.indexOf("#"))for(var a=r.replace(/##/g,"");-1!==a.indexOf("#"+(n+1));)++n;for(var i=new ln(r,this.settings),o=[],s=i.lex();"EOF"!==s.text;)o.push(s),s=i.lex();return o.reverse(),{tokens:o,numArgs:n}}return r},e.isDefined=function(t){return this.macros.has(t)||an.hasOwnProperty(t)||X.math.hasOwnProperty(t)||X.text.hasOwnProperty(t)||vn.hasOwnProperty(t)},e.isExpandable=function(t){var e=this.macros.get(t);return null!=e?"string"==typeof e||"function"==typeof e||!e.unexpandable:an.hasOwnProperty(t)},t}(),yn={"́":{text:"\\'",math:"\\acute"},"̀":{text:"\\`",math:"\\grave"},"̈":{text:'\\"',math:"\\ddot"},"̃":{text:"\\~",math:"\\tilde"},"̄":{text:"\\=",math:"\\bar"},"̆":{text:"\\u",math:"\\breve"},"̌":{text:"\\v",math:"\\check"},"̂":{text:"\\^",math:"\\hat"},"̇":{text:"\\.",math:"\\dot"},"̊":{text:"\\r",math:"\\mathring"},"̋":{text:"\\H"}},wn={"á":"á","à":"à","ä":"ä","ǟ":"ǟ","ã":"ã","ā":"ā","ă":"ă","ắ":"ắ","ằ":"ằ","ẵ":"ẵ","ǎ":"ǎ","â":"â","ấ":"ấ","ầ":"ầ","ẫ":"ẫ","ȧ":"ȧ","ǡ":"ǡ","å":"å","ǻ":"ǻ","ḃ":"ḃ","ć":"ć","č":"č","ĉ":"ĉ","ċ":"ċ","ď":"ď","ḋ":"ḋ","é":"é","è":"è","ë":"ë","ẽ":"ẽ","ē":"ē","ḗ":"ḗ","ḕ":"ḕ","ĕ":"ĕ","ě":"ě","ê":"ê","ế":"ế","ề":"ề","ễ":"ễ","ė":"ė","ḟ":"ḟ","ǵ":"ǵ","ḡ":"ḡ","ğ":"ğ","ǧ":"ǧ","ĝ":"ĝ","ġ":"ġ","ḧ":"ḧ","ȟ":"ȟ","ĥ":"ĥ","ḣ":"ḣ","í":"í","ì":"ì","ï":"ï","ḯ":"ḯ","ĩ":"ĩ","ī":"ī","ĭ":"ĭ","ǐ":"ǐ","î":"î","ǰ":"ǰ","ĵ":"ĵ","ḱ":"ḱ","ǩ":"ǩ","ĺ":"ĺ","ľ":"ľ","ḿ":"ḿ","ṁ":"ṁ","ń":"ń","ǹ":"ǹ","ñ":"ñ","ň":"ň","ṅ":"ṅ","ó":"ó","ò":"ò","ö":"ö","ȫ":"ȫ","õ":"õ","ṍ":"ṍ","ṏ":"ṏ","ȭ":"ȭ","ō":"ō","ṓ":"ṓ","ṑ":"ṑ","ŏ":"ŏ","ǒ":"ǒ","ô":"ô","ố":"ố","ồ":"ồ","ỗ":"ỗ","ȯ":"ȯ","ȱ":"ȱ","ő":"ő","ṕ":"ṕ","ṗ":"ṗ","ŕ":"ŕ","ř":"ř","ṙ":"ṙ","ś":"ś","ṥ":"ṥ","š":"š","ṧ":"ṧ","ŝ":"ŝ","ṡ":"ṡ","ẗ":"ẗ","ť":"ť","ṫ":"ṫ","ú":"ú","ù":"ù","ü":"ü","ǘ":"ǘ","ǜ":"ǜ","ǖ":"ǖ","ǚ":"ǚ","ũ":"ũ","ṹ":"ṹ","ū":"ū","ṻ":"ṻ","ŭ":"ŭ","ǔ":"ǔ","û":"û","ů":"ů","ű":"ű","ṽ":"ṽ","ẃ":"ẃ","ẁ":"ẁ","ẅ":"ẅ","ŵ":"ŵ","ẇ":"ẇ","ẘ":"ẘ","ẍ":"ẍ","ẋ":"ẋ","ý":"ý","ỳ":"ỳ","ÿ":"ÿ","ỹ":"ỹ","ȳ":"ȳ","ŷ":"ŷ","ẏ":"ẏ","ẙ":"ẙ","ź":"ź","ž":"ž","ẑ":"ẑ","ż":"ż","Á":"Á","À":"À","Ä":"Ä","Ǟ":"Ǟ","Ã":"Ã","Ā":"Ā","Ă":"Ă","Ắ":"Ắ","Ằ":"Ằ","Ẵ":"Ẵ","Ǎ":"Ǎ","Â":"Â","Ấ":"Ấ","Ầ":"Ầ","Ẫ":"Ẫ","Ȧ":"Ȧ","Ǡ":"Ǡ","Å":"Å","Ǻ":"Ǻ","Ḃ":"Ḃ","Ć":"Ć","Č":"Č","Ĉ":"Ĉ","Ċ":"Ċ","Ď":"Ď","Ḋ":"Ḋ","É":"É","È":"È","Ë":"Ë","Ẽ":"Ẽ","Ē":"Ē","Ḗ":"Ḗ","Ḕ":"Ḕ","Ĕ":"Ĕ","Ě":"Ě","Ê":"Ê","Ế":"Ế","Ề":"Ề","Ễ":"Ễ","Ė":"Ė","Ḟ":"Ḟ","Ǵ":"Ǵ","Ḡ":"Ḡ","Ğ":"Ğ","Ǧ":"Ǧ","Ĝ":"Ĝ","Ġ":"Ġ","Ḧ":"Ḧ","Ȟ":"Ȟ","Ĥ":"Ĥ","Ḣ":"Ḣ","Í":"Í","Ì":"Ì","Ï":"Ï","Ḯ":"Ḯ","Ĩ":"Ĩ","Ī":"Ī","Ĭ":"Ĭ","Ǐ":"Ǐ","Î":"Î","İ":"İ","Ĵ":"Ĵ","Ḱ":"Ḱ","Ǩ":"Ǩ","Ĺ":"Ĺ","Ľ":"Ľ","Ḿ":"Ḿ","Ṁ":"Ṁ","Ń":"Ń","Ǹ":"Ǹ","Ñ":"Ñ","Ň":"Ň","Ṅ":"Ṅ","Ó":"Ó","Ò":"Ò","Ö":"Ö","Ȫ":"Ȫ","Õ":"Õ","Ṍ":"Ṍ","Ṏ":"Ṏ","Ȭ":"Ȭ","Ō":"Ō","Ṓ":"Ṓ","Ṑ":"Ṑ","Ŏ":"Ŏ","Ǒ":"Ǒ","Ô":"Ô","Ố":"Ố","Ồ":"Ồ","Ỗ":"Ỗ","Ȯ":"Ȯ","Ȱ":"Ȱ","Ő":"Ő","Ṕ":"Ṕ","Ṗ":"Ṗ","Ŕ":"Ŕ","Ř":"Ř","Ṙ":"Ṙ","Ś":"Ś","Ṥ":"Ṥ","Š":"Š","Ṧ":"Ṧ","Ŝ":"Ŝ","Ṡ":"Ṡ","Ť":"Ť","Ṫ":"Ṫ","Ú":"Ú","Ù":"Ù","Ü":"Ü","Ǘ":"Ǘ","Ǜ":"Ǜ","Ǖ":"Ǖ","Ǚ":"Ǚ","Ũ":"Ũ","Ṹ":"Ṹ","Ū":"Ū","Ṻ":"Ṻ","Ŭ":"Ŭ","Ǔ":"Ǔ","Û":"Û","Ů":"Ů","Ű":"Ű","Ṽ":"Ṽ","Ẃ":"Ẃ","Ẁ":"Ẁ","Ẅ":"Ẅ","Ŵ":"Ŵ","Ẇ":"Ẇ","Ẍ":"Ẍ","Ẋ":"Ẋ","Ý":"Ý","Ỳ":"Ỳ","Ÿ":"Ÿ","Ỹ":"Ỹ","Ȳ":"Ȳ","Ŷ":"Ŷ","Ẏ":"Ẏ","Ź":"Ź","Ž":"Ž","Ẑ":"Ẑ","Ż":"Ż","ά":"ά","ὰ":"ὰ","ᾱ":"ᾱ","ᾰ":"ᾰ","έ":"έ","ὲ":"ὲ","ή":"ή","ὴ":"ὴ","ί":"ί","ὶ":"ὶ","ϊ":"ϊ","ΐ":"ΐ","ῒ":"ῒ","ῑ":"ῑ","ῐ":"ῐ","ό":"ό","ὸ":"ὸ","ύ":"ύ","ὺ":"ὺ","ϋ":"ϋ","ΰ":"ΰ","ῢ":"ῢ","ῡ":"ῡ","ῠ":"ῠ","ώ":"ώ","ὼ":"ὼ","Ύ":"Ύ","Ὺ":"Ὺ","Ϋ":"Ϋ","Ῡ":"Ῡ","Ῠ":"Ῠ","Ώ":"Ώ","Ὼ":"Ὼ"},kn=function(){function t(t,e){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new bn(t,e,this.mode),this.settings=e,this.leftrightDepth=0}var e=t.prototype;return e.expect=function(t,e){if(void 0===e&&(e=!0),this.fetch().text!==t)throw new o("Expected '"+t+"', got '"+this.fetch().text+"'",this.fetch());e&&this.consume()},e.consume=function(){this.nextToken=null},e.fetch=function(){return null==this.nextToken&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken},e.switchMode=function(t){this.mode=t,this.gullet.switchMode(t)},e.parse=function(){this.settings.globalGroup||this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");var t=this.parseExpression(!1);return this.expect("EOF"),this.settings.globalGroup||this.gullet.endGroup(),t},e.parseExpression=function(e,r){for(var n=[];;){"math"===this.mode&&this.consumeSpaces();var a=this.fetch();if(-1!==t.endOfExpression.indexOf(a.text))break;if(r&&a.text===r)break;if(e&&an[a.text]&&an[a.text].infix)break;var i=this.parseAtom(r);if(!i)break;"internal"!==i.type&&n.push(i)}return"text"===this.mode&&this.formLigatures(n),this.handleInfixNodes(n)},e.handleInfixNodes=function(t){for(var e,r=-1,n=0;n0&&!h||0===s&&!h&&"math"===this.mode,c=this.parseGroupOfType("argument to '"+t+"'",l,h,n,m);if(!c){if(h){i.push(null);continue}throw new o("Expected group after '"+t+"'",this.fetch())}(h?i:a).push(c)}return{args:a,optArgs:i}},e.parseGroupOfType=function(t,e,r,n,a){switch(e){case"color":return a&&this.consumeSpaces(),this.parseColorGroup(r);case"size":return a&&this.consumeSpaces(),this.parseSizeGroup(r);case"url":return this.parseUrlGroup(r,a);case"math":case"text":return this.parseGroup(t,r,n,void 0,e,a);case"hbox":var i=this.parseGroup(t,r,n,void 0,"text",a);return i?{type:"styling",mode:i.mode,body:[i],style:"text"}:i;case"raw":if(a&&this.consumeSpaces(),r&&"{"===this.fetch().text)return null;var s=this.parseStringGroup("raw",r,!0);if(s)return{type:"raw",mode:"text",string:s.text};throw new o("Expected raw group",this.fetch());case"original":case null:case void 0:return this.parseGroup(t,r,n,void 0,void 0,a);default:throw new o("Unknown group type as "+t,this.fetch())}},e.consumeSpaces=function(){for(;" "===this.fetch().text;)this.consume()},e.parseStringGroup=function(t,e,r){var n=e?"[":"{",a=e?"]":"}",i=this.fetch();if(i.text!==n){if(e)return null;if(r&&"EOF"!==i.text&&/[^{}[\]]/.test(i.text))return this.consume(),i}var s=this.mode;this.mode="text",this.expect(n);for(var l,h="",m=this.fetch(),c=0,u=m;(l=this.fetch()).text!==a||r&&c>0;){switch(l.text){case"EOF":throw new o("Unexpected end of input in "+t,m.range(u,h));case n:c++;break;case a:c--}h+=(u=l).text,this.consume()}return this.expect(a),this.mode=s,m.range(u,h)},e.parseRegexGroup=function(t,e){var r=this.mode;this.mode="text";for(var n,a=this.fetch(),i=a,s="";"EOF"!==(n=this.fetch()).text&&t.test(s+n.text);)s+=(i=n).text,this.consume();if(""===s)throw new o("Invalid "+e+": '"+a.text+"'",a);return this.mode=r,a.range(i,s)},e.parseColorGroup=function(t){var e=this.parseStringGroup("color",t);if(!e)return null;var r=/^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i.exec(e.text);if(!r)throw new o("Invalid color: '"+e.text+"'",e);var n=r[0];return/^[0-9a-f]{6}$/i.test(n)&&(n="#"+n),{type:"color-token",mode:this.mode,color:n}},e.parseSizeGroup=function(t){var e,r=!1;if(!(e=t||"{"===this.fetch().text?this.parseStringGroup("size",t):this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/,"size")))return null;t||0!==e.text.length||(e.text="0pt",r=!0);var n=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e.text);if(!n)throw new o("Invalid size: '"+e.text+"'",e);var a={number:+(n[1]+n[2]),unit:n[3]};if(!At(a))throw new o("Invalid unit: '"+a.unit+"'",e);return{type:"size",mode:this.mode,value:a,isBlank:r}},e.parseUrlGroup=function(t,e){this.gullet.lexer.setCatcode("%",13);var r=this.parseStringGroup("url",t,!0);if(this.gullet.lexer.setCatcode("%",14),!r)return null;var n=r.text.replace(/\\([#$%&~_^{}])/g,"$1");return{type:"url",mode:this.mode,url:n}},e.parseGroup=function(e,r,a,i,s,l){var h=this.mode;s&&this.switchMode(s),l&&this.consumeSpaces();var m,c=this.fetch(),u=c.text;if(r?"["===u:"{"===u||"\\begingroup"===u){this.consume();var p=t.endOfGroup[u];this.gullet.beginGroup();var d=this.parseExpression(!1,p),f=this.fetch();this.expect(p),this.gullet.endGroup(),m={type:"ordgroup",mode:this.mode,loc:n.range(c,f),body:d,semisimple:"\\begingroup"===u||void 0}}else if(r)m=null;else if(null==(m=this.parseFunction(i,e,a)||this.parseSymbol())&&"\\"===u[0]&&!vn.hasOwnProperty(u)){if(this.settings.throwOnError)throw new o("Undefined control sequence: "+u,c);m=this.formatUnsupportedCmd(u),this.consume()}return s&&this.switchMode(h),m},e.formLigatures=function(t){for(var e=t.length-1,r=0;r=0&&this.settings.reportNonstrict("unicodeTextInMathMode",'Latin-1/Unicode text character "'+e[0]+'" used in math mode',t);var l,h=X[this.mode][e].group,m=n.range(t);if(Y.hasOwnProperty(h)){var c=h;l={type:"atom",mode:this.mode,family:c,loc:m,text:e}}else l={type:h,mode:this.mode,loc:m,text:e};i=l}else{if(!(e.charCodeAt(0)>=128))return null;this.settings.strict&&(M(e.charCodeAt(0))?"math"===this.mode&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+e[0]+'" used in math mode',t):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+e[0]+'" ('+e.charCodeAt(0)+")",t)),i={type:"textord",mode:"text",loc:n.range(t),text:e}}if(this.consume(),s)for(var u=0;u void) => void ); -const styleHref = (document.currentScript as any).src.replace('notebook-out/katex.js', '') + 'node_modules/katex/dist/katex.min.css'; +const styleHref = (document.currentScript as any).src.replace(/katex.js$/, 'katex.min.css'); const link = document.createElement('link'); link.rel = 'stylesheet'; diff --git a/extensions/notebook-markdown-extensions/package.json b/extensions/notebook-markdown-extensions/package.json index 74f29cfc77d..d5cd3bb4c0a 100644 --- a/extensions/notebook-markdown-extensions/package.json +++ b/extensions/notebook-markdown-extensions/package.json @@ -31,15 +31,14 @@ "scripts": { "compile": "npm run build-notebook", "watch": "npm run build-notebook", - "build-notebook": "npx webpack-cli --config webpack.notebook.js --mode production" - }, - "dependencies": { - "@iktakahiro/markdown-it-katex": "^4.0.1", - "markdown-it-emoji": "^2.0.0" + "build-notebook": "npx webpack-cli --config webpack.notebook.js --mode production", + "postinstall": "npm run build-notebook" }, "devDependencies": { + "@iktakahiro/markdown-it-katex": "^4.0.1", "@types/markdown-it": "^0.0.0", - "markdown-it": "^12.0.4" + "markdown-it": "^12.0.4", + "markdown-it-emoji": "^2.0.0" }, "repository": { "type": "git", diff --git a/extensions/notebook-markdown-extensions/webpack.notebook.js b/extensions/notebook-markdown-extensions/webpack.notebook.js index dc60df5bc11..18044ea235a 100644 --- a/extensions/notebook-markdown-extensions/webpack.notebook.js +++ b/extensions/notebook-markdown-extensions/webpack.notebook.js @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ const path = require('path'); +const CopyPlugin = require('copy-webpack-plugin'); module.exports = { entry: { @@ -23,5 +24,20 @@ module.exports = { output: { filename: '[name].js', path: path.resolve(__dirname, 'notebook-out') - } + }, + plugins: [ + // @ts-ignore + new CopyPlugin({ + patterns: [ + { + from: './node_modules/katex/dist/katex.min.css', + to: 'katex.min.css' + }, + { + from: './node_modules/katex/dist/fonts', + to: 'fonts/' + }, + ], + }), + ] }; diff --git a/extensions/postinstall.js b/extensions/postinstall.js index da4fa3e9d04..d509a0b327c 100644 --- a/extensions/postinstall.js +++ b/extensions/postinstall.js @@ -8,7 +8,6 @@ const fs = require('fs'); const path = require('path'); -const rimraf = require('rimraf'); const root = path.join(__dirname, 'node_modules', 'typescript'); @@ -21,7 +20,7 @@ function processRoot() { if (!toKeep.has(name)) { const filePath = path.join(root, name); console.log(`Removed ${filePath}`); - rimraf.sync(filePath); + fs.rmdirSync(filePath, { recursive: true }); } } } diff --git a/extensions/simple-browser/media/index.js b/extensions/simple-browser/media/index.js index d74299e1e71..c9a5b89b490 100644 --- a/extensions/simple-browser/media/index.js +++ b/extensions/simple-browser/media/index.js @@ -1,2 +1,2 @@ -!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(1),r=acquireVsCodeApi();const c=function(){const e=document.getElementById("simple-browser-settings");if(e){const t=e.getAttribute("data-settings");if(t)return JSON.parse(t)}throw new Error("Could not load settings")}(),a=document.querySelector("iframe"),u=document.querySelector(".header"),d=u.querySelector(".url-input"),i=u.querySelector(".forward-button"),s=u.querySelector(".back-button"),l=u.querySelector(".reload-button"),f=u.querySelector(".open-external-button");function y(e){document.body.classList.toggle("enable-focus-lock-indicator",e)}window.addEventListener("message",e=>{switch(e.data.type){case"focus":a.focus();break;case"didChangeFocusLockIndicatorEnabled":y(e.data.enabled)}}),o.onceDocumentLoaded(()=>{function e(e){try{const t=new URL(e);t.searchParams.append("vscodeBrowserReqId",Date.now().toString()),a.src=t.toString()}catch(t){a.src=e}}setInterval(()=>{var e;const t="IFRAME"===(null===(e=document.activeElement)||void 0===e?void 0:e.tagName);document.body.classList.toggle("iframe-focused",t)},50),a.addEventListener("load",()=>{}),d.addEventListener("change",t=>{e(t.target.value)}),i.addEventListener("click",()=>{history.forward()}),s.addEventListener("click",()=>{history.back()}),f.addEventListener("click",()=>{r.postMessage({type:"openExternal",url:d.value})}),l.addEventListener("click",()=>{a.src=d.value}),e(c.url),d.value=c.url,y(c.focusLockIndicatorEnabled)})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.onceDocumentLoaded=void 0,t.onceDocumentLoaded=function(e){"loading"===document.readyState||"uninitialized"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}}]); +!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(1),r=acquireVsCodeApi();const c=function(){const e=document.getElementById("simple-browser-settings");if(e){const t=e.getAttribute("data-settings");if(t)return JSON.parse(t)}throw new Error("Could not load settings")}(),a=document.querySelector("iframe"),u=document.querySelector(".header"),d=u.querySelector(".url-input"),i=u.querySelector(".forward-button"),l=u.querySelector(".back-button"),s=u.querySelector(".reload-button"),f=u.querySelector(".open-external-button");function y(e){document.body.classList.toggle("enable-focus-lock-indicator",e)}window.addEventListener("message",e=>{switch(e.data.type){case"focus":a.focus();break;case"didChangeFocusLockIndicatorEnabled":y(e.data.enabled)}}),(0,o.onceDocumentLoaded)(()=>{function e(e){try{const t=new URL(e);t.searchParams.append("vscodeBrowserReqId",Date.now().toString()),a.src=t.toString()}catch(t){a.src=e}}setInterval(()=>{var e;const t="IFRAME"===(null===(e=document.activeElement)||void 0===e?void 0:e.tagName);document.body.classList.toggle("iframe-focused",t)},50),a.addEventListener("load",()=>{}),d.addEventListener("change",t=>{e(t.target.value)}),i.addEventListener("click",()=>{history.forward()}),l.addEventListener("click",()=>{history.back()}),f.addEventListener("click",()=>{r.postMessage({type:"openExternal",url:d.value})}),s.addEventListener("click",()=>{e(d.value)}),e(c.url),d.value=c.url,y(c.focusLockIndicatorEnabled)})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.onceDocumentLoaded=void 0,t.onceDocumentLoaded=function(e){"loading"===document.readyState||"uninitialized"===document.readyState?document.addEventListener("DOMContentLoaded",e):e()}}]); //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/extensions/simple-browser/media/index.js.map b/extensions/simple-browser/media/index.js.map index df62d30257b..614e719426b 100644 --- a/extensions/simple-browser/media/index.js.map +++ b/extensions/simple-browser/media/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./preview-src/index.ts","webpack:///./preview-src/events.ts"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","events_1","vscode","acquireVsCodeApi","settings","element","document","getElementById","data","getAttribute","JSON","parse","Error","getSettings","iframe","querySelector","header","input","forwardButton","backButton","reloadButton","openExternalButton","toggleFocusLockIndicatorEnabled","enabled","body","classList","toggle","window","addEventListener","e","type","focus","onceDocumentLoaded","navigateTo","rawUrl","url","URL","searchParams","append","Date","now","toString","src","_a","setInterval","iframeFocused","activeElement","tagName","target","history","forward","back","postMessage","focusLockIndicatorEnabled","f","readyState"],"mappings":"aACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QAKfF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,GAIjBlC,EAAoBA,EAAoBmC,EAAI,G,+BC7ErDrB,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,IACtD,MAAMe,EAAW,EAAQ,GACnBC,EAASC,mBAWf,MAAMC,EAVN,WACI,MAAMC,EAAUC,SAASC,eAAe,2BACxC,GAAIF,EAAS,CACT,MAAMG,EAAOH,EAAQI,aAAa,iBAClC,GAAID,EACA,OAAOE,KAAKC,MAAMH,GAG1B,MAAM,IAAII,MAAM,2BAEHC,GACXC,EAASR,SAASS,cAAc,UAChCC,EAASV,SAASS,cAAc,WAChCE,EAAQD,EAAOD,cAAc,cAC7BG,EAAgBF,EAAOD,cAAc,mBACrCI,EAAaH,EAAOD,cAAc,gBAClCK,EAAeJ,EAAOD,cAAc,kBACpCM,EAAqBL,EAAOD,cAAc,yBA8DhD,SAASO,EAAgCC,GACrCjB,SAASkB,KAAKC,UAAUC,OAAO,8BAA+BH,GA9DlEI,OAAOC,iBAAiB,UAAWC,IAC/B,OAAQA,EAAErB,KAAKsB,MACX,IAAK,QAEGhB,EAAOiB,QACP,MAER,IAAK,qCAEGT,EAAgCO,EAAErB,KAAKe,YAKvDtB,EAAS+B,mBAAmB,KAkCxB,SAASC,EAAWC,GAChB,IACI,MAAMC,EAAM,IAAIC,IAAIF,GAGpBC,EAAIE,aAAaC,OAAO,qBAAsBC,KAAKC,MAAMC,YACzD3B,EAAO4B,IAAMP,EAAIM,WAErB,MAAOE,GACH7B,EAAO4B,IAAMR,GA1CrBU,YAAY,KACR,IAAID,EACJ,MAAME,EAAoG,YAAjD,QAAjCF,EAAKrC,SAASwC,qBAAkC,IAAPH,OAAgB,EAASA,EAAGI,SAC7FzC,SAASkB,KAAKC,UAAUC,OAAO,iBAAkBmB,IAClD,IACH/B,EAAOc,iBAAiB,OAAQ,QAGhCX,EAAMW,iBAAiB,SAAUC,IAE7BI,EADYJ,EAAEmB,OAAO9D,SAGzBgC,EAAcU,iBAAiB,QAAS,KACpCqB,QAAQC,YAEZ/B,EAAWS,iBAAiB,QAAS,KACjCqB,QAAQE,SAEZ9B,EAAmBO,iBAAiB,QAAS,KACzC1B,EAAOkD,YAAY,CACftB,KAAM,eACNK,IAAKlB,EAAM/B,UAGnBkC,EAAaQ,iBAAiB,QAAS,KAInCd,EAAO4B,IAAMzB,EAAM/B,QAEvB+C,EAAW7B,EAAS+B,KACpBlB,EAAM/B,MAAQkB,EAAS+B,IACvBb,EAAgClB,EAASiD,8B,6BCpE7C1E,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,IACtDnB,EAAQiE,wBAAqB,EAS7BjE,EAAQiE,mBARR,SAA4BsB,GACI,YAAxBhD,SAASiD,YAAoD,kBAAxBjD,SAASiD,WAC9CjD,SAASsB,iBAAiB,mBAAoB0B,GAG9CA","file":"index.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","\"use strict\";\n/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst events_1 = require(\"./events\");\nconst vscode = acquireVsCodeApi();\nfunction getSettings() {\n const element = document.getElementById('simple-browser-settings');\n if (element) {\n const data = element.getAttribute('data-settings');\n if (data) {\n return JSON.parse(data);\n }\n }\n throw new Error(`Could not load settings`);\n}\nconst settings = getSettings();\nconst iframe = document.querySelector('iframe');\nconst header = document.querySelector('.header');\nconst input = header.querySelector('.url-input');\nconst forwardButton = header.querySelector('.forward-button');\nconst backButton = header.querySelector('.back-button');\nconst reloadButton = header.querySelector('.reload-button');\nconst openExternalButton = header.querySelector('.open-external-button');\nwindow.addEventListener('message', e => {\n switch (e.data.type) {\n case 'focus':\n {\n iframe.focus();\n break;\n }\n case 'didChangeFocusLockIndicatorEnabled':\n {\n toggleFocusLockIndicatorEnabled(e.data.enabled);\n break;\n }\n }\n});\nevents_1.onceDocumentLoaded(() => {\n setInterval(() => {\n var _a;\n const iframeFocused = ((_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.tagName) === 'IFRAME';\n document.body.classList.toggle('iframe-focused', iframeFocused);\n }, 50);\n iframe.addEventListener('load', () => {\n // Noop\n });\n input.addEventListener('change', e => {\n const url = e.target.value;\n navigateTo(url);\n });\n forwardButton.addEventListener('click', () => {\n history.forward();\n });\n backButton.addEventListener('click', () => {\n history.back();\n });\n openExternalButton.addEventListener('click', () => {\n vscode.postMessage({\n type: 'openExternal',\n url: input.value\n });\n });\n reloadButton.addEventListener('click', () => {\n // This does not seem to trigger what we want\n // history.go(0);\n // This incorrectly adds entries to the history but does reload\n iframe.src = input.value;\n });\n navigateTo(settings.url);\n input.value = settings.url;\n toggleFocusLockIndicatorEnabled(settings.focusLockIndicatorEnabled);\n function navigateTo(rawUrl) {\n try {\n const url = new URL(rawUrl);\n // Try to bust the cache for the iframe\n // There does not appear to be any way to reliably do this except modifying the url\n url.searchParams.append('vscodeBrowserReqId', Date.now().toString());\n iframe.src = url.toString();\n }\n catch (_a) {\n iframe.src = rawUrl;\n }\n }\n});\nfunction toggleFocusLockIndicatorEnabled(enabled) {\n document.body.classList.toggle('enable-focus-lock-indicator', enabled);\n}\n","\"use strict\";\n/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.onceDocumentLoaded = void 0;\nfunction onceDocumentLoaded(f) {\n if (document.readyState === 'loading' || document.readyState === 'uninitialized') {\n document.addEventListener('DOMContentLoaded', f);\n }\n else {\n f();\n }\n}\nexports.onceDocumentLoaded = onceDocumentLoaded;\n"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./preview-src/index.ts","webpack:///./preview-src/events.ts"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","events_1","vscode","acquireVsCodeApi","settings","element","document","getElementById","data","getAttribute","JSON","parse","Error","getSettings","iframe","querySelector","header","input","forwardButton","backButton","reloadButton","openExternalButton","toggleFocusLockIndicatorEnabled","enabled","body","classList","toggle","window","addEventListener","e","type","focus","onceDocumentLoaded","navigateTo","rawUrl","url","URL","searchParams","append","Date","now","toString","src","_a","setInterval","iframeFocused","activeElement","tagName","target","history","forward","back","postMessage","focusLockIndicatorEnabled","f","readyState"],"mappings":"aACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QAKfF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,GAIjBlC,EAAoBA,EAAoBmC,EAAI,G,+BC7ErDrB,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,IACtD,MAAMe,EAAW,EAAQ,GACnBC,EAASC,mBAWf,MAAMC,EAVN,WACI,MAAMC,EAAUC,SAASC,eAAe,2BACxC,GAAIF,EAAS,CACT,MAAMG,EAAOH,EAAQI,aAAa,iBAClC,GAAID,EACA,OAAOE,KAAKC,MAAMH,GAG1B,MAAM,IAAII,MAAM,2BAEHC,GACXC,EAASR,SAASS,cAAc,UAChCC,EAASV,SAASS,cAAc,WAChCE,EAAQD,EAAOD,cAAc,cAC7BG,EAAgBF,EAAOD,cAAc,mBACrCI,EAAaH,EAAOD,cAAc,gBAClCK,EAAeJ,EAAOD,cAAc,kBACpCM,EAAqBL,EAAOD,cAAc,yBAgEhD,SAASO,EAAgCC,GACrCjB,SAASkB,KAAKC,UAAUC,OAAO,8BAA+BH,GAhElEI,OAAOC,iBAAiB,UAAWC,IAC/B,OAAQA,EAAErB,KAAKsB,MACX,IAAK,QAEGhB,EAAOiB,QACP,MAER,IAAK,qCAEGT,EAAgCO,EAAErB,KAAKe,aAKvD,EAAItB,EAAS+B,oBAAoB,KAoC7B,SAASC,EAAWC,GAChB,IACI,MAAMC,EAAM,IAAIC,IAAIF,GAGpBC,EAAIE,aAAaC,OAAO,qBAAsBC,KAAKC,MAAMC,YACzD3B,EAAO4B,IAAMP,EAAIM,WAErB,MAAOE,GACH7B,EAAO4B,IAAMR,GA5CrBU,YAAY,KACR,IAAID,EACJ,MAAME,EAAoG,YAAjD,QAAjCF,EAAKrC,SAASwC,qBAAkC,IAAPH,OAAgB,EAASA,EAAGI,SAC7FzC,SAASkB,KAAKC,UAAUC,OAAO,iBAAkBmB,IAClD,IACH/B,EAAOc,iBAAiB,OAAQ,QAGhCX,EAAMW,iBAAiB,SAAUC,IAE7BI,EADYJ,EAAEmB,OAAO9D,SAGzBgC,EAAcU,iBAAiB,QAAS,KACpCqB,QAAQC,YAEZ/B,EAAWS,iBAAiB,QAAS,KACjCqB,QAAQE,SAEZ9B,EAAmBO,iBAAiB,QAAS,KACzC1B,EAAOkD,YAAY,CACftB,KAAM,eACNK,IAAKlB,EAAM/B,UAGnBkC,EAAaQ,iBAAiB,QAAS,KAMnCK,EAAWhB,EAAM/B,SAErB+C,EAAW7B,EAAS+B,KACpBlB,EAAM/B,MAAQkB,EAAS+B,IACvBb,EAAgClB,EAASiD,8B,6BCtE7C1E,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,IACtDnB,EAAQiE,wBAAqB,EAS7BjE,EAAQiE,mBARR,SAA4BsB,GACI,YAAxBhD,SAASiD,YAAoD,kBAAxBjD,SAASiD,WAC9CjD,SAASsB,iBAAiB,mBAAoB0B,GAG9CA","file":"index.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","\"use strict\";\n/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst events_1 = require(\"./events\");\nconst vscode = acquireVsCodeApi();\nfunction getSettings() {\n const element = document.getElementById('simple-browser-settings');\n if (element) {\n const data = element.getAttribute('data-settings');\n if (data) {\n return JSON.parse(data);\n }\n }\n throw new Error(`Could not load settings`);\n}\nconst settings = getSettings();\nconst iframe = document.querySelector('iframe');\nconst header = document.querySelector('.header');\nconst input = header.querySelector('.url-input');\nconst forwardButton = header.querySelector('.forward-button');\nconst backButton = header.querySelector('.back-button');\nconst reloadButton = header.querySelector('.reload-button');\nconst openExternalButton = header.querySelector('.open-external-button');\nwindow.addEventListener('message', e => {\n switch (e.data.type) {\n case 'focus':\n {\n iframe.focus();\n break;\n }\n case 'didChangeFocusLockIndicatorEnabled':\n {\n toggleFocusLockIndicatorEnabled(e.data.enabled);\n break;\n }\n }\n});\n(0, events_1.onceDocumentLoaded)(() => {\n setInterval(() => {\n var _a;\n const iframeFocused = ((_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.tagName) === 'IFRAME';\n document.body.classList.toggle('iframe-focused', iframeFocused);\n }, 50);\n iframe.addEventListener('load', () => {\n // Noop\n });\n input.addEventListener('change', e => {\n const url = e.target.value;\n navigateTo(url);\n });\n forwardButton.addEventListener('click', () => {\n history.forward();\n });\n backButton.addEventListener('click', () => {\n history.back();\n });\n openExternalButton.addEventListener('click', () => {\n vscode.postMessage({\n type: 'openExternal',\n url: input.value\n });\n });\n reloadButton.addEventListener('click', () => {\n // This does not seem to trigger what we want\n // history.go(0);\n // This incorrectly adds entries to the history but does reload\n // It also always incorrectly always loads the value in the input bar,\n // which may not match the current page if the user has navigated\n navigateTo(input.value);\n });\n navigateTo(settings.url);\n input.value = settings.url;\n toggleFocusLockIndicatorEnabled(settings.focusLockIndicatorEnabled);\n function navigateTo(rawUrl) {\n try {\n const url = new URL(rawUrl);\n // Try to bust the cache for the iframe\n // There does not appear to be any way to reliably do this except modifying the url\n url.searchParams.append('vscodeBrowserReqId', Date.now().toString());\n iframe.src = url.toString();\n }\n catch (_a) {\n iframe.src = rawUrl;\n }\n }\n});\nfunction toggleFocusLockIndicatorEnabled(enabled) {\n document.body.classList.toggle('enable-focus-lock-indicator', enabled);\n}\n","\"use strict\";\n/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.onceDocumentLoaded = void 0;\nfunction onceDocumentLoaded(f) {\n if (document.readyState === 'loading' || document.readyState === 'uninitialized') {\n document.addEventListener('DOMContentLoaded', f);\n }\n else {\n f();\n }\n}\nexports.onceDocumentLoaded = onceDocumentLoaded;\n"],"sourceRoot":""} \ No newline at end of file diff --git a/extensions/simple-browser/preview-src/index.ts b/extensions/simple-browser/preview-src/index.ts index 83cb3dedb16..36fe4eb8e15 100644 --- a/extensions/simple-browser/preview-src/index.ts +++ b/extensions/simple-browser/preview-src/index.ts @@ -80,7 +80,9 @@ onceDocumentLoaded(() => { // history.go(0); // This incorrectly adds entries to the history but does reload - iframe.src = input.value; + // It also always incorrectly always loads the value in the input bar, + // which may not match the current page if the user has navigated + navigateTo(input.value); }); navigateTo(settings.url); diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index d5844e1af8e..d2b72e46ba8 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -9,7 +9,9 @@ "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "enableProposedApi": true, - "requiresWorkspaceTrust": "onDemand", + "workspaceTrust": { + "required": "onDemand" + }, "engines": { "vscode": "^1.30.0" }, @@ -19,13 +21,12 @@ "dependencies": { "jsonc-parser": "^2.2.1", "semver": "5.5.1", - "typescript-vscode-sh-plugin": "^0.6.14", + "typescript-vscode-sh-plugin": "^0.7.1", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.1.1" }, "devDependencies": { "@types/node": "^12.19.9", - "@types/rimraf": "^2.0.4", "@types/semver": "^5.5.0" }, "scripts": { diff --git a/extensions/typescript-language-features/src/languageFeatures/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts index f64c0b27553..4284366e273 100644 --- a/extensions/typescript-language-features/src/languageFeatures/refactor.ts +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -335,7 +335,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { + async _provideSemanticTokens(document: vscode.TextDocument, requestArg: Proto.EncodedSemanticClassificationsRequestArgs, token: vscode.CancellationToken): Promise { const file = this.client.toOpenedFilePath(document); if (!file) { return null; @@ -75,6 +74,8 @@ class DocumentSemanticTokensProvider implements vscode.DocumentSemanticTokensPro const versionBeforeRequest = document.version; + requestArg.format = '2020'; + const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', requestArg, token, { cancelOnResourceChange: document.uri }); @@ -148,9 +149,42 @@ function waitForDocumentChangesToEnd(document: vscode.TextDocument) { } -// typescript-vscode-sh-plugin encodes type and modifiers in the classification: +// typescript encodes type and modifiers in the classification: // TSClassification = (TokenType + 1) << 8 + TokenModifier +declare const enum TokenType { + class = 0, + enum = 1, + interface = 2, + namespace = 3, + typeParameter = 4, + type = 5, + parameter = 6, + variable = 7, + enumMember = 8, + property = 9, + function = 10, + method = 11, + _ = 12 +} +declare const enum TokenModifier { + declaration = 0, + static = 1, + async = 2, + readonly = 3, + defaultLibrary = 4, + local = 5, + _ = 6 +} +declare const enum TokenEncodingConsts { + typeOffset = 8, + modifierMask = 255 +} +declare const enum VersionRequirement { + major = 3, + minor = 7 +} + function getTokenTypeFromClassification(tsClassification: number): number | undefined { if (tsClassification > TokenEncodingConsts.modifierMask) { return (tsClassification >> TokenEncodingConsts.typeOffset) - 1; @@ -174,7 +208,7 @@ tokenTypes[TokenType.variable] = 'variable'; tokenTypes[TokenType.enumMember] = 'enumMember'; tokenTypes[TokenType.property] = 'property'; tokenTypes[TokenType.function] = 'function'; -tokenTypes[TokenType.member] = 'method'; +tokenTypes[TokenType.method] = 'method'; const tokenModifiers: string[] = []; tokenModifiers[TokenModifier.async] = 'async'; @@ -184,14 +218,6 @@ tokenModifiers[TokenModifier.static] = 'static'; tokenModifiers[TokenModifier.local] = 'local'; tokenModifiers[TokenModifier.defaultLibrary] = 'defaultLibrary'; -// make sure token types and modifiers are complete -if (tokenTypes.filter(t => !!t).length !== TokenType._) { - console.warn('typescript-vscode-sh-plugin has added new tokens types.'); -} -if (tokenModifiers.filter(t => !!t).length !== TokenModifier._) { - console.warn('typescript-vscode-sh-plugin has added new tokens modifiers.'); -} - // mapping for the original ExperimentalProtocol.ClassificationType from TypeScript (only used when plugin is not available) const tokenTypeMap: number[] = []; tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = TokenType.class; diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index f95020cdbe7..f6a849aa15a 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -2,37 +2,11 @@ # yarn lockfile v1 -"@types/glob@*": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" - integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - -"@types/minimatch@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== - -"@types/node@*": - version "14.11.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256" - integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA== - "@types/node@^12.19.9": version "12.19.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.9.tgz#990ad687ad8b26ef6dcc34a4f69c33d40c95b679" integrity sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q== -"@types/rimraf@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.4.tgz#403887b0b53c6100a6c35d2ab24f6ccc042fec46" - integrity sha512-8gBudvllD2A/c0CcEX/BivIDorHFt5UI5m46TsNj8DjWCCTTZT74kEe4g+QsY7P/B9WdO98d82zZgXO/RQzu2Q== - dependencies: - "@types/glob" "*" - "@types/node" "*" - "@types/semver@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" @@ -74,10 +48,10 @@ semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -typescript-vscode-sh-plugin@^0.6.14: - version "0.6.14" - resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.6.14.tgz#a81031b502f6346a26ea49ce082438c3e353bb38" - integrity sha512-AkNlRBbI6K7gk29O92qthNSvc6jjmNQ6isVXoYxkFwPa8D04tIv2SOPd+sd+mNpso4tNdL2gy7nVtrd5yFqvlA== +typescript-vscode-sh-plugin@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.7.1.tgz#0be744032aa7fdcb764497342fd0fd2d093ed7ca" + integrity sha512-upXxgeI21tEMJxRVxF4PekQBM+eF0cRp0GgD2sblQkEm43NR+Fcbusa+FKAekUgwkbjVq1Loq+vKCCUbxM6R8A== vscode-extension-telemetry@0.1.1: version "0.1.1" diff --git a/extensions/vscode-api-tests/.gitignore b/extensions/vscode-api-tests/.gitignore index 8e5962ee727..19685d0f18e 100644 --- a/extensions/vscode-api-tests/.gitignore +++ b/extensions/vscode-api-tests/.gitignore @@ -1,2 +1,3 @@ out -node_modules \ No newline at end of file +node_modules +testWorkspace/.vscode/tasks.json diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 7e576805d5c..6d39b43c989 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -5,7 +5,9 @@ "publisher": "vscode", "license": "MIT", "enableProposedApi": true, - "requiresWorkspaceTrust": "onDemand", + "workspaceTrust": { + "required": "onDemand" + }, "private": true, "activationEvents": [], "main": "./out/extension", diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 72e771489bc..027370243b0 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -894,7 +894,7 @@ suite('Notebook API tests', function () { await saveAllFilesAndCloseAll(secondResource); }); - test('multiple tabs: different editors with same document', async function () { + test.skip('multiple tabs: different editors with same document', async function () { const resource = await createRandomFile('', undefined, '.vsctestnb'); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const firstNotebookEditor = vscode.window.activeNotebookEditor; diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts index 5ec438a1714..8d3c89507d6 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.tasks.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, env, UIKind, ShellExecution, TaskExecution, Terminal, Event, workspace, ConfigurationTarget } from 'vscode'; +import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, env, UIKind, ShellExecution, TaskExecution, Terminal, Event, workspace, ConfigurationTarget, TaskProcessStartEvent } from 'vscode'; import { assertNoRpc } from '../utils'; // Disable tasks tests: @@ -32,245 +32,300 @@ import { assertNoRpc } from '../utils'; disposables.length = 0; }); - test('CustomExecution task should start and shutdown successfully', async () => { - interface CustomTestingTaskDefinition extends TaskDefinition { - /** - * One of the task properties. This can be used to customize the task in the tasks.json - */ - customProp1: string; - } - const taskType: string = 'customTesting'; - const taskName = 'First custom task'; - let isPseudoterminalClosed = false; - // There's a strict order that should be observed here: - // 1. The terminal opens - // 2. The terminal is written to. - // 3. The terminal is closed. - enum TestOrder { - Start, - TerminalOpened, - TerminalWritten, - TerminalClosed - } - - let testOrder = TestOrder.Start; - - // Launch the task - const terminal = await new Promise(r => { - disposables.push(window.onDidOpenTerminal(e => { - assert.strictEqual(testOrder, TestOrder.Start); - testOrder = TestOrder.TerminalOpened; - r(e); - })); - disposables.push(tasks.registerTaskProvider(taskType, { - provideTasks: () => { - const result: Task[] = []; - const kind: CustomTestingTaskDefinition = { - type: taskType, - customProp1: 'testing task one' - }; - const writeEmitter = new EventEmitter(); - const execution = new CustomExecution((): Thenable => { - const pty: Pseudoterminal = { - onDidWrite: writeEmitter.event, - open: () => writeEmitter.fire('testing\r\n'), - close: () => isPseudoterminalClosed = true - }; - return Promise.resolve(pty); + suite('ShellExecution', () => { + test('Execution from onDidEndTaskProcess and onDidStartTaskProcess are equal to original', () => { + return new Promise(async (resolve) => { + const task = new Task({ type: 'testTask' }, TaskScope.Workspace, 'echo', 'testTask', new ShellExecution('echo', ['hello test'])); + let taskExecution: TaskExecution | undefined; + const executeDoneEvent: EventEmitter = new EventEmitter(); + const taskExecutionShouldBeSet: Promise = new Promise(resolve => { + const disposable = executeDoneEvent.event(() => { + resolve(); + disposable.dispose(); }); - const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution); - result.push(task); - return result; + }); + let count = 2; + const progressMade: EventEmitter = new EventEmitter(); + let startSucceeded = false; + let endSucceeded = false; + disposables.push(progressMade.event(() => { + count--; + if ((count === 0) && startSucceeded && endSucceeded) { + resolve(); + } + })); + + + disposables.push(tasks.onDidStartTaskProcess(async (e) => { + await taskExecutionShouldBeSet; + if (e.execution === taskExecution) { + startSucceeded = true; + progressMade.fire(); + } + })); + + disposables.push(tasks.onDidEndTaskProcess(async (e) => { + await taskExecutionShouldBeSet; + if (e.execution === taskExecution) { + endSucceeded = true; + progressMade.fire(); + } + })); + taskExecution = await tasks.executeTask(task); + executeDoneEvent.fire(); + }); + }); + + test('dependsOn task should start with a different processId (#118256)', async () => { + // Set up dependsOn task by creating tasks.json since this is not possible via the API + // Tasks API + const tasksConfig = workspace.getConfiguration('tasks'); + await tasksConfig.update('version', '2.0.0', ConfigurationTarget.Workspace); + await tasksConfig.update('tasks', [ + { + label: 'taskToDependOn', + type: 'shell', + command: 'sleep 1', + problemMatcher: [] }, - resolveTask(_task: Task): Task | undefined { - assert.fail('resolveTask should not trigger during the test'); + { + label: 'Run this task', + type: 'shell', + command: 'sleep 1', + problemMatcher: [], + dependsOn: 'taskToDependOn' } - })); - commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`); - }); + ], ConfigurationTarget.Workspace); - // Verify the output - await new Promise(r => { - disposables.push(window.onDidWriteTerminalData(e => { - if (e.terminal !== terminal) { - return; - } - assert.strictEqual(testOrder, TestOrder.TerminalOpened); - testOrder = TestOrder.TerminalWritten; - assert.notStrictEqual(terminal, undefined); - assert.strictEqual(e.data, 'testing\r\n'); - r(); - })); - }); + // Run the task + commands.executeCommand('workbench.action.tasks.runTask', 'Run this task'); - // Dispose the terminal - await new Promise(r => { - disposables.push(window.onDidCloseTerminal(() => { - assert.strictEqual(testOrder, TestOrder.TerminalWritten); - testOrder = TestOrder.TerminalClosed; - // Pseudoterminal.close should have fired by now, additionally we want - // to make sure all events are flushed before continuing with more tests - assert.ok(isPseudoterminalClosed); - r(); - })); - terminal.dispose(); - }); - }); - - test('sync CustomExecution task should flush all data on close', async () => { - interface CustomTestingTaskDefinition extends TaskDefinition { - /** - * One of the task properties. This can be used to customize the task in the tasks.json - */ - customProp1: string; - } - const taskType: string = 'customTesting'; - const taskName = 'First custom task'; - - // Launch the task - const terminal = await new Promise(r => { - disposables.push(window.onDidOpenTerminal(e => r(e))); - disposables.push(tasks.registerTaskProvider(taskType, { - provideTasks: () => { - const result: Task[] = []; - const kind: CustomTestingTaskDefinition = { - type: taskType, - customProp1: 'testing task one' - }; - const writeEmitter = new EventEmitter(); - const closeEmitter = new EventEmitter(); - const execution = new CustomExecution((): Thenable => { - const pty: Pseudoterminal = { - onDidWrite: writeEmitter.event, - onDidClose: closeEmitter.event, - open: () => { - writeEmitter.fire('exiting'); - closeEmitter.fire(); - }, - close: () => { } - }; - return Promise.resolve(pty); - }); - const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution); - result.push(task); - return result; - }, - resolveTask(_task: Task): Task | undefined { - assert.fail('resolveTask should not trigger during the test'); - } - })); - commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`); - }); - - // Verify the output - await new Promise(r => { - disposables.push(window.onDidWriteTerminalData(e => { - if (e.terminal !== terminal) { - return; - } - assert.strictEqual(e.data, 'exiting'); - r(); - })); - }); - - // Dispose the terminal - await new Promise(r => { - disposables.push(window.onDidCloseTerminal(() => r())); - terminal.dispose(); - }); - }); - - test('Execution from onDidEndTaskProcess and onDidStartTaskProcess are equal to original', () => { - return new Promise(async (resolve) => { - const task = new Task({ type: 'testTask' }, TaskScope.Workspace, 'echo', 'testTask', new ShellExecution('echo', ['hello test'])); - let taskExecution: TaskExecution | undefined; - const executeDoneEvent: EventEmitter = new EventEmitter(); - const taskExecutionShouldBeSet: Promise = new Promise(resolve => { - const disposable = executeDoneEvent.event(() => { - resolve(); - disposable.dispose(); + // Listen for first task and verify valid process ID + const startEvent1 = await new Promise(r => { + const listener = tasks.onDidStartTaskProcess(async (e) => { + if (e.execution.task.name === 'taskToDependOn') { + listener.dispose(); + r(e); + } }); }); - let count = 2; - const progressMade: EventEmitter = new EventEmitter(); - let startSucceeded = false; - let endSucceeded = false; - disposables.push(progressMade.event(() => { - count--; - if ((count === 0) && startSucceeded && endSucceeded) { - resolve(); - } - })); + assert.ok(startEvent1.processId); + // Listen for second task, verify valid process ID and that it's not the process ID of + // the first task + const startEvent2 = await new Promise(r => { + const listener = tasks.onDidStartTaskProcess(async (e) => { + if (e.execution.task.name === 'Run this task') { + listener.dispose(); + r(e); + } + }); + }); + assert.ok(startEvent2.processId); + assert.notStrictEqual(startEvent1.processId, startEvent2.processId); - disposables.push(tasks.onDidStartTaskProcess(async (e) => { - await taskExecutionShouldBeSet; - if (e.execution === taskExecution) { - startSucceeded = true; - progressMade.fire(); - } - })); - - disposables.push(tasks.onDidEndTaskProcess(async (e) => { - await taskExecutionShouldBeSet; - if (e.execution === taskExecution) { - endSucceeded = true; - progressMade.fire(); - } - })); - taskExecution = await tasks.executeTask(task); - executeDoneEvent.fire(); + // Clear out tasks config + await tasksConfig.update('tasks', []); }); }); - // https://github.com/microsoft/vscode/issues/100577 - test('A CustomExecution task can be fetched and executed', () => { - return new Promise(async (resolve, reject) => { - class CustomTerminal implements Pseudoterminal { - private readonly writeEmitter = new EventEmitter(); - public readonly onDidWrite: Event = this.writeEmitter.event; - public async close(): Promise { } - private closeEmitter = new EventEmitter(); - onDidClose: Event = this.closeEmitter.event; - public open(): void { - this.closeEmitter.fire(); - resolve(); - } + suite('CustomExecution', () => { + test('task should start and shutdown successfully', async () => { + interface CustomTestingTaskDefinition extends TaskDefinition { + /** + * One of the task properties. This can be used to customize the task in the tasks.json + */ + customProp1: string; + } + const taskType: string = 'customTesting'; + const taskName = 'First custom task'; + let isPseudoterminalClosed = false; + // There's a strict order that should be observed here: + // 1. The terminal opens + // 2. The terminal is written to. + // 3. The terminal is closed. + enum TestOrder { + Start, + TerminalOpened, + TerminalWritten, + TerminalClosed } - function buildTask(): Task { - const task = new Task( - { - type: 'customTesting', + let testOrder = TestOrder.Start; + + // Launch the task + const terminal = await new Promise(r => { + disposables.push(window.onDidOpenTerminal(e => { + assert.strictEqual(testOrder, TestOrder.Start); + testOrder = TestOrder.TerminalOpened; + r(e); + })); + disposables.push(tasks.registerTaskProvider(taskType, { + provideTasks: () => { + const result: Task[] = []; + const kind: CustomTestingTaskDefinition = { + type: taskType, + customProp1: 'testing task one' + }; + const writeEmitter = new EventEmitter(); + const execution = new CustomExecution((): Thenable => { + const pty: Pseudoterminal = { + onDidWrite: writeEmitter.event, + open: () => writeEmitter.fire('testing\r\n'), + close: () => isPseudoterminalClosed = true + }; + return Promise.resolve(pty); + }); + const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution); + result.push(task); + return result; }, - TaskScope.Workspace, - 'Test Task', - 'customTesting', - new CustomExecution( - async (): Promise => { - return new CustomTerminal(); - } - ) - ); - return task; - } + resolveTask(_task: Task): Task | undefined { + assert.fail('resolveTask should not trigger during the test'); + } + })); + commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`); + }); - disposables.push(tasks.registerTaskProvider('customTesting', { - provideTasks: () => { - return [buildTask()]; - }, - resolveTask(_task: Task): undefined { - return undefined; + // Verify the output + await new Promise(r => { + disposables.push(window.onDidWriteTerminalData(e => { + if (e.terminal !== terminal) { + return; + } + assert.strictEqual(testOrder, TestOrder.TerminalOpened); + testOrder = TestOrder.TerminalWritten; + assert.notStrictEqual(terminal, undefined); + assert.strictEqual(e.data, 'testing\r\n'); + r(); + })); + }); + + // Dispose the terminal + await new Promise(r => { + disposables.push(window.onDidCloseTerminal(() => { + assert.strictEqual(testOrder, TestOrder.TerminalWritten); + testOrder = TestOrder.TerminalClosed; + // Pseudoterminal.close should have fired by now, additionally we want + // to make sure all events are flushed before continuing with more tests + assert.ok(isPseudoterminalClosed); + r(); + })); + terminal.dispose(); + }); + }); + + test('sync task should flush all data on close', async () => { + interface CustomTestingTaskDefinition extends TaskDefinition { + /** + * One of the task properties. This can be used to customize the task in the tasks.json + */ + customProp1: string; + } + const taskType: string = 'customTesting'; + const taskName = 'First custom task'; + + // Launch the task + const terminal = await new Promise(r => { + disposables.push(window.onDidOpenTerminal(e => r(e))); + disposables.push(tasks.registerTaskProvider(taskType, { + provideTasks: () => { + const result: Task[] = []; + const kind: CustomTestingTaskDefinition = { + type: taskType, + customProp1: 'testing task one' + }; + const writeEmitter = new EventEmitter(); + const closeEmitter = new EventEmitter(); + const execution = new CustomExecution((): Thenable => { + const pty: Pseudoterminal = { + onDidWrite: writeEmitter.event, + onDidClose: closeEmitter.event, + open: () => { + writeEmitter.fire('exiting'); + closeEmitter.fire(); + }, + close: () => { } + }; + return Promise.resolve(pty); + }); + const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution); + result.push(task); + return result; + }, + resolveTask(_task: Task): Task | undefined { + assert.fail('resolveTask should not trigger during the test'); + } + })); + commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`); + }); + + // Verify the output + await new Promise(r => { + disposables.push(window.onDidWriteTerminalData(e => { + if (e.terminal !== terminal) { + return; + } + assert.strictEqual(e.data, 'exiting'); + r(); + })); + }); + + // Dispose the terminal + await new Promise(r => { + disposables.push(window.onDidCloseTerminal(() => r())); + terminal.dispose(); + }); + }); + + test('A task can be fetched and executed (#100577)', () => { + return new Promise(async (resolve, reject) => { + class CustomTerminal implements Pseudoterminal { + private readonly writeEmitter = new EventEmitter(); + public readonly onDidWrite: Event = this.writeEmitter.event; + public async close(): Promise { } + private closeEmitter = new EventEmitter(); + onDidClose: Event = this.closeEmitter.event; + public open(): void { + this.closeEmitter.fire(); + resolve(); + } } - })); - const task = await tasks.fetchTasks({ type: 'customTesting' }); + function buildTask(): Task { + const task = new Task( + { + type: 'customTesting', + }, + TaskScope.Workspace, + 'Test Task', + 'customTesting', + new CustomExecution( + async (): Promise => { + return new CustomTerminal(); + } + ) + ); + return task; + } - if (task && task.length > 0) { - await tasks.executeTask(task[0]); - } else { - reject('fetched task can\'t be undefined'); - } + disposables.push(tasks.registerTaskProvider('customTesting', { + provideTasks: () => { + return [buildTask()]; + }, + resolveTask(_task: Task): undefined { + return undefined; + } + })); + + const task = await tasks.fetchTasks({ type: 'customTesting' }); + + if (task && task.length > 0) { + await tasks.executeTask(task[0]); + } else { + reject('fetched task can\'t be undefined'); + } + }); }); }); }); diff --git a/package.json b/package.json index d4955303b20..22a7b5200e1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.55.0", - "distro": "31fef7780072c13c0c49b0ad6321d4861acbc3ed", + "distro": "ba61d25cf002e9a939ea098bba278e965df7a6e7", "author": { "name": "Microsoft Corporation" }, @@ -78,7 +78,7 @@ "tas-client-umd": "0.1.2", "v8-inspect-profiler": "^0.0.20", "vscode-oniguruma": "1.3.1", - "vscode-proxy-agent": "^0.6.0", + "vscode-proxy-agent": "^0.7.0", "vscode-regexpp": "^3.1.0", "vscode-ripgrep": "^1.11.1", "vscode-sqlite3": "4.0.10", diff --git a/remote/package.json b/remote/package.json index 7722334fce2..0c0e4c62a31 100644 --- a/remote/package.json +++ b/remote/package.json @@ -18,7 +18,7 @@ "spdlog": "^0.11.1", "tas-client-umd": "0.1.2", "vscode-oniguruma": "1.3.1", - "vscode-proxy-agent": "^0.6.0", + "vscode-proxy-agent": "^0.7.0", "vscode-regexpp": "^3.1.0", "vscode-ripgrep": "^1.11.1", "vscode-textmate": "5.2.0", diff --git a/remote/yarn.lock b/remote/yarn.lock index ef6d571818e..ea2f9930fcf 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -376,10 +376,10 @@ vscode-oniguruma@1.3.1: resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.3.1.tgz#e2383879c3485b19f533ec34efea9d7a2b14be8f" integrity sha512-gz6ZBofA7UXafVA+m2Yt2zHKgXC2qedArprIsHAPKByTkwq9l5y/izAGckqxYml7mSbYxTRTfdRwsFq3cwF4LQ== -vscode-proxy-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.6.0.tgz#3ea5c3c82f7abe945d690eb34b2c877cf5833f12" - integrity sha512-dvoLmEO/IxkbcNrRHH6ey8ITfvau4wDg01S+iAJ5Pq/FoAl2ZeE4cK5VEnQ2JHqM20kTLhyZfkjdHq6l7/T+xA== +vscode-proxy-agent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.7.0.tgz#adf19dfbc9beba508b1c72a83cd64b5d1cb9d84c" + integrity sha512-eXGwlzwQJJqutYHjhC0UfBw1Sp8p2UygXACQjMSMKkIufeF68c5Z12s7v7UVBkqW3KfgEh/zDaO/bPkWLI/RZw== dependencies: debug "^3.1.0" http-proxy-agent "^2.1.0" diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index d912576a766..a60bb77be2a 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -699,8 +699,8 @@ export interface CodeAction { * @internal */ export const enum CodeActionTriggerType { - Auto = 1, - Manual = 2, + Invoke = 1, + Auto = 2, } /** diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 7bbcacf2a2b..7a66be2ed6f 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -247,7 +247,7 @@ CommandsRegistry.registerCommand('_executeCodeActionProvider', async function (a const codeActionSet = await getCodeActions( model, validatedRangeOrSelection, - { type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include } }, + { type: modes.CodeActionTriggerType.Invoke, filter: { includeSourceActions: true, include } }, Progress.None, CancellationToken.None); diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 65c19a781f6..aa54079ff85 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -124,7 +124,7 @@ export class QuickFixController extends Disposable implements IEditorContributio MessageController.get(this._editor).closeMessage(); const triggerPosition = this._editor.getPosition(); - this._trigger({ type: CodeActionTriggerType.Manual, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } }); + this._trigger({ type: CodeActionTriggerType.Invoke, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } }); } private _trigger(trigger: CodeActionTrigger) { diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index c3ef8c90f78..d6e380ddc78 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -247,7 +247,7 @@ export class CodeActionModel extends Disposable { } const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, Progress.None, token)); - if (trigger.trigger.type === CodeActionTriggerType.Manual) { + if (trigger.trigger.type === CodeActionTriggerType.Invoke) { this._progressService?.showWhile(actions, 250); } diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index 9f037de5ae6..099789d9504 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -77,7 +77,7 @@ export class CodeActionUi extends Disposable { this._lightBulbWidget.getValue().update(actions, newState.trigger, newState.position); - if (newState.trigger.type === CodeActionTriggerType.Manual) { + if (newState.trigger.type === CodeActionTriggerType.Invoke) { if (newState.trigger.filter?.include) { // Triggered for specific scope // Check to see if we want to auto apply. diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index 53a80a51108..c4987968a98 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -127,7 +127,7 @@ suite('CodeAction', () => { new CodeActionItem(testData.tsLint.abc, provider) ]; - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None); assert.equal(actions.length, 6); assert.deepEqual(actions, expected); }); diff --git a/src/vs/editor/contrib/hover/markerHoverParticipant.ts b/src/vs/editor/contrib/hover/markerHoverParticipant.ts index bb55bc810c2..d13cdc5f9bd 100644 --- a/src/vs/editor/contrib/hover/markerHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/markerHoverParticipant.ts @@ -46,7 +46,7 @@ export class MarkerHover implements IHoverPart { } const markerCodeActionTrigger: CodeActionTrigger = { - type: CodeActionTriggerType.Manual, + type: CodeActionTriggerType.Invoke, filter: { include: CodeActionKind.QuickFix } }; diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index ea75dae1406..48403521d5f 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -113,22 +113,23 @@ export interface IAuthenticationContribution { readonly label: string; } -export interface IWelcomeItem { +export interface IWalkthroughTask { readonly id: string; readonly title: string; readonly description: string; - readonly button: { title: string } & ({ command?: never, link: string } | { command: string, link?: never }), - readonly media: { path: string | { hc: string, light: string, dark: string }, altText: string }, - readonly doneOn?: - | { event: string; command?: never } - | { event?: never; command: string }; + readonly button: + | { title: string, link: string, command?: never } + | { title: string, command: string, link?: never }, + readonly media: { path: string, altText: string }, + readonly doneOn?: { command: string }; readonly when?: string; } -export interface IWelcomeCategory { +export interface IWalkthrough { readonly id: string, readonly title: string; readonly description: string; + readonly tasks: IWalkthroughTask[]; readonly when?: string; } @@ -151,13 +152,12 @@ export interface IExtensionContributions { readonly customEditors?: readonly IWebviewEditor[]; readonly codeActions?: readonly ICodeActionContribution[]; authentication?: IAuthenticationContribution[]; - welcomeItems?: { [category: string]: IWelcomeItem[] }; - welcomeCategories?: IWelcomeCategory[]; + walkthroughs?: IWalkthrough[]; } export type ExtensionKind = 'ui' | 'workspace' | 'web'; - export type ExtensionWorkspaceTrustRequirement = false | 'onStart' | 'onDemand'; +export type ExtensionWorkspaceTrust = { required: ExtensionWorkspaceTrustRequirement, description?: string }; export function isIExtensionIdentifier(thing: any): thing is IExtensionIdentifier { return thing @@ -214,7 +214,7 @@ export interface IExtensionManifest { readonly enableProposedApi?: boolean; readonly api?: string; readonly scripts?: { [key: string]: string; }; - readonly requiresWorkspaceTrust?: ExtensionWorkspaceTrustRequirement; + readonly workspaceTrust?: ExtensionWorkspaceTrust; } export const enum ExtensionType { diff --git a/src/vs/platform/terminal/common/terminalRecorder.ts b/src/vs/platform/terminal/common/terminalRecorder.ts index 6341bcc17a5..e8a6e17c366 100644 --- a/src/vs/platform/terminal/common/terminalRecorder.ts +++ b/src/vs/platform/terminal/common/terminalRecorder.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/terminalProcess'; +import { IPtyHostProcessReplayEvent, ReplayEntry } from 'vs/platform/terminal/common/terminalProcess'; const MAX_RECORDER_DATA_SIZE = 1024 * 1024; // 1MB @@ -13,8 +13,6 @@ interface RecorderEntry { data: string[]; } -export interface ReplayEntry { cols: number; rows: number; data: string; } - export interface IRemoteTerminalProcessReplayEvent { events: ReplayEntry[]; } @@ -22,11 +20,10 @@ export interface IRemoteTerminalProcessReplayEvent { export class TerminalRecorder { private _entries: RecorderEntry[]; - private _totalDataLength: number; + private _totalDataLength: number = 0; constructor(cols: number, rows: number) { this._entries = [{ cols, rows, data: [] }]; - this._totalDataLength = 0; } public recordResize(cols: number, rows: number): void { diff --git a/src/vs/platform/terminal/test/common/terminalRecorder.test.ts b/src/vs/platform/terminal/test/common/terminalRecorder.test.ts new file mode 100644 index 00000000000..007ce51448d --- /dev/null +++ b/src/vs/platform/terminal/test/common/terminalRecorder.test.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder'; +import { ReplayEntry } from 'vs/platform/terminal/common/terminalProcess'; + +function eventsEqual(recorder: TerminalRecorder, expected: ReplayEntry[]) { + const actual = recorder.generateReplayEvent().events; + for (let i = 0; i < expected.length; i++) { + assert.deepStrictEqual(actual[i], expected[i]); + } +} + +suite('TerminalRecorder', () => { + test('should record dimensions', () => { + const recorder = new TerminalRecorder(1, 2); + eventsEqual(recorder, [ + { cols: 1, rows: 2, data: '' } + ]); + recorder.recordData('a'); + recorder.recordResize(3, 4); + eventsEqual(recorder, [ + { cols: 1, rows: 2, data: 'a' }, + { cols: 3, rows: 4, data: '' } + ]); + }); + test('should ignore resize events without data', () => { + const recorder = new TerminalRecorder(1, 2); + eventsEqual(recorder, [ + { cols: 1, rows: 2, data: '' } + ]); + recorder.recordResize(3, 4); + eventsEqual(recorder, [ + { cols: 3, rows: 4, data: '' } + ]); + }); + test('should record data and combine it into the previous resize event', () => { + const recorder = new TerminalRecorder(1, 2); + recorder.recordData('a'); + recorder.recordData('b'); + recorder.recordResize(3, 4); + recorder.recordData('c'); + recorder.recordData('d'); + eventsEqual(recorder, [ + { cols: 1, rows: 2, data: 'ab' }, + { cols: 3, rows: 4, data: 'cd' } + ]); + }); +}); diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index 0124ffeab7d..2931ef5e051 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -118,6 +118,7 @@ export abstract class AbstractSynchroniser extends Disposable { readonly onDidChangeLocal: Event = this._onDidChangeLocal.event; protected readonly lastSyncResource: URI; + private hasSyncResourceStateVersionChanged: boolean = false; protected readonly syncResourceLogLabel: string; private syncHeaders: IHeaders = {}; @@ -566,8 +567,7 @@ export abstract class AbstractSynchroniser extends Disposable { const machineId = await this.currentMachineIdPromise; const isLastSyncFromCurrentMachine = !!remoteUserData.syncData?.machineId && remoteUserData.syncData.machineId === machineId; - // For preview, use remoteUserData if lastSyncUserData does not exists and last sync is from current machine - const lastSyncUserDataForPreview = lastSyncUserData === null && isLastSyncFromCurrentMachine ? remoteUserData : lastSyncUserData; + const lastSyncUserDataForPreview = lastSyncUserData === null && isLastSyncFromCurrentMachine && !this.hasSyncResourceStateVersionChanged ? remoteUserData : lastSyncUserData; const resourcePreviewResults = await this.generateSyncPreview(remoteUserData, lastSyncUserDataForPreview, token); const resourcePreviews: IEditableResourcePreview[] = []; @@ -616,6 +616,14 @@ export abstract class AbstractSynchroniser extends Disposable { try { const content = await this.fileService.readFile(this.lastSyncResource); const parsed = JSON.parse(content.value.toString()); + const resourceSyncStateVersion = this.userDataSyncResourceEnablementService.getResourceSyncStateVersion(this.resource); + this.hasSyncResourceStateVersionChanged = parsed.version && resourceSyncStateVersion && parsed.version !== resourceSyncStateVersion; + if (this.hasSyncResourceStateVersionChanged) { + this.logService.info(`${this.syncResourceLogLabel}: Reset last sync state because last sync state version ${parsed.version} is not compatible with current sync state version ${resourceSyncStateVersion}.`); + await this.resetLocal(); + return null; + } + const userData: IUserData = parsed as IUserData; if (userData.content === null) { return { ref: parsed.ref, syncData: null } as T; @@ -637,7 +645,12 @@ export abstract class AbstractSynchroniser extends Disposable { } protected async updateLastSyncUserData(lastSyncRemoteUserData: IRemoteUserData, additionalProps: IStringDictionary = {}): Promise { - const lastSyncUserData: IUserData = { ref: lastSyncRemoteUserData.ref, content: lastSyncRemoteUserData.syncData ? JSON.stringify(lastSyncRemoteUserData.syncData) : null, ...additionalProps }; + if (additionalProps['ref'] || additionalProps['content'] || additionalProps['version']) { + throw new Error('Cannot have core properties as additional'); + } + + const version = this.userDataSyncResourceEnablementService.getResourceSyncStateVersion(this.resource); + const lastSyncUserData = { ref: lastSyncRemoteUserData.ref, content: lastSyncRemoteUserData.syncData ? JSON.stringify(lastSyncRemoteUserData.syncData) : null, version, ...additionalProps }; await this.fileService.writeFile(this.lastSyncResource, VSBuffer.fromString(JSON.stringify(lastSyncUserData))); } diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index d34583109a8..f15de01dc95 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -408,6 +408,8 @@ export interface IUserDataSyncResourceEnablementService { readonly onDidChangeResourceEnablement: Event<[SyncResource, boolean]>; isResourceEnabled(resource: SyncResource): boolean; setResourceEnablement(resource: SyncResource, enabled: boolean): void; + + getResourceSyncStateVersion(resource: SyncResource): string | undefined; } export interface ISyncTask { diff --git a/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts index c96fbad455b..93ae033bd5f 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncResourceEnablementService.ts @@ -41,6 +41,10 @@ export class UserDataSyncResourceEnablementService extends Disposable implements } } + getResourceSyncStateVersion(resource: SyncResource): string | undefined { + return undefined; + } + private storeResourceEnablement(resourceEnablementKey: string, enabled: boolean): void { this.storageService.store(resourceEnablementKey, enabled, StorageScope.GLOBAL, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); } diff --git a/src/vs/platform/workspace/common/workspaceTrust.ts b/src/vs/platform/workspace/common/workspaceTrust.ts index 47075c714df..74bc4625793 100644 --- a/src/vs/platform/workspace/common/workspaceTrust.ts +++ b/src/vs/platform/workspace/common/workspaceTrust.ts @@ -44,7 +44,14 @@ export interface IWorkspaceTrustModel { getTrustStateInfo(): IWorkspaceTrustStateInfo; } +export interface WorkspaceTrustRequestButton { + label: string; + type: 'ContinueWithTrust' | 'ContinueWithoutTrust' | 'Manage' | 'Cancel' +} + export interface WorkspaceTrustRequest { + buttons?: WorkspaceTrustRequestButton[]; + message?: string; modal: boolean; } @@ -53,9 +60,11 @@ export interface IWorkspaceTrustRequestModel { readonly onDidInitiateRequest: Event; readonly onDidCompleteRequest: Event; + readonly onDidCancelRequest: Event; initiateRequest(request?: WorkspaceTrustRequest): void; completeRequest(trustState?: WorkspaceTrustState): void; + cancelRequest(): void; } export interface WorkspaceTrustStateChangeEvent { diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index a2b4adcad78..ac88f7bf9ee 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2668,6 +2668,134 @@ declare module 'vscode' { provideEvaluatableExpression(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } + /** + * Provide inline value as text. + */ + export class InlineValueText { + /** + * The document range for which the inline value applies. + */ + readonly range: Range; + /** + * The text of the inline value. + */ + readonly text: string; + /** + * Creates a new InlineValueText object. + * + * @param range The document line where to show the inline value. + * @param text The value to be shown for the line. + */ + constructor(range: Range, text: string); + } + + /** + * Provide inline value through a variable lookup. + * If only a range is specified, the variable name will be extracted from the underlying document. + * An optional variable name can be used to override the extracted name. + */ + export class InlineValueVariableLookup { + /** + * The document range for which the inline value applies. + * The range is used to extract the variable name from the underlying document. + */ + readonly range: Range; + /** + * If specified the name of the variable to look up. + */ + readonly variableName?: string; + /** + * How to perform the lookup. + */ + readonly caseSensitiveLookup: boolean; + /** + * Creates a new InlineValueVariableLookup object. + * + * @param range The document line where to show the inline value. + * @param variableName The name of the variable to look up. + * @param caseSensitiveLookup How to perform the lookup. If missing lookup is case sensitive. + */ + constructor(range: Range, variableName?: string, caseSensitiveLookup?: boolean); + } + + /** + * Provide an inline value through an expression evaluation. + * If only a range is specified, the expression will be extracted from the underlying document. + * An optional expression can be used to override the extracted expression. + */ + export class InlineValueEvaluatableExpression { + /** + * The document range for which the inline value applies. + * The range is used to extract the evaluatable expression from the underlying document. + */ + readonly range: Range; + /** + * If specified the expression overrides the extracted expression. + */ + readonly expression?: string; + /** + * Creates a new InlineValueEvaluatableExpression object. + * + * @param range The range in the underlying document from which the evaluatable expression is extracted. + * @param expression If specified overrides the extracted expression. + */ + constructor(range: Range, expression?: string); + } + + /** + * Inline value information can be provided by different means: + * - directly as a text value (class InlineValueText). + * - as a name to use for a variable lookup (class InlineValueVariableLookup) + * - as an evaluatable expression (class InlineValueEvaluatableExpression) + * The InlineValue types combines all inline value types into one type. + */ + export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueEvaluatableExpression; + + /** + * A value-object that contains contextual information when requesting inline values from a InlineValuesProvider. + */ + export interface InlineValueContext { + + /** + * The stack frame (as a DAP Id) where the execution has stopped. + */ + readonly frameId: number; + + /** + * The document range where execution has stopped. + * Typically the end position of the range denotes the line where the inline values are shown. + */ + readonly stoppedLocation: Range; + } + + /** + * The inline values provider interface defines the contract between extensions and the VS Code debugger inline values feature. + * In this contract the provider returns inline value information for a given document range + * and VS Code shows this information in the editor at the end of lines. + */ + export interface InlineValuesProvider { + + /** + * An optional event to signal that inline values have changed. + * @see [EventEmitter](#EventEmitter) + */ + onDidChangeInlineValues?: Event | undefined; + + /** + * Provide "inline value" information for a given document and range. + * VS Code calls this method whenever debugging stops in the given document. + * The returned inline values information is rendered in the editor at the end of lines. + * + * @param document The document for which the inline values information is needed. + * @param viewPort The visible document range for which inline values should be computed. + * @param context A bag containing contextual information like the current location. + * @param token A cancellation token. + * @return An array of InlineValueDescriptors or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideInlineValues(document: TextDocument, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; + } + /** * A document highlight kind. */ @@ -10787,6 +10915,21 @@ declare module 'vscode' { */ export function registerEvaluatableExpressionProvider(selector: DocumentSelector, provider: EvaluatableExpressionProvider): Disposable; + /** + * Register a provider that returns data for the debugger's 'inline value' feature. + * Whenever the generic VS Code debugger has stopped in a source file, providers registered for the language of the file + * are called to return textual data that will be shown in the editor at the end of lines. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An inline values provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerInlineValuesProvider(selector: DocumentSelector, provider: InlineValuesProvider): Disposable; + /** * Register a document highlight provider. * diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 21fe65c61b4..4a90f0d8647 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -664,156 +664,6 @@ declare module 'vscode' { //#endregion - //#region inline value provider: https://github.com/microsoft/vscode/issues/105690 - - /** - * The inline values provider interface defines the contract between extensions and the VS Code debugger inline values feature. - * In this contract the provider returns inline value information for a given document range - * and VS Code shows this information in the editor at the end of lines. - */ - export interface InlineValuesProvider { - - /** - * An optional event to signal that inline values have changed. - * @see [EventEmitter](#EventEmitter) - */ - onDidChangeInlineValues?: Event | undefined; - - /** - * Provide "inline value" information for a given document and range. - * VS Code calls this method whenever debugging stops in the given document. - * The returned inline values information is rendered in the editor at the end of lines. - * - * @param document The document for which the inline values information is needed. - * @param viewPort The visible document range for which inline values should be computed. - * @param context A bag containing contextual information like the current location. - * @param token A cancellation token. - * @return An array of InlineValueDescriptors or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideInlineValues(document: TextDocument, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; - } - - /** - * A value-object that contains contextual information when requesting inline values from a InlineValuesProvider. - */ - export interface InlineValueContext { - - /** - * The stack frame (as a DAP Id) where the execution has stopped. - */ - readonly frameId: number; - - /** - * The document range where execution has stopped. - * Typically the end position of the range denotes the line where the inline values are shown. - */ - readonly stoppedLocation: Range; - } - - /** - * Inline value information can be provided by different means: - * - directly as a text value (class InlineValueText). - * - as a name to use for a variable lookup (class InlineValueVariableLookup) - * - as an evaluatable expression (class InlineValueEvaluatableExpression) - * The InlineValue types combines all inline value types into one type. - */ - export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueEvaluatableExpression; - - /** - * Provide inline value as text. - */ - export class InlineValueText { - /** - * The document range for which the inline value applies. - */ - readonly range: Range; - /** - * The text of the inline value. - */ - readonly text: string; - /** - * Creates a new InlineValueText object. - * - * @param range The document line where to show the inline value. - * @param text The value to be shown for the line. - */ - constructor(range: Range, text: string); - } - - /** - * Provide inline value through a variable lookup. - * If only a range is specified, the variable name will be extracted from the underlying document. - * An optional variable name can be used to override the extracted name. - */ - export class InlineValueVariableLookup { - /** - * The document range for which the inline value applies. - * The range is used to extract the variable name from the underlying document. - */ - readonly range: Range; - /** - * If specified the name of the variable to look up. - */ - readonly variableName?: string; - /** - * How to perform the lookup. - */ - readonly caseSensitiveLookup: boolean; - /** - * Creates a new InlineValueVariableLookup object. - * - * @param range The document line where to show the inline value. - * @param variableName The name of the variable to look up. - * @param caseSensitiveLookup How to perform the lookup. If missing lookup is case sensitive. - */ - constructor(range: Range, variableName?: string, caseSensitiveLookup?: boolean); - } - - /** - * Provide an inline value through an expression evaluation. - * If only a range is specified, the expression will be extracted from the underlying document. - * An optional expression can be used to override the extracted expression. - */ - export class InlineValueEvaluatableExpression { - /** - * The document range for which the inline value applies. - * The range is used to extract the evaluatable expression from the underlying document. - */ - readonly range: Range; - /** - * If specified the expression overrides the extracted expression. - */ - readonly expression?: string; - /** - * Creates a new InlineValueEvaluatableExpression object. - * - * @param range The range in the underlying document from which the evaluatable expression is extracted. - * @param expression If specified overrides the extracted expression. - */ - constructor(range: Range, expression?: string); - } - - export namespace languages { - - /** - * Register a provider that returns data for the debugger's 'inline value' feature. - * Whenever the generic VS Code debugger has stopped in a source file, providers registered for the language of the file - * are called to return textual data that will be shown in the editor at the end of lines. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An inline values provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerInlineValuesProvider(selector: DocumentSelector, provider: InlineValuesProvider): Disposable; - } - - //#endregion - // eslint-disable-next-line vscode-dts-region-comments //#region @weinand: variables view action contributions @@ -1270,7 +1120,7 @@ declare module 'vscode' { readonly document: NotebookDocument; /** - * The primary selected cell on this notebook editor. + * @deprecated */ // todo@API should not be undefined, rather a default readonly selection?: NotebookCell; @@ -1460,6 +1310,8 @@ declare module 'vscode' { // code specific mime types // application/x.notebook.error-traceback + // application/x.notebook.stdout + // application/x.notebook.stderr // application/x.notebook.stream export class NotebookCellOutputItem { @@ -2820,18 +2672,18 @@ declare module 'vscode' { * The reason why code actions were requested. */ export enum CodeActionTriggerKind { + /** + * Code actions were explicitly requested by the user or by an extension. + */ + Invoke = 1, + /** * Code actions were requested automatically. * * This typically happens when current selection in a file changes, but can * also be triggered when file content changes. */ - Automatic = 1, - - /** - * Code actions were requested maually by the user or an extension. - */ - Manual = 2, + Automatic = 2, } export interface CodeActionContext { @@ -2857,6 +2709,11 @@ declare module 'vscode' { } export interface PortAttributesProvider { + /** + * Provides attributes for the given ports. For ports that your extension doesn't know about, simply don't include + * them in the returned array. For example, if `providePortAttributes` is called with ports [3000, 4000] but your + * extension doesn't know anything about those ports you can return an empty array. + */ providePortAttributes(ports: number[], pid: number | undefined, commandLine: string | undefined, token: CancellationToken): ProviderResult; } @@ -2869,6 +2726,7 @@ declare module 'vscode' { * * @param portSelector If registerPortAttributesProvider is called after you start your process then you may already * know the range of ports or the pid of your process. + * The `portRange` is start inclusive and end exclusive. * @param provider The PortAttributesProvider */ export function registerPortAttributesProvider(portSelector: { pid?: number, portRange?: [number, number] }, provider: PortAttributesProvider): Disposable; diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index ec724cb5309..287bcd08305 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -142,7 +142,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb } else if (dto.type === 'function') { this.debugService.addFunctionBreakpoint(dto.functionName, dto.id); } else if (dto.type === 'data') { - this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes); + this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes, dto.accessType); } } return Promise.resolve(); diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index bfe986601f1..b049f8646d8 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -15,7 +15,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ITerminalExternalLinkProvider, ITerminalInstance, ITerminalInstanceService, ITerminalLink, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; -import { IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IAvailableProfilesRequest as IAvailableProfilesRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @extHostNamedCustomer(MainContext.MainThreadTerminalService) @@ -66,7 +66,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._toDispose.add(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.instanceId : null))); this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => instance && this._onTitleChanged(instance.instanceId, instance.title))); this._toDispose.add(_terminalService.configHelper.onWorkspacePermissionsChanged(isAllowed => this._onWorkspacePermissionsChanged(isAllowed))); - this._toDispose.add(_terminalService.onRequestAvailableShells(e => this._onRequestAvailableShells(e))); + this._toDispose.add(_terminalService.onRequestAvailableProfiles(e => this._onRequestAvailableProfiles(e))); // ITerminalInstanceService listeners if (terminalInstanceService.onRequestDefaultShellAndArgs) { @@ -349,9 +349,9 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape return true; } - private async _onRequestAvailableShells(req: IAvailableShellsRequest): Promise { + private async _onRequestAvailableProfiles(req: IAvailableProfilesRequest): Promise { if (this._isPrimaryExtHost()) { - req.callback(await this._proxy.$getAvailableShells()); + req.callback(await this._proxy.$getAvailableProfiles(req.quickLaunchOnly)); } } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index fd84b560c25..d4bc6efc9eb 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -425,7 +425,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostLanguageFeatures.registerEvaluatableExpressionProvider(extension, checkSelector(selector), provider, extension.identifier); }, registerInlineValuesProvider(selector: vscode.DocumentSelector, provider: vscode.InlineValuesProvider): vscode.Disposable { - checkProposedApiEnabled(extension); return extHostLanguageFeatures.registerInlineValuesProvider(extension, checkSelector(selector), provider, extension.identifier); }, registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 8bbee973ac7..f36f80b7871 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -61,6 +61,7 @@ import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplore import { WorkspaceTrustRequest, WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal'; +import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -1613,11 +1614,6 @@ export interface IShellLaunchConfigDto { hideFromUser?: boolean; } -export interface IShellDefinitionDto { - label: string; - path: string; -} - export interface IShellAndArgsDto { shell: string; args: string[] | string | undefined; @@ -1657,7 +1653,7 @@ export interface ExtHostTerminalServiceShape { $acceptProcessRequestCwd(id: number): void; $acceptProcessRequestLatency(id: number): number; $acceptWorkspacePermissionsChanged(isAllowed: boolean): void; - $getAvailableShells(): Promise; + $getAvailableProfiles(quickLaunchOnly: boolean): Promise; $getDefaultShellAndArgs(useAutomationShell: boolean): Promise; $provideLinks(id: number, line: string): Promise; $activateLink(id: number, linkId: number): void; @@ -1705,6 +1701,7 @@ export interface IDataBreakpointDto extends IBreakpointDto { canPersist: boolean; label: string; accessTypes?: DebugProtocol.DataBreakpointAccessType[]; + accessType: DebugProtocol.DataBreakpointAccessType; } export interface ISourceBreakpointDto extends IBreakpointDto { diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index fcf05eadee2..a41873b3bf2 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -547,12 +547,14 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme ); } - public $extensionTestsExecute(): Promise { - return this._doHandleExtensionTests().then(undefined, error => { + public async $extensionTestsExecute(): Promise { + await this._readyToRunExtensions.wait(); + try { + return this._doHandleExtensionTests(); + } catch (error) { console.error(error); // ensure any error message makes it onto the console - - return Promise.reject(error); - }); + throw error; + } } private async _doHandleExtensionTests(): Promise { diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index d7db691c043..0d32f885bd7 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -16,6 +16,7 @@ import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/common/extHostTyp import { isPromiseCanceledError } from 'vs/base/common/errors'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { coalesce } from 'vs/base/common/arrays'; +import Severity from 'vs/base/common/severity'; export type Item = string | QuickPickItem; @@ -632,7 +633,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx set validationMessage(validationMessage: string | undefined) { this._validationMessage = validationMessage; - this.update({ validationMessage }); + this.update({ validationMessage, severity: validationMessage ? Severity.Error : Severity.Ignore }); } } diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 75a2229767a..8c34992d106 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -5,7 +5,7 @@ import type * as vscode from 'vscode'; import { Event, Emitter } from 'vs/base/common/event'; -import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; @@ -21,6 +21,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal'; import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering'; +import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal'; export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable { @@ -327,7 +328,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal; public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string; public abstract getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string; - public abstract $getAvailableShells(): Promise; + public abstract $getAvailableProfiles(quickLaunchOnly: boolean): Promise; public abstract $getDefaultShellAndArgs(useAutomationShell: boolean): Promise; public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void; @@ -778,7 +779,7 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService { throw new NotSupportedError(); } - public $getAvailableShells(): Promise { + public $getAvailableProfiles(quickLaunchOnly: boolean): Promise { throw new NotSupportedError(); } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index a0012a4aae1..0254eb67fe4 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1741,11 +1741,11 @@ export namespace CodeActionTriggerKind { export function to(value: modes.CodeActionTriggerType): types.CodeActionTriggerKind { switch (value) { + case modes.CodeActionTriggerType.Invoke: + return types.CodeActionTriggerKind.Invoke; + case modes.CodeActionTriggerType.Auto: return types.CodeActionTriggerKind.Automatic; - - case modes.CodeActionTriggerType.Manual: - return types.CodeActionTriggerKind.Manual; } } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 88833b1730f..bef6a7bf01c 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1179,8 +1179,8 @@ export class DocumentSymbol { export enum CodeActionTriggerKind { - Automatic = 1, - Manual = 2, + Invoke = 1, + Automatic = 2, } @es5ClassCompat diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index f2c8f2e1811..c8c7f4cd50c 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -5,6 +5,7 @@ import * as nls from 'vs/nls'; import type * as vscode from 'vscode'; +import * as platform from 'vs/base/common/platform'; import { DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes'; import { ExecutableDebugAdapter, SocketDebugAdapter, NamedPipeDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; @@ -109,10 +110,23 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { if (giveShellTimeToInitialize) { // give a new terminal some time to initialize the shell await new Promise(resolve => setTimeout(resolve, 1000)); + } else { + if (configProvider.getConfiguration('debug.terminal').get('clearBeforeReusing')) { + // clear terminal before reusing it + if (shell.indexOf('powershell') >= 0 || shell.indexOf('pwsh') >= 0 || shell.indexOf('cmd.exe') >= 0) { + terminal.sendText('cls'); + } else if (shell.indexOf('bash') >= 0) { + terminal.sendText('clear'); + } else if (platform.isWindows) { + terminal.sendText('cls'); + } else { + terminal.sendText('clear'); + } + } } const command = prepareCommand(shell, args.args, cwdForPrepareCommand, args.env); - terminal.sendText(command, true); + terminal.sendText(command); // Mark terminal as unused when its session ends, see #112055 const sessionListener = this.onDidTerminateDebugSession(s => { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index b401e7a2a75..930b010a09c 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -9,15 +9,16 @@ import { generateUuid } from 'vs/base/common/uuid'; import { getSystemShell, getSystemShellSync } from 'vs/base/node/shell'; import { ILogService } from 'vs/platform/log/common/log'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { IShellAndArgsDto, IShellDefinitionDto } from 'vs/workbench/api/common/extHost.protocol'; +import { IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfigProvider, ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService'; import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/common/extHostTerminalService'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { ITerminalConfiguration, ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; -import { detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal'; +import { detectAvailableProfiles } from 'vs/workbench/contrib/terminal/node/terminalProfiles'; import type * as vscode from 'vscode'; export class ExtHostTerminalService extends BaseExtHostTerminalService { @@ -131,8 +132,9 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider); } - public $getAvailableShells(): Promise { - return detectAvailableShells(); + public async $getAvailableProfiles(quickLaunchOnly: boolean): Promise { + const config = await (await this._extHostConfiguration.getConfigProvider()).getConfiguration().get('terminal.integrated'); + return detectAvailableProfiles(quickLaunchOnly, this._logService, config as ITerminalConfiguration, this._variableResolver, this._lastActiveWorkspace); } public async $getDefaultShellAndArgs(useAutomationShell: boolean): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index ce84ce7cf34..c2335024ea2 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -16,7 +16,7 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView' import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IDebugService, VIEWLET_ID, DEBUG_PANEL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA, - CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, getStateLabel, State, CONTEXT_WATCH_ITEM_TYPE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, + CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX, BREAKPOINT_EDITOR_CONTRIBUTION_ID, REPL_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, EDITOR_CONTRIBUTION_ID, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, getStateLabel, State, CONTEXT_WATCH_ITEM_TYPE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, } from 'vs/workbench/contrib/debug/common/debug'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService'; @@ -32,7 +32,7 @@ import { launchSchemaId } from 'vs/workbench/services/configuration/common/confi import { LoadedScriptsView } from 'vs/workbench/contrib/debug/browser/loadedScriptsView'; import { RunToCursorAction } from 'vs/workbench/contrib/debug/browser/debugEditorActions'; import { WatchExpressionsView, ADD_WATCH_LABEL, REMOVE_WATCH_EXPRESSIONS_COMMAND_ID, REMOVE_WATCH_EXPRESSIONS_LABEL, ADD_WATCH_ID } from 'vs/workbench/contrib/debug/browser/watchExpressionsView'; -import { VariablesView, SET_VARIABLE_ID, COPY_VALUE_ID, BREAK_WHEN_VALUE_CHANGES_ID, COPY_EVALUATE_PATH_ID, ADD_TO_WATCH_ID } from 'vs/workbench/contrib/debug/browser/variablesView'; +import { VariablesView, SET_VARIABLE_ID, COPY_VALUE_ID, BREAK_WHEN_VALUE_CHANGES_ID, COPY_EVALUATE_PATH_ID, ADD_TO_WATCH_ID, BREAK_WHEN_VALUE_IS_ACCESSED_ID, BREAK_WHEN_VALUE_IS_READ_ID } from 'vs/workbench/contrib/debug/browser/variablesView'; import { Repl } from 'vs/workbench/contrib/debug/browser/repl'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView'; @@ -138,7 +138,9 @@ registerDebugViewMenuItem(MenuId.DebugVariablesContext, SET_VARIABLE_ID, nls.loc registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_VALUE_ID, nls.localize('copyValue', "Copy Value"), 10, undefined, undefined, '5_cutcopypaste'); registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_EVALUATE_PATH_ID, nls.localize('copyAsExpression', "Copy as Expression"), 20, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '5_cutcopypaste'); registerDebugViewMenuItem(MenuId.DebugVariablesContext, ADD_TO_WATCH_ID, nls.localize('addToWatchExpressions', "Add to Watch"), 100, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, 'z_commands'); -registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_CHANGES_ID, nls.localize('breakWhenValueChanges', "Break When Value Changes"), 200, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, undefined, 'z_commands'); +registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_IS_READ_ID, nls.localize('breakWhenValueIsRead', "Break When Value Is Read"), 200, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, undefined, 'z_commands'); +registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_CHANGES_ID, nls.localize('breakWhenValueChanges', "Break When Value Changes"), 210, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, undefined, 'z_commands'); +registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_IS_ACCESSED_ID, nls.localize('breakWhenValueIsAccessed', "Break When Value Is Accessed"), 220, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, undefined, 'z_commands'); registerDebugViewMenuItem(MenuId.DebugWatchContext, ADD_WATCH_ID, ADD_WATCH_LABEL, 10, undefined, undefined, '3_modification'); registerDebugViewMenuItem(MenuId.DebugWatchContext, EDIT_EXPRESSION_COMMAND_ID, nls.localize('editWatchExpression', "Edit Expression"), 20, CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), undefined, '3_modification'); @@ -398,6 +400,11 @@ configurationRegistry.registerConfiguration({ description: nls.localize('debug.console.closeOnEnd', "Controls if the debug console should be automatically closed when the debug session ends."), default: false }, + 'debug.terminal.clearBeforeReusing': { + type: 'boolean', + description: nls.localize({ comment: ['This is the description for a setting'], key: 'debug.terminal.clearBeforeReusing' }, "Before starting a new debug session in an integrated or external terminal, clear the terminal."), + default: false + }, 'debug.openDebug': { enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart', 'openOnDebugBreak'], default: 'openOnFirstSessionStart', diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 8101a3c16a9..4395af9429a 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -30,7 +30,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IViewsService } from 'vs/workbench/common/views'; import { deepClone } from 'vs/base/common/objects'; -import { isWeb } from 'vs/base/common/platform'; +import { isWeb, isWindows } from 'vs/base/common/platform'; export const ADD_CONFIGURATION_ID = 'debug.addConfiguration'; export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint'; @@ -250,7 +250,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: STEP_INTO_ID, weight: KeybindingWeight.WorkbenchContrib + 10, // Have a stronger weight to have priority over full screen when debugging - primary: KeyCode.F11, + primary: (isWeb && isWindows) ? (KeyMod.Alt | KeyCode.F11) : KeyCode.F11, // Windows browsers use F11 for full screen, thus use alt+F11 as the default shortcut // Use a more flexible when clause to not allow full screen command to take over when F11 pressed a lot of times when: CONTEXT_DEBUG_STATE.notEqualsTo('inactive'), handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index b4e66b54020..25a28ac5cbf 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -924,8 +924,8 @@ export class DebugService implements IDebugService { await this.sendFunctionBreakpoints(); } - async addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined): Promise { - this.model.addDataBreakpoint(label, dataId, canPersist, accessTypes); + async addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, accessType: DebugProtocol.DataBreakpointAccessType): Promise { + this.model.addDataBreakpoint(label, dataId, canPersist, accessTypes, accessType); this.debugStorage.storeBreakpoints(this.model); await this.sendDataBreakpoints(); this.debugStorage.storeBreakpoints(this.model); diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 665894a0bad..537bf780c48 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -6,7 +6,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import * as dom from 'vs/base/browser/dom'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED, IStackFrame, CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT, IDataBreakpointInfoResponse, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, VARIABLES_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED, IStackFrame, CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT, IDataBreakpointInfoResponse, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, VARIABLES_VIEW_ID, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; import { Variable, Scope, ErrorScope, StackFrame, Expression } from 'vs/workbench/contrib/debug/common/debugModel'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -58,6 +58,8 @@ export class VariablesView extends ViewPane { private menu: IMenu; private debugProtocolVariableMenuContext: IContextKey; private breakWhenValueChangesSupported: IContextKey; + private breakWhenValueIsAccessedSupported: IContextKey; + private breakWhenValueIsReadSupported: IContextKey; private variableEvaluateName: IContextKey; constructor( @@ -80,6 +82,8 @@ export class VariablesView extends ViewPane { this._register(this.menu); this.debugProtocolVariableMenuContext = CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT.bindTo(contextKeyService); this.breakWhenValueChangesSupported = CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED.bindTo(contextKeyService); + this.breakWhenValueIsAccessedSupported = CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED.bindTo(contextKeyService); + this.breakWhenValueIsReadSupported = CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED.bindTo(contextKeyService); this.variableEvaluateName = CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT.bindTo(contextKeyService); // Use scheduler to prevent unnecessary flashing @@ -206,10 +210,30 @@ export class VariablesView extends ViewPane { const session = this.debugService.getViewModel().focusedSession; this.variableEvaluateName.set(!!variable.evaluateName); this.breakWhenValueChangesSupported.reset(); + this.breakWhenValueIsAccessedSupported.reset(); + this.breakWhenValueIsReadSupported.reset(); if (session && session.capabilities.supportsDataBreakpoints) { dataBreakpointInfoResponse = await session.dataBreakpointInfo(variable.name, variable.parent.reference); const dataBreakpointId = dataBreakpointInfoResponse?.dataId; - this.breakWhenValueChangesSupported.set(!!dataBreakpointId); + const dataBreakpointAccessTypes = dataBreakpointInfoResponse?.accessTypes; + if (!dataBreakpointAccessTypes) { + // Assumes default behaviour: Supports breakWhenValueChanges + this.breakWhenValueChangesSupported.set(!!dataBreakpointId); + } else { + dataBreakpointAccessTypes.forEach(accessType => { + switch (accessType) { + case 'read': + this.breakWhenValueIsReadSupported.set(!!dataBreakpointId); + break; + case 'write': + this.breakWhenValueChangesSupported.set(!!dataBreakpointId); + break; + case 'readWrite': + this.breakWhenValueIsAccessedSupported.set(!!dataBreakpointId); + break; + } + }); + } } const context: IVariablesContext = { @@ -435,7 +459,29 @@ CommandsRegistry.registerCommand({ handler: async (accessor: ServicesAccessor) => { const debugService = accessor.get(IDebugService); if (dataBreakpointInfoResponse) { - await debugService.addDataBreakpoint(dataBreakpointInfoResponse.description, dataBreakpointInfoResponse.dataId!, !!dataBreakpointInfoResponse.canPersist, dataBreakpointInfoResponse.accessTypes); + await debugService.addDataBreakpoint(dataBreakpointInfoResponse.description, dataBreakpointInfoResponse.dataId!, !!dataBreakpointInfoResponse.canPersist, dataBreakpointInfoResponse.accessTypes, 'write'); + } + } +}); + +export const BREAK_WHEN_VALUE_IS_ACCESSED_ID = 'debug.breakWhenValueIsAccessed'; +CommandsRegistry.registerCommand({ + id: BREAK_WHEN_VALUE_IS_ACCESSED_ID, + handler: async (accessor: ServicesAccessor) => { + const debugService = accessor.get(IDebugService); + if (dataBreakpointInfoResponse) { + await debugService.addDataBreakpoint(dataBreakpointInfoResponse.description, dataBreakpointInfoResponse.dataId!, !!dataBreakpointInfoResponse.canPersist, dataBreakpointInfoResponse.accessTypes, 'readWrite'); + } + } +}); + +export const BREAK_WHEN_VALUE_IS_READ_ID = 'debug.breakWhenValueIsRead'; +CommandsRegistry.registerCommand({ + id: BREAK_WHEN_VALUE_IS_READ_ID, + handler: async (accessor: ServicesAccessor) => { + const debugService = accessor.get(IDebugService); + if (dataBreakpointInfoResponse) { + await debugService.addDataBreakpoint(dataBreakpointInfoResponse.description, dataBreakpointInfoResponse.dataId!, !!dataBreakpointInfoResponse.canPersist, dataBreakpointInfoResponse.accessTypes, 'read'); } } }); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 632cb17c069..da4772034b0 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -58,6 +58,7 @@ export const CONTEXT_CALLSTACK_ITEM_STOPPED = new RawContextKey('callSt export const CONTEXT_CALLSTACK_SESSION_HAS_ONE_THREAD = new RawContextKey('callStackSessionHasOneThread', false, { type: 'boolean', description: nls.localize('callStackSessionHasOneThread', "True when the focused session in the CALL STACK view has exactly one thread. Used internally for inline menus in the CALL STACK view.") }); export const CONTEXT_WATCH_ITEM_TYPE = new RawContextKey('watchItemType', undefined, { type: 'string', description: nls.localize('watchItemType', "Represents the item type of the focused element in the WATCH view. For example: 'expression', 'variable'") }); export const CONTEXT_BREAKPOINT_ITEM_TYPE = new RawContextKey('breakpointItemType', undefined, { type: 'string', description: nls.localize('breakpointItemType', "Represents the item type of the focused element in the BREAKPOINTS view. For example: 'breakpoint', 'exceptionBreakppint', 'functionBreakpoint', 'dataBreakpoint'") }); +export const CONTEXT_BREAKPOINT_ACCESS_TYPE = new RawContextKey('breakpointAccessType', undefined, { type: 'string', description: nls.localize('breakpointAccessType', "Represents the access type of the focused data breakpoint in the BREAKPOINTS view. For example: 'read', 'readWrite', 'write'") }); export const CONTEXT_BREAKPOINT_SUPPORTS_CONDITION = new RawContextKey('breakpointSupportsCondition', false, { type: 'boolean', description: nls.localize('breakpointSupportsCondition', "True when the focused breakpoint supports conditions.") }); export const CONTEXT_LOADED_SCRIPTS_SUPPORTED = new RawContextKey('loadedScriptsSupported', false, { type: 'boolean', description: nls.localize('loadedScriptsSupported', "True when the focused sessions supports the LOADED SCRIPTS view") }); export const CONTEXT_LOADED_SCRIPTS_ITEM_TYPE = new RawContextKey('loadedScriptsItemType', undefined, { type: 'string', description: nls.localize('loadedScriptsItemType', "Represents the item type of the focused element in the LOADED SCRIPTS view.") }); @@ -72,6 +73,8 @@ export const CONTEXT_DEBUGGERS_AVAILABLE = new RawContextKey('debuggers export const CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT = new RawContextKey('debugProtocolVariableMenuContext', undefined, { type: 'string', description: nls.localize('debugProtocolVariableMenuContext', "Represents the context the debug adapter sets on the focused variable in the VARIABLES view.") }); export const CONTEXT_SET_VARIABLE_SUPPORTED = new RawContextKey('debugSetVariableSupported', false, { type: 'boolean', description: nls.localize('debugSetVariableSupported', "True when the focused session supports 'setVariable' request.") }); export const CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED = new RawContextKey('breakWhenValueChangesSupported', false, { type: 'boolean', description: nls.localize('breakWhenValueChangesSupported', "True when the focused session supports to break when value changes.") }); +export const CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED = new RawContextKey('breakWhenValueIsAccessedSupported', false, { type: 'boolean', description: nls.localize('breakWhenValueIsAccessedSupported', "True when the focused breakpoint supports to break when value is accessed.") }); +export const CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED = new RawContextKey('breakWhenValueIsReadSupported', false, { type: 'boolean', description: nls.localize('breakWhenValueIsReadSupported', "True when the focused breakpoint supports to break when value is read.") }); export const CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT = new RawContextKey('variableEvaluateNamePresent', false, { type: 'boolean', description: nls.localize('variableEvaluateNamePresent', "True when the focused variable has an 'evalauteName' field set") }); export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey('exceptionWidgetVisible', false, { type: 'boolean', description: nls.localize('exceptionWidgetVisible', "True when the exception widget is visible.") }); export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey('multiSessionRepl', false, { type: 'boolean', description: nls.localize('multiSessionRepl', "True when there is more than 1 debug console.") }); @@ -423,6 +426,7 @@ export interface IDataBreakpoint extends IBaseBreakpoint { readonly description: string; readonly dataId: string; readonly canPersist: boolean; + readonly accessType: DebugProtocol.DataBreakpointAccessType; } export interface IExceptionInfo { @@ -871,7 +875,7 @@ export interface IDebugService { /** * Adds a new data breakpoint. */ - addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined): Promise; + addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, accessType: DebugProtocol.DataBreakpointAccessType): Promise; /** * Removes all data breakpoints. If id is passed only removes the data breakpoint with the passed id. diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 4da1f90e7cc..a72057fba60 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -121,13 +121,20 @@ export class ExpressionContainer implements IExpressionContainer { private async fetchVariables(start: number | undefined, count: number | undefined, filter: 'indexed' | 'named' | undefined): Promise { try { const response = await this.session!.variables(this.reference || 0, this.threadId, filter, start, count); - return response && response.body && response.body.variables - ? distinct(response.body.variables.filter(v => !!v), v => v.name).map((v: IDebugProtocolVariableWithContext) => { - if (isString(v.value) && isString(v.name) && typeof v.variablesReference === 'number') { - return new Variable(this.session, this.threadId, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type, v.__vscodeVariableMenuContext); - } - return new Variable(this.session, this.threadId, this, 0, '', undefined, nls.localize('invalidVariableAttributes', "Invalid variable attributes"), 0, 0, { kind: 'virtual' }, undefined, undefined, false); - }) : []; + if (!response || !response.body || !response.body.variables) { + return []; + } + + const nameCount = new Map(); + return response.body.variables.filter(v => !!v).map((v: IDebugProtocolVariableWithContext) => { + if (isString(v.value) && isString(v.name) && typeof v.variablesReference === 'number') { + const count = nameCount.get(v.name) || 0; + const idDuplicationIndex = count > 0 ? count.toString() : ''; + nameCount.set(v.name, count + 1); + return new Variable(this.session, this.threadId, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type, v.__vscodeVariableMenuContext, true, 0, idDuplicationIndex); + } + return new Variable(this.session, this.threadId, this, 0, '', undefined, nls.localize('invalidVariableAttributes', "Invalid variable attributes"), 0, 0, { kind: 'virtual' }, undefined, undefined, false); + }); } catch (e) { return [new Variable(this.session, this.threadId, this, 0, '', undefined, e.message, 0, 0, { kind: 'virtual' }, undefined, undefined, false)]; } @@ -225,9 +232,10 @@ export class Variable extends ExpressionContainer implements IExpression { public type: string | undefined = undefined, public variableMenuContext: string | undefined = undefined, public available = true, - startOfVariables = 0 + startOfVariables = 0, + idDuplicationIndex = '', ) { - super(session, threadId, reference, `variable:${parent.getId()}:${name}`, namedVariables, indexedVariables, startOfVariables); + super(session, threadId, reference, `variable:${parent.getId()}:${name}:${idDuplicationIndex}`, namedVariables, indexedVariables, startOfVariables); this.value = value || ''; } @@ -836,7 +844,8 @@ export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint { hitCondition: string | undefined, condition: string | undefined, logMessage: string | undefined, - private accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, + public accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, + public accessType: DebugProtocol.DataBreakpointAccessType, id = generateUuid() ) { super(enabled, hitCondition, condition, logMessage, id); @@ -847,7 +856,7 @@ export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint { result.description = this.description; result.dataId = this.dataId; result.accessTypes = this.accessTypes; - + result.accessType = this.accessType; return result; } @@ -1284,8 +1293,8 @@ export class DebugModel implements IDebugModel { this._onDidChangeBreakpoints.fire({ removed, sessionOnly: false }); } - addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined): void { - const newDataBreakpoint = new DataBreakpoint(label, dataId, canPersist, true, undefined, undefined, undefined, accessTypes); + addDataBreakpoint(label: string, dataId: string, canPersist: boolean, accessTypes: DebugProtocol.DataBreakpointAccessType[] | undefined, accessType: DebugProtocol.DataBreakpointAccessType): void { + const newDataBreakpoint = new DataBreakpoint(label, dataId, canPersist, true, undefined, undefined, undefined, accessTypes, accessType); this.dataBreakopints.push(newDataBreakpoint); this._onDidChangeBreakpoints.fire({ added: [newDataBreakpoint], sessionOnly: false }); } diff --git a/src/vs/workbench/contrib/debug/common/debugStorage.ts b/src/vs/workbench/contrib/debug/common/debugStorage.ts index 8d2c5e12bb9..f237fff5ad1 100644 --- a/src/vs/workbench/contrib/debug/common/debugStorage.ts +++ b/src/vs/workbench/contrib/debug/common/debugStorage.ts @@ -69,7 +69,7 @@ export class DebugStorage { let result: DataBreakpoint[] | undefined; try { result = JSON.parse(this.storageService.get(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((dbp: any) => { - return new DataBreakpoint(dbp.description, dbp.dataId, true, dbp.enabled, dbp.hitCondition, dbp.condition, dbp.logMessage, dbp.accessTypes); + return new DataBreakpoint(dbp.description, dbp.dataId, true, dbp.enabled, dbp.hitCondition, dbp.condition, dbp.logMessage, dbp.accessTypes, dbp.accessType); }); } catch (e) { } diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index f122602c397..d98cb1cba8e 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -228,13 +228,15 @@ suite('Debug - Breakpoints', () => { let eventCount = 0; model.onDidChangeBreakpoints(() => eventCount++); - model.addDataBreakpoint('label', 'id', true, ['read']); - model.addDataBreakpoint('second', 'secondId', false, ['readWrite']); + model.addDataBreakpoint('label', 'id', true, ['read'], 'read'); + model.addDataBreakpoint('second', 'secondId', false, ['readWrite'], 'readWrite'); const dataBreakpoints = model.getDataBreakpoints(); assert.strictEqual(dataBreakpoints[0].canPersist, true); assert.strictEqual(dataBreakpoints[0].dataId, 'id'); + assert.strictEqual(dataBreakpoints[0].accessType, 'read'); assert.strictEqual(dataBreakpoints[1].canPersist, false); assert.strictEqual(dataBreakpoints[1].description, 'second'); + assert.strictEqual(dataBreakpoints[1].accessType, 'readWrite'); assert.strictEqual(eventCount, 2); @@ -282,7 +284,7 @@ suite('Debug - Breakpoints', () => { assert.strictEqual(result.message, 'Disabled Logpoint'); assert.strictEqual(result.icon.id, 'debug-breakpoint-log-disabled'); - model.addDataBreakpoint('label', 'id', true, ['read']); + model.addDataBreakpoint('label', 'id', true, ['read'], 'read'); const dataBreakpoints = model.getDataBreakpoints(); result = getBreakpointMessageAndIcon(State.Stopped, true, dataBreakpoints[0]); assert.strictEqual(result.message, 'Data Breakpoint'); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 5ef55d54b7a..953846bf84f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -65,7 +65,7 @@ import { EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { isArray } from 'vs/base/common/types'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; import { Promises } from 'vs/base/common/async'; @@ -332,7 +332,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @INotificationService private readonly notificationService: INotificationService, + @IDialogService private readonly dialogService: IDialogService, @ICommandService private readonly commandService: ICommandService, ) { super(); @@ -466,28 +466,11 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi run: async () => { await this.extensionsWorkbenchService.checkForUpdates(); const outdated = this.extensionsWorkbenchService.outdated; - if (!outdated.length) { - this.notificationService.info(localize('noUpdatesAvailable', "All extensions are up to date.")); - return; + if (outdated.length) { + return runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@outdated ')); + } else { + return this.dialogService.show(Severity.Info, localize('noUpdatesAvailable', "All extensions are up to date."), []); } - - let msgAvailableExtensions = outdated.length === 1 ? localize('singleUpdateAvailable', "An extension update is available.") : localize('updatesAvailable', "{0} extension updates are available.", outdated.length); - - const disabledExtensionsCount = outdated.filter(ext => ext.local && !this.extensionEnablementService.isEnabled(ext.local)).length; - if (disabledExtensionsCount) { - if (outdated.length === 1) { - msgAvailableExtensions = localize('singleDisabledUpdateAvailable', "An update to an extension which is disabled is available."); - } else if (disabledExtensionsCount === 1) { - msgAvailableExtensions = localize('updatesAvailableOneDisabled', "{0} extension updates are available. One of them is for a disabled extension.", outdated.length); - } else if (disabledExtensionsCount === outdated.length) { - msgAvailableExtensions = localize('updatesAvailableAllDisabled', "{0} extension updates are available. All of them are for disabled extensions.", outdated.length); - } else { - msgAvailableExtensions = localize('updatesAvailableIncludingDisabled', "{0} extension updates are available. {1} of them are for disabled extensions.", outdated.length, disabledExtensionsCount); - } - } - - this.viewletService.openViewlet(VIEWLET_ID, true); - this.notificationService.info(msgAvailableExtensions); } }); diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 33f3b350805..5a8be901f33 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -368,10 +368,6 @@ line-height: 22px; } -.extension-editor > .body > .content details > summary::-webkit-details-marker { - color: rgba(128, 128, 128, 0.5); -} - .extension-editor > .body > .content table tr:nth-child(odd) { background-color: rgba(130, 130, 130, 0.04); } diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index 4980ee077d5..1ef4caee830 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -9,14 +9,13 @@ import { basename, isEqual } from 'vs/base/common/resources'; import { Action, IAction } from 'vs/base/common/actions'; import { URI } from 'vs/base/common/uri'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; -import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel, ITextFileSaveAsOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ResourceMap } from 'vs/base/common/map'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { TextFileContentProvider } from 'vs/workbench/contrib/files/common/files'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; @@ -54,7 +53,7 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa @IEditorService private readonly editorService: IEditorService, @ITextModelService textModelService: ITextModelService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IStorageService private readonly storageService: IStorageService, + @IStorageService private readonly storageService: IStorageService ) { super(); @@ -78,7 +77,7 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa let activeConflictResolutionResource: URI | undefined; const activeInput = this.editorService.activeEditor; - if (activeInput instanceof DiffEditorInput && activeInput.originalInput instanceof ResourceEditorInput && activeInput.modifiedInput instanceof FileEditorInput) { + if (activeInput instanceof DiffEditorInput) { const resource = activeInput.originalInput.resource; if (resource?.scheme === CONFLICT_RESOLUTION_SCHEME) { isActiveEditorSaveConflictResolution = true; @@ -331,16 +330,26 @@ class SaveModelAsAction extends Action { } private findEditor(): IEditorIdentifier | undefined { - for (const group of this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE)) { + let preferredMatchingEditor: IEditorIdentifier | undefined; + let otherMatchingEditors: IEditorIdentifier[] = []; + + FindEditorLoop: for (const group of this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE)) { const editors = this.editorService.findEditors(this.model.resource, group); for (const editor of editors) { if (editor instanceof FileEditorInput) { - return { editor, groupId: group.id }; + // We prefer a `FileEditorInput` for "Save As", but it is possible + // that a custom editor is leveraging the text file model and as + // such we need to fallback to any other editor having the resource + // opened for running the save. + preferredMatchingEditor = { editor, groupId: group.id }; + break FindEditorLoop; } + + otherMatchingEditors.push({ editor, groupId: group.id }); } } - return undefined; + return preferredMatchingEditor || otherMatchingEditors[0]; } } @@ -387,9 +396,16 @@ class ConfigureSaveConflictAction extends Action { } } -export const acceptLocalChangesCommand = async (accessor: ServicesAccessor, resource: URI) => { +export const acceptLocalChangesCommand = (accessor: ServicesAccessor, resource: URI) => { + return acceptOrRevertLocalChangesCommand(accessor, resource, true); +}; + +export const revertLocalChangesCommand = (accessor: ServicesAccessor, resource: URI) => { + return acceptOrRevertLocalChangesCommand(accessor, resource, false); +}; + +async function acceptOrRevertLocalChangesCommand(accessor: ServicesAccessor, resource: URI, accept: boolean) { const editorService = accessor.get(IEditorService); - const resolverService = accessor.get(ITextModelService); const editorPane = editorService.activeEditorPane; if (!editorPane) { @@ -399,58 +415,20 @@ export const acceptLocalChangesCommand = async (accessor: ServicesAccessor, reso const editor = editorPane.input; const group = editorPane.group; - const reference = await resolverService.createModelReference(resource); - const model = reference.object as IResolvedTextFileEditorModel; + // Hide any previously shown message about how to use these actions + clearPendingResolveSaveConflictMessages(); - try { - - // hide any previously shown message about how to use these actions - clearPendingResolveSaveConflictMessages(); - - // Trigger save - await model.save({ ignoreModifiedSince: true, reason: SaveReason.EXPLICIT }); - - // Reopen file input - await editorService.openEditor({ resource: model.resource }, group); - - // Clean up - group.closeEditor(editor); - editor.dispose(); - } finally { - reference.dispose(); - } -}; - -export const revertLocalChangesCommand = async (accessor: ServicesAccessor, resource: URI) => { - const editorService = accessor.get(IEditorService); - const resolverService = accessor.get(ITextModelService); - - const editorPane = editorService.activeEditorPane; - if (!editorPane) { - return; + // Accept or revert + if (accept) { + const options: ITextFileSaveAsOptions = { ignoreModifiedSince: true, reason: SaveReason.EXPLICIT }; + await editorService.save({ editor, groupId: group.id }, options); + } else { + await editorService.revert({ editor, groupId: group.id }); } - const editor = editorPane.input; - const group = editorPane.group; + // Reopen original editor + await editorService.openEditor({ resource }, group); - const reference = await resolverService.createModelReference(resource); - const model = reference.object as ITextFileEditorModel; - - try { - - // hide any previously shown message about how to use these actions - clearPendingResolveSaveConflictMessages(); - - // Revert on model - await model.revert(); - - // Reopen file input - await editorService.openEditor({ resource: model.resource }, group); - - // Clean up - group.closeEditor(editor); - editor.dispose(); - } finally { - reference.dispose(); - } -}; + // Clean up + return group.closeEditor(editor); +} diff --git a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts index 0d70177238a..6a8d6e8e761 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts @@ -38,6 +38,7 @@ class DefaultFormatter extends Disposable implements IWorkbenchContribution { static readonly configName = 'editor.defaultFormatter'; static extensionIds: (string | null)[] = []; + static extensionItemLabels: string[] = []; static extensionDescriptions: string[] = []; constructor( @@ -73,15 +74,18 @@ class DefaultFormatter extends Disposable implements IWorkbenchContribution { }); DefaultFormatter.extensionIds.length = 0; + DefaultFormatter.extensionItemLabels.length = 0; DefaultFormatter.extensionDescriptions.length = 0; DefaultFormatter.extensionIds.push(null); + DefaultFormatter.extensionItemLabels.push(nls.localize('null', 'None')); DefaultFormatter.extensionDescriptions.push(nls.localize('nullFormatterDescription', "None")); for (const extension of extensions) { if (extension.main || extension.browser) { DefaultFormatter.extensionIds.push(extension.identifier.value); - DefaultFormatter.extensionDescriptions.push(extension.description || ''); + DefaultFormatter.extensionItemLabels.push(extension.displayName ?? ''); + DefaultFormatter.extensionDescriptions.push(extension.description ?? ''); } } } @@ -182,6 +186,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis type: ['string', 'null'], default: null, enum: DefaultFormatter.extensionIds, + enumItemLabels: DefaultFormatter.extensionItemLabels, markdownEnumDescriptions: DefaultFormatter.extensionDescriptions } } diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index c092329a8c0..f66d8357d67 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -695,7 +695,7 @@ export class MarkerViewModel extends Disposable { if (!this.codeActionsPromise) { this.codeActionsPromise = createCancelablePromise(cancellationToken => { return getCodeActions(model, new Range(this.marker.range.startLineNumber, this.marker.range.startColumn, this.marker.range.endLineNumber, this.marker.range.endColumn), { - type: CodeActionTriggerType.Manual, filter: { include: CodeActionKind.QuickFix } + type: CodeActionTriggerType.Invoke, filter: { include: CodeActionKind.QuickFix } }, Progress.None, cancellationToken).then(actions => { return this._register(actions); }); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/cellOperations.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/cellOperations.ts index 714b2eb4c0a..3febe508c21 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/cellOperations.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/cellOperations.ts @@ -3,23 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; -import { registerAction2 } from 'vs/platform/actions/common/actions'; +import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { INotebookCellActionContext, NotebookCellAction } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; -import { expandCellRangesWithHiddenCells, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { Range } from 'vs/editor/common/core/range'; +import { CellOverflowToolbarGroups, CellToolbarOrder, CELL_TITLE_CELL_GROUP_ID, INotebookCellActionContext, NotebookCellAction } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { CellEditState, expandCellRangesWithHiddenCells, ICellViewModel, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { CellEditType, cellRangeContains, cellRangesToIndexes, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, cellRangeContains, cellRangesToIndexes, ICellRange, NOTEBOOK_EDITOR_CURSOR_BEGIN_END, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { cloneNotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; +import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; +import { IBulkEditService, ResourceEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; +import { ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits'; const MOVE_CELL_UP_COMMAND_ID = 'notebook.cell.moveUp'; const MOVE_CELL_DOWN_COMMAND_ID = 'notebook.cell.moveDown'; const COPY_CELL_UP_COMMAND_ID = 'notebook.cell.copyUp'; const COPY_CELL_DOWN_COMMAND_ID = 'notebook.cell.copyDown'; +const SPLIT_CELL_COMMAND_ID = 'notebook.cell.split'; +const JOIN_CELL_ABOVE_COMMAND_ID = 'notebook.cell.joinAbove'; +const JOIN_CELL_BELOW_COMMAND_ID = 'notebook.cell.joinBelow'; registerAction2(class extends NotebookCellAction { constructor() { @@ -61,8 +68,7 @@ registerAction2(class extends NotebookCellAction { } }); - -async function moveCellRange(context: INotebookCellActionContext, direction: 'up' | 'down'): Promise { +export async function moveCellRange(context: INotebookCellActionContext, direction: 'up' | 'down'): Promise { const viewModel = context.notebookEditor.viewModel; if (!viewModel) { return; @@ -176,7 +182,7 @@ registerAction2(class extends NotebookCellAction { } }); -async function copyCellRange(context: INotebookCellActionContext, direction: 'up' | 'down'): Promise { +export async function copyCellRange(context: INotebookCellActionContext, direction: 'up' | 'down'): Promise { const viewModel = context.notebookEditor.viewModel; if (!viewModel) { return; @@ -242,3 +248,274 @@ async function copyCellRange(context: INotebookCellActionContext, direction: 'up context.notebookEditor.revealCellRangeInView(focusRange); } } + +export async function splitCell(context: INotebookCellActionContext): Promise { + const newCells = await context.notebookEditor.splitNotebookCell(context.cell); + if (newCells) { + context.notebookEditor.focusNotebookCell(newCells[newCells.length - 1], 'editor'); + } +} + +registerAction2(class extends NotebookCellAction { + constructor() { + super( + { + id: SPLIT_CELL_COMMAND_ID, + title: localize('notebookActions.splitCell', "Split Cell"), + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_EDITOR_CURSOR_BEGIN_END.toNegated()), + order: CellToolbarOrder.SplitCell, + group: CELL_TITLE_CELL_GROUP_ID, + // alt: { + // id: JOIN_CELL_BELOW_COMMAND_ID, + // title: localize('notebookActions.joinCellBelow', "Join with Next Cell") + // } + }, + icon: icons.splitCellIcon, + keybinding: { + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_EDITOR_CURSOR_BEGIN_END.toNegated()), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH), + weight: KeybindingWeight.WorkbenchContrib + }, + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { + return splitCell(context); + } +}); + +export async function joinNotebookCells(viewModel: NotebookViewModel, range: ICellRange, direction: 'above' | 'below', constraint?: CellKind): Promise<{ edits: ResourceEdit[], cell: ICellViewModel, endFocus: ICellRange, endSelections: ICellRange[] } | null> { + if (!viewModel || !viewModel.metadata.editable) { + return null; + } + + const cells = viewModel.viewCells.slice(range.start, range.end); + + if (!cells.length) { + return null; + } + + if (range.start === 0 && direction === 'above') { + return null; + } + + if (range.end === viewModel.length && direction === 'below') { + return null; + } + + for (let i = 0; i < cells.length; i++) { + const cell = cells[i]; + if (!cell.getEvaluatedMetadata(viewModel.notebookDocument.metadata).editable) { + return null; + } + + if (constraint && cell.cellKind !== constraint) { + return null; + } + } + + if (direction === 'above') { + const above = viewModel.viewCells[range.start - 1] as CellViewModel; + if (constraint && above.cellKind !== constraint) { + return null; + } + + if (!above.getEvaluatedMetadata(viewModel.notebookDocument.metadata).editable) { + return null; + } + + const insertContent = cells.map(cell => (cell.textBuffer.getEOL() ?? '') + cell.getText()).join(''); + const aboveCellLineCount = above.textBuffer.getLineCount(); + const aboveCellLastLineEndColumn = above.textBuffer.getLineLength(aboveCellLineCount); + + return { + edits: [ + new ResourceTextEdit(above.uri, { range: new Range(aboveCellLineCount, aboveCellLastLineEndColumn + 1, aboveCellLineCount, aboveCellLastLineEndColumn + 1), text: insertContent }), + new ResourceNotebookCellEdit(viewModel.notebookDocument.uri, + { + editType: CellEditType.Replace, + index: range.start, + count: range.end - range.start, + cells: [] + } + ) + ], + cell: above, + endFocus: { start: range.start - 1, end: range.start }, + endSelections: [{ start: range.start - 1, end: range.start }] + }; + } else { + const below = viewModel.viewCells[range.end] as CellViewModel; + if (constraint && below.cellKind !== constraint) { + return null; + } + + if (!below.getEvaluatedMetadata(viewModel.notebookDocument.metadata).editable) { + return null; + } + + const cell = cells[0]; + const restCells = [...cells.slice(1), below]; + const insertContent = restCells.map(cl => (cl.textBuffer.getEOL() ?? '') + cl.getText()).join(''); + + const cellLineCount = cell.textBuffer.getLineCount(); + const cellLastLineEndColumn = cell.textBuffer.getLineLength(cellLineCount); + + return { + edits: [ + new ResourceTextEdit(cell.uri, { range: new Range(cellLineCount, cellLastLineEndColumn + 1, cellLineCount, cellLastLineEndColumn + 1), text: insertContent }), + new ResourceNotebookCellEdit(viewModel.notebookDocument.uri, + { + editType: CellEditType.Replace, + index: range.start + 1, + count: range.end - range.start, + cells: [] + } + ) + ], + cell, + endFocus: { start: range.start, end: range.start + 1 }, + endSelections: [{ start: range.start, end: range.start + 1 }] + }; + } +} + +export async function joinCellsWithSurrounds(bulkEditService: IBulkEditService, context: INotebookCellActionContext, direction: 'above' | 'below'): Promise { + const viewModel = context.notebookEditor.viewModel; + let ret: { + edits: ResourceEdit[]; + cell: ICellViewModel; + endFocus: ICellRange; + endSelections: ICellRange[]; + } | null = null; + + if (context.ui) { + const cellIndex = viewModel.getCellIndex(context.cell); + ret = await joinNotebookCells(viewModel, { start: cellIndex, end: cellIndex + 1 }, direction); + if (!ret) { + return; + } + + await bulkEditService.apply( + ret?.edits, + { quotableLabel: 'Join Notebook Cells' } + ); + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: ret.endFocus, selections: ret.endSelections }); + ret.cell.editState = CellEditState.Editing; + context.notebookEditor.revealCellRangeInView(viewModel.getFocus()); + } else { + const selections = viewModel.getSelections(); + if (!selections.length) { + return; + } + + const focus = viewModel.getFocus(); + let edits: ResourceEdit[] = []; + let cell: ICellViewModel | null = null; + let cells: ICellViewModel[] = []; + + for (let i = selections.length - 1; i >= 0; i--) { + const selection = selections[i]; + const containFocus = cellRangeContains(selection, focus); + + if ( + selection.end >= viewModel.length && direction === 'below' + || selection.start === 0 && direction === 'above' + ) { + if (containFocus) { + cell = viewModel.getCellByIndex(focus.start); + } + + cells.push(...viewModel.viewCells.slice(selection.start, selection.end)); + continue; + } + + const singleRet = await joinNotebookCells(viewModel, selection, direction); + + if (!singleRet) { + return; + } + + edits.push(...singleRet.edits); + cells.push(singleRet.cell); + + if (containFocus) { + cell = singleRet.cell; + } + } + + if (!edits.length) { + return; + } + + if (!cell || !cells.length) { + return; + } + + await bulkEditService.apply( + edits, + { quotableLabel: 'Join Notebook Cells' } + ); + + cells.forEach(cell => { + cell.editState = CellEditState.Editing; + }); + + viewModel.updateSelectionsState({ kind: SelectionStateType.Handle, primary: cell.handle, selections: cells.map(cell => cell.handle) }); + context.notebookEditor.revealCellRangeInView(viewModel.getFocus()); + } +} + +registerAction2(class extends NotebookCellAction { + constructor() { + super( + { + id: JOIN_CELL_ABOVE_COMMAND_ID, + title: localize('notebookActions.joinCellAbove', "Join With Previous Cell"), + keybinding: { + when: NOTEBOOK_EDITOR_FOCUSED, + primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.KEY_J, + weight: KeybindingWeight.WorkbenchContrib + }, + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), + group: CellOverflowToolbarGroups.Edit, + order: 10 + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { + const bulkEditService = accessor.get(IBulkEditService); + return joinCellsWithSurrounds(bulkEditService, context, 'above'); + } +}); + +registerAction2(class extends NotebookCellAction { + constructor() { + super( + { + id: JOIN_CELL_BELOW_COMMAND_ID, + title: localize('notebookActions.joinCellBelow', "Join With Next Cell"), + keybinding: { + when: NOTEBOOK_EDITOR_FOCUSED, + primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.KEY_J, + weight: KeybindingWeight.WorkbenchContrib + }, + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), + group: CellOverflowToolbarGroups.Edit, + order: 11 + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { + const bulkEditService = accessor.get(IBulkEditService); + return joinCellsWithSurrounds(bulkEditService, context, 'below'); + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/test/cellOperations.test.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/test/cellOperations.test.ts new file mode 100644 index 00000000000..9a28c1d777b --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/test/cellOperations.test.ts @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; +import { Range } from 'vs/editor/common/core/range'; +import { ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits'; +import { copyCellRange, joinNotebookCells, moveCellRange } from 'vs/workbench/contrib/notebook/browser/contrib/cellOperations/cellOperations'; +import { FoldingModel, updateFoldingStateAtIndex } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; +import { CellEditType, CellKind, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; + +suite('CellOperations', () => { + test('Move cells - single cell', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markdown, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markdown, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}] + ], + async (editor) => { + const viewModel = editor.viewModel; + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 1, end: 2 }, selections: [{ start: 1, end: 2 }] }); + await moveCellRange({ notebookEditor: editor, cell: viewModel.viewCells[1] }, 'down'); + assert.strictEqual(viewModel.viewCells[2].getText(), 'var b = 1;'); + }); + }); + + test('Move cells - multiple cells in a selection', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markdown, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markdown, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}] + ], + async (editor) => { + const viewModel = editor.viewModel; + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 1, end: 2 }, selections: [{ start: 0, end: 2 }] }); + await moveCellRange({ notebookEditor: editor, cell: viewModel.viewCells[1] }, 'down'); + assert.strictEqual(viewModel.viewCells[0].getText(), '# header b'); + assert.strictEqual(viewModel.viewCells[1].getText(), '# header a'); + assert.strictEqual(viewModel.viewCells[2].getText(), 'var b = 1;'); + }); + }); + + test('Move cells - move with folding ranges', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markdown, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markdown, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}] + ], + async (editor) => { + const viewModel = editor.viewModel; + const foldingModel = new FoldingModel(); + foldingModel.attachViewModel(viewModel); + updateFoldingStateAtIndex(foldingModel, 0, true); + updateFoldingStateAtIndex(foldingModel, 1, true); + viewModel.updateFoldingRanges(foldingModel.regions); + editor.setHiddenAreas([{ start: 1, end: 2 }]); + editor.setHiddenAreas(viewModel.getHiddenRanges()); + + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 0, end: 1 }, selections: [{ start: 0, end: 1 }] }); + await moveCellRange({ notebookEditor: editor, cell: viewModel.viewCells[1] }, 'down'); + assert.strictEqual(viewModel.viewCells[0].getText(), '# header b'); + assert.strictEqual(viewModel.viewCells[1].getText(), '# header a'); + assert.strictEqual(viewModel.viewCells[2].getText(), 'var b = 1;'); + }); + }); + + test('Copy/duplicate cells - single cell', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markdown, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markdown, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}] + ], + async (editor) => { + const viewModel = editor.viewModel; + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 1, end: 2 }, selections: [{ start: 1, end: 2 }] }); + await copyCellRange({ notebookEditor: editor, cell: viewModel.viewCells[1] }, 'down'); + assert.strictEqual(viewModel.viewCells.length, 6); + assert.strictEqual(viewModel.viewCells[1].getText(), 'var b = 1;'); + assert.strictEqual(viewModel.viewCells[2].getText(), 'var b = 1;'); + }); + }); + + test('Copy/duplicate cells - multiple cells in a selection', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markdown, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markdown, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}] + ], + async (editor) => { + const viewModel = editor.viewModel; + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 1, end: 2 }, selections: [{ start: 0, end: 2 }] }); + await copyCellRange({ notebookEditor: editor, cell: viewModel.viewCells[1] }, 'down'); + assert.strictEqual(viewModel.viewCells.length, 7); + assert.strictEqual(viewModel.viewCells[0].getText(), '# header a'); + assert.strictEqual(viewModel.viewCells[1].getText(), 'var b = 1;'); + assert.strictEqual(viewModel.viewCells[2].getText(), '# header a'); + assert.strictEqual(viewModel.viewCells[3].getText(), 'var b = 1;'); + }); + }); + + test('Copy/duplicate cells - move with folding ranges', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markdown, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markdown, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}] + ], + async (editor) => { + const viewModel = editor.viewModel; + const foldingModel = new FoldingModel(); + foldingModel.attachViewModel(viewModel); + updateFoldingStateAtIndex(foldingModel, 0, true); + updateFoldingStateAtIndex(foldingModel, 1, true); + viewModel.updateFoldingRanges(foldingModel.regions); + editor.setHiddenAreas([{ start: 1, end: 2 }]); + editor.setHiddenAreas(viewModel.getHiddenRanges()); + + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 0, end: 1 }, selections: [{ start: 0, end: 1 }] }); + await copyCellRange({ notebookEditor: editor, cell: viewModel.viewCells[1] }, 'down'); + assert.strictEqual(viewModel.viewCells.length, 7); + assert.strictEqual(viewModel.viewCells[0].getText(), '# header a'); + assert.strictEqual(viewModel.viewCells[1].getText(), 'var b = 1;'); + assert.strictEqual(viewModel.viewCells[2].getText(), '# header a'); + assert.strictEqual(viewModel.viewCells[3].getText(), 'var b = 1;'); + }); + }); + + test('Join cell with below - single cell', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markdown, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markdown, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}] + ], + async (editor, accessor) => { + const viewModel = editor.viewModel; + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 3, end: 4 }, selections: [{ start: 3, end: 4 }] }); + const ret = await joinNotebookCells(editor.viewModel, { start: 3, end: 4 }, 'below'); + assert.strictEqual(ret?.edits.length, 2); + assert.deepStrictEqual(ret?.edits[0], new ResourceTextEdit(viewModel.viewCells[3].uri, { + range: new Range(1, 11, 1, 11), text: viewModel.viewCells[4].textBuffer.getEOL() + 'var c = 3;' + })); + assert.deepStrictEqual(ret?.edits[1], new ResourceNotebookCellEdit(viewModel.notebookDocument.uri, + { + editType: CellEditType.Replace, + index: 4, + count: 1, + cells: [] + } + )); + }); + }); + + test('Join cell with above - single cell', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markdown, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markdown, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}] + ], + async (editor, accessor) => { + const viewModel = editor.viewModel; + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 3, end: 4 }, selections: [{ start: 3, end: 4 }] }); + const ret = await joinNotebookCells(editor.viewModel, { start: 4, end: 5 }, 'above'); + assert.strictEqual(ret?.edits.length, 2); + assert.deepStrictEqual(ret?.edits[0], new ResourceTextEdit(viewModel.viewCells[3].uri, { + range: new Range(1, 11, 1, 11), text: viewModel.viewCells[4].textBuffer.getEOL() + 'var c = 3;' + })); + assert.deepStrictEqual(ret?.edits[1], new ResourceNotebookCellEdit(viewModel.notebookDocument.uri, + { + editType: CellEditType.Replace, + index: 4, + count: 1, + cells: [] + } + )); + }); + }); + + test('Join cell with below - multiple cells', async function () { + await withTestNotebook( + [ + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}] + ], + async (editor, accessor) => { + const viewModel = editor.viewModel; + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 1, end: 2 }, selections: [{ start: 0, end: 2 }] }); + const ret = await joinNotebookCells(editor.viewModel, { start: 0, end: 2 }, 'below'); + assert.strictEqual(ret?.edits.length, 2); + assert.deepStrictEqual(ret?.edits[0], new ResourceTextEdit(viewModel.viewCells[0].uri, { + range: new Range(1, 11, 1, 11), text: viewModel.viewCells[1].textBuffer.getEOL() + 'var b = 2;' + viewModel.viewCells[2].textBuffer.getEOL() + 'var c = 3;' + })); + assert.deepStrictEqual(ret?.edits[1], new ResourceNotebookCellEdit(viewModel.notebookDocument.uri, + { + editType: CellEditType.Replace, + index: 1, + count: 2, + cells: [] + } + )); + }); + }); + + test('Join cell with above - multiple cells', async function () { + await withTestNotebook( + [ + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}] + ], + async (editor, accessor) => { + const viewModel = editor.viewModel; + viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 2, end: 3 }, selections: [{ start: 1, end: 3 }] }); + const ret = await joinNotebookCells(editor.viewModel, { start: 1, end: 3 }, 'above'); + assert.strictEqual(ret?.edits.length, 2); + assert.deepStrictEqual(ret?.edits[0], new ResourceTextEdit(viewModel.viewCells[0].uri, { + range: new Range(1, 11, 1, 11), text: viewModel.viewCells[1].textBuffer.getEOL() + 'var b = 2;' + viewModel.viewCells[2].textBuffer.getEOL() + 'var c = 3;' + })); + assert.deepStrictEqual(ret?.edits[1], new ResourceNotebookCellEdit(viewModel.notebookDocument.uri, + { + editType: CellEditType.Replace, + index: 1, + count: 2, + cells: [] + } + )); + }); + }); +}); + diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts index 1c8864a87a2..03db8421ef2 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts @@ -3,18 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { expandCellRangesWithHiddenCells, getNotebookEditorFromEditorPane, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { expandCellRangesWithHiddenCells, getNotebookEditorFromEditorPane, ICellViewModel, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { cloneNotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { CellEditType, ICellEditOperation, ICellRange, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import * as platform from 'vs/base/common/platform'; +import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { CellOverflowToolbarGroups, INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; class NotebookClipboardContribution extends Disposable { @@ -193,3 +202,241 @@ class NotebookClipboardContribution extends Disposable { const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(NotebookClipboardContribution, LifecyclePhase.Ready); + +const COPY_CELL_COMMAND_ID = 'notebook.cell.copy'; +const CUT_CELL_COMMAND_ID = 'notebook.cell.cut'; +const PASTE_CELL_COMMAND_ID = 'notebook.cell.paste'; +const PASTE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.pasteAbove'; + +registerAction2(class extends NotebookCellAction { + constructor() { + super( + { + id: COPY_CELL_COMMAND_ID, + title: localize('notebookActions.copy', "Copy Cell"), + menu: { + id: MenuId.NotebookCellTitle, + when: NOTEBOOK_EDITOR_FOCUSED, + group: CellOverflowToolbarGroups.Copy, + }, + keybinding: platform.isNative ? undefined : { + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] }, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), + weight: KeybindingWeight.WorkbenchContrib + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { + const clipboardService = accessor.get(IClipboardService); + const notebookService = accessor.get(INotebookService); + if (context.notebookEditor.hasOutputTextSelection()) { + document.execCommand('copy'); + return; + } + + const viewModel = context.notebookEditor.viewModel; + const selections = viewModel.getSelections(); + const targetCellIndex = viewModel.getCellIndex(context.cell); + const containingSelection = selections.find(selection => selection.start <= targetCellIndex && targetCellIndex < selection.end); + + if (containingSelection) { + const cells = viewModel.viewCells.slice(containingSelection.start, containingSelection.end); + + clipboardService.writeText(cells.map(cell => cell.getText()).join('\n')); + notebookService.setToCopy(cells.map(cell => cell.model), true); + } else { + clipboardService.writeText(context.cell.getText()); + notebookService.setToCopy([context.cell.model], true); + } + } +}); + +registerAction2(class extends NotebookCellAction { + constructor() { + super( + { + id: CUT_CELL_COMMAND_ID, + title: localize('notebookActions.cut', "Cut Cell"), + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), + group: CellOverflowToolbarGroups.Copy, + }, + keybinding: platform.isNative ? undefined : { + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), + primary: KeyMod.CtrlCmd | KeyCode.KEY_X, + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] }, + weight: KeybindingWeight.WorkbenchContrib + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { + const clipboardService = accessor.get(IClipboardService); + const notebookService = accessor.get(INotebookService); + clipboardService.writeText(context.cell.getText()); + const viewModel = context.notebookEditor.viewModel; + + if (!viewModel || !viewModel.metadata.editable) { + return; + } + + const selections = viewModel.getSelections(); + const targetCellIndex = viewModel.getCellIndex(context.cell); + const containingSelection = selections.find(selection => selection.start <= targetCellIndex && targetCellIndex < selection.end); + + if (containingSelection) { + const cellTextModels = viewModel.viewCells.slice(containingSelection.start, containingSelection.end).map(cell => cell.model); + let finalSelections: ICellRange[] = []; + const delta = containingSelection.end - containingSelection.start; + for (let i = 0; i < selections.length; i++) { + const selection = selections[i]; + + if (selection.end <= targetCellIndex) { + finalSelections.push(selection); + } else if (selection.start > targetCellIndex) { + finalSelections.push({ start: selection.start - delta, end: selection.end - delta }); + } else { + finalSelections.push({ start: containingSelection.start, end: containingSelection.start + 1 }); + } + } + + viewModel.notebookDocument.applyEdits([{ + editType: CellEditType.Replace, index: containingSelection.start, count: containingSelection.end - containingSelection.start, cells: [] + }], true, { kind: SelectionStateType.Index, focus: viewModel.getFocus(), selections: viewModel.getSelections() }, () => { + const newFocusCellIdx = containingSelection.start < context.notebookEditor.viewModel.notebookDocument.length ? containingSelection.start : context.notebookEditor.viewModel.notebookDocument.length - 1; + + return { + kind: SelectionStateType.Index, focus: { start: newFocusCellIdx, end: newFocusCellIdx + 1 }, selections: finalSelections + }; + }, undefined); + notebookService.setToCopy(cellTextModels, true); + } else { + viewModel.deleteCell(viewModel.getCellIndex(context.cell), true); + notebookService.setToCopy([context.cell.model], false); + } + } +}); + +registerAction2(class extends NotebookAction { + constructor() { + super( + { + id: PASTE_CELL_COMMAND_ID, + title: localize('notebookActions.paste', "Paste Cell"), + menu: { + id: MenuId.NotebookCellTitle, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE), + group: CellOverflowToolbarGroups.Copy, + }, + keybinding: platform.isNative ? undefined : { + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, + linux: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, + weight: KeybindingWeight.EditorContrib + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { + const notebookService = accessor.get(INotebookService); + const pasteCells = notebookService.getToCopy(); + + const viewModel = context.notebookEditor.viewModel; + + if (!viewModel || !viewModel.metadata.editable) { + return; + } + + if (!pasteCells) { + return; + } + + const currCellIndex = context.cell && viewModel.getCellIndex(context.cell); + + let topPastedCell: CellViewModel | undefined = undefined; + pasteCells.items.reverse().map(cell => { + return { + source: cell.getValue(), + language: cell.language, + cellKind: cell.cellKind, + outputs: cell.outputs, + metadata: { + editable: cell.metadata?.editable, + breakpointMargin: cell.metadata?.breakpointMargin, + hasExecutionOrder: cell.metadata?.hasExecutionOrder, + inputCollapsed: cell.metadata?.inputCollapsed, + outputCollapsed: cell.metadata?.outputCollapsed, + custom: cell.metadata?.custom + } + }; + }).forEach(pasteCell => { + const newIdx = typeof currCellIndex === 'number' ? currCellIndex + 1 : 0; + topPastedCell = viewModel.createCell(newIdx, pasteCell.source, pasteCell.language, pasteCell.cellKind, pasteCell.metadata, pasteCell.outputs, true); + }); + + if (topPastedCell) { + context.notebookEditor.focusNotebookCell(topPastedCell, 'container'); + } + } +}); + +registerAction2(class extends NotebookCellAction { + constructor() { + super( + { + id: PASTE_CELL_ABOVE_COMMAND_ID, + title: localize('notebookActions.pasteAbove', "Paste Cell Above"), + keybinding: { + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V, + weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT + }, + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { + const notebookService = accessor.get(INotebookService); + const pasteCells = notebookService.getToCopy(); + + const viewModel = context.notebookEditor.viewModel; + + if (!viewModel || !viewModel.metadata.editable) { + return; + } + + if (!pasteCells) { + return; + } + + const currCellIndex = viewModel.getCellIndex(context.cell); + + let topPastedCell: CellViewModel | undefined = undefined; + pasteCells.items.reverse().map(cell => { + return { + source: cell.getValue(), + language: cell.language, + cellKind: cell.cellKind, + outputs: cell.outputs, + metadata: { + editable: cell.metadata?.editable, + breakpointMargin: cell.metadata?.breakpointMargin, + hasExecutionOrder: cell.metadata?.hasExecutionOrder, + inputCollapsed: cell.metadata?.inputCollapsed, + outputCollapsed: cell.metadata?.outputCollapsed, + custom: cell.metadata?.custom + } + }; + }).forEach(pasteCell => { + topPastedCell = viewModel.createCell(currCellIndex, pasteCell.source, pasteCell.language, pasteCell.cellKind, pasteCell.metadata, pasteCell.outputs, true); + return; + }); + + if (topPastedCell) { + context.notebookEditor.focusNotebookCell(topPastedCell, 'container'); + } + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index b33fcb33b14..46371e2fe9e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -5,7 +5,6 @@ import * as glob from 'vs/base/common/glob'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import * as platform from 'vs/base/common/platform'; import { URI, UriComponents } from 'vs/base/common/uri'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; @@ -13,7 +12,6 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { localize } from 'vs/nls'; import { Action2, IAction2Options, MenuId, MenuItemAction, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext, InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; @@ -21,9 +19,9 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { CATEGORIES } from 'vs/workbench/common/actions'; -import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, getNotebookEditorFromEditorPane, IActiveNotebookEditor, ICellViewModel, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_RUN_STATE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, getNotebookEditorFromEditorPane, IActiveNotebookEditor, ICellViewModel, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_RUN_STATE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellEditType, CellKind, ICellEditOperation, ICellRange, INotebookDocumentFilter, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BEGIN_END, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, ICellEditOperation, ICellRange, INotebookDocumentFilter, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, SelectionStateType, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -59,14 +57,6 @@ const EDIT_CELL_COMMAND_ID = 'notebook.cell.edit'; const QUIT_EDIT_CELL_COMMAND_ID = 'notebook.cell.quitEdit'; const DELETE_CELL_COMMAND_ID = 'notebook.cell.delete'; -const COPY_CELL_COMMAND_ID = 'notebook.cell.copy'; -const CUT_CELL_COMMAND_ID = 'notebook.cell.cut'; -const PASTE_CELL_COMMAND_ID = 'notebook.cell.paste'; -const PASTE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.pasteAbove'; -const SPLIT_CELL_COMMAND_ID = 'notebook.cell.split'; -const JOIN_CELL_ABOVE_COMMAND_ID = 'notebook.cell.joinAbove'; -const JOIN_CELL_BELOW_COMMAND_ID = 'notebook.cell.joinBelow'; - const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution'; const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow'; const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow'; @@ -86,16 +76,16 @@ export const NOTEBOOK_ACTIONS_CATEGORY = { value: localize('notebookActions.cate export const CELL_TITLE_CELL_GROUP_ID = 'inline/cell'; export const CELL_TITLE_OUTPUT_GROUP_ID = 'inline/output'; -const EDITOR_WIDGET_ACTION_WEIGHT = KeybindingWeight.EditorContrib; // smaller than Suggest Widget, etc +export const NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT = KeybindingWeight.EditorContrib; // smaller than Suggest Widget, etc -const enum CellToolbarOrder { +export const enum CellToolbarOrder { EditCell, SplitCell, SaveCell, ClearCellOutput } -const enum CellOverflowToolbarGroups { +export const enum CellOverflowToolbarGroups { Copy = '1_copy', Insert = '2_insert', Edit = '3_edit', @@ -168,7 +158,7 @@ function getContextFromUri(accessor: ServicesAccessor, context?: any) { return undefined; } -abstract class NotebookAction extends Action2 { +export abstract class NotebookAction extends Action2 { constructor(desc: IAction2Options) { if (desc.f1 !== false) { desc.f1 = false; @@ -268,7 +258,7 @@ registerAction2(class ExecuteCell extends NotebookCellAction { win: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter }, - weight: EDITOR_WIDGET_ACTION_WEIGHT + weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT }, menu: { id: MenuId.NotebookCellExecute, @@ -464,7 +454,7 @@ registerAction2(class ExecuteCellSelectBelow extends NotebookCellAction { keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, primary: KeyMod.Shift | KeyCode.Enter, - weight: EDITOR_WIDGET_ACTION_WEIGHT + weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT }, }); } @@ -501,7 +491,7 @@ registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction { keybinding: { when: NOTEBOOK_CELL_LIST_FOCUSED, primary: KeyMod.Alt | KeyCode.Enter, - weight: EDITOR_WIDGET_ACTION_WEIGHT + weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT }, }); } @@ -984,7 +974,7 @@ registerAction2(class extends NotebookCellAction { EditorContextKeys.hasMultipleSelections.toNegated() ), primary: KeyCode.Escape, - weight: EDITOR_WIDGET_ACTION_WEIGHT - 5 + weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT - 5 }, }); } @@ -1021,204 +1011,72 @@ registerAction2(class extends NotebookCellAction { } async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - const index = context.notebookEditor.viewModel.getCellIndex(context.cell); - const result = await context.notebookEditor.deleteNotebookCell(context.cell); + const viewModel = context.notebookEditor.viewModel; + if (!viewModel || !viewModel.metadata.editable) { + return; + } - if (result) { - // deletion succeeds, move focus to the next cell - const nextCellIdx = index < context.notebookEditor.viewModel.length ? index : context.notebookEditor.viewModel.length - 1; - if (nextCellIdx >= 0) { - context.notebookEditor.focusNotebookCell(context.notebookEditor.viewModel.viewCells[nextCellIdx], 'container'); + const selections = viewModel.getSelections(); + const targetCellIndex = viewModel.getCellIndex(context.cell); + const containingSelection = selections.find(selection => selection.start <= targetCellIndex && targetCellIndex < selection.end); + + if (containingSelection) { + let finalSelections: ICellRange[] = []; + const delta = containingSelection.end - containingSelection.start; + for (let i = 0; i < selections.length; i++) { + const selection = selections[i]; + + if (selection.end <= targetCellIndex) { + finalSelections.push(selection); + } else if (selection.start > targetCellIndex) { + finalSelections.push({ start: selection.start - delta, end: selection.end - delta }); + } else { + finalSelections.push({ start: containingSelection.start, end: containingSelection.start + 1 }); + } } - } - } -}); -registerAction2(class extends NotebookCellAction { - constructor() { - super( - { - id: COPY_CELL_COMMAND_ID, - title: localize('notebookActions.copy', "Copy Cell"), - menu: { - id: MenuId.NotebookCellTitle, - when: NOTEBOOK_EDITOR_FOCUSED, - group: CellOverflowToolbarGroups.Copy, - }, - keybinding: platform.isNative ? undefined : { - primary: KeyMod.CtrlCmd | KeyCode.KEY_C, - win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] }, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - weight: KeybindingWeight.WorkbenchContrib + viewModel.notebookDocument.applyEdits([{ + editType: CellEditType.Replace, index: containingSelection.start, count: containingSelection.end - containingSelection.start, cells: [] + }], true, { kind: SelectionStateType.Index, focus: viewModel.getFocus(), selections: viewModel.getSelections() }, () => { + const newFocusCellIdx = containingSelection.start < context.notebookEditor.viewModel.notebookDocument.length ? containingSelection.start : context.notebookEditor.viewModel.notebookDocument.length - 1; + + return { + kind: SelectionStateType.Index, focus: { start: newFocusCellIdx, end: newFocusCellIdx + 1 }, selections: finalSelections + }; + }, undefined); + } else { + let finalSelections: ICellRange[] = []; + for (let i = 0; i < selections.length; i++) { + const selection = selections[i]; + + if (selection.end <= targetCellIndex) { + finalSelections.push(selection); + } else if (selection.start > targetCellIndex) { + finalSelections.push({ start: selection.start - 1, end: selection.end - 1 }); + } else { + finalSelections.push({ start: targetCellIndex, end: targetCellIndex + 1 }); } - }); - } + } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - const clipboardService = accessor.get(IClipboardService); - const notebookService = accessor.get(INotebookService); - if (context.notebookEditor.hasOutputTextSelection()) { - document.execCommand('copy'); - return; - } + viewModel.notebookDocument.applyEdits([{ + editType: CellEditType.Replace, index: targetCellIndex, count: 1, cells: [] + }], true, { kind: SelectionStateType.Index, focus: viewModel.getFocus(), selections: viewModel.getSelections() }, () => { + const newFocusCellIdx = targetCellIndex < context.notebookEditor.viewModel.notebookDocument.length ? targetCellIndex : context.notebookEditor.viewModel.notebookDocument.length - 1; + return { + kind: SelectionStateType.Index, focus: { start: newFocusCellIdx, end: newFocusCellIdx + 1 }, selections: finalSelections + }; + }, undefined); - clipboardService.writeText(context.cell.getText()); - notebookService.setToCopy([context.cell.model], true); - } -}); + // const result = await context.notebookEditor.deleteNotebookCell(context.cell); -registerAction2(class extends NotebookCellAction { - constructor() { - super( - { - id: CUT_CELL_COMMAND_ID, - title: localize('notebookActions.cut', "Cut Cell"), - menu: { - id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), - group: CellOverflowToolbarGroups.Copy, - }, - keybinding: platform.isNative ? undefined : { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_X, - win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] }, - weight: KeybindingWeight.WorkbenchContrib - } - }); - } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - const clipboardService = accessor.get(IClipboardService); - const notebookService = accessor.get(INotebookService); - clipboardService.writeText(context.cell.getText()); - const viewModel = context.notebookEditor.viewModel; - - if (!viewModel || !viewModel.metadata.editable) { - return; - } - - viewModel.deleteCell(viewModel.getCellIndex(context.cell), true); - notebookService.setToCopy([context.cell.model], false); - } -}); - -registerAction2(class extends NotebookAction { - constructor() { - super( - { - id: PASTE_CELL_COMMAND_ID, - title: localize('notebookActions.paste', "Paste Cell"), - menu: { - id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE), - group: CellOverflowToolbarGroups.Copy, - }, - keybinding: platform.isNative ? undefined : { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyCode.KEY_V, - win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, - linux: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, - weight: KeybindingWeight.EditorContrib - } - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { - const notebookService = accessor.get(INotebookService); - const pasteCells = notebookService.getToCopy(); - - const viewModel = context.notebookEditor.viewModel; - - if (!viewModel || !viewModel.metadata.editable) { - return; - } - - if (!pasteCells) { - return; - } - - const currCellIndex = context.cell && viewModel.getCellIndex(context.cell); - - let topPastedCell: CellViewModel | undefined = undefined; - pasteCells.items.reverse().map(cell => { - return { - source: cell.getValue(), - language: cell.language, - cellKind: cell.cellKind, - outputs: cell.outputs, - metadata: { - editable: cell.metadata?.editable, - breakpointMargin: cell.metadata?.breakpointMargin, - hasExecutionOrder: cell.metadata?.hasExecutionOrder, - inputCollapsed: cell.metadata?.inputCollapsed, - outputCollapsed: cell.metadata?.outputCollapsed, - custom: cell.metadata?.custom - } - }; - }).forEach(pasteCell => { - const newIdx = typeof currCellIndex === 'number' ? currCellIndex + 1 : 0; - topPastedCell = viewModel.createCell(newIdx, pasteCell.source, pasteCell.language, pasteCell.cellKind, pasteCell.metadata, pasteCell.outputs, true); - }); - - if (topPastedCell) { - context.notebookEditor.focusNotebookCell(topPastedCell, 'container'); - } - } -}); - -registerAction2(class extends NotebookCellAction { - constructor() { - super( - { - id: PASTE_CELL_ABOVE_COMMAND_ID, - title: localize('notebookActions.pasteAbove', "Paste Cell Above"), - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V, - weight: EDITOR_WIDGET_ACTION_WEIGHT - }, - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - const notebookService = accessor.get(INotebookService); - const pasteCells = notebookService.getToCopy(); - - const viewModel = context.notebookEditor.viewModel; - - if (!viewModel || !viewModel.metadata.editable) { - return; - } - - if (!pasteCells) { - return; - } - - const currCellIndex = viewModel.getCellIndex(context.cell); - - let topPastedCell: CellViewModel | undefined = undefined; - pasteCells.items.reverse().map(cell => { - return { - source: cell.getValue(), - language: cell.language, - cellKind: cell.cellKind, - outputs: cell.outputs, - metadata: { - editable: cell.metadata?.editable, - breakpointMargin: cell.metadata?.breakpointMargin, - hasExecutionOrder: cell.metadata?.hasExecutionOrder, - inputCollapsed: cell.metadata?.inputCollapsed, - outputCollapsed: cell.metadata?.outputCollapsed, - custom: cell.metadata?.custom - } - }; - }).forEach(pasteCell => { - topPastedCell = viewModel.createCell(currCellIndex, pasteCell.source, pasteCell.language, pasteCell.cellKind, pasteCell.metadata, pasteCell.outputs, true); - return; - }); - - if (topPastedCell) { - context.notebookEditor.focusNotebookCell(topPastedCell, 'container'); + // if (result) { + // // deletion succeeds, move focus to the next cell + // const nextCellIdx = targetCellIndex < context.notebookEditor.viewModel.length ? targetCellIndex : context.notebookEditor.viewModel.length - 1; + // if (nextCellIdx >= 0) { + // context.notebookEditor.focusNotebookCell(context.notebookEditor.viewModel.viewCells[nextCellIdx], 'container'); + // } + // } } } }); @@ -1237,7 +1095,7 @@ registerAction2(class extends NotebookCellAction { NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')), primary: KeyCode.DownArrow, - weight: EDITOR_WIDGET_ACTION_WEIGHT + weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT }, { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED), @@ -1283,7 +1141,7 @@ registerAction2(class extends NotebookCellAction { NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('bottom'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')), primary: KeyCode.UpArrow, - weight: EDITOR_WIDGET_ACTION_WEIGHT + weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT }, }); } @@ -1678,101 +1536,6 @@ registerAction2(class extends NotebookAction { } }); -async function splitCell(context: INotebookCellActionContext): Promise { - const newCells = await context.notebookEditor.splitNotebookCell(context.cell); - if (newCells) { - context.notebookEditor.focusNotebookCell(newCells[newCells.length - 1], 'editor'); - } -} - -registerAction2(class extends NotebookCellAction { - constructor() { - super( - { - id: SPLIT_CELL_COMMAND_ID, - title: localize('notebookActions.splitCell', "Split Cell"), - menu: { - id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_EDITOR_CURSOR_BEGIN_END.toNegated()), - order: CellToolbarOrder.SplitCell, - group: CELL_TITLE_CELL_GROUP_ID, - // alt: { - // id: JOIN_CELL_BELOW_COMMAND_ID, - // title: localize('notebookActions.joinCellBelow', "Join with Next Cell") - // } - }, - icon: icons.splitCellIcon, - keybinding: { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_EDITOR_CURSOR_BEGIN_END.toNegated()), - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH), - weight: KeybindingWeight.WorkbenchContrib - }, - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - return splitCell(context); - } -}); - - -async function joinCells(context: INotebookCellActionContext, direction: 'above' | 'below'): Promise { - const cell = await context.notebookEditor.joinNotebookCells(context.cell, direction); - if (cell) { - context.notebookEditor.focusNotebookCell(cell, 'editor'); - } -} - -registerAction2(class extends NotebookCellAction { - constructor() { - super( - { - id: JOIN_CELL_ABOVE_COMMAND_ID, - title: localize('notebookActions.joinCellAbove', "Join With Previous Cell"), - keybinding: { - when: NOTEBOOK_EDITOR_FOCUSED, - primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.KEY_J, - weight: KeybindingWeight.WorkbenchContrib - }, - menu: { - id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), - group: CellOverflowToolbarGroups.Edit, - order: 10 - } - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - return joinCells(context, 'above'); - } -}); - -registerAction2(class extends NotebookCellAction { - constructor() { - super( - { - id: JOIN_CELL_BELOW_COMMAND_ID, - title: localize('notebookActions.joinCellBelow', "Join With Next Cell"), - keybinding: { - when: NOTEBOOK_EDITOR_FOCUSED, - primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.KEY_J, - weight: KeybindingWeight.WorkbenchContrib - }, - menu: { - id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), - group: CellOverflowToolbarGroups.Edit, - order: 11 - } - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - return joinCells(context, 'below'); - } -}); - registerAction2(class extends NotebookCellAction { constructor() { super({ diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts index 1e0aa2bb9ec..73eeb0fab5c 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/test/notebookFolding.test.ts @@ -14,7 +14,7 @@ suite('Notebook Folding', () => { instantiationService.spy(IUndoRedoService, 'pushElement'); test('Folding based on markdown cells', async function () { - await withTestNotebook(instantiationService, + await withTestNotebook( [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -24,7 +24,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingController = new FoldingModel(); foldingController.attachViewModel(viewModel); @@ -41,7 +42,6 @@ suite('Notebook Folding', () => { test('Top level header in a cell wins', async function () { await withTestNotebook( - instantiationService, [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -51,7 +51,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingController = new FoldingModel(); foldingController.attachViewModel(viewModel); @@ -73,7 +74,6 @@ suite('Notebook Folding', () => { test('Folding', async function () { await withTestNotebook( - instantiationService, [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -83,7 +83,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); updateFoldingStateAtIndex(foldingModel, 0, true); @@ -95,7 +96,6 @@ suite('Notebook Folding', () => { ); await withTestNotebook( - instantiationService, [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -105,7 +105,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); updateFoldingStateAtIndex(foldingModel, 2, true); @@ -118,7 +119,6 @@ suite('Notebook Folding', () => { ); await withTestNotebook( - instantiationService, [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -128,7 +128,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); updateFoldingStateAtIndex(foldingModel, 2, true); @@ -143,7 +144,6 @@ suite('Notebook Folding', () => { test('Nested Folding', async function () { await withTestNotebook( - instantiationService, [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -153,7 +153,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); updateFoldingStateAtIndex(foldingModel, 0, true); @@ -198,7 +199,6 @@ suite('Notebook Folding', () => { test('Folding Memento', async function () { await withTestNotebook( - instantiationService, [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -213,7 +213,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); foldingModel.applyMemento([{ start: 2, end: 6 }]); @@ -227,7 +228,6 @@ suite('Notebook Folding', () => { ); await withTestNotebook( - instantiationService, [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -242,7 +242,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); foldingModel.applyMemento([ @@ -260,7 +261,6 @@ suite('Notebook Folding', () => { ); await withTestNotebook( - instantiationService, [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -275,7 +275,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); foldingModel.applyMemento([ @@ -295,7 +296,6 @@ suite('Notebook Folding', () => { test('View Index', async function () { await withTestNotebook( - instantiationService, [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -310,7 +310,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); foldingModel.applyMemento([{ start: 2, end: 6 }]); @@ -332,7 +333,6 @@ suite('Notebook Folding', () => { ); await withTestNotebook( - instantiationService, [ ['# header 1', 'markdown', CellKind.Markdown, [], {}], ['body', 'markdown', CellKind.Markdown, [], {}], @@ -347,7 +347,8 @@ suite('Notebook Folding', () => { ['## header 2.2', 'markdown', CellKind.Markdown, [], {}], ['var e = 7;', 'markdown', CellKind.Markdown, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); foldingModel.applyMemento([ diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/outline/test/notebookOutline.test.ts b/src/vs/workbench/contrib/notebook/browser/contrib/outline/test/notebookOutline.test.ts index 3d9bcfa2640..f06e3f8a775 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/outline/test/notebookOutline.test.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/outline/test/notebookOutline.test.ts @@ -30,7 +30,7 @@ suite('Notebook Outline', function () { }); function withNotebookOutline(cells: [source: string, lang: string, kind: CellKind, output?: IOutputDto[], metadata?: NotebookCellMetadata][], callback: (outline: NotebookCellOutline, editor: IActiveNotebookEditor) => R): Promise { - return withTestNotebook(instantiationService, cells, (editor) => { + return withTestNotebook(cells, (editor) => { if (!editor.hasModel()) { assert.ok(false, 'MUST have active text editor'); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index bc1637cac4d..f96d8ca6c4d 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -143,7 +143,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD markdownCellDrag(cellId: string, position: { clientY: number }): void { // throw new Error('Method not implemented.'); } - markdownCellDragEnd(cellId: string, position: { clientY: number }): void { + markdownCellDragEnd(cellId: string): void { + // throw new Error('Method not implemented.'); + } + markdownCellDrop(cellId: string) { // throw new Error('Method not implemented.'); } diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 281f36eb0c3..a85c8b7e8db 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -177,6 +177,10 @@ word-wrap: break-word; } +.monaco-workbench .notebookOverlay .output > div.foreground.error .output-stream { + color: red; /*TODO@rebornix theme color*/ +} + .monaco-workbench .notebookOverlay .cell-drag-image .output .multi-mimetype-output { display: none; } @@ -196,7 +200,7 @@ } .monaco-workbench .notebookOverlay .output .error_message { - color: red; + color: red; /*TODO@rebornix theme color*/ } .monaco-workbench .notebookOverlay .output .error > div { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 87df6fcd0c7..9e3c54e6c61 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -166,7 +166,8 @@ export interface ICommonNotebookEditor { setMarkdownCellEditState(cellId: string, editState: CellEditState): void; markdownCellDragStart(cellId: string, position: { clientY: number }): void; markdownCellDrag(cellId: string, position: { clientY: number }): void; - markdownCellDragEnd(cellId: string, position: { clientY: number, ctrlKey: boolean, altKey: boolean }): void; + markdownCellDrop(cellId: string, position: { clientY: number, ctrlKey: boolean, altKey: boolean }): void; + markdownCellDragEnd(cellId: string): void; } //#endregion @@ -410,11 +411,6 @@ export interface INotebookEditor extends ICommonNotebookEditor { */ splitNotebookCell(cell: ICellViewModel): Promise; - /** - * Joins the given cell either with the cell above or the one below depending on the given direction. - */ - joinNotebookCells(cell: ICellViewModel, direction: 'above' | 'below', constraint?: CellKind): Promise; - /** * Delete a cell from the notebook */ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 863b2089dc1..5d544864fda 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -5,6 +5,7 @@ import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; +import * as aria from 'vs/base/browser/ui/aria/aria'; import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { IAction } from 'vs/base/common/actions'; @@ -66,6 +67,8 @@ import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookS import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { isWeb } from 'vs/base/common/platform'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; const $ = DOM.$; @@ -220,6 +223,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _isDisposed: boolean = false; + private useRenderer = false; + get isDisposed() { return this._isDisposed; } @@ -312,6 +317,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor readonly creationOptions: INotebookEditorCreationOptions, @IInstantiationService instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, + @IAccessibilityService accessibilityService: IAccessibilityService, @INotebookService private notebookService: INotebookService, @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -326,6 +332,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor super(); this.isEmbedded = creationOptions.isEmbedded || false; + this.useRenderer = this.configurationService.getValue('notebook.experimental.useMarkdownRenderer') ?? (!isWeb && !accessibilityService.isScreenReaderOptimized()); + this._overlayContainer = document.createElement('div'); this.scopedContextKeyService = contextKeyService.createScoped(this._overlayContainer); this.instantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])); @@ -518,7 +526,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const getScopedContextKeyService = (container: HTMLElement) => this._list.contextKeyService.createScoped(container); const renderers = [ this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._dndController, getScopedContextKeyService), - this.instantiationService.createInstance(MarkdownCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService), + this.instantiationService.createInstance(MarkdownCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService, { useRenderer: this.useRenderer }), ]; this._list = this.instantiationService.createInstance( @@ -560,7 +568,18 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor listInactiveFocusOutline: editorBackground, }, accessibilityProvider: { - getAriaLabel() { return null; }, + getAriaLabel: (element) => { + if (!this.viewModel) { + return ''; + } + const index = this.viewModel.getCellIndex(element); + + if (index >= 0) { + return `Cell ${index}, ${element.cellKind === CellKind.Markdown ? 'markdown' : 'code'} cell`; + } + + return ''; + }, getWidgetAriaLabel() { return nls.localize('notebookTreeAriaLabel', "Notebook"); } @@ -1029,9 +1048,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor })); // init rendering - const useRenderer = this.configurationService.getValue('notebook.experimental.useMarkdownRenderer'); - - if (useRenderer) { + if (this.useRenderer) { await this._warmupWithMarkdownRenderer(this.viewModel, viewState); } else { this._list.attachViewModel(this.viewModel); @@ -1669,31 +1686,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return this.viewModel.splitNotebookCell(index); } - async joinNotebookCells(cell: ICellViewModel, direction: 'above' | 'below', constraint?: CellKind): Promise { - if (!this.viewModel) { - return null; - } - - if (!this.viewModel.metadata.editable) { - return null; - } - - const index = this.viewModel.getCellIndex(cell); - const ret = await this.viewModel.joinNotebookCells(index, direction, constraint); - - if (ret) { - ret.deletedCells.forEach(cell => { - if (this._pendingLayouts.has(cell)) { - this._pendingLayouts.get(cell)!.dispose(); - } - }); - - return ret.cell; - } else { - return null; - } - } - async deleteNotebookCell(cell: ICellViewModel): Promise { if (!this.viewModel) { return false; @@ -1815,6 +1807,27 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return undefined; } + private _cellFocusAria(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output') { + const index = this._notebookViewModel?.getCellIndex(cell); + + if (index !== undefined && index >= 0) { + let position = ''; + switch (focusItem) { + case 'editor': + position = `the inner ${cell.cellKind === CellKind.Markdown ? 'markdown' : 'code'} editor is focused, press escape to focus the cell container`; + break; + case 'output': + position = `the cell output is focused, press escape to focus the cell container`; + break; + case 'container': + position = `the ${cell.cellKind === CellKind.Markdown ? 'markdown preview' : 'cell container'} is focused, press enter to focus the inner ${cell.cellKind === CellKind.Markdown ? 'markdown' : 'code'} editor`; + break; + default: + break; + } + aria.alert(`Cell ${this._notebookViewModel?.getCellIndex(cell)}, ${position} `); + } + } focusNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output', options?: IFocusNotebookCellOptions) { if (this._isDisposed) { return; @@ -1822,6 +1835,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor if (focusItem === 'editor') { this.focusElement(cell); + this._cellFocusAria(cell, focusItem); this._list.focusView(); cell.editState = CellEditState.Editing; @@ -1831,6 +1845,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } else if (focusItem === 'output') { this.focusElement(cell); + this._cellFocusAria(cell, focusItem); this._list.focusView(); if (!this._webview) { @@ -1853,6 +1868,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor cell.focusMode = CellFocusMode.Container; this.focusElement(cell); + this._cellFocusAria(cell, focusItem); if (!options?.skipReveal) { this.revealInCenterIfOutsideViewport(cell); } @@ -1903,8 +1919,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } async createMarkdownPreview(cell: MarkdownCellViewModel) { - const useRenderer = this.configurationService.getValue('notebook.experimental.useMarkdownRenderer'); - if (!useRenderer) { + if (!this.useRenderer) { // TODO: handle case where custom renderer is disabled? return; } @@ -2065,31 +2080,36 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor setMarkdownCellEditState(cellId: string, editState: CellEditState): void { const cell = this.getCellById(cellId); - if (cell && cell instanceof MarkdownCellViewModel) { + if (cell instanceof MarkdownCellViewModel) { cell.editState = editState; } } markdownCellDragStart(cellId: string, ctx: { clientY: number }): void { const cell = this.getCellById(cellId); - if (cell && cell instanceof MarkdownCellViewModel) { + if (cell instanceof MarkdownCellViewModel) { this._dndController?.startExplicitDrag(cell, ctx); } } markdownCellDrag(cellId: string, ctx: { clientY: number }): void { - const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId); - - if (cell && cell instanceof MarkdownCellViewModel) { + const cell = this.getCellById(cellId); + if (cell instanceof MarkdownCellViewModel) { this._dndController?.explicitDrag(cell, ctx); } } - markdownCellDragEnd(cellId: string, ctx: { clientY: number, ctrlKey: boolean, altKey: boolean }): void { - const cell = this.viewModel?.viewCells.find(vc => vc.id === cellId); + markdownCellDrop(cellId: string, ctx: { clientY: number, ctrlKey: boolean, altKey: boolean }): void { + const cell = this.getCellById(cellId); + if (cell instanceof MarkdownCellViewModel) { + this._dndController?.explicitDrop(cell, ctx); + } + } - if (cell && cell instanceof MarkdownCellViewModel) { - this._dndController?.endExplicitDrag(cell, ctx); + markdownCellDragEnd(cellId: string): void { + const cell = this.getCellById(cellId); + if (cell instanceof MarkdownCellViewModel) { + this._dndController?.endExplicitDrag(cell); } } @@ -2469,7 +2489,9 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin: 0px ${CELL_MARGIN * 2}px 0px ${CELL_MARGIN}px; }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN}px; }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${CELL_TOP_MARGIN}px; }`); - collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container:not(.webview-backed-markdown-cell) { padding-bottom: ${MARKDOWN_CELL_BOTTOM_MARGIN}px; padding-top: ${MARKDOWN_CELL_TOP_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container { padding-bottom: ${MARKDOWN_CELL_BOTTOM_MARGIN}px; padding-top: ${MARKDOWN_CELL_TOP_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container.webview-backed-markdown-cell { padding: 0; }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .webview-backed-markdown-cell .cell.code { padding-top: ${MARKDOWN_CELL_TOP_MARGIN}px; }`); collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`); diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index f9d7a1c805b..381a7828292 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -196,6 +196,10 @@ export class NotebookCellList extends WorkbenchList implements ID const top = this.getViewScrollTop(); const bottom = this.getViewScrollBottom(); + if (top >= bottom) { + return; + } + const topViewIndex = clamp(this.view.indexAt(top), 0, this.view.length - 1); const topElement = this.view.element(topViewIndex); const topModelIndex = this._viewModel!.getCellIndex(topElement); diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index 5c67edba3ac..ebf414449b6 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -153,7 +153,7 @@ class CodeRendererContrib extends Disposable implements IOutputRendererContribut class StreamRendererContrib extends Disposable implements IOutputRendererContribution { getMimetypes() { - return ['application/x.notebook.stream']; + return ['application/x.notebook.stdout', 'application/x.notebook.stream']; } constructor( @@ -180,6 +180,19 @@ class StreamRendererContrib extends Disposable implements IOutputRendererContrib } } +class StderrRendererContrib extends StreamRendererContrib { + + getMimetypes() { + return ['application/x.notebook.stderr']; + } + + render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI | undefined): IRenderOutput { + const result = super.render(output, items, container, notebookUri); + container.classList.add('error'); + return result; + } +} + class ErrorRendererContrib extends Disposable implements IOutputRendererContribution { getMimetypes() { @@ -392,6 +405,7 @@ NotebookRegistry.registerOutputTransform('plain', PlainTextRendererContrib); NotebookRegistry.registerOutputTransform('code', CodeRendererContrib); NotebookRegistry.registerOutputTransform('error-trace', ErrorRendererContrib); NotebookRegistry.registerOutputTransform('stream-text', StreamRendererContrib); +NotebookRegistry.registerOutputTransform('stderr', StderrRendererContrib); export function getOutputSimpleEditorOptions(): IEditorOptions { return { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index a51ad899b8f..53a5f855cce 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -97,26 +97,35 @@ export interface IToggleMarkdownPreviewMessage extends BaseToWebviewMessage { export interface ICellDragStartMessage extends BaseToWebviewMessage { type: 'cell-drag-start'; - cellId: string; - position: { clientX: number, clientY: number }; + readonly cellId: string; + readonly position: { + readonly clientY: number; + }; } export interface ICellDragMessage extends BaseToWebviewMessage { type: 'cell-drag'; - cellId: string; - position: { clientX: number, clientY: number }; + readonly cellId: string; + readonly position: { + readonly clientY: number; + }; +} + +export interface ICellDropMessage extends BaseToWebviewMessage { + readonly type: 'cell-drop'; + readonly cellId: string; + readonly ctrlKey: boolean + readonly altKey: boolean; + readonly position: { + readonly clientY: number; + }; } export interface ICellDragEndMessage extends BaseToWebviewMessage { readonly type: 'cell-drag-end'; readonly cellId: string; - readonly ctrlKey: boolean - readonly altKey: boolean; - readonly position: { - readonly clientX: number; - readonly clientY: number; - }; } + export interface IInitializedMarkdownPreviewMessage extends BaseToWebviewMessage { readonly type: 'initializedMarkdownPreview'; } @@ -290,6 +299,7 @@ export type FromWebviewMessage = | IToggleMarkdownPreviewMessage | ICellDragStartMessage | ICellDragMessage + | ICellDropMessage | ICellDragEndMessage | IInitializedMarkdownPreviewMessage ; @@ -416,7 +426,7 @@ export class BackLayerWebView extends Disposable { /* markdown */ #container > div > div.preview { color: var(--vscode-foreground); - width: calc(100% - ${this.options.cellMargin}px); + width: 100%; padding-left: ${this.options.leftMargin}px; padding-top: ${this.options.previewNodePadding}px; padding-bottom: ${this.options.previewNodePadding}px; @@ -915,17 +925,21 @@ var requirejs = (function() { this.notebookEditor.markdownCellDrag(data.cellId, data.position); break; } - case 'cell-drag-end': + case 'cell-drop': { - this.notebookEditor.markdownCellDragEnd(data.cellId, { + this.notebookEditor.markdownCellDrop(data.cellId, { clientY: data.position.clientY, ctrlKey: data.ctrlKey, altKey: data.altKey, }); break; } + case 'cell-drag-end': + { + this.notebookEditor.markdownCellDragEnd(data.cellId); + break; + } } - })); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts index dc56b01e974..f996743ab6c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts @@ -336,9 +336,30 @@ export class CellDragAndDropController extends Disposable { const insertionIndicatorAbsolutePos = dropDirection === 'above' ? cellTop : cellTop + cellHeight; this.updateInsertIndicator(dropDirection, insertionIndicatorAbsolutePos); } + + // Try scrolling list if needed + if (this.currentDraggedCell !== cell) { + return; + } + + const viewRect = this.notebookEditor.getDomNode().getBoundingClientRect(); + const eventPositionInView = position.clientY - this.list.scrollTop; + + const scrollMargin = 0.2; + const maxScrollPerFrame = 20; + const eventPositionRatio = eventPositionInView / viewRect.height; + if (eventPositionRatio < scrollMargin) { + this.list.scrollTop -= maxScrollPerFrame * (1 - eventPositionRatio / scrollMargin); + } else if (eventPositionRatio > 1 - scrollMargin) { + this.list.scrollTop += maxScrollPerFrame * (1 - ((1 - eventPositionRatio) / scrollMargin)); + } } - public endExplicitDrag(cell: ICellViewModel, ctx: { clientY: number, ctrlKey: boolean, altKey: boolean }) { + public endExplicitDrag(_cell: ICellViewModel) { + this.setInsertIndicatorVisibility(false); + } + + public explicitDrop(cell: ICellViewModel, ctx: { clientY: number, ctrlKey: boolean, altKey: boolean }) { this.currentDraggedCell = undefined; this.setInsertIndicatorVisibility(false); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts index c6689e531c3..5300a88235f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts @@ -12,7 +12,7 @@ import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/commo import { CodeCellRenderTemplate, ICellOutputViewModel, IInsetRenderOutput, INotebookEditor, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { BUILTIN_RENDERER_ID, CellUri, NotebookCellOutputsSplice, IOrderedMimeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { BUILTIN_RENDERER_ID, CellUri, NotebookCellOutputsSplice, IOrderedMimeType, mimeTypeIsAlwaysSecure } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; @@ -72,7 +72,9 @@ export class CellOutputElement extends Disposable { updateDOMTop(top: number) { if (this.useDedicatedDOM) { - this.domNode.style.top = `${top}px`; + if (this.domNode) { + this.domNode.style.top = `${top}px`; + } } } @@ -98,47 +100,37 @@ export class CellOutputElement extends Disposable { } const notebookTextModel = this.notebookEditor.viewModel.notebookDocument; - - let renderResult: IRenderOutput | undefined = undefined; - - // Reuse output item div - this.useDedicatedDOM = !(!beforeElement && this.output.supportAppend() && this.previousDivSupportAppend()); - this.domNode = this.useDedicatedDOM ? DOM.$('.output-inner-container') : this.outputContainer.lastChild as HTMLElement; - - if (this.output.supportAppend()) { - this.domNode.classList.add('support-append'); - } - const [mimeTypes, pick] = this.output.resolveMimeTypes(notebookTextModel); - if (!mimeTypes.find(mimeType => mimeType.isTrusted)) { + if (!mimeTypes.find(mimeType => mimeType.isTrusted) || mimeTypes.length === 0) { this.viewCell.updateOutputHeight(index, 0); return undefined; } + const pickedMimeTypeRenderer = mimeTypes[pick]; + // Reuse output item div + this.useDedicatedDOM = !(!beforeElement && this.output.supportAppend() && this.previousDivSupportAppend(pickedMimeTypeRenderer.mimeType)); + this.domNode = this.useDedicatedDOM ? DOM.$('.output-inner-container') : this.outputContainer.lastChild as HTMLElement; + this.domNode.setAttribute('output-mime-type', pickedMimeTypeRenderer.mimeType); + if (mimeTypes.filter(mimeType => mimeType.isTrusted).length > 1) { this.attachMimetypeSwitcher(this.domNode, notebookTextModel, mimeTypes); } - if (mimeTypes.length !== 0) { - const pickedMimeTypeRenderer = mimeTypes[pick]; - const notebookUri = CellUri.parse(this.viewCell.uri)?.notebook; + const notebookUri = CellUri.parse(this.viewCell.uri)?.notebook; - if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) { - const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId); - renderResult = renderer - ? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType } - : this.notebookEditor.getOutputRenderer().render(this.output, this.domNode, pickedMimeTypeRenderer.mimeType, notebookUri); - } else { - renderResult = this.notebookEditor.getOutputRenderer().render(this.output, this.domNode, pickedMimeTypeRenderer.mimeType, notebookUri); - } - - this.output.pickedMimeType = pick; + if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) { + const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId); + this.renderResult = renderer + ? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType } + : this.notebookEditor.getOutputRenderer().render(this.output, this.domNode, pickedMimeTypeRenderer.mimeType, notebookUri); + } else { + this.renderResult = this.notebookEditor.getOutputRenderer().render(this.output, this.domNode, pickedMimeTypeRenderer.mimeType, notebookUri); } - this.renderResult = renderResult; + this.output.pickedMimeType = pick; - if (!renderResult) { + if (!this.renderResult) { this.viewCell.updateOutputHeight(index, 0); return undefined; } @@ -149,15 +141,15 @@ export class CellOutputElement extends Disposable { this.outputContainer.appendChild(this.domNode); } - if (renderResult.type !== RenderOutputType.Mainframe) { - this.notebookEditor.createOutput(this.viewCell, renderResult, this.viewCell.getOutputOffset(index)); + if (this.renderResult.type !== RenderOutputType.Mainframe) { + this.notebookEditor.createOutput(this.viewCell, this.renderResult, this.viewCell.getOutputOffset(index)); this.domNode.classList.add('background'); } else { this.domNode.classList.add('foreground', 'output-element'); this.domNode.style.position = 'absolute'; } - if (renderResult.type === RenderOutputType.Html || renderResult.type === RenderOutputType.Extension) { + if (this.renderResult.type === RenderOutputType.Html || this.renderResult.type === RenderOutputType.Extension) { // the output is rendered in the webview, which has resize listener internally // no-op return { initRenderIsSynchronous: false }; @@ -209,8 +201,14 @@ export class CellOutputElement extends Disposable { this.localDisposableStore.add(elementSizeObserver); } - private previousDivSupportAppend() { - return this.outputContainer.lastChild && (this.outputContainer.lastChild).classList.contains('support-append'); + private previousDivSupportAppend(mimeType: string) { + const lastChild = this.outputContainer.lastChild as HTMLElement | null; + + if (lastChild) { + return lastChild.getAttribute('output-mime-type') === mimeType; + } + + return false; } private async attachMimetypeSwitcher(outputItemDiv: HTMLElement, notebookTextModel: NotebookTextModel, mimeTypes: readonly IOrderedMimeType[]) { @@ -413,17 +411,7 @@ export class CellOutputContainer extends Disposable { // not trusted const secureOutput = outputs.filter(output => { const mimeTypes = output.model.outputs.map(op => op.mime); - - if (mimeTypes.indexOf('application/x.notebook.stream') >= 0 - || mimeTypes.indexOf('application/x.notebook.error-traceback') >= 0 - || mimeTypes.indexOf('text/plain') >= 0 - || mimeTypes.indexOf('text/markdown') >= 0 - || mimeTypes.indexOf('application/json') >= 0 - || mimeTypes.includes('image/png')) { - return true; - } - - return false; + return mimeTypes.some(mimeTypeIsAlwaysSecure); }); return secureOutput; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index bf8ff84ece8..b3fdbf16f77 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -5,6 +5,7 @@ import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; +import * as aria from 'vs/base/browser/ui/aria/aria'; import { domEvent } from 'vs/base/browser/event'; import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -375,11 +376,14 @@ abstract class AbstractCellRenderer { export class MarkdownCellRenderer extends AbstractCellRenderer implements IListRenderer { static readonly TEMPLATE_ID = 'markdown_cell'; + private readonly useRenderer: boolean; + constructor( notebookEditor: INotebookEditor, dndController: CellDragAndDropController, private renderedEditors: Map, contextKeyServiceProvider: (container: HTMLElement) => IContextKeyService, + options: { useRenderer: boolean }, @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, @IContextMenuService contextMenuService: IContextMenuService, @@ -387,6 +391,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR @INotificationService notificationService: INotificationService, ) { super(instantiationService, notebookEditor, contextMenuService, configurationService, keybindingService, notificationService, contextKeyServiceProvider, 'markdown', dndController); + this.useRenderer = options.useRenderer; } get templateId() { @@ -428,10 +433,8 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService)); - const useRenderer = !!(this.configurationService.getValue('notebook.experimental.useMarkdownRenderer')); - const templateData: MarkdownCellRenderTemplate = { - useRenderer, + useRenderer: this.useRenderer, rootContainer, collapsedPart, expandButton, @@ -456,7 +459,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR toJSON: () => { return {}; } }; - if (!useRenderer) { + if (!this.useRenderer) { this.dndController.registerDragHandle(templateData, rootContainer, container, () => this.getDragImage(templateData)); } @@ -562,6 +565,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR this.setupCellToolbarActions(templateData, elementDisposables); const toolbarContext = { + ui: true, cell: element, notebookEditor: this.notebookEditor, $mid: 12 @@ -879,7 +883,13 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.updateExecutionOrder(metadata, templateData); templateData.statusBar.cellStatusMessageContainer.textContent = metadata?.statusMessage || ''; - templateData.cellRunState.renderState(element.metadata?.runState); + templateData.cellRunState.renderState(element.metadata?.runState, () => { + if (!this.notebookEditor.viewModel) { + return -1; + } + + return this.notebookEditor.viewModel.getCellIndex(element); + }); if (metadata.runState === NotebookCellRunState.Running) { if (metadata.runStartTime) { @@ -1015,6 +1025,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.setupCellToolbarActions(templateData, elementDisposables); const toolbarContext = { + ui: true, cell: element, cellTemplate: templateData, notebookEditor: this.notebookEditor, @@ -1093,6 +1104,7 @@ export class RunStateRenderer { private spinnerTimer: any | undefined; private pendingNewState: NotebookCellRunState | undefined; + private lastRunState: NotebookCellRunState | undefined; constructor(private readonly element: HTMLElement) { DOM.hide(element); @@ -1105,23 +1117,28 @@ export class RunStateRenderer { } } - renderState(runState: NotebookCellRunState = NotebookCellRunState.Idle) { + renderState(runState: NotebookCellRunState = NotebookCellRunState.Idle, getCellIndex: () => number) { if (this.spinnerTimer) { this.pendingNewState = runState; return; } if (runState === NotebookCellRunState.Success) { + aria.alert(`Code cell at ${getCellIndex()} finishes running successfully`); DOM.reset(this.element, renderIcon(successStateIcon)); } else if (runState === NotebookCellRunState.Error) { + aria.alert(`Code cell at ${getCellIndex()} finishes running with errors`); DOM.reset(this.element, renderIcon(errorStateIcon)); } else if (runState === NotebookCellRunState.Running) { + if (this.lastRunState !== NotebookCellRunState.Running) { + aria.alert(`Code cell at ${getCellIndex()} starts running`); + } DOM.reset(this.element, renderIcon(syncing)); this.spinnerTimer = setTimeout(() => { this.spinnerTimer = undefined; if (this.pendingNewState) { - this.renderState(this.pendingNewState); + this.renderState(this.pendingNewState, getCellIndex); this.pendingNewState = undefined; } }, RunStateRenderer.MIN_SPINNER_TIME); @@ -1134,6 +1151,8 @@ export class RunStateRenderer { } else { this.element.style.display = 'flex'; } + + this.lastRunState = runState; } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index 19c811241d2..e1111a8887a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -195,7 +195,9 @@ export class StatefulMarkdownCell extends Disposable { if (this.useRenderer) { // the markdown preview's height might already be updated after the renderer calls `element.getHeight()` - this.relayoutCell(); + if (this.viewCell.layoutInfo.totalHeight > 0) { + this.relayoutCell(); + } // Update for selection this._register(this.notebookEditor.onDidChangeSelection(() => { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 43e8c4c8e47..89400ecc8b7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -6,7 +6,7 @@ import type { Event } from 'vs/base/common/event'; import type { IDisposable } from 'vs/base/common/lifecycle'; import { RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { FromWebviewMessage, IBlurOutputMessage, ICellDragEndMessage, ICellDragMessage, ICellDragStartMessage, IClickedDataUrlMessage, ICustomRendererMessage, IDimensionMessage, IFocusMarkdownPreviewMessage, IMouseEnterMarkdownPreviewMessage, IMouseEnterMessage, IMouseLeaveMarkdownPreviewMessage, IMouseLeaveMessage, IToggleMarkdownPreviewMessage, IWheelMessage, ToWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; +import { FromWebviewMessage, IBlurOutputMessage, ICellDropMessage, ICellDragMessage, ICellDragStartMessage, IClickedDataUrlMessage, ICustomRendererMessage, IDimensionMessage, IFocusMarkdownPreviewMessage, IMouseEnterMarkdownPreviewMessage, IMouseEnterMessage, IMouseLeaveMarkdownPreviewMessage, IMouseLeaveMessage, IToggleMarkdownPreviewMessage, IWheelMessage, ToWebviewMessage, ICellDragEndMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; // !! IMPORTANT !! everything must be in-line within the webviewPreloads // function. Imports are not allowed. This is stringifies and injected into @@ -695,29 +695,6 @@ function webviewPreloads() { type: 'initialized' }); - document.addEventListener('dragover', e => { - // Allow dropping dragged markdown cells - e.preventDefault(); - }); - - const markdownCellDragDataType = 'x-vscode-markdown-cell-drag'; - - document.addEventListener('drop', e => { - const data = e.dataTransfer?.getData(markdownCellDragDataType); - if (!data) { - return; - } - e.preventDefault(); - - const { cellId } = JSON.parse(data); - postNotebookMessage('cell-drag-end', { - cellId: cellId, - ctrlKey: e.ctrlKey, - altKey: e.altKey, - position: { clientX: e.clientX, clientY: e.clientY }, - }); - }); - function createMarkdownPreview(cellId: string, content: string, top: number) { const container = document.getElementById('container')!; const cellContainer = document.createElement('div'); @@ -750,28 +727,15 @@ function webviewPreloads() { previewContainerNode.setAttribute('draggable', 'true'); previewContainerNode.addEventListener('dragstart', e => { - if (!e.dataTransfer) { - return; - } - e.dataTransfer.setData(markdownCellDragDataType, JSON.stringify({ cellId })); - - (e.target as HTMLElement).classList.add('dragging'); - - postNotebookMessage('cell-drag-start', { - cellId: cellId, - position: { clientX: e.clientX, clientY: e.clientY }, - }); + markdownPreviewDragManager.startDrag(e, cellId); }); previewContainerNode.addEventListener('drag', e => { - postNotebookMessage('cell-drag', { - cellId: cellId, - position: { clientX: e.clientX, clientY: e.clientY }, - }); + markdownPreviewDragManager.updateDrag(e, cellId); }); previewContainerNode.addEventListener('dragend', e => { - (e.target as HTMLElement).classList.remove('dragging'); + markdownPreviewDragManager.endDrag(e, cellId); }); cellContainer.appendChild(previewContainerNode); @@ -831,6 +795,85 @@ function webviewPreloads() { isOutput: false }); } + + const markdownCellDragDataType = 'x-vscode-markdown-cell-drag'; + + const markdownPreviewDragManager = new class MarkdownPreviewDragManager { + + private currentDrag: { cellId: string, clientY: number } | undefined; + + constructor() { + document.addEventListener('dragover', e => { + // Allow dropping dragged markdown cells + e.preventDefault(); + }); + + document.addEventListener('drop', e => { + e.preventDefault(); + this.currentDrag = undefined; + + const data = e.dataTransfer?.getData(markdownCellDragDataType); + if (!data) { + return; + } + + const { cellId } = JSON.parse(data); + postNotebookMessage('cell-drop', { + cellId: cellId, + ctrlKey: e.ctrlKey, + altKey: e.altKey, + position: { clientY: e.clientY }, + }); + }); + } + + startDrag(e: DragEvent, cellId: string) { + if (!e.dataTransfer) { + return; + } + + this.currentDrag = { cellId, clientY: e.clientY }; + + e.dataTransfer.setData(markdownCellDragDataType, JSON.stringify({ cellId })); + + (e.target as HTMLElement).classList.add('dragging'); + + postNotebookMessage('cell-drag-start', { + cellId: cellId, + position: { clientY: e.clientY }, + }); + + // Continuously send updates while dragging instead of relying on `updateDrag`. + // This lets us scroll the list based on drag position. + const trySendDragUpdate = () => { + if (this.currentDrag?.cellId !== cellId) { + return; + } + + postNotebookMessage('cell-drag', { + cellId: cellId, + position: { clientY: this.currentDrag.clientY }, + }); + requestAnimationFrame(trySendDragUpdate); + }; + requestAnimationFrame(trySendDragUpdate); + } + + updateDrag(e: DragEvent, cellId: string) { + if (cellId !== this.currentDrag?.cellId) { + this.currentDrag = undefined; + } + this.currentDrag = { cellId, clientY: e.clientY }; + } + + endDrag(e: DragEvent, cellId: string) { + this.currentDrag = undefined; + (e.target as HTMLElement).classList.remove('dragging'); + postNotebookMessage('cell-drag-end', { + cellId: cellId + }); + } + }(); } export function preloadsScriptStr(values: { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel.ts index 5a4db12a75b..f5e9373c8a0 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel.ts @@ -6,7 +6,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ICellOutputViewModel, IGenericCellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ICellOutput, IOrderedMimeType, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellOutput, IOrderedMimeType, mimeTypeIsMergeable, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; let handle = 0; @@ -34,8 +34,8 @@ export class CellOutputViewModel extends Disposable implements ICellOutputViewMo } supportAppend() { - // if there is any mime type other than `application/x.notebook.stream`, then it's not mergeable. - return !this._outputRawData.outputs.find(op => op.mime !== 'application/x.notebook.stream'); + // if there is any mime type that's not mergeable then the whole output is not mergeable. + return this._outputRawData.outputs.every(op => mimeTypeIsMergeable(op.mime)); } resolveMimeTypes(textModel: NotebookTextModel): [readonly IOrderedMimeType[], number] { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 9ddd1f01299..9ae95f4fba8 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -878,106 +878,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return null; } - async joinNotebookCells(index: number, direction: 'above' | 'below', constraint?: CellKind): Promise<{ cell: ICellViewModel, deletedCells: ICellViewModel[] } | null> { - const cell = this.viewCells[index] as CellViewModel; - - if (!this.metadata.editable) { - return null; - } - - if (!cell.getEvaluatedMetadata(this.notebookDocument.metadata).editable) { - return null; - } - - if (constraint && cell.cellKind !== constraint) { - return null; - } - - if (index === 0 && direction === 'above') { - return null; - } - - if (index === this.length - 1 && direction === 'below') { - return null; - } - - if (direction === 'above') { - const above = this.viewCells[index - 1] as CellViewModel; - if (constraint && above.cellKind !== constraint) { - return null; - } - - if (!above.getEvaluatedMetadata(this.notebookDocument.metadata).editable) { - return null; - } - - await above.resolveTextModel(); - if (!above.hasModel()) { - return null; - } - - const endSelections = [above.handle]; - const insertContent = (cell.textModel?.getEOL() ?? '') + cell.getText(); - const aboveCellLineCount = above.textModel.getLineCount(); - const aboveCellLastLineEndColumn = above.textModel.getLineLength(aboveCellLineCount); - - await this._bulkEditService.apply( - [ - new ResourceTextEdit(above.uri, { range: new Range(aboveCellLineCount, aboveCellLastLineEndColumn + 1, aboveCellLineCount, aboveCellLastLineEndColumn + 1), text: insertContent }), - new ResourceNotebookCellEdit(this._notebook.uri, - { - editType: CellEditType.Replace, - index: index, - count: 1, - cells: [] - } - ) - ], - { quotableLabel: 'Join Notebook Cells' } - ); - - this.selectionHandles = endSelections; - - return { cell: above, deletedCells: [cell] }; - } else { - const below = this.viewCells[index + 1] as CellViewModel; - if (constraint && below.cellKind !== constraint) { - return null; - } - - if (!below.getEvaluatedMetadata(this.notebookDocument.metadata).editable) { - return null; - } - - await cell.resolveTextModel(); - if (!cell.hasModel()) { - return null; - } - - const insertContent = (cell.textModel?.getEOL() ?? '') + below.getText(); - - const cellLineCount = cell.textModel.getLineCount(); - const cellLastLineEndColumn = cell.textModel.getLineLength(cellLineCount); - - await this._bulkEditService.apply( - [ - new ResourceTextEdit(cell.uri, { range: new Range(cellLineCount, cellLastLineEndColumn + 1, cellLineCount, cellLastLineEndColumn + 1), text: insertContent }), - new ResourceNotebookCellEdit(this._notebook.uri, - { - editType: CellEditType.Replace, - index: index + 1, - count: 1, - cells: [] - } - ) - ], - { quotableLabel: 'Join Notebook Cells' } - ); - - return { cell, deletedCells: [below] }; - } - } - getEditorViewState(): INotebookEditorViewState { const editingCells: { [key: number]: boolean } = {}; this._viewCells.forEach((cell, i) => { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 3c4d8eb1c8d..c48363ef2ed 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -219,6 +219,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel private _operationManager: NotebookOperationManager; private _eventEmitter: DelayedEmitter; + get length() { + return this._cells.length; + } + get cells(): readonly NotebookCellTextModel[] { return this._cells; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 20d063da235..9baeed7cc7b 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -463,6 +463,7 @@ export namespace CellUri { type MimeTypeInfo = { alwaysSecure?: boolean; supportedByCore?: boolean; + mergeable?: boolean; }; const _mimeTypeInfo = new Map([ @@ -476,6 +477,9 @@ const _mimeTypeInfo = new Map([ ['image/jpeg', { supportedByCore: true }], ['text/x-javascript', { supportedByCore: true }], ['application/x.notebook.error-traceback', { alwaysSecure: true, supportedByCore: true }], + ['application/x.notebook.stream', { alwaysSecure: true, supportedByCore: true, mergeable: true }], + ['application/x.notebook.stdout', { alwaysSecure: true, supportedByCore: true, mergeable: true }], + ['application/x.notebook.stderr', { alwaysSecure: true, supportedByCore: true, mergeable: true }], ]); export function mimeTypeIsAlwaysSecure(mimeType: string): boolean { @@ -486,6 +490,10 @@ export function mimeTypeSupportedByCore(mimeType: string) { return _mimeTypeInfo.get(mimeType)?.supportedByCore ?? false; } +export function mimeTypeIsMergeable(mimeType: string): boolean { + return _mimeTypeInfo.get(mimeType)?.mergeable ?? false; +} + // if (isWindows) { // value = value.replace(/\//g, '\\'); // } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 7eef151b82a..294e6611938 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -71,8 +71,8 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM this._register(this._workingCopyService.registerWorkingCopy(workingCopyAdapter)); this._register(this._fileService.onDidFilesChange(async e => { - if (this.isDirty() || !this.isResolved()) { - // skip when dirty or unresolved... + if (this.isDirty() || !this.isResolved() || this._saveSequentializer.hasPending()) { + // skip when dirty, unresolved, or when saving return; } if (!e.affects(this.resource, FileChangeType.UPDATED)) { @@ -81,6 +81,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM } const stats = await this._resolveStats(this.resource); if (stats && this._lastResolvedFileStat && stats.etag !== this._lastResolvedFileStat.etag) { + this._logService.debug('[notebook editor model] trigger load after file event'); this.load({ forceReadFromDisk: true }); } })); @@ -151,6 +152,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM async load(options?: INotebookLoadOptions): Promise { if (options?.forceReadFromDisk) { + this._logService.debug('[notebook editor model] load from provider (forceRead)', this.resource.toString()); this._loadFromProvider(undefined); assertType(this.isResolved()); return this; @@ -166,6 +168,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM return this; // Make sure meanwhile someone else did not succeed in loading } + this._logService.debug('[notebook editor model] load from provider', this.resource.toString()); await this._loadFromProvider(backup?.meta?.backupId); assertType(this.isResolved()); return this; @@ -193,6 +196,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM } if (!this.notebook) { + this._logService.debug('[notebook editor model] loading NEW notebook', this.resource.toString()); // FRESH there is no notebook yet and we are now creating it // UGLY @@ -235,6 +239,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM } else { // UPDATE exitsing notebook with data that we have just fetched + this._logService.debug('[notebook editor model] loading onto EXISTING notebook', this.resource.toString()); this.notebook.metadata = data.data.metadata; this.notebook.transientOptions = data.transientOptions; const edits: ICellEditOperation[] = [{ editType: CellEditType.Replace, index: 0, count: this.notebook.cells.length, cells: data.data.cells }]; diff --git a/src/vs/workbench/contrib/notebook/test/notebookEditor.test.ts b/src/vs/workbench/contrib/notebook/test/notebookEditor.test.ts index 27e7f4c6914..ce65216a184 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookEditor.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookEditor.test.ts @@ -16,7 +16,6 @@ suite('ListViewInfoAccessor', () => { test('basics', async function () { await withTestNotebook( - instantiationService, [ ['# header a', 'markdown', CellKind.Markdown, [], {}], ['var b = 1;', 'javascript', CellKind.Code, [], {}], @@ -24,7 +23,8 @@ suite('ListViewInfoAccessor', () => { ['var b = 2;', 'javascript', CellKind.Code, [], {}], ['var c = 3;', 'javascript', CellKind.Code, [], {}] ], - (_, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); diff --git a/src/vs/workbench/contrib/notebook/test/notebookEditorKernelManager.test.ts b/src/vs/workbench/contrib/notebook/test/notebookEditorKernelManager.test.ts index 5e33080f8a5..9a25a984e5c 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookEditorKernelManager.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookEditorKernelManager.test.ts @@ -31,7 +31,7 @@ suite('NotebookEditorKernelManager', () => { const loadKernelPreloads = async () => { }; async function withTestNotebook(cells: [string, string, CellKind, IOutputDto[], NotebookCellMetadata][], callback: (viewModel: NotebookViewModel, textModel: NotebookTextModel) => void | Promise) { - return _withTestNotebook(instantiationService, cells, (_editor, viewModel, textModel) => callback(viewModel, textModel)); + return _withTestNotebook(cells, (editor) => callback(editor.viewModel, editor.viewModel.notebookDocument)); } test('ctor', () => { diff --git a/src/vs/workbench/contrib/notebook/test/notebookSelection.test.ts b/src/vs/workbench/contrib/notebook/test/notebookSelection.test.ts index c7dd1c62c1d..18b9fcb98ab 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookSelection.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookSelection.test.ts @@ -26,12 +26,12 @@ suite('NotebookCellList focus/selection', () => { test('notebook cell list setFocus', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], {}], ['var b = 2;', 'javascript', CellKind.Code, [], {}] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const cellList = createNotebookCellList(instantiationService); cellList.attachViewModel(viewModel); @@ -47,12 +47,12 @@ suite('NotebookCellList focus/selection', () => { test('notebook cell list setSelections', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], {}], ['var b = 2;', 'javascript', CellKind.Code, [], {}] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const cellList = createNotebookCellList(instantiationService); cellList.attachViewModel(viewModel); @@ -69,12 +69,12 @@ suite('NotebookCellList focus/selection', () => { test('notebook cell list setFocus', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], {}], ['var b = 2;', 'javascript', CellKind.Code, [], {}] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const cellList = createNotebookCellList(instantiationService); cellList.attachViewModel(viewModel); @@ -93,7 +93,6 @@ suite('NotebookCellList focus/selection', () => { test('notebook cell list focus/selection from UI', async function () { await withTestNotebook( - instantiationService, [ ['# header a', 'markdown', CellKind.Markdown, [], {}], ['var b = 1;', 'javascript', CellKind.Code, [], {}], @@ -101,7 +100,8 @@ suite('NotebookCellList focus/selection', () => { ['var b = 2;', 'javascript', CellKind.Code, [], {}], ['# header c', 'markdown', CellKind.Markdown, [], {}] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const cellList = createNotebookCellList(instantiationService); cellList.attachViewModel(viewModel); assert.deepStrictEqual(viewModel.getFocus(), { start: 0, end: 1 }); @@ -129,7 +129,6 @@ suite('NotebookCellList focus/selection', () => { test('notebook cell list focus/selection with folding regions', async function () { await withTestNotebook( - instantiationService, [ ['# header a', 'markdown', CellKind.Markdown, [], {}], ['var b = 1;', 'javascript', CellKind.Code, [], {}], @@ -137,7 +136,8 @@ suite('NotebookCellList focus/selection', () => { ['var b = 2;', 'javascript', CellKind.Code, [], {}], ['# header c', 'markdown', CellKind.Markdown, [], {}] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); @@ -174,7 +174,6 @@ suite('NotebookCellList focus/selection', () => { test('notebook cell list focus/selection with folding regions and applyEdits', async function () { await withTestNotebook( - instantiationService, [ ['# header a', 'markdown', CellKind.Markdown, [], {}], ['var b = 1;', 'javascript', CellKind.Code, [], {}], @@ -184,7 +183,8 @@ suite('NotebookCellList focus/selection', () => { ['# header d', 'markdown', CellKind.Markdown, [], {}], ['var e = 4;', 'javascript', CellKind.Code, [], {}], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); @@ -228,7 +228,6 @@ suite('NotebookCellList focus/selection', () => { test('notebook cell list getModelIndex', async function () { await withTestNotebook( - instantiationService, [ ['# header a', 'markdown', CellKind.Markdown, [], {}], ['var b = 1;', 'javascript', CellKind.Code, [], {}], @@ -236,7 +235,8 @@ suite('NotebookCellList focus/selection', () => { ['var b = 2;', 'javascript', CellKind.Code, [], {}], ['# header c', 'markdown', CellKind.Markdown, [], {}] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const foldingModel = new FoldingModel(); foldingModel.attachViewModel(viewModel); @@ -258,12 +258,12 @@ suite('NotebookCellList focus/selection', () => { test('notebook validate range', async () => { await withTestNotebook( - instantiationService, [ ['# header a', 'markdown', CellKind.Markdown, [], {}], ['var b = 1;', 'javascript', CellKind.Code, [], {}] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; assert.deepStrictEqual(viewModel.validateRange(null), null); assert.deepStrictEqual(viewModel.validateRange(undefined), null); assert.deepStrictEqual(viewModel.validateRange({ start: 0, end: 0 }), null); @@ -278,12 +278,12 @@ suite('NotebookCellList focus/selection', () => { test('notebook updateSelectionState', async function () { await withTestNotebook( - instantiationService, [ ['# header a', 'markdown', CellKind.Markdown, [], {}], ['var b = 1;', 'javascript', CellKind.Code, [], {}] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 1, end: 2 }, selections: [{ start: 1, end: 2 }, { start: -1, end: 0 }] }); assert.deepStrictEqual(viewModel.getSelections(), [{ start: 1, end: 2 }]); }); @@ -291,12 +291,12 @@ suite('NotebookCellList focus/selection', () => { test('notebook cell selection w/ cell deletion', async function () { await withTestNotebook( - instantiationService, [ ['# header a', 'markdown', CellKind.Markdown, [], {}], ['var b = 1;', 'javascript', CellKind.Code, [], {}] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 1, end: 2 }, selections: [{ start: 1, end: 2 }] }); viewModel.deleteCell(1, true, false); assert.deepStrictEqual(viewModel.getFocus(), { start: 0, end: 1 }); diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index 079191fba8c..1f9790c7cb0 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -16,14 +16,15 @@ suite('NotebookTextModel', () => { test('insert', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], - (editor, viewModel, textModel) => { + (editor) => { + const viewModel = editor.viewModel; + const textModel = editor.viewModel.notebookDocument; textModel.applyEdits([ { editType: CellEditType.Replace, index: 1, count: 0, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, { editType: CellEditType.Replace, index: 3, count: 0, cells: [new TestCell(viewModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], textModelService)] }, @@ -39,14 +40,15 @@ suite('NotebookTextModel', () => { test('multiple inserts at same position', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], - (editor, viewModel, textModel) => { + (editor) => { + const viewModel = editor.viewModel; + const textModel = editor.viewModel.notebookDocument; textModel.applyEdits([ { editType: CellEditType.Replace, index: 1, count: 0, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, { editType: CellEditType.Replace, index: 1, count: 0, cells: [new TestCell(viewModel.viewType, 6, 'var f = 6;', 'javascript', CellKind.Code, [], textModelService)] }, @@ -62,14 +64,14 @@ suite('NotebookTextModel', () => { test('delete', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], - (editor, viewModel, textModel) => { + (editor) => { + const textModel = editor.viewModel.notebookDocument; textModel.applyEdits([ { editType: CellEditType.Replace, index: 1, count: 1, cells: [] }, { editType: CellEditType.Replace, index: 3, count: 1, cells: [] }, @@ -83,14 +85,15 @@ suite('NotebookTextModel', () => { test('delete + insert', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], - (editor, viewModel, textModel) => { + (editor) => { + const viewModel = editor.viewModel; + const textModel = editor.viewModel.notebookDocument; textModel.applyEdits([ { editType: CellEditType.Replace, index: 1, count: 1, cells: [] }, { editType: CellEditType.Replace, index: 3, count: 0, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, @@ -106,14 +109,15 @@ suite('NotebookTextModel', () => { test('delete + insert at same position', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], - (editor, viewModel, textModel) => { + (editor) => { + const viewModel = editor.viewModel; + const textModel = editor.viewModel.notebookDocument; textModel.applyEdits([ { editType: CellEditType.Replace, index: 1, count: 1, cells: [] }, { editType: CellEditType.Replace, index: 1, count: 0, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, @@ -129,14 +133,15 @@ suite('NotebookTextModel', () => { test('(replace) delete + insert at same position', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], - (editor, viewModel, textModel) => { + (editor) => { + const viewModel = editor.viewModel; + const textModel = editor.viewModel.notebookDocument; textModel.applyEdits([ { editType: CellEditType.Replace, index: 1, count: 1, cells: [new TestCell(viewModel.viewType, 5, 'var e = 5;', 'javascript', CellKind.Code, [], textModelService)] }, ], true, undefined, () => undefined, undefined); @@ -151,11 +156,11 @@ suite('NotebookTextModel', () => { test('output', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ], - (editor, viewModel, textModel) => { + (editor) => { + const textModel = editor.viewModel.notebookDocument; // invalid index 1 assert.throws(() => { @@ -224,11 +229,11 @@ suite('NotebookTextModel', () => { test('metadata', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ], - (editor, viewModel, textModel) => { + (editor) => { + const textModel = editor.viewModel.notebookDocument; // invalid index 1 assert.throws(() => { @@ -262,14 +267,15 @@ suite('NotebookTextModel', () => { test('multiple inserts in one edit', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], - (editor, viewModel, textModel) => { + (editor) => { + const viewModel = editor.viewModel; + const textModel = editor.viewModel.notebookDocument; let changeEvent: NotebookTextModelChangedEvent | undefined = undefined; const eventListener = textModel.onDidChangeContent(e => { changeEvent = e; @@ -297,14 +303,14 @@ suite('NotebookTextModel', () => { test('insert and metadata change in one edit', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] ], - (editor, viewModel, textModel) => { + (editor) => { + const textModel = editor.viewModel.notebookDocument; let changeEvent: NotebookTextModelChangedEvent | undefined = undefined; const eventListener = textModel.onDidChangeContent(e => { changeEvent = e; @@ -331,9 +337,10 @@ suite('NotebookTextModel', () => { test('Updating appending/updating output in Notebooks does not work as expected #117273', function () { - withTestNotebook(instantiationService, [ + withTestNotebook([ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }] - ], (_editor, _view, model) => { + ], (editor) => { + const model = editor.viewModel.notebookDocument; assert.strictEqual(model.cells.length, 1); assert.strictEqual(model.cells[0].outputs.length, 0); diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 1bd2891e978..9de4cd5238e 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -32,12 +32,12 @@ suite('NotebookViewModel', () => { test('insert/delete', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; assert.equal(viewModel.viewCells[0].metadata?.editable, true); assert.equal(viewModel.viewCells[1].metadata?.editable, false); @@ -56,13 +56,13 @@ suite('NotebookViewModel', () => { test('move cells down', async function () { await withTestNotebook( - instantiationService, [ ['//a', 'javascript', CellKind.Code, [], { editable: true }], ['//b', 'javascript', CellKind.Code, [], { editable: true }], ['//c', 'javascript', CellKind.Code, [], { editable: true }], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; viewModel.moveCellToIdx(0, 1, 0, true); // no-op assert.equal(viewModel.viewCells[0].getText(), '//a'); @@ -85,13 +85,13 @@ suite('NotebookViewModel', () => { test('move cells up', async function () { await withTestNotebook( - instantiationService, [ ['//a', 'javascript', CellKind.Code, [], { editable: true }], ['//b', 'javascript', CellKind.Code, [], { editable: true }], ['//c', 'javascript', CellKind.Code, [], { editable: true }], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; viewModel.moveCellToIdx(1, 1, 0, true); // b, a, c assert.equal(viewModel.viewCells[0].getText(), '//b'); @@ -108,12 +108,12 @@ suite('NotebookViewModel', () => { test('index', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true }] ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const firstViewCell = viewModel.viewCells[0]; const lastViewCell = viewModel.viewCells[viewModel.viewCells.length - 1]; @@ -135,7 +135,6 @@ suite('NotebookViewModel', () => { test('metadata', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], {}], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true }], @@ -143,7 +142,8 @@ suite('NotebookViewModel', () => { ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }], ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false }], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; viewModel.notebookDocument.metadata = { editable: true, cellEditable: true, cellHasExecutionOrder: true, trusted: true }; const defaults = { hasExecutionOrder: true }; @@ -237,11 +237,8 @@ function getVisibleCells(cells: T[], hiddenRanges: ICellRange[]) { } suite('NotebookViewModel Decorations', () => { - const instantiationService = setupInstantiationService(); - test('tracking range', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], {}], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true }], @@ -249,7 +246,8 @@ suite('NotebookViewModel Decorations', () => { ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }], ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false }], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const trackedId = viewModel.setTrackedRange('test', { start: 1, end: 2 }, TrackedRangeStickiness.GrowsOnlyWhenTypingAfter); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, @@ -297,7 +295,6 @@ suite('NotebookViewModel Decorations', () => { test('tracking range 2', async function () { await withTestNotebook( - instantiationService, [ ['var a = 1;', 'javascript', CellKind.Code, [], {}], ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true }], @@ -307,7 +304,8 @@ suite('NotebookViewModel Decorations', () => { ['var e = 6;', 'javascript', CellKind.Code, [], { editable: false }], ['var e = 7;', 'javascript', CellKind.Code, [], { editable: false }], ], - (editor, viewModel) => { + (editor) => { + const viewModel = editor.viewModel; const trackedId = viewModel.setTrackedRange('test', { start: 1, end: 3 }, TrackedRangeStickiness.GrowsOnlyWhenTypingAfter); assert.deepEqual(viewModel.getTrackedRange(trackedId!), { start: 1, diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 936b24e7a3d..f8e20a4a655 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -4,17 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { NotImplementedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; -import { Range } from 'vs/editor/common/core/range'; -import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -22,14 +16,12 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { IListService, ListService } from 'vs/platform/list/browser/listService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { EditorModel } from 'vs/workbench/common/editor'; -import { ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, INotebookDeltaDecoration, INotebookEditorCreationOptions, NotebookEditorOptions, ICellOutputViewModel, IInsetRenderOutput, ICommonCellInfo, IGenericCellViewModel, INotebookCellOutputLayoutInfo, CellEditState, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; +import { ICellViewModel, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; -import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; +import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellUri, ICellRange, INotebookEditorModel, INotebookKernel, IOutputDto, IResolvedNotebookEditorModel, NotebookCellMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; +import { CellKind, CellUri, ICellRange, INotebookEditorModel, IOutputDto, IResolvedNotebookEditorModel, NotebookCellMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; @@ -39,8 +31,10 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { IFileStatWithMetadata } from 'vs/platform/files/common/files'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; +import { ListViewInfoAccessor } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { mock } from 'vs/base/test/common/mock'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export class TestCell extends NotebookCellTextModel { constructor( @@ -56,329 +50,6 @@ export class TestCell extends NotebookCellTextModel { } } -export class TestNotebookEditor implements INotebookEditor { - isEmbedded = false; - private _isDisposed = false; - - get isDisposed() { - return this._isDisposed; - } - - creationOptions: INotebookEditorCreationOptions = { isEmbedded: false }; - - constructor(readonly viewModel: NotebookViewModel) { } - getCellRangeFromViewRange(startIndex: number, endIndex: number): ICellRange | undefined { - throw new Error('Method not implemented.'); - } - getViewIndex(cell: ICellViewModel): number { - throw new Error('Method not implemented.'); - } - getCellsFromViewRange(startIndex: number, endIndex: number): ICellViewModel[] { - throw new Error('Method not implemented.'); - } - getFocus(): ICellRange | undefined { - return undefined; - } - getVisibleRangesPlusViewportAboveBelow(): ICellRange[] { - throw new Error('Method not implemented.'); - } - getSelection(): ICellRange | undefined { - throw new Error('Method not implemented.'); - } - getSelections(): ICellRange[] { - throw new Error('Method not implemented.'); - } - getSelectionViewModels(): ICellViewModel[] { - throw new Error('Method not implemented.'); - } - revealCellRangeInView(range: ICellRange): void { - throw new Error('Method not implemented.'); - } - revealInViewAtTop(cell: ICellViewModel): void { - throw new Error('Method not implemented.'); - } - getCellOutputLayoutInfo(cell: IGenericCellViewModel): INotebookCellOutputLayoutInfo { - throw new Error('Method not implemented.'); - } - focusNextNotebookCell(cell: ICellViewModel, focus: 'editor' | 'container' | 'output'): void { - throw new Error('Method not implemented.'); - } - getCellByInfo(cellInfo: ICommonCellInfo): ICellViewModel { - throw new Error('Method not implemented.'); - } - getCellById(cellId: string): ICellViewModel { - throw new Error('Method not implemented.'); - } - updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, height: number, isInit: boolean): void { - throw new Error('Method not implemented.'); - } - - setMarkdownCellEditState(cellId: string, editState: CellEditState): void { - throw new Error('Method not implemented.'); - } - markdownCellDragStart(cellId: string, position: { clientY: number }): void { - throw new Error('Method not implemented.'); - } - markdownCellDrag(cellId: string, position: { clientY: number }): void { - throw new Error('Method not implemented.'); - } - markdownCellDragEnd(cellId: string, position: { clientY: number }): void { - throw new Error('Method not implemented.'); - } - updateMarkdownPreviewSelectionState(cell: ICellViewModel, isSelected: boolean): Promise { - throw new Error('Method not implemented.'); - } - async beginComputeContributedKernels(): Promise { - return []; - } - setEditorDecorations(key: string, range: ICellRange): void { - // throw new Error('Method not implemented.'); - } - removeEditorDecorations(key: string): void { - // throw new Error('Method not implemented.'); - } - setOptions(options: NotebookEditorOptions | undefined): Promise { - throw new Error('Method not implemented.'); - } - - hideInset(output: ICellOutputViewModel): void { - throw new Error('Method not implemented.'); - } - - multipleKernelsAvailable: boolean = false; - onDidScroll: Event = new Emitter().event; - onDidChangeAvailableKernels: Event = new Emitter().event; - onDidChangeActiveCell: Event = new Emitter().event; - onDidChangeVisibleRanges: Event = new Emitter().event; - onDidChangeSelection: Event = new Emitter().event; - visibleRanges: ICellRange[] = []; - textModel?: NotebookTextModel | undefined; - - hasModel(): this is IActiveNotebookEditor { - return true; - } - - onDidFocusEditorWidget: Event = new Emitter().event; - hasFocus(): boolean { - return true; - } - - hasWebviewFocus() { - return false; - } - - hasOutputTextSelection() { - return false; - } - - getId(): string { - return 'notebook.testEditor'; - } - - cursorNavigationMode = false; - activeKernel: INotebookKernel | undefined; - onDidChangeKernel: Event = new Emitter().event; - onDidChangeActiveEditor: Event = new Emitter().event; - activeCodeEditor: IEditor | undefined; - getDomNode(): HTMLElement { - throw new Error('Method not implemented.'); - } - - getOverflowContainerDomNode(): HTMLElement { - throw new Error('Method not implemented.'); - } - - private _onDidChangeModel = new Emitter(); - onDidChangeModel: Event = this._onDidChangeModel.event; - getContribution(id: string): T { - throw new Error('Method not implemented.'); - } - onMouseUp(listener: (e: INotebookEditorMouseEvent) => void): IDisposable { - throw new Error('Method not implemented.'); - } - onMouseDown(listener: (e: INotebookEditorMouseEvent) => void): IDisposable { - throw new Error('Method not implemented.'); - } - - setHiddenAreas(_ranges: ICellRange[]): boolean { - throw new Error('Method not implemented.'); - } - - getInnerWebview(): Webview | undefined { - throw new Error('Method not implemented.'); - } - - cancelNotebookCellExecution(cell: ICellViewModel): void { - throw new Error('Method not implemented.'); - } - - executeNotebook(): Promise { - throw new Error('Method not implemented.'); - } - - cancelNotebookExecution(): void { - throw new Error('Method not implemented.'); - } - - executeNotebookCell(cell: ICellViewModel): Promise { - throw new Error('Method not implemented.'); - } - - postMessage(): void { - throw new Error('Method not implemented.'); - } - - addClassName(className: string): void { - throw new Error('Method not implemented.'); - } - - removeClassName(className: string): void { - throw new Error('Method not implemented.'); - } - - setCellEditorSelection(cell: CellViewModel, selection: Range): void { - throw new Error('Method not implemented.'); - } - - focusElement(cell: CellViewModel): void { - throw new Error('Method not implemented.'); - } - - moveCellDown(cell: CellViewModel): Promise { - throw new Error('Method not implemented.'); - } - - moveCellUp(cell: CellViewModel): Promise { - throw new Error('Method not implemented.'); - } - - async moveCellsToIdx(index: number, length: number, toIdx: number): Promise { - throw new Error('Method not implemented.'); - } - - splitNotebookCell(cell: ICellViewModel): Promise { - throw new Error('Method not implemented.'); - } - - joinNotebookCells(cell: ICellViewModel, direction: 'above' | 'below', constraint?: CellKind): Promise { - throw new Error('Method not implemented.'); - } - - setSelection(cell: CellViewModel, selection: Range): void { - throw new Error('Method not implemented.'); - } - revealRangeInViewAsync(cell: CellViewModel, range: Range): Promise { - throw new Error('Method not implemented.'); - } - revealRangeInCenterAsync(cell: CellViewModel, range: Range): Promise { - throw new Error('Method not implemented.'); - } - revealRangeInCenterIfOutsideViewportAsync(cell: CellViewModel, range: Range): Promise { - throw new Error('Method not implemented.'); - } - - revealLineInViewAsync(cell: CellViewModel, line: number): Promise { - throw new Error('Method not implemented.'); - } - getLayoutInfo(): NotebookLayoutInfo { - throw new Error('Method not implemented.'); - } - revealLineInCenterIfOutsideViewportAsync(cell: CellViewModel, line: number): Promise { - throw new Error('Method not implemented.'); - } - revealLineInCenterAsync(cell: CellViewModel, line: number): Promise { - throw new Error('Method not implemented.'); - } - focus(): void { - throw new Error('Method not implemented.'); - } - showFind(): void { - throw new Error('Method not implemented.'); - } - hideFind(): void { - throw new Error('Method not implemented.'); - } - revealInView(cell: CellViewModel): void { - throw new Error('Method not implemented.'); - } - revealInCenter(cell: CellViewModel): void { - throw new Error('Method not implemented.'); - } - revealInCenterIfOutsideViewport(cell: CellViewModel): void { - throw new Error('Method not implemented.'); - } - insertNotebookCell(cell: CellViewModel, type: CellKind, direction: 'above' | 'below'): CellViewModel { - throw new Error('Method not implemented.'); - } - deleteNotebookCell(cell: CellViewModel): Promise { - throw new Error('Method not implemented.'); - } - focusNotebookCell(cell: CellViewModel, focusItem: 'editor' | 'container' | 'output'): void { - // throw new Error('Method not implemented.'); - } - getActiveCell(): CellViewModel | undefined { - // throw new Error('Method not implemented.'); - return; - } - async layoutNotebookCell(cell: CellViewModel, height: number): Promise { - // throw new Error('Method not implemented.'); - return; - } - createOutput(cell: CellViewModel, output: IInsetRenderOutput, offset: number): Promise { - return Promise.resolve(); - } - createMarkdownPreview(cell: ICellViewModel): Promise { - return Promise.resolve(); - } - async unhideMarkdownPreview(cell: ICellViewModel): Promise { - // noop - } - async hideMarkdownPreview(cell: ICellViewModel): Promise { - // noop - } - removeMarkdownPreview(cell: ICellViewModel): Promise { - return Promise.resolve(); - } - updateMarkdownCellHeight(cellId: string, height: number, isInit: boolean): void { - // noop - } - removeInset(output: ICellOutputViewModel): void { - // throw new Error('Method not implemented.'); - } - triggerScroll(event: IMouseWheelEvent): void { - // throw new Error('Method not implemented.'); - } - getFontInfo(): BareFontInfo | undefined { - return BareFontInfo.createFromRawSettings({ - fontFamily: 'Monaco', - }, 1, 1, true); - } - getOutputRenderer(): OutputRenderer { - throw new Error('Method not implemented.'); - } - - changeModelDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null { - throw new Error('Method not implemented.'); - } - - deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] { - throw new Error('Method not implemented.'); - } - - deltaCellOutputContainerClassNames(cellId: string, added: string[], removed: string[]): void { - throw new Error('Method not implemented.'); - } - - dispose() { - this._isDisposed = true; - } -} - -// export function createTestCellViewModel(instantiationService: IInstantiationService, viewType: string, notebookHandle: number, cellhandle: number, source: string[], language: string, cellKind: CellKind, outputs: IOutput[]) { -// const mockCell = new TestCell(viewType, cellhandle, source, language, cellKind, outputs); -// return createCellViewModel(instantiationService, viewType, notebookHandle, mockCell); -// } - export class NotebookEditorTestModel extends EditorModel implements INotebookEditorModel { private _dirty = false; @@ -454,7 +125,6 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi export function setupInstantiationService() { const instantiationService = new TestInstantiationService(); - instantiationService.stub(IUndoRedoService, instantiationService.createInstance(UndoRedoService)); instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(IThemeService, new TestThemeService()); @@ -466,15 +136,10 @@ export function setupInstantiationService() { return instantiationService; } -export async function withTestNotebook(accessor: ServicesAccessor, cells: [source: string, lang: string, kind: CellKind, output?: IOutputDto[], metadata?: NotebookCellMetadata][], callback: (editor: TestNotebookEditor, viewModel: NotebookViewModel, textModel: NotebookTextModel) => Promise | R): Promise { - - const instantiationService = accessor.get(IInstantiationService); - const undoRedoService = accessor.get(IUndoRedoService); - const textModelService = accessor.get(ITextModelService); - const bulkEditService = accessor.get(IBulkEditService); - +export async function withTestNotebook(cells: [source: string, lang: string, kind: CellKind, output?: IOutputDto[], metadata?: NotebookCellMetadata][], callback: (editor: IActiveNotebookEditor, accessor: ServicesAccessor) => Promise | R): Promise { + const instantiationService = setupInstantiationService(); const viewType = 'notebook'; - const notebook = new NotebookTextModel(viewType, URI.parse('test'), cells.map(cell => { + const notebook = instantiationService.createInstance(NotebookTextModel, viewType, URI.parse('test'), cells.map(cell => { return { source: cell[0], language: cell[1], @@ -482,13 +147,33 @@ export async function withTestNotebook(accessor: ServicesAccessor, cell outputs: cell[3] ?? [], metadata: cell[4] }; - }), notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }, undoRedoService, textModelService); + }), notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }); + const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); - const viewModel = new NotebookViewModel(viewType, model.notebook, eventDispatcher, null, instantiationService, bulkEditService, undoRedoService); - const editor = new TestNotebookEditor(viewModel); + const viewModel: NotebookViewModel = instantiationService.createInstance(NotebookViewModel, viewType, model.notebook, eventDispatcher, null); - const res = await callback(editor, viewModel, notebook); + const cellList = createNotebookCellList(instantiationService); + cellList.attachViewModel(viewModel); + const listViewInfoAccessor = new ListViewInfoAccessor(cellList); + + const notebookEditor: IActiveNotebookEditor = new class extends mock() { + onDidChangeModel: Event = new Emitter().event; + get viewModel() { return viewModel; } + hasModel(): this is IActiveNotebookEditor { + return !!this.viewModel; + } + getFocus() { return viewModel.getFocus(); } + getSelections() { return viewModel.getSelections(); } + getViewIndex(cell: ICellViewModel) { return listViewInfoAccessor.getViewIndex(cell); } + getCellRangeFromViewRange(startIndex: number, endIndex: number) { return listViewInfoAccessor.getCellRangeFromViewRange(startIndex, endIndex); } + revealCellRangeInView() { } + setHiddenAreas(_ranges: ICellRange[]): boolean { + return cellList.setHiddenAreas(_ranges, true); + } + }; + + const res = await callback(notebookEditor, instantiationService); if (res instanceof Promise) { res.finally(() => viewModel.dispose()); } else { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index bba5ee88334..a65db9f9b45 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -1010,7 +1010,7 @@ class UnsupportedSettingsRenderer extends Disposable { severity: MarkerSeverity.Hint, tags: [MarkerTag.Unnecessary], ...setting.range, - message: nls.localize('unsupportedRemoteMachineSetting', "This setting cannot be applied in this window. It will be applied when you open local window.") + message: nls.localize('unsupportedRemoteMachineSetting', "This setting cannot be applied in this window. It will be applied when you open a local window.") }); } } diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index 34099db8509..a41b38ef4db 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -293,7 +293,7 @@ class OnAutoForwardedAction extends Disposable { } private basicMessage(tunnel: RemoteTunnel) { - return nls.localize('remote.tunnelsView.automaticForward', "Your service running on port {0} is available. ", + return nls.localize('remote.tunnelsView.automaticForward', "Your application running on port {0} is available. ", tunnel.tunnelRemotePort); } diff --git a/src/vs/workbench/contrib/remote/browser/remoteIcons.ts b/src/vs/workbench/contrib/remote/browser/remoteIcons.ts index b8a1498e2dd..99bd6a48f17 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIcons.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIcons.ts @@ -25,5 +25,6 @@ export const stopForwardIcon = registerIcon('ports-stop-forward-icon', Codicon.x export const openBrowserIcon = registerIcon('ports-open-browser-icon', Codicon.globe, nls.localize('openBrowserIcon', 'Icon for the open browser action.')); export const openPreviewIcon = registerIcon('ports-open-preview-icon', Codicon.openPreview, nls.localize('openPreviewIcon', 'Icon for the open preview action.')); export const copyAddressIcon = registerIcon('ports-copy-address-icon', Codicon.clippy, nls.localize('copyAddressIcon', 'Icon for the copy local address action.')); +export const labelPortIcon = registerIcon('ports-label-icon', Codicon.tag, nls.localize('labelPortIcon', 'Icon for the label port action.')); export const forwardedPortWithoutProcessIcon = registerIcon('ports-forwarded-without-process-icon', Codicon.circleOutline, nls.localize('forwardedPortWithoutProcessIcon', 'Icon for forwarded ports that don\'t have a running process.')); export const forwardedPortWithProcessIcon = registerIcon('ports-forwarded-with-process-icon', Codicon.circleFilled, nls.localize('forwardedPortWithProcessIcon', 'Icon for forwarded ports that do have a running process.')); diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 710a2ee4052..296fb1f6965 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -39,7 +39,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { copyAddressIcon, forwardedPortWithoutProcessIcon, forwardedPortWithProcessIcon, forwardPortIcon, openBrowserIcon, openPreviewIcon, portsViewIcon, privatePortIcon, publicPortIcon, stopForwardIcon } from 'vs/workbench/contrib/remote/browser/remoteIcons'; +import { copyAddressIcon, forwardedPortWithoutProcessIcon, forwardedPortWithProcessIcon, forwardPortIcon, labelPortIcon, openBrowserIcon, openPreviewIcon, portsViewIcon, privatePortIcon, publicPortIcon, stopForwardIcon } from 'vs/workbench/contrib/remote/browser/remoteIcons'; import { IExternalUriOpenerService } from 'vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { isWeb } from 'vs/base/common/platform'; @@ -696,7 +696,8 @@ export class TunnelPanel extends ViewPane { } }, getWidgetAriaLabel: () => nls.localize('tunnelView', "Tunnel View") - } + }, + openOnSingleClick: false } ) as WorkbenchTable; @@ -717,10 +718,9 @@ export class TunnelPanel extends ViewPane { rerender(); })); - // TODO@alexr00 Joao asks: why the debounce? - this._register(Event.debounce(this.table.onDidOpen, (last, event) => event, 75, true)(e => { - if (e.element && (e.element.tunnelType === TunnelType.Add)) { - this.commandService.executeCommand(ForwardPortAction.INLINE_ID); + this._register(this.table.onDidOpen(e => { + if (e.element && (e.element.tunnelType === TunnelType.Forwarded)) { + this.commandService.executeCommand(LabelTunnelAction.ID); } })); @@ -741,7 +741,9 @@ export class TunnelPanel extends ViewPane { this.table.reveal(this.table.indexOf(this.viewModel.input)); } } else { - this.table.setFocus(this.lastFocus); + if (e && (e.tunnel.tunnelType !== TunnelType.Add)) { + this.table.setFocus(this.lastFocus); + } this.focus(); } })); @@ -1329,15 +1331,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ ContextKeyExpr.or(WebContextKey.negate(), TunnelPrivacyContextKey.isEqualTo(TunnelPrivacy.Public)), ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Detected))) })); -MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ - group: '0_manage', // 0_manage is used by extensions, so try not to change it - order: 0, - command: { - id: LabelTunnelAction.ID, - title: LabelTunnelAction.LABEL, - }, - when: TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded) -})); +// The group 0_manage is used by extensions, so try not to change it MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ group: '2_localaddress', order: 0, @@ -1402,6 +1396,15 @@ MenuRegistry.appendMenuItem(MenuId.TunnelPortInline, ({ }, when: TunnelTypeContextKey.isEqualTo(TunnelType.Candidate) })); +MenuRegistry.appendMenuItem(MenuId.TunnelPortInline, ({ + order: 1, + command: { + id: LabelTunnelAction.ID, + title: LabelTunnelAction.LABEL, + icon: labelPortIcon + }, + when: TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded) +})); MenuRegistry.appendMenuItem(MenuId.TunnelPortInline, ({ order: 2, command: { diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 707332a5ec5..fe5715db615 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -175,21 +175,21 @@ Registry.as(ConfigurationExtensions.Configuration) 'label': { type: 'string', description: localize('remote.portsAttributes.label', "Label that will be shown in the UI for this port."), - default: localize('remote.portsAttributes.labelDefault', "Labeled Port") + default: localize('remote.portsAttributes.labelDefault', "Application") } }, default: { - 'label': localize('remote.portsAttributes.labelDefault', "Labeled Port"), + 'label': localize('remote.portsAttributes.labelDefault', "Application"), 'onAutoForward': 'notify' } } }, - markdownDescription: localize('remote.portsAttributes', "Set properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Labeled Port\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```"), - defaultSnippets: [{ body: { '${1:3000}': { label: '${2:My Port}', onAutoForward: 'openPreview' } } }], + markdownDescription: localize('remote.portsAttributes', "Set properties that are applied when a specific port number is forwarded. For example:\n\n```\n\"3000\": {\n \"label\": \"Application\"\n},\n\"40000-55000\": {\n \"onAutoForward\": \"ignore\"\n},\n\".+\\\\/server.js\": {\n \"onAutoForward\": \"openPreview\"\n}\n```"), + defaultSnippets: [{ body: { '${1:3000}': { label: '${2:Application}', onAutoForward: 'openPreview' } } }], errorMessage: localize('remote.portsAttributes.patternError', "Must be a port number, range of port numbers, or regular expression."), additionalProperties: false }, - 'remote.portsAttributes.defaults': { + 'remote.unconfiguredPortsAttributes': { type: 'object', properties: { 'onAutoForward': { @@ -213,13 +213,11 @@ Registry.as(ConfigurationExtensions.Configuration) 'label': { type: 'string', description: localize('remote.portsAttributes.label', "Label that will be shown in the UI for this port."), - default: localize('remote.portsAttributes.labelDefault', "Labeled Port") + default: localize('remote.portsAttributes.labelDefault', "Application") } }, - default: { - 'onAutoForward': 'notify' - }, - markdownDescription: localize('remote.portsAttributes.defaults', "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"notify\"\n}\n```"), + defaultSnippets: [{ body: { onAutoForward: 'ignore' } }], + markdownDescription: localize('remote.portsAttributes.defaults', "Set default properties that are applied to all ports that don't get properties from the setting `remote.portsAttributes`. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```"), additionalProperties: false } } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 24a77eaec85..9061009b2ed 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -313,6 +313,7 @@ export class SearchView extends ViewPane { this.inputPatternIncludes = this._register(this.instantiationService.createInstance(IncludePatternInputWidget, folderIncludesList, this.contextViewService, { ariaLabel: nls.localize('label.includes', 'Search Include Patterns'), + placeholder: nls.localize('placeholder.includes', "(e.g. *.ts, src/**/include)"), history: patternIncludesHistory, })); @@ -330,6 +331,7 @@ export class SearchView extends ViewPane { dom.append(excludesList, $('h4', undefined, excludesTitle)); this.inputPatternExcludes = this._register(this.instantiationService.createInstance(ExcludePatternInputWidget, excludesList, this.contextViewService, { ariaLabel: nls.localize('label.excludes', 'Search Exclude Patterns'), + placeholder: nls.localize('placeholder.excludes', "(e.g. *.ts, src/**/exclude)"), history: patternExclusionsHistory, })); diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index 5e6c6f746fb..f1abc80d5d3 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -44,7 +44,7 @@ export abstract class AbstractProblemCollector implements IDisposable { private buffer: string[]; private bufferLength: number; private openModels: IStringDictionary; - private readonly modelListeners = new DisposableStore(); + protected readonly modelListeners = new DisposableStore(); private tail: Promise | undefined; // [owner] -> ApplyToKind @@ -58,7 +58,7 @@ export abstract class AbstractProblemCollector implements IDisposable { protected _onDidStateChange: Emitter; - constructor(problemMatchers: ProblemMatcher[], protected markerService: IMarkerService, private modelService: IModelService, fileService?: IFileService) { + constructor(problemMatchers: ProblemMatcher[], protected markerService: IMarkerService, protected modelService: IModelService, fileService?: IFileService) { this.matchers = Object.create(null); this.bufferLength = 1; problemMatchers.map(elem => createLineMatcher(elem, fileService)).forEach((matcher) => { @@ -406,6 +406,8 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement private currentOwner: string | undefined; private currentResource: string | undefined; + private lines: string[] = []; + constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService, fileService?: IFileService) { super(problemMatchers, markerService, modelService, fileService); this.problemMatchers = problemMatchers; @@ -423,6 +425,27 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement }); } }); + + this.modelListeners.add(this.modelService.onModelRemoved(modelEvent => { + let markerChanged: IDisposable | undefined = + Event.debounce(this.markerService.onMarkerChanged, (last: readonly URI[] | undefined, e: readonly URI[]) => { + return (last ?? []).concat(e); + }, 500)(async (markerEvent) => { + markerChanged?.dispose(); + markerChanged = undefined; + if (!markerEvent.includes(modelEvent.uri)) { + return; + } + const oldLines = Array.from(this.lines); + for (const line of oldLines) { + await this.processLineInternal(line); + } + }); + setTimeout(async () => { + markerChanged?.dispose(); + markerChanged = undefined; + }, 600); + })); } public aboutToStart(): void { @@ -439,6 +462,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement if (await this.tryBegin(line) || this.tryFinish(line)) { return; } + this.lines.push(line); let markerMatch = this.tryFindMarker(line); if (!markerMatch) { return; @@ -472,6 +496,8 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement } this._activeBackgroundMatchers.add(background.key); result = true; + this.lines = []; + this.lines.push(line); this._onDidStateChange.fire(ProblemCollectorEvent.create(ProblemCollectorEventKind.BackgroundProcessingBegins)); this.cleanMarkerCaches(); this.resetCurrentResource(); @@ -498,6 +524,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement this.resetCurrentResource(); this._onDidStateChange.fire(ProblemCollectorEvent.create(ProblemCollectorEventKind.BackgroundProcessingEnds)); result = true; + this.lines.push(line); let owner = background.matcher.owner; this.cleanMarkers(owner); this.cleanMarkerCaches(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 242a5289199..669bcec8f79 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IOffProcessTerminalService, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensions, ITerminalLaunchError, ITerminalTabLayoutInfoById, TerminalShellType } from 'vs/platform/terminal/common/terminal'; -import { IAvailableShellsRequest, ICommandTracker, IDefaultShellAndArgsRequest, INavigationMode, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, LinuxDistro, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IAvailableProfilesRequest, ICommandTracker, IDefaultShellAndArgsRequest, INavigationMode, IRemoteTerminalAttachTarget, ITerminalProfile, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, LinuxDistro, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import type { Terminal as XTermTerminal } from 'xterm'; import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11'; @@ -96,9 +96,10 @@ export interface ITerminalService { onInstancesChanged: Event; onInstanceTitleChanged: Event; onActiveInstanceChanged: Event; - onRequestAvailableShells: Event; + onRequestAvailableProfiles: Event; onDidRegisterProcessSupport: Event; onDidChangeConnectionState: Event; + onProfilesConfigChanged: Event; /** * Creates a terminal. @@ -154,7 +155,12 @@ export interface ITerminalService { */ registerLinkProvider(linkProvider: ITerminalExternalLinkProvider): IDisposable; - selectDefaultShell(): Promise; + selectDefaultProfile(): Promise; + + /** + * Gets the detected terminal profiles for the platform + */ + getAvailableProfiles(): ITerminalProfile[]; setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; manageWorkspaceShellPermissions(): void; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 8a1b69ac752..784a74d1990 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -21,7 +21,7 @@ import { ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/com import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILabelService } from 'vs/platform/label/common/label'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IPickOptions, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { ILocalTerminalService } from 'vs/platform/terminal/common/terminal'; @@ -30,7 +30,7 @@ import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/w import { FindInFilesCommand, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions'; import { Direction, IRemoteTerminalService, ITerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalQuickAccessProvider } from 'vs/workbench/contrib/terminal/browser/terminalQuickAccess'; -import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_ACTION_CATEGORY, TERMINAL_COMMAND_ID, TERMINAL_VIEW_ID, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IRemoteTerminalAttachTarget, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_ACTION_CATEGORY, TERMINAL_COMMAND_ID, TERMINAL_VIEW_ID, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; @@ -38,7 +38,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export const switchTerminalActionViewItemSeparator = '─────────'; -export const selectDefaultShellTitle = localize('workbench.action.terminal.selectDefaultShell', "Select Default Shell"); +export const selectDefaultProfileTitle = localize('workbench.action.terminal.selectDefaultProfile', "Select Default Profile"); export const configureTerminalSettingsTitle = localize('workbench.action.terminal.openSettings', "Configure Terminal Settings"); const enum ContextMenuGroup { @@ -1273,7 +1273,7 @@ export function registerTerminalActions() { title: { value: localize('workbench.action.terminal.kill', "Kill the Active Terminal Instance"), original: 'Kill the Active Terminal Instance' }, f1: true, category, - precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, + precondition: ContextKeyExpr.or(KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN), icon: Codicon.trash, menu: { id: MenuId.ViewTitle, @@ -1332,15 +1332,15 @@ export function registerTerminalActions() { registerAction2(class extends Action2 { constructor() { super({ - id: TERMINAL_COMMAND_ID.SELECT_DEFAULT_SHELL, - title: { value: localize('workbench.action.terminal.selectDefaultShell', "Select Default Shell"), original: 'Select Default Shell' }, + id: TERMINAL_COMMAND_ID.SELECT_DEFAULT_PROFILE, + title: { value: localize('workbench.action.terminal.selectDefaultProfile', "Select Default Profile"), original: 'Select Default Profile' }, f1: true, category, precondition: KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED }); } async run(accessor: ServicesAccessor) { - await accessor.get(ITerminalService).selectDefaultShell(); + await accessor.get(ITerminalService).selectDefaultProfile(); } }); registerAction2(class extends Action2 { @@ -1438,6 +1438,7 @@ export function registerTerminalActions() { async run(accessor: ServicesAccessor, item?: string) { const terminalService = accessor.get(ITerminalService); const terminalContributionService = accessor.get(ITerminalContributionService); + const notificationService = accessor.get(INotificationService); const commandService = accessor.get(ICommandService); if (!item || !item.split) { return Promise.resolve(null); @@ -1446,9 +1447,9 @@ export function registerTerminalActions() { terminalService.refreshActiveTab(); return Promise.resolve(null); } - if (item === selectDefaultShellTitle) { + if (item === selectDefaultProfileTitle) { terminalService.refreshActiveTab(); - return terminalService.selectDefaultShell(); + return terminalService.selectDefaultProfile(); } if (item === configureTerminalSettingsTitle) { await commandService.executeCommand(TERMINAL_COMMAND_ID.CONFIGURE_TERMINAL_SETTINGS); @@ -1459,13 +1460,43 @@ export function registerTerminalActions() { terminalService.setActiveTabByIndex(Number(indexMatches[1]) - 1); return terminalService.showPanel(true); } + const detectedProfiles = await terminalService.getAvailableProfiles(); - const customType = terminalContributionService.terminalTypes.find(t => t.title === item); - if (customType) { - return commandService.executeCommand(customType.command); + // Remove 'New ' from the selected item to get the profile name + const profileSelection = item.substring(4); + + if (detectedProfiles) { + let launchConfig = detectedProfiles?.find((profile: { profileName: string; }) => profile.profileName === profileSelection); + if (launchConfig && !launchConfig.isWorkspaceProfile) { + const instance = terminalService.createTerminal({ executable: launchConfig.path, name: launchConfig.profileName, args: launchConfig.args }); + terminalService.setActiveInstance(instance); + } else if (launchConfig && launchConfig.isWorkspaceProfile) { + notificationService.prompt(Severity.Info, `Do you allow this workspace to modify your terminal profile? ${item}`, + [{ + label: 'Allow', + run: () => { + const instance = terminalService.createTerminal({ executable: launchConfig?.path, name: launchConfig?.profileName, args: launchConfig?.args }); + terminalService.setActiveInstance(instance); + } + }, + { + label: 'Disallow', + run: () => { + const activeInstance = terminalService.getActiveInstance(); + if (activeInstance) { + terminalService.setActiveInstance(activeInstance); + } + } + }] + ); + } + } else { + const customType = terminalContributionService.terminalTypes.find(t => t.title === item); + if (customType) { + return commandService.executeCommand(customType.command); + } + console.warn(`Unmatched terminal item: "${item}"`); } - - console.warn(`Unmatched terminal item: "${item}"`); return Promise.resolve(); } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 8ae511a0893..f7d1510209d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -220,16 +220,18 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { const shellConfigValue = this._configurationService.inspect(`terminal.integrated.shell.${platformKey}`); const shellArgsConfigValue = this._configurationService.inspect(`terminal.integrated.shellArgs.${platformKey}`); const envConfigValue = this._configurationService.inspect<{ [key: string]: string }>(`terminal.integrated.env.${platformKey}`); + const profiles = this._configurationService.inspect<{ [key: string]: string }>(`terminal.integrated.profiles.${platformKey}`); // Check if workspace setting exists and whether it's allowed let isWorkspaceShellAllowed: boolean | undefined = false; - if (shellConfigValue.workspaceValue !== undefined || shellArgsConfigValue.workspaceValue !== undefined || envConfigValue.workspaceValue !== undefined) { + if (shellConfigValue.workspaceValue !== undefined || shellArgsConfigValue.workspaceValue !== undefined || envConfigValue.workspaceValue !== undefined || profiles.workspaceValue !== undefined) { isWorkspaceShellAllowed = this.isWorkspaceShellAllowed(undefined); } // Always allow [] args as it would lead to an odd error message and should not be dangerous if (shellConfigValue.workspaceValue === undefined && envConfigValue.workspaceValue === undefined && - shellArgsConfigValue.workspaceValue && shellArgsConfigValue.workspaceValue.length === 0) { + shellArgsConfigValue.workspaceValue && shellArgsConfigValue.workspaceValue.length === 0 + && profiles.workspaceValue === undefined) { isWorkspaceShellAllowed = true; } @@ -248,6 +250,10 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { if (envConfigValue.workspaceValue) { envString = `env: {${Object.keys(envConfigValue.workspaceValue).map(k => `${k}:${envConfigValue.workspaceValue![k]}`).join(', ')}}`; } + let profilesString: string | undefined; + if (profiles.workspaceValue) { + profilesString = `profiles: {${Object.keys(profiles.workspaceValue).map(k => `${k}:${profiles.workspaceValue![k]}`).join(', ')}}`; + } // Should not be localized as it's json-like syntax referencing settings keys const workspaceConfigStrings: string[] = []; if (shellString) { @@ -259,6 +265,9 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { if (envString) { workspaceConfigStrings.push(envString); } + if (profilesString) { + workspaceConfigStrings.push(profilesString); + } const workspaceConfigString = workspaceConfigStrings.join(', '); this._notificationService.prompt(Severity.Info, nls.localize('terminal.integrated.allowWorkspaceShell', "Do you allow this workspace to modify your terminal shell? {0}", workspaceConfigString), [{ diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index ec9401662fa..c1bf4d5dcd8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -48,6 +48,7 @@ import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IProcessDataEvent, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal'; +import { IProductService } from 'vs/platform/product/common/productService'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -202,7 +203,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { @ILogService private readonly _logService: ILogService, @IStorageService private readonly _storageService: IStorageService, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, - @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService + @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, + @IProductService private readonly _productService: IProductService ) { super(); @@ -576,7 +578,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { !TERMINAL_CREATION_COMMANDS.includes(resolveResult.commandId)) { this._notificationService.prompt( Severity.Info, - nls.localize('configure terminal settings', "Some keybindings are dispatched to the workbench by default."), + nls.localize('keybindingHandling', "Some keybindings don't go to the terminal by default and are handled by {0} instead.", this._productService.nameLong), [ { label: nls.localize('configureTerminalSettings', "Configure Terminal Settings"), @@ -1127,16 +1129,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } - @debounce(500) public reuseTerminal(shell: IShellLaunchConfig, reset: boolean = false): void { // Unsubscribe any key listener we may have. this._pressAnyKeyToCloseListener?.dispose(); this._pressAnyKeyToCloseListener = undefined; if (this._xterm) { - if (reset) { - this._xterm.reset(); - } else { + if (!reset) { // Ensure new processes' output starts at start of new line this._xterm.write('\n\x1b[G'); } @@ -1168,11 +1167,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Set the new shell launch config this._shellLaunchConfig = shell; // Must be done before calling _createProcess() - this._processManager.relaunch(this._shellLaunchConfig, this._cols, this._rows, this._accessibilityService.isScreenReaderOptimized()); + this._processManager.relaunch(this._shellLaunchConfig, this._cols, this._rows, this._accessibilityService.isScreenReaderOptimized(), reset); - this._xtermTypeAhead?.reset(this._processManager); + this._xtermTypeAhead?.reset(); } + @debounce(1000) public relaunch(): void { this.reuseTerminal(this._shellLaunchConfig, true); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 6921fc2911c..4a92c9d82ae 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -28,6 +28,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentVariableInfo, IEnvironmentVariableService, IMergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, TerminalShellType, ILocalTerminalService, IOffProcessTerminalService } from 'vs/platform/terminal/common/terminal'; +import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -72,6 +73,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce private _hasWrittenData: boolean = false; private _ptyResponsiveListener: IDisposable | undefined; private _ptyListenersAttached: boolean = false; + private _dataFilter: SeamlessRelaunchDataFilter; private readonly _onPtyDisconnect = this._register(new Emitter()); public get onPtyDisconnect(): Event { return this._onPtyDisconnect.event; } @@ -124,14 +126,19 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce super(); this._localTerminalService = localTerminalService; - this.ptyProcessReady = new Promise(c => { - this.onProcessReady(() => { - this._logService.debug(`Terminal process ready (shellProcessId: ${this.shellProcessId})`); - c(undefined); - }); - }); + this.ptyProcessReady = this._createPtyProcessReadyPromise(); this.getLatency(); this._ackDataBufferer = new AckDataBufferer(e => this._process?.acknowledgeDataEvent(e)); + this._dataFilter = this._instantiationService.createInstance(SeamlessRelaunchDataFilter); + this._dataFilter.onProcessData(ev => { + const data = (typeof ev === 'string' ? ev : ev.data); + const sync = (typeof ev === 'string' ? false : ev.sync); + const beforeProcessDataEvent: IBeforeProcessDataEvent = { data }; + this._onBeforeProcessData.fire(beforeProcessDataEvent); + if (beforeProcessDataEvent.data && beforeProcessDataEvent.data.length > 0) { + this._onProcessData.fire({ data: beforeProcessDataEvent.data, sync }); + } + }); } public dispose(immediate: boolean = false): void { @@ -147,6 +154,16 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce super.dispose(); } + private _createPtyProcessReadyPromise(): Promise { + return new Promise(c => { + const listener = this.onProcessReady(() => { + this._logService.debug(`Terminal process ready (shellProcessId: ${this.shellProcessId})`); + listener.dispose(); + c(undefined); + }); + }); + } + public detachFromProcess(): void { if (this._process?.detach) { this._process.detach(); @@ -157,7 +174,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, - isScreenReaderModeEnabled: boolean + isScreenReaderModeEnabled: boolean, + reset: boolean = true ): Promise { if (shellLaunchConfig.isExtensionCustomPtyTerminal) { this._processType = ProcessType.ExtensionTerminal; @@ -238,15 +256,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this.processState = ProcessState.LAUNCHING; - this._process.onProcessData(ev => { - const data = (typeof ev === 'string' ? ev : ev.data); - const sync = (typeof ev === 'string' ? false : ev.sync); - const beforeProcessDataEvent: IBeforeProcessDataEvent = { data }; - this._onBeforeProcessData.fire(beforeProcessDataEvent); - if (beforeProcessDataEvent.data && beforeProcessDataEvent.data.length > 0) { - this._onProcessData.fire({ data: beforeProcessDataEvent.data, sync }); - } - }); + this._dataFilter.newProcess(this._process, reset); this._process.onProcessReady((e: { pid: number, cwd: string }) => { this.shellProcessId = e.pid; @@ -285,14 +295,10 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce return undefined; } - public async relaunch(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, isScreenReaderModeEnabled: boolean): Promise { - this.ptyProcessReady = new Promise(c => { - this.onProcessReady(() => { - this._logService.debug(`Terminal process ready (shellProcessId: ${this.shellProcessId})`); - c(undefined); - }); - }); - return this.createProcess(shellLaunchConfig, cols, rows, isScreenReaderModeEnabled); + public async relaunch(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, isScreenReaderModeEnabled: boolean, reset: boolean): Promise { + this.ptyProcessReady = this._createPtyProcessReadyPromise(); + this._logService.trace(`Relaunching terminal instance ${this._instanceId}`); + return this.createProcess(shellLaunchConfig, cols, rows, isScreenReaderModeEnabled, reset); } // Fetch any extension environment additions and apply them @@ -431,6 +437,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce public async write(data: string): Promise { await this.ptyProcessReady; + this._dataFilter.triggerSwap(); this._hasWrittenData = true; if (this.shellProcessId || this._processType === ProcessType.ExtensionTerminal) { if (this._process) { @@ -516,3 +523,151 @@ class AckDataBufferer { } } } + +const enum SeamlessRelaunchConstants { + /** + * How long to record data events for new terminals. + */ + RecordTerminalDuration = 10000, + /** + * The maximum duration after a relaunch occurs to trigger a swap. + */ + SwapWaitMaximumDuration = 3000 +} + +/** + * Filters data events from the process and supports seamlessly restarting swapping out the process + * with another, delaying the swap in output in order to minimize flickering/clearing of the + * terminal. + */ +class SeamlessRelaunchDataFilter extends Disposable { + private _firstRecorder?: TerminalRecorder; + private _secondRecorder?: TerminalRecorder; + private _firstDisposable?: IDisposable; + private _secondDisposable?: IDisposable; + private _dataListener?: IDisposable; + private _activeProcess?: ITerminalChildProcess; + + private _recordingTimeout?: number; + private _swapTimeout?: number; + + private readonly _onProcessData = this._register(new Emitter()); + public get onProcessData(): Event { return this._onProcessData.event; } + + constructor( + @ILogService private readonly _logService: ILogService + ) { + super(); + } + + newProcess(process: ITerminalChildProcess, reset: boolean) { + // Stop listening to the old process + this._dataListener?.dispose(); + this._activeProcess = process; + + // If the process is new, relaunch has timed out or the terminal should not reset, start + // listening and firing data events immediately + if (!this._firstRecorder || !reset) { + this._firstDisposable?.dispose(); + [this._firstRecorder, this._firstDisposable] = this._createRecorder(process); + this._dataListener = process.onProcessData(e => this._onProcessData.fire(e)); + if (this._recordingTimeout) { + window.clearTimeout(this._recordingTimeout); + } + this._recordingTimeout = window.setTimeout(() => this._stopRecording(), SeamlessRelaunchConstants.RecordTerminalDuration); + return; + } + + // Trigger a swap if there was a recent relaunch + if (this._secondRecorder) { + this.triggerSwap(); + } + + this._swapTimeout = window.setTimeout(() => this.triggerSwap(), SeamlessRelaunchConstants.SwapWaitMaximumDuration); + + // Pause all outgoing data events + this._dataListener?.dispose(); + + this._firstDisposable?.dispose(); + const recorder = this._createRecorder(process); + [this._secondRecorder, this._secondDisposable] = recorder; + } + + /** + * Trigger the swap of the processes if needed (eg. timeout, input) + */ + triggerSwap() { + // Clear the swap timeout if it exists + if (this._swapTimeout) { + window.clearTimeout(this._swapTimeout); + this._swapTimeout = undefined; + } + + // Do nothing if there's nothing being recorder + if (!this._firstRecorder) { + return; + } + + // Clear the first recorder if no second process was attached before the swap trigger + if (!this._secondRecorder) { + this._firstRecorder = undefined; + this._firstDisposable?.dispose(); + return; + } + + // Generate data for each recorder + const firstData = this._getDataFromRecorder(this._firstRecorder); + const secondData = this._getDataFromRecorder(this._secondRecorder); + + // Re-write the terminal if the data differs + if (firstData === secondData) { + this._logService.trace(`Seamless terminal relaunch - identical content`); + } else { + this._logService.trace(`Seamless terminal relaunch - resetting content`); + // Fire full reset (RIS) followed by the new data so the update happens in the same frame + this._onProcessData.fire({ data: `\x1bc${secondData}`, sync: false }); + } + + // Set up the new data listener + this._dataListener?.dispose(); + this._dataListener = this._activeProcess!.onProcessData(e => this._onProcessData.fire(e)); + + // Replace first recorder with second + this._firstRecorder = this._secondRecorder; + this._firstDisposable?.dispose(); + this._firstDisposable = this._secondDisposable; + if (this._recordingTimeout) { + window.clearTimeout(this._recordingTimeout); + } + this._recordingTimeout = window.setTimeout(() => this._stopRecording(), SeamlessRelaunchConstants.RecordTerminalDuration); + } + + private _stopRecording() { + // Continue recording if a swap is coming + if (this._swapTimeout) { + return; + } + + // Clear the timeout + if (this._recordingTimeout) { + window.clearTimeout(this._recordingTimeout); + this._recordingTimeout = undefined; + } + + // Stop recording + this._firstRecorder = undefined; + this._firstDisposable?.dispose(); + this._secondRecorder = undefined; + this._secondDisposable?.dispose(); + } + + private _createRecorder(process: ITerminalChildProcess): [TerminalRecorder, IDisposable] { + const recorder = new TerminalRecorder(0, 0); + const disposable = process.onProcessData(e => recorder.recordData(typeof e === 'string' ? e : e.data)); + return [recorder, disposable]; + } + + private _getDataFromRecorder(recorder: TerminalRecorder): string { + return recorder.generateReplayEvent().events.filter(e => !!e.data).map(e => e.data).join(''); + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 09cd2b8843b..4060fc3b10d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { timeout } from 'vs/base/common/async'; -import { debounce } from 'vs/base/common/decorators'; +import { debounce, throttle } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { basename } from 'vs/base/common/path'; @@ -24,9 +24,10 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; -import { IAvailableShellsRequest, IRemoteTerminalAttachTarget, IShellDefinition, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, LinuxDistro, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IAvailableProfilesRequest, IRemoteTerminalAttachTarget, ITerminalProfile, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, LinuxDistro, TERMINAL_VIEW_ID, ITerminalProfileObject, ITerminalExecutable, ITerminalProfileSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ILifecycleService, ShutdownReason, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -68,6 +69,8 @@ export class TerminalService implements ITerminalService { private _localTerminalsInitPromise: Promise | undefined; private _connectionState: TerminalConnectionState; + private _availableProfiles: ITerminalProfile[] | undefined; + public get configHelper(): ITerminalConfigHelper { return this._configHelper; } private readonly _onActiveTabChanged = new Emitter(); @@ -94,14 +97,15 @@ export class TerminalService implements ITerminalService { public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } private readonly _onTabDisposed = new Emitter(); public get onTabDisposed(): Event { return this._onTabDisposed.event; } - private readonly _onRequestAvailableShells = new Emitter(); - public get onRequestAvailableShells(): Event { return this._onRequestAvailableShells.event; } + private readonly _onRequestAvailableProfiles = new Emitter(); + public get onRequestAvailableProfiles(): Event { return this._onRequestAvailableProfiles.event; } private readonly _onDidRegisterProcessSupport = new Emitter(); public get onDidRegisterProcessSupport(): Event { return this._onDidRegisterProcessSupport.event; } private readonly _onDidChangeConnectionState = new Emitter(); public get onDidChangeConnectionState(): Event { return this._onDidChangeConnectionState.event; } public get connectionState(): TerminalConnectionState { return this._connectionState; } - + private readonly _onProfilesConfigChanged = new Emitter(); + public get onProfilesConfigChanged(): Event { return this._onProfilesConfigChanged.event; } private readonly _localTerminalService?: ILocalTerminalService; constructor( @@ -118,6 +122,7 @@ export class TerminalService implements ITerminalService { @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @IRemoteTerminalService private readonly _remoteTerminalService: IRemoteTerminalService, @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IExtensionService private readonly _extensionService: IExtensionService, @optional(ILocalTerminalService) localTerminalService: ILocalTerminalService ) { this._localTerminalService = localTerminalService; @@ -142,16 +147,34 @@ export class TerminalService implements ITerminalService { this._processSupportContextKey = KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED.bindTo(this._contextKeyService); this._processSupportContextKey.set(!isWeb || this._remoteAgentService.getConnection() !== null); + this._configurationService.onDidChangeConfiguration(async e => { + if (e.affectsConfiguration('terminal.integrated.profiles.windows') || + e.affectsConfiguration('terminal.integrated.profiles.osx') || + e.affectsConfiguration('terminal.integrated.profiles.linux') || + e.affectsConfiguration('terminal.integrated.detectWslProfiles')) { + this._onProfilesConfigChanged.fire(); + } + }); + const enableTerminalReconnection = this.configHelper.config.enablePersistentSessions; + // Connect to the extension host if it's there, set the connection state to connected when + // it's done. This should happen even when there is no extension host. this._connectionState = TerminalConnectionState.Connecting; + let initPromise: Promise; if (!!this._environmentService.remoteAuthority && enableTerminalReconnection) { - this._remoteTerminalsInitPromise = this._reconnectToRemoteTerminals(); + initPromise = this._remoteTerminalsInitPromise = this._reconnectToRemoteTerminals(); } else if (enableTerminalReconnection) { - this._localTerminalsInitPromise = this._reconnectToLocalTerminals(); + initPromise = this._localTerminalsInitPromise = this._reconnectToLocalTerminals(); } else { - this._connectionState = TerminalConnectionState.Connected; + initPromise = Promise.resolve(); } + initPromise.then(() => this._setConnected()); + } + + private _setConnected() { + this._connectionState = TerminalConnectionState.Connected; + this._onDidChangeConnectionState.fire(); } private async _reconnectToRemoteTerminals(): Promise { @@ -167,11 +190,9 @@ export class TerminalService implements ITerminalService { count: reconnectCounter }; this._telemetryService.publicLog('terminalReconnection', data); - this._connectionState = TerminalConnectionState.Connected; // now that terminals have been restored, // attach listeners to update remote when terminals are changed this.attachProcessLayoutListeners(true); - this._onDidChangeConnectionState.fire(); } private async _reconnectToLocalTerminals(): Promise { @@ -186,8 +207,6 @@ export class TerminalService implements ITerminalService { // now that terminals have been restored, // attach listeners to update local state when terminals are changed this.attachProcessLayoutListeners(false); - this._connectionState = TerminalConnectionState.Connected; - this._onDidChangeConnectionState.fire(); } private _recreateTerminalTabs(layoutInfo?: ITerminalsLayoutInfo): number { @@ -271,6 +290,77 @@ export class TerminalService implements ITerminalService { this._extHostsReady[remoteAuthority]!.resolve(); } + public getAvailableProfiles(): ITerminalProfile[] { + this._updateAvailableProfiles(); + return this._availableProfiles?.map(s => ({ profileName: s.profileName, path: s.path, args: s.args, isWorkspaceProfile: this._getWorkspaceProfilePermissions(s) } as ITerminalProfile)) || []; + } + + private _getWorkspaceProfilePermissions(profile: ITerminalProfile): boolean { + if (isWindows) { + const profiles = this._configurationService.inspect<{ [key: string]: ITerminalProfileObject }>(`terminal.integrated.profiles.windows`); + if (profiles && profiles.workspaceValue && profiles.defaultValue) { + const workspaceProfile = Object.entries(profiles.workspaceValue).find(p => p[0] === profile.profileName); + const defaultProfile = Object.entries(profiles.defaultValue).find(p => p[0] === profile.profileName); + if (workspaceProfile && defaultProfile && workspaceProfile[0] === defaultProfile[0]) { + let result = !this._terminalProfileObjectEqual(workspaceProfile[1], defaultProfile[1]); + return result; + } else if (!workspaceProfile && !defaultProfile) { + // user profile + return false; + } else { + // this key is missing from either default or the workspace config + return true; + } + } + } + return false; + } + + private _terminalProfileObjectEqual(one?: ITerminalProfileObject, two?: ITerminalProfileObject): boolean { + if (one === null && two === null) { + return true; + } else if ((one as ITerminalExecutable).path && (two as ITerminalExecutable).path) { + const oneExec = (one as ITerminalExecutable); + const twoExec = (two as ITerminalExecutable); + return ((Array.isArray(oneExec.path) && Array.isArray(twoExec.path) && oneExec.path.length === twoExec.path.length && oneExec.path.every((p, index) => p === twoExec.path[index])) || + (oneExec.path === twoExec.path) + ) && ((Array.isArray(oneExec.args) && Array.isArray(twoExec.args) && oneExec.args?.every((a, index) => a === twoExec.args?.[index])) || + (oneExec.args === twoExec.args) + ); + } else if ((one as ITerminalProfileSource).source && (two as ITerminalProfileSource).source) { + const oneSource = (one as ITerminalProfileSource); + const twoSource = (two as ITerminalProfileSource); + return oneSource.source === twoSource.source; + } + return false; + } + + // avoid checking this very often, every ten seconds shoulds suffice + @throttle(10000) + private async _updateAvailableProfiles(): Promise { + const result = await this._detectProfiles(true); + if (result !== this._availableProfiles) { + this._availableProfiles = result; + this._onProfilesConfigChanged.fire(); + } + } + + private async _detectProfiles(quickLaunchOnly: boolean): Promise { + await this._extensionService.whenInstalledExtensionsRegistered(); + // Wait for the remoteAuthority to be ready (and listening for events) before firing + // the event to spawn the ext host process + const conn = this._remoteAgentService.getConnection(); + const remoteAuthority = conn ? conn.remoteAuthority : 'null'; + await this._whenExtHostReady(remoteAuthority); + return new Promise(r => this._onRequestAvailableProfiles.fire({ callback: r, quickLaunchOnly: quickLaunchOnly })); + } + + private async _whenExtHostReady(remoteAuthority: string): Promise { + this._createExtHostReadyEntry(remoteAuthority); + this._updateAvailableProfiles(); + return this._extHostsReady[remoteAuthority]!.promise; + } + private _createExtHostReadyEntry(remoteAuthority: string): void { if (this._extHostsReady[remoteAuthority]) { return; @@ -563,7 +653,7 @@ export class TerminalService implements ITerminalService { instance.addDisposable(instance.onLinksReady(this._onInstanceLinksReady.fire, this._onInstanceLinksReady)); instance.addDisposable(instance.onDimensionsChanged(() => { this._onInstanceDimensionsChanged.fire(instance); - if (this.configHelper.config.enablePersistentSessions) { + if (this.configHelper.config.enablePersistentSessions && this.isProcessSupportRegistered) { !!this._environmentService.remoteAuthority ? this._updateRemoteState() : this._updateLocalState(); } })); @@ -732,19 +822,19 @@ export class TerminalService implements ITerminalService { }); } - public async selectDefaultShell(): Promise { - const shells = await this._detectShells(); + public async selectDefaultProfile(): Promise { + const profiles = await this._detectProfiles(false); const options: IPickOptions = { placeHolder: nls.localize('terminal.integrated.chooseWindowsShell', "Select your preferred terminal shell, you can change this later in your settings") }; - const quickPickItems = shells.map((s): IQuickPickItem => { - return { label: s.label, description: s.path }; + const quickPickItems = profiles.map((p): IQuickPickItem => { + return { label: p.profileName, description: p.path }; }); const value = await this._quickInputService.pick(quickPickItems, options); if (!value) { return undefined; } - const shell = value.description; + const profile = value.description; const env = await this._remoteAgentService.getEnvironment(); let platformKey: string; if (env) { @@ -752,11 +842,7 @@ export class TerminalService implements ITerminalService { } else { platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux'); } - await this._configurationService.updateValue(`terminal.integrated.shell.${platformKey}`, shell, ConfigurationTarget.USER); - } - - private _detectShells(): Promise { - return new Promise(r => this._onRequestAvailableShells.fire({ callback: r })); + await this._configurationService.updateValue(`terminal.integrated.shell.${platformKey}`, profile, ConfigurationTarget.USER); } public createInstance(container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance { @@ -773,7 +859,7 @@ export class TerminalService implements ITerminalService { } public createTerminal(shell: IShellLaunchConfig = {}): ITerminalInstance { - if (!this.isProcessSupportRegistered) { + if (!shell.isExtensionCustomPtyTerminal && !this.isProcessSupportRegistered) { throw new Error('Could not create terminal when process support is not registered'); } if (shell.hideFromUser) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index 835915af1b9..e59ee62c274 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -1330,10 +1330,8 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { })); } - public reset(processManager: ITerminalProcessManager) { + public reset() { this.lastRow = undefined; - this.processManager = processManager; - this._register(this.processManager.onBeforeProcessData(e => this.onBeforeProcessData(e))); } private deferClearingPredictions() { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 152bffdb9ab..b752ef83738 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -13,7 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, IColorTheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; -import { configureTerminalSettingsTitle, selectDefaultShellTitle, switchTerminalActionViewItemSeparator } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { configureTerminalSettingsTitle, selectDefaultProfileTitle, switchTerminalActionViewItemSeparator } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { URI } from 'vs/base/common/uri'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; @@ -34,7 +34,6 @@ import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/comm import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; -import { equals } from 'vs/base/common/arrays'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -50,6 +49,7 @@ export class TerminalViewPane extends ViewPane { private _findWidget: TerminalFindWidget | undefined; private _terminalsInitialized = false; private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 }; + private _isWelcomeShowing: boolean = false; constructor( options: IViewPaneOptions, @@ -76,13 +76,21 @@ export class TerminalViewPane extends ViewPane { } this._onDidChangeViewWelcomeState.fire(); }); + this._terminalService.onInstanceCreated(() => { + if (!this._isWelcomeShowing) { + return; + } + this._isWelcomeShowing = true; + this._onDidChangeViewWelcomeState.fire(); + if (this._terminalContainer) { + this._terminalContainer.style.display = 'block'; + this.layoutBody(this._terminalContainer.offsetHeight, this._terminalContainer.offsetWidth); + } + }); } protected renderBody(container: HTMLElement): void { super.renderBody(container); - if (this.shouldShowWelcome()) { - return; - } this._parentDomElement = container; this._parentDomElement.classList.add('integrated-terminal'); @@ -90,6 +98,7 @@ export class TerminalViewPane extends ViewPane { this._terminalContainer = document.createElement('div'); this._terminalContainer.classList.add('terminal-outer-container'); + this._terminalContainer.style.display = this.shouldShowWelcome() ? 'none' : 'block'; this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); @@ -150,9 +159,6 @@ export class TerminalViewPane extends ViewPane { protected layoutBody(height: number, width: number): void { super.layoutBody(height, width); - if (this.shouldShowWelcome()) { - return; - } this._bodyDimensions.width = width; this._bodyDimensions.height = height; @@ -339,7 +345,8 @@ export class TerminalViewPane extends ViewPane { } shouldShowWelcome(): boolean { - return !this._terminalService.isProcessSupportRegistered; + this._isWelcomeShowing = !this._terminalService.isProcessSupportRegistered && this._terminalService.terminalInstances.length === 0; + return this._isWelcomeShowing; } } @@ -358,22 +365,21 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = class SwitchTerminalActionViewItem extends SelectActionViewItem { - private _lastOptions: ISelectOptionItem[] = []; - private _lastActiveTab: number = 0; constructor( action: IAction, @ITerminalService private readonly _terminalService: ITerminalService, @IThemeService private readonly _themeService: IThemeService, @ITerminalContributionService private readonly _contributions: ITerminalContributionService, - @IContextViewService contextViewService: IContextViewService, + @IContextViewService contextViewService: IContextViewService ) { super(null, action, getTerminalSelectOpenItems(_terminalService, _contributions), _terminalService.activeTabIndex, contextViewService, { ariaLabel: nls.localize('terminals', 'Open Terminals.'), optionsAsChildren: true }); - - this._register(_terminalService.onInstancesChanged(this._updateItems, this)); - this._register(_terminalService.onActiveTabChanged(this._updateItems, this)); - this._register(_terminalService.onInstanceTitleChanged(this._updateItems, this)); - this._register(_terminalService.onTabDisposed(this._updateItems, this)); - this._register(_terminalService.onDidChangeConnectionState(this._updateItems, this)); + this._register(_terminalService.onInstancesChanged(() => this._updateItems(), this)); + this._register(_terminalService.onActiveTabChanged(() => this._updateItems(), this)); + this._register(_terminalService.onInstanceTitleChanged(() => this._updateItems(), this)); + this._register(_terminalService.onTabDisposed(() => this._updateItems(), this)); + this._register(_terminalService.onDidChangeConnectionState(() => this._updateItems(), this)); + this._register(_terminalService.onProfilesConfigChanged(() => this._updateItems(), this)); + this._register(_terminalService.onRequestAvailableProfiles(() => this._updateItems(), this)); this._register(attachSelectBoxStyler(this.selectBox, this._themeService)); } @@ -387,18 +393,12 @@ class SwitchTerminalActionViewItem extends SelectActionViewItem { private _updateItems(): void { const options = getTerminalSelectOpenItems(this._terminalService, this._contributions); - // only update options if they've changed - if (!equals(Object.values(options), Object.values(this._lastOptions)) || this._lastActiveTab !== this._terminalService.activeTabIndex) { - this.setOptions(options, this._terminalService.activeTabIndex); - this._lastOptions = options; - this._lastActiveTab = this._terminalService.activeTabIndex; - } + this.setOptions(options, this._terminalService.activeTabIndex); } } function getTerminalSelectOpenItems(terminalService: ITerminalService, contributions: ITerminalContributionService): ISelectOptionItem[] { let items: ISelectOptionItem[]; - if (terminalService.connectionState === TerminalConnectionState.Connected) { items = terminalService.getTabLabels().map(label => { return { text: label }; @@ -409,15 +409,18 @@ function getTerminalSelectOpenItems(terminalService: ITerminalService, contribut items.push({ text: switchTerminalActionViewItemSeparator, isDisabled: true }); + items.push(...getProfileSelectOptionItems(terminalService)); + for (const contributed of contributions.terminalTypes) { items.push({ text: contributed.title }); } - - // Only show the select default shell command if process support is registered (ie. not web) - if (terminalService.isProcessSupportRegistered) { - items.push({ text: selectDefaultShellTitle }); - } - + items.push({ text: switchTerminalActionViewItemSeparator, isDisabled: true }); + items.push({ text: selectDefaultProfileTitle }); items.push({ text: configureTerminalSettingsTitle }); return items; } + +function getProfileSelectOptionItems(terminalService: ITerminalService): ISelectOptionItem[] { + const detectedProfiles = terminalService.getAvailableProfiles(); + return detectedProfiles?.map((shell: { profileName: string; }) => ({ text: 'New ' + shell.profileName } as ISelectOptionItem)) || []; +} diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 62094770b0e..ba98b4a40d0 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -95,6 +95,12 @@ export interface ITerminalConfiguration { osx: string[]; windows: string[]; }; + profiles: { + linux: Map; + osx: Map; + windows: Map; + }; + detectWslProfiles: boolean; altClickMovesCursor: boolean; macOptionIsMeta: boolean; macOptionClickForcesSelection: boolean; @@ -217,16 +223,35 @@ export interface IBeforeProcessDataEvent { data: string; } -export interface IShellDefinition { - label: string; +export interface ITerminalProfile { + profileName: string; path: string; + isWorkspaceProfile?: boolean; + args?: string | string[] | undefined; } -export interface IAvailableShellsRequest { - callback: (shells: IShellDefinition[]) => void; +export const enum ProfileSource { + 'Git Bash', + 'PowerShell' } +export interface ITerminalExecutable { + path: string | string[]; + args?: string | string[] | undefined; +} +export interface ITerminalProfileSource { + source: ProfileSource; +} + +export type ProfileName = string; + +export type ITerminalProfileObject = ITerminalExecutable | ITerminalProfileSource | null; + +export interface IAvailableProfilesRequest { + callback: (shells: ITerminalProfile[]) => void; + quickLaunchOnly: boolean; +} export interface IDefaultShellAndArgsRequest { useAutomationShell: boolean; callback: (shell: string, args: string[] | string | undefined) => void; @@ -262,7 +287,7 @@ export interface ITerminalProcessManager extends IDisposable { dispose(immediate?: boolean): void; detachFromProcess(): void; createProcess(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, isScreenReaderModeEnabled: boolean): Promise; - relaunch(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, isScreenReaderModeEnabled: boolean): Promise; + relaunch(shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, isScreenReaderModeEnabled: boolean, reset: boolean): Promise; write(data: string): void; setDimensions(cols: number, rows: number): Promise; setDimensions(cols: number, rows: number, sync: false): Promise; @@ -322,10 +347,6 @@ export interface IStartExtensionTerminalRequest { callback: (error: ITerminalLaunchError | undefined) => void; } -export interface IAvailableShellsRequest { - callback: (shells: IShellDefinition[]) => void; -} - export interface IDefaultShellAndArgsRequest { useAutomationShell: boolean; callback: (shell: string, args: string[] | string | undefined) => void; @@ -377,7 +398,7 @@ export const enum TERMINAL_COMMAND_ID { FOCUS_NEXT = 'workbench.action.terminal.focusNext', FOCUS_PREVIOUS = 'workbench.action.terminal.focusPrevious', PASTE = 'workbench.action.terminal.paste', - SELECT_DEFAULT_SHELL = 'workbench.action.terminal.selectDefaultShell', + SELECT_DEFAULT_PROFILE = 'workbench.action.terminal.selectDefaultShell', RUN_SELECTED_TEXT = 'workbench.action.terminal.runSelectedText', RUN_ACTIVE_FILE = 'workbench.action.terminal.runActiveFile', SWITCH_TERMINAL = 'workbench.action.terminal.switchTerminal', diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index b4ddfa8a6d4..30678944711 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -80,6 +80,62 @@ export const terminalConfiguration: IConfigurationNode = { ], default: [] }, + 'terminal.integrated.profiles.windows': { + markdownDescription: localize({ + key: 'terminal.integrated.profiles.windows', + comment: ['{0}, {1}, {2}, and {3} are the `source`, `path`, `profileName`, and optional `args` settings keys'] + }, + "The windows shell profiles to select from when creating a new terminal via the terminal dropdown. Set to null to exclude them, use the {0} property to use the default detected configuration. Or, set the {1}, {2}, and optional {3}", '`source`', '`path`', '`profileName`', '`args`.'), + type: 'object', + default: { + 'PowerShell': { + source: 'PowerShell' + }, + 'Git Bash': { + source: 'Git Bash' + }, + 'Command Prompt': { + path: + [ + '${env:windir}\\Sysnative\\cmd.exe', + '${env:windir}\\System32\\cmd.exe' + ], + args: [] + }, + 'Windows PowerShell': { + comment: 'note that this will not be included in the quickSelect drop down if another version of powershell is installed', + path: + [ + '${env:windir}\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.exe', + '${env:windir}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' + ], + args: [] + }, + 'WSL': null, + 'Cygwin': null + }, + }, + 'terminal.integrated.profiles.osx': { + markdownDescription: localize({ + key: 'terminal.integrated.profile.osx', + comment: ['{0}, {1}, and {2} are the `path`, `profileName`, and optional `args` settings keys'] + }, + "The osx shell profiles to select from when creating a new terminal via the terminal dropdown. When set, these will override the default detected profiles. They are comprised of a {0}, {1}, and optional {2}", '`path`', '`profileName`', '`args`.'), + type: 'object' + }, + 'terminal.integrated.profiles.linux': { + markdownDescription: localize({ + key: 'terminal.integrated.profile.linux', + comment: ['{0}, {1}, and {2} are the `path`, `profileName`, and optional `args` settings keys'] + }, + "The linux shell profiles to select from when creating a new terminal via the terminal dropdown. When set, these will override the default detected profiles. They are comprised of a {0}, {1}, and optional {2}", '`path`', '`profileName`', '`args`.'), + type: 'object' + }, + 'terminal.integrated.detectWslProfiles': { + description: localize('terminal.integrated.detectWslProfiles', 'Controls whether or not WSL distros are detected as default profiles'), + type: 'boolean', + default: true + }, 'terminal.integrated.macOptionIsMeta': { description: localize('terminal.integrated.macOptionIsMeta', "Controls whether to treat the option key as the meta key in the terminal on macOS."), type: 'boolean', diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts index 9e67808966f..18f764df2bc 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeContribution.ts @@ -7,7 +7,6 @@ import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { INativeOpenFileRequest } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; -import { linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { execFile } from 'child_process'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -18,6 +17,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment'; +import { linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; export class TerminalNativeContribution extends Disposable implements IWorkbenchContribution { public _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index 2a135cb5230..51a10b87cf7 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -6,11 +6,7 @@ import * as fs from 'fs'; import * as platform from 'vs/base/common/platform'; import { SymlinkSupport } from 'vs/base/node/pfs'; -import { coalesce } from 'vs/base/common/arrays'; -import { normalize, basename } from 'vs/base/common/path'; -import { enumeratePowerShellInstallations } from 'vs/base/node/powershell'; -import { getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment'; -import { IShellDefinition, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; +import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; let detectedDistro = LinuxDistro.Unknown; if (platform.isLinux) { @@ -30,96 +26,3 @@ if (platform.isLinux) { } export const linuxDistro = detectedDistro; - -export function detectAvailableShells(): Promise { - return platform.isWindows ? detectAvailableWindowsShells() : detectAvailableUnixShells(); -} - -async function detectAvailableWindowsShells(): Promise { - // Determine the correct System32 path. We want to point to Sysnative - // when the 32-bit version of VS Code is running on a 64-bit machine. - // The reason for this is because PowerShell's important PSReadline - // module doesn't work if this is not the case. See #27915. - const is32ProcessOn64Windows = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'); - const system32Path = `${process.env['windir']}\\${is32ProcessOn64Windows ? 'Sysnative' : 'System32'}`; - - let useWSLexe = false; - - if (getWindowsBuildNumber() >= 16299) { - useWSLexe = true; - } - - const expectedLocations: { [key: string]: string[] } = { - 'Command Prompt': [`${system32Path}\\cmd.exe`], - 'WSL Bash': [`${system32Path}\\${useWSLexe ? 'wsl.exe' : 'bash.exe'}`], - 'Git Bash': [ - `${process.env['ProgramW6432']}\\Git\\bin\\bash.exe`, - `${process.env['ProgramW6432']}\\Git\\usr\\bin\\bash.exe`, - `${process.env['ProgramFiles']}\\Git\\bin\\bash.exe`, - `${process.env['ProgramFiles']}\\Git\\usr\\bin\\bash.exe`, - `${process.env['LocalAppData']}\\Programs\\Git\\bin\\bash.exe`, - ], - // See #75945 - // Cygwin: [ - // `${process.env['HOMEDRIVE']}\\cygwin64\\bin\\bash.exe`, - // `${process.env['HOMEDRIVE']}\\cygwin\\bin\\bash.exe` - // ] - }; - - // Add all of the different kinds of PowerShells - for await (const pwshExe of enumeratePowerShellInstallations()) { - expectedLocations[pwshExe.displayName] = [pwshExe.exePath]; - } - - const promises: Promise[] = []; - Object.keys(expectedLocations).forEach(key => promises.push(validateShellPaths(key, expectedLocations[key]))); - const shells = await Promise.all(promises); - return coalesce(shells); -} - -async function detectAvailableUnixShells(): Promise { - const contents = await fs.promises.readFile('/etc/shells', 'utf8'); - const shells = contents.split('\n').filter(e => e.trim().indexOf('#') !== 0 && e.trim().length > 0); - return shells.map(e => { - return { - label: basename(e), - path: e - }; - }); -} - -async function validateShellPaths(label: string, potentialPaths: string[]): Promise { - if (potentialPaths.length === 0) { - return Promise.resolve(undefined); - } - const current = potentialPaths.shift()!; - if (current! === '') { - return validateShellPaths(label, potentialPaths); - } - try { - const result = await fs.promises.stat(normalize(current)); - if (result.isFile() || result.isSymbolicLink()) { - return { - label, - path: current - }; - } - } catch (e) { - // Also try using lstat as some symbolic links on Windows - // throw 'permission denied' using 'stat' but don't throw - // using 'lstat' - try { - const result = await fs.promises.lstat(normalize(current)); - if (result.isFile() || result.isSymbolicLink()) { - return { - label, - path: current - }; - } - } - catch (e) { - // noop - } - } - return validateShellPaths(label, potentialPaths); -} diff --git a/src/vs/workbench/contrib/terminal/node/terminalProfiles.ts b/src/vs/workbench/contrib/terminal/node/terminalProfiles.ts new file mode 100644 index 00000000000..b8226f79f46 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/node/terminalProfiles.ts @@ -0,0 +1,279 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import * as platform from 'vs/base/common/platform'; +import { coalesce } from 'vs/base/common/arrays'; +import { normalize, basename } from 'vs/base/common/path'; +import { enumeratePowerShellInstallations } from 'vs/base/node/powershell'; +import { getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment'; +import { ITerminalConfiguration, ITerminalExecutable, ITerminalProfile, ITerminalProfileSource } from 'vs/workbench/contrib/terminal/common/terminal'; +import * as cp from 'child_process'; +import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { ITestTerminalConfig } from 'vs/workbench/contrib/terminal/test/node/terminalProfiles.test'; +import { ILogService } from 'vs/platform/log/common/log'; + +export interface IStatProvider { + stat(path: string): boolean, + lstat(path: string): boolean +} + +interface IPotentialTerminalProfile { + profileName: string, + paths: string[], + args?: string[] +} + +export function detectAvailableProfiles(quickLaunchOnly: boolean, logService?: ILogService, config?: ITerminalConfiguration | ITestTerminalConfig, variableResolver?: ExtHostVariableResolverService, workspaceFolder?: IWorkspaceFolder, statProvider?: IStatProvider): Promise { + return platform.isWindows ? detectAvailableWindowsProfiles(quickLaunchOnly, logService, config?.detectWslProfiles, config?.profiles?.windows, variableResolver, workspaceFolder, statProvider) : detectAvailableUnixProfiles(); +} + +async function detectAvailableWindowsProfiles(quickLaunchOnly: boolean, logService?: ILogService, detectWslProfiles?: boolean, configProfiles?: any, variableResolver?: ExtHostVariableResolverService, workspaceFolder?: IWorkspaceFolder, statProvider?: IStatProvider): Promise { + // Determine the correct System32 path. We want to point to Sysnative + // when the 32-bit version of VS Code is running on a 64-bit machine. + // The reason for this is because PowerShell's important PSReadline + // module doesn't work if this is not the case. See #27915. + const is32ProcessOn64Windows = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'); + const system32Path = `${process.env['windir']}\\${is32ProcessOn64Windows ? 'Sysnative' : 'System32'}`; + + let useWSLexe = false; + + if (getWindowsBuildNumber() >= 16299) { + useWSLexe = true; + } + + let expectedProfiles: IPotentialTerminalProfile[] = [ + { + profileName: 'Command Prompt', + paths: [`${system32Path}\\cmd.exe`] + }, + { + profileName: 'Git Bash', + paths: [ + `${process.env['ProgramW6432']}\\Git\\bin\\bash.exe`, + `${process.env['ProgramW6432']}\\Git\\usr\\bin\\bash.exe`, + `${process.env['ProgramFiles']}\\Git\\bin\\bash.exe`, + `${process.env['ProgramFiles']}\\Git\\usr\\bin\\bash.exe`, + `${process.env['LocalAppData']}\\Programs\\Git\\bin\\bash.exe`, + ], + args: ['--login'] + }, + { + profileName: 'Cygwin', + paths: [ + `${process.env['HOMEDRIVE']}\\cygwin64\\bin\\bash.exe`, + `${process.env['HOMEDRIVE']}\\cygwin\\bin\\bash.exe` + ], + args: ['-l'] + }, + ... await getWslProfiles(`${system32Path}\\${useWSLexe ? 'wsl.exe' : 'bash.exe'}`, detectWslProfiles, logService), + ... await getPowershellProfiles() + ]; + + const promises: Promise[] = []; + expectedProfiles.forEach(profile => promises.push(validateProfilePaths(profile.profileName, profile.paths, statProvider, profile.args))); + const profiles = await Promise.all(promises); + + let detectedProfiles = coalesce(profiles); + + if (!quickLaunchOnly) { + return detectedProfiles; + } + let validProfiles: ITerminalProfile[] = []; + + if (detectedProfiles && configProfiles) { + for (const [profileKey, value] of Object.entries(configProfiles)) { + if (value !== null) { + if ((value as ITerminalExecutable).path) { + let profile; + const customProfile = (value as ITerminalExecutable); + if (Array.isArray(customProfile.path)) { + let resolvedPaths: string[] = []; + for (const p of customProfile.path) { + const resolved = variableResolver?.resolve(workspaceFolder, p); + if (resolved) { + resolvedPaths.push(resolved); + } else if (statProvider) { + // used by tests + resolvedPaths.push(p); + } else { + logService?.trace(`Could not resolve path ${p} in workspace folder ${workspaceFolder}`); + } + } + profile = detectedProfiles?.find(profile => resolvedPaths.includes(profile.path)); + if (!profile) { + logService?.trace(`Could not detect path ${JSON.stringify(resolvedPaths)}`); + } + } else { + let resolved = variableResolver?.resolve(workspaceFolder, customProfile.path); + if (resolved) { + profile = detectedProfiles?.find(profile => profile.path === resolved); + } else if (statProvider) { + // used by tests + resolved = customProfile.path; + } else { + logService?.trace(`Could not resolve path ${customProfile.path} in workspace folder ${workspaceFolder}`); + } + if (!profile) { + logService?.trace(`Could not detect path ${resolved}`); + } + } + if (profile) { + if (customProfile.args) { + validProfiles?.push({ profileName: profileKey, path: profile.path, args: customProfile.args }); + } else { + validProfiles?.push({ profileName: profileKey, path: profile.path }); + } + } + } else if ((value as ITerminalProfileSource).source) { + // source + let sourceKey = (value as ITerminalProfileSource).source; + const profile = detectedProfiles?.find(profile => profile.profileName === sourceKey.toString()); + if (profile) { + validProfiles?.push({ profileName: profileKey, path: profile.path, args: profile.args }); + } else { + logService?.trace(`No source with key ${sourceKey}`); + } + } else { + logService?.trace(`Entry in terminal.profiles.windows is not of type ITerminalExecutable or Source`, profileKey, value); + } + } + } + } else { + logService?.trace(`No detected profiles ${JSON.stringify(detectedProfiles)} or ${JSON.stringify(configProfiles)}`); + } + + // only show the windows powershell profile if no other powershell profile exists + if (validProfiles.find(p => p.path.endsWith('pwsh.exe'))) { + validProfiles = validProfiles.filter(p => p.profileName !== 'Windows PowerShell'); + } + return validProfiles; +} + +async function getPowershellProfiles(): Promise { + let profiles: IPotentialTerminalProfile[] = []; + // Add all of the different kinds of PowerShells + for await (const pwshExe of enumeratePowerShellInstallations()) { + profiles.push({ profileName: pwshExe.displayName, paths: [pwshExe.exePath] }); + } + return profiles; +} + +async function getWslProfiles(wslPath: string, detectWslProfiles?: boolean, logService?: ILogService): Promise { + let profiles: IPotentialTerminalProfile[] = []; + if (detectWslProfiles) { + const distroOutput = await new Promise(r => cp.exec('wsl.exe -l', (err, stdout) => err ? logService?.trace('problem occurred when getting wsl distros', err) : r(stdout))); + if (distroOutput) { + let regex = new RegExp(/[\r?\n]/); + let distroNames = Buffer.from(distroOutput).toString('utf8').split(regex).filter(t => t.trim().length > 0 && t !== ''); + // don't need the Windows Subsystem for Linux Distributions header + distroNames.shift(); + for (const distroName of distroNames) { + let s = ''; + let counter = 0; + for (const c of Array.from(distroName)) { + if (counter % 2 === 1) { + // every other character is junk / a rectangle + s += c; + } + counter++; + } + if (s.endsWith('(Default)')) { + // Ubuntu (Default) -> Ubuntu bc (Default) won't work + s = s.substring(0, s.length - 10); + } + + // docker-desktop-data is used by docker-desktop to store container images and isn't a valid profile type + if (s !== '' && s !== 'docker-desktop-data') { + let profile = { profileName: `${s} (WSL)`, paths: [wslPath], args: [`-d`, `${s}`] }; + profiles.push(profile); + } + } + return profiles; + } + } + return []; +} + +async function detectAvailableUnixProfiles(): Promise { + const contents = await fs.promises.readFile('/etc/shells', 'utf8'); + const profiles = contents.split('\n').filter(e => e.trim().indexOf('#') !== 0 && e.trim().length > 0); + return profiles.map(e => { + return { + profileName: basename(e), + path: e + }; + }); +} + +async function validateProfilePaths(label: string, potentialPaths: string[], statProvider?: IStatProvider, args?: string[]): Promise { + if (potentialPaths.length === 0) { + return Promise.resolve(undefined); + } + const current = potentialPaths.shift()!; + if (current! === '') { + return validateProfilePaths(label, potentialPaths, statProvider, args); + } + if (statProvider) { + if (statProvider.stat(current)) { + if (args) { + return { + profileName: label, + path: current, + args: args + }; + } else { + return { + profileName: label, + path: current + }; + } + } + } else { + try { + const result = await fs.promises.stat(normalize(current)); + if (result.isFile() || result.isSymbolicLink()) { + if (args) { + return { + profileName: label, + path: current, + args: args + }; + } else { + return { + profileName: label, + path: current + }; + } + } + } catch (e) { + // Also try using lstat as some symbolic links on Windows + // throw 'permission denied' using 'stat' but don't throw + // using 'lstat' + try { + const result = await fs.promises.lstat(normalize(current)); + if (result.isFile() || result.isSymbolicLink()) { + if (args) { + return { + profileName: label, + path: current, + args: args + }; + } else { + return { + profileName: label, + path: current + }; + } + } + } + catch (e) { + // noop + } + } + } + return validateProfilePaths(label, potentialPaths, statProvider, args); +} diff --git a/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts b/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts new file mode 100644 index 00000000000..de60306a5e2 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { isWindows } from 'vs/base/common/platform'; +import { detectAvailableProfiles, IStatProvider } from 'vs/workbench/contrib/terminal/node/terminalProfiles'; + +export interface ITestTerminalConfig { + profiles: { + windows: { + [key: string]: { path?: string[], source?: string } + } + }, + detectWslProfiles: boolean +} + +suite('Workbench - TerminalProfiles', () => { + suite('detectAvailableProfiles', () => { + if (isWindows) { + suite('detectAvailableWindowsProfiles', async () => { + // test('should detect cmd prompt', async () => { + // const _paths = ['C:\\WINDOWS\\System32\\cmd.exe']; + // let config: ITestTerminalConfig = { + // profiles: { + // windows: { + // 'Command Prompt': { path: _paths } + // } + // }, + // detectWslProfiles: false + // }; + // const profiles = await detectAvailableProfiles(true, undefined, config, undefined, undefined, createStatProvider(_paths)); + // const expected = [{ profileName: 'Command Prompt', path: _paths[0] }]; + // assert.deepStrictEqual(expected, profiles); + // }); + test('should detect Git Bash and provide login args', async () => { + const _paths = [`C:\\Program Files\\Git\\bin\\bash.exe`]; + let config = { + profiles: { + windows: { + 'Git Bash': { + source: 'Git Bash' + }, + }, + }, + detectWslProfiles: false + }; + const profiles = await detectAvailableProfiles(true, undefined, config, undefined, undefined, createStatProvider(_paths)); + const expected = [{ profileName: 'Git Bash', path: _paths[0], args: ['--login'] }]; + assert.deepStrictEqual(profiles, expected); + }); + }); + } + }); +}); +function createStatProvider(expectedPaths: string[]): IStatProvider { + const provider = { + stat(path: string) { + return expectedPaths.includes(path); + }, + lstat(path: string) { + return expectedPaths.includes(path); + } + }; + return provider; +} diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts index 080e44e0176..3c5c4557e39 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts @@ -10,6 +10,7 @@ import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -17,6 +18,7 @@ import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } fro import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { IGettingStartedService } from 'vs/workbench/services/gettingStarted/common/gettingStartedService'; export * as icons from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedIcons'; @@ -131,6 +133,38 @@ registerAction2(class extends Action2 { } }); +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'gettingStarted.markTaskComplete', + title: localize('gettingStarted.markTaskComplete', "Mark Task Complete"), + category, + }); + } + + run(accessor: ServicesAccessor, arg: string) { + if (!arg) { return; } + const gettingStartedService = accessor.get(IGettingStartedService); + gettingStartedService.progressTask(arg); + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'gettingStarted.markTaskIncomplete', + title: localize('gettingStarted.markTaskInomplete', "Mark Task Incomplete"), + category, + }); + } + + run(accessor: ServicesAccessor, arg: string) { + if (!arg) { return; } + const gettingStartedService = accessor.get(IGettingStartedService); + gettingStartedService.deprogressTask(arg); + } +}); + Registry.as(ConfigurationExtensions.Configuration) .registerConfiguration({ @@ -145,3 +179,132 @@ Registry.as(ConfigurationExtensions.Configuration) }, } }); + +ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'walkthroughs', + jsonSchema: { + doNotSuggest: true, + description: localize('walkthroughs', "Contribute collections of tasks to help users with your extension. Experimental, available in VS Code Insiders only."), + type: 'array', + items: { + type: 'object', + required: ['id', 'title', 'description', 'tasks'], + defaultSnippets: [{ body: { 'id': '$1', 'title': '$2', 'description': '$3', 'tasks': [] } }], + properties: { + id: { + type: 'string', + description: localize('walkthroughs.id', "Unique identifier for this walkthrough."), + }, + title: { + type: 'string', + description: localize('walkthroughs.title', "Title of walkthrough.") + }, + description: { + type: 'string', + description: localize('walkthroughs.description', "Description of walkthrough.") + }, + when: { + type: 'string', + description: localize('walkthroughs.when', "Context key expression to control the visibility of this walkthrough.") + }, + tasks: { + type: 'array', + description: localize('walkthroughs.tasks', "Tasks to complete as part of this walkthrough."), + items: { + type: 'object', + required: ['id', 'title', 'description', 'button', 'media'], + defaultSnippets: [{ + body: { + 'id': '$1', 'title': '$2', 'description': '$3', + 'button': { 'title': '$4', 'command': '$5' }, + 'doneOn': { 'command': '$5' }, + 'media': { 'path': '$6', 'altText': '$7' } + } + }], + properties: { + id: { + type: 'string', + description: localize('walkthroughs.tasks.id', "Unique identifier for this task. This is used to keep track of which tasks have been completed."), + }, + title: { + type: 'string', + description: localize('walkthroughs.tasks.title', "Title of task.") + }, + description: { + type: 'string', + description: localize('walkthroughs.tasks.description', "Description of task.") + }, + button: { + description: localize('walkthroughs.tasks.button', "The task's button, which can either link to an external resource or run a command"), + oneOf: [ + { + type: 'object', + required: ['title', 'command'], + defaultSnippets: [{ 'body': { 'title': '$1', 'command': '$2' } }], + properties: { + title: { + type: 'string', + description: localize('walkthroughs.tasks.button.title', "Title of button.") + }, + command: { + type: 'string', + description: localize('walkthroughs.tasks.button.command', "Command to run when button is clicked.") + } + } + }, + { + type: 'object', + required: ['title', 'link'], + defaultSnippets: [{ 'body': { 'title': '$1', 'link': '$2' } }], + properties: { + title: { + type: 'string', + description: localize('walkthroughs.tasks.button.title', "Title of button.") + }, + link: { + type: 'string', + description: localize('walkthroughs.tasks.button.link', "Link to open when button is clicked. Opening this link will mark the task completed.") + } + } + } + ] + }, + media: { + type: 'object', + required: ['path', 'altText'], + description: localize('walkthroughs.tasks.media', "Image to show alongside this task."), + defaultSnippets: [{ 'body': { 'altText': '$1', 'path': '$2' } }], + properties: { + path: { + description: localize('walkthroughs.tasks.media.path', "Path to an image, relative to extension directory."), + type: 'string', + }, + altText: { + type: 'string', + description: localize('walkthroughs.tasks.media.altText', "Alternate text to display when the image cannot be loaded or in screen readers.") + } + } + }, + doneOn: { + description: localize('walkthroughs.tasks.doneOn', "Signal to mark task as complete."), + type: 'object', + required: ['command'], + defaultSnippets: [{ 'body': { command: '$1' } }], + properties: { + 'command': { + description: localize('walkthroughs.tasks.oneOn.command', "Mark task done when the specified command is executed."), + type: 'string' + } + }, + }, + when: { + type: 'string', + description: localize('walkthroughs.tasks.when', "Context key expression to control the visibility of this task.") + } + } + } + } + } + } + } +}); diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css index 79534fc5d53..698b148e147 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.css @@ -85,21 +85,31 @@ .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories { display: flex; flex-direction: column; + align-items: center; + justify-content: center; overflow: hidden; padding: 20px; } +.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .categories-slide-container { + width: 90%; + max-width: 1200px; +} + .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .gap { flex: 150px 0 1000 } .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .header { display: flex; - align-items: center; justify-content: center; flex-direction: column; } +.monaco-workbench .part.editor > .content .gettingStartedContainer.height-constrained .gettingStartedSlide.categories .header { + display: none; +} + .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .category-title { margin-bottom: 4px; font-weight: 500; @@ -112,13 +122,12 @@ .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .categories-split-view { display: flex; - justify-content: space-evenly; } .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .categories-split-view .categories-left, .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .categories-split-view .categories-right { - flex: 1; - max-width: 400px; + flex: 1 1 0; + overflow: hidden; } .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .categories-split-view ul { @@ -130,6 +139,9 @@ .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .categories-split-view li { list-style: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.categories .categories-split-view .path { @@ -252,6 +264,7 @@ .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task { display: flex; width: 100%; + margin: 4px 0; overflow: hidden; transition: height .1s linear; } @@ -288,6 +301,15 @@ align-items: center; } +.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .shortcut-message { + opacity: 0.8; + font-size: 8pt; +} + +.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .shortcut-message .keybinding { + font-weight: bold; +} + .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail .getting-started-task .task-next { margin-left: auto; margin-right: 10px; @@ -392,14 +414,6 @@ text-align: center; } -/* .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide h1 { - font-size: 32px; - font-weight: normal; - border-bottom: none; - margin: 0; - padding: 0; -} */ - .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide.detail h2 { font-weight: normal; line-height: 26px; @@ -433,6 +447,9 @@ background: transparent; margin: 2px; cursor: pointer; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; } .monaco-workbench .part.editor > .content .gettingStartedContainer .button-link:hover { @@ -442,3 +459,9 @@ .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide .showOnStartup { text-align: center; } + +.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide .showOnStartup label { + position: relative; + top: -2px; + left: 5px; +} diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts index 55da1273653..c49b38f242c 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts @@ -9,7 +9,7 @@ import { IInstantiationService, optional } from 'vs/platform/instantiation/commo import { EditorInput, EditorOptions, IEditorInputFactory, IEditorOpenContext } from 'vs/workbench/common/editor'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { assertIsDefined } from 'vs/base/common/types'; -import { $, addDisposableListener, reset } from 'vs/base/browser/dom'; +import { $, addDisposableListener, Dimension, reset } from 'vs/base/browser/dom'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IProductService } from 'vs/platform/product/common/productService'; import { IGettingStartedCategoryWithProgress, IGettingStartedService } from 'vs/workbench/services/gettingStarted/common/gettingStartedService'; @@ -114,6 +114,7 @@ export class GettingStartedPage extends EditorPane { private tasExperimentService?: ITASExperimentService; private previousSelection?: string; private recentlyOpened: Promise; + private selectedTaskElement?: HTMLDivElement; constructor( @ICommandService private readonly commandService: ICommandService, @@ -174,8 +175,8 @@ export class GettingStartedPage extends EditorPane { } else { badgeelement.parentElement?.setAttribute('aria-checked', 'false'); - badgeelement.classList.add(...ThemeIcon.asClassNameArray(gettingStartedUncheckedCodicon)); badgeelement.classList.remove('complete', ...ThemeIcon.asClassNameArray(gettingStartedCheckedCodicon)); + badgeelement.classList.add(...ThemeIcon.asClassNameArray(gettingStartedUncheckedCodicon)); } }); } @@ -243,8 +244,17 @@ export class GettingStartedPage extends EditorPane { this.selectTask(argument); break; } - case 'completeTask': { - this.gettingStartedService.progressTask(argument); + case 'toggleTaskCompletion': { + if (!this.currentCategory || this.currentCategory.content.type !== 'items') { + throw Error('cannot run task action for category of non items type' + this.currentCategory?.id); + } + + const taskToggle = assertIsDefined(this.currentCategory?.content.items.find(task => task.id === argument)); + if (taskToggle.done) { + this.gettingStartedService.deprogressTask(argument); + } else { + this.gettingStartedService.progressTask(argument); + } break; } case 'runTaskAction': { @@ -287,6 +297,7 @@ export class GettingStartedPage extends EditorPane { if (this.editorInput.selectedTask === id && contractIfAlreadySelected) { this.previousSelection = this.editorInput.selectedTask; this.editorInput.selectedTask = undefined; + this.selectedTaskElement = undefined; return; } taskElement.style.height = `${taskElement.scrollHeight}px`; @@ -294,6 +305,7 @@ export class GettingStartedPage extends EditorPane { throw Error('cannot expand task for category of non items type' + this.currentCategory?.id); } this.editorInput.selectedTask = id; + this.selectedTaskElement = taskElement; const taskToExpand = assertIsDefined(this.currentCategory.content.items.find(task => task.id === id)); mediaElement.setAttribute('alt', taskToExpand.media.altText); @@ -426,24 +438,22 @@ export class GettingStartedPage extends EditorPane { const categoriesSlide = assertIsDefined(this.container.querySelector('.gettingStartedSlideCategory') as HTMLElement); reset(categoriesSlide, - $('.gap'), - $('.header', {}, - $('h1.product-name.caption', {}, localize('gettingStarted.vscode', "Visual Studio Code")), - $('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved")), - ), - $('.categories-split-view', {}, - $('.gap'), - $('.categories-left', {}, - this.buildStartList(), - this.buildRecentlyOpenedList(), + $('.categories-slide-container', {}, + $('.header', {}, + $('h1.product-name.caption', {}, localize('gettingStarted.vscode', "Visual Studio Code")), + $('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved")), ), - $('.categories-right', {}, - $('h2', {}, localize('gettingStarted', "Getting Started")), - this.categoriesScrollbar.getDomNode(), - ), - $('.gap'), + $('.categories-split-view', {}, + $('.categories-left', {}, + this.buildStartList(), + this.buildRecentlyOpenedList(), + ), + $('.categories-right', {}, + $('h2', {}, localize('gettingStarted', "Getting Started")), + this.categoriesScrollbar.getDomNode(), + ), + ) ), - $('.gap'), $('.footer', {}, $('p.showOnStartup', {}, showOnStartupCheckbox, @@ -567,10 +577,8 @@ export class GettingStartedPage extends EditorPane { if (entry.content.type === 'items') { return undefined; } - const li = $('li', {}, this.iconWidgetFor(entry)); - const button = $('button.button-link', { 'x-dispatch': 'selectCategory:' + entry.id }); - - button.innerText = entry.title; + const li = $('li', {}); + const button = $('button.button-link', { 'x-dispatch': 'selectCategory:' + entry.id }, this.iconWidgetFor(entry), $('span', {}, entry.title)); button.title = entry.description; li.appendChild(button); @@ -593,20 +601,17 @@ export class GettingStartedPage extends EditorPane { - layout() { + layout(size: Dimension) { this.categoriesScrollbar?.scanDomNode(); this.detailsScrollbar?.scanDomNode(); this.detailImageScrollbar?.scanDomNode(); + this.container.classList[size.height <= 685 ? 'add' : 'remove']('height-constrained'); - // Don't let our spacer elements move around based on internal content changes, only when the external size changes - this.container.querySelectorAll('.gap').forEach(element => { - element.style.width = ''; - element.style.height = ''; - setTimeout(() => { - element.style.width = `${element.clientWidth}px`; - element.style.height = `${element.clientHeight}px`; - }, 0); - }); + + if (this.selectedTaskElement) { + this.selectedTaskElement.style.height = ``; // unset or the scrollHeight will just be the old height + this.selectedTaskElement.style.height = `${this.selectedTaskElement.scrollHeight}px`; + } } private updateCategoryProgress() { @@ -674,38 +679,50 @@ export class GettingStartedPage extends EditorPane { $('.category-description.description', {}, category.description)))); const categoryElements = category.content.items.map( - (task, i, arr) => $('button.getting-started-task', - { - 'x-dispatch': 'selectTask:' + task.id, - 'data-task-id': task.id, - 'aria-expanded': 'false', - 'aria-checked': '' + task.done, - 'role': 'listitem', - }, - $('.codicon' + (task.done ? '.complete' + ThemeIcon.asCSSSelector(gettingStartedCheckedCodicon) : ThemeIcon.asCSSSelector(gettingStartedUncheckedCodicon)), + (task, i, arr) => { + + const codicon = $('.codicon' + (task.done ? '.complete' + ThemeIcon.asCSSSelector(gettingStartedCheckedCodicon) : ThemeIcon.asCSSSelector(gettingStartedUncheckedCodicon)), { 'data-done-task-id': task.id, - 'x-dispatch': 'completeTask:' + task.id, - }), - $('.task-description-container', {}, + 'x-dispatch': 'toggleTaskCompletion:' + task.id, + }); + + const taskActions = $('.actions', {}, + $('button.emphasis.getting-started-task-action', + { 'x-dispatch': 'runTaskAction:' + task.id }, + task.button.title), + ...( + arr[i + 1] + ? [$('button.task-next.button-link', { 'x-dispatch': 'selectTask:' + arr[i + 1].id }, localize('next', "Next")),] + : nextCategory + ? [$('button.task-next.button-link', { 'x-dispatch': 'selectCategory:' + nextCategory.id }, localize('nextPage', "Next Page")),] + : [] + )); + + + const taskDescription = $('.task-description-container', {}, $('h3.task-title', {}, task.title), $('.task-description.description', {}, task.description), $('.image-description', { 'aria-label': localize('imageShowing', "Image showing {0}", task.media.altText) }), - $('.actions', {}, - ...( - task.button - ? [$('button.emphasis.getting-started-task-action', { 'x-dispatch': 'runTaskAction:' + task.id }, - task.button.title + (task.button.command ? this.getKeybindingLabel(task.button.command) : '') - )] - : []), - ...( - arr[i + 1] - ? [$('button.task-next.button-link', { 'x-dispatch': 'selectTask:' + arr[i + 1].id }, localize('next', "Next")),] - : nextCategory - ? [$('button.task-next.button-link', { 'x-dispatch': 'selectCategory:' + nextCategory.id }, localize('nextPage', "Next Page")),] - : [] - )) - ))); + taskActions, + ); + + const keybindingLabel = (task.button.command && this.getKeybindingLabel(task.button.command)); + if (keybindingLabel) { + taskDescription.appendChild($('span.shortcut-message', {}, 'Pro Tip: Use keyboard shortcut ', $('span.keybinding', {}, keybindingLabel))); + } + + return $('button.getting-started-task', + { + 'x-dispatch': 'selectTask:' + task.id, + 'data-task-id': task.id, + 'aria-expanded': 'false', + 'aria-checked': '' + task.done, + 'role': 'listitem', + }, + codicon, + taskDescription); + }); const detailContainer = $('.getting-started-detail-container', { 'role': 'list' }); if (this.detailsScrollbar) { this.detailsScrollbar.getDomNode().remove(); this.detailsScrollbar.dispose(); } @@ -729,7 +746,7 @@ export class GettingStartedPage extends EditorPane { private getKeybindingLabel(command: string) { const binding = this.keybindingService.lookupKeybinding(command); if (!binding) { return ''; } - else { return ` (${binding.getLabel()})`; } + else { return binding.getLabel() ?? ''; } } private async scrollPrev() { @@ -877,7 +894,7 @@ registerThemingParticipant((theme, collector) => { if (link) { collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer a { color: ${link}; }`); collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link { color: ${link}; }`); - collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link * { color: ${link}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .gettingStartedContainer .button-link .scroll-button { color: ${link}; }`); } const activeLink = theme.getColor(textLinkActiveForeground); if (activeLink) { diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index 5f06b8c2b02..2f7b9e16adb 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -17,7 +17,7 @@ import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/brows import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IModelService } from 'vs/editor/common/services/modelService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { localize } from 'vs/nls'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -39,7 +39,6 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { CancellationToken } from 'vs/base/common/cancellation'; import { domEvent } from 'vs/base/browser/event'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService'; export const WALK_THROUGH_FOCUS = new RawContextKey('interactivePlaygroundFocus', false); @@ -67,7 +66,6 @@ export class WalkThroughPart extends EditorPane { private lastFocus: HTMLElement | undefined; private size: Dimension | undefined; private editorMemento: IEditorMemento; - private tasExperimentService: ITASExperimentService | undefined; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -82,12 +80,10 @@ export class WalkThroughPart extends EditorPane { @INotificationService private readonly notificationService: INotificationService, @IExtensionService private readonly extensionService: IExtensionService, @IEditorGroupsService editorGroupService: IEditorGroupsService, - @optional(ITASExperimentService) tasExperimentService: ITASExperimentService, ) { super(WalkThroughPart.ID, telemetryService, themeService, storageService); this.editorFocus = WALK_THROUGH_FOCUS.bindTo(this.contextKeyService); this.editorMemento = this.getEditorMemento(editorGroupService, WALK_THROUGH_EDITOR_VIEW_STATE_PREFERENCE_KEY); - this.tasExperimentService = tasExperimentService; } createEditor(container: HTMLElement): void { @@ -194,15 +190,6 @@ export class WalkThroughPart extends EditorPane { this.notificationService.info(localize('walkThrough.gitNotFound', "It looks like Git is not installed on your system.")); return; } - if (uri.scheme === 'command' && uri.path === 'workbench.action.files.newUntitledFile') { - Promise.all([ - this.tasExperimentService?.getTreatment('newuntitledmode'), - this.openerService.open(this.addFrom(uri)), - ]).then(([newUntitledMode]) => { - return newUntitledMode && this.openerService.open(this.addFrom(URI.parse('command:workbench.action.editor.changeLanguageMode'))); - }); - return; - } this.openerService.open(this.addFrom(uri)); } diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 26f0d614f7a..8b285d2e546 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -90,38 +90,49 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben this.telemetryService.publicLog2('workspaceTrustRequested', { modal: this.requestModel.trustRequest.modal, workspaceId: this.workspaceContextService.getWorkspace().id, - extensions: (await this.extensionService.getExtensions()).filter(ext => !!ext.requiresWorkspaceTrust).map(ext => ext.identifier.value) + extensions: (await this.extensionService.getExtensions()).filter(ext => !!ext.workspaceTrust).map(ext => ext.identifier.value) }); if (this.requestModel.trustRequest.modal) { + // Message + const defaultMessage = localize('immediateTrustRequestMessage', "A feature you are trying to use may be a security risk if you do not trust the source of the files or folders you currently have open."); + const message = this.requestModel.trustRequest.message ?? defaultMessage; + + // Buttons + const buttons = this.requestModel.trustRequest.buttons ?? [ + { label: localize('grantWorkspaceTrustButton', "Continue"), type: 'ContinueWithTrust' }, + { label: localize('manageWorkspaceTrustButton', "Learn More"), type: 'Manage' } + ]; + // Add Cancel button if not provided + if (!buttons.some(b => b.type === 'Cancel')) { + buttons.push({ label: localize('cancelWorkspaceTrustButton', "Cancel"), type: 'Cancel' }); + } + + // Dialog const result = await this.dialogService.show( Severity.Warning, localize('immediateTrustRequestTitle', "Do you trust the files in this folder?"), - [ - localize('grantWorkspaceTrustButton', "Trust"), - localize('denyWorkspaceTrustButton', "Don't Trust"), - localize('manageWorkspaceTrustButton', "Manage"), - localize('cancelWorkspaceTrustButton', "Cancel"), - ], + buttons.map(b => b.label), { - cancelId: 3, - detail: localize('immediateTrustRequestDetail', "A feature you are trying to use may be a security risk if you do not trust the source of the files or folders you currently have open.\n\nYou should only trust this workspace if you trust its source. Otherwise, features will be enabled that may compromise your device or personal information."), + cancelId: buttons.findIndex(b => b.type === 'Cancel'), + detail: localize('immediateTrustRequestDetail', "{0}\n\nYou should only trust this workspace if you trust its source. Otherwise, features will be enabled that may compromise your device or personal information.", message), } ); - switch (result.choice) { - case 0: // Trust + // Dialog result + switch (buttons[result.choice].type) { + case 'ContinueWithTrust': this.requestModel.completeRequest(WorkspaceTrustState.Trusted); break; - case 1: // Don't Trust - this.requestModel.completeRequest(WorkspaceTrustState.Untrusted); - break; - case 2: // Manage + case 'ContinueWithoutTrust': this.requestModel.completeRequest(undefined); + break; + case 'Manage': + this.requestModel.cancelRequest(); await this.commandService.executeCommand('workbench.trust.manage'); break; - default: // Cancel - this.requestModel.completeRequest(undefined); + case 'Cancel': + this.requestModel.cancelRequest(); break; } } diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts index 8894c6f0630..853d136deb6 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts @@ -248,7 +248,7 @@ export class WorkspaceTrustEditor extends EditorPane { } private async getExtensionsByTrustRequirement(extensions: IExtensionStatus[], trustRequirement: ExtensionWorkspaceTrustRequirement): Promise { - const filtered = extensions.filter(ext => ext.local.manifest.requiresWorkspaceTrust === trustRequirement); + const filtered = extensions.filter(ext => ext.local.manifest.workspaceTrust?.required === trustRequirement); const ids = filtered.map(ext => ext.identifier.id); return getExtensions(ids, this.extensionWorkbenchService); diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index f1bf55fbe5e..fc289efd555 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -281,7 +281,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench private _isDisabledByTrustRequirement(extension: IExtension): boolean { const workspaceTrustState = this.workspaceTrustService.getWorkspaceTrustState(); - if (extension.manifest.requiresWorkspaceTrust === 'onStart') { + if (extension.manifest.workspaceTrust?.required === 'onStart') { if (workspaceTrustState !== WorkspaceTrustState.Trusted) { this._addToWorkspaceDisabledExtensionsByTrustRequirement(extension); } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index c6a2489375b..835a2851834 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -361,7 +361,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench } protected async checkForWorkspaceTrust(manifest: IExtensionManifest): Promise { - if (manifest.requiresWorkspaceTrust === 'onStart') { + if (manifest.workspaceTrust?.required === 'onStart') { const trustState = await this.workspaceTrustService.requireWorkspaceTrust(); return trustState === WorkspaceTrustState.Trusted ? Promise.resolve() : Promise.reject(canceled()); } diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index f08798dd813..9689215d3db 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -270,13 +270,13 @@ export function throwProposedApiError(extension: IExtensionDescription): never { } export function checkRequiresWorkspaceTrust(extension: IExtensionDescription): void { - if (!extension.requiresWorkspaceTrust) { + if (!extension.workspaceTrust?.required) { throwRequiresWorkspaceTrustError(extension); } } export function throwRequiresWorkspaceTrustError(extension: IExtensionDescription): void { - throw new Error(`[${extension.identifier.value}]: This API is only available when the "requiresWorkspaceTrust" is set to "onStart" or "onDemand" in the extension's package.json.`); + throw new Error(`[${extension.identifier.value}]: This API is only available when the "workspaceTrust.require" is set to "onStart" or "onDemand" in the extension's package.json.`); } export function toExtension(extensionDescription: IExtensionDescription): IExtension { diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 920bfd4beb7..1d37bfff3da 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -72,11 +72,11 @@ export function connectProxyResolver( function createPatchedModules(configProvider: ExtHostConfigProvider, resolveProxy: ReturnType) { const proxySetting = { config: configProvider.getConfiguration('http') - .get('proxySupport') || 'off' + .get<'override' | 'on' | 'off'>('proxySupport') || 'off' }; configProvider.onDidChangeConfiguration(e => { proxySetting.config = configProvider.getConfiguration('http') - .get('proxySupport') || 'off'; + .get<'override' | 'on' | 'off'>('proxySupport') || 'off'; }); const certSetting = { config: !!configProvider.getConfiguration('http') diff --git a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts index 6a84a655b09..97df7c29ff1 100644 --- a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts +++ b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts @@ -54,7 +54,7 @@ export interface IFilesConfigurationService { readonly hotExitConfiguration: string | undefined; - preventSaveConflicts(resource: URI, language: string): boolean; + preventSaveConflicts(resource: URI, language?: string): boolean; } export class FilesConfigurationService extends Disposable implements IFilesConfigurationService { @@ -203,7 +203,7 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi return this.currentHotExitConfig; } - preventSaveConflicts(resource: URI, language: string): boolean { + preventSaveConflicts(resource: URI, language?: string): boolean { return this.configurationService.getValue('files.saveConflictResolution', { resource, overrideIdentifier: language }) !== 'overwriteFileOnDisk'; } } diff --git a/src/vs/workbench/services/gettingStarted/common/gettingStartedRegistry.ts b/src/vs/workbench/services/gettingStarted/common/gettingStartedRegistry.ts index 22da432fbcc..c1c1c46eaee 100644 --- a/src/vs/workbench/services/gettingStarted/common/gettingStartedRegistry.ts +++ b/src/vs/workbench/services/gettingStarted/common/gettingStartedRegistry.ts @@ -7,11 +7,9 @@ import { Emitter, Event } from 'vs/base/common/event'; import { FileAccess } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; -import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { content } from 'vs/workbench/services/gettingStarted/common/gettingStartedContent'; -import { localize } from 'vs/nls'; export const enum GettingStartedCategory { Beginner = 'Beginner', @@ -172,161 +170,3 @@ content.forEach(category => { Registry.add(GettingStartedRegistryID, registryImpl); export const GettingStartedRegistry: IGettingStartedRegistry = Registry.as(GettingStartedRegistryID); - -ExtensionsRegistry.registerExtensionPoint({ - extensionPoint: 'welcomeItems', - jsonSchema: { - doNotSuggest: true, - description: localize('gettingStarted', "Contribute items to help users in getting started with your extension. Keys correspond to categories contributed via welcomeCategories contribution point. Experimental, available in VS Code Insiders only."), - type: 'object', - additionalProperties: { - type: 'array', - items: { - type: 'object', - required: ['id', 'title', 'description', 'button', 'media'], - defaultSnippets: [{ body: { 'id': '$1', 'title': '$2', 'description': '$3', 'button': { 'title': '$4' }, 'media': { 'path': '$5', 'altText': '$6' } } }], - properties: { - id: { - type: 'string', - description: localize('gettingStarted.id', "Unique identifier for this item."), - }, - title: { - type: 'string', - description: localize('gettingStarted.title', "Title of item.") - }, - description: { - type: 'string', - description: localize('gettingStarted.description', "Description of item.") - }, - button: { - description: localize('gettingStarted.button', "The item's button, which can either link to an external resource or run a command"), - oneOf: [ - { - type: 'object', - required: ['title', 'command'], - defaultSnippets: [{ 'body': { 'title': '$1', 'command': '$2' } }], - properties: { - title: { - type: 'string', - description: localize('gettingStarted.button.title', "Title of button.") - }, - command: { - type: 'string', - description: localize('gettingStarted.button.command', "Command to run when button is clicked. Running this command will mark the item completed.") - } - } - }, - { - type: 'object', - required: ['title', 'link'], - defaultSnippets: [{ 'body': { 'title': '$1', 'link': '$2' } }], - properties: { - title: { - type: 'string', - description: localize('gettingStarted.button.title', "Title of button.") - }, - link: { - type: 'string', - description: localize('gettingStarted.button.link', "Link to open when button is clicked. Opening this link will mark the item completed.") - } - } - } - ] - }, - media: { - type: 'object', - required: ['path', 'altText'], - description: localize('gettingStarted.media', "Image to show alongside this item."), - defaultSnippets: [{ 'body': { 'altText': '$1' } }], - properties: { - path: { - description: localize('gettingStarted.media.path', "Either a single string path to an image to be used on all color themes, or separate paths for light, dark, and high contrast themes."), - oneOf: [ - { - type: 'string', - defaultSnippets: [{ 'body': '$1' }], - }, - { - type: 'object', - defaultSnippets: [{ 'body': { 'hc': '$1', 'light': '$2', 'dark': '$3' } }], - required: ['hc', 'light', 'dark'], - properties: { - hc: { type: 'string' }, - light: { type: 'string' }, - dark: { type: 'string' }, - } - }, - ] - }, - altText: { - type: 'string', - description: localize('gettingStarted.media.altText', "Alternate text to display when the image cannot be loaded or in screen readers.") - } - } - }, - doneOn: { - oneOf: [ - { - type: 'object', - required: ['event'], - properties: { - 'event': { - description: localize('gettingStarted.oneOn.event', "Mark item done when the specified event is marked via the invoking the `welcomeItems.markEvent` command."), - type: 'string' - } - } - }, - { - type: 'object', - required: ['command'], - properties: { - 'command': { - description: localize('gettingStarted.oneOn.command', "Mark item done when the specified command is executed."), - type: 'string' - } - } - }, - ], - description: localize('gettingStarted.doneOn', "Signal to mark item as complete. If not defined, running the button's command will mark the item complete.") - }, - when: { - type: 'string', - description: localize('gettingStarted.when', "Context key expression to control the visibility of this getting started item.") - } - } - } - } - } -}); - -ExtensionsRegistry.registerExtensionPoint({ - extensionPoint: 'welcomeCategories', - jsonSchema: { - doNotSuggest: true, - description: localize('welcomeCategories', "Contribute categories of items to help users in getting started with your extension. Items themselves are contributed via welcomeItems contribution point. Experimental, available in VS Code Insiders only."), - type: 'array', - items: { - type: 'object', - required: ['id', 'title', 'description'], - defaultSnippets: [{ body: { 'id': '$1', 'title': '$2', 'description': '$3' } }], - properties: { - id: { - type: 'string', - description: localize('welcomeCategories.id', "Unique identifier for this category."), - }, - title: { - type: 'string', - description: localize('welcomeCategories.title', "Title of category.") - }, - description: { - type: 'string', - description: localize('welcomeCategories.description', "Description of category.") - }, - when: { - type: 'string', - description: localize('welcomeCategories.when', "Context key expression to control the visibility of this category.") - } - } - } - } -}); diff --git a/src/vs/workbench/services/gettingStarted/common/gettingStartedService.ts b/src/vs/workbench/services/gettingStarted/common/gettingStartedService.ts index 0cfaeb32de6..277520be3bc 100644 --- a/src/vs/workbench/services/gettingStarted/common/gettingStartedService.ts +++ b/src/vs/workbench/services/gettingStarted/common/gettingStartedService.ts @@ -51,6 +51,7 @@ export interface IGettingStartedService { progressByEvent(eventName: string): void; progressTask(id: string): void; + deprogressTask(id: string): void; } export class GettingStartedService extends Disposable implements IGettingStartedService { @@ -139,20 +140,17 @@ export class GettingStartedService extends Disposable implements IGettingStarted if (!this.trackedExtensions.has(ExtensionIdentifier.toKey(extension.identifier))) { this.trackedExtensions.add(ExtensionIdentifier.toKey(extension.identifier)); - if ((extension.contributes?.welcomeCategories || extension.contributes?.welcomeItems) && this.productService.quality === 'stable') { + if ((extension.contributes?.walkthroughs?.length) && this.productService.quality === 'stable') { console.warn('Extension', extension.identifier.value, 'contributes welcome page content but this is a Stable build and extension contributions are only available in Insiders. The contributed content will be disregarded.'); return; } - const contributedCategories = new Map(); - - extension.contributes?.welcomeCategories?.forEach(category => { - const categoryID = extension.identifier.value + '.' + category.id; - contributedCategories.set(category.id, categoryID); + extension.contributes?.walkthroughs?.forEach(section => { + const categoryID = extension.identifier.value + '#' + section.id; this.registry.registerCategory({ content: { type: 'items' }, - description: category.description, - title: category.title, + description: section.description, + title: section.title, id: categoryID, icon: { type: 'image', @@ -160,31 +158,30 @@ export class GettingStartedService extends Disposable implements IGettingStarted ? FileAccess.asBrowserUri(joinPath(extension.extensionLocation, extension.icon)).toString(true) : DefaultIconPath }, - when: ContextKeyExpr.deserialize(category.when) ?? ContextKeyExpr.true(), + when: ContextKeyExpr.deserialize(section.when) ?? ContextKeyExpr.true(), }); - }); + try { - Object.entries(extension.contributes?.welcomeItems ?? {}).forEach(([category, items]) => - items.forEach((item, index) => - this.registry.registerTask({ - button: item.button, - description: item.description, - media: { type: 'image', altText: item.media.altText, path: convertPaths(item.media.path) }, - doneOn: item.doneOn?.event - ? { eventFired: item.doneOn.event } - : item.doneOn?.command ? - { commandExecuted: item.doneOn.command } - : item.button.command - ? { commandExecuted: item.button.command } - : { eventFired: `linkOpened:${item.button.link}` }, - id: extension.identifier.value + '.' + item.id, - title: item.title, - when: ContextKeyExpr.deserialize(item.when) ?? ContextKeyExpr.true(), - category: contributedCategories.get(category) ?? category, - order: index, - }) - ) - ); + section.tasks.forEach((task, index) => + this.registry.registerTask({ + button: task.button, + description: task.description, + media: { type: 'image', altText: task.media.altText, path: convertPaths(task.media.path) }, + doneOn: task.doneOn?.command + ? { commandExecuted: task.doneOn.command } + : task.button.command + ? { commandExecuted: task.button.command } + : { eventFired: `linkOpened:${task.button.link}` }, + id: extension.identifier.value + '#' + task.id, + title: task.title, + when: ContextKeyExpr.deserialize(task.when) ?? ContextKeyExpr.true(), + category: categoryID, + order: index, + })); + } catch (e) { + console.error('Error registering walkthrough tasks for ', categoryID, e); + } + }); } } @@ -264,6 +261,13 @@ export class GettingStartedService extends Disposable implements IGettingStarted } } + deprogressTask(id: string) { + delete this.taskProgress[id]; + this.memento.saveMemento(); + const task = this.registry.getTask(id); + this._onDidProgressTask.fire(this.getTaskProgress(task)); + } + private progressByCommand(command: string) { const listening = this.commandListeners.get(command) ?? []; listening.forEach(id => this.progressTask(id)); diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index 4bb2f960c50..ca7b1c79ef2 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -154,7 +154,7 @@ interface PortAttributes extends Attributes { export class PortsAttributes extends Disposable { private static SETTING = 'remote.portsAttributes'; - private static DEFAULTS = 'remote.portsAttributes.defaults'; + private static DEFAULTS = 'remote.unconfiguredPortsAttributes'; private static RANGE = /^(\d+)\-(\d+)$/; private portsAttributes: PortAttributes[] = []; private defaultPortAttributes: Attributes | undefined; @@ -164,7 +164,7 @@ export class PortsAttributes extends Disposable { constructor(private readonly configurationService: IConfigurationService) { super(); this._register(configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(PortsAttributes.SETTING)) { + if (e.affectsConfiguration(PortsAttributes.SETTING) || e.affectsConfiguration(PortsAttributes.DEFAULTS)) { this.updateAttributes(); } })); @@ -645,7 +645,7 @@ export class TunnelModel extends Disposable { const allProviderResults = await Promise.all(flatten(this.portAttributesProviders.map(provider => { return Array.from(pidToPortsMapping.entries()).map(entry => { const portGroup = entry[1]; - const matchingCandidate = matchingCandidates.get(portGroup[1]); + const matchingCandidate = matchingCandidates.get(portGroup[0]); return provider.providePortAttributes(portGroup, matchingCandidate?.pid, matchingCandidate?.detail, new CancellationTokenSource().token); }); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index dee93ec518f..1476ebc0921 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -373,7 +373,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE modelListeners.add(model.onDidLoad(reason => this._onDidLoad.fire({ model, reason }))); modelListeners.add(model.onDidChangeDirty(() => this._onDidChangeDirty.fire(model))); modelListeners.add(model.onDidSaveError(() => this._onDidSaveError.fire(model))); - modelListeners.add(model.onDidSave(reason => this._onDidSave.fire({ model: model, reason }))); + modelListeners.add(model.onDidSave(reason => this._onDidSave.fire({ model, reason }))); modelListeners.add(model.onDidRevert(() => this._onDidRevert.fire(model))); modelListeners.add(model.onDidChangeEncoding(() => this._onDidChangeEncoding.fire(model))); diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncResourceEnablementService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncResourceEnablementService.ts new file mode 100644 index 00000000000..8c361845bda --- /dev/null +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncResourceEnablementService.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncResourceEnablementService as BaseUserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + +export class UserDataSyncResourceEnablementService extends BaseUserDataSyncResourceEnablementService implements IUserDataSyncResourceEnablementService { + + constructor( + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IStorageService storageService: IStorageService, + @ITelemetryService telemetryService: ITelemetryService, + ) { + super(storageService, telemetryService); + } + + getResourceSyncStateVersion(resource: SyncResource): string | undefined { + return resource === SyncResource.Extensions ? this.environmentService.options?.settingsSyncOptions?.extensionsSyncStateVersion : undefined; + } + +} + +registerSingleton(IUserDataSyncResourceEnablementService, UserDataSyncResourceEnablementService); diff --git a/src/vs/workbench/services/workingCopy/common/fileWorkingCopy.ts b/src/vs/workbench/services/workingCopy/common/fileWorkingCopy.ts new file mode 100644 index 00000000000..69f83c09634 --- /dev/null +++ b/src/vs/workbench/services/workingCopy/common/fileWorkingCopy.ts @@ -0,0 +1,1211 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import { Event, Emitter } from 'vs/base/common/event'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ETAG_DISABLED, FileChangesEvent, FileChangeType, FileOperationError, FileOperationResult, FileSystemProviderCapabilities, IFileService, IFileStatWithMetadata, IFileStreamContent } from 'vs/platform/files/common/files'; +import { ISaveOptions, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; +import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { TaskSequentializer, timeout } from 'vs/base/common/async'; +import { ILogService } from 'vs/platform/log/common/log'; +import { DefaultEndOfLine, ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; +import { assertIsDefined } from 'vs/base/common/types'; +import { ITextFileEditorModel, ITextFileService, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { newWriteableBufferStream, VSBuffer, VSBufferReadableStream } from 'vs/base/common/buffer'; +import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; +import { isWindows } from 'vs/base/common/platform'; + +export interface IFileWorkingCopyModelFactory { + + /** + * Asks the file working copy delegate to create a model from the given + * content under the provided resource. The content may originate from + * different sources depending on context: + * - from a backup if that exists + * - from the underlying file resource + * - passed in from the caller + * + * @param resource the `URI` of the model + * @param contents the content of the model to create it + * @param token support for cancellation + */ + createModel(resource: URI, contents: VSBufferReadableStream, token: CancellationToken): Promise; +} + +/** + * The underlying model of a file working copy provides some + * methods for the file working copy to function. The model is + * typically only available after the working copy has been + * resolved via it's `resolve()` method. + */ +export interface IFileWorkingCopyModel { + + /** + * An even that fires whenever the content of the file working + * copy model changes. The file working copy will listen to these + * changes and mark the working copy as dirty whenever this event + * fires. + * + * Note: ONLY report changes to the model but not the underlying + * file. The file working copy is tracking changes to the file + * automatically. + */ + readonly onDidChangeContent: Event; + + /** + * An event emitted right before disposing the model. + */ + readonly onWillDispose: Event; + + /** + * Snapshots the model's current content for writing. This must include + * any changes that were made to the model that are in memory. + * + * @param token support for cancellation + */ + snapshot(token: CancellationToken): Promise; + + /** + * Updates the model with the provided contents. The implementation should + * behave in a similar fashion as `IFileWorkingCopyModelFactory#createModel` + * except that here the model already exists and just needs to update to + * the provided contents. + * + * @param the contents to use for the model + * @param token support for cancellation + */ + update(contents: VSBufferReadableStream, token: CancellationToken): Promise; + + /** + * TODO@bpasero should find a better name here maybe together + * with the pushStackElement concept since this is around + * undo/redo? + */ + getAlternativeVersionId(): number; + + /** + * Close the current undo-redo element. This offers a way + * to create an undo/redo stop point. + * + * This method may for example be called right before the + * save is triggered so that the user can always undo back + * to the state before saving. + * + * TODO@bpasero rename to beforeSave()? + */ + pushStackElement(): void; + + /** + * @deprecated do not invest to implement, will try to get this to + * work through the other `snapshot` method. + */ + _backupSnapshot(): ITextSnapshot | undefined; +} + +export interface IFileWorkingCopyModelContentChangedEvent { + + /** + * Flag that indicates that this event was generated while undoing. + */ + readonly isUndoing: boolean; + + /** + * Flag that indicates that this event was generated while redoing. + */ + readonly isRedoing: boolean; +} + +/** + * A file based `IWorkingCopy` is backed by a `URI` from a + * known file system provider. Given this assumption, a lot + * of functionality can be built on top, such as saving in + * a secure way to prevent data loss. + */ +export interface IFileWorkingCopy extends IWorkingCopy, Disposable { + + /** + * An event for when a file working copy was resolved. + */ + readonly onDidResolve: Event; + + /** + * An event for when a file working copy was saved successfully. + */ + readonly onDidSave: Event; + + /** + * An event indicating that a file working copy save operation failed. + */ + readonly onDidSaveError: Event; + + /** + * An event for when the file working copy was reverted. + */ + readonly onDidRevert: Event; + + /** + * An event for when the orphaned state of the file working copy changes. + */ + readonly onDidChangeOrphaned: Event; + + /** + * An event for when the file working copy has been disposed. + */ + readonly onDispose: Event; + + /** + * Provides access to the underlying model of this file + * based working copy. As long as the file working copy + * has not been resolved, the model is `undefined`. + */ + readonly model: T | undefined; + + /** + * Whether we have a resolved model or not. + */ + isResolved(): this is IResolvedFileWorkingCopy; + + /** + * Whether the file working copy has been disposed or not. + */ + isDisposed(): boolean; + + /** + * Resolves a file working copy. + */ + resolve(options?: IFileWorkingCopyResolveOptions): Promise>; +} + +export interface IResolvedFileWorkingCopy extends IFileWorkingCopy { + + /** + * A resolved file working copy has a resolved model `T`. + */ + readonly model: T; +} + +/** + * States the file working copy can be in. + */ +export const enum FileWorkingCopyState { + + /** + * A file working copy is saved. + */ + SAVED, + + /** + * A file working copy is dirty. + */ + DIRTY, + + /** + * A file working copy is currently being saved but + * this operation has not completed yet. + */ + PENDING_SAVE, + + /** + * A file working copy is in conflict mode when changes + * cannot be saved because the underlying file has changed. + * File working copies in conflict mode are always dirty. + */ + CONFLICT, + + /** + * A file working copy is in orphan state when the underlying + * file has been deleted. + */ + ORPHAN, + + /** + * Any error that happens during a save that is not causing + * the `FileWorkingCopyState.CONFLICT` state. + * File working copies in error mode are always dirty. + */ + ERROR +} + +/** + * Metadata associated with a file working copy backup. + */ +interface IFileWorkingCopyBackupMetaData { + mtime: number; + ctime: number; + size: number; + etag: string; + orphaned: boolean; +} + +export interface IFileWorkingCopySaveOptions extends ISaveOptions { + + /** + * Save the file working copy with an attempt to unlock it. + */ + writeUnlock?: boolean; + + /** + * Save the file working copy with elevated privileges. + * + * Note: This may not be supported in all environments. + */ + writeElevated?: boolean; + + /** + * Allows to write to a file working copy even if it has been + * modified on disk. This should only be triggered from an + * explicit user action. + */ + ignoreModifiedSince?: boolean; + + /** + * If set, will bubble up the file working copy save error to + * the caller instead of handling it. + */ + ignoreErrorHandler?: boolean; +} + +export interface IFileWorkingCopyResolveOptions { + + /** + * The contents to use for the file working copy if known. If not + * provided, the contents will be retrieved from the underlying + * resource or backup if present. + * + * If contents are provided, the file working copy will be marked + * as dirty right from the beginning. + */ + contents?: VSBufferReadableStream; + + /** + * Go to disk bypassing any cache of the file working copy if any. + */ + forceReadFromDisk?: boolean; +} + +export class FileWorkingCopy extends Disposable implements IFileWorkingCopy { + + readonly capabilities: WorkingCopyCapabilities = WorkingCopyCapabilities.None; + + private _model: T | undefined = undefined; + get model(): T | undefined { return this._model; } + + //#region events + + private readonly _onDidChangeContent = this._register(new Emitter()); + readonly onDidChangeContent = this._onDidChangeContent.event; + + private readonly _onDidResolve = this._register(new Emitter()); + readonly onDidResolve = this._onDidResolve.event; + + private readonly _onDidChangeDirty = this._register(new Emitter()); + readonly onDidChangeDirty = this._onDidChangeDirty.event; + + private readonly _onDidSaveError = this._register(new Emitter()); + readonly onDidSaveError = this._onDidSaveError.event; + + private readonly _onDidSave = this._register(new Emitter()); + readonly onDidSave = this._onDidSave.event; + + private readonly _onDidRevert = this._register(new Emitter()); + readonly onDidRevert = this._onDidRevert.event; + + private readonly _onDidChangeOrphaned = this._register(new Emitter()); + readonly onDidChangeOrphaned = this._onDidChangeOrphaned.event; + + private readonly _onDispose = this._register(new Emitter()); + readonly onDispose = this._onDispose.event; + + //#endregion + + constructor( + readonly resource: URI, + readonly name: string, + private readonly modelFactory: IFileWorkingCopyModelFactory, + @IFileService private readonly fileService: IFileService, + @ILogService private readonly logService: ILogService, + @ITextFileService private readonly textFileService: ITextFileService, + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, + @IBackupFileService private readonly backupFileService: IBackupFileService, + @IWorkingCopyService workingCopyService: IWorkingCopyService + ) { + super(); + + if (!fileService.canHandleResource(this.resource)) { + throw new Error(`The file working copy resource ${this.resource.toString(true)} does not have an associated file system provider.`); + } + + // Make known to working copy service + this._register(workingCopyService.registerWorkingCopy(this)); + + this.registerListeners(); + } + + //#region Orphaned Tracking + + private inOrphanMode = false; + + private registerListeners(): void { + this._register(this.fileService.onDidFilesChange(e => this.onDidFilesChange(e))); + } + + private async onDidFilesChange(e: FileChangesEvent): Promise { + let fileEventImpactsUs = false; + let newInOrphanModeGuess: boolean | undefined; + + // If we are currently orphaned, we check if the file was added back + if (this.inOrphanMode) { + const fileWorkingCopyResourceAdded = e.contains(this.resource, FileChangeType.ADDED); + if (fileWorkingCopyResourceAdded) { + newInOrphanModeGuess = false; + fileEventImpactsUs = true; + } + } + + // Otherwise we check if the file was deleted + else { + const fileWorkingCopyResourceDeleted = e.contains(this.resource, FileChangeType.DELETED); + if (fileWorkingCopyResourceDeleted) { + newInOrphanModeGuess = true; + fileEventImpactsUs = true; + } + } + + if (fileEventImpactsUs && this.inOrphanMode !== newInOrphanModeGuess) { + let newInOrphanModeValidated: boolean = false; + if (newInOrphanModeGuess) { + // We have received reports of users seeing delete events even though the file still + // exists (network shares issue: https://github.com/microsoft/vscode/issues/13665). + // Since we do not want to mark the working copy as orphaned, we have to check if the + // file is really gone and not just a faulty file event. + await timeout(100); + + if (this.isDisposed()) { + newInOrphanModeValidated = true; + } else { + const exists = await this.fileService.exists(this.resource); + newInOrphanModeValidated = !exists; + } + } + + if (this.inOrphanMode !== newInOrphanModeValidated && !this.isDisposed()) { + this.setOrphaned(newInOrphanModeValidated); + } + } + } + + private setOrphaned(orphaned: boolean): void { + if (this.inOrphanMode !== orphaned) { + this.inOrphanMode = orphaned; + this._onDidChangeOrphaned.fire(); + } + } + + //#endregion + + //#region Dirty + + private dirty = false; + private bufferSavedVersionId: number | undefined; + + isDirty(): this is IResolvedFileWorkingCopy { + return this.dirty; + } + + setDirty(dirty: boolean): void { + if (!this.isResolved()) { + return; // only resolved working copies can be marked dirty + } + + // Track dirty state and version id + const wasDirty = this.dirty; + this.doSetDirty(dirty); + + // Emit as Event if dirty changed + if (dirty !== wasDirty) { + this._onDidChangeDirty.fire(); + } + } + + private doSetDirty(dirty: boolean): () => void { + const wasDirty = this.dirty; + const wasInConflictMode = this.inConflictMode; + const wasInErrorMode = this.inErrorMode; + const oldBufferSavedVersionId = this.bufferSavedVersionId; + + if (!dirty) { + this.dirty = false; + this.inConflictMode = false; + this.inErrorMode = false; + this.updateSavedVersionId(); + } else { + this.dirty = true; + } + + // Return function to revert this call + return () => { + this.dirty = wasDirty; + this.inConflictMode = wasInConflictMode; + this.inErrorMode = wasInErrorMode; + this.bufferSavedVersionId = oldBufferSavedVersionId; + }; + } + + //#endregion + + //#region Resolve + + private lastResolvedFileStat: IFileStatWithMetadata | undefined; + + async resolve(options?: IFileWorkingCopyResolveOptions): Promise> { + this.logService.trace('[file working copy] resolve() - enter', this.resource.toString(true)); + + // Return early if we are disposed + if (this.isDisposed()) { + this.logService.trace('[file working copy] resolve() - exit - without resolving because file working copy is disposed', this.resource.toString(true)); + + return this; + } + + // Unless there are explicit contents provided, it is important that we do not + // resolve a working copy that is dirty or is in the process of saving to prevent + // data loss. + if (!options?.contents && (this.dirty || this.saveSequentializer.hasPending())) { + this.logService.trace('[file working copy] resolve() - exit - without resolving because file working copy is dirty or being saved', this.resource.toString(true)); + + return this; + } + + return this.doResolve(options); + } + + private async doResolve(options?: IFileWorkingCopyResolveOptions): Promise> { + + // First check if we have contents to use for the working copy + if (options?.contents) { + return this.resolveFromBuffer(options.contents, options); + } + + // Second, check if we have a backup to resolve from (only for new working copies) + const isNew = !this.isResolved(); + if (isNew) { + const resolvedFromBackup = await this.resolveFromBackup(options); + if (resolvedFromBackup) { + return resolvedFromBackup; + } + } + + // Finally, resolve from file resource + return this.resolveFromFile(options); + } + + private async resolveFromBuffer(buffer: VSBufferReadableStream, options?: IFileWorkingCopyResolveOptions): Promise> { + this.logService.trace('[file working copy] resolveFromBuffer()', this.resource.toString(true)); + + // Try to resolve metdata from disk + let mtime: number; + let ctime: number; + let size: number; + let etag: string; + try { + const metadata = await this.fileService.resolve(this.resource, { resolveMetadata: true }); + mtime = metadata.mtime; + ctime = metadata.ctime; + size = metadata.size; + etag = metadata.etag; + + // Clear orphaned state when resolving was successful + this.setOrphaned(false); + } catch (error) { + + // Put some fallback values in error case + mtime = Date.now(); + ctime = Date.now(); + size = 0; + etag = ETAG_DISABLED; + + // Apply orphaned state based on error code + this.setOrphaned(error.fileOperationResult === FileOperationResult.FILE_NOT_FOUND); + } + + // Resolve with buffer + await this.resolveFromContent({ + resource: this.resource, + name: this.name, + mtime, + ctime, + size, + etag, + value: buffer + }, true /* dirty (resolved from buffer) */); + + return this; + } + + private async resolveFromBackup(options?: IFileWorkingCopyResolveOptions): Promise | undefined> { + + // Resolve backup if any + const backup = await this.backupFileService.resolve(this.resource); + + // Abort if someone else managed to resolve the working copy by now + let isNew = !this.isResolved(); + if (!isNew) { + this.logService.trace('[file working copy] resolveFromBackup() - exit - withoutresolving because previously new file working copy got created meanwhile', this.resource.toString(true)); + + return this; // imply that resolving has happened in another operation + } + + // Try to resolve from backup if we have any + if (backup) { + return this.doResolveFromBackup(backup, options); + } + + // Otherwise signal back that resolving did not happen + return undefined; + } + + private async doResolveFromBackup(backup: IResolvedBackup, options?: IFileWorkingCopyResolveOptions): Promise> { + this.logService.trace('[file working copy] doResolveFromBackup()', this.resource.toString(true)); + + // Resolve with backup + await this.resolveFromContent({ + resource: this.resource, + name: this.name, + mtime: backup.meta ? backup.meta.mtime : Date.now(), + ctime: backup.meta ? backup.meta.ctime : Date.now(), + size: backup.meta ? backup.meta.size : 0, + etag: backup.meta ? backup.meta.etag : ETAG_DISABLED, // etag disabled if unknown! + value: this.textBufferFactoryToStream(backup.value) + }, true /* dirty (resolved from backup) */); + + // Restore orphaned flag based on state + if (backup.meta && backup.meta.orphaned) { + this.setOrphaned(true); + } + + return this; + } + + private async resolveFromFile(options?: IFileWorkingCopyResolveOptions): Promise> { + this.logService.trace('[file working copy] resolveFromFile()', this.resource.toString(true)); + + const forceReadFromDisk = options?.forceReadFromDisk; + + // Decide on etag + let etag: string | undefined; + if (forceReadFromDisk) { + etag = ETAG_DISABLED; // disable ETag if we enforce to read from disk + } else if (this.lastResolvedFileStat) { + etag = this.lastResolvedFileStat.etag; // otherwise respect etag to support caching + } + + // Remember current version before doing any long running operation + // to ensure we are not changing a working copy that was changed + // meanwhile + const currentVersionId = this.versionId; + + // Resolve Content + try { + const content = await this.fileService.readFileStream(this.resource, { etag }); + + // Clear orphaned state when resolving was successful + this.setOrphaned(false); + + // Return early if the working copy content has changed + // meanwhile to prevent loosing any changes + if (currentVersionId !== this.versionId) { + this.logService.trace('[file working copy] resolveFromFile() - exit - without resolving because file working copy content changed', this.resource.toString(true)); + + return this; + } + + return await this.resolveFromContent(content, false /* not dirty (resolved from file) */); + } catch (error) { + const result = error.fileOperationResult; + + // Apply orphaned state based on error code + this.setOrphaned(result === FileOperationResult.FILE_NOT_FOUND); + + // NotModified status is expected and can be handled gracefully + if (result === FileOperationResult.FILE_NOT_MODIFIED_SINCE) { + return this; + } + + // Ignore when a working copy has been resolved once and the file was deleted + // meanwhile. Since we already have the working copy resolved, we can return to + // this state and update the orphaned flag to indicate that this working copy + // has no version on disk anymore. + if (this.isResolved() && result === FileOperationResult.FILE_NOT_FOUND) { + return this; + } + + // Otherwise bubble up the error + throw error; + } + } + + private async resolveFromContent(content: IFileStreamContent, dirty: boolean): Promise> { + this.logService.trace('[file working copy] resolveFromContent() - enter', this.resource.toString(true)); + + // Return early if we are disposed + if (this.isDisposed()) { + this.logService.trace('[file working copy] resolveFromContent() - exit - because working copy is disposed', this.resource.toString(true)); + + return this; + } + + // Update our resolved disk stat + this.updateLastResolvedFileStat({ + resource: this.resource, + name: content.name, + mtime: content.mtime, + ctime: content.ctime, + size: content.size, + etag: content.etag, + isFile: true, + isDirectory: false, + isSymbolicLink: false + }); + + // Update existing model if we had been resolved + if (this.isResolved()) { + await this.doUpdateModel(content.value); + } + + // Create new model otherwise + else { + await this.doCreateModel(content.value); + } + + // Update working copy dirty flag. This is very important to call + // in both cases of dirty or not because it conditionally updates + // the `bufferSavedVersionId` to determine the version when to consider + // the working copy as saved again (e.g. when undoing back to the + // saved state) + this.setDirty(!!dirty); + + // Emit as event + this._onDidResolve.fire(); + + return this; + } + + private async doCreateModel(contents: VSBufferReadableStream): Promise { + this.logService.trace('[file working copy] doCreateModel()', this.resource.toString(true)); + + // Create model + this._model = await this.modelFactory.createModel(this.resource, contents, CancellationToken.None); + + // Model listeners + this.installModelListeners(this._model); + } + + private ignoreDirtyOnModelContentChange = false; + + private async doUpdateModel(contents: VSBufferReadableStream): Promise { + this.logService.trace('[file working copy] doUpdateModel()', this.resource.toString(true)); + + // Update model value in a block that ignores content change events for dirty tracking + this.ignoreDirtyOnModelContentChange = true; + try { + await this.model?.update(contents, CancellationToken.None); + } finally { + this.ignoreDirtyOnModelContentChange = false; + } + } + + private installModelListeners(model: IFileWorkingCopyModel): void { + + // See https://github.com/microsoft/vscode/issues/30189 + // This code has been extracted to a different method because it caused a memory leak + // where `value` was captured in the content change listener closure scope. + + // Content Change + this._register(model.onDidChangeContent(e => this.onModelContentChanged(model, e.isUndoing || e.isRedoing))); + + // Lifecycle + this._register(model.onWillDispose(() => this.dispose())); + } + + private onModelContentChanged(model: IFileWorkingCopyModel, isUndoingOrRedoing: boolean): void { + this.logService.trace(`[file working copy] onModelContentChanged() - enter`, this.resource.toString(true)); + + // In any case increment the version id because it tracks the textual content state of the model at all times + this.versionId++; + this.logService.trace(`[file working copy] onModelContentChanged() - new versionId ${this.versionId}`, this.resource.toString(true)); + + // Remember when the user changed the model through a undo/redo operation. + // We need this information to throttle save participants to fix + // https://github.com/microsoft/vscode/issues/102542 + if (isUndoingOrRedoing) { + this.lastContentChangeFromUndoRedo = Date.now(); + } + + // We mark check for a dirty-state change upon model content change, unless: + // - explicitly instructed to ignore it (e.g. from model.resolve()) + // - the model is readonly (in that case we never assume the change was done by the user) + if (!this.ignoreDirtyOnModelContentChange && !this.isReadonly()) { + + // The contents changed as a matter of Undo and the version reached matches the saved one + // In this case we clear the dirty flag and emit a SAVED event to indicate this state. + if (model.getAlternativeVersionId() === this.bufferSavedVersionId) { + this.logService.trace('[file working copy] onModelContentChanged() - model content changed back to last saved version', this.resource.toString(true)); + + // Clear flags + const wasDirty = this.dirty; + this.setDirty(false); + + // Emit revert event if we were dirty + if (wasDirty) { + this._onDidRevert.fire(); + } + } + + // Otherwise the content has changed and we signal this as becoming dirty + else { + this.logService.trace('[file working copy] onModelContentChanged() - model content changed and marked as dirty', this.resource.toString(true)); + + // Mark as dirty + this.setDirty(true); + } + } + + // Emit as event + this._onDidChangeContent.fire(); + } + + //#endregion + + //#region Backup + + async backup(token: CancellationToken): Promise { + + // Fill in metadata if we are resolved + let meta: IFileWorkingCopyBackupMetaData | undefined = undefined; + if (this.lastResolvedFileStat) { + meta = { + mtime: this.lastResolvedFileStat.mtime, + ctime: this.lastResolvedFileStat.ctime, + size: this.lastResolvedFileStat.size, + etag: this.lastResolvedFileStat.etag, + orphaned: this.inOrphanMode + }; + } + + return { meta, content: this.model?._backupSnapshot() }; + } + + //#endregion + + //#region Save + + private versionId = 0; + + private static readonly UNDO_REDO_SAVE_PARTICIPANTS_AUTO_SAVE_THROTTLE_THRESHOLD = 500; + private lastContentChangeFromUndoRedo: number | undefined = undefined; + + private readonly saveSequentializer = new TaskSequentializer(); + + async save(options: IFileWorkingCopySaveOptions = Object.create(null)): Promise { + if (!this.isResolved()) { + return false; + } + + if (this.isReadonly()) { + this.logService.trace('[file working copy] save() - ignoring request for readonly resource', this.resource.toString(true)); + + return false; // if working copy is readonly we do not attempt to save at all + } + + if ( + (this.hasState(FileWorkingCopyState.CONFLICT) || this.hasState(FileWorkingCopyState.ERROR)) && + (options.reason === SaveReason.AUTO || options.reason === SaveReason.FOCUS_CHANGE || options.reason === SaveReason.WINDOW_CHANGE) + ) { + this.logService.trace('[file working copy] save() - ignoring auto save request for file working copy that is in conflict or error', this.resource.toString(true)); + + return false; // if working copy is in save conflict or error, do not save unless save reason is explicit + } + + // Actually do save + this.logService.trace('[file working copy] save() - enter', this.resource.toString(true)); + await this.doSave(options); + this.logService.trace('[file working copy] save() - exit', this.resource.toString(true)); + + return true; + } + + private async doSave(options: IFileWorkingCopySaveOptions): Promise { + if (typeof options.reason !== 'number') { + options.reason = SaveReason.EXPLICIT; + } + + let versionId = this.versionId; + this.logService.trace(`[file working copy] doSave(${versionId}) - enter with versionId ${versionId}`, this.resource.toString(true)); + + // Lookup any running pending save for this versionId and return it if found + // + // Scenario: user invoked the save action multiple times quickly for the same contents + // while the save was not yet finished to disk + // + if (this.saveSequentializer.hasPending(versionId)) { + this.logService.trace(`[file working copy] doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource.toString(true)); + + return this.saveSequentializer.pending; + } + + // Return early if not dirty (unless forced) + // + // Scenario: user invoked save action even though the working copy is not dirty + if (!options.force && !this.dirty) { + this.logService.trace(`[file working copy] doSave(${versionId}) - exit - because not dirty and/or versionId is different (this.isDirty: ${this.dirty}, this.versionId: ${this.versionId})`, this.resource.toString(true)); + + return; + } + + // Return if currently saving by storing this save request as the next save that should happen. + // Never ever must 2 saves execute at the same time because this can lead to dirty writes and race conditions. + // + // Scenario A: auto save was triggered and is currently busy saving to disk. this takes long enough that another auto save + // kicks in. + // Scenario B: save is very slow (e.g. network share) and the user manages to change the buffer and trigger another save + // while the first save has not returned yet. + // + if (this.saveSequentializer.hasPending()) { + this.logService.trace(`[file working copy] doSave(${versionId}) - exit - because busy saving`, this.resource.toString(true)); + + // Indicate to the save sequentializer that we want to + // cancel the pending operation so that ours can run + // before the pending one finishes. + // Currently this will try to cancel pending save + // participants but never a pending save. + this.saveSequentializer.cancelPending(); + + // Register this as the next upcoming save and return + return this.saveSequentializer.setNext(() => this.doSave(options)); + } + + // Push all edit operations to the undo stack so that the user has a chance to + // Ctrl+Z back to the saved version. + if (this.isResolved()) { + this.model.pushStackElement(); + } + + const saveCancellation = new CancellationTokenSource(); + + return this.saveSequentializer.setPending(versionId, (async () => { + + // A save participant can still change the working copy now + // and since we are so close to saving we do not want to trigger + // another auto save or similar, so we block this + // In addition we update our version right after in case it changed + // because of a working copy change + // Save participants can also be skipped through API. + if (this.isResolved() && !options.skipSaveParticipants && this.isTextFileModel(this.model)) { + try { + + // Measure the time it took from the last undo/redo operation to this save. If this + // time is below `UNDO_REDO_SAVE_PARTICIPANTS_THROTTLE_THRESHOLD`, we make sure to + // delay the save participant for the remaining time if the reason is auto save. + // + // This fixes the following issue: + // - the user has configured auto save with delay of 100ms or shorter + // - the user has a save participant enabled that modifies the file on each save + // - the user types into the file and the file gets saved + // - the user triggers undo operation + // - this will undo the save participant change but trigger the save participant right after + // - the user has no chance to undo over the save participant + // + // Reported as: https://github.com/microsoft/vscode/issues/102542 + if (options.reason === SaveReason.AUTO && typeof this.lastContentChangeFromUndoRedo === 'number') { + const timeFromUndoRedoToSave = Date.now() - this.lastContentChangeFromUndoRedo; + if (timeFromUndoRedoToSave < FileWorkingCopy.UNDO_REDO_SAVE_PARTICIPANTS_AUTO_SAVE_THROTTLE_THRESHOLD) { + await timeout(FileWorkingCopy.UNDO_REDO_SAVE_PARTICIPANTS_AUTO_SAVE_THROTTLE_THRESHOLD - timeFromUndoRedoToSave); + } + } + + // Run save participants unless save was cancelled meanwhile + if (!saveCancellation.token.isCancellationRequested) { + await this.textFileService.files.runSaveParticipants(this.model, { reason: options.reason ?? SaveReason.EXPLICIT }, saveCancellation.token); + } + } catch (error) { + this.logService.error(`[file working copy] runSaveParticipants(${versionId}) - resulted in an error: ${error.toString()}`, this.resource.toString(true)); + } + } + + // It is possible that a subsequent save is cancelling this + // running save. As such we return early when we detect that + // However, we do not pass the token into the file service + // because that is an atomic operation currently without + // cancellation support, so we dispose the cancellation if + // it was not cancelled yet. + if (saveCancellation.token.isCancellationRequested) { + return; + } else { + saveCancellation.dispose(); + } + + // We have to protect against being disposed at this point. It could be that the save() operation + // was triggerd followed by a dispose() operation right after without waiting. Typically we cannot + // be disposed if we are dirty, but if we are not dirty, save() and dispose() can still be triggered + // one after the other without waiting for the save() to complete. If we are disposed(), we risk + // saving contents to disk that are stale (see https://github.com/microsoft/vscode/issues/50942). + // To fix this issue, we will not store the contents to disk when we got disposed. + if (this.isDisposed()) { + return; + } + + // We require a resolved working copy from this point on, since we are about to write data to disk. + if (!this.isResolved()) { + return; + } + + // update versionId with its new value (if pre-save changes happened) + versionId = this.versionId; + + // Clear error flag since we are trying to save again + this.inErrorMode = false; + + // Save to Disk. We mark the save operation as currently pending with + // the latest versionId because it might have changed from a save + // participant triggering + this.logService.trace(`[file working copy] doSave(${versionId}) - before write()`, this.resource.toString(true)); + const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); + const resolvedFileWorkingCopy = this; + return this.saveSequentializer.setPending(versionId, (async () => { + try { + const stat = await this.fileService.writeFile(lastResolvedFileStat.resource, await resolvedFileWorkingCopy.model.snapshot(CancellationToken.None), { + mtime: lastResolvedFileStat.mtime, + etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource)) ? ETAG_DISABLED : lastResolvedFileStat.etag, + unlock: options.writeUnlock + }); + + this.handleSaveSuccess(stat, versionId, options); + } catch (error) { + this.handleSaveError(error, versionId, options); + } + })()); + })(), () => saveCancellation.cancel()); + } + + private handleSaveSuccess(stat: IFileStatWithMetadata, versionId: number, options: IFileWorkingCopySaveOptions): void { + + // Updated resolved stat with updated stat + this.updateLastResolvedFileStat(stat); + + // Update dirty state unless working copy has changed meanwhile + if (versionId === this.versionId) { + this.logService.trace(`[file working copy] handleSaveSuccess(${versionId}) - setting dirty to false because versionId did not change`, this.resource.toString(true)); + this.setDirty(false); + } else { + this.logService.trace(`[file working copy] handleSaveSuccess(${versionId}) - not setting dirty to false because versionId did change meanwhile`, this.resource.toString(true)); + } + + // Update orphan state given save was successful + this.setOrphaned(false); + + // Emit Save Event + this._onDidSave.fire(options.reason ?? SaveReason.EXPLICIT); + } + + private handleSaveError(error: Error, versionId: number, options: IFileWorkingCopySaveOptions): void { + this.logService.error(`[file working copy] handleSaveError(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource.toString(true)); + + // Return early if the save() call was made asking to + // handle the save error itself. + if (options.ignoreErrorHandler) { + throw error; + } + + // In any case of an error, we mark the working copy as dirty to prevent data loss + // It could be possible that the write corrupted the file on disk (e.g. when + // an error happened after truncating the file) and as such we want to preserve + // the working copy contents to prevent data loss. + this.setDirty(true); + + // Flag as error state + this.inErrorMode = true; + + // Look out for a save conflict + if ((error as FileOperationError).fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE) { + this.inConflictMode = true; + } + + // Delegate to save error handler + let throwError = true; + if (this.isTextFileModel(this.model)) { + this.textFileService.files.saveErrorHandler.onSaveError(error, this.model); + throwError = false; + } + + // Emit as event + this._onDidSaveError.fire(); + + if (throwError) { + throw error; + } + } + + private updateSavedVersionId(): void { + + // we remember the models alternate version id to remember when the version + // of the model matches with the saved version on disk. we need to keep this + // in order to find out if the model changed back to a saved version (e.g. + // when undoing long enough to reach to a version that is saved and then to + // clear the dirty flag) + if (this.isResolved()) { + this.bufferSavedVersionId = this.model.getAlternativeVersionId(); + } + } + + private updateLastResolvedFileStat(newFileStat: IFileStatWithMetadata): void { + + // First resolve - just take + if (!this.lastResolvedFileStat) { + this.lastResolvedFileStat = newFileStat; + } + + // Subsequent resolve - make sure that we only assign it if the mtime + // is equal or has advanced. + // This prevents race conditions from resolving and saving. If a save + // comes in late after a revert was called, the mtime could be out of + // sync. + else if (this.lastResolvedFileStat.mtime <= newFileStat.mtime) { + this.lastResolvedFileStat = newFileStat; + } + } + + //#endregion + + //#region Revert + + async revert(options?: IRevertOptions): Promise { + if (!this.isResolved()) { + return; + } + + // Unset flags + const wasDirty = this.dirty; + const undoSetDirty = this.doSetDirty(false); + + // Force read from disk unless reverting soft + const softUndo = options?.soft; + if (!softUndo) { + try { + await this.resolve({ forceReadFromDisk: true }); + } catch (error) { + + // FileNotFound means the file got deleted meanwhile, so ignore it + if ((error as FileOperationError).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) { + + // Set flags back to previous values, we are still dirty if revert failed + undoSetDirty(); + + throw error; + } + } + } + + // Emit file change event + this._onDidRevert.fire(); + + // Emit dirty change event + if (wasDirty) { + this._onDidChangeDirty.fire(); + } + } + + //#endregion + + //#region State + + private inConflictMode = false; + private inErrorMode = false; + + hasState(state: FileWorkingCopyState): boolean { + switch (state) { + case FileWorkingCopyState.CONFLICT: + return this.inConflictMode; + case FileWorkingCopyState.DIRTY: + return this.dirty; + case FileWorkingCopyState.ERROR: + return this.inErrorMode; + case FileWorkingCopyState.ORPHAN: + return this.inOrphanMode; + case FileWorkingCopyState.PENDING_SAVE: + return this.saveSequentializer.hasPending(); + case FileWorkingCopyState.SAVED: + return !this.dirty; + } + } + + joinState(state: FileWorkingCopyState.PENDING_SAVE): Promise { + return this.saveSequentializer.pending ?? Promise.resolve(); + } + + //#endregion + + //#region Utilities + + isResolved(): this is IResolvedFileWorkingCopy { + return !!this.model; + } + + isReadonly(): boolean { + return this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly); + } + + getStat(): IFileStatWithMetadata | undefined { + return this.lastResolvedFileStat; + } + + //#endregion + + //#region Dispose + + private disposed = false; + + isDisposed(): boolean { + return this.disposed; + } + + dispose(): void { + this.logService.trace('[file working copy] dispose()', this.resource.toString(true)); + + // State + this.disposed = true; + this.inConflictMode = false; + this.inOrphanMode = false; + this.inErrorMode = false; + + // Event + this._onDispose.fire(); + + super.dispose(); + } + + //#endregion + + //#region Remainders of text file model world (TODO@bpasero callers have to be handled in a generic way) + + private isTextFileModel(model: unknown): model is ITextFileEditorModel { + const textFileModel = this.textFileService.files.get(this.resource); + + return !!(textFileModel && this.model && (textFileModel as unknown) === (this.model as unknown)); + } + + private textBufferFactoryToStream(factory: ITextBufferFactory): VSBufferReadableStream { + const stream = newWriteableBufferStream(); + + const contents = snapshotToString(factory.create(isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).textBuffer.createSnapshot(false)); + stream.end(VSBuffer.fromString(contents)); + + return stream; + } + + //#endregion +} diff --git a/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts b/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts new file mode 100644 index 00000000000..45d624cd2ff --- /dev/null +++ b/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts @@ -0,0 +1,421 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { FileWorkingCopy, IFileWorkingCopy, IFileWorkingCopyModel, IFileWorkingCopyModelFactory } from 'vs/workbench/services/workingCopy/common/fileWorkingCopy'; +import { SaveReason } from 'vs/workbench/common/editor'; +import { ResourceMap } from 'vs/base/common/map'; +import { ResourceQueue } from 'vs/base/common/async'; +import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files'; +import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { URI } from 'vs/base/common/uri'; +import { VSBufferReadableStream } from 'vs/base/common/buffer'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +/** + * The only one that should be dealing with `IFileWorkingCopy` and handle all + * operations that are working copy related, such as save/revert, backup + * and resolving. + */ +export interface IFileWorkingCopyManager { + + /** + * An event for when a file working copy was created. + */ + readonly onDidCreate: Event>; + + /** + * An event for when a file working copy was resolved. + */ + readonly onDidResolve: Event>; + + /** + * An event for when a file working copy changed it's dirty state. + */ + readonly onDidChangeDirty: Event>; + + /** + * An event for when a file working copy failed to save. + */ + readonly onDidSaveError: Event>; + + /** + * An event for when a file working copy successfully saved. + */ + readonly onDidSave: Event>; + + /** + * An event for when a file working copy was reverted. + */ + readonly onDidRevert: Event>; + + /** + * Access to all known file working copies within the manager. + */ + readonly workingCopies: IFileWorkingCopy[]; + + /** + * Returns the file working copy for the provided resource + * or undefined if none. + */ + get(resource: URI): IFileWorkingCopy | undefined; + + /** + * Allows to resolve a file working copy. + */ + resolve(resource: URI, options?: IFileWorkingCopyResolveOptions): Promise>; + + /** + * Waits for the file working copy to be ready to be disposed. There may be + * conditions under which the file working copy cannot be disposed, e.g. when + * it is dirty. Once the promise is settled, it is safe to dispose. + */ + canDispose(workingCopy: IFileWorkingCopy): true | Promise; +} + +export interface IFileWorkingCopySaveEvent { + + /** + * The file working copy that was successfully saved. + */ + workingCopy: IFileWorkingCopy; + + /** + * The reason why the file working copy was saved. + */ + reason: SaveReason; +} + +export interface IFileWorkingCopyResolveOptions { + + /** + * The contents to use for the file working copy if known. + * If not provided, the contents will be retrieved from the + * underlying resource or backup if present. + * + * If contents are provided, the file working copy will be marked + * as dirty right from the beginning. + */ + contents?: VSBufferReadableStream; + + /** + * If the file working copy was already resolved before, + * allows to trigger a reload of it to fetch the latest contents: + * - async: resolve() will return immediately and trigger + * a reload that will run in the background. + * - sync: resolve() will only return resolved when the + * file working copy has finished reloading. + */ + reload?: { + async: boolean + }; +} + +export class FileWorkingCopyManager extends Disposable implements IFileWorkingCopyManager { + + //#region Events + + private readonly _onDidCreate = this._register(new Emitter>()); + readonly onDidCreate = this._onDidCreate.event; + + private readonly _onDidResolve = this._register(new Emitter>()); + readonly onDidResolve = this._onDidResolve.event; + + private readonly _onDidChangeDirty = this._register(new Emitter>()); + readonly onDidChangeDirty = this._onDidChangeDirty.event; + + private readonly _onDidSaveError = this._register(new Emitter>()); + readonly onDidSaveError = this._onDidSaveError.event; + + private readonly _onDidSave = this._register(new Emitter>()); + readonly onDidSave = this._onDidSave.event; + + private readonly _onDidRevert = this._register(new Emitter>()); + readonly onDidRevert = this._onDidRevert.event; + + //#endregion + + private readonly mapResourceToWorkingCopy = new ResourceMap>(); + private readonly mapResourceToWorkingCopyListeners = new ResourceMap(); + private readonly mapResourceToDisposeListener = new ResourceMap(); + private readonly mapResourceToPendingWorkingCopyResolve = new ResourceMap>>(); + + private readonly workingCopyResolveQueue = this._register(new ResourceQueue()); + + get workingCopies(): IFileWorkingCopy[] { + return [...this.mapResourceToWorkingCopy.values()]; + } + + constructor( + private readonly modelFactory: IFileWorkingCopyModelFactory, + @IFileService private readonly fileService: IFileService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @ILabelService private readonly labelService: ILabelService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super(); + + this.registerListeners(); + } + + private registerListeners(): void { + + // Update working copies from file change events + this._register(this.fileService.onDidFilesChange(e => this.onDidFilesChange(e))); + + // Lifecycle + this.lifecycleService.onShutdown(() => this.dispose); + } + + private onDidFilesChange(e: FileChangesEvent): void { + for (const workingCopy of this.workingCopies) { + if (workingCopy.isDirty() || !workingCopy.isResolved()) { + continue; // require a resolved, saved working copy to continue + } + + // Trigger a resolve for any update or add event that impacts + // the working copy. We also consider the added event + // because it could be that a file was added and updated + // right after. + if (e.contains(workingCopy.resource, FileChangeType.UPDATED, FileChangeType.ADDED)) { + this.queueWorkingCopyResolve(workingCopy); + } + } + } + + private queueWorkingCopyResolve(workingCopy: IFileWorkingCopy): void { + + // Resolves a working copy to update (use a queue to prevent accumulation of + // resolve when the resolving actually takes long. At most we only want the + // queue to have a size of 2 (1 running resolve and 1 queued resolve). + const queue = this.workingCopyResolveQueue.queueFor(workingCopy.resource); + if (queue.size <= 1) { + queue.queue(async () => { + try { + await workingCopy.resolve(); + } catch (error) { + onUnexpectedError(error); + } + }); + } + } + + get(resource: URI): IFileWorkingCopy | undefined { + return this.mapResourceToWorkingCopy.get(resource); + } + + async resolve(resource: URI, options?: IFileWorkingCopyResolveOptions): Promise> { + + // Await a pending working copy resolve first before proceeding + // to ensure that we never resolve a working copy more than once + // in parallel + const pendingResolve = this.joinPendingResolve(resource); + if (pendingResolve) { + await pendingResolve; + } + + let workingCopyPromise: Promise>; + let workingCopy = this.get(resource); + let didCreateWorkingCopy = false; + + // Working copy exists + if (workingCopy) { + + // Always reload if contents are provided + if (options?.contents) { + workingCopyPromise = workingCopy.resolve(options); + } + + // Reload async or sync based on options + else if (options?.reload) { + + // async reload: trigger a reload but return immediately + if (options.reload.async) { + workingCopyPromise = Promise.resolve(workingCopy); + workingCopy.resolve(options); + } + + // sync reload: do not return until working copy reloaded + else { + workingCopyPromise = workingCopy.resolve(options); + } + } + + // Do not reload + else { + workingCopyPromise = Promise.resolve(workingCopy); + } + } + + // File working copy does not exist + else { + didCreateWorkingCopy = true; + + const newWorkingCopy = workingCopy = this.instantiationService.createInstance(FileWorkingCopy, resource, this.labelService.getUriBasenameLabel(resource), this.modelFactory) as unknown as IFileWorkingCopy; + workingCopyPromise = workingCopy.resolve(options); + + this.registerWorkingCopy(newWorkingCopy); + } + + // Store pending resolve to avoid race conditions + this.mapResourceToPendingWorkingCopyResolve.set(resource, workingCopyPromise); + + // Make known to manager (if not already known) + this.add(resource, workingCopy); + + // Emit some events if we created the working copy + if (didCreateWorkingCopy) { + this._onDidCreate.fire(workingCopy); + + // If the working copy is dirty right from the beginning, + // make sure to emit this as an event + if (workingCopy.isDirty()) { + this._onDidChangeDirty.fire(workingCopy); + } + } + + try { + const resolvedWorkingCopy = await workingCopyPromise; + + // Remove from pending resolves + this.mapResourceToPendingWorkingCopyResolve.delete(resource); + + // File working copy can be dirty if a backup was restored, so we make sure to + // have this event delivered if we created the working copy here + if (didCreateWorkingCopy && resolvedWorkingCopy.isDirty()) { + this._onDidChangeDirty.fire(resolvedWorkingCopy); + } + + return resolvedWorkingCopy; + } catch (error) { + + // Free resources of this invalid working copy + if (workingCopy) { + workingCopy.dispose(); + } + + // Remove from pending resolves + this.mapResourceToPendingWorkingCopyResolve.delete(resource); + + throw error; + } + } + + private joinPendingResolve(resource: URI): Promise | undefined { + const pendingWorkingCopyResolve = this.mapResourceToPendingWorkingCopyResolve.get(resource); + if (pendingWorkingCopyResolve) { + return pendingWorkingCopyResolve.then(undefined, error => {/* ignore any error here, it will bubble to the original requestor*/ }); + } + + return undefined; + } + + private registerWorkingCopy(workingCopy: IFileWorkingCopy): void { + + // Install working copy listeners + const workingCopyListeners = new DisposableStore(); + workingCopyListeners.add(workingCopy.onDidResolve(() => this._onDidResolve.fire(workingCopy))); + workingCopyListeners.add(workingCopy.onDidChangeDirty(() => this._onDidChangeDirty.fire(workingCopy))); + workingCopyListeners.add(workingCopy.onDidSaveError(() => this._onDidSaveError.fire(workingCopy))); + workingCopyListeners.add(workingCopy.onDidSave(reason => this._onDidSave.fire({ workingCopy: workingCopy, reason }))); + workingCopyListeners.add(workingCopy.onDidRevert(() => this._onDidRevert.fire(workingCopy))); + + // Keep for disposal + this.mapResourceToWorkingCopyListeners.set(workingCopy.resource, workingCopyListeners); + } + + protected add(resource: URI, workingCopy: IFileWorkingCopy): void { + const knownWorkingCopy = this.mapResourceToWorkingCopy.get(resource); + if (knownWorkingCopy === workingCopy) { + return; // already cached + } + + // dispose any previously stored dispose listener for this resource + const disposeListener = this.mapResourceToDisposeListener.get(resource); + if (disposeListener) { + disposeListener.dispose(); + } + + // store in cache but remove when working copy gets disposed + this.mapResourceToWorkingCopy.set(resource, workingCopy); + this.mapResourceToDisposeListener.set(resource, workingCopy.onDispose(() => this.remove(resource))); + } + + protected remove(resource: URI): void { + this.mapResourceToWorkingCopy.delete(resource); + + const disposeListener = this.mapResourceToDisposeListener.get(resource); + if (disposeListener) { + dispose(disposeListener); + this.mapResourceToDisposeListener.delete(resource); + } + + const workingCopyListener = this.mapResourceToWorkingCopyListeners.get(resource); + if (workingCopyListener) { + dispose(workingCopyListener); + this.mapResourceToWorkingCopyListeners.delete(resource); + } + } + + clear(): void { + + // working copy caches + this.mapResourceToWorkingCopy.clear(); + this.mapResourceToPendingWorkingCopyResolve.clear(); + + // dispose the dispose listeners + this.mapResourceToDisposeListener.forEach(listener => listener.dispose()); + this.mapResourceToDisposeListener.clear(); + + // dispose the working copy change listeners + this.mapResourceToWorkingCopyListeners.forEach(listener => listener.dispose()); + this.mapResourceToWorkingCopyListeners.clear(); + } + + canDispose(workingCopy: IFileWorkingCopy): true | Promise { + + // quick return if working copy already disposed or not dirty and not resolving + if ( + workingCopy.isDisposed() || + (!this.mapResourceToPendingWorkingCopyResolve.has(workingCopy.resource) && !workingCopy.isDirty()) + ) { + return true; + } + + // promise based return in all other cases + return this.doCanDispose(workingCopy); + } + + private async doCanDispose(workingCopy: IFileWorkingCopy): Promise { + + // if we have a pending working copy resolve, await it first and then try again + const pendingResolve = this.joinPendingResolve(workingCopy.resource); + if (pendingResolve) { + await pendingResolve; + + return this.canDispose(workingCopy); + } + + // dirty working copy: we do not allow to dispose dirty working copys + // to prevent data loss cases. dirty working copys can only be disposed when + // they are either saved or reverted + if (workingCopy.isDirty()) { + await Event.toPromise(workingCopy.onDidChangeDirty); + + return this.canDispose(workingCopy); + } + + return true; + } + + dispose(): void { + super.dispose(); + + this.clear(); + } +} diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index 7d60bff7703..607d6c29305 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -16,6 +16,7 @@ import { isEqual, isEqualOrParent } from 'vs/base/common/extpath'; import { EditorModel } from 'vs/workbench/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { dirname, resolve } from 'vs/base/common/path'; +import { canceled } from 'vs/base/common/errors'; export const WORKSPACE_TRUST_ENABLED = 'workspace.trustEnabled'; export const WORKSPACE_TRUST_STORAGE_KEY = 'content.trust.model.key'; @@ -191,6 +192,9 @@ export class WorkspaceTrustRequestModel extends Disposable implements IWorkspace private readonly _onDidCompleteRequest = this._register(new Emitter()); readonly onDidCompleteRequest = this._onDidCompleteRequest.event; + private readonly _onDidCancelRequest = this._register(new Emitter()); + readonly onDidCancelRequest = this._onDidCancelRequest.event; + initiateRequest(request: WorkspaceTrustRequest): void { if (this.trustRequest && (!request.modal || this.trustRequest.modal)) { return; @@ -204,6 +208,11 @@ export class WorkspaceTrustRequestModel extends Disposable implements IWorkspace this.trustRequest = undefined; this._onDidCompleteRequest.fire(trustState); } + + cancelRequest(): void { + this.trustRequest = undefined; + this._onDidCancelRequest.fire(); + } } export class WorkspaceTrustService extends Disposable implements IWorkspaceTrustService { @@ -217,8 +226,11 @@ export class WorkspaceTrustService extends Disposable implements IWorkspaceTrust readonly onDidChangeTrustState = this._onDidChangeTrustState.event; private _currentTrustState: WorkspaceTrustState = WorkspaceTrustState.Unknown; - private _inFlightResolver?: (trustState: WorkspaceTrustState) => void; private _trustRequestPromise?: Promise; + private _inFlightResolver?: (trustState: WorkspaceTrustState) => void; + private _modalTrustRequestPromise?: Promise; + private _modalTrustRequestResolver?: (trustState: WorkspaceTrustState) => void; + private _modalTrustRequestRejecter?: (error: Error) => void; private _workspace: IWorkspace; private readonly _ctxWorkspaceTrustState: IContextKey; @@ -241,8 +253,10 @@ export class WorkspaceTrustService extends Disposable implements IWorkspaceTrust this.logInitialWorkspaceTrustInfo(); + this._register(this.workspaceService.onDidChangeWorkspaceFolders(() => this.currentTrustState = this.calculateWorkspaceTrustState())); this._register(this.dataModel.onDidChangeTrustState(() => this.currentTrustState = this.calculateWorkspaceTrustState())); this._register(this.requestModel.onDidCompleteRequest((trustState) => this.onTrustRequestCompleted(trustState))); + this._register(this.requestModel.onDidCancelRequest(() => this.onTrustRequestCancelled())); this._ctxWorkspaceTrustState = WorkspaceTrustContext.TrustState.bindTo(contextKeyService); this._ctxWorkspaceTrustPendingRequest = WorkspaceTrustContext.PendingRequest.bindTo(contextKeyService); @@ -358,6 +372,9 @@ export class WorkspaceTrustService extends Disposable implements IWorkspaceTrust } private onTrustRequestCompleted(trustState?: WorkspaceTrustState): void { + if (this._modalTrustRequestResolver) { + this._modalTrustRequestResolver(trustState === undefined ? this.currentTrustState : trustState); + } if (this._inFlightResolver) { this._inFlightResolver(trustState === undefined ? this.currentTrustState : trustState); } @@ -365,6 +382,10 @@ export class WorkspaceTrustService extends Disposable implements IWorkspaceTrust this._inFlightResolver = undefined; this._trustRequestPromise = undefined; + this._modalTrustRequestResolver = undefined; + this._modalTrustRequestRejecter = undefined; + this._modalTrustRequestPromise = undefined; + if (trustState === undefined) { return; } @@ -377,6 +398,16 @@ export class WorkspaceTrustService extends Disposable implements IWorkspaceTrust this._ctxWorkspaceTrustState.set(trustState); } + private onTrustRequestCancelled(): void { + if (this._modalTrustRequestRejecter) { + this._modalTrustRequestRejecter(canceled()); + } + + this._modalTrustRequestResolver = undefined; + this._modalTrustRequestRejecter = undefined; + this._modalTrustRequestPromise = undefined; + } + getWorkspaceTrustState(): WorkspaceTrustState { return this.currentTrustState; } @@ -386,31 +417,42 @@ export class WorkspaceTrustService extends Disposable implements IWorkspaceTrust } async requireWorkspaceTrust(request: WorkspaceTrustRequest = { modal: true }): Promise { + // Trusted workspace if (this.currentTrustState === WorkspaceTrustState.Trusted) { return this.currentTrustState; } + // Untrusted workspace - soft request if (this.currentTrustState === WorkspaceTrustState.Untrusted && !request.modal) { return this.currentTrustState; } - if (this._trustRequestPromise) { - if (request.modal && - this.requestModel.trustRequest && - !this.requestModel.trustRequest.modal) { - this.requestModel.initiateRequest(request); + if (request.modal) { + // Modal request + if (!this._modalTrustRequestPromise) { + // Create promise + this._modalTrustRequestPromise = new Promise((resolve, reject) => { + this._modalTrustRequestResolver = resolve; + this._modalTrustRequestRejecter = reject; + }); + } else { + // Return existing promises + return this._modalTrustRequestPromise; + } + } else { + // Soft request + if (!this._trustRequestPromise) { + this._trustRequestPromise = new Promise(resolve => { + this._inFlightResolver = resolve; + }); + } else { + return this._trustRequestPromise; } - - return this._trustRequestPromise; } - this._trustRequestPromise = new Promise(resolve => { - this._inFlightResolver = resolve; - }); - this.requestModel.initiateRequest(request); this._ctxWorkspaceTrustPendingRequest.set(true); - return this._trustRequestPromise; + return request.modal ? this._modalTrustRequestPromise! : this._trustRequestPromise!; } } diff --git a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts index b40a45b3539..9e29d3ac043 100644 --- a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts @@ -591,7 +591,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 2); const [first, second] = actions; assert.strictEqual(first.action.title, 'Testing1'); @@ -615,7 +615,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 1); const [first] = actions; assert.strictEqual(first.action.title, 'Testing1'); @@ -638,7 +638,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 1); }); @@ -656,7 +656,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Invoke }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 1); }); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 2fd80a3c538..3f9d179715b 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -80,6 +80,7 @@ import 'vs/workbench/services/extensionManagement/browser/builtinExtensionsScann import 'vs/workbench/services/extensionRecommendations/common/extensionIgnoredRecommendationsService'; import 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; import 'vs/workbench/services/notification/common/notificationService'; +import 'vs/workbench/services/userDataSync/browser/userDataSyncResourceEnablementService'; import 'vs/workbench/services/userDataSync/common/userDataSyncUtil'; import 'vs/workbench/services/remote/common/remoteExplorerService'; import 'vs/workbench/services/workingCopy/common/workingCopyService'; @@ -118,12 +119,9 @@ import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/common/downloadService'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IUserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; -import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; import { ExtensionsStorageSyncService, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; -registerSingleton(IUserDataSyncResourceEnablementService, UserDataSyncResourceEnablementService); registerSingleton(IIgnoredExtensionsManagementService, IgnoredExtensionsManagementService); registerSingleton(IGlobalExtensionEnablementService, GlobalExtensionEnablementService); registerSingleton(IExtensionsStorageSyncService, ExtensionsStorageSyncService); diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 6ca7f49bc65..7d3a06dd857 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -224,6 +224,12 @@ interface ISettingsSyncOptions { */ readonly enabled: boolean; + /** + * Version of extensions sync state. + * Extensions sync state will be reset if version is provided and different from previous version. + */ + readonly extensionsSyncStateVersion?: string; + /** * Handler is being called when the user changes Settings Sync enablement. */ diff --git a/test/smoke/src/areas/notebook/notebook.test.ts b/test/smoke/src/areas/notebook/notebook.test.ts index e8dae86262b..8d0c2468705 100644 --- a/test/smoke/src/areas/notebook/notebook.test.ts +++ b/test/smoke/src/areas/notebook/notebook.test.ts @@ -25,34 +25,34 @@ export function setup() { await app.workbench.quickaccess.runCommand('workbench.action.closeActiveEditor'); }); - it('inserts/edits code cell', async function () { - const app = this.app as Application; - await app.workbench.notebook.openNotebook(); - await app.workbench.notebook.focusNextCell(); - await app.workbench.notebook.insertNotebookCell('code'); - await app.workbench.notebook.waitForTypeInEditor('// some code'); - await app.workbench.notebook.stopEditingCell(); - }); + // it('inserts/edits code cell', async function () { + // const app = this.app as Application; + // await app.workbench.notebook.openNotebook(); + // await app.workbench.notebook.focusNextCell(); + // await app.workbench.notebook.insertNotebookCell('code'); + // await app.workbench.notebook.waitForTypeInEditor('// some code'); + // await app.workbench.notebook.stopEditingCell(); + // }); - it('inserts/edits markdown cell', async function () { - const app = this.app as Application; - await app.workbench.notebook.openNotebook(); - await app.workbench.notebook.focusNextCell(); - await app.workbench.notebook.insertNotebookCell('markdown'); - await app.workbench.notebook.waitForTypeInEditor('## hello2! '); - await app.workbench.notebook.stopEditingCell(); - await app.workbench.notebook.waitForMarkdownContents('h2', 'hello2!'); - }); + // it('inserts/edits markdown cell', async function () { + // const app = this.app as Application; + // await app.workbench.notebook.openNotebook(); + // await app.workbench.notebook.focusNextCell(); + // await app.workbench.notebook.insertNotebookCell('markdown'); + // await app.workbench.notebook.waitForTypeInEditor('## hello2! '); + // await app.workbench.notebook.stopEditingCell(); + // await app.workbench.notebook.waitForMarkdownContents('h2', 'hello2!'); + // }); - it('moves focus as it inserts/deletes a cell', async function () { - const app = this.app as Application; - await app.workbench.notebook.openNotebook(); - await app.workbench.notebook.insertNotebookCell('code'); - await app.workbench.notebook.waitForActiveCellEditorContents(''); - await app.workbench.notebook.stopEditingCell(); - await app.workbench.notebook.deleteActiveCell(); - await app.workbench.notebook.waitForMarkdownContents('p', 'Markdown Cell'); - }); + // it('moves focus as it inserts/deletes a cell', async function () { + // const app = this.app as Application; + // await app.workbench.notebook.openNotebook(); + // await app.workbench.notebook.insertNotebookCell('code'); + // await app.workbench.notebook.waitForActiveCellEditorContents(''); + // await app.workbench.notebook.stopEditingCell(); + // await app.workbench.notebook.deleteActiveCell(); + // await app.workbench.notebook.waitForMarkdownContents('p', 'Markdown Cell'); + // }); it.skip('moves focus in and out of output', async function () { // TODO@rebornix https://github.com/microsoft/vscode/issues/113882 const app = this.app as Application; diff --git a/yarn.lock b/yarn.lock index 1cd880523e2..a906f56e968 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10096,10 +10096,10 @@ vscode-oniguruma@1.3.1: resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.3.1.tgz#e2383879c3485b19f533ec34efea9d7a2b14be8f" integrity sha512-gz6ZBofA7UXafVA+m2Yt2zHKgXC2qedArprIsHAPKByTkwq9l5y/izAGckqxYml7mSbYxTRTfdRwsFq3cwF4LQ== -vscode-proxy-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.6.0.tgz#3ea5c3c82f7abe945d690eb34b2c877cf5833f12" - integrity sha512-dvoLmEO/IxkbcNrRHH6ey8ITfvau4wDg01S+iAJ5Pq/FoAl2ZeE4cK5VEnQ2JHqM20kTLhyZfkjdHq6l7/T+xA== +vscode-proxy-agent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.7.0.tgz#adf19dfbc9beba508b1c72a83cd64b5d1cb9d84c" + integrity sha512-eXGwlzwQJJqutYHjhC0UfBw1Sp8p2UygXACQjMSMKkIufeF68c5Z12s7v7UVBkqW3KfgEh/zDaO/bPkWLI/RZw== dependencies: debug "^3.1.0" http-proxy-agent "^2.1.0"