diff --git a/extensions/positron-duckdb/src/extension.ts b/extensions/positron-duckdb/src/extension.ts index 63b576f59b01..456b8252e693 100644 --- a/extensions/positron-duckdb/src/extension.ts +++ b/extensions/positron-duckdb/src/extension.ts @@ -1056,6 +1056,9 @@ END`; export_data_selection: { support_status: SupportStatus.Unsupported, supported_formats: [] + }, + convert_to_code: { + support_status: SupportStatus.Unsupported, } } }; @@ -1179,6 +1182,9 @@ END`; ExportFormat.Tsv, ExportFormat.Html ] + }, + convert_to_code: { + support_status: SupportStatus.Unsupported, } } }; diff --git a/extensions/positron-duckdb/src/interfaces.ts b/extensions/positron-duckdb/src/interfaces.ts index 4718607ebabb..56a8bf886764 100644 --- a/extensions/positron-duckdb/src/interfaces.ts +++ b/extensions/positron-duckdb/src/interfaces.ts @@ -902,6 +902,11 @@ export interface SupportedFeatures { */ export_data_selection: ExportDataSelectionFeatures; + /** + * Support for 'convert_to_code' RPC and its features + */ + convert_to_code: ConvertToCodeFeatures; + } /** @@ -1000,6 +1005,24 @@ export interface SetSortColumnsFeatures { } +/** + * Feature flags for 'convert_to_code' RPC + */ +export interface ConvertToCodeFeatures { + /** + * The support status for this RPC method + * */ + support_status: SupportStatus; + /** + * The supported code syntax names + */ + supported_code_syntaxes?: Array; +} + +export interface CodeSyntaxName { + code_syntax_name: string; +} + /** * A selection on the data grid, for copying to the clipboard or other * actions diff --git a/extensions/positron-duckdb/src/test/extension.test.ts b/extensions/positron-duckdb/src/test/extension.test.ts index abc1780b57a3..4335cc8bdb3b 100644 --- a/extensions/positron-duckdb/src/test/extension.test.ts +++ b/extensions/positron-duckdb/src/test/extension.test.ts @@ -104,7 +104,7 @@ async function createTempTable( for (let i = 0; i < length; i++) { tuples.push(`(${columns.map(c => c.values[i]).join(', ')})`); } - + // Use explicit column names in INSERT to ensure proper ordering const columnNames = columns.map(c => quoteIdentifier(c.name)).join(', '); await runQuery(`INSERT INTO ${tableName} (${columnNames}) VALUES\n${tuples.join(',\n')};`); @@ -243,7 +243,8 @@ suite('Positron DuckDB Extension Test Suite', () => { ExportFormat.Tsv, ExportFormat.Html ] - } + }, + convert_to_code: { support_status: SupportStatus.Unsupported } } } satisfies BackendState); diff --git a/src/vs/workbench/browser/positronModalDialogs/components/dropdownEntry.tsx b/src/vs/workbench/browser/positronModalDialogs/components/dropdownEntry.tsx new file mode 100644 index 000000000000..992ec43cbe19 --- /dev/null +++ b/src/vs/workbench/browser/positronModalDialogs/components/dropdownEntry.tsx @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2025 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +// React. +import React from 'react'; + +/** + * DropdownEntryProps interface. + */ +interface DropdownEntryProps { + title: string; + subtitle?: string; + group?: string; +} + +/** + * DropdownEntry component. + * @param props The dropdown entry props. + * @returns The rendered component + */ +export const DropdownEntry = (props: DropdownEntryProps) => { + // Render. + return ( +
+
+ {props.title} +
+ {props.group ?
{props.group}
: null} +
+ ); +}; diff --git a/src/vs/workbench/browser/positronModalDialogs/convertToCodeModalDialog.tsx b/src/vs/workbench/browser/positronModalDialogs/convertToCodeModalDialog.tsx new file mode 100644 index 000000000000..8360f3d36546 --- /dev/null +++ b/src/vs/workbench/browser/positronModalDialogs/convertToCodeModalDialog.tsx @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2025 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +// React. +import React, { useEffect, useState } from 'react'; + +// Other dependencies. +import { localize } from '../../../nls.js'; +import { VerticalStack } from '../positronComponents/positronModalDialog/components/verticalStack.js'; +import { OKCancelModalDialog } from '../positronComponents/positronModalDialog/positronOKCancelModalDialog.js'; +import { IPositronDataExplorerInstance } from '../../services/positronDataExplorer/browser/interfaces/positronDataExplorerInstance.js'; +import { PositronDataExplorerCommandId } from '../../contrib/positronDataExplorerEditor/browser/positronDataExplorerActions.js'; +import { DropDownListBox } from '../positronComponents/dropDownListBox/dropDownListBox.js'; +import { DropDownListBoxItem } from '../positronComponents/dropDownListBox/dropDownListBoxItem.js'; +import { DropdownEntry } from './components/dropdownEntry.js'; +import { CodeSyntaxName } from '../../services/languageRuntime/common/positronDataExplorerComm.js'; +import { PositronModalReactRenderer } from '../../../base/browser/positronModalReactRenderer.js'; +import { usePositronReactServicesContext } from '../../../base/browser/positronReactRendererContext.js'; + +/** + * Shows the convert to code modal dialog. + * @param dataExplorerClientInstance The data explorer client instance. + * @returns A promise that resolves when the dialog is closed. + */ +export const showConvertToCodeModalDialog = async ( + dataExplorerClientInstance: IPositronDataExplorerInstance, +): Promise => { + // Create the renderer. + const renderer = new PositronModalReactRenderer() + + // Show the copy as code dialog. + renderer.render( + + ); +}; + +/** + * ConvertToCodeDialogProps interface. + */ +interface ConvertToCodeDialogProps { + dataExplorerClientInstance: IPositronDataExplorerInstance + renderer: PositronModalReactRenderer; +} + + +/** + * ConvertToCodeModalDialog component. + * @param props The component properties. + * @returns The rendered component. + */ +export const ConvertToCodeModalDialog = (props: ConvertToCodeDialogProps) => { + // Service hooks. + const services = usePositronReactServicesContext(); + + // State hooks. + const instance = props.dataExplorerClientInstance.dataExplorerClientInstance; + const codeSyntaxOptions = instance.cachedBackendState?.supported_features?.convert_to_code?.code_syntaxes ?? []; + + const [selectedSyntax, setSelectedSyntax] = useState(instance.suggestedSyntax); + + const [codeString, setCodeString] = useState(undefined); + + useEffect(() => { + const getCodeString = async () => { + try { + // Execute the command to get the code string based on the selected syntax. + const result = await services.commandService.executeCommand(PositronDataExplorerCommandId.ConvertToCodeAction, selectedSyntax); + setCodeString(result); + } catch (error) { + if (selectedSyntax) { + setCodeString(localize( + 'positron.dataExplorer.getCodeStringWithSyntax', + "Cannot generate code for type {0}", + selectedSyntax.code_syntax_name + )); + } else { + setCodeString(localize( + 'positron.dataExplorer.getCodeStringNoSyntax', + "Cannot generate code" + )); + } + } + }; + + getCodeString(); // Call the async function + }, [selectedSyntax, services.commandService]); + + // Construct the syntax options dropdown entries + const syntaxDropdownEntries = () => { + return syntaxInfoToDropDownItems(codeSyntaxOptions); + }; + + const syntaxInfoToDropDownItems = ( + syntaxes: CodeSyntaxName[] + ): DropDownListBoxItem[] => { + return syntaxes.map( + (syntax) => + new DropDownListBoxItem({ + identifier: syntax.code_syntax_name, + value: syntax, + }) + ); + }; + + const syntaxDropdownTitle = (): string => { + // if selectedSyntax is an object with code_syntax_name, return that name + if (typeof selectedSyntax === 'object' && 'code_syntax_name' in selectedSyntax) { + return (selectedSyntax as CodeSyntaxName).code_syntax_name; + } + return localize('positron.dataExplorer.selectCodeSyntax', 'Select Code Syntax'); + } + + const onSelectionChanged = async (item: DropDownListBoxItem) => { + const typedItem = item as DropDownListBoxItem; + setSelectedSyntax(typedItem.options.value); + + // Execute the command to get the code string based on the selected syntax. + try { + const exc = await services.commandService.executeCommand(PositronDataExplorerCommandId.ConvertToCodeAction, typedItem.options.value); + setCodeString(exc); + } catch (error) { + setCodeString(localize( + 'positron.dataExplorer.cannotGenerateCodeForType', + "Cannot generate code for type {0}", + typedItem.options.value.code_syntax_name + )); + } + }; + + // Render. + return ( + localize( + 'positronConvertToCodeModalDialogTitle', + "Convert to Code" + ))()} + width={400} + onAccept={() => props.renderer.dispose()} + onCancel={() => props.renderer.dispose()} + > + + ( + + )} + entries={syntaxDropdownEntries()} + title={syntaxDropdownTitle()} + onSelectionChanged={onSelectionChanged} + /> +
+					{codeString}
+				
+
+ +
+ ); +}; diff --git a/src/vs/workbench/browser/positronNewFolderFlow/components/steps/dropdownEntry.tsx b/src/vs/workbench/browser/positronNewFolderFlow/components/steps/dropdownEntry.tsx index 4675591bec45..ee8d34e0e815 100644 --- a/src/vs/workbench/browser/positronNewFolderFlow/components/steps/dropdownEntry.tsx +++ b/src/vs/workbench/browser/positronNewFolderFlow/components/steps/dropdownEntry.tsx @@ -35,7 +35,7 @@ export const DropdownEntry = (props: DropdownEntryProps) => {
{props.subtitle}
- {props.group ?
{props.group}
: null} + {props.group &&
{props.group}
} ); }; diff --git a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerActions.ts b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerActions.ts index df49c98b98d9..43267893fb4f 100644 --- a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerActions.ts +++ b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerActions.ts @@ -18,13 +18,16 @@ import { INotificationService, Severity } from '../../../../platform/notificatio import { IPositronDataExplorerEditor } from './positronDataExplorerEditor.js'; import { IPositronDataExplorerService, PositronDataExplorerLayout } from '../../../services/positronDataExplorer/browser/interfaces/positronDataExplorerService.js'; import { PositronDataExplorerEditorInput } from './positronDataExplorerEditorInput.js'; -import { POSITRON_DATA_EXPLORER_IS_ACTIVE_EDITOR, POSITRON_DATA_EXPLORER_IS_COLUMN_SORTING, POSITRON_DATA_EXPLORER_IS_PLAINTEXT, POSITRON_DATA_EXPLORER_LAYOUT } from './positronDataExplorerContextKeys.js'; +import { POSITRON_DATA_EXPLORER_IS_ACTIVE_EDITOR, POSITRON_DATA_EXPLORER_IS_COLUMN_SORTING, POSITRON_DATA_EXPLORER_IS_CONVERT_TO_CODE_ENABLED, POSITRON_DATA_EXPLORER_CODE_SYNTAXES_AVAILABLE, POSITRON_DATA_EXPLORER_IS_PLAINTEXT, POSITRON_DATA_EXPLORER_LAYOUT } from './positronDataExplorerContextKeys.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { PositronDataExplorerUri } from '../../../services/positronDataExplorer/common/positronDataExplorerUri.js'; import { EditorOpenSource } from '../../../../platform/editor/common/editor.js'; import { IPathService } from '../../../services/path/common/pathService.js'; import { toLocalResource } from '../../../../base/common/resources.js'; import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; +import { showConvertToCodeModalDialog } from '../../../browser/positronModalDialogs/convertToCodeModalDialog.js'; +import { IPositronDataExplorerInstance } from '../../../services/positronDataExplorer/browser/interfaces/positronDataExplorerInstance.js'; +import { CodeSyntaxName } from '../../../services/languageRuntime/common/positronDataExplorerComm.js'; /** * Positron data explorer action category. @@ -53,7 +56,9 @@ export const enum PositronDataExplorerCommandId { SummaryOnLeftAction = 'workbench.action.positronDataExplorer.summaryOnLeft', SummaryOnRightAction = 'workbench.action.positronDataExplorer.summaryOnRight', ClearColumnSortingAction = 'workbench.action.positronDataExplorer.clearColumnSorting', - OpenAsPlaintext = 'workbench.action.positronDataExplorer.openAsPlaintext' + OpenAsPlaintext = 'workbench.action.positronDataExplorer.openAsPlaintext', + ConvertToCodeAction = 'workbench.action.positronDataExplorer.convertToCode', + ConvertToCodeModalAction = 'workbench.action.positronDataExplorer.convertToCodeModal', } /** @@ -74,6 +79,58 @@ export const getPositronDataExplorerEditorFromEditorPane = ( return undefined; }; +const getPositronDataExplorerInstance = async ( + accessor: ServicesAccessor +): Promise => { + // Access the services we need. + const editorService = accessor.get(IEditorService); + const positronDataExplorerService = accessor.get(IPositronDataExplorerService); + const notificationService = accessor.get(INotificationService); + + /** + * Notifies the user that no data explorer instance was found. + */ + const notifyUserThatDataExplorerNotFound = () => { + // Notify the user. + notificationService.notify({ + severity: Severity.Error, + message: localize( + 'positron.dataExplorer.noActiveDataExplorer', + "No Positron Data Explorer found." + ), + sticky: false + }); + }; + // Get the Positron data explorer editor. + const positronDataExplorerEditor = getPositronDataExplorerEditorFromEditorPane( + editorService.activeEditorPane + ); + + // Make sure that the Positron data explorer editor was returned. + if (!positronDataExplorerEditor) { + notifyUserThatDataExplorerNotFound(); + return; + } + + // Get the identifier. + const identifier = positronDataExplorerEditor.identifier; + + // Make sure the identifier was returned. + if (!identifier) { + notifyUserThatDataExplorerNotFound(); + return; + } + const positronDataExplorerInstance = positronDataExplorerService.getInstance(identifier); + + if (!positronDataExplorerInstance) { + notifyUserThatDataExplorerNotFound(); + return; + } + + // Get the Positron data explorer instance. + return positronDataExplorerInstance; +}; + /** * PositronDataExplorerCopyAction action. */ @@ -688,6 +745,105 @@ class PositronDataExplorerClearColumnSortingAction extends Action2 { } } +/** + * PositronDataExplorerConvertToCodeAction action. + */ +class PositronDataExplorerConvertToCodeAction extends Action2 { + /** + * Constructor. + */ + constructor() { + super({ + id: PositronDataExplorerCommandId.ConvertToCodeAction, + title: { + value: localize('positronDataExplorer.convertToCode', 'Convert to Code'), + original: 'Convert to Code' + }, + category, + precondition: POSITRON_DATA_EXPLORER_IS_ACTIVE_EDITOR, + }); + } + + /** + * Runs the action. + * @param accessor The services accessor. + */ + async run(accessor: ServicesAccessor, desiredSyntax: CodeSyntaxName): Promise { + const positronDataExplorerInstance = await getPositronDataExplorerInstance(accessor); + if (!positronDataExplorerInstance) { + return undefined; + } + const code = await positronDataExplorerInstance.convertToCode(desiredSyntax); + + // Export filters as code. + return code; + } +} + + +/** + * The PositronDataExplorerConvertToCodeModalAction. + */ +class PositronDataExplorerConvertToCodeModalAction extends Action2 { + /** + * Constructor. + */ + constructor() { + super({ + id: PositronDataExplorerCommandId.ConvertToCodeModalAction, + title: { + value: localize('positronDataExplorer.convertToCodeModal', 'Convert to Code'), + original: 'Convert to Code' + }, + category, + positronActionBarOptions: { + controlType: 'button', + displayTitle: true, + }, + f1: true, + precondition: ContextKeyExpr.and( + POSITRON_DATA_EXPLORER_IS_ACTIVE_EDITOR, + POSITRON_DATA_EXPLORER_CODE_SYNTAXES_AVAILABLE + ), + icon: Codicon.code, + menu: [ + { + id: MenuId.EditorActionsLeft, + when: ContextKeyExpr.and( + POSITRON_DATA_EXPLORER_IS_ACTIVE_EDITOR, + POSITRON_DATA_EXPLORER_IS_CONVERT_TO_CODE_ENABLED + ), + }, + { + id: MenuId.EditorTitle, + group: 'navigation', + when: ContextKeyExpr.and( + POSITRON_DATA_EXPLORER_IS_ACTIVE_EDITOR, + POSITRON_DATA_EXPLORER_IS_CONVERT_TO_CODE_ENABLED + ), + } + ] + }); + } + + /** + * Runs action. + * @param accessor The services accessor. + */ + override async run(accessor: ServicesAccessor): Promise { + // Access the services we need. + const positronDataExplorerInstance = await getPositronDataExplorerInstance(accessor); + + if (!positronDataExplorerInstance) { + return undefined; + } + + await showConvertToCodeModalDialog( + positronDataExplorerInstance, + ); + } +} + /** * PositronDataExplorerOpenAsPlaintextAction action. */ @@ -787,4 +943,6 @@ export function registerPositronDataExplorerActions() { registerAction2(PositronDataExplorerSummaryOnRightAction); registerAction2(PositronDataExplorerClearColumnSortingAction); registerAction2(PositronDataExplorerOpenAsPlaintextAction); + registerAction2(PositronDataExplorerConvertToCodeAction); + registerAction2(PositronDataExplorerConvertToCodeModalAction); } diff --git a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerContextKeys.ts b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerContextKeys.ts index f358a402343e..721752d98ee3 100644 --- a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerContextKeys.ts +++ b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerContextKeys.ts @@ -30,3 +30,11 @@ export const POSITRON_DATA_EXPLORER_IS_PLAINTEXT = new RawContextKey( 'positronDataExplorerIsPlaintext', false ); +export const POSITRON_DATA_EXPLORER_IS_CONVERT_TO_CODE_ENABLED = new RawContextKey( + 'positronDataExplorerIsConvertToCodeEnabled', + false +); +export const POSITRON_DATA_EXPLORER_CODE_SYNTAXES_AVAILABLE = new RawContextKey( + 'positronDataExplorerCodeSyntaxesAvailable', + false +); diff --git a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.tsx b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.tsx index 823735fb47f0..2156e4269b6b 100644 --- a/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.tsx +++ b/src/vs/workbench/contrib/positronDataExplorerEditor/browser/positronDataExplorerEditor.tsx @@ -27,7 +27,10 @@ import { PositronDataExplorerUri } from '../../../services/positronDataExplorer/ import { IPositronDataExplorerService, PositronDataExplorerLayout } from '../../../services/positronDataExplorer/browser/interfaces/positronDataExplorerService.js'; import { PositronDataExplorerEditorInput } from './positronDataExplorerEditorInput.js'; import { PositronDataExplorerClosed, PositronDataExplorerClosedStatus } from '../../../browser/positronDataExplorer/components/dataExplorerClosed/positronDataExplorerClosed.js'; -import { POSITRON_DATA_EXPLORER_IS_COLUMN_SORTING, POSITRON_DATA_EXPLORER_IS_PLAINTEXT, POSITRON_DATA_EXPLORER_LAYOUT } from './positronDataExplorerContextKeys.js'; +import { POSITRON_DATA_EXPLORER_CODE_SYNTAXES_AVAILABLE, POSITRON_DATA_EXPLORER_IS_COLUMN_SORTING, POSITRON_DATA_EXPLORER_IS_CONVERT_TO_CODE_ENABLED, POSITRON_DATA_EXPLORER_IS_PLAINTEXT, POSITRON_DATA_EXPLORER_LAYOUT } from './positronDataExplorerContextKeys.js'; +import { checkDataExplorerConvertToCodeEnabled, DATA_EXPLORER_CONVERT_TO_CODE } from '../../../services/positronDataExplorer/common/positronDataExplorerConvertToCodeConfig.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { SupportStatus } from '../../../services/languageRuntime/common/positronDataExplorerComm.js'; /** * IPositronDataExplorerEditorOptions interface. @@ -93,6 +96,16 @@ export class PositronDataExplorerEditor extends EditorPane implements IPositronD */ private readonly _isPlaintextContextKey: IContextKey; + /** + * Gets the is convert to code enabled context key. + */ + private readonly _isConvertToCodeEnabledContextKey: IContextKey; + + /** + * Gets the code syntaxes available context key. + */ + private readonly _codeSyntaxesAvailableContextKey: IContextKey; + /** * The onSizeChanged event emitter. */ @@ -202,6 +215,7 @@ export class PositronDataExplorerEditor extends EditorPane implements IPositronD */ constructor( readonly _group: IEditorGroup, + @IConfigurationService private readonly _configurationService: IConfigurationService, @IPositronDataExplorerService private readonly _positronDataExplorerService: IPositronDataExplorerService, @IStorageService storageService: IStorageService, @ITelemetryService telemetryService: ITelemetryService, @@ -229,6 +243,27 @@ export class PositronDataExplorerEditor extends EditorPane implements IPositronD this._isPlaintextContextKey = POSITRON_DATA_EXPLORER_IS_PLAINTEXT.bindTo( this._group.scopedContextKeyService ); + this._isConvertToCodeEnabledContextKey = POSITRON_DATA_EXPLORER_IS_CONVERT_TO_CODE_ENABLED.bindTo( + this._group.scopedContextKeyService + ); + this._codeSyntaxesAvailableContextKey = POSITRON_DATA_EXPLORER_CODE_SYNTAXES_AVAILABLE.bindTo( + this._group.scopedContextKeyService + ); + + // Set the convert to code context key based on the configuration value. + this._isConvertToCodeEnabledContextKey.set( + checkDataExplorerConvertToCodeEnabled(this._configurationService) + ); + + + // Listen for configuration changes to the convert to code setting and update the context key accordingly. + this._register(this._configurationService.onDidChangeConfiguration(event => { + if (event.affectsConfiguration(DATA_EXPLORER_CONVERT_TO_CODE)) { + this._isConvertToCodeEnabledContextKey.set( + checkDataExplorerConvertToCodeEnabled(this._configurationService) + ); + } + })); } /** @@ -319,6 +354,14 @@ export class PositronDataExplorerEditor extends EditorPane implements IPositronD input.setName?.(`Data: ${display_name}`); } + // set context keys for convert to code and code syntaxes availability + const convertToCode = backendState.supported_features.convert_to_code; + if (backendState.supported_features.convert_to_code.support_status === SupportStatus.Unsupported) { + this._isConvertToCodeEnabledContextKey.set(false); + } + this._codeSyntaxesAvailableContextKey.set( + !!(convertToCode.code_syntaxes && convertToCode.code_syntaxes.length > 0) + ); }); // Set the context keys. diff --git a/src/vs/workbench/services/languageRuntime/common/languageRuntimeDataExplorerClient.ts b/src/vs/workbench/services/languageRuntime/common/languageRuntimeDataExplorerClient.ts index 76b18a56f898..1e69d5e5c67a 100644 --- a/src/vs/workbench/services/languageRuntime/common/languageRuntimeDataExplorerClient.ts +++ b/src/vs/workbench/services/languageRuntime/common/languageRuntimeDataExplorerClient.ts @@ -570,7 +570,8 @@ export class DataExplorerClientInstance extends Disposable { private async isSyntaxSupported(syntax: CodeSyntaxName, backendState: BackendState): Promise { await this.isConvertToCodeSupported(backendState); - if (syntax && !backendState.supported_features.convert_to_code.code_syntaxes?.some(s => s === syntax)) { + + if (syntax && !backendState.supported_features.convert_to_code.code_syntaxes?.some(s => s.code_syntax_name === syntax.code_syntax_name)) { throw new Error(`Code syntax "${syntax}" is not supported by the backend.`); } } diff --git a/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerConvertToCodeConfig.ts b/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerConvertToCodeConfig.ts new file mode 100644 index 000000000000..2e66e24d399b --- /dev/null +++ b/src/vs/workbench/services/positronDataExplorer/common/positronDataExplorerConvertToCodeConfig.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2025 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from '../../../../nls.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { + ConfigurationScope, + Extensions, + IConfigurationRegistry, +} from '../../../../platform/configuration/common/configurationRegistry.js'; +import { Registry } from '../../../../platform/registry/common/platform.js'; +import { positronConfigurationNodeBase } from '../../languageRuntime/common/languageRuntime.js'; + +// Key for the configuration setting +export const DATA_EXPLORER_CONVERT_TO_CODE = + 'dataExplorer.convertToCode'; + + +/** + * Retrieves the value of the configuration setting that determines whether to enable + * the convert to code feature in the Positron Data Explorer. + * @param configurationService The configuration service + * @returns Whether to enable the convert to code feature in the Positron Data Explorer + */ +export function checkDataExplorerConvertToCodeEnabled( + configurationService: IConfigurationService +) { + return Boolean( + configurationService.getValue(DATA_EXPLORER_CONVERT_TO_CODE) + ); +} + +// Register the configuration setting +const configurationRegistry = Registry.as( + Extensions.Configuration +); +configurationRegistry.registerConfiguration({ + ...positronConfigurationNodeBase, + scope: ConfigurationScope.MACHINE_OVERRIDABLE, + properties: { + [DATA_EXPLORER_CONVERT_TO_CODE]: { + type: 'boolean', + default: false, + markdownDescription: localize( + 'positron.dataExplorer.convertToCode', + '**CAUTION**: Enable experimental Data Explorer "Convert to Code" feature. This feature is experimental and may not work as expected. Use at your own risk.' + ), + }, + }, +});