diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 004b53a635a..81a03ed3250 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -135,7 +135,7 @@ suite('Notebook API tests', function () { id: 'mainKernel', label: 'Notebook Test Kernel', isPreferred: true, - supportedLanguages: ['typescript'], + supportedLanguages: ['typescript', 'javascript'], executeAllCells: async (_document: vscode.NotebookDocument) => { const edit = new vscode.WorkspaceEdit(); @@ -174,7 +174,7 @@ suite('Notebook API tests', function () { id: 'secondaryKernel', label: 'Notebook Secondary Test Kernel', isPreferred: false, - supportedLanguages: ['typescript'], + supportedLanguages: ['typescript', 'javascript'], executeAllCells: async (_document: vscode.NotebookDocument) => { const edit = new vscode.WorkspaceEdit(); edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([ @@ -471,6 +471,30 @@ suite('Notebook API tests', function () { await saveFileAndCloseAll(resource); }); + test('change cell language', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].language, 'typescript'); + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].cellKind, vscode.NotebookCellKind.Code); + await withEvent(vscode.notebook.onDidChangeCellLanguage, async event => { + await vscode.commands.executeCommand('notebook.cell.changeLanguage', { start: 0, end: 1 }, 'javascript'); + await event; + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].language, 'javascript'); + }); + + // switch to markdown will change the cell kind + await withEvent(vscode.notebook.onDidChangeNotebookCells, async event => { + await vscode.commands.executeCommand('notebook.cell.changeLanguage', { start: 0, end: 1 }, 'markdown'); + await event; + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].language, 'markdown'); + assert.strictEqual(vscode.window.activeNotebookEditor?.document.cells[0].cellKind, vscode.NotebookCellKind.Markdown); + }); + + await saveAllFilesAndCloseAll(resource); + }); + test('edit API (replaceCells)', async function () { assertInitalState(); const resource = await createRandomFile('', undefined, '.vsctestnb'); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 26772735238..56f85535ae3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -1607,19 +1607,81 @@ interface ILanguagePickInput extends IQuickPickItem { description: string; } -export class ChangeCellLanguageAction extends NotebookCellAction { + +interface IChangeCellContext extends INotebookCellActionContext { + // TODO@rebornix : `cells` + // range: ICellRange; + language?: string; +} + +export class ChangeCellLanguageAction extends NotebookCellAction { constructor() { super({ id: CHANGE_CELL_LANGUAGE, title: localize('changeLanguage', 'Change Cell Language'), + description: { + description: localize('changeLanguage', 'Change Cell Language'), + args: [ + { + name: 'range', + description: 'The cell range', + schema: { + 'type': 'object', + 'required': ['start', 'end'], + 'properties': { + 'start': { + 'type': 'number' + }, + 'end': { + 'type': 'number' + } + } + } + }, + { + name: 'language', + description: 'The target cell language', + schema: { + 'type': 'string' + } + } + ] + } }); } - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - this.showLanguagePicker(accessor, context); + protected getCellContextFromArgs(accessor: ServicesAccessor, context?: ICellRange, ...additionalArgs: any[]): IChangeCellContext | undefined { + if (!context || typeof context.start !== 'number' || typeof context.end !== 'number' || context.start >= context.end) { + return; + } + + const language = additionalArgs.length && typeof additionalArgs[0] === 'string' ? additionalArgs[0] : undefined; + const activeEditorContext = this.getEditorContextFromArgsOrActive(accessor); + + if (!activeEditorContext || !activeEditorContext.notebookEditor.viewModel || context.start >= activeEditorContext.notebookEditor.viewModel.viewCells.length) { + return; + } + + const cells = activeEditorContext.notebookEditor.viewModel.viewCells; + + // TODO@rebornix, support multiple cells + return { + notebookEditor: activeEditorContext.notebookEditor, + cell: cells[context.start], + language + }; } - private async showLanguagePicker(accessor: ServicesAccessor, context: INotebookCellActionContext) { + + async runWithContext(accessor: ServicesAccessor, context: IChangeCellContext): Promise { + if (context.language) { + await this.setLanguage(context, context.language); + } else { + await this.showLanguagePicker(accessor, context); + } + } + + private async showLanguagePicker(accessor: ServicesAccessor, context: IChangeCellContext) { const topItems: ILanguagePickInput[] = []; const mainItems: ILanguagePickInput[] = []; @@ -1671,21 +1733,25 @@ export class ChangeCellLanguageAction extends NotebookCellAction { const selection = await quickInputService.pick(picks, { placeHolder: localize('pickLanguageToConfigure', "Select Language Mode") }) as ILanguagePickInput | undefined; if (selection && selection.languageId) { - if (selection.languageId === 'markdown' && context.cell?.language !== 'markdown') { - const newCell = await changeCellToKind(CellKind.Markdown, { cell: context.cell, notebookEditor: context.notebookEditor }, 'markdown'); - if (newCell) { - context.notebookEditor.focusNotebookCell(newCell, 'editor'); - } - } else if (selection.languageId !== 'markdown' && context.cell?.cellKind === CellKind.Markdown) { - await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, selection.languageId); - } else { - const index = context.notebookEditor.viewModel.notebookDocument.cells.indexOf(context.cell.model); - context.notebookEditor.viewModel.notebookDocument.applyEdits( - context.notebookEditor.viewModel.notebookDocument.versionId, - [{ editType: CellEditType.CellLanguage, index, language: selection.languageId }], - true, undefined, () => undefined, undefined - ); + await this.setLanguage(context, selection.languageId); + } + } + + private async setLanguage(context: IChangeCellContext, languageId: string) { + if (languageId === 'markdown' && context.cell?.language !== 'markdown') { + const newCell = await changeCellToKind(CellKind.Markdown, { cell: context.cell, notebookEditor: context.notebookEditor }, 'markdown'); + if (newCell) { + context.notebookEditor.focusNotebookCell(newCell, 'editor'); } + } else if (languageId !== 'markdown' && context.cell?.cellKind === CellKind.Markdown) { + await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, languageId); + } else { + const index = context.notebookEditor.viewModel.notebookDocument.cells.indexOf(context.cell.model); + context.notebookEditor.viewModel.notebookDocument.applyEdits( + context.notebookEditor.viewModel.notebookDocument.versionId, + [{ editType: CellEditType.CellLanguage, index, language: languageId }], + true, undefined, () => undefined, undefined + ); } }