mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
Merge branch 'main' into alex/ghost-text
This commit is contained in:
commit
94f47f44c9
|
@ -1,14 +1,14 @@
|
|||
# Code - OSS Development Container
|
||||
|
||||
This repository includes configuration for a development container for working with Code - OSS in an isolated local container or using [GitHub Codespaces](https://github.com/features/codespaces).
|
||||
This repository includes configuration for a development container for working with Code - OSS in a local container or using [GitHub Codespaces](https://github.com/features/codespaces).
|
||||
|
||||
> **Tip:** The default VNC password is `vscode`. The VNC server runs on port `5901` with a web client at `6080`. For better performance, we recommend using a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Applications like the macOS Screen Sharing app will not perform as well.
|
||||
> **Tip:** The default VNC password is `vscode`. The VNC server runs on port `5901` and a web client is available on port `6080`.
|
||||
|
||||
## Quick start - local
|
||||
|
||||
1. Install Docker Desktop or Docker for Linux on your local machine. (See [docs](https://aka.ms/vscode-remote/containers/getting-started) for additional details.)
|
||||
|
||||
2. **Important**: Docker needs at least **4 Cores and 6 GB of RAM (8 GB recommended)** to run full build. If you on macOS, or using the old Hyper-V engine for Windows, update these values for Docker Desktop by right-clicking on the Docker status bar item, going to **Preferences/Settings > Resources > Advanced**.
|
||||
2. **Important**: Docker needs at least **4 Cores and 6 GB of RAM (8 GB recommended)** to run a full build. If you are on macOS, or are using the old Hyper-V engine for Windows, update these values for Docker Desktop by right-clicking on the Docker status bar item and going to **Preferences/Settings > Resources > Advanced**.
|
||||
|
||||
> **Note:** The [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) extension is included in the container so you can keep an eye on CPU/Memory in the status bar.
|
||||
|
||||
|
@ -16,53 +16,56 @@ This repository includes configuration for a development container for working w
|
|||
|
||||
![Image of Remote - Containers extension](https://microsoft.github.io/vscode-remote-release/images/remote-containers-extn.png)
|
||||
|
||||
> Note that the Remote - Containers extension requires the Visual Studio Code distribution of Code - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details.
|
||||
> **Note:** The Remote - Containers extension requires the Visual Studio Code distribution of Code - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details.
|
||||
|
||||
4. Press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> and select **Remote-Containers: Clone Repository in Container Volume...**.
|
||||
4. Press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> or <kbd>F1</kbd> and select **Remote-Containers: Clone Repository in Container Volume...**.
|
||||
|
||||
> **Tip:** While you can use your local source tree instead, operations like `yarn install` can be slow on macOS or using the Hyper-V engine on Windows. We recommend the "clone repository in container" approach instead since it uses "named volume" rather than the local filesystem.
|
||||
> **Tip:** While you can use your local source tree instead, operations like `yarn install` can be slow on macOS or when using the Hyper-V engine on Windows. We recommend the "clone repository in container" approach instead since it uses "named volume" rather than the local filesystem.
|
||||
|
||||
5. Type `https://github.com/microsoft/vscode` (or a branch or PR URL) in the input box and press <kbd>Enter</kbd>.
|
||||
|
||||
6. After the container is running, open a web browser and go to [http://localhost:6080](http://localhost:6080) or use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password.
|
||||
6. After the container is running, open a web browser and go to [http://localhost:6080](http://localhost:6080), or use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password.
|
||||
|
||||
Anything you start in VS Code or the integrated terminal will appear here.
|
||||
Anything you start in VS Code, or the integrated terminal, will appear here.
|
||||
|
||||
Next: **[Try it out!](#try-it)**
|
||||
|
||||
## Quick start - GitHub Codespaces
|
||||
|
||||
> **IMPORTANT:** You need to use a "Standard" sized codespace or larger (4-core, 8GB) since VS Code needs 6GB of RAM to compile. This is now the default for GitHub Codespaces, but do not downgrade to "Basic" unless you do not intend to compile.
|
||||
1. From the [microsoft/vscode GitHub repository](https://github.com/microsoft/vscode), click on the **Code** dropdown, select **Open with Codespaces**, and then click on **New codespace**. If prompted, select the **Standard** machine size (which is also the default).
|
||||
|
||||
1. From the [microsoft/vscode GitHub repository](https://github.com/microsoft/vscode), click on the **Code** dropdown, select **Open with Codespaces**, and the **New codespace**
|
||||
> **Note:** You will not see these options within GitHub if you are not in the Codespaces beta.
|
||||
|
||||
> Note that you will not see these options if you are not in the beta yet.
|
||||
2. After the codespace is up and running in your browser, press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> or <kbd>F1</kbd> and select **Ports: Focus on Ports View**.
|
||||
|
||||
2. After the codespace is up and running in your browser, press <kbd>F1</kbd> and select **Ports: Focus on Ports View**.
|
||||
3. You should see **VNC web client (6080)** under in the list of ports. Select the line and click on the globe icon to open it in a browser tab.
|
||||
|
||||
3. You should see port `6080` under **Forwarded Ports**. Select the line and click on the globe icon to open it in a browser tab.
|
||||
|
||||
> If you do not see port `6080`, press <kbd>F1</kbd>, select **Forward a Port** and enter port `6080`.
|
||||
> **Tip:** If you do not see the port, <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> or <kbd>F1</kbd>, select **Forward a Port** and enter port `6080`.
|
||||
|
||||
4. In the new tab, you should see noVNC. Click **Connect** and enter `vscode` as the password.
|
||||
|
||||
Anything you start in VS Code or the integrated terminal will appear here.
|
||||
Anything you start in VS Code, or the integrated terminal, will appear here.
|
||||
|
||||
Next: **[Try it out!](#try-it)**
|
||||
|
||||
### Using VS Code with GitHub Codespaces
|
||||
|
||||
You will likely see better performance when accessing the codespace you created from VS Code since you can use a[VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Here's how to do it.
|
||||
You may see improved VNC responsiveness when accessing a codespace from VS Code client since you can use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Here's how to do it.
|
||||
|
||||
1. [Create a codespace](#quick-start---github-codespaces) if you have not already.
|
||||
1. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the the [GitHub Codespaces extension](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces).
|
||||
|
||||
2. Set up [VS Code for use with GitHub Codespaces](https://docs.github.com/github/developing-online-with-codespaces/using-codespaces-in-visual-studio-code)
|
||||
> **Note:** The GitHub Codespaces extension requires the Visual Studio Code distribution of Code - OSS.
|
||||
|
||||
3. After the VS Code is up and running, press <kbd>F1</kbd>, choose **Codespaces: Connect to Codespace**, and select the codespace you created.
|
||||
2. After the VS Code is up and running, press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> or <kbd>F1</kbd>, choose **Codespaces: Create New Codespace**, and use the following settings:
|
||||
- `microsoft/vscode` for the repository.
|
||||
- Select any branch (e.g. **main**) - you select a different one later.
|
||||
- Choose **Standard** (4-core, 8GB) as the size.
|
||||
|
||||
4. After you've connected to the codespace, use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password.
|
||||
4. After you have connected to the codespace, you can use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password.
|
||||
|
||||
5. Anything you start in VS Code or the integrated terminal will appear here.
|
||||
> **Tip:** You may also need change your VNC client's **Picture Quaility** setting to **High** to get a full color desktop.
|
||||
|
||||
5. Anything you start in VS Code, or the integrated terminal, will appear here.
|
||||
|
||||
Next: **[Try it out!](#try-it)**
|
||||
|
||||
|
@ -70,20 +73,18 @@ Next: **[Try it out!](#try-it)**
|
|||
|
||||
This container uses the [Fluxbox](http://fluxbox.org/) window manager to keep things lean. **Right-click on the desktop** to see menu options. It works with GNOME and GTK applications, so other tools can be installed if needed.
|
||||
|
||||
Note you can also set the resolution from the command line by typing `set-resolution`.
|
||||
> **Note:** You can also set the resolution from the command line by typing `set-resolution`.
|
||||
|
||||
To start working with Code - OSS, follow these steps:
|
||||
|
||||
1. In your local VS Code, open a terminal (<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>\`</kbd>) and type the following commands:
|
||||
1. In your local VS Code client, open a terminal (<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>\`</kbd>) and type the following commands:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
bash scripts/code.sh
|
||||
```
|
||||
|
||||
Note that a previous run of `yarn install` will already be cached, so this step should simply pick up any recent differences.
|
||||
|
||||
2. After the build is complete, open a web browser or a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to the desktop environnement as described in the quick start and enter `vscode` as the password.
|
||||
2. After the build is complete, open a web browser or a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to the desktop environment as described in the quick start and enter `vscode` as the password.
|
||||
|
||||
3. You should now see Code - OSS!
|
||||
|
||||
|
@ -91,7 +92,7 @@ Next, let's try debugging.
|
|||
|
||||
1. Shut down Code - OSS by clicking the box in the upper right corner of the Code - OSS window through your browser or VNC viewer.
|
||||
|
||||
2. Go to your local VS Code client, and use Run / Debug view to launch the **VS Code** configuration. (Typically the default, so you can likely just press <kbd>F5</kbd>).
|
||||
2. Go to your local VS Code client, and use the **Run / Debug** view to launch the **VS Code** configuration. (Typically the default, so you can likely just press <kbd>F5</kbd>).
|
||||
|
||||
> **Note:** If launching times out, you can increase the value of `timeout` in the "VS Code", "Attach Main Process", "Attach Extension Host", and "Attach to Shared Process" configurations in [launch.json](../.vscode/launch.json). However, running `scripts/code.sh` first will set up Electron which will usually solve timeout issues.
|
||||
|
||||
|
|
|
@ -3,20 +3,26 @@
|
|||
|
||||
// Image contents: https://github.com/microsoft/vscode-dev-containers/blob/master/repository-containers/images/github.com/microsoft/vscode/.devcontainer/base.Dockerfile
|
||||
"image": "mcr.microsoft.com/vscode/devcontainers/repos/microsoft/vscode:branch-main",
|
||||
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/home/node/workspace/vscode,type=bind,consistency=cached",
|
||||
"workspaceFolder": "/home/node/workspace/vscode",
|
||||
"overrideCommand": false,
|
||||
"runArgs": [ "--init", "--security-opt", "seccomp=unconfined"],
|
||||
|
||||
"settings": {
|
||||
"terminal.integrated.shell.linux": "/bin/bash",
|
||||
"resmon.show.battery": false,
|
||||
"resmon.show.cpufreq": false
|
||||
},
|
||||
|
||||
// noVNC, VNC, debug ports
|
||||
"forwardPorts": [6080, 5901, 9222],
|
||||
// noVNC, VNC
|
||||
"forwardPorts": [6080, 5901],
|
||||
"portsAttributes": {
|
||||
"6080": {
|
||||
"label": "VNC web client (noVNC)",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"5901": {
|
||||
"label": "VNC TCP port",
|
||||
"onAutoForward": "silent"
|
||||
}
|
||||
},
|
||||
|
||||
"extensions": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
|
|
1
.github/classifier.json
vendored
1
.github/classifier.json
vendored
|
@ -7,6 +7,7 @@
|
|||
"labels": {
|
||||
"L10N": {"assign": []},
|
||||
"VIM": {"assign": []},
|
||||
"accessibility": { "assign": ["isidorn"]},
|
||||
"api": {"assign": ["jrieken"]},
|
||||
"api-finalization": {"assign": []},
|
||||
"api-proposal": {"assign": ["jrieken"]},
|
||||
|
|
196
.vscode/searches/api-todos.code-search
vendored
Normal file
196
.vscode/searches/api-todos.code-search
vendored
Normal file
|
@ -0,0 +1,196 @@
|
|||
# Query: todo@API
|
||||
# Flags: WordMatch OpenEditors
|
||||
# Including: */vscode.proposed.d.ts
|
||||
# ContextLines: 1
|
||||
|
||||
56 results - 1 file
|
||||
|
||||
src/vs/vscode.proposed.d.ts:
|
||||
1098 */
|
||||
1099: // todo@API should this be called `notebookType` or `notebookKind`
|
||||
1100 readonly viewType: string;
|
||||
|
||||
1159
|
||||
1160: // todo@API jsdoc
|
||||
1161 export class NotebookCellMetadata {
|
||||
|
||||
1165 */
|
||||
1166: // todo@API decouple from metadata? extract as dedicated field or inside an options object and leave metadata purely for extensions?
|
||||
1167 readonly inputCollapsed?: boolean;
|
||||
|
||||
1171 */
|
||||
1172: // todo@API decouple from metadata? extract as dedicated field or inside an options object and leave metadata purely for extensions?
|
||||
1173 readonly outputCollapsed?: boolean;
|
||||
|
||||
1215 */
|
||||
1216: // todo@API use duration instead of start/end?
|
||||
1217 readonly startTime?: number;
|
||||
|
||||
1224
|
||||
1225: // todo@API jsdoc
|
||||
1226: // todo@API remove this and use simple {}?
|
||||
1227 export class NotebookDocumentMetadata {
|
||||
|
||||
1290 */
|
||||
1291: // todo@API document which mime types are supported out of the box and
|
||||
1292 // which are considered secure
|
||||
|
||||
1365
|
||||
1366: //todo@API remove in favour of NotebookCellOutput#metadata
|
||||
1367 metadata?: { [key: string]: any };
|
||||
|
||||
1383 */
|
||||
1384: //todo@API - add sugar function to add more outputs
|
||||
1385 export class NotebookCellOutput {
|
||||
|
||||
1407
|
||||
1408: //todo@API have this OR NotebookCellOutputItem#metadata but not both? Preference for this.
|
||||
1409 metadata?: { [key: string]: any };
|
||||
|
||||
1471 * @param languageId The language identifier of the source value.
|
||||
1472: * @param outputs //TODO@API remove from ctor?
|
||||
1473: * @param metadata //TODO@API remove from ctor?
|
||||
1474: * @param executionSummary //TODO@API remove from ctor?
|
||||
1475 */
|
||||
|
||||
1558 */
|
||||
1559: // todo@API ...NotebookDocument... or just ...Notebook... just like...Cell... above
|
||||
1560 transientDocumentMetadata?: { [K in keyof NotebookDocumentMetadata]?: boolean };
|
||||
|
||||
1617
|
||||
1618: // todo@api jsdoc
|
||||
1619: // todo@api Inline unless we can come up with more (future) properties
|
||||
1620 export interface NotebookCellExecuteStartContext {
|
||||
|
||||
1627
|
||||
1628: // todo@api jsdoc
|
||||
1629: // todo@api Inline unless we can come up with more (future) properties
|
||||
1630 export interface NotebookCellExecuteEndContext {
|
||||
|
||||
1634 */
|
||||
1635: // todo@api undefined === false, so by default cells execution fails?
|
||||
1636 success?: boolean;
|
||||
|
||||
1672
|
||||
1673: // todo@API inline context object?
|
||||
1674 start(context?: NotebookCellExecuteStartContext): void;
|
||||
1675
|
||||
1676: // todo@API inline context object?
|
||||
1677 end(result?: NotebookCellExecuteEndContext): void;
|
||||
|
||||
1751 */
|
||||
1752: // todo@api rename to notebookType?
|
||||
1753 readonly viewType: string;
|
||||
|
||||
1790 */
|
||||
1791: // todo@API rename to supportsExecutionOrder, usesExecutionOrder
|
||||
1792 hasExecutionOrder?: boolean;
|
||||
|
||||
1849
|
||||
1850: // todo@API allow add, not remove
|
||||
1851 readonly rendererScripts: NotebookRendererScript[];
|
||||
|
||||
1870
|
||||
1871: //todo@API validate this works
|
||||
1872 asWebviewUri(localResource: Uri): Uri;
|
||||
|
||||
1899 */
|
||||
1900: // todo@api remove? use cell.notebook instead?
|
||||
1901 readonly document: NotebookDocument;
|
||||
|
||||
1910 */
|
||||
1911: // todo@API rename to state?
|
||||
1912 readonly executionState: NotebookCellExecutionState;
|
||||
|
||||
1930
|
||||
1931: // todo@api jsdoc
|
||||
1932 export class NotebookCellStatusBarItem {
|
||||
|
||||
1942
|
||||
1943: // todo@api jsdoc
|
||||
1944 export interface NotebookCellStatusBarItemProvider {
|
||||
|
||||
1952 */
|
||||
1953: // todo@API rename to ...NotebookCell...
|
||||
1954 provideCellStatusBarItems(cell: NotebookCell, token: CancellationToken): ProviderResult<NotebookCellStatusBarItem[]>;
|
||||
|
||||
1965 */
|
||||
1966: // todo@api rename to notebooks?
|
||||
1967: // todo@api what should be in this namespace? should notebookDocuments and friends be in the workspace namespace?
|
||||
1968 export namespace notebook {
|
||||
|
||||
2035 * @param handler
|
||||
2036: * @param rendererScripts todo@API should renderer scripts come later?
|
||||
2037 */
|
||||
|
||||
2042 */
|
||||
2043: // todo@API this is an event that is fired for a property that cells don't have and that makes me wonder
|
||||
2044 // how a correct consumer work, e.g the consumer could have been late and missed an event?
|
||||
|
||||
2046
|
||||
2047: // todo@api jsdoc
|
||||
2048 export function registerNotebookCellStatusBarItemProvider(notebookType: string, provider: NotebookCellStatusBarItemProvider): Disposable;
|
||||
|
||||
2118 readonly start: number;
|
||||
2119: // todo@API end? Use NotebookCellRange instead?
|
||||
2120 readonly deletedCount: number;
|
||||
2121: // todo@API removedCells, deletedCells?
|
||||
2122 readonly deletedItems: NotebookCell[];
|
||||
2123: // todo@API addedCells, insertedCells, newCells?
|
||||
2124 readonly items: NotebookCell[];
|
||||
|
||||
2183
|
||||
2184: // todo@API add onDidChangeNotebookCellOutputs
|
||||
2185 export const onDidChangeCellOutputs: Event<NotebookCellOutputsChangeEvent>;
|
||||
2186
|
||||
2187: // todo@API add onDidChangeNotebookCellMetadata
|
||||
2188 export const onDidChangeCellMetadata: Event<NotebookCellMetadataChangeEvent>;
|
||||
|
||||
2206
|
||||
2207: // todo@API add NotebookEdit-type which handles all these cases?
|
||||
2208 // export class NotebookEdit {
|
||||
|
||||
2223 export interface WorkspaceEdit {
|
||||
2224: // todo@API add NotebookEdit-type which handles all these cases?
|
||||
2225 replaceNotebookMetadata(uri: Uri, value: NotebookDocumentMetadata): void;
|
||||
|
||||
2285 */
|
||||
2286: // todo@API really needed? we didn't find a user here
|
||||
2287 export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument;
|
||||
|
||||
2339
|
||||
2340: // todo@API use openNotebookDOCUMENT to align with openCustomDocument etc?
|
||||
2341: // todo@API rename to NotebookDocumentContentProvider
|
||||
2342 export interface NotebookContentProvider {
|
||||
|
||||
2352
|
||||
2353: // todo@API use NotebookData instead
|
||||
2354 saveNotebook(document: NotebookDocument, token: CancellationToken): Thenable<void>;
|
||||
2355
|
||||
2356: // todo@API use NotebookData instead
|
||||
2357 saveNotebookAs(targetResource: Uri, document: NotebookDocument, token: CancellationToken): Thenable<void>;
|
||||
2358
|
||||
2359: // todo@API use NotebookData instead
|
||||
2360 backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, token: CancellationToken): Thenable<NotebookDocumentBackup>;
|
||||
|
||||
2364
|
||||
2365: // TODO@api use NotebookDocumentFilter instead of just notebookType:string?
|
||||
2366: // TODO@API options duplicates the more powerful variant on NotebookContentProvider
|
||||
2367 export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions): Disposable;
|
||||
|
||||
2647
|
||||
2648: // todo@API Split between Inlay- and OverlayHints (InlayHint are for a position, OverlayHints for a non-empty range)
|
||||
2649: // todo@API add "mini-markdown" for links and styles
|
||||
2650 // (done) remove description
|
||||
|
||||
2699
|
||||
2700: // todo@API make range first argument
|
||||
2701 constructor(text: string, position: Position, kind?: InlayHintKind);
|
||||
|
||||
3564
|
||||
3565: // TODO@API must be a class
|
||||
3566 export interface OpenEditorInfo {
|
||||
|
||||
3574
|
||||
3575: // todo@API proper event type
|
||||
3576 export const onDidChangeOpenEditors: Event<void>;
|
|
@ -57,10 +57,10 @@ VS Code includes a set of built-in extensions located in the [extensions](extens
|
|||
|
||||
## Development Container
|
||||
|
||||
This repository includes a Visual Studio Code Remote - Containers / Codespaces development container.
|
||||
This repository includes a Visual Studio Code Remote - Containers / GitHub Codespaces development container.
|
||||
|
||||
- For [Remote - Containers](https://aka.ms/vscode-remote/download/containers), use the **Remote-Containers: Open Repository in Container...** command which creates a Docker volume for better disk I/O on macOS and Windows.
|
||||
- For Codespaces, install the [Visual Studio Codespaces](https://aka.ms/vscs-ext-vscode) extension in VS Code, and use the **Codespaces: Create New Codespace** command.
|
||||
- For [Remote - Containers](https://aka.ms/vscode-remote/download/containers), use the **Remote-Containers: Clone Repository in Container Volume...** command which creates a Docker volume for better disk I/O on macOS and Windows.
|
||||
- For Codespaces, install the [Github Codespaces](https://marketplace.visualstudio.com/items?itemName=GitHub.codespacese) extension in VS Code, and use the **Codespaces: Create New Codespace** command.
|
||||
|
||||
Docker / the Codespace should have at least **4 Cores and 6 GB of RAM (8 GB recommended)** to run full build. See the [development container README](.devcontainer/README.md) for more information.
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
"p-limit": "^3.1.0",
|
||||
"plist": "^3.0.1",
|
||||
"source-map": "0.6.1",
|
||||
"typescript": "^4.3.0-dev.20210503",
|
||||
"typescript": "^4.4.0-dev.20210528",
|
||||
"vsce": "1.48.0",
|
||||
"vscode-universal": "deepak1556/universal#61454d96223b774c53cda10f72c2098c0ce02d58"
|
||||
},
|
||||
|
|
|
@ -1879,10 +1879,10 @@ typescript@^4.1.3:
|
|||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7"
|
||||
integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==
|
||||
|
||||
typescript@^4.3.0-dev.20210503:
|
||||
version "4.3.0-dev.20210503"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.0-dev.20210503.tgz#0a3eb480676effd4975beb5a2f097530ed53550a"
|
||||
integrity sha512-Gj3TQve5PLCZoPBy96Yp6Y3+jNLmms0i3ynhxEJAKgax7Fxui29/uG/DClbBtKz1peNhzXwikXVFFAV1BB/3mw==
|
||||
typescript@^4.4.0-dev.20210528:
|
||||
version "4.4.0-dev.20210528"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.0-dev.20210528.tgz#42453bc42e9d9df8ad0741c207c24d56407c0347"
|
||||
integrity sha512-ACV+mYKC+PhWUXIDUL6qmFClIdrKc20KRxDePt8bniCgkKQD4XRYKl7m02paxJM3nTMRdlfjs0ncaslA5BA1GA==
|
||||
|
||||
uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||
version "1.0.5"
|
||||
|
|
|
@ -566,7 +566,8 @@ export class AzureActiveDirectoryService {
|
|||
});
|
||||
|
||||
const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints');
|
||||
const endpoint = proxyEndpoints && proxyEndpoints['microsoft'] || `${loginEndpointUrl}${tenant}/oauth2/v2.0/token`;
|
||||
const endpointUrl = proxyEndpoints?.microsoft || loginEndpointUrl;
|
||||
const endpoint = `${endpointUrl}${tenant}/oauth2/v2.0/token`;
|
||||
|
||||
const result = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
|
@ -602,7 +603,10 @@ export class AzureActiveDirectoryService {
|
|||
|
||||
let result: Response;
|
||||
try {
|
||||
result = await fetch(`https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`, {
|
||||
const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints');
|
||||
const endpointUrl = proxyEndpoints?.microsoft || loginEndpointUrl;
|
||||
const endpoint = `${endpointUrl}${tenant}/oauth2/v2.0/token`;
|
||||
result = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
"selection.background": "#008000",
|
||||
"editor.selectionBackground": "#FFFFFF",
|
||||
"statusBarItem.remoteBackground": "#00000000",
|
||||
"ports.iconRunningProcessForeground": "#FFFFFF"
|
||||
"ports.iconRunningProcessForeground": "#FFFFFF",
|
||||
"editorWhitespace.foreground": "#7c7c7c"
|
||||
},
|
||||
"tokenColors": [
|
||||
{
|
||||
|
|
|
@ -475,10 +475,11 @@ suite('Notebook API tests', function () {
|
|||
const secondCell = vscode.window.activeNotebookEditor!.document.cellAt(1);
|
||||
assert.strictEqual(secondCell!.outputs.length, 1);
|
||||
assert.deepStrictEqual(secondCell!.outputs[0].metadata, { testOutputMetadata: true });
|
||||
assert.strictEqual(secondCell!.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(secondCell!.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.strictEqual(new TextDecoder().decode(secondCell!.outputs[0].outputs[0].data), 'Hello World');
|
||||
assert.deepStrictEqual(secondCell!.outputs[0].outputs[0].metadata, { testOutputItemMetadata: true });
|
||||
assert.strictEqual((<any>secondCell!.outputs[0]).outputs.length, 1); //todo@jrieken will FAIL once the backwards compatibility is gone
|
||||
assert.strictEqual(secondCell!.outputs[0].items.length, 1);
|
||||
assert.strictEqual(secondCell!.outputs[0].items[0].mime, 'text/plain');
|
||||
assert.strictEqual(new TextDecoder().decode(secondCell!.outputs[0].items[0].data), 'Hello World');
|
||||
assert.deepStrictEqual(secondCell!.outputs[0].items[0].metadata, { testOutputItemMetadata: true });
|
||||
assert.strictEqual(secondCell!.executionSummary?.executionOrder, 5);
|
||||
assert.strictEqual(secondCell!.executionSummary?.success, true);
|
||||
|
||||
|
@ -746,9 +747,9 @@ suite('Notebook API tests', function () {
|
|||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
await event;
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
|
||||
assert.strictEqual(cell.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'my output');
|
||||
assert.strictEqual(cell.outputs[0].items.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].items[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].items[0].data), 'my output');
|
||||
});
|
||||
|
||||
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs, async (event) => {
|
||||
|
@ -756,9 +757,9 @@ suite('Notebook API tests', function () {
|
|||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
await event;
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
|
||||
assert.strictEqual(cell.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'my second output');
|
||||
assert.strictEqual(cell.outputs[0].items.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].items[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].items[0].data), 'my second output');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -796,9 +797,9 @@ suite('Notebook API tests', function () {
|
|||
await vscode.commands.executeCommand('notebook.cell.cancelExecution');
|
||||
await event;
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
|
||||
assert.strictEqual(cell.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'Canceled');
|
||||
assert.strictEqual(cell.outputs[0].items.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].items[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].items[0].data), 'Canceled');
|
||||
});
|
||||
|
||||
cancelableKernel.controller.dispose();
|
||||
|
@ -838,9 +839,9 @@ suite('Notebook API tests', function () {
|
|||
await vscode.commands.executeCommand('notebook.cell.cancelExecution');
|
||||
await event;
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
|
||||
assert.strictEqual(cell.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'Interrupted');
|
||||
assert.strictEqual(cell.outputs[0].items.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].items[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].items[0].data), 'Interrupted');
|
||||
});
|
||||
|
||||
interruptableKernel.controller.dispose();
|
||||
|
@ -1184,7 +1185,7 @@ suite('Notebook API tests', function () {
|
|||
vscode.NotebookCellOutputItem.text('Some output', 'text/plain', undefined)
|
||||
])]);
|
||||
assert.strictEqual(cell.notebook.cellAt(0).outputs.length, 1);
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell.notebook.cellAt(0).outputs[0].outputs[0].data), 'Some output');
|
||||
assert.deepStrictEqual(new TextDecoder().decode(cell.notebook.cellAt(0).outputs[0].items[0].data), 'Some output');
|
||||
task.end({});
|
||||
called = true;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.57.0",
|
||||
"distro": "2f889e0f6ee745795ab935602189cac9672a478b",
|
||||
"distro": "7eccefdc46cee4d9a6b8f55bbc2e14045b19d382",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
@ -189,7 +189,7 @@
|
|||
"style-loader": "^1.0.0",
|
||||
"ts-loader": "^6.2.1",
|
||||
"tsec": "0.1.4",
|
||||
"typescript": "^4.3.0-dev.20210503",
|
||||
"typescript": "^4.4.0-dev.20210528",
|
||||
"typescript-formatter": "7.1.0",
|
||||
"underscore": "^1.12.1",
|
||||
"vinyl": "^2.0.0",
|
||||
|
|
|
@ -37,3 +37,10 @@
|
|||
line-height: 16px;
|
||||
margin-left: -4px;
|
||||
}
|
||||
|
||||
.monaco-dropdown-with-primary > .dropdown-action-container > .monaco-dropdown > .dropdown-label > .action-label {
|
||||
display: block;
|
||||
background-size: 16px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
|
|
@ -554,3 +554,37 @@ export function mapFind<T, R>(array: Iterable<T>, mapFn: (value: T) => R | undef
|
|||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like Math.min with a delegate, and returns the winning index
|
||||
*/
|
||||
export function minIndex<T>(array: readonly T[], fn: (value: T) => number): number {
|
||||
let minValue = Number.MAX_SAFE_INTEGER;
|
||||
let minIdx = 0;
|
||||
array.forEach((value, i) => {
|
||||
const thisValue = fn(value);
|
||||
if (thisValue < minValue) {
|
||||
minValue = thisValue;
|
||||
minIdx = i;
|
||||
}
|
||||
});
|
||||
|
||||
return minIdx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like Math.max with a delegate, and returns the winning index
|
||||
*/
|
||||
export function maxIndex<T>(array: readonly T[], fn: (value: T) => number): number {
|
||||
let minValue = Number.MIN_SAFE_INTEGER;
|
||||
let maxIdx = 0;
|
||||
array.forEach((value, i) => {
|
||||
const thisValue = fn(value);
|
||||
if (thisValue > minValue) {
|
||||
minValue = thisValue;
|
||||
maxIdx = i;
|
||||
}
|
||||
});
|
||||
|
||||
return maxIdx;
|
||||
}
|
||||
|
|
|
@ -295,4 +295,20 @@ suite('Arrays', () => {
|
|||
remove();
|
||||
assert.strictEqual(array.length, 0);
|
||||
});
|
||||
|
||||
test('minIndex', () => {
|
||||
const array = ['a', 'b', 'c'];
|
||||
assert.strictEqual(arrays.minIndex(array, value => array.indexOf(value)), 0);
|
||||
assert.strictEqual(arrays.minIndex(array, value => -array.indexOf(value)), 2);
|
||||
assert.strictEqual(arrays.minIndex(array, _value => 0), 0);
|
||||
assert.strictEqual(arrays.minIndex(array, value => value === 'b' ? 0 : 5), 1);
|
||||
});
|
||||
|
||||
test('maxIndex', () => {
|
||||
const array = ['a', 'b', 'c'];
|
||||
assert.strictEqual(arrays.maxIndex(array, value => array.indexOf(value)), 2);
|
||||
assert.strictEqual(arrays.maxIndex(array, value => -array.indexOf(value)), 0);
|
||||
assert.strictEqual(arrays.maxIndex(array, _value => 0), 0);
|
||||
assert.strictEqual(arrays.maxIndex(array, value => value === 'b' ? 5 : 0), 1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ import { GhostTextController, ShowNextInlineCompletionAction, ShowPreviousInline
|
|||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IViewZoneData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
import { ITextContentData, IViewZoneData } from 'vs/editor/browser/controller/mouseTarget';
|
||||
|
||||
export class InlineCompletionsHover implements IHoverPart {
|
||||
constructor(
|
||||
|
@ -51,16 +51,19 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan
|
|||
return new HoverForeignElementAnchor(1000, this, Range.fromPositions(viewZoneData.positionBefore || viewZoneData.position, viewZoneData.positionBefore || viewZoneData.position));
|
||||
}
|
||||
}
|
||||
if (mouseEvent.target.type === MouseTargetType.CONTENT_EMPTY) {
|
||||
if (mouseEvent.target.type === MouseTargetType.CONTENT_EMPTY && mouseEvent.target.range) {
|
||||
// handle the case where the mouse is over the empty portion of a line following ghost text
|
||||
if (mouseEvent.target.range && controller.shouldShowHoverAt(mouseEvent.target.range)) {
|
||||
if (controller.shouldShowHoverAt(mouseEvent.target.range)) {
|
||||
return new HoverForeignElementAnchor(1000, this, mouseEvent.target.range);
|
||||
}
|
||||
}
|
||||
if (mouseEvent.target.type === MouseTargetType.CONTENT_TEXT && mouseEvent.target.range && mouseEvent.target.detail) {
|
||||
// handle the case where the mouse is directly over ghost text
|
||||
const mightBeForeignElement = (<ITextContentData>mouseEvent.target.detail).mightBeForeignElement;
|
||||
if (mightBeForeignElement && controller.shouldShowHoverAt(mouseEvent.target.range)) {
|
||||
return new HoverForeignElementAnchor(1000, this, mouseEvent.target.range);
|
||||
}
|
||||
}
|
||||
// let mightBeForeignElement = false;
|
||||
// if (mouseEvent.target.type === MouseTargetType.CONTENT_TEXT && mouseEvent.target.detail) {
|
||||
// mightBeForeignElement = (<ITextContentData>mouseEvent.target.detail).mightBeForeignElement;
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IContextMenuProvider } from 'vs/base/browser/contextmenu';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
|
||||
import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
|
@ -21,6 +22,10 @@ export class DropdownWithPrimaryActionViewItem extends BaseActionViewItem {
|
|||
private _container: HTMLElement | null = null;
|
||||
private _dropdownContainer: HTMLElement | null = null;
|
||||
|
||||
get onDidChangeDropdownVisibility(): Event<boolean> {
|
||||
return this._dropdown.onDidChangeVisibility;
|
||||
}
|
||||
|
||||
constructor(
|
||||
primaryAction: MenuItemAction,
|
||||
dropdownAction: IAction,
|
||||
|
@ -33,10 +38,17 @@ export class DropdownWithPrimaryActionViewItem extends BaseActionViewItem {
|
|||
super(null, primaryAction);
|
||||
this._primaryAction = new MenuEntryActionViewItem(primaryAction, _keybindingService, _notificationService);
|
||||
this._dropdown = new DropdownMenuActionViewItem(dropdownAction, dropdownMenuActions, this._contextMenuProvider, {
|
||||
menuAsChild: true
|
||||
menuAsChild: true,
|
||||
classNames: ['codicon', 'codicon-chevron-down']
|
||||
});
|
||||
}
|
||||
|
||||
override setActionContext(newContext: unknown): void {
|
||||
super.setActionContext(newContext);
|
||||
this._primaryAction.setActionContext(newContext);
|
||||
this._dropdown.setActionContext(newContext);
|
||||
}
|
||||
|
||||
override render(container: HTMLElement): void {
|
||||
this._container = container;
|
||||
super.render(this._container);
|
||||
|
|
|
@ -131,7 +131,6 @@ export interface IWalkthrough {
|
|||
readonly title: string;
|
||||
readonly description: string;
|
||||
readonly steps: IWalkthroughStep[];
|
||||
readonly primary?: boolean;
|
||||
readonly when?: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,29 +32,32 @@ export interface WorkspaceTrustRequestOptions {
|
|||
readonly message?: string;
|
||||
}
|
||||
|
||||
export type WorkspaceTrustChangeEvent = Event<boolean>;
|
||||
export const IWorkspaceTrustManagementService = createDecorator<IWorkspaceTrustManagementService>('workspaceTrustManagementService');
|
||||
|
||||
export interface IWorkspaceTrustManagementService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
onDidChangeTrust: WorkspaceTrustChangeEvent;
|
||||
onDidChangeTrust: Event<boolean>;
|
||||
onDidChangeTrustedFolders: Event<void>;
|
||||
onDidInitiateWorkspaceTrustRequestOnStartup: Event<void>;
|
||||
get acceptsOutOfWorkspaceFiles(): boolean;
|
||||
set acceptsOutOfWorkspaceFiles(value: boolean);
|
||||
addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable;
|
||||
initializeWorkspaceTrust(): Promise<void>;
|
||||
|
||||
readonly workspaceTrustInitialized: Promise<void>;
|
||||
|
||||
acceptsOutOfWorkspaceFiles: boolean;
|
||||
isWorkpaceTrusted(): boolean;
|
||||
|
||||
canSetParentFolderTrust(): boolean;
|
||||
setParentFolderTrust(trusted: boolean): Promise<void>;
|
||||
canSetWorkspaceTrust(): Promise<boolean>;
|
||||
recalculateWorkspaceTrust(): Promise<void>;
|
||||
|
||||
canSetWorkspaceTrust(): boolean;
|
||||
setWorkspaceTrust(trusted: boolean): Promise<void>;
|
||||
getUriTrustInfo(folder: URI): Promise<IWorkspaceTrustUriInfo>;
|
||||
setUrisTrust(folders: URI[], trusted: boolean): Promise<void>;
|
||||
getTrustedFolders(): URI[];
|
||||
setTrustedFolders(folders: URI[]): Promise<void>;
|
||||
|
||||
getUriTrustInfo(uri: URI): Promise<IWorkspaceTrustUriInfo>;
|
||||
setUrisTrust(uri: URI[], trusted: boolean): Promise<void>;
|
||||
|
||||
getTrustedUris(): URI[];
|
||||
setTrustedUris(uris: URI[]): Promise<void>;
|
||||
|
||||
addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable;
|
||||
}
|
||||
|
||||
export const enum WorkspaceTrustUriResponse {
|
||||
|
|
245
src/vs/vscode.proposed.d.ts
vendored
245
src/vs/vscode.proposed.d.ts
vendored
|
@ -1090,11 +1090,12 @@ declare module 'vscode' {
|
|||
* saved on disk and therefore the `scheme` must be checked before trying to access the underlying file or siblings on disk.
|
||||
*
|
||||
* @see {@link FileSystemProvider}
|
||||
* @see {@link TextDocumentContentProvider}
|
||||
*/
|
||||
readonly uri: Uri;
|
||||
|
||||
// todo@API should we really expose this?
|
||||
/**
|
||||
* The type of notebook.
|
||||
*/
|
||||
// todo@API should this be called `notebookType` or `notebookKind`
|
||||
readonly viewType: string;
|
||||
|
||||
|
@ -1148,24 +1149,27 @@ declare module 'vscode' {
|
|||
getCells(range?: NotebookRange): NotebookCell[];
|
||||
|
||||
/**
|
||||
* Save the document. The saving will be handled by the corresponding content provider
|
||||
* Save the document. The saving will be handled by the corresponding {@link NotebookSerializer serializer}.
|
||||
*
|
||||
* @return A promise that will resolve to true when the document
|
||||
* has been saved. If the file was not dirty or the save failed,
|
||||
* will return false.
|
||||
* has been saved. Will return false if the file was not dirty or when save failed.
|
||||
*/
|
||||
save(): Thenable<boolean>;
|
||||
}
|
||||
|
||||
// todo@API jsdoc
|
||||
export class NotebookCellMetadata {
|
||||
|
||||
/**
|
||||
* Whether a code cell's editor is collapsed
|
||||
*/
|
||||
// todo@API decouple from metadata? extract as dedicated field or inside an options object and leave metadata purely for extensions?
|
||||
readonly inputCollapsed?: boolean;
|
||||
|
||||
/**
|
||||
* Whether a code cell's outputs are collapsed
|
||||
*/
|
||||
// todo@API decouple from metadata? extract as dedicated field or inside an options object and leave metadata purely for extensions?
|
||||
readonly outputCollapsed?: boolean;
|
||||
|
||||
/**
|
||||
|
@ -1191,13 +1195,35 @@ declare module 'vscode' {
|
|||
with(change: { inputCollapsed?: boolean | null, outputCollapsed?: boolean | null, [key: string]: any }): NotebookCellMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* The summary of a notebook cell execution.
|
||||
*/
|
||||
export interface NotebookCellExecutionSummary {
|
||||
|
||||
/**
|
||||
* The order in which the execution happened.
|
||||
*/
|
||||
readonly executionOrder?: number;
|
||||
|
||||
/**
|
||||
* If the exclusive finished successfully.
|
||||
*/
|
||||
readonly success?: boolean;
|
||||
|
||||
/**
|
||||
* The unix timestamp at which execution started.
|
||||
*/
|
||||
// todo@API use duration instead of start/end?
|
||||
readonly startTime?: number;
|
||||
|
||||
/**
|
||||
* The unix timestamp at which execution ended.
|
||||
*/
|
||||
readonly endTime?: number;
|
||||
}
|
||||
|
||||
// todo@API jsdoc
|
||||
// todo@API remove this and use simple {}?
|
||||
export class NotebookDocumentMetadata {
|
||||
/**
|
||||
* Additional attributes of the document metadata.
|
||||
|
@ -1220,7 +1246,7 @@ declare module 'vscode' {
|
|||
}
|
||||
|
||||
/**
|
||||
* A notebook range represents on ordered pair of two cell indicies.
|
||||
* A notebook range represents an ordered pair of two cell indicies.
|
||||
* It is guaranteed that start is less than or equal to end.
|
||||
*/
|
||||
export class NotebookRange {
|
||||
|
@ -1337,7 +1363,7 @@ declare module 'vscode' {
|
|||
*/
|
||||
data: Uint8Array;
|
||||
|
||||
//todo@API
|
||||
//todo@API remove in favour of NotebookCellOutput#metadata
|
||||
metadata?: { [key: string]: any };
|
||||
|
||||
/**
|
||||
|
@ -1377,14 +1403,26 @@ declare module 'vscode' {
|
|||
* ])
|
||||
* ```
|
||||
*/
|
||||
//todo@API rename to items
|
||||
outputs: NotebookCellOutputItem[];
|
||||
items: NotebookCellOutputItem[];
|
||||
|
||||
//todo@API
|
||||
//todo@API have this OR NotebookCellOutputItem#metadata but not both? Preference for this.
|
||||
metadata?: { [key: string]: any };
|
||||
|
||||
/**
|
||||
* Create new notebook output.
|
||||
*
|
||||
* @param outputs Notebook output items.
|
||||
* @param metadata Optional metadata.
|
||||
*/
|
||||
constructor(outputs: NotebookCellOutputItem[], metadata?: { [key: string]: any });
|
||||
|
||||
/**
|
||||
* Create new notebook output.
|
||||
*
|
||||
* @param outputs Notebook output items.
|
||||
* @param id Identifier of this output.
|
||||
* @param metadata Optional metadata.
|
||||
*/
|
||||
constructor(outputs: NotebookCellOutputItem[], id: string, metadata?: { [key: string]: any });
|
||||
}
|
||||
|
||||
|
@ -1431,9 +1469,9 @@ declare module 'vscode' {
|
|||
* @param kind The kind.
|
||||
* @param value The source value.
|
||||
* @param languageId The language identifier of the source value.
|
||||
* @param outputs //TODO@API remove ctor?
|
||||
* @param metadata //TODO@API remove ctor?
|
||||
* @param executionSummary //TODO@API remove ctor?
|
||||
* @param outputs //TODO@API remove from ctor?
|
||||
* @param metadata //TODO@API remove from ctor?
|
||||
* @param executionSummary //TODO@API remove from ctor?
|
||||
*/
|
||||
constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, executionSummary?: NotebookCellExecutionSummary);
|
||||
}
|
||||
|
@ -1495,6 +1533,12 @@ declare module 'vscode' {
|
|||
serializeNotebook(data: NotebookData, token: CancellationToken): Uint8Array | Thenable<Uint8Array>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notebook content options define what parts of a notebook are persisted. Note
|
||||
*
|
||||
* For instance, a notebook serializer can opt-out of saving outputs and in that case the editor doesn't mark a
|
||||
* notebooks as {@link NotebookDocument.isDirty dirty} when its output has changed.
|
||||
*/
|
||||
export interface NotebookDocumentContentOptions {
|
||||
/**
|
||||
* Controls if outputs change will trigger notebook document content change and if it will be used in the diff editor
|
||||
|
@ -1512,9 +1556,13 @@ declare module 'vscode' {
|
|||
* Controls if a document metadata property change will trigger notebook document content change and if it will be used in the diff editor
|
||||
* Default to false. If the content provider doesn't persisit a metadata property in the file document, it should be set to true.
|
||||
*/
|
||||
// todo@API ...NotebookDocument... or just ...Notebook... just like...Cell... above
|
||||
transientDocumentMetadata?: { [K in keyof NotebookDocumentMetadata]?: boolean };
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback that is invoked by the editor whenever cell execution has been triggered.
|
||||
*/
|
||||
export interface NotebookExecuteHandler {
|
||||
/**
|
||||
* @param cells The notebook cells to execute.
|
||||
|
@ -1524,12 +1572,28 @@ declare module 'vscode' {
|
|||
(this: NotebookController, cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController): void | Thenable<void>
|
||||
}
|
||||
|
||||
/**
|
||||
* Notebook controller affinity for notebook documents.
|
||||
*
|
||||
* @see {@link NotebookController.updateNotebookAffinity}
|
||||
*/
|
||||
export enum NotebookControllerAffinity {
|
||||
|
||||
/**
|
||||
* Default affinity.
|
||||
*/
|
||||
Default = 1,
|
||||
|
||||
/**
|
||||
* A controller is preferred for a notebook.
|
||||
*/
|
||||
Preferred = 2
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a script that is loaded into the notebook renderer before rendering output. This allows
|
||||
* to provide and share functionality for notebook markup and notebook output renderers.
|
||||
*/
|
||||
export class NotebookRendererScript {
|
||||
|
||||
/**
|
||||
|
@ -1551,6 +1615,8 @@ declare module 'vscode' {
|
|||
constructor(uri: Uri, provides?: string | string[]);
|
||||
}
|
||||
|
||||
// todo@api jsdoc
|
||||
// todo@api Inline unless we can come up with more (future) properties
|
||||
export interface NotebookCellExecuteStartContext {
|
||||
/**
|
||||
* The time that execution began, in milliseconds in the Unix epoch. Used to drive the clock
|
||||
|
@ -1559,11 +1625,14 @@ declare module 'vscode' {
|
|||
startTime?: number;
|
||||
}
|
||||
|
||||
// todo@api jsdoc
|
||||
// todo@api Inline unless we can come up with more (future) properties
|
||||
export interface NotebookCellExecuteEndContext {
|
||||
/**
|
||||
* If true, a green check is shown on the cell status bar.
|
||||
* If false, a red X is shown.
|
||||
*/
|
||||
// todo@api undefined === false, so by default cells execution fails?
|
||||
success?: boolean;
|
||||
|
||||
/**
|
||||
|
@ -1572,18 +1641,13 @@ declare module 'vscode' {
|
|||
endTime?: number;
|
||||
}
|
||||
|
||||
// todo@API jsdoc slightly outdated: kernel, notebook.createNotebookCellExecution
|
||||
/**
|
||||
* A NotebookCellExecution is how the kernel modifies a notebook cell as it is executing. When
|
||||
* {@link notebook.createNotebookCellExecution `createNotebookCellExecution`} is called, the cell
|
||||
* enters the Pending state. When `start()` is called on the execution task, it enters the Executing state. When
|
||||
* `end()` is called, it enters the Idle state. While in the Executing state, cell outputs can be
|
||||
* modified with the methods on the run task.
|
||||
* A NotebookCellExecution is how {@link NotebookController notebook controller} modify a notebook cell as
|
||||
* it is executing.
|
||||
*
|
||||
* All outputs methods operate on this NotebookCellExecution's cell by default. They optionally take
|
||||
* a cellIndex parameter that allows them to modify the outputs of other cells. `appendOutputItems` and
|
||||
* `replaceOutputItems` operate on the output with the given ID, which can be an output on any cell. They
|
||||
* all resolve once the output edit has been applied.
|
||||
* When a cell execution object is created, the cell enters the {@link NotebookCellExecutionState.Pending `Pending`} state.
|
||||
* When {@link NotebookCellExecution.start `start(...)`} is called on the execution task, it enters the {@link NotebookCellExecutionState.Executing `Executing`} state. When
|
||||
* {@link NotebookCellExecution.end `end(...)`} is called, it enters the {@link NotebookCellExecutionState.Idle `Idle`} state.
|
||||
*/
|
||||
export interface NotebookCellExecution {
|
||||
|
||||
|
@ -1601,9 +1665,6 @@ declare module 'vscode' {
|
|||
*/
|
||||
readonly token: CancellationToken;
|
||||
|
||||
//todo@API remove? use cell.notebook instead?
|
||||
readonly document: NotebookDocument;
|
||||
|
||||
/**
|
||||
* Set and unset the order of this cell execution.
|
||||
*/
|
||||
|
@ -1611,18 +1672,70 @@ declare module 'vscode' {
|
|||
|
||||
// todo@API inline context object?
|
||||
start(context?: NotebookCellExecuteStartContext): void;
|
||||
|
||||
// todo@API inline context object?
|
||||
end(result?: NotebookCellExecuteEndContext): void;
|
||||
|
||||
// todo@API use object instead of index? FF
|
||||
clearOutput(cellIndex?: number): Thenable<void>;
|
||||
appendOutput(out: NotebookCellOutput | NotebookCellOutput[], cellIndex?: number): Thenable<void>;
|
||||
replaceOutput(out: NotebookCellOutput | NotebookCellOutput[], cellIndex?: number): Thenable<void>;
|
||||
// todo@API use object instead of index?
|
||||
appendOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], outputId: string): Thenable<void>;
|
||||
replaceOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], outputId: string): Thenable<void>;
|
||||
/**
|
||||
* Clears the output of the cell that is executing or of another cell that is affected by this execution.
|
||||
*
|
||||
* @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of
|
||||
* this execution.
|
||||
* @return A thenable that resolves when the operation finished.
|
||||
*/
|
||||
clearOutput(cell?: NotebookCell): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Replace the output of the cell that is executing or of another cell that is affected by this execution.
|
||||
*
|
||||
* @param out Output that replaces the current output.
|
||||
* @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of
|
||||
* this execution.
|
||||
* @return A thenable that resolves when the operation finished.
|
||||
*/
|
||||
replaceOutput(out: NotebookCellOutput | NotebookCellOutput[], cell?: NotebookCell): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Append to the output of the cell that is executing or to another cell that is affected by this execution.
|
||||
*
|
||||
* @param out Output that is appended to the current output.
|
||||
* @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of
|
||||
* this execution.
|
||||
* @return A thenable that resolves when the operation finished.
|
||||
*/
|
||||
appendOutput(out: NotebookCellOutput | NotebookCellOutput[], cell?: NotebookCell): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Replace all output items of existing cell output.
|
||||
*
|
||||
* @param items Output items that replace the items of existing output.
|
||||
* @param output Output object or the identifier of one.
|
||||
* @return A thenable that resolves when the operation finished.
|
||||
*/
|
||||
replaceOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], output: NotebookCellOutput | string): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Append output items to existing cell output.
|
||||
*
|
||||
* @param items Output items that are append to existing output.
|
||||
* @param output Output object or the identifier of one.
|
||||
* @return A thenable that resolves when the operation finished.
|
||||
*/
|
||||
appendOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], output: NotebookCellOutput | string): Thenable<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A notebook controller represents an entity that can execute notebook cells. This is often referred to as a kernel.
|
||||
*
|
||||
* There can be multiple controllers and the editor will let users choose which controller to use for a certain notebook. The
|
||||
* {@link NotebookController.viewType `viewType`}-property defines for what kind of notebooks a controller is for and
|
||||
* the {@link NotebookController.updateNotebookAffinity `updateNotebookAffinity`}-function allows controllers to set a preference
|
||||
* for specific notebooks.
|
||||
*
|
||||
* When a cell is being run the editor will invoke the {@link NotebookController.executeHandler `executeHandler`} and a controller
|
||||
* is expected to create and finalize a {@link NotebookCellExecution notebook cell execution}. However, controllers are also free
|
||||
* to create executions by themselves.
|
||||
*/
|
||||
export interface NotebookController {
|
||||
|
||||
/**
|
||||
|
@ -1636,6 +1749,7 @@ declare module 'vscode' {
|
|||
/**
|
||||
* The notebook view type this controller is for.
|
||||
*/
|
||||
// todo@api rename to notebookType?
|
||||
readonly viewType: string;
|
||||
|
||||
/**
|
||||
|
@ -1689,8 +1803,8 @@ declare module 'vscode' {
|
|||
* By default cell execution is canceled via {@link NotebookCellExecution.token tokens}. Cancellation
|
||||
* tokens require that a controller can keep track of its execution so that it can cancel a specific execution at a later
|
||||
* point. Not all scenarios allow for that, eg. REPL-style controllers often work by interrupting whatever is currently
|
||||
* running. For those cases the {@link NotebookInterruptHandler interrupt handler} exists - it can be thought of as the
|
||||
* equivalent of `SIGINT` or `Control+C` in terminals.
|
||||
* running. For those cases the interrupt handler exists - it can be thought of as the equivalent of `SIGINT`
|
||||
* or `Control+C` in terminals.
|
||||
*
|
||||
* _Note_ that supporting {@link NotebookCellExecution.token cancellation tokens} is preferred and that interrupt handlers should
|
||||
* only be used when tokens cannot be supported.
|
||||
|
@ -1711,7 +1825,7 @@ declare module 'vscode' {
|
|||
|
||||
/**
|
||||
* A controller can set affinities for specific notebook documents. This allows a controller
|
||||
* to be more important for some notebooks.
|
||||
* to be presented more prominent for some notebooks.
|
||||
*
|
||||
* @param notebook The notebook for which a priority is set.
|
||||
* @param affinity A controller affinity
|
||||
|
@ -1721,8 +1835,11 @@ declare module 'vscode' {
|
|||
/**
|
||||
* Create a cell execution task.
|
||||
*
|
||||
* _Note_ that there can only be one execution per cell at a time and that an error is thrown if
|
||||
* a cell execution is created while another is still active.
|
||||
*
|
||||
* This should be used in response to the {@link NotebookController.executeHandler execution handler}
|
||||
* being calleed or when cell execution has been started else, e.g when a cell was already
|
||||
* being called or when cell execution has been started else, e.g when a cell was already
|
||||
* executing or when cell execution was triggered from another source.
|
||||
*
|
||||
* @param cell The notebook cell for which to create the execution.
|
||||
|
@ -1734,7 +1851,8 @@ declare module 'vscode' {
|
|||
readonly rendererScripts: NotebookRendererScript[];
|
||||
|
||||
/**
|
||||
* An event that fires when a renderer (see `preloads`) has send a message to the controller.
|
||||
* An event that fires when a {@link NotebookController.rendererScripts renderer script} has send a message to
|
||||
* the controller.
|
||||
*/
|
||||
readonly onDidReceiveMessage: Event<{ editor: NotebookEditor, message: any }>;
|
||||
|
||||
|
@ -1754,18 +1872,43 @@ declare module 'vscode' {
|
|||
asWebviewUri(localResource: Uri): Uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* The execution state of a notebook cell.
|
||||
*/
|
||||
export enum NotebookCellExecutionState {
|
||||
/**
|
||||
* The cell is idle.
|
||||
*/
|
||||
Idle = 1,
|
||||
/**
|
||||
* Execution for the cell is pending.
|
||||
*/
|
||||
Pending = 2,
|
||||
/**
|
||||
* The cell is currently executing.
|
||||
*/
|
||||
Executing = 3,
|
||||
}
|
||||
|
||||
/**
|
||||
* An event describing a cell execution state change.
|
||||
*/
|
||||
export interface NotebookCellExecutionStateChangeEvent {
|
||||
/**
|
||||
* The {@link NotebookDocument notebook document} for which the cell execution state has changed.
|
||||
*/
|
||||
// todo@api remove? use cell.notebook instead?
|
||||
readonly document: NotebookDocument;
|
||||
|
||||
/**
|
||||
* The {@link NotebookCell cell} for which the execution state has changed.
|
||||
*/
|
||||
readonly cell: NotebookCell;
|
||||
|
||||
/**
|
||||
* The new execution state of the cell.
|
||||
*/
|
||||
// todo@API rename to state?
|
||||
readonly executionState: NotebookCellExecutionState;
|
||||
}
|
||||
|
||||
|
@ -1785,6 +1928,7 @@ declare module 'vscode' {
|
|||
Right = 2
|
||||
}
|
||||
|
||||
// todo@api jsdoc
|
||||
export class NotebookCellStatusBarItem {
|
||||
text: string;
|
||||
alignment: NotebookCellStatusBarAlignment;
|
||||
|
@ -1796,6 +1940,7 @@ declare module 'vscode' {
|
|||
constructor(text: string, alignment: NotebookCellStatusBarAlignment, command?: string | Command, tooltip?: string, priority?: number, accessibilityInformation?: AccessibilityInformation);
|
||||
}
|
||||
|
||||
// todo@api jsdoc
|
||||
export interface NotebookCellStatusBarItemProvider {
|
||||
/**
|
||||
* Implement and fire this event to signal that statusbar items have changed. The provide method will be called again.
|
||||
|
@ -1805,9 +1950,21 @@ declare module 'vscode' {
|
|||
/**
|
||||
* The provider will be called when the cell scrolls into view, when its content, outputs, language, or metadata change, and when it changes execution state.
|
||||
*/
|
||||
// todo@API rename to ...NotebookCell...
|
||||
provideCellStatusBarItems(cell: NotebookCell, token: CancellationToken): ProviderResult<NotebookCellStatusBarItem[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespace for notebooks.
|
||||
*
|
||||
* The notebooks functionality is composed of three loosly coupled components:
|
||||
*
|
||||
* 1. {@link NotebookSerializer} enable the editor to open and save notebooks
|
||||
* 2. {@link NotebookController} own the execution of notebooks, e.g they create output from code cells.
|
||||
* 3. NotebookRenderer present notebook output in the editor. They run in a separate context.
|
||||
*/
|
||||
// todo@api rename to notebooks?
|
||||
// todo@api what should be in this namespace? should notebookDocuments and friends be in the workspace namespace?
|
||||
export namespace notebook {
|
||||
|
||||
/**
|
||||
|
@ -1876,14 +2033,18 @@ declare module 'vscode' {
|
|||
* @param viewType A notebook view type for which this controller is for.
|
||||
* @param label The label of the controller
|
||||
* @param handler
|
||||
* @param rendererScripts
|
||||
* @param rendererScripts todo@API should renderer scripts come later?
|
||||
*/
|
||||
export function createNotebookController(id: string, viewType: string, label: string, handler?: NotebookExecuteHandler, rendererScripts?: NotebookRendererScript[]): NotebookController;
|
||||
|
||||
// todo@API what is this used for?
|
||||
// todo@API qualify cell, ...NotebookCell...
|
||||
/**
|
||||
* An {@link Event} which fires when the execution state of a cell has changed.
|
||||
*/
|
||||
// todo@API this is an event that is fired for a property that cells don't have and that makes me wonder
|
||||
// how a correct consumer work, e.g the consumer could have been late and missed an event?
|
||||
export const onDidChangeNotebookCellExecutionState: Event<NotebookCellExecutionStateChangeEvent>;
|
||||
|
||||
// todo@api jsdoc
|
||||
export function registerNotebookCellStatusBarItemProvider(notebookType: string, provider: NotebookCellStatusBarItemProvider): Disposable;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import { isEqual, isEqualOrParent, toLocalResource } from 'vs/base/common/resour
|
|||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { FileOperation, FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
import { FileOperation, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
|
@ -445,16 +445,12 @@ class MainThreadCustomEditorModel extends ResourceWorkingCopy implements ICustom
|
|||
private readonly _onDidChangeContent: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
|
||||
|
||||
readonly onDidChangeEditable = Event.None;
|
||||
readonly onDidChangeReadonly = Event.None;
|
||||
|
||||
//#endregion
|
||||
|
||||
public isEditable(): boolean {
|
||||
return this._editable;
|
||||
}
|
||||
|
||||
public isOnReadonlyFileSystem(): boolean {
|
||||
return this.fileService.hasCapability(this.editorResource, FileSystemProviderCapabilities.Readonly);
|
||||
public isReadonly(): boolean {
|
||||
return !this._editable;
|
||||
}
|
||||
|
||||
public get viewType() {
|
||||
|
|
|
@ -44,7 +44,7 @@ export class ExtHostCell {
|
|||
};
|
||||
}
|
||||
|
||||
private _outputs: extHostTypes.NotebookCellOutput[];
|
||||
private _outputs: vscode.NotebookCellOutput[];
|
||||
private _metadata: extHostTypes.NotebookCellMetadata;
|
||||
private _previousResult: vscode.NotebookCellExecutionSummary | undefined;
|
||||
|
||||
|
@ -102,9 +102,9 @@ export class ExtHostCell {
|
|||
const output = this._outputs.find(op => op.id === outputId);
|
||||
if (output) {
|
||||
if (!append) {
|
||||
output.outputs.length = 0;
|
||||
output.items.length = 0;
|
||||
}
|
||||
output.outputs.push(...newItems);
|
||||
output.items.push(...newItems);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -381,38 +381,53 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
this.applyEdits([edit]);
|
||||
}
|
||||
|
||||
private cellIndexToHandle(cellIndex: number | undefined): number {
|
||||
if (typeof cellIndex !== 'number') {
|
||||
return this._cell.handle;
|
||||
private cellIndexToHandle(cellOrCellIndex: vscode.NotebookCell | number | undefined): number {
|
||||
let cell: ExtHostCell | undefined = this._cell;
|
||||
if (typeof cellOrCellIndex === 'number') {
|
||||
// todo@jrieken remove support for number shortly
|
||||
cell = this._document.getCellFromIndex(cellOrCellIndex);
|
||||
} else if (cellOrCellIndex) {
|
||||
cell = this._document.getCellFromApiCell(cellOrCellIndex);
|
||||
}
|
||||
const cell = this._document.getCellFromIndex(cellIndex);
|
||||
if (!cell) {
|
||||
throw new Error('INVALID cell index');
|
||||
throw new Error('INVALID cell');
|
||||
}
|
||||
return cell.handle;
|
||||
}
|
||||
|
||||
private validateAndConvertOutputs(items: vscode.NotebookCellOutput[]): IOutputDto[] {
|
||||
return items.map(output => {
|
||||
const newOutput = NotebookCellOutput.ensureUniqueMimeTypes(output.outputs, true);
|
||||
if (newOutput === output.outputs) {
|
||||
const newOutput = NotebookCellOutput.ensureUniqueMimeTypes(output.items, true);
|
||||
if (newOutput === output.items) {
|
||||
return extHostTypeConverters.NotebookCellOutput.from(output);
|
||||
}
|
||||
return extHostTypeConverters.NotebookCellOutput.from({
|
||||
outputs: newOutput,
|
||||
items: newOutput,
|
||||
id: output.id,
|
||||
metadata: output.metadata
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async updateOutputs(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell: vscode.NotebookCell | number | undefined, append: boolean): Promise<void> {
|
||||
const handle = this.cellIndexToHandle(cell);
|
||||
const outputDtos = this.validateAndConvertOutputs(asArray(outputs));
|
||||
return this.applyEditSoon({ editType: CellEditType.Output, handle, append, outputs: outputDtos });
|
||||
}
|
||||
|
||||
private async updateOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputOrOutputId: vscode.NotebookCellOutput | string, append: boolean): Promise<void> {
|
||||
if (NotebookCellOutput.isNotebookCellOutput(outputOrOutputId)) {
|
||||
outputOrOutputId = outputOrOutputId.id;
|
||||
}
|
||||
items = NotebookCellOutput.ensureUniqueMimeTypes(asArray(items), true);
|
||||
return this.applyEditSoon({ editType: CellEditType.OutputItems, items: items.map(extHostTypeConverters.NotebookCellOutputItem.from), outputId: outputOrOutputId, append });
|
||||
}
|
||||
|
||||
asApiObject(): vscode.NotebookCellExecution {
|
||||
const that = this;
|
||||
return Object.freeze(<vscode.NotebookCellExecution>{
|
||||
const result: vscode.NotebookCellExecution = {
|
||||
get token() { return that._tokenSource.token; },
|
||||
get document() { return that._document.apiNotebook; },
|
||||
get cell() { return that._cell.apiCell; },
|
||||
|
||||
get executionOrder() { return that._executionOrder; },
|
||||
set executionOrder(v: number | undefined) {
|
||||
that._executionOrder = v;
|
||||
|
@ -450,37 +465,32 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
});
|
||||
},
|
||||
|
||||
clearOutput(cellIndex?: number): Thenable<void> {
|
||||
clearOutput(cell?: vscode.NotebookCell | number): Thenable<void> {
|
||||
that.verifyStateForOutput();
|
||||
return this.replaceOutput([], cellIndex);
|
||||
return that.updateOutputs([], cell, false);
|
||||
},
|
||||
|
||||
async appendOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
|
||||
async appendOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell?: vscode.NotebookCell | number): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
const handle = that.cellIndexToHandle(cellIndex);
|
||||
const outputDtos = that.validateAndConvertOutputs(asArray(outputs));
|
||||
return that.applyEditSoon({ editType: CellEditType.Output, handle, append: true, outputs: outputDtos });
|
||||
return that.updateOutputs(outputs, cell, true);
|
||||
},
|
||||
|
||||
async replaceOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
|
||||
async replaceOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell?: vscode.NotebookCell | number): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
const handle = that.cellIndexToHandle(cellIndex);
|
||||
const outputDtos = that.validateAndConvertOutputs(asArray(outputs));
|
||||
return that.applyEditSoon({ editType: CellEditType.Output, handle, outputs: outputDtos });
|
||||
return that.updateOutputs(outputs, cell, false);
|
||||
},
|
||||
|
||||
async appendOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
|
||||
async appendOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], output: vscode.NotebookCellOutput | string): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
items = NotebookCellOutput.ensureUniqueMimeTypes(asArray(items), true);
|
||||
return that.applyEditSoon({ editType: CellEditType.OutputItems, append: true, items: items.map(extHostTypeConverters.NotebookCellOutputItem.from), outputId });
|
||||
that.updateOutputItems(items, output, true);
|
||||
},
|
||||
|
||||
async replaceOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
|
||||
async replaceOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], output: vscode.NotebookCellOutput | string): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
items = NotebookCellOutput.ensureUniqueMimeTypes(asArray(items), true);
|
||||
return that.applyEditSoon({ editType: CellEditType.OutputItems, items: items.map(extHostTypeConverters.NotebookCellOutputItem.from), outputId });
|
||||
that.updateOutputItems(items, output, false);
|
||||
}
|
||||
});
|
||||
};
|
||||
return Object.freeze(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1547,10 +1547,10 @@ export namespace NotebookCellOutputItem {
|
|||
}
|
||||
|
||||
export namespace NotebookCellOutput {
|
||||
export function from(output: types.NotebookCellOutput): notebooks.IOutputDto {
|
||||
export function from(output: vscode.NotebookCellOutput): notebooks.IOutputDto {
|
||||
return {
|
||||
outputId: output.id,
|
||||
outputs: output.outputs.map(NotebookCellOutputItem.from),
|
||||
outputs: output.items.map(NotebookCellOutputItem.from),
|
||||
metadata: output.metadata
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { IRelativePattern } from 'vs/base/common/glob';
|
|||
import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { ReadonlyMapView, ResourceMap } from 'vs/base/common/map';
|
||||
import { normalizeMimeType } from 'vs/base/common/mime';
|
||||
import { isStringArray } from 'vs/base/common/types';
|
||||
import { isArray, isStringArray } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
|
||||
|
@ -3075,11 +3075,11 @@ export class NotebookCellData {
|
|||
kind: NotebookCellKind;
|
||||
value: string;
|
||||
languageId: string;
|
||||
outputs?: NotebookCellOutput[];
|
||||
outputs?: vscode.NotebookCellOutput[];
|
||||
metadata?: NotebookCellMetadata;
|
||||
executionSummary?: vscode.NotebookCellExecutionSummary;
|
||||
|
||||
constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, executionSummary?: vscode.NotebookCellExecutionSummary) {
|
||||
constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: vscode.NotebookCellOutput[], metadata?: NotebookCellMetadata, executionSummary?: vscode.NotebookCellExecutionSummary) {
|
||||
this.kind = kind;
|
||||
this.value = value;
|
||||
this.languageId = languageId;
|
||||
|
@ -3164,6 +3164,16 @@ export class NotebookCellOutputItem {
|
|||
|
||||
export class NotebookCellOutput {
|
||||
|
||||
static isNotebookCellOutput(candidate: any): candidate is vscode.NotebookCellOutput {
|
||||
if (candidate instanceof NotebookCellOutput) {
|
||||
return true;
|
||||
}
|
||||
if (!candidate || typeof candidate !== 'object') {
|
||||
return false;
|
||||
}
|
||||
return typeof (<NotebookCellOutput>candidate).id === 'string' && isArray((<NotebookCellOutput>candidate).items);
|
||||
}
|
||||
|
||||
static ensureUniqueMimeTypes(items: NotebookCellOutputItem[], warn: boolean = false): NotebookCellOutputItem[] {
|
||||
const seen = new Set<string>();
|
||||
const removeIdx = new Set<number>();
|
||||
|
@ -3187,15 +3197,18 @@ export class NotebookCellOutput {
|
|||
}
|
||||
|
||||
id: string;
|
||||
outputs: NotebookCellOutputItem[];
|
||||
items: NotebookCellOutputItem[];
|
||||
metadata?: Record<string, any>;
|
||||
|
||||
get outputs() { return this.items; }
|
||||
set outputs(value) { this.items = value; }
|
||||
|
||||
constructor(
|
||||
outputs: NotebookCellOutputItem[],
|
||||
items: NotebookCellOutputItem[],
|
||||
idOrMetadata?: string | Record<string, any>,
|
||||
metadata?: Record<string, any>
|
||||
) {
|
||||
this.outputs = NotebookCellOutput.ensureUniqueMimeTypes(outputs, true);
|
||||
this.items = NotebookCellOutput.ensureUniqueMimeTypes(items, true);
|
||||
if (typeof idOrMetadata === 'string') {
|
||||
this.id = idOrMetadata;
|
||||
this.metadata = metadata;
|
||||
|
|
|
@ -60,7 +60,7 @@ import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/ur
|
|||
import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService';
|
||||
import { BrowserWindow } from 'vs/workbench/browser/window';
|
||||
import { ITimerService } from 'vs/workbench/services/timer/browser/timerService';
|
||||
import { RemoteWorkspaceTrustManagementService, WorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/common/workspaceTrust';
|
||||
import { WorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/common/workspaceTrust';
|
||||
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
@ -210,10 +210,7 @@ class BrowserMain extends Disposable {
|
|||
]);
|
||||
|
||||
// Workspace Trust Service
|
||||
const workspaceTrustManagementService = !environmentService.remoteAuthority ?
|
||||
new WorkspaceTrustManagementService(configurationService, storageService, uriIdentityService, environmentService, configurationService) :
|
||||
new RemoteWorkspaceTrustManagementService(configurationService, storageService, uriIdentityService, environmentService, configurationService, remoteAuthorityResolverService);
|
||||
await workspaceTrustManagementService.initializeWorkspaceTrust();
|
||||
const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, storageService, uriIdentityService, environmentService, configurationService, remoteAuthorityResolverService);
|
||||
serviceCollection.set(IWorkspaceTrustManagementService, workspaceTrustManagementService);
|
||||
|
||||
// Update workspace trust so that configuration is updated accordingly
|
||||
|
|
|
@ -16,9 +16,6 @@ import { dirname, isEqual } from 'vs/base/common/resources';
|
|||
*/
|
||||
export abstract class AbstractResourceEditorInput extends EditorInput implements IEditorInputWithPreferredResource {
|
||||
|
||||
private _preferredResource: URI;
|
||||
get preferredResource(): URI { return this._preferredResource; }
|
||||
|
||||
override get capabilities(): EditorInputCapabilities {
|
||||
let capabilities = EditorInputCapabilities.None;
|
||||
|
||||
|
@ -33,6 +30,9 @@ export abstract class AbstractResourceEditorInput extends EditorInput implements
|
|||
return capabilities;
|
||||
}
|
||||
|
||||
private _preferredResource: URI;
|
||||
get preferredResource(): URI { return this._preferredResource; }
|
||||
|
||||
constructor(
|
||||
readonly resource: URI,
|
||||
preferredResource: URI | undefined,
|
||||
|
@ -48,7 +48,7 @@ export abstract class AbstractResourceEditorInput extends EditorInput implements
|
|||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Clear label memoizer on certain events that have impact
|
||||
// Clear our labels on certain label related events
|
||||
this._register(this.labelService.onDidChangeFormatters(e => this.onLabelEvent(e.scheme)));
|
||||
this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onLabelEvent(e.scheme)));
|
||||
this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onLabelEvent(e.scheme)));
|
||||
|
|
|
@ -105,7 +105,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
override getTelemetryDescriptor(): { [key: string]: unknown } {
|
||||
const descriptor = this.primary.getTelemetryDescriptor();
|
||||
|
||||
return Object.assign(descriptor, super.getTelemetryDescriptor());
|
||||
return { ...descriptor, ...super.getTelemetryDescriptor() };
|
||||
}
|
||||
|
||||
override isDirty(): boolean {
|
||||
|
@ -129,7 +129,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
}
|
||||
|
||||
override matches(otherInput: unknown): boolean {
|
||||
if (otherInput === this) {
|
||||
if (super.matches(otherInput)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -165,6 +165,17 @@ export class TextResourceEditorInput extends AbstractTextResourceEditorInput imp
|
|||
}
|
||||
|
||||
override async resolve(): Promise<ITextEditorModel> {
|
||||
|
||||
// Unset preferred contents and mode after resolving
|
||||
// once to prevent these properties to stick. We still
|
||||
// want the user to change the language mode in the editor
|
||||
// and want to show updated contents (if any) in future
|
||||
// `resolve` calls.
|
||||
const preferredContents = this.preferredContents;
|
||||
const preferredMode = this.preferredMode;
|
||||
this.preferredContents = undefined;
|
||||
this.preferredMode = undefined;
|
||||
|
||||
if (!this.modelReference) {
|
||||
this.modelReference = this.textModelResolverService.createModelReference(this.resource);
|
||||
}
|
||||
|
@ -183,23 +194,15 @@ export class TextResourceEditorInput extends AbstractTextResourceEditorInput imp
|
|||
this.cachedModel = model;
|
||||
|
||||
// Set contents and mode if preferred
|
||||
if (typeof this.preferredContents === 'string' || typeof this.preferredMode === 'string') {
|
||||
model.updateTextEditorModel(typeof this.preferredContents === 'string' ? createTextBufferFactory(this.preferredContents) : undefined, this.preferredMode);
|
||||
if (typeof preferredContents === 'string' || typeof preferredMode === 'string') {
|
||||
model.updateTextEditorModel(typeof preferredContents === 'string' ? createTextBufferFactory(preferredContents) : undefined, preferredMode);
|
||||
}
|
||||
|
||||
// Unset preferred contents and mode after having applied
|
||||
// them once to prevent these properties to stick. We still
|
||||
// want the user to change the language mode in the editor
|
||||
// and want to show updated contents (if any) in future
|
||||
// `resolve` calls.
|
||||
this.preferredContents = undefined;
|
||||
this.preferredMode = undefined;
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
override matches(otherInput: unknown): boolean {
|
||||
if (otherInput === this) {
|
||||
if (super.matches(otherInput)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { IReference } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { dirname, isEqual } from 'vs/base/common/resources';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
|
||||
import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
|
@ -79,6 +79,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
|||
@IFileDialogService private readonly fileDialogService: IFileDialogService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IUndoRedoService private readonly undoRedoService: IUndoRedoService,
|
||||
@IFileService private readonly fileService: IFileService
|
||||
) {
|
||||
super(id, viewType, '', webview, webviewWorkbenchService);
|
||||
this._editorResource = resource;
|
||||
|
@ -86,6 +87,36 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
|||
this._defaultDirtyState = options.startsDirty;
|
||||
this._backupId = options.backupId;
|
||||
this._untitledDocumentData = options.untitledDocumentData;
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Clear our labels on certain label related events
|
||||
this._register(this.labelService.onDidChangeFormatters(e => this.onLabelEvent(e.scheme)));
|
||||
this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onLabelEvent(e.scheme)));
|
||||
this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onLabelEvent(e.scheme)));
|
||||
}
|
||||
|
||||
private onLabelEvent(scheme: string): void {
|
||||
if (scheme === this.resource.scheme) {
|
||||
this.updateLabel();
|
||||
}
|
||||
}
|
||||
|
||||
private updateLabel(): void {
|
||||
|
||||
// Clear any cached labels from before
|
||||
this._shortDescription = undefined;
|
||||
this._mediumDescription = undefined;
|
||||
this._longDescription = undefined;
|
||||
this._shortTitle = undefined;
|
||||
this._mediumTitle = undefined;
|
||||
this._longTitle = undefined;
|
||||
|
||||
// Trigger recompute of label
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
|
||||
public override get typeId(): string {
|
||||
|
@ -99,8 +130,14 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
|||
capabilities |= EditorInputCapabilities.Singleton;
|
||||
}
|
||||
|
||||
if (this._modelRef && !this._modelRef.object.isEditable()) {
|
||||
capabilities |= EditorInputCapabilities.Readonly;
|
||||
if (this._modelRef) {
|
||||
if (this._modelRef.object.isReadonly()) {
|
||||
capabilities |= EditorInputCapabilities.Readonly;
|
||||
}
|
||||
} else {
|
||||
if (this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly)) {
|
||||
capabilities |= EditorInputCapabilities.Readonly;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.resource.scheme === Schemas.untitled) {
|
||||
|
@ -115,56 +152,101 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
|||
return this.decorateLabel(name);
|
||||
}
|
||||
|
||||
override matches(other: IEditorInput): boolean {
|
||||
override getDescription(verbosity = Verbosity.MEDIUM): string | undefined {
|
||||
switch (verbosity) {
|
||||
case Verbosity.SHORT:
|
||||
return this.shortDescription;
|
||||
case Verbosity.LONG:
|
||||
return this.longDescription;
|
||||
case Verbosity.MEDIUM:
|
||||
default:
|
||||
return this.mediumDescription;
|
||||
}
|
||||
}
|
||||
|
||||
private _shortDescription: string | undefined = undefined;
|
||||
private get shortDescription(): string {
|
||||
if (typeof this._shortDescription !== 'string') {
|
||||
this._shortDescription = this.labelService.getUriBasenameLabel(dirname(this.resource));
|
||||
}
|
||||
|
||||
return this._shortDescription;
|
||||
}
|
||||
|
||||
private _mediumDescription: string | undefined = undefined;
|
||||
private get mediumDescription(): string {
|
||||
if (typeof this._mediumDescription !== 'string') {
|
||||
this._mediumDescription = this.labelService.getUriLabel(dirname(this.resource), { relative: true });
|
||||
}
|
||||
|
||||
return this._mediumDescription;
|
||||
}
|
||||
|
||||
private _longDescription: string | undefined = undefined;
|
||||
private get longDescription(): string {
|
||||
if (typeof this._longDescription !== 'string') {
|
||||
this._longDescription = this.labelService.getUriLabel(dirname(this.resource));
|
||||
}
|
||||
|
||||
return this._longDescription;
|
||||
}
|
||||
|
||||
private _shortTitle: string | undefined = undefined;
|
||||
private get shortTitle(): string {
|
||||
if (typeof this._shortTitle !== 'string') {
|
||||
this._shortTitle = this.getName();
|
||||
}
|
||||
|
||||
return this._shortTitle;
|
||||
}
|
||||
|
||||
private _mediumTitle: string | undefined = undefined;
|
||||
private get mediumTitle(): string {
|
||||
if (typeof this._mediumTitle !== 'string') {
|
||||
this._mediumTitle = this.labelService.getUriLabel(this.resource, { relative: true });
|
||||
}
|
||||
|
||||
return this._mediumTitle;
|
||||
}
|
||||
|
||||
private _longTitle: string | undefined = undefined;
|
||||
private get longTitle(): string {
|
||||
if (typeof this._longTitle !== 'string') {
|
||||
this._longTitle = this.labelService.getUriLabel(this.resource);
|
||||
}
|
||||
|
||||
return this._longTitle;
|
||||
}
|
||||
|
||||
override getTitle(verbosity?: Verbosity): string {
|
||||
switch (verbosity) {
|
||||
case Verbosity.SHORT:
|
||||
return this.decorateLabel(this.shortTitle);
|
||||
case Verbosity.LONG:
|
||||
return this.decorateLabel(this.longTitle);
|
||||
default:
|
||||
case Verbosity.MEDIUM:
|
||||
return this.decorateLabel(this.mediumTitle);
|
||||
}
|
||||
}
|
||||
|
||||
private decorateLabel(label: string): string {
|
||||
const readonly = this.hasCapability(EditorInputCapabilities.Readonly);
|
||||
const orphaned = !!this._modelRef?.object.isOrphaned();
|
||||
|
||||
return decorateFileEditorLabel(label, { orphaned, readonly });
|
||||
}
|
||||
|
||||
public override matches(other: IEditorInput): boolean {
|
||||
return this === other || (other instanceof CustomEditorInput
|
||||
&& this.viewType === other.viewType
|
||||
&& isEqual(this.resource, other.resource));
|
||||
}
|
||||
|
||||
override copy(): IEditorInput {
|
||||
public override copy(): IEditorInput {
|
||||
return CustomEditorInput.create(this.instantiationService, this.resource, this.viewType, this.group, this.webview.options);
|
||||
}
|
||||
|
||||
@memoize
|
||||
private get shortTitle(): string {
|
||||
return this.getName();
|
||||
}
|
||||
|
||||
@memoize
|
||||
private get mediumTitle(): string {
|
||||
return this.labelService.getUriLabel(this.resource, { relative: true });
|
||||
}
|
||||
|
||||
@memoize
|
||||
private get longTitle(): string {
|
||||
return this.labelService.getUriLabel(this.resource);
|
||||
}
|
||||
|
||||
public override getTitle(verbosity?: Verbosity): string {
|
||||
switch (verbosity) {
|
||||
case Verbosity.SHORT:
|
||||
return this.decorateLabel(this.shortTitle);
|
||||
default:
|
||||
case Verbosity.MEDIUM:
|
||||
return this.decorateLabel(this.mediumTitle);
|
||||
case Verbosity.LONG:
|
||||
return this.decorateLabel(this.longTitle);
|
||||
}
|
||||
}
|
||||
|
||||
private decorateLabel(label: string): string {
|
||||
const orphaned = !!this._modelRef?.object.isOrphaned();
|
||||
|
||||
const readonly = this._modelRef
|
||||
? this._modelRef.object.isEditable() && this._modelRef.object.isOnReadonlyFileSystem()
|
||||
: false;
|
||||
|
||||
return decorateFileEditorLabel(label, {
|
||||
orphaned,
|
||||
readonly
|
||||
});
|
||||
}
|
||||
|
||||
public override isDirty(): boolean {
|
||||
if (!this._modelRef) {
|
||||
return !!this._defaultDirtyState;
|
||||
|
@ -226,7 +308,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
|||
this._modelRef = this._register(assertIsDefined(await this.customEditorService.models.tryRetain(this.resource, this.viewType)));
|
||||
this._register(this._modelRef.object.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
|
||||
this._register(this._modelRef.object.onDidChangeOrphaned(() => this._onDidChangeLabel.fire()));
|
||||
this._register(this._modelRef.object.onDidChangeEditable(() => this._onDidChangeCapabilities.fire()));
|
||||
this._register(this._modelRef.object.onDidChangeReadonly(() => this._onDidChangeCapabilities.fire()));
|
||||
// If we're loading untitled file data we should ensure it's dirty
|
||||
if (this._untitledDocumentData) {
|
||||
this._defaultDirtyState = true;
|
||||
|
@ -239,7 +321,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
|||
return null;
|
||||
}
|
||||
|
||||
override rename(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined {
|
||||
public override rename(group: GroupIdentifier, newResource: URI): { editor: IEditorInput } | undefined {
|
||||
// See if we can keep using the same custom editor provider
|
||||
const editorInfo = this.customEditorService.getCustomEditor(this.viewType);
|
||||
if (editorInfo?.matches(newResource)) {
|
||||
|
@ -293,18 +375,18 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput {
|
|||
return other;
|
||||
}
|
||||
|
||||
get backupId(): string | undefined {
|
||||
public get backupId(): string | undefined {
|
||||
if (this._modelRef) {
|
||||
return this._modelRef.object.backupId;
|
||||
}
|
||||
return this._backupId;
|
||||
}
|
||||
|
||||
get untitledDocumentData(): VSBuffer | undefined {
|
||||
public get untitledDocumentData(): VSBuffer | undefined {
|
||||
return this._untitledDocumentData;
|
||||
}
|
||||
|
||||
override asResourceEditorInput(groupId: GroupIdentifier): IResourceEditorInput {
|
||||
public override asResourceEditorInput(groupId: GroupIdentifier): IResourceEditorInput {
|
||||
return {
|
||||
resource: this.resource,
|
||||
options: {
|
||||
|
|
|
@ -57,9 +57,8 @@ export interface ICustomEditorModel extends IDisposable {
|
|||
readonly resource: URI;
|
||||
readonly backupId: string | undefined;
|
||||
|
||||
isEditable(): boolean;
|
||||
readonly onDidChangeEditable: Event<void>;
|
||||
isOnReadonlyFileSystem(): boolean;
|
||||
isReadonly(): boolean;
|
||||
readonly onDidChangeReadonly: Event<void>;
|
||||
|
||||
isOrphaned(): boolean;
|
||||
readonly onDidChangeOrphaned: Event<void>;
|
||||
|
|
|
@ -8,7 +8,6 @@ import { Disposable, IReference } from 'vs/base/common/lifecycle';
|
|||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor';
|
||||
import { ICustomEditorModel } from 'vs/workbench/contrib/customEditor/common/customEditor';
|
||||
|
@ -33,15 +32,14 @@ export class CustomTextEditorModel extends Disposable implements ICustomEditorMo
|
|||
private readonly _onDidChangeOrphaned = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeOrphaned = this._onDidChangeOrphaned.event;
|
||||
|
||||
private readonly _onDidChangeEditable = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeEditable = this._onDidChangeEditable.event;
|
||||
private readonly _onDidChangeReadonly = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeReadonly = this._onDidChangeReadonly.event;
|
||||
|
||||
constructor(
|
||||
public readonly viewType: string,
|
||||
private readonly _resource: URI,
|
||||
private readonly _model: IReference<IResolvedTextEditorModel>,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService
|
||||
) {
|
||||
super();
|
||||
|
||||
|
@ -50,7 +48,7 @@ export class CustomTextEditorModel extends Disposable implements ICustomEditorMo
|
|||
this._textFileModel = this.textFileService.files.get(_resource);
|
||||
if (this._textFileModel) {
|
||||
this._register(this._textFileModel.onDidChangeOrphaned(() => this._onDidChangeOrphaned.fire()));
|
||||
this._register(this._textFileModel.onDidChangeReadonly(() => this._onDidChangeEditable.fire()));
|
||||
this._register(this._textFileModel.onDidChangeReadonly(() => this._onDidChangeReadonly.fire()));
|
||||
}
|
||||
|
||||
this._register(this.textFileService.files.onDidChangeDirty(e => {
|
||||
|
@ -65,12 +63,8 @@ export class CustomTextEditorModel extends Disposable implements ICustomEditorMo
|
|||
return this._resource;
|
||||
}
|
||||
|
||||
public isEditable(): boolean {
|
||||
return !this._model.object.isReadonly();
|
||||
}
|
||||
|
||||
public isOnReadonlyFileSystem(): boolean {
|
||||
return this._fileService.hasCapability(this._resource, FileSystemProviderCapabilities.Readonly);
|
||||
public isReadonly(): boolean {
|
||||
return this._model.object.isReadonly();
|
||||
}
|
||||
|
||||
public get backupId() {
|
||||
|
|
|
@ -308,22 +308,23 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements
|
|||
private async doResolveAsText(): Promise<ITextFileEditorModel | BinaryEditorModel> {
|
||||
try {
|
||||
|
||||
// Unset preferred contents after having applied it once
|
||||
// to prevent this property to stick. We still want future
|
||||
// `resolve` calls to fetch the contents from disk.
|
||||
const preferredContents = this.preferredContents;
|
||||
this.preferredContents = undefined;
|
||||
|
||||
// Resolve resource via text file service and only allow
|
||||
// to open binary files if we are instructed so
|
||||
await this.textFileService.files.resolve(this.resource, {
|
||||
mode: this.preferredMode,
|
||||
encoding: this.preferredEncoding,
|
||||
contents: typeof this.preferredContents === 'string' ? createTextBufferFactory(this.preferredContents) : undefined,
|
||||
contents: typeof preferredContents === 'string' ? createTextBufferFactory(preferredContents) : undefined,
|
||||
reload: { async: true }, // trigger a reload of the model if it exists already but do not wait to show the model
|
||||
allowBinary: this.forceOpenAs === ForceOpenAs.Text,
|
||||
reason: TextFileResolveReason.EDITOR
|
||||
});
|
||||
|
||||
// Unset preferred contents after having applied it once
|
||||
// to prevent this property to stick. We still want future
|
||||
// `resolve` calls to fetch the contents from disk.
|
||||
this.preferredContents = undefined;
|
||||
|
||||
// This is a bit ugly, because we first resolve the model and then resolve a model reference. the reason being that binary
|
||||
// or very large files do not resolve to a text file model but should be opened as binary files without text. First calling into
|
||||
// resolve() ensures we are not creating model references for these kind of resources.
|
||||
|
@ -402,7 +403,7 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements
|
|||
}
|
||||
|
||||
override matches(otherInput: unknown): boolean {
|
||||
if (otherInput === this) {
|
||||
if (super.matches(otherInput)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -460,7 +460,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
|||
id: CUT_FILE_ID,
|
||||
title: nls.localize('cut', "Cut")
|
||||
},
|
||||
when: ExplorerRootContext.toNegated()
|
||||
when: ContextKeyExpr.and(ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext)
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
|
||||
|
|
|
@ -831,6 +831,10 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
|
|||
if (!containsDragType(originalEvent, DataTransfers.FILES, CodeDataTransfers.FILES, DataTransfers.RESOURCES)) {
|
||||
return false;
|
||||
}
|
||||
if (isWeb) {
|
||||
// Drag and drop from vscode to web is not supported #115535
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Other-Tree DND
|
||||
|
|
|
@ -157,7 +157,7 @@ function removeEmbeddedSVGs(documentContent: string): string {
|
|||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7', 'h8', 'br', 'b', 'i', 'strong', 'em', 'a', 'pre', 'code', 'img', 'tt',
|
||||
'div', 'ins', 'del', 'sup', 'sub', 'p', 'ol', 'ul', 'table', 'thead', 'tbody', 'tfoot', 'blockquote', 'dl', 'dt',
|
||||
'dd', 'kbd', 'q', 'samp', 'var', 'hr', 'ruby', 'rt', 'rp', 'li', 'tr', 'td', 'th', 's', 'strike', 'summary', 'details',
|
||||
'caption', 'figure', 'figcaption', 'abbr', 'bdo', 'cite', 'dfn', 'mark', 'small', 'span', 'time', 'wbr', 'checkbox', 'checklist'
|
||||
'caption', 'figure', 'figcaption', 'abbr', 'bdo', 'cite', 'dfn', 'mark', 'small', 'span', 'time', 'wbr', 'checkbox', 'checklist', 'vertically-centered'
|
||||
],
|
||||
allowedAttributes: {
|
||||
'*': [
|
||||
|
|
|
@ -34,7 +34,7 @@ import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } f
|
|||
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { flatten, maxIndex, minIndex } from 'vs/base/common/arrays';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
|
||||
// Kernel Command
|
||||
|
@ -62,6 +62,8 @@ const DELETE_CELL_COMMAND_ID = 'notebook.cell.delete';
|
|||
const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution';
|
||||
const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow';
|
||||
const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow';
|
||||
const EXECUTE_CELL_AND_BELOW = 'notebook.cell.executeCellAndBelow';
|
||||
const EXECUTE_CELLS_ABOVE = 'notebook.cell.executeCellsAbove';
|
||||
const CLEAR_CELL_OUTPUTS_COMMAND_ID = 'notebook.cell.clearOutputs';
|
||||
const CENTER_ACTIVE_CELL = 'notebook.centerActiveCell';
|
||||
|
||||
|
@ -410,6 +412,74 @@ function parseMultiCellExecutionArgs(accessor: ServicesAccessor, ...args: any[])
|
|||
return context;
|
||||
}
|
||||
|
||||
registerAction2(class ExecuteAboveCells extends NotebookMultiCellAction<INotebookActionContext> {
|
||||
constructor() {
|
||||
super({
|
||||
id: EXECUTE_CELLS_ABOVE,
|
||||
precondition: executeCellCondition,
|
||||
title: localize('notebookActions.executeAbove', "Execute Above Cells"),
|
||||
menu: {
|
||||
id: MenuId.NotebookCellExecute,
|
||||
when: executeCellCondition
|
||||
},
|
||||
icon: icons.executeAboveIcon
|
||||
});
|
||||
}
|
||||
|
||||
parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookActionContext | undefined {
|
||||
return parseMultiCellExecutionArgs(accessor, ...args);
|
||||
}
|
||||
|
||||
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
|
||||
let endCellIdx: number | undefined = undefined;
|
||||
if (context.ui && context.cell) {
|
||||
endCellIdx = context.notebookEditor.viewModel.getCellIndex(context.cell);
|
||||
} else if (context.selectedCells) {
|
||||
endCellIdx = maxIndex(context.selectedCells, cell => context.notebookEditor.viewModel.getCellIndex(cell));
|
||||
}
|
||||
|
||||
if (typeof endCellIdx === 'number') {
|
||||
const range = { start: 0, end: endCellIdx };
|
||||
const cells = context.notebookEditor.viewModel.getCells(range);
|
||||
context.notebookEditor.executeNotebookCells(cells);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class ExecuteCellAndBelow extends NotebookMultiCellAction<INotebookActionContext> {
|
||||
constructor() {
|
||||
super({
|
||||
id: EXECUTE_CELL_AND_BELOW,
|
||||
precondition: executeCellCondition,
|
||||
title: localize('notebookActions.executeBelow', "Execute Cell and Below"),
|
||||
menu: {
|
||||
id: MenuId.NotebookCellExecute,
|
||||
when: executeCellCondition,
|
||||
},
|
||||
icon: icons.executeBelowIcon
|
||||
});
|
||||
}
|
||||
|
||||
parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookActionContext | undefined {
|
||||
return parseMultiCellExecutionArgs(accessor, ...args);
|
||||
}
|
||||
|
||||
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
|
||||
let startCellIdx: number | undefined = undefined;
|
||||
if (context.ui && context.cell) {
|
||||
startCellIdx = context.notebookEditor.viewModel.getCellIndex(context.cell);
|
||||
} else if (context.selectedCells) {
|
||||
startCellIdx = minIndex(context.selectedCells, cell => context.notebookEditor.viewModel.getCellIndex(cell));
|
||||
}
|
||||
|
||||
if (typeof startCellIdx === 'number') {
|
||||
const range = { start: startCellIdx, end: context.notebookEditor.viewModel.viewCells.length };
|
||||
const cells = context.notebookEditor.viewModel.getCells(range);
|
||||
context.notebookEditor.executeNotebookCells(cells);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class ExecuteCell extends NotebookMultiCellAction<INotebookActionContext> {
|
||||
constructor() {
|
||||
super({
|
||||
|
|
|
@ -498,7 +498,7 @@
|
|||
height: initial;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar .codicon {
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar .action-item:not(.monaco-dropdown-with-primary) .codicon {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
|
@ -508,6 +508,7 @@
|
|||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .run-button-container .monaco-toolbar,
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .run-button-container .monaco-toolbar,
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-run-toolbar-dropdown-active .run-button-container .monaco-toolbar,
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .run-button-container .monaco-toolbar {
|
||||
visibility: visible;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEd
|
|||
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl';
|
||||
import { CellKind, CellToolbarLocKey, CellToolbarVisibility, CellUri, DisplayOrderKey, UndoRedoPerCell, ExperimentalUseMarkdownRenderer, getCellUndoRedoComparisonKey, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookTextDiffEditorPreview, NotebookWorkingCopyTypeIdentifier, ShowCellStatusBarKey, CompactView, FocusIndicator, InsertToolbarPosition, GlobalToolbar, ConsolidatedOutputButton, ShowFoldingControls, DragAndDropEnabled, ShowCellStatusBarAfterExecuteKey, NotebookCellEditorOptionsCustomizations } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind, CellToolbarLocKey, CellToolbarVisibility, CellUri, DisplayOrderKey, UndoRedoPerCell, ExperimentalUseMarkdownRenderer, getCellUndoRedoComparisonKey, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookTextDiffEditorPreview, NotebookWorkingCopyTypeIdentifier, ShowCellStatusBarKey, CompactView, FocusIndicator, InsertToolbarPosition, GlobalToolbar, ConsolidatedOutputButton, ShowFoldingControls, DragAndDropEnabled, ShowCellStatusBarAfterExecuteKey, NotebookCellEditorOptionsCustomizations, ConsolidatedRunButton } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
|
||||
|
@ -694,6 +694,12 @@ configurationRegistry.registerConfiguration({
|
|||
default: true,
|
||||
tags: ['notebookLayout']
|
||||
},
|
||||
[ConsolidatedRunButton]: {
|
||||
description: nls.localize('notebook.consolidatedRunButton.description', "Control whether extra actions are shown in a dropdown next to the run button."),
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
tags: ['notebookLayout']
|
||||
},
|
||||
[NotebookCellEditorOptionsCustomizations]: editorOptionsCustomizationSchema
|
||||
}
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebo
|
|||
import { IReference } from 'vs/base/common/lifecycle';
|
||||
import { INotebookDiffEditorModel, IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
interface NotebookEditorInputOptions {
|
||||
startDirty?: boolean;
|
||||
|
@ -70,7 +71,8 @@ export class NotebookDiffEditorInput extends EditorInput {
|
|||
public readonly options: NotebookEditorInputOptions,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService,
|
||||
@IFileDialogService private readonly _fileDialogService: IFileDialogService
|
||||
@IFileDialogService private readonly _fileDialogService: IFileDialogService,
|
||||
@IFileService private readonly _fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
this._defaultDirtyState = !!options.startDirty;
|
||||
|
@ -87,6 +89,16 @@ export class NotebookDiffEditorInput extends EditorInput {
|
|||
capabilities |= EditorInputCapabilities.Untitled;
|
||||
}
|
||||
|
||||
if (this._modifiedTextModel) {
|
||||
if (this._modifiedTextModel.object.isReadonly()) {
|
||||
capabilities |= EditorInputCapabilities.Readonly;
|
||||
}
|
||||
} else {
|
||||
if (this._fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly)) {
|
||||
capabilities |= EditorInputCapabilities.Readonly;
|
||||
}
|
||||
}
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
|
@ -214,7 +226,7 @@ ${patterns}
|
|||
}
|
||||
|
||||
override matches(otherInput: unknown): boolean {
|
||||
if (this === otherInput) {
|
||||
if (super.matches(otherInput)) {
|
||||
return true;
|
||||
}
|
||||
if (otherInput instanceof NotebookDiffEditorInput) {
|
||||
|
|
|
@ -11,6 +11,8 @@ export const configureKernelIcon = registerIcon('notebook-kernel-configure', Cod
|
|||
export const selectKernelIcon = registerIcon('notebook-kernel-select', Codicon.serverEnvironment, localize('selectKernelIcon', 'Configure icon to select a kernel in notebook editors.'));
|
||||
|
||||
export const executeIcon = registerIcon('notebook-execute', Codicon.play, localize('executeIcon', 'Icon to execute in notebook editors.'));
|
||||
export const executeAboveIcon = registerIcon('notebook-execute-above', Codicon.runAbove, localize('executeAboveIcon', 'Icon to execute above cells in notebook editors.'));
|
||||
export const executeBelowIcon = registerIcon('notebook-execute-below', Codicon.runBelow, localize('executeBelowIcon', 'Icon to execute below cells in notebook editors.'));
|
||||
export const stopIcon = registerIcon('notebook-stop', Codicon.primitiveSquare, localize('stopIcon', 'Icon to stop an execution in notebook editors.'));
|
||||
export const deleteCellIcon = registerIcon('notebook-delete-cell', Codicon.trash, localize('deleteCellIcon', 'Icon to delete a cell in notebook editors.'));
|
||||
export const executeAllIcon = registerIcon('notebook-execute-all', Codicon.runAll, localize('executeAllIcon', 'Icon to execute all cells in notebook editors.'));
|
||||
|
|
|
@ -192,7 +192,7 @@ export interface ICreationRequestMessage {
|
|||
type: 'html';
|
||||
content:
|
||||
| { type: RenderOutputType.Html; htmlContent: string }
|
||||
| { type: RenderOutputType.Extension; outputId: string; valueBytes: Uint8Array, metadata: unknown; mimeType: string };
|
||||
| { type: RenderOutputType.Extension; outputId: string; valueBytes: Uint8Array, metadata: unknown; metadata2: unknown, mimeType: string };
|
||||
cellId: string;
|
||||
outputId: string;
|
||||
cellTop: number;
|
||||
|
@ -833,6 +833,16 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
|
|||
}
|
||||
|
||||
async createWebview(): Promise<void> {
|
||||
const baseUrl = this.asWebviewUri(dirname(this.documentUri), undefined);
|
||||
|
||||
// Python hasn't moved to use a preload to load require support yet.
|
||||
// For all other notebooks, we no longer want to include our loader.
|
||||
if (!this.documentUri.path.toLowerCase().endsWith('.ipynb')) {
|
||||
const htmlContent = this.generateContent('', baseUrl.toString());
|
||||
this._initialize(htmlContent);
|
||||
return;
|
||||
}
|
||||
|
||||
let coreDependencies = '';
|
||||
let resolveFunc: () => void;
|
||||
|
||||
|
@ -840,7 +850,6 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
|
|||
resolveFunc = resolve;
|
||||
});
|
||||
|
||||
const baseUrl = this.asWebviewUri(dirname(this.documentUri), undefined);
|
||||
|
||||
if (!isWeb) {
|
||||
const loaderUri = FileAccess.asFileUri('vs/loader.js', require);
|
||||
|
@ -889,7 +898,7 @@ var requirejs = (function() {
|
|||
await this._initalized;
|
||||
}
|
||||
|
||||
private async _initialize(content: string) {
|
||||
private _initialize(content: string) {
|
||||
if (!document.body.contains(this.element)) {
|
||||
throw new Error('Element is already detached from the DOM tree');
|
||||
}
|
||||
|
@ -1515,6 +1524,7 @@ var requirejs = (function() {
|
|||
mimeType: content.mimeType,
|
||||
valueBytes: new Uint8Array(outputDto?.valueBytes ?? []),
|
||||
metadata: outputDto?.metadata,
|
||||
metadata2: output.metadata
|
||||
},
|
||||
};
|
||||
} else {
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as Codicons from 'vs/base/common/codicons';
|
||||
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 * as aria from 'vs/base/browser/ui/aria/aria';
|
||||
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
|
||||
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import * as Codicons from 'vs/base/common/codicons';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { combinedDisposable, Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
@ -26,6 +26,7 @@ import { ITextModel } from 'vs/editor/common/model';
|
|||
import * as modes from 'vs/editor/common/modes';
|
||||
import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer';
|
||||
import { localize } from 'vs/nls';
|
||||
import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem';
|
||||
import { createActionViewItem, createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IMenu, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
@ -35,10 +36,15 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { syncing } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { DeleteCellAction, INotebookActionContext, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
|
||||
import { BaseCellRenderTemplate, CellEditState, CodeCellLayoutInfo, CodeCellRenderTemplate, EXPAND_CELL_INPUT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { errorStateIcon, successStateIcon, unfoldIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView';
|
||||
import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys';
|
||||
import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd';
|
||||
import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions';
|
||||
import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus';
|
||||
import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets';
|
||||
import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell';
|
||||
|
@ -46,12 +52,7 @@ import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view
|
|||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
|
||||
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
import { CellEditType, CellKind, NotebookCellMetadata, NotebookCellExecutionState, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { errorStateIcon, successStateIcon, unfoldIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
import { syncing } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions';
|
||||
import { CellEditType, CellKind, NotebookCellExecutionState, NotebookCellInternalMetadata, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
|
||||
|
||||
const $ = DOM.$;
|
||||
|
@ -94,11 +95,11 @@ abstract class AbstractCellRenderer {
|
|||
protected readonly notebookEditor: INotebookEditor,
|
||||
protected readonly contextMenuService: IContextMenuService,
|
||||
configurationService: IConfigurationService,
|
||||
private readonly keybindingService: IKeybindingService,
|
||||
private readonly notificationService: INotificationService,
|
||||
protected readonly keybindingService: IKeybindingService,
|
||||
protected readonly notificationService: INotificationService,
|
||||
protected readonly contextKeyServiceProvider: (container: HTMLElement) => IContextKeyService,
|
||||
language: string,
|
||||
protected dndController: CellDragAndDropController | undefined,
|
||||
protected dndController: CellDragAndDropController | undefined
|
||||
) {
|
||||
this.editorOptions = new CellEditorOptions(notebookEditor.notebookOptions, configurationService, language);
|
||||
this.cellMenus = this.instantiationService.createInstance(CellMenus);
|
||||
|
@ -700,7 +701,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
|
|||
const cellContainer = DOM.append(container, $('.cell.code'));
|
||||
const runButtonContainer = DOM.append(cellContainer, $('.run-button-container'));
|
||||
|
||||
const runToolbar = disposables.add(this.setupRunToolbar(runButtonContainer, contextKeyService, disposables));
|
||||
const runToolbar = this.setupRunToolbar(runButtonContainer, container, contextKeyService, disposables);
|
||||
const executionOrderLabel = DOM.append(cellContainer, $('div.execution-count-label'));
|
||||
|
||||
const editorPart = DOM.append(cellContainer, $('.cell-editor-part'));
|
||||
|
@ -831,18 +832,59 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
|
|||
return combinedDisposable(dragHandleListener, collapsedPartListener);
|
||||
}
|
||||
|
||||
private setupRunToolbar(runButtonContainer: HTMLElement, contextKeyService: IContextKeyService, disposables: DisposableStore): ToolBar {
|
||||
const runToolbar = this.createToolbar(runButtonContainer);
|
||||
const runMenu = this.cellMenus.getCellExecuteMenu(contextKeyService);
|
||||
const update = () => {
|
||||
const actions = this.getCellToolbarActions(runMenu);
|
||||
runToolbar.setActions(actions.primary, actions.secondary);
|
||||
};
|
||||
disposables.add(runMenu);
|
||||
disposables.add(runMenu.onDidChange(() => {
|
||||
update();
|
||||
private createRunCellToolbar(container: HTMLElement, cellContainer: HTMLElement, contextKeyService: IContextKeyService, disposables: DisposableStore): ToolBar {
|
||||
const actionViewItemDisposables = disposables.add(new DisposableStore());
|
||||
const dropdownAction = disposables.add(new Action('notebook.moreRunActions', localize('notebook.moreRunActionsLabel', "More..."), 'codicon-chevron-down', true));
|
||||
|
||||
const toolbar = disposables.add(new ToolBar(container, this.contextMenuService, {
|
||||
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
|
||||
actionViewItemProvider: _action => {
|
||||
actionViewItemDisposables.clear();
|
||||
|
||||
const actions = this.getCellToolbarActions(this.cellMenus.getCellExecuteMenu(contextKeyService));
|
||||
const primary = actions.primary[0];
|
||||
if (!(primary instanceof MenuItemAction)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!actions.secondary.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!this.notebookEditor.notebookOptions.getLayoutConfiguration().consolidatedRunButton) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const item = new DropdownWithPrimaryActionViewItem(
|
||||
primary,
|
||||
dropdownAction,
|
||||
actions.secondary,
|
||||
'notebook-cell-run-toolbar',
|
||||
this.contextMenuService,
|
||||
this.keybindingService,
|
||||
this.notificationService);
|
||||
actionViewItemDisposables.add(item.onDidChangeDropdownVisibility(visible => {
|
||||
cellContainer.classList.toggle('cell-run-toolbar-dropdown-active', visible);
|
||||
}));
|
||||
|
||||
return item;
|
||||
},
|
||||
renderDropdownAsChildElement: true
|
||||
}));
|
||||
update();
|
||||
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
private setupRunToolbar(runButtonContainer: HTMLElement, cellContainer: HTMLElement, contextKeyService: IContextKeyService, disposables: DisposableStore): ToolBar {
|
||||
const menu = this.cellMenus.getCellExecuteMenu(contextKeyService);
|
||||
const runToolbar = this.createRunCellToolbar(runButtonContainer, cellContainer, contextKeyService, disposables);
|
||||
const updateActions = () => {
|
||||
const actions = this.getCellToolbarActions(this.cellMenus.getCellExecuteMenu(contextKeyService));
|
||||
runToolbar.setActions(actions.primary);
|
||||
};
|
||||
updateActions();
|
||||
disposables.add(menu.onDidChange(updateActions));
|
||||
disposables.add(this.notebookEditor.notebookOptions.onDidChangeOptions(updateActions));
|
||||
return runToolbar;
|
||||
}
|
||||
|
||||
|
|
|
@ -464,6 +464,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
|
|||
|
||||
mime: string;
|
||||
metadata: unknown;
|
||||
metadata2: unknown;
|
||||
|
||||
text(): string;
|
||||
json(): any;
|
||||
|
@ -643,6 +644,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
|
|||
element: outputNode,
|
||||
mime: content.mimeType,
|
||||
metadata: content.metadata,
|
||||
metadata2: content.metadata2,
|
||||
data() {
|
||||
return content.valueBytes;
|
||||
},
|
||||
|
@ -1036,6 +1038,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
|
|||
element,
|
||||
mime: 'text/markdown',
|
||||
metadata: undefined,
|
||||
metadata2: undefined,
|
||||
outputId: undefined,
|
||||
text() { return content; },
|
||||
json() { return undefined; },
|
||||
|
|
|
@ -180,12 +180,12 @@ export interface IOutputItemDto {
|
|||
export interface IOutputDto {
|
||||
outputs: IOutputItemDto[];
|
||||
outputId: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface ICellOutput {
|
||||
outputs: IOutputItemDto[];
|
||||
// metadata?: NotebookCellOutsputMetadata;
|
||||
metadata?: Record<string, any>;
|
||||
outputId: string;
|
||||
onDidChangeData: Event<void>;
|
||||
replaceData(items: IOutputItemDto[]): void;
|
||||
|
@ -921,6 +921,7 @@ export const ConsolidatedOutputButton = 'notebook.consolidatedOutputButton';
|
|||
export const ShowFoldingControls = 'notebook.showFoldingControls';
|
||||
export const DragAndDropEnabled = 'notebook.dragAndDropEnabled';
|
||||
export const NotebookCellEditorOptionsCustomizations = 'notebook.editorOptionsCustomizations';
|
||||
export const ConsolidatedRunButton = 'notebook.consolidatedRunButton';
|
||||
|
||||
export const enum CellStatusbarAlignment {
|
||||
Left = 1,
|
||||
|
|
|
@ -210,7 +210,7 @@ export class NotebookEditorInput extends AbstractResourceEditorInput {
|
|||
}
|
||||
|
||||
override matches(otherInput: unknown): boolean {
|
||||
if (this === otherInput) {
|
||||
if (super.matches(otherInput)) {
|
||||
return true;
|
||||
}
|
||||
if (otherInput instanceof NotebookEditorInput) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { CellToolbarLocKey, CellToolbarVisibility, CompactView, ConsolidatedOutputButton, DragAndDropEnabled, ExperimentalInsertToolbarAlignment, FocusIndicator, GlobalToolbar, InsertToolbarPosition, NotebookCellEditorOptionsCustomizations, ShowCellStatusBarAfterExecuteKey, ShowCellStatusBarKey, ShowFoldingControls } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellToolbarLocKey, CellToolbarVisibility, CompactView, ConsolidatedOutputButton, ConsolidatedRunButton, DragAndDropEnabled, ExperimentalInsertToolbarAlignment, FocusIndicator, GlobalToolbar, InsertToolbarPosition, NotebookCellEditorOptionsCustomizations, ShowCellStatusBarAfterExecuteKey, ShowCellStatusBarKey, ShowFoldingControls } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
|
||||
const SCROLLABLE_ELEMENT_PADDING_TOP = 18;
|
||||
|
||||
|
@ -54,6 +54,7 @@ export interface NotebookLayoutConfiguration {
|
|||
insertToolbarAlignment: 'left' | 'center';
|
||||
globalToolbar: boolean;
|
||||
consolidatedOutputButton: boolean;
|
||||
consolidatedRunButton: boolean;
|
||||
showFoldingControls: 'always' | 'mouseover';
|
||||
dragAndDropEnabled: boolean;
|
||||
fontSize: number;
|
||||
|
@ -74,6 +75,7 @@ interface NotebookOptionsChangeEvent {
|
|||
globalToolbar?: boolean;
|
||||
showFoldingControls?: boolean;
|
||||
consolidatedOutputButton?: boolean;
|
||||
consolidatedRunButton?: boolean;
|
||||
dragAndDropEnabled?: boolean;
|
||||
fontSize?: boolean;
|
||||
editorOptionsCustomizations?: boolean;
|
||||
|
@ -110,6 +112,7 @@ export class NotebookOptions {
|
|||
const showCellStatusBarAfterExecute = this.configurationService.getValue<boolean>(ShowCellStatusBarAfterExecuteKey);
|
||||
const globalToolbar = this.configurationService.getValue<boolean | undefined>(GlobalToolbar) ?? false;
|
||||
const consolidatedOutputButton = this.configurationService.getValue<boolean | undefined>(ConsolidatedOutputButton) ?? true;
|
||||
const consolidatedRunButton = this.configurationService.getValue<boolean | undefined>(ConsolidatedRunButton) ?? false;
|
||||
const dragAndDropEnabled = this.configurationService.getValue<boolean | undefined>(DragAndDropEnabled) ?? true;
|
||||
const cellToolbarLocation = this.configurationService.getValue<string | { [key: string]: string }>(CellToolbarLocKey) ?? { 'default': 'right' };
|
||||
const cellToolbarInteraction = this.configurationService.getValue<string>(CellToolbarVisibility);
|
||||
|
@ -142,6 +145,7 @@ export class NotebookOptions {
|
|||
showCellStatusBarAfterExecute,
|
||||
globalToolbar,
|
||||
consolidatedOutputButton,
|
||||
consolidatedRunButton,
|
||||
dragAndDropEnabled,
|
||||
cellToolbarLocation,
|
||||
cellToolbarInteraction,
|
||||
|
@ -177,6 +181,7 @@ export class NotebookOptions {
|
|||
const insertToolbarAlignment = e.affectsConfiguration(ExperimentalInsertToolbarAlignment);
|
||||
const globalToolbar = e.affectsConfiguration(GlobalToolbar);
|
||||
const consolidatedOutputButton = e.affectsConfiguration(ConsolidatedOutputButton);
|
||||
const consolidatedRunButton = e.affectsConfiguration(ConsolidatedRunButton);
|
||||
const showFoldingControls = e.affectsConfiguration(ShowFoldingControls);
|
||||
const dragAndDropEnabled = e.affectsConfiguration(DragAndDropEnabled);
|
||||
const fontSize = e.affectsConfiguration('editor.fontSize');
|
||||
|
@ -193,6 +198,7 @@ export class NotebookOptions {
|
|||
&& !insertToolbarAlignment
|
||||
&& !globalToolbar
|
||||
&& !consolidatedOutputButton
|
||||
&& !consolidatedRunButton
|
||||
&& !showFoldingControls
|
||||
&& !dragAndDropEnabled
|
||||
&& !fontSize
|
||||
|
@ -246,6 +252,10 @@ export class NotebookOptions {
|
|||
configuration.consolidatedOutputButton = this.configurationService.getValue<boolean>(ConsolidatedOutputButton) ?? true;
|
||||
}
|
||||
|
||||
if (consolidatedRunButton) {
|
||||
configuration.consolidatedRunButton = this.configurationService.getValue<boolean>(ConsolidatedRunButton) ?? true;
|
||||
}
|
||||
|
||||
if (showFoldingControls) {
|
||||
configuration.showFoldingControls = this._computeShowFoldingControlsOption();
|
||||
}
|
||||
|
@ -277,6 +287,7 @@ export class NotebookOptions {
|
|||
globalToolbar,
|
||||
showFoldingControls,
|
||||
consolidatedOutputButton,
|
||||
consolidatedRunButton,
|
||||
dragAndDropEnabled,
|
||||
fontSize,
|
||||
editorOptionsCustomizations
|
||||
|
|
|
@ -214,7 +214,7 @@ export class SearchEditorInput extends EditorInput {
|
|||
}
|
||||
|
||||
override matches(other: unknown) {
|
||||
if (this === other) { return true; }
|
||||
if (super.matches(other)) { return true; }
|
||||
|
||||
if (other instanceof SearchEditorInput) {
|
||||
return !!(other.modelUri.fragment && other.modelUri.fragment === this.modelUri.fragment) || !!(other.backingUri && isEqual(other.backingUri, this.backingUri));
|
||||
|
|
|
@ -77,7 +77,7 @@ export class ReleaseNotesManager {
|
|||
this._webviewWorkbenchService.revealWebview(this._currentReleaseNotes, activeEditorPane ? activeEditorPane.group : this._editorGroupService.activeGroup, false);
|
||||
} else {
|
||||
this._currentReleaseNotes = this._webviewWorkbenchService.createWebview(
|
||||
'vs_code_release_notes',
|
||||
generateUuid(),
|
||||
'releaseNotes',
|
||||
title,
|
||||
{ group: ACTIVE_GROUP, preserveFocus: false },
|
||||
|
|
|
@ -83,6 +83,8 @@ namespace WebviewState {
|
|||
|
||||
export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
|
||||
|
||||
protected readonly _expectedServiceWorkerVersion = 2; // Keep this in sync with the version in service-worker.js
|
||||
|
||||
private _element: T | undefined;
|
||||
protected get element(): T | undefined { return this._element; }
|
||||
|
||||
|
@ -250,7 +252,7 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
|
|||
this.loadResource(entry.id, uri, entry.ifNoneMatch);
|
||||
} catch (e) {
|
||||
this._send('did-load-resource', {
|
||||
id,
|
||||
id: entry.id,
|
||||
status: 404,
|
||||
path: entry.path,
|
||||
});
|
||||
|
|
|
@ -25,6 +25,7 @@ const isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 &&
|
|||
|
||||
const searchParams = new URL(location.toString()).searchParams;
|
||||
const ID = searchParams.get('id');
|
||||
const expectedWorkerVersion = parseInt(searchParams.get('swVersion'));
|
||||
|
||||
/**
|
||||
* Use polling to track focus of main webview and iframes within the webview
|
||||
|
@ -210,16 +211,16 @@ const workerReady = new Promise(async (resolve, reject) => {
|
|||
return reject(new Error('Service Workers are not enabled in browser. Webviews will not work.'));
|
||||
}
|
||||
|
||||
const expectedWorkerVersion = 2;
|
||||
const swPath = `service-worker.js${self.location.search}`;
|
||||
|
||||
navigator.serviceWorker.register(`service-worker.js${self.location.search}`).then(
|
||||
navigator.serviceWorker.register(swPath).then(
|
||||
async registration => {
|
||||
await navigator.serviceWorker.ready;
|
||||
|
||||
/**
|
||||
* @param {MessageEvent} event
|
||||
*/
|
||||
const versionHandler = (event) => {
|
||||
const versionHandler = async (event) => {
|
||||
if (event.data.channel !== 'version') {
|
||||
return;
|
||||
}
|
||||
|
@ -228,10 +229,16 @@ const workerReady = new Promise(async (resolve, reject) => {
|
|||
if (event.data.version === expectedWorkerVersion) {
|
||||
return resolve();
|
||||
} else {
|
||||
// If we have the wrong version, try once to unregister and re-register
|
||||
return registration.update()
|
||||
console.log(`Found unexpected service worker version. Found: ${event.data.version}. Expected: ${expectedWorkerVersion}`);
|
||||
console.log(`Attempting to reload service worker`);
|
||||
|
||||
// If we have the wrong version, try once (and only once) to unregister and re-register
|
||||
// Note that `.update` doesn't seem to work desktop electron at the moment so we use
|
||||
// `unregister` and `register` here.
|
||||
return registration.unregister()
|
||||
.then(() => navigator.serviceWorker.register(swPath))
|
||||
.then(() => navigator.serviceWorker.ready)
|
||||
.finally(resolve);
|
||||
.finally(() => { resolve(); });
|
||||
}
|
||||
};
|
||||
navigator.serviceWorker.addEventListener('message', versionHandler);
|
||||
|
|
|
@ -100,6 +100,7 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
|||
protected initElement(extension: WebviewExtensionDescription | undefined, options: WebviewOptions, extraParams?: { [key: string]: string }) {
|
||||
const params: { [key: string]: string } = {
|
||||
id: this.id,
|
||||
swVersion: String(this._expectedServiceWorkerVersion),
|
||||
extensionId: extension?.id.value ?? '', // The extensionId and purpose in the URL are used for filtering in js-debug:
|
||||
...extraParams,
|
||||
'vscode-resource-base-authority': this.webviewRootResourceAuthority,
|
||||
|
|
|
@ -159,7 +159,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
|||
// and not the `vscode-file` URI because preload scripts are loaded
|
||||
// via node.js from the main side and only allow `file:` protocol
|
||||
this.element!.preload = FileAccess.asFileUri('./pre/electron-index.js', require).toString(true);
|
||||
this.element!.src = `${Schemas.vscodeWebview}://${this.id}/electron-browser-index.html?platform=electron&id=${this.id}&vscode-resource-base-authority=${encodeURIComponent(this.webviewRootResourceAuthority)}`;
|
||||
this.element!.src = `${Schemas.vscodeWebview}://${this.id}/electron-browser-index.html?platform=electron&id=${this.id}&vscode-resource-base-authority=${encodeURIComponent(this.webviewRootResourceAuthority)}&swVersion=${this._expectedServiceWorkerVersion}`;
|
||||
}
|
||||
|
||||
protected createElement(options: WebviewOptions) {
|
||||
|
|
|
@ -70,11 +70,7 @@ export class ElectronIframeWebview extends IFrameWebview {
|
|||
}
|
||||
|
||||
protected override get webviewContentEndpoint(): string {
|
||||
const endpoint = `${Schemas.vscodeWebview}://${this.id}`;
|
||||
if (endpoint[endpoint.length - 1] === '/') {
|
||||
return endpoint.slice(0, endpoint.length - 1);
|
||||
}
|
||||
return endpoint;
|
||||
return `${Schemas.vscodeWebview}://${this.id}`;
|
||||
}
|
||||
|
||||
protected override async doPostMessage(channel: string, data?: any): Promise<void> {
|
||||
|
|
|
@ -233,15 +233,26 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
|
|||
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
...workbenchConfigurationNodeBase,
|
||||
properties: {
|
||||
'workbench.welcomePage.walkthroughs.openOnInstall': {
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: localize('workbench.welcomePage.walkthroughs.openOnInstall', "When enabled, an extension's walkthrough will open upon install the extension. Walkthroughs are the items contributed the the 'Getting Started' section of the welcome page")
|
||||
}
|
||||
}
|
||||
});
|
||||
if (product.quality !== 'stable') {
|
||||
configurationRegistry.registerConfiguration({
|
||||
...workbenchConfigurationNodeBase,
|
||||
properties: {
|
||||
'workbench.welcomePage.experimental.extensionContributions': {
|
||||
'workbench.welcomePage.experimental.startEntryContributions': {
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: localize('workbench.welcomePage.experimental.extensionContributions', "When enabled, allow extensions to contribute items to the \"Getting Started\" and \"Start\" sections of the welcome page. Experimental, subject to breakage as api changes.")
|
||||
description: localize('workbench.welcomePage.experimental.startEntryContributions', "When enabled, allow extensions to contribute items to the \"Start\" section of the welcome page. Experimental, subject to breakage as api changes.")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -41,7 +41,7 @@ import { GettingStartedInput } from 'vs/workbench/contrib/welcome/gettingStarted
|
|||
import { GroupDirection, GroupsOrder, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { LinkedText } from 'vs/base/common/linkedText';
|
||||
import { ILink, LinkedText } from 'vs/base/common/linkedText';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
|
||||
import { Link } from 'vs/platform/opener/browser/link';
|
||||
|
@ -117,6 +117,8 @@ export class GettingStartedPage extends EditorPane {
|
|||
private stepsContent!: HTMLElement;
|
||||
private stepMediaComponent!: HTMLElement;
|
||||
|
||||
private layoutMarkdown: (() => void) | undefined;
|
||||
|
||||
private webviewID = generateUuid();
|
||||
|
||||
constructor(
|
||||
|
@ -469,6 +471,17 @@ export class GettingStartedPage extends EditorPane {
|
|||
mediaElement.setAttribute('alt', media.altText);
|
||||
this.updateMediaSourceForColorMode(mediaElement, media.path);
|
||||
|
||||
this.stepDisposables.add(addDisposableListener(mediaElement, 'click', () => {
|
||||
const hrefs = flatten(stepToExpand.description.map(lt => lt.nodes.filter((node): node is ILink => typeof node !== 'string').map(node => node.href)));
|
||||
if (hrefs.length === 1) {
|
||||
const href = hrefs[0];
|
||||
if (href.startsWith('http')) {
|
||||
this.telemetryService.publicLog2<GettingStartedActionEvent, GettingStartedActionClassification>('gettingStarted.ActionExecuted', { command: 'runStepAction', argument: href });
|
||||
this.openerService.open(href);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this.stepDisposables.add(this.themeService.onDidColorThemeChange(() => this.updateMediaSourceForColorMode(mediaElement, media.path)));
|
||||
|
||||
} else if (stepToExpand.media.type === 'markdown') {
|
||||
|
@ -488,7 +501,11 @@ export class GettingStartedPage extends EditorPane {
|
|||
|
||||
const postTrueKeysMessage = () => {
|
||||
const enabledContextKeys = serializedContextKeyExprs?.filter(expr => this.contextService.contextMatchesRules(ContextKeyExpr.deserialize(expr)));
|
||||
if (enabledContextKeys) { webview.postMessage({ enabledContextKeys }); }
|
||||
if (enabledContextKeys) {
|
||||
webview.postMessage({
|
||||
enabledContextKeys
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let isDisposed = false;
|
||||
|
@ -517,6 +534,10 @@ export class GettingStartedPage extends EditorPane {
|
|||
if (e.affectsSome(watchingKeys)) { postTrueKeysMessage(); }
|
||||
}));
|
||||
|
||||
this.layoutMarkdown = () => { webview.postMessage({ layout: true }); };
|
||||
this.stepDisposables.add({ dispose: () => this.layoutMarkdown = undefined });
|
||||
this.layoutMarkdown();
|
||||
|
||||
postTrueKeysMessage();
|
||||
|
||||
webview.onMessage(e => {
|
||||
|
@ -575,7 +596,8 @@ export class GettingStartedPage extends EditorPane {
|
|||
|
||||
private updateMediaSourceForColorMode(element: HTMLImageElement, sources: { hc: URI, dark: URI, light: URI }) {
|
||||
const themeType = this.themeService.getColorTheme().type;
|
||||
element.srcset = sources[themeType].toString(true).replace(/ /g, '%20') + ' 1.5x';
|
||||
const src = sources[themeType].toString(true).replace(/ /g, '%20');
|
||||
element.srcset = src.toLowerCase().endsWith('.svg') ? src : (src + ' 1.5x');
|
||||
}
|
||||
|
||||
private async renderMarkdown(path: URI, base: URI): Promise<string> {
|
||||
|
@ -618,6 +640,7 @@ export class GettingStartedPage extends EditorPane {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
checkbox.checked > img {
|
||||
|
@ -644,9 +667,14 @@ export class GettingStartedPage extends EditorPane {
|
|||
});
|
||||
|
||||
window.addEventListener('message', event => {
|
||||
document.querySelectorAll('.checked').forEach(element => element.classList.remove('checked'))
|
||||
for (const key of event.data.enabledContextKeys) {
|
||||
document.querySelectorAll('[checked-on="' + key + '"]').forEach(element => element.classList.add('checked'))
|
||||
document.querySelectorAll('vertically-centered').forEach(element => {
|
||||
element.style.marginTop = Math.max((document.body.scrollHeight - element.scrollHeight) * 2/5, 10) + 'px';
|
||||
})
|
||||
if (event.data.enabledContextKeys) {
|
||||
document.querySelectorAll('.checked').forEach(element => element.classList.remove('checked'))
|
||||
for (const key of event.data.enabledContextKeys) {
|
||||
document.querySelectorAll('[checked-on="' + key + '"]').forEach(element => element.classList.add('checked'))
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -928,6 +956,8 @@ export class GettingStartedPage extends EditorPane {
|
|||
this.gettingStartedList?.layout(size);
|
||||
this.recentlyOpenedList?.layout(size);
|
||||
|
||||
this.layoutMarkdown?.();
|
||||
|
||||
this.container.classList[size.height <= 600 ? 'add' : 'remove']('height-constrained');
|
||||
this.container.classList[size.width <= 400 ? 'add' : 'remove']('width-constrained');
|
||||
this.container.classList[size.width <= 800 ? 'add' : 'remove']('width-semi-constrained');
|
||||
|
|
|
@ -11,7 +11,7 @@ export const walkthroughsExtensionPoint = ExtensionsRegistry.registerExtensionPo
|
|||
extensionPoint: 'walkthroughs',
|
||||
jsonSchema: {
|
||||
doNotSuggest: true,
|
||||
description: localize('walkthroughs', "Contribute collections of steps to help users with your extension. Experimental, available in VS Code Insiders only."),
|
||||
description: localize('walkthroughs', "Contribute walkthroughs to help users getting started with your extension."),
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
|
@ -31,8 +31,7 @@ export const walkthroughsExtensionPoint = ExtensionsRegistry.registerExtensionPo
|
|||
description: localize('walkthroughs.description', "Description of walkthrough.")
|
||||
},
|
||||
primary: {
|
||||
type: 'boolean',
|
||||
description: localize('walkthroughs.primary', "if this is a `primary` walkthrough, hinting if it should be opened on install of the extension. The first `primary` walkthough with a `when` condition matching the current context may be opened by core on install of the extension.")
|
||||
deprecationMessage: localize('walkthroughs.primary.deprecated', "Deprecated. The first walkthrough with a satisfied when condition will be opened on install.")
|
||||
},
|
||||
when: {
|
||||
type: 'string',
|
||||
|
|
|
@ -366,45 +366,37 @@ export class GettingStartedService extends Disposable implements IGettingStarted
|
|||
}
|
||||
};
|
||||
|
||||
let sectionToOpen: string | undefined;
|
||||
|
||||
if (!(extension.contributes?.walkthroughs?.length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
if (!this.configurationService.getValue<boolean>('workbench.welcomePage.experimental.extensionContributions')) {
|
||||
console.warn('Extension', extension.identifier.value, 'contributes welcome page content but the welcome page extension contribution feature flag has not been set. Set `workbench.welcomePage.experimental.extensionContributions` to begin using this experimental feature.');
|
||||
return;
|
||||
}
|
||||
|
||||
extension.contributes.startEntries?.forEach(entry => {
|
||||
const entryID = extension.identifier.value + '#startEntry#' + idForStartEntry(entry);
|
||||
this.registerStartEntry({
|
||||
content: {
|
||||
type: 'startEntry',
|
||||
command: entry.command,
|
||||
},
|
||||
description: entry.description,
|
||||
title: entry.title,
|
||||
id: entryID,
|
||||
order: 0,
|
||||
when: ContextKeyExpr.deserialize(entry.when) ?? ContextKeyExpr.true(),
|
||||
icon: {
|
||||
type: 'image',
|
||||
path: extension.icon
|
||||
? FileAccess.asBrowserUri(joinPath(extension.extensionLocation, extension.icon)).toString(true)
|
||||
: DefaultIconPath
|
||||
}
|
||||
if (this.configurationService.getValue<boolean>('workbench.welcomePage.experimental.startEntryContributions') && this.productService.quality !== 'stable') {
|
||||
extension.contributes.startEntries?.forEach(entry => {
|
||||
const entryID = extension.identifier.value + '#startEntry#' + idForStartEntry(entry);
|
||||
this.registerStartEntry({
|
||||
content: {
|
||||
type: 'startEntry',
|
||||
command: entry.command,
|
||||
},
|
||||
description: entry.description,
|
||||
title: entry.title,
|
||||
id: entryID,
|
||||
order: 0,
|
||||
when: ContextKeyExpr.deserialize(entry.when) ?? ContextKeyExpr.true(),
|
||||
icon: {
|
||||
type: 'image',
|
||||
path: extension.icon
|
||||
? FileAccess.asBrowserUri(joinPath(extension.extensionLocation, extension.icon)).toString(true)
|
||||
: DefaultIconPath
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
await Promise.all(extension.contributes?.walkthroughs?.map(async walkthrough => {
|
||||
let sectionToOpen: string | undefined;
|
||||
let sectionToOpenIndex = Math.max();
|
||||
await Promise.all(extension.contributes?.walkthroughs?.map(async (walkthrough, index) => {
|
||||
const categoryID = extension.identifier.value + '#' + walkthrough.id;
|
||||
|
||||
const override = await Promise.race([
|
||||
|
@ -414,11 +406,13 @@ export class GettingStartedService extends Disposable implements IGettingStarted
|
|||
|
||||
if (
|
||||
this.sessionInstalledExtensions.has(extension.identifier.value)
|
||||
&& walkthrough.primary
|
||||
&& this.contextService.contextMatchesRules(ContextKeyExpr.deserialize(override ?? walkthrough.when) ?? ContextKeyExpr.true())
|
||||
) {
|
||||
this.sessionInstalledExtensions.delete(extension.identifier.value);
|
||||
sectionToOpen = categoryID;
|
||||
if (index < sectionToOpenIndex) {
|
||||
sectionToOpen = categoryID;
|
||||
sectionToOpenIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
const walkthoughDescriptior = {
|
||||
|
@ -433,7 +427,7 @@ export class GettingStartedService extends Disposable implements IGettingStarted
|
|||
? FileAccess.asBrowserUri(joinPath(extension.extensionLocation, extension.icon)).toString(true)
|
||||
: DefaultIconPath
|
||||
},
|
||||
when: ContextKeyExpr.deserialize(walkthrough.when) ?? ContextKeyExpr.true(),
|
||||
when: ContextKeyExpr.deserialize(override ?? walkthrough.when) ?? ContextKeyExpr.true(),
|
||||
} as const;
|
||||
|
||||
const steps = (walkthrough.steps ?? (walkthrough as any).tasks).map((step, index) => {
|
||||
|
@ -498,7 +492,7 @@ export class GettingStartedService extends Disposable implements IGettingStarted
|
|||
|
||||
this.triggerInstalledExtensionsRegistered();
|
||||
|
||||
if (sectionToOpen && this.configurationService.getValue<string>('workbench.welcomePage.experimental.extensionContributions') !== 'hide') {
|
||||
if (sectionToOpen && this.configurationService.getValue<string>('workbench.welcomePage.walkthroughs.openOnInstall')) {
|
||||
this.commandService.executeCommand('workbench.action.openWalkthrough', sectionToOpen);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/workbench/contrib/welcome/gettingStarted/common/media/example_markdown_media';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
@ -182,6 +183,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
|
|||
id: 'pickColorTheme',
|
||||
title: localize('gettingStarted.pickColor.title', "Choose the look you want"),
|
||||
description: localize('gettingStarted.pickColor.description', "The right color palette helps you focus on your code, is easy on your eyes, and is simply more fun to use.\n[Browse Color Themes](command:workbench.action.selectTheme)"),
|
||||
completionEvents: ['onSettingChanged:workbench.colorTheme'],
|
||||
media: { type: 'markdown', path: 'example_markdown_media', }
|
||||
},
|
||||
{
|
||||
|
@ -209,18 +211,17 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
|
|||
},
|
||||
},
|
||||
{
|
||||
id: 'settingsSync',
|
||||
title: localize('gettingStarted.settingsSync.title', "Sync your stuff across devices"),
|
||||
description: localize('gettingStarted.settingsSync.description', "Never lose the perfect VS Code setup! Settings Sync will back up and share settings, keybindings & extensions across several installations.\n[Enable Settings Sync](command:workbench.userDataSync.actions.turnOn)"),
|
||||
when: 'syncStatus != uninitialized',
|
||||
completionEvents: ['onEvent:sync-enabled'],
|
||||
id: 'workspaceTrust',
|
||||
title: localize('gettingStarted.workspaceTrust.title', "Safely browse and edit code"),
|
||||
description: localize('gettingStarted.workspaceTrust.description', "[Workspace Trust](https://github.com/microsoft/vscode-docs/blob/workspaceTrust/docs/editor/workspace-trust.md) lets you decide whether your project folders should **allow or restrict** automatic code execution __(required for extensions, debugging, etc)__.\nOpening a file/folder will prompt to grant trust. You can always [enable trust](command:toSide:workbench.action.manageTrustedDomain) later."),
|
||||
when: '!isWorkspaceTrusted && workspaceFolderCount == 0',
|
||||
media: {
|
||||
type: 'image', altText: 'The "Turn on Sync" entry in the settings gear menu.', path: {
|
||||
dark: 'dark/settingsSync.png',
|
||||
light: 'light/settingsSync.png',
|
||||
hc: 'hc/settingsSync.png',
|
||||
type: 'image', altText: 'Workspace Trust editor in Restricted mode and a primary button for switching to Trusted mode.', path: {
|
||||
dark: 'dark/workspaceTrust.svg',
|
||||
light: 'light/workspaceTrust.svg',
|
||||
hc: 'dark/workspaceTrust.svg',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'pickAFolderTask-Mac',
|
||||
|
@ -250,7 +251,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
|
|||
},
|
||||
{
|
||||
id: 'quickOpen',
|
||||
title: localize('gettingStarted.quickOpen.title', "Quickly navigate between your file"),
|
||||
title: localize('gettingStarted.quickOpen.title', "Quickly navigate between your files"),
|
||||
description: localize('gettingStarted.quickOpen.description', "Navigate between files in an instant with one keystroke. Tip: Open multiple files by pressing the right arrow key.\n[Quick Open a File](command:toSide:workbench.action.quickOpen)"),
|
||||
when: 'workspaceFolderCount != 0',
|
||||
media: {
|
||||
|
@ -323,6 +324,20 @@ export const walkthroughs: GettingStartedWalkthroughContent = [
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'settingsSync',
|
||||
title: localize('gettingStarted.settingsSync.title', "Sync your stuff across devices"),
|
||||
description: localize('gettingStarted.settingsSync.description', "Never lose the perfect VS Code setup! Settings Sync will back up and share settings, keybindings & extensions across several installations.\n[Enable Settings Sync](command:workbench.userDataSync.actions.turnOn)"),
|
||||
when: 'syncStatus != uninitialized',
|
||||
completionEvents: ['onEvent:sync-enabled'],
|
||||
media: {
|
||||
type: 'image', altText: 'The "Turn on Sync" entry in the settings gear menu.', path: {
|
||||
dark: 'dark/settingsSync.png',
|
||||
light: 'light/settingsSync.png',
|
||||
hc: 'hc/settingsSync.png',
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'videoTutorial',
|
||||
title: localize('gettingStarted.videoTutorial.title', "Lean back and learn"),
|
||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 30 KiB |
|
@ -7,41 +7,23 @@ import { escape } from 'vs/base/common/strings';
|
|||
import { localize } from 'vs/nls';
|
||||
|
||||
export default () => `
|
||||
<vertically-centered>
|
||||
<checklist>
|
||||
<checkbox on-checked="setTheme:Default Light+" checked-on="config.workbench.colorTheme == 'Default Light+'">
|
||||
<img width="200" src="./light.png"/>
|
||||
<img width="150" src="./light.png"/>
|
||||
${escape(localize('light', "Light"))}
|
||||
</checkbox>
|
||||
<checkbox on-checked="setTheme:Default Dark+" checked-on="config.workbench.colorTheme == 'Default Dark+'">
|
||||
<img width="200" src="./dark.png"/>
|
||||
<img width="150" src="./dark.png"/>
|
||||
${escape(localize('dark', "Dark"))}
|
||||
</checkbox>
|
||||
<checkbox on-checked="setTheme:Quiet Light" checked-on="config.workbench.colorTheme == 'Quiet Light'">
|
||||
<img width="200" src="./quiet-light.png"/>
|
||||
Quiet Light
|
||||
</checkbox>
|
||||
<checkbox on-checked="setTheme:Monokai" checked-on="config.workbench.colorTheme == 'Monokai'">
|
||||
<img width="200" src="./monokai.png"/>
|
||||
Monokai
|
||||
</checkbox>
|
||||
<checkbox on-checked="command:workbench.action.selectTheme" checked-on="false">
|
||||
<img width="200" src="./more.png"/>
|
||||
See More...
|
||||
<checkbox on-checked="setTheme:Default High Contrast" checked-on="config.workbench.colorTheme == 'Default High Contrast'">
|
||||
<img width="150" src="./monokai.png"/>
|
||||
${escape(localize('HighContrast', "High Contrast"))}
|
||||
</checkbox>
|
||||
</checklist>
|
||||
|
||||
\`\`\`js
|
||||
const btn = document.getElementById('btn')
|
||||
let count = 0
|
||||
function render() {
|
||||
btn.innerText = \`Count: \${count}\`
|
||||
}
|
||||
btn.addEventListener('click', () => {
|
||||
// Count from 1 to 10.
|
||||
if (count < 10) {
|
||||
count += 1
|
||||
render()
|
||||
}
|
||||
})
|
||||
\`\`\`
|
||||
<checkbox on-checked="command:workbench.action.selectTheme" checked-on="false">
|
||||
${escape(localize('seeMore', "See More Themes..."))}
|
||||
</checkbox>
|
||||
</vertically-centered>
|
||||
`;
|
||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 30 KiB |
|
@ -6,15 +6,36 @@
|
|||
import { localize } from 'vs/nls';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { WelcomePageContribution, WelcomePageAction, WelcomeInputSerializer, DEFAULT_STARTUP_EDITOR_CONFIG } from 'vs/workbench/contrib/welcome/page/browser/welcomePage';
|
||||
import { WelcomePageContribution, WelcomePageAction, WelcomeInputSerializer } from 'vs/workbench/contrib/welcome/page/browser/welcomePage';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions, CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IEditorInputFactoryRegistry, EditorExtensions } from 'vs/workbench/common/editor';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
||||
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
.registerConfiguration(DEFAULT_STARTUP_EDITOR_CONFIG);
|
||||
.registerConfiguration({
|
||||
...workbenchConfigurationNodeBase,
|
||||
'properties': {
|
||||
'workbench.startupEditor': {
|
||||
'scope': ConfigurationScope.RESOURCE,
|
||||
'type': 'string',
|
||||
'enum': ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench', 'gettingStarted', 'gettingStartedInEmptyWorkbench'],
|
||||
'enumDescriptions': [
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the legacy Welcome page."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file (only applies when opening an empty window)."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the legacy Welcome page when opening an empty workbench."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.gettingStarted' }, "Open the new Welcome Page with content to aid in getting started with VS Code and extensions."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.gettingStartedInEmptyWorkbench' }, "When opening an empty workbench, open the new Welcome Page with content to aid in getting started with VS Code and extensions.")
|
||||
],
|
||||
'default': 'welcomePage',
|
||||
'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none are restored from the previous session.")
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
|
||||
.registerWorkbenchContribution(WelcomePageContribution, LifecyclePhase.Restored);
|
||||
|
|
|
@ -10,7 +10,7 @@ import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/c
|
|||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { onUnexpectedError, isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
|
||||
|
@ -48,63 +48,15 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la
|
|||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { GettingStartedInput, gettingStartedInputTypeId } from 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedInput';
|
||||
import { welcomeButtonBackground, welcomeButtonHoverBackground, welcomePageBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePageColors';
|
||||
import { ITASExperimentService } from 'vs/workbench/services/experiment/common/experimentService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationNode, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
|
||||
|
||||
export const DEFAULT_STARTUP_EDITOR_CONFIG: IConfigurationNode = {
|
||||
...workbenchConfigurationNodeBase,
|
||||
'properties': {
|
||||
'workbench.startupEditor': {
|
||||
'scope': ConfigurationScope.RESOURCE,
|
||||
'type': 'string',
|
||||
'enum': ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench', 'gettingStarted'],
|
||||
'enumDescriptions': [...[
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the Welcome page."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file (only applies when opening an empty window)."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.gettingStarted' }, "Open the Getting Started page.")]
|
||||
],
|
||||
'default': 'welcomePage',
|
||||
'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none are restored from the previous session.")
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export const EXPERIMENTAL_GETTING_STARTED_STARTUP_EDITOR_CONFIG: IConfigurationNode = {
|
||||
...workbenchConfigurationNodeBase,
|
||||
'properties': {
|
||||
'workbench.startupEditor': {
|
||||
'scope': ConfigurationScope.RESOURCE,
|
||||
'type': 'string',
|
||||
'enum': ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench', 'gettingStarted'],
|
||||
'enumDescriptions': [...[
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the Welcome page."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file (only applies when opening an empty window)."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."),
|
||||
localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.gettingStarted' }, "Open the Getting Started page.")]
|
||||
],
|
||||
'default': 'gettingStarted',
|
||||
'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none are restored from the previous session.")
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const configurationKey = 'workbench.startupEditor';
|
||||
const oldConfigurationKey = 'workbench.welcome.enabled';
|
||||
const telemetryFrom = 'welcomePage';
|
||||
|
||||
export class WelcomePageContribution implements IWorkbenchContribution {
|
||||
private experimentManagementComplete: Promise<void>;
|
||||
private tasExperimentService: ITASExperimentService | undefined;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
|
@ -116,11 +68,7 @@ export class WelcomePageContribution implements IWorkbenchContribution {
|
|||
@ILifecycleService private readonly lifecycleService: ILifecycleService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@ICommandService private readonly commandService: ICommandService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@optional(ITASExperimentService) tasExperimentService: ITASExperimentService,
|
||||
) {
|
||||
this.tasExperimentService = tasExperimentService;
|
||||
|
||||
// Run immediately to minimize time spent waiting for exp service.
|
||||
this.experimentManagementComplete = this.manageDefaultValuesForGettingStartedExperiment().catch(onUnexpectedError);
|
||||
|
@ -133,43 +81,6 @@ export class WelcomePageContribution implements IWorkbenchContribution {
|
|||
if (this.lifecycleService.startupKind === StartupKind.ReloadedWindow || config.value !== config.defaultValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.configurationService.getValue('workbench.gettingStartedTreatmentOverride')) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).deregisterConfigurations([DEFAULT_STARTUP_EDITOR_CONFIG]);
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration(EXPERIMENTAL_GETTING_STARTED_STARTUP_EDITOR_CONFIG);
|
||||
}
|
||||
|
||||
let someValueReturned = false;
|
||||
type GettingStartedTreatmentData = { value: string; };
|
||||
type GettingStartedTreatmentClassification = { value: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; };
|
||||
|
||||
const tasUseGettingStartedAsDefault = this.tasExperimentService?.getTreatment<boolean>('StartupGettingStarted')
|
||||
.then(result => {
|
||||
this.logService.trace('StartupGettingStarted:', result);
|
||||
this.telemetryService.publicLog2<GettingStartedTreatmentData, GettingStartedTreatmentClassification>('gettingStartedTreatmentValue', { value: '' + !!result });
|
||||
someValueReturned = true;
|
||||
return result;
|
||||
})
|
||||
.catch(error => {
|
||||
this.logService.error('Recieved error when consulting experiment service for getting started experiment', error);
|
||||
this.telemetryService.publicLog2<GettingStartedTreatmentData, GettingStartedTreatmentClassification>('gettingStartedTreatmentValue', { value: 'err' });
|
||||
someValueReturned = true;
|
||||
return false;
|
||||
});
|
||||
|
||||
const fallback = new Promise<false>(c => setTimeout(() => c(false), 2000)).then(
|
||||
() => {
|
||||
if (!someValueReturned) { this.logService.trace('Unable to read getting started treatment data in time, falling back to welcome'); }
|
||||
someValueReturned = true;
|
||||
}
|
||||
);
|
||||
|
||||
const useGettingStartedAsDefault = !!await Promise.race([tasUseGettingStartedAsDefault, fallback]);
|
||||
if (useGettingStartedAsDefault) {
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).deregisterConfigurations([DEFAULT_STARTUP_EDITOR_CONFIG]);
|
||||
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration(EXPERIMENTAL_GETTING_STARTED_STARTUP_EDITOR_CONFIG);
|
||||
}
|
||||
}
|
||||
|
||||
private async run() {
|
||||
|
@ -225,7 +136,7 @@ export class WelcomePageContribution implements IWorkbenchContribution {
|
|||
await this.experimentManagementComplete;
|
||||
|
||||
const startupEditorSetting = this.configurationService.getValue(configurationKey);
|
||||
const startupEditorTypeID = startupEditorSetting === 'gettingStarted' ? gettingStartedInputTypeId : welcomeInputTypeId;
|
||||
const startupEditorTypeID = (startupEditorSetting === 'gettingStarted' || startupEditorSetting === 'gettingStartedInEmptyWorkbench') ? gettingStartedInputTypeId : welcomeInputTypeId;
|
||||
const editor = this.editorService.activeEditor;
|
||||
|
||||
// Ensure that the welcome editor won't get opened more than once
|
||||
|
@ -252,7 +163,10 @@ function isWelcomePageEnabled(configurationService: IConfigurationService, conte
|
|||
if (startupEditor.value === 'readme' && startupEditor.userValue !== 'readme') {
|
||||
console.error('Warning: `workbench.startupEditor: readme` setting ignored due to being set somewhere other than user settings');
|
||||
}
|
||||
return startupEditor.value === 'welcomePage' || startupEditor.value === 'gettingStarted' || startupEditor.userValue === 'readme' || startupEditor.value === 'welcomePageInEmptyWorkbench' && contextService.getWorkbenchState() === WorkbenchState.EMPTY;
|
||||
return startupEditor.value === 'welcomePage'
|
||||
|| startupEditor.value === 'gettingStarted'
|
||||
|| startupEditor.userValue === 'readme'
|
||||
|| (contextService.getWorkbenchState() === WorkbenchState.EMPTY && (startupEditor.value === 'welcomePageInEmptyWorkbench' || startupEditor.value === 'gettingStartedInEmptyWorkbench'));
|
||||
}
|
||||
|
||||
export class WelcomePageAction extends Action {
|
||||
|
|
|
@ -131,10 +131,7 @@ export class WalkThroughInput extends EditorInput {
|
|||
}
|
||||
|
||||
if (otherInput instanceof WalkThroughInput) {
|
||||
let otherResourceEditorInput = <WalkThroughInput>otherInput;
|
||||
|
||||
// Compare by properties
|
||||
return isEqual(otherResourceEditorInput.options.resource, this.options.resource);
|
||||
return isEqual(otherInput.options.resource, this.options.resource);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -45,6 +45,8 @@ import { IHostService } from 'vs/workbench/services/host/browser/host';
|
|||
import { IBannerItem, IBannerService } from 'vs/workbench/services/banner/browser/bannerService';
|
||||
import { isVirtualWorkspace } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
const BANNER_RESTRICTED_MODE = 'workbench.banner.restrictedMode';
|
||||
const BANNER_VIRTUAL_WORKSPACE = 'workbench.banner.virtualWorkspace';
|
||||
|
@ -77,6 +79,8 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben
|
|||
@IStatusbarService private readonly statusbarService: IStatusbarService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IWorkspaceTrustRequestService private readonly workspaceTrustRequestService: IWorkspaceTrustRequestService,
|
||||
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
@IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService,
|
||||
@IBannerService private readonly bannerService: IBannerService,
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
) {
|
||||
|
@ -84,25 +88,35 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben
|
|||
|
||||
this.statusbarEntryAccessor = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
|
||||
if (isWorkspaceTrustEnabled(configurationService)) {
|
||||
this.registerListeners();
|
||||
this.createStatusbarEntry();
|
||||
(async () => {
|
||||
|
||||
// Set empty workspace trust state
|
||||
this.setEmptyWorkspaceTrustState();
|
||||
await this.workspaceTrustManagementService.workspaceTrustInitialized;
|
||||
|
||||
// Show modal dialog
|
||||
if (this.hostService.hasFocus) {
|
||||
this.showModalOnStart();
|
||||
} else {
|
||||
const focusDisposable = this.hostService.onDidChangeFocus(focused => {
|
||||
if (focused) {
|
||||
focusDisposable.dispose();
|
||||
this.showModalOnStart();
|
||||
}
|
||||
});
|
||||
// Workaround until isTrusted from resolver is available pre-resolution
|
||||
if (this.workbenchEnvironmentService.remoteAuthority) {
|
||||
await this.remoteAuthorityResolverService.resolveAuthority(this.workbenchEnvironmentService.remoteAuthority);
|
||||
}
|
||||
}
|
||||
|
||||
if (isWorkspaceTrustEnabled(configurationService)) {
|
||||
this.registerListeners();
|
||||
this.createStatusbarEntry();
|
||||
|
||||
// Set empty workspace trust state
|
||||
this.setEmptyWorkspaceTrustState();
|
||||
|
||||
// Show modal dialog
|
||||
if (this.hostService.hasFocus) {
|
||||
this.showModalOnStart();
|
||||
} else {
|
||||
const focusDisposable = this.hostService.onDidChangeFocus(focused => {
|
||||
if (focused) {
|
||||
focusDisposable.dispose();
|
||||
this.showModalOnStart();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
private get startupPromptSetting(): 'always' | 'once' | 'never' {
|
||||
|
@ -168,7 +182,7 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben
|
|||
}
|
||||
|
||||
// Don't show modal prompt if workspace trust cannot be changed
|
||||
if (!(await this.workspaceTrustManagementService.canSetWorkspaceTrust())) {
|
||||
if (!(this.workspaceTrustManagementService.canSetWorkspaceTrust())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -447,9 +461,6 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben
|
|||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.workspaceTrustManagementService.onDidInitiateWorkspaceTrustRequestOnStartup(() => {
|
||||
this.showModalOnStart();
|
||||
}));
|
||||
|
||||
this._register(this.workspaceTrustRequestService.onDidInitiateWorkspaceTrustRequest(async requestOptions => {
|
||||
// Message
|
||||
|
@ -700,7 +711,7 @@ class WorkspaceTrustTelemetryContribution extends Disposable implements IWorkben
|
|||
};
|
||||
|
||||
this.telemetryService.publicLog2<WorkspaceTrustInfoEvent, WorkspaceTrustInfoEventClassification>('workspaceTrustFolderCounts', {
|
||||
trustedFoldersCount: this.workspaceTrustManagementService.getTrustedFolders().length,
|
||||
trustedFoldersCount: this.workspaceTrustManagementService.getTrustedUris().length,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ class WorkspaceTrustedUrisTable extends Disposable {
|
|||
currentWorkspaceUris.push(currentWorkspace.configuration);
|
||||
}
|
||||
|
||||
const entries = this.workspaceTrustManagementService.getTrustedFolders().map(uri => {
|
||||
const entries = this.workspaceTrustManagementService.getTrustedUris().map(uri => {
|
||||
|
||||
let relatedToCurrentWorkspace = false;
|
||||
for (const workspaceUri of currentWorkspaceUris) {
|
||||
|
@ -215,7 +215,7 @@ class WorkspaceTrustedUrisTable extends Disposable {
|
|||
}
|
||||
|
||||
acceptEdit(item: ITrustedUriItem, uri: URI) {
|
||||
const trustedFolders = this.workspaceTrustManagementService.getTrustedFolders();
|
||||
const trustedFolders = this.workspaceTrustManagementService.getTrustedUris();
|
||||
const index = this.getIndexOfTrustedUriEntry(item);
|
||||
|
||||
if (index >= trustedFolders.length) {
|
||||
|
@ -224,7 +224,7 @@ class WorkspaceTrustedUrisTable extends Disposable {
|
|||
trustedFolders[index] = uri;
|
||||
}
|
||||
|
||||
this.workspaceTrustManagementService.setTrustedFolders(trustedFolders);
|
||||
this.workspaceTrustManagementService.setTrustedUris(trustedFolders);
|
||||
this._onDidAcceptEdit.fire(item);
|
||||
}
|
||||
|
||||
|
@ -803,13 +803,13 @@ export class WorkspaceTrustEditor extends EditorPane {
|
|||
], xListIcon.classNamesArray);
|
||||
|
||||
if (this.workspaceTrustManagementService.isWorkpaceTrusted()) {
|
||||
if (await this.workspaceTrustManagementService.canSetWorkspaceTrust()) {
|
||||
if (this.workspaceTrustManagementService.canSetWorkspaceTrust()) {
|
||||
this.addDontTrustButtonToElement(untrustedContainer);
|
||||
} else {
|
||||
this.addTrustedTextToElement(untrustedContainer);
|
||||
}
|
||||
} else {
|
||||
if (await this.workspaceTrustManagementService.canSetWorkspaceTrust()) {
|
||||
if (this.workspaceTrustManagementService.canSetWorkspaceTrust()) {
|
||||
this.addTrustButtonToElement(trustedContainer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/
|
|||
import { LoggerChannelClient, LogLevelChannelClient } from 'vs/platform/log/common/logIpc';
|
||||
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { NativeLogService } from 'vs/workbench/services/log/electron-sandbox/logService';
|
||||
import { RemoteWorkspaceTrustManagementService, WorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/common/workspaceTrust';
|
||||
import { WorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/common/workspaceTrust';
|
||||
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { registerWindowDriver } from 'vs/platform/driver/electron-sandbox/driver';
|
||||
|
||||
|
@ -264,10 +264,7 @@ export abstract class SharedDesktopMain extends Disposable {
|
|||
]);
|
||||
|
||||
// Workspace Trust Service
|
||||
const workspaceTrustManagementService = !environmentService.remoteAuthority ?
|
||||
new WorkspaceTrustManagementService(configurationService, storageService, uriIdentityService, environmentService, configurationService) :
|
||||
new RemoteWorkspaceTrustManagementService(configurationService, storageService, uriIdentityService, environmentService, configurationService, remoteAuthorityResolverService);
|
||||
await workspaceTrustManagementService.initializeWorkspaceTrust();
|
||||
const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, storageService, uriIdentityService, environmentService, configurationService, remoteAuthorityResolverService);
|
||||
serviceCollection.set(IWorkspaceTrustManagementService, workspaceTrustManagementService);
|
||||
|
||||
// Update workspace trust so that configuration is updated accordingly
|
||||
|
|
|
@ -354,9 +354,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
});
|
||||
|
||||
// Now that the canonical URI provider has been registered, we
|
||||
// need to refresh workspace trust before resolving the authority
|
||||
await this._workspaceTrustManagementService.recalculateWorkspaceTrust();
|
||||
|
||||
// need to wait for the trust state to be initialized
|
||||
await this._workspaceTrustManagementService.workspaceTrustInitialized;
|
||||
let resolverResult: ResolverResult;
|
||||
|
||||
try {
|
||||
|
|
|
@ -135,7 +135,7 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp
|
|||
}
|
||||
|
||||
override matches(otherInput: unknown): boolean {
|
||||
if (otherInput === this) {
|
||||
if (super.matches(otherInput)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
|||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IRemoteAuthorityResolverService, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { WorkspaceTrustRequestOptions, IWorkspaceTrustManagementService, IWorkspaceTrustInfo, IWorkspaceTrustUriInfo, IWorkspaceTrustRequestService, IWorkspaceTrustTransitionParticipant, WorkspaceTrustUriResponse } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { isSingleFolderWorkspaceIdentifier, isUntitledWorkspace, toWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { Memento, MementoObject } from 'vs/workbench/common/memento';
|
||||
|
@ -46,22 +46,56 @@ export function isWorkspaceTrustEnabled(configurationService: IConfigurationServ
|
|||
return (configurationService.inspect<boolean>(WORKSPACE_TRUST_ENABLED).userValue ?? configurationService.inspect<boolean>(WORKSPACE_TRUST_ENABLED).defaultValue) ?? false;
|
||||
}
|
||||
|
||||
|
||||
export class CanonicalWorkspace implements IWorkspace {
|
||||
constructor(
|
||||
private readonly originalWorkspace: IWorkspace,
|
||||
private readonly canonicalFolderUris: URI[],
|
||||
private readonly canonicalConfiguration: URI | null | undefined
|
||||
) { }
|
||||
|
||||
|
||||
get folders(): IWorkspaceFolder[] {
|
||||
return this.originalWorkspace.folders.map((folder, index) => {
|
||||
return {
|
||||
index: folder.index,
|
||||
name: folder.name,
|
||||
toResource: folder.toResource,
|
||||
uri: this.canonicalFolderUris[index]
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
get configuration(): URI | null | undefined {
|
||||
return this.canonicalConfiguration ?? this.originalWorkspace.configuration;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.originalWorkspace.id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class WorkspaceTrustManagementService extends Disposable implements IWorkspaceTrustManagementService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly storageKey = WORKSPACE_TRUST_STORAGE_KEY;
|
||||
|
||||
private _initialized: boolean;
|
||||
private _workspaceTrustInitializedPromise: Promise<void>;
|
||||
private _workspaceTrustInitializedPromiseResolve!: () => void;
|
||||
private _remoteAuthority: ResolverResult | undefined;
|
||||
|
||||
private readonly _onDidChangeTrust = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeTrust = this._onDidChangeTrust.event;
|
||||
|
||||
private readonly _onDidChangeTrustedFolders = this._register(new Emitter<void>());
|
||||
readonly onDidChangeTrustedFolders = this._onDidChangeTrustedFolders.event;
|
||||
|
||||
protected readonly _onDidInitiateWorkspaceTrustRequestOnStartup = this._register(new Emitter<void>());
|
||||
readonly onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event;
|
||||
|
||||
private _trustStateInfo: IWorkspaceTrustInfo;
|
||||
private _canonicalWorkspace: IWorkspace;
|
||||
|
||||
protected readonly _trustState: WorkspaceTrustState;
|
||||
private readonly _trustTransitionManager: WorkspaceTrustTransitionManager;
|
||||
|
@ -70,33 +104,78 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
|
||||
@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkspaceContextService protected readonly workspaceService: IWorkspaceContextService
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
|
||||
@IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService,
|
||||
|
||||
) {
|
||||
super();
|
||||
|
||||
this._canonicalWorkspace = this.workspaceService.getWorkspace();
|
||||
this._initialized = false;
|
||||
this._workspaceTrustInitializedPromise = new Promise((resolve) => {
|
||||
this._workspaceTrustInitializedPromiseResolve = resolve;
|
||||
});
|
||||
|
||||
this._trustState = new WorkspaceTrustState(this.storageService);
|
||||
this._trustTransitionManager = this._register(new WorkspaceTrustTransitionManager());
|
||||
|
||||
this._trustStateInfo = this.loadTrustInfo();
|
||||
this._trustState.isTrusted = this.calculateWorkspaceTrust();
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
//#region private interface
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.workspaceService.onDidChangeWorkspaceFolders(async () => await this.recalculateWorkspaceTrust()));
|
||||
this._register(this.workspaceService.onDidChangeWorkbenchState(async () => await this.recalculateWorkspaceTrust()));
|
||||
|
||||
// Resolve the workspace uris and resolve the initialization promise
|
||||
this.resolveCanonicalWorkspaceUris().then(async () => {
|
||||
this._initialized = true;
|
||||
await this.updateWorkspaceTrust();
|
||||
this._workspaceTrustInitializedPromiseResolve();
|
||||
});
|
||||
|
||||
// Remote - resolve remote authority
|
||||
if (this.environmentService.remoteAuthority) {
|
||||
this.remoteAuthorityResolverService.resolveAuthority(this.environmentService.remoteAuthority)
|
||||
.then(async result => {
|
||||
this._remoteAuthority = result;
|
||||
await this.updateWorkspaceTrust();
|
||||
});
|
||||
}
|
||||
|
||||
this._register(this.workspaceService.onDidChangeWorkspaceFolders(async () => await this.updateWorkspaceTrust()));
|
||||
this._register(this.workspaceService.onDidChangeWorkbenchState(async () => await this.updateWorkspaceTrust()));
|
||||
this._register(this.storageService.onDidChangeValue(async changeEvent => {
|
||||
/* This will only execute if storage was changed by a user action in a separate window */
|
||||
if (changeEvent.key === this.storageKey && JSON.stringify(this._trustStateInfo) !== JSON.stringify(this.loadTrustInfo())) {
|
||||
this._trustStateInfo = this.loadTrustInfo();
|
||||
this._onDidChangeTrustedFolders.fire();
|
||||
|
||||
await this.recalculateWorkspaceTrust();
|
||||
await this.updateWorkspaceTrust();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private async getCanonicalUri(uri: URI): Promise<URI> {
|
||||
return this.environmentService.remoteAuthority && uri.scheme === Schemas.vscodeRemote ?
|
||||
await this.remoteAuthorityResolverService.getCanonicalURI(uri) : uri;
|
||||
}
|
||||
|
||||
private async resolveCanonicalWorkspaceUris(): Promise<void> {
|
||||
const workspaceUris = this.workspaceService.getWorkspace().folders.map(f => f.uri);
|
||||
const canonicalWorkspaceFolders = await Promise.all(workspaceUris.map(uri => this.getCanonicalUri(uri)));
|
||||
|
||||
let canonicalWorkspaceConfiguration = this.workspaceService.getWorkspace().configuration;
|
||||
if (canonicalWorkspaceConfiguration && !isUntitledWorkspace(canonicalWorkspaceConfiguration, this.environmentService)) {
|
||||
canonicalWorkspaceConfiguration = await this.getCanonicalUri(canonicalWorkspaceConfiguration);
|
||||
}
|
||||
|
||||
this._canonicalWorkspace = new CanonicalWorkspace(this.workspaceService.getWorkspace(), canonicalWorkspaceFolders, canonicalWorkspaceConfiguration);
|
||||
}
|
||||
|
||||
private loadTrustInfo(): IWorkspaceTrustInfo {
|
||||
const infoAsString = this.storageService.get(this.storageKey, StorageScope.GLOBAL);
|
||||
|
||||
|
@ -127,12 +206,12 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
this.storageService.store(this.storageKey, JSON.stringify(this._trustStateInfo), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
this._onDidChangeTrustedFolders.fire();
|
||||
|
||||
await this.recalculateWorkspaceTrust();
|
||||
await this.updateWorkspaceTrust();
|
||||
}
|
||||
|
||||
protected getWorkspaceUris(): URI[] {
|
||||
const workspaceUris = this.workspaceService.getWorkspace().folders.map(f => f.uri);
|
||||
const workspaceConfiguration = this.workspaceService.getWorkspace().configuration;
|
||||
private getWorkspaceUris(): URI[] {
|
||||
const workspaceUris = this._canonicalWorkspace.folders.map(f => f.uri);
|
||||
const workspaceConfiguration = this._canonicalWorkspace.configuration;
|
||||
if (workspaceConfiguration && !isUntitledWorkspace(workspaceConfiguration, this.environmentService)) {
|
||||
workspaceUris.push(workspaceConfiguration);
|
||||
}
|
||||
|
@ -140,11 +219,20 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
return workspaceUris;
|
||||
}
|
||||
|
||||
protected async calculateWorkspaceTrust(): Promise<boolean> {
|
||||
private calculateWorkspaceTrust(): boolean {
|
||||
if (!isWorkspaceTrustEnabled(this.configurationService)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this._initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remote - remote authority explicitly sets workspace trust
|
||||
if (this.environmentService.remoteAuthority && this._remoteAuthority?.options?.isTrusted !== undefined) {
|
||||
return this._remoteAuthority.options.isTrusted;
|
||||
}
|
||||
|
||||
if (this.environmentService.extensionTestsLocationURI) {
|
||||
return true; // trust running tests with vscode-test
|
||||
}
|
||||
|
@ -155,36 +243,16 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
return this._trustState.isTrusted ?? false;
|
||||
}
|
||||
|
||||
const workspaceUris = this.getWorkspaceUris();
|
||||
const trusted = await this.getUrisTrust(workspaceUris);
|
||||
|
||||
return trusted;
|
||||
return this.getUrisTrust(this.getWorkspaceUris());
|
||||
}
|
||||
|
||||
protected async getCanonicalUri(uri: URI): Promise<URI> {
|
||||
return uri;
|
||||
}
|
||||
|
||||
protected async getUrisTrust(uris: URI[]): Promise<boolean> {
|
||||
let state = true;
|
||||
for (const uri of uris) {
|
||||
const { trusted } = await this.getUriTrustInfo(uri);
|
||||
|
||||
if (!trusted) {
|
||||
state = trusted;
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
protected async updateWorkspaceTrust(trusted?: boolean): Promise<void> {
|
||||
private async updateWorkspaceTrust(trusted?: boolean): Promise<void> {
|
||||
if (!isWorkspaceTrustEnabled(this.configurationService)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (trusted === undefined) {
|
||||
await this.resolveCanonicalWorkspaceUris();
|
||||
trusted = await this.calculateWorkspaceTrust();
|
||||
}
|
||||
|
||||
|
@ -200,19 +268,21 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
this._onDidChangeTrust.fire(trusted);
|
||||
}
|
||||
|
||||
get acceptsOutOfWorkspaceFiles(): boolean {
|
||||
return this._trustState.acceptsOutOfWorkspaceFiles;
|
||||
private getUrisTrust(uris: URI[]): boolean {
|
||||
let state = true;
|
||||
for (const uri of uris) {
|
||||
const { trusted } = this.doGetUriTrustInfo(uri);
|
||||
|
||||
if (!trusted) {
|
||||
state = trusted;
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
set acceptsOutOfWorkspaceFiles(value: boolean) {
|
||||
this._trustState.acceptsOutOfWorkspaceFiles = value;
|
||||
}
|
||||
|
||||
addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable {
|
||||
return this._trustTransitionManager.addWorkspaceTrustTransitionParticipant(participant);
|
||||
}
|
||||
|
||||
async getUriTrustInfo(uri: URI): Promise<IWorkspaceTrustUriInfo> {
|
||||
private doGetUriTrustInfo(uri: URI): IWorkspaceTrustUriInfo {
|
||||
// Return trusted when workspace trust is disabled
|
||||
if (!isWorkspaceTrustEnabled(this.configurationService)) {
|
||||
return { trusted: true, uri };
|
||||
|
@ -221,11 +291,10 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
let resultState = false;
|
||||
let maxLength = -1;
|
||||
|
||||
const canonicalUri = await this.getCanonicalUri(uri);
|
||||
let resultUri = canonicalUri;
|
||||
let resultUri = uri;
|
||||
|
||||
for (const trustInfo of this._trustStateInfo.uriTrustInfo) {
|
||||
if (this.uriIdentityService.extUri.isEqualOrParent(canonicalUri, trustInfo.uri)) {
|
||||
if (this.uriIdentityService.extUri.isEqualOrParent(uri, trustInfo.uri)) {
|
||||
const fsPath = trustInfo.uri.fsPath;
|
||||
if (fsPath.length > maxLength) {
|
||||
maxLength = fsPath.length;
|
||||
|
@ -238,21 +307,19 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
return { trusted: resultState, uri: resultUri };
|
||||
}
|
||||
|
||||
async setUrisTrust(uris: URI[], trusted: boolean): Promise<void> {
|
||||
private async doSetUrisTrust(uris: URI[], trusted: boolean): Promise<void> {
|
||||
let changed = false;
|
||||
|
||||
for (const uri of uris) {
|
||||
const canonicalUri = await this.getCanonicalUri(uri);
|
||||
|
||||
if (trusted) {
|
||||
const foundItem = this._trustStateInfo.uriTrustInfo.find(trustInfo => this.uriIdentityService.extUri.isEqual(trustInfo.uri, canonicalUri));
|
||||
const foundItem = this._trustStateInfo.uriTrustInfo.find(trustInfo => this.uriIdentityService.extUri.isEqual(trustInfo.uri, uri));
|
||||
if (!foundItem) {
|
||||
this._trustStateInfo.uriTrustInfo.push({ uri: canonicalUri, trusted: true });
|
||||
this._trustStateInfo.uriTrustInfo.push({ uri, trusted: true });
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
const previousLength = this._trustStateInfo.uriTrustInfo.length;
|
||||
this._trustStateInfo.uriTrustInfo = this._trustStateInfo.uriTrustInfo.filter(trustInfo => !this.uriIdentityService.extUri.isEqual(trustInfo.uri, canonicalUri));
|
||||
this._trustStateInfo.uriTrustInfo = this._trustStateInfo.uriTrustInfo.filter(trustInfo => !this.uriIdentityService.extUri.isEqual(trustInfo.uri, uri));
|
||||
if (previousLength !== this._trustStateInfo.uriTrustInfo.length) {
|
||||
changed = true;
|
||||
}
|
||||
|
@ -264,7 +331,46 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
}
|
||||
}
|
||||
|
||||
async canSetWorkspaceTrust(): Promise<boolean> {
|
||||
//#endregion
|
||||
|
||||
//#region public interface
|
||||
|
||||
get workspaceTrustInitialized(): Promise<void> {
|
||||
return this._workspaceTrustInitializedPromise;
|
||||
}
|
||||
|
||||
get acceptsOutOfWorkspaceFiles(): boolean {
|
||||
return this._trustState.acceptsOutOfWorkspaceFiles;
|
||||
}
|
||||
|
||||
set acceptsOutOfWorkspaceFiles(value: boolean) {
|
||||
this._trustState.acceptsOutOfWorkspaceFiles = value;
|
||||
}
|
||||
|
||||
isWorkpaceTrusted(): boolean {
|
||||
return this._trustState.isTrusted ?? false;
|
||||
}
|
||||
|
||||
canSetParentFolderTrust(): boolean {
|
||||
const workspaceIdentifier = toWorkspaceIdentifier(this._canonicalWorkspace);
|
||||
return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && workspaceIdentifier.uri.scheme === Schemas.file;
|
||||
}
|
||||
|
||||
async setParentFolderTrust(trusted: boolean): Promise<void> {
|
||||
const workspaceIdentifier = toWorkspaceIdentifier(this._canonicalWorkspace);
|
||||
if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && workspaceIdentifier.uri.scheme === Schemas.file) {
|
||||
const { parentPath } = splitName(workspaceIdentifier.uri.fsPath);
|
||||
|
||||
await this.setUrisTrust([URI.file(parentPath)], trusted);
|
||||
}
|
||||
}
|
||||
|
||||
canSetWorkspaceTrust(): boolean {
|
||||
// Remote - remote authority not yet resolved, or remote authority explicitly sets workspace trust
|
||||
if (this.environmentService.remoteAuthority && (!this._remoteAuthority || this._remoteAuthority.options?.isTrusted !== undefined)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Empty workspace
|
||||
if (this.workspaceService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
return true;
|
||||
|
@ -277,13 +383,13 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
|
||||
// Trusted workspace
|
||||
// Can only be trusted explicitly in the single folder scenario
|
||||
const workspaceIdentifier = toWorkspaceIdentifier(this.workspaceService.getWorkspace());
|
||||
const workspaceIdentifier = toWorkspaceIdentifier(this._canonicalWorkspace);
|
||||
if (!(isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && workspaceIdentifier.uri.scheme === Schemas.file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the current folder isn't trusted directly, return false
|
||||
const trustInfo = await this.getUriTrustInfo(workspaceIdentifier.uri);
|
||||
const trustInfo = this.doGetUriTrustInfo(workspaceIdentifier.uri);
|
||||
if (!trustInfo.trusted || !this.uriIdentityService.extUri.isEqual(workspaceIdentifier.uri, trustInfo.uri)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -291,7 +397,7 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
// Check if the parent is also trusted
|
||||
if (this.canSetParentFolderTrust()) {
|
||||
const { parentPath } = splitName(workspaceIdentifier.uri.fsPath);
|
||||
const parentPathTrustInfo = await this.getUriTrustInfo(URI.file(parentPath));
|
||||
const parentPathTrustInfo = this.doGetUriTrustInfo(URI.file(parentPath));
|
||||
if (parentPathTrustInfo.trusted) {
|
||||
return false;
|
||||
}
|
||||
|
@ -300,32 +406,6 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
return true;
|
||||
}
|
||||
|
||||
canSetParentFolderTrust(): boolean {
|
||||
const workspaceIdentifier = toWorkspaceIdentifier(this.workspaceService.getWorkspace());
|
||||
return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && workspaceIdentifier.uri.scheme === Schemas.file;
|
||||
}
|
||||
|
||||
async initializeWorkspaceTrust(): Promise<void> {
|
||||
this._trustState.isTrusted = isWorkspaceTrustEnabled(this.configurationService) ? await this.calculateWorkspaceTrust() : true;
|
||||
}
|
||||
|
||||
isWorkpaceTrusted(): boolean {
|
||||
return this._trustState.isTrusted ?? false;
|
||||
}
|
||||
|
||||
async recalculateWorkspaceTrust(): Promise<void> {
|
||||
await this.updateWorkspaceTrust();
|
||||
}
|
||||
|
||||
async setParentFolderTrust(trusted: boolean): Promise<void> {
|
||||
const workspaceIdentifier = toWorkspaceIdentifier(this.workspaceService.getWorkspace());
|
||||
if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && workspaceIdentifier.uri.scheme === Schemas.file) {
|
||||
const { parentPath } = splitName(workspaceIdentifier.uri.fsPath);
|
||||
|
||||
await this.setUrisTrust([URI.file(parentPath)], trusted);
|
||||
}
|
||||
}
|
||||
|
||||
async setWorkspaceTrust(trusted: boolean): Promise<void> {
|
||||
// Empty workspace
|
||||
if (this.workspaceService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
|
@ -337,11 +417,24 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
await this.setUrisTrust(workspaceFolders, trusted);
|
||||
}
|
||||
|
||||
getTrustedFolders(): URI[] {
|
||||
async getUriTrustInfo(uri: URI): Promise<IWorkspaceTrustUriInfo> {
|
||||
// Return trusted when workspace trust is disabled
|
||||
if (!isWorkspaceTrustEnabled(this.configurationService)) {
|
||||
return { trusted: true, uri };
|
||||
}
|
||||
|
||||
return this.doGetUriTrustInfo(await this.getCanonicalUri(uri));
|
||||
}
|
||||
|
||||
async setUrisTrust(uris: URI[], trusted: boolean): Promise<void> {
|
||||
this.doSetUrisTrust(await Promise.all(uris.map(uri => this.getCanonicalUri(uri))), trusted);
|
||||
}
|
||||
|
||||
getTrustedUris(): URI[] {
|
||||
return this._trustStateInfo.uriTrustInfo.map(info => info.uri);
|
||||
}
|
||||
|
||||
async setTrustedFolders(uris: URI[]): Promise<void> {
|
||||
async setTrustedUris(uris: URI[]): Promise<void> {
|
||||
this._trustStateInfo.uriTrustInfo = [];
|
||||
for (const uri of uris) {
|
||||
const canonicalUri = await this.getCanonicalUri(uri);
|
||||
|
@ -366,79 +459,12 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork
|
|||
|
||||
await this.saveTrustInfo();
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoteWorkspaceTrustManagementService extends WorkspaceTrustManagementService {
|
||||
|
||||
private _remoteAuthority: ResolverResult | undefined;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IUriIdentityService uriIdentityService: IUriIdentityService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkspaceContextService workspaceService: IWorkspaceContextService,
|
||||
@IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService,
|
||||
) {
|
||||
super(configurationService, storageService, uriIdentityService, environmentService, workspaceService);
|
||||
|
||||
// Remote - resolve remote authority
|
||||
if (this.environmentService.remoteAuthority) {
|
||||
this.remoteAuthorityResolverService.resolveAuthority(this.environmentService.remoteAuthority)
|
||||
.then(async result => {
|
||||
this._remoteAuthority = result;
|
||||
await this.recalculateWorkspaceTrust();
|
||||
|
||||
// Show workspace trust modal dialog
|
||||
this._onDidInitiateWorkspaceTrustRequestOnStartup.fire();
|
||||
});
|
||||
}
|
||||
addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable {
|
||||
return this._trustTransitionManager.addWorkspaceTrustTransitionParticipant(participant);
|
||||
}
|
||||
|
||||
override async canSetWorkspaceTrust(): Promise<boolean> {
|
||||
// Remote - remote authority not yet resolved, or remote authority explicitly sets workspace trust
|
||||
if (this.environmentService.remoteAuthority && (!this._remoteAuthority || this._remoteAuthority.options?.isTrusted !== undefined)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.canSetWorkspaceTrust();
|
||||
}
|
||||
|
||||
override async initializeWorkspaceTrust(): Promise<void> {
|
||||
this._trustState.isTrusted = !isWorkspaceTrustEnabled(this.configurationService);
|
||||
}
|
||||
|
||||
protected override async calculateWorkspaceTrust(): Promise<boolean> {
|
||||
if (!isWorkspaceTrustEnabled(this.configurationService)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.environmentService.extensionTestsLocationURI) {
|
||||
return true; // trust running tests with vscode-test
|
||||
}
|
||||
|
||||
// Remote - remote authority explicitly sets workspace trust
|
||||
if (this.environmentService.remoteAuthority && this._remoteAuthority?.options?.isTrusted !== undefined) {
|
||||
return this._remoteAuthority.options.isTrusted;
|
||||
}
|
||||
|
||||
if (this.workspaceService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
// Use memento if present, otherwise default to restricted mode
|
||||
// Workspace may transition to trusted based on the opened editors
|
||||
return this._trustState.isTrusted ?? false;
|
||||
}
|
||||
|
||||
// Workspace
|
||||
const workspaceUris = this.getWorkspaceUris();
|
||||
const trusted = await this.getUrisTrust(workspaceUris);
|
||||
|
||||
return trusted;
|
||||
}
|
||||
|
||||
protected override async getCanonicalUri(uri: URI): Promise<URI> {
|
||||
return this.environmentService.remoteAuthority ?
|
||||
await this.remoteAuthorityResolverService.getCanonicalURI(uri) : uri;
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
|
||||
export class WorkspaceTrustRequestService extends Disposable implements IWorkspaceTrustRequestService {
|
||||
|
|
|
@ -39,7 +39,7 @@ export class TestWorkspaceTrustManagementService implements IWorkspaceTrustManag
|
|||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getTrustedFolders(): URI[] {
|
||||
getTrustedUris(): URI[] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ export class TestWorkspaceTrustManagementService implements IWorkspaceTrustManag
|
|||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
async setTrustedFolders(folders: URI[]): Promise<void> {
|
||||
async setTrustedUris(folders: URI[]): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
|
@ -63,11 +63,7 @@ export class TestWorkspaceTrustManagementService implements IWorkspaceTrustManag
|
|||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
canSetWorkspaceTrust(): Promise<boolean> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
initializeWorkspaceTrust(): Promise<void> {
|
||||
canSetWorkspaceTrust(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
|
@ -75,8 +71,8 @@ export class TestWorkspaceTrustManagementService implements IWorkspaceTrustManag
|
|||
return this.trusted;
|
||||
}
|
||||
|
||||
recalculateWorkspaceTrust(): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
get workspaceTrustInitialized(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async setWorkspaceTrust(trusted: boolean): Promise<void> {
|
||||
|
|
|
@ -9540,10 +9540,10 @@ typescript@^2.6.2:
|
|||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"
|
||||
integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=
|
||||
|
||||
typescript@^4.3.0-dev.20210503:
|
||||
version "4.3.0-dev.20210503"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.0-dev.20210503.tgz#0a3eb480676effd4975beb5a2f097530ed53550a"
|
||||
integrity sha512-Gj3TQve5PLCZoPBy96Yp6Y3+jNLmms0i3ynhxEJAKgax7Fxui29/uG/DClbBtKz1peNhzXwikXVFFAV1BB/3mw==
|
||||
typescript@^4.4.0-dev.20210528:
|
||||
version "4.4.0-dev.20210528"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.0-dev.20210528.tgz#42453bc42e9d9df8ad0741c207c24d56407c0347"
|
||||
integrity sha512-ACV+mYKC+PhWUXIDUL6qmFClIdrKc20KRxDePt8bniCgkKQD4XRYKl7m02paxJM3nTMRdlfjs0ncaslA5BA1GA==
|
||||
|
||||
typical@^4.0.0:
|
||||
version "4.0.0"
|
||||
|
|
Loading…
Reference in a new issue