Repository: vscode
Issue: Explore Notebook formatting via Code Actions
Labels: under-discussion,on-testplan,notebook-format,notebook-code-actions

## Overview
Opening this issue up as a discussion point for the current work around notebook formatting. As a way to accomplish this without the introduction of new API, the thought is to introduce a new Code Action Kind `notebook.format.extensionID`. Looping in some relevant notebook formatting people.
cc: @rebornix @karthiknadig @charliermarsh

--- 
### Proposed Plan
- Extension authors will provide a Code Action that wraps all of their formatting into a single action, with the kind `notebook.format.xyz` 
	- We recommend that the `xyz` is unique to your extension, but this is not a restriction
	- We require that you have a complete `DocumentFilter` that contains `notebookType` 
- Only one formatting Code Action will be used against the notebook. 
	- If more than one extension registers a provider for this type, users will be prompted to choose between them and define a setting `"notebook.defaultFormatter": "extensionID"` (mirrors the editor setting) 
	- If an extension registers multiple `notebook.format.xyz` providers, only the first will be used.
	- If a `notebook.format` Code Action is applied on save, the standard `provideDocumentFormatting` flow will be skipped
- The `notebook.format` Code Action can be applied in the following ways:
	- Setting `"notebook.formatOnSave.enabled": true` (*this means that there will only be explicit triggering of this for saves, due to `formatOnSave` requiring the file to NOT be saved after a delay*)
	- Command palette `Format Notebook`
	- Notebook Context Menu (🔜)

### Rough Example
- Extension (pseudo)Code:
```ts
export async function activate(context: vscode.ExtensionContext) {
	registerCodeActionsProvider(DocumentFilter, new ProviderName(), providedKinds);
}

class ProviderName {
	static readonly kind = "notebook.format.extensionID";
	
	public provideCodeActions(
		document: vscode.TextDocument,
		_range: vscode.Range | vscode.Selection,
		_context: vscode.CodeActionContext,
		_token: vscode.CancellationToken
	): vscode.CodeAction[] | undefined {
		const nbEdits: NotebookEdit[] | TextEdit[] = {
			// wrap all formatting edits here
		}
		
		const fix = new vscode.CodeAction(
			'Apply Notebook Formatting',
			ProviderName.kind,
		);
		fix.edit = new vscode.WorkspaceEdit();
		fix.edit.set(notebookDocument.uri, nbEdits);
		return [fix];
	}
}
```
- User `settings.json`:
```
{
	"notebook.defaultFormatter": "extensionID", // extensionID
	"notebook.formatOnSave.enabled": true,
	
	"notebook.codeActionsOnSave": { // these will occur BEFORE the formatter
		"source.xyz": "explicit",
	}
}
```

### Other relevant notes
- Code Action Kind prefix rules
	- Code Action Providers that return a kind prefixed with `notebook.` will only ever be called against the 0th cell of the notebook. Additionally, the provided `TextDocument` will always correspond to the 0th cell in the notebook and contain a `uri` that can retrieve the notebook. It is the responsibility of the provider to extract the `NotebookDocument` from this (an example will be provided in a sample extension, coming 🔜).
	- If a Code Action is added to settings under `notebook.codeActionsOnSave` without the `notebook.` prefix, the codeaction will be applied to every valid cell asynchronously, in parallel.
- Using `NotebookEdit.replaceCells()` is less performant than simply using `TextEdit.replace()` against that document. Additionally, using `NotebookEdit.replaceCells()` will fully replace cells, including output. When possible, use `TextEdits` for editing just the content of a cell.


--- 
## Questions
- Feel free to leave any and all thoughts below :+1: