diff --git a/CHANGELOG.md b/CHANGELOG.md index 54e1c11..f91082c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ All notable changes to Simple DB are documented in this file. +## 0.1.5 - 2026-08-08 + +### Added + +- **Simple DB: Open Configuration** as the single Simple DB action exposed in the Command Palette. It opens the same Simple DB Activity Bar container as clicking the Simple DB icon. +- **Open Configuration** at the top of the SQL editor's **Simple DB** right-click submenu. +- A visible **Cancel / Close** choice in the SQL-file connection picker and connection-creation engine/SQLite selectors. + +### Changed + +- SQL-file actions are kept in the editor's **Simple DB** context submenu instead of the Command Palette: connection selection, statement/script execution, and definition/declaration navigation. +- Quick-pick/input flows can be dismissed with `Esc` or by clicking outside instead of forcing focus to stay in the prompt. + +### Fixed + +- Cancelling **Create New Connection...** from the SQL-file connection picker now closes the whole selection flow instead of reopening the parent picker. + ## 0.1.4 - 2026-08-08 ### Added diff --git a/README.md b/README.md index 15dbc49..0e31432 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Simple DB -Simple DB `0.1.4` is a Visual Studio Code extension written entirely in JavaScript for working with **SQLite, PostgreSQL, MySQL, SQL Server, and Oracle** through a single interface. +Simple DB `0.1.5` is a Visual Studio Code extension written entirely in JavaScript for working with **SQLite, PostgreSQL, MySQL, SQL Server, and Oracle** through a single interface. The extension opens regular VS Code SQL documents and loads `src/extension.js` directly: there is no TypeScript, `tsconfig.json`, `dist` folder, or compilation step. @@ -25,10 +25,10 @@ The extension opens regular VS Code SQL documents and loads `src/extension.js` d ## Quick start -1. Open **Simple DB** in the Activity Bar and choose **Create Connection**. +1. Open **Simple DB** in the Activity Bar and choose **Create Connection**. The Command Palette exposes one Simple DB entry, **Simple DB: Open Configuration**, which opens this same view. 2. Select the database engine and enter a connection name. SQLite uses the native file picker; network databases create a JSON profile with the correct default parameters. 3. Edit the generated JSON if needed, press `Ctrl+S`, then use **Test Connection** or **Connect** from the connection menu. Passwords stay outside JSON in VS Code `SecretStorage`. -4. Open or create any `.sql` file. Click the database icon or **Simple DB: Select Connection** in the status bar and choose the connection this file should use. The picker also offers **Create New Connection...**. If you press `Ctrl+Enter` before choosing, Simple DB opens the same picker and then attaches the selected connection automatically. +4. Open or create any `.sql` file. Right-click and use **Simple DB > Select / Change Connection...**, or click the database status-bar item, to choose the connection this file should use. The picker also offers **Create New Connection...**. If you press `Ctrl+Enter` before choosing, Simple DB opens the same picker and then attaches the selected connection automatically. 5. Press `Ctrl+Enter` (**Run Statement**) to run the selection or statement at the cursor. Press `F5` (**Run Script**) for the entire document. Saved SQL files keep their selected connection when closed and reopened. 6. Results appear automatically in the resizable **Simple DB — Results** panel below the SQL editor. `F12` / **Go to Definition** opens the implementation/source; **Go to Declaration** opens the declaration when the engine exposes a separate one. @@ -42,7 +42,7 @@ The extension opens regular VS Code SQL documents and loads `src/extension.js` d | SQL Server | `mssql` | databases, schemas, tables, views, procedures/functions, indexes, triggers, sequences, types, synonyms, `GO` | | Oracle | `oracledb` Thin | schemas, tables, views/materialized views, procedures/functions, packages, indexes, triggers, sequences, types, synonyms, PL/SQL | -Oracle uses the default `node-oracledb` Thin mode, so normal connections do not require Oracle Client to be installed. Simple DB `0.1.4` uses SQL authentication with a username and password for SQL Server. +Oracle uses the default `node-oracledb` Thin mode, so normal connections do not require Oracle Client to be installed. Simple DB `0.1.5` uses SQL authentication with a username and password for SQL Server. ## No imposed row limit by default @@ -145,13 +145,16 @@ The `id` is generated and managed by Simple DB. Do not change it. Use **Simple D Right-clicking inside an editor now shows a native **Simple DB** submenu with the main actions: +- Open Configuration - Select / Change Connection... - Run Statement (`Ctrl+Enter`) - Run Script (`F5`) - Go to Definition (`F12`) - Go to Declaration -The lower-right status bar shows the profile currently attached to the SQL file. **Select / Change Connection...** marks that profile in the picker and lets the file switch to another one. If there are no profiles yet, **Create New Connection...** is available directly in the picker. +**Open Configuration** opens the same Simple DB Activity Bar view as clicking the Simple DB icon, where all configured database connections and their explorer actions are available. The Command Palette intentionally exposes only **Simple DB: Open Configuration**; SQL-file actions stay in this right-click submenu, their keyboard shortcuts, and the SQL editor controls. + +The lower-right status bar shows the profile currently attached to the SQL file. **Select / Change Connection...** marks that profile in the picker and lets the file switch to another one. If there are no profiles yet, **Create New Connection...** is available directly in the picker. The picker and connection-creation prompts can be cancelled with **Cancel / Close**, `Esc`, or by clicking outside. Cancelling nested connection creation closes the selection flow instead of reopening it. Navigation resolves against the connection attached to the SQL file and opens database-provided source/DDL in a read-only virtual SQL document. Oracle package **Go to Declaration** targets the package specification while **Go to Definition** targets the package body. When an overload can be identified from the call arguments, Simple DB selects it; when several overloads remain valid, it presents all valid locations instead of guessing. `alias.column` references are resolved back to their underlying table/view column, Oracle and SQL Server synonyms are followed to the target object when it is local/resolvable, and navigation can continue from one opened database definition into another. If catalog/source permissions are insufficient, Simple DB reports that explicitly. diff --git a/package-lock.json b/package-lock.json index d89c864..0ea8ef0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "simple-db-suzdalenko", - "version": "0.1.4", + "version": "0.1.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "simple-db-suzdalenko", - "version": "0.1.4", + "version": "0.1.5", "license": "MIT", "dependencies": { "exceljs": "4.4.0", diff --git a/package.json b/package.json index 65bf907..fd60b57 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "simple-db-suzdalenko", "displayName": "Simple DB - SQL Client for SQLite, MySQL, PostgreSQL, SQL Server & Oracle", "description": "SQL client and database editor for SQLite, MySQL, PostgreSQL, SQL Server (MSSQL) and Oracle. Browse database objects, run queries, manage DDL and export results.", - "version": "0.1.4", + "version": "0.1.5", "publisher": "suzdalenko-dev", "license": "MIT", "icon": "resources/simple-db-marketplace.png", @@ -61,6 +61,12 @@ "main": "./src/extension.js", "contributes": { "commands": [ + { + "command": "simpleDb.openConfiguration", + "title": "Open Configuration", + "category": "Simple DB", + "icon": "$(settings-gear)" + }, { "command": "simpleDb.addConnection", "title": "Create Connection", @@ -253,6 +259,10 @@ }, "menus": { "commandPalette": [ + { + "command": "simpleDb.addConnection", + "when": "false" + }, { "command": "simpleDb.refreshConnections", "when": "false" @@ -285,10 +295,38 @@ "command": "simpleDb.deleteConnection", "when": "false" }, + { + "command": "simpleDb.newQuery", + "when": "false" + }, + { + "command": "simpleDb.changeEditorConnection", + "when": "false" + }, + { + "command": "simpleDb.executeCurrent", + "when": "false" + }, { "command": "simpleDb.executeSelection", "when": "false" }, + { + "command": "simpleDb.executeDocument", + "when": "false" + }, + { + "command": "simpleDb.goToDefinition", + "when": "false" + }, + { + "command": "simpleDb.goToDeclaration", + "when": "false" + }, + { + "command": "simpleDb.cancelQuery", + "when": "false" + }, { "command": "simpleDb.beginTransaction", "when": "false" @@ -450,30 +488,35 @@ } ], "simpleDb.editorContextMenu": [ + { + "command": "simpleDb.openConfiguration", + "when": "editorLangId == sql", + "group": "1_configuration@1" + }, { "command": "simpleDb.changeEditorConnection", "when": "editorLangId == sql && resourceScheme != simple-db-definition", - "group": "1_connection@1" + "group": "2_connection@1" }, { "command": "simpleDb.executeCurrent", "when": "editorLangId == sql && resourceScheme != simple-db-definition", - "group": "2_run@1" + "group": "3_run@1" }, { "command": "simpleDb.executeDocument", "when": "editorLangId == sql && resourceScheme != simple-db-definition", - "group": "2_run@2" + "group": "3_run@2" }, { "command": "simpleDb.goToDefinition", "when": "editorLangId == sql", - "group": "3_navigation@1" + "group": "4_navigation@1" }, { "command": "simpleDb.goToDeclaration", "when": "editorLangId == sql", - "group": "3_navigation@2" + "group": "4_navigation@2" } ] }, diff --git a/src/extension.js b/src/extension.js index 10381ac..202aa89 100644 --- a/src/extension.js +++ b/src/extension.js @@ -44,15 +44,21 @@ async function pickProfile(connectionStore, title = 'Simple DB — Select Connec if (!profiles.length) { throw new Error('No connections are configured. Create a connection first.'); } - const pick = await vscode.window.showQuickPick( - profiles.map((profile) => ({ - label: `$(database) ${profile.name}`, - description: getDatabaseEngine(profile.engine)?.displayName || profile.engine, - profile, - })), - { title, ignoreFocusOut: true }, - ); - return pick?.profile || null; + const items = profiles.map((profile) => ({ + label: `$(database) ${profile.name}`, + description: getDatabaseEngine(profile.engine)?.displayName || profile.engine, + profile, + })); + items.push({ + label: '$(close) Cancel / Close', + description: 'Dismiss this menu', + cancel: true, + }); + const pick = await vscode.window.showQuickPick(items, { + title, + ignoreFocusOut: false, + }); + return pick && !pick.cancel ? pick.profile : null; } function databaseContext(profile, node) { @@ -195,6 +201,10 @@ async function activate(context) { editorSessionManager, ); + registerCommand(context, 'simpleDb.openConfiguration', async () => { + await vscode.commands.executeCommand('workbench.view.extension.simpleDb'); + }); + registerCommand(context, 'simpleDb.addConnection', async (node) => { const form = await promptConnection({ engineId: node?.engineId }); if (!form) return; @@ -480,7 +490,7 @@ async function activate(context) { const name = await vscode.window.showInputBox({ title: `Simple DB — Create ${OBJECT_LABELS[objectType] || objectType}`, prompt: 'New object name', - ignoreFocusOut: true, + ignoreFocusOut: false, validateInput: (value) => (value.trim() ? null : 'Name is required.'), }); if (!name) return; diff --git a/src/managers/editorSessionManager.js b/src/managers/editorSessionManager.js index 1ff568e..d72adac 100644 --- a/src/managers/editorSessionManager.js +++ b/src/managers/editorSessionManager.js @@ -140,39 +140,43 @@ class EditorSessionManager { } async _pickConnection(session, document) { - while (true) { - const profiles = this.connectionStore.list(); - const items = profiles.map((profile) => ({ - label: - (profile.id === session?.profileId ? '$(check) ' : '$(database) ') + - profile.name, - description: getDatabaseEngine(profile.engine)?.displayName || profile.engine, - detail: databaseForProfile(profile), - profile, - })); - items.push({ + const profiles = this.connectionStore.list(); + const items = profiles.map((profile) => ({ + label: + (profile.id === session?.profileId ? '$(check) ' : '$(database) ') + + profile.name, + description: getDatabaseEngine(profile.engine)?.displayName || profile.engine, + detail: databaseForProfile(profile), + profile, + })); + items.push( + { label: '$(add) Create New Connection...', description: 'Simple DB', createConnection: true, - }); - const pick = await vscode.window.showQuickPick(items, { - title: 'Simple DB — Select Connection for SQL File', - placeHolder: session - ? 'Choose a different connection for this SQL file' - : 'Choose the connection this SQL file should use', - ignoreFocusOut: true, - }); - if (!pick) return null; - if (!pick.createConnection) return pick.profile; - - const created = await vscode.commands.executeCommand('simpleDb.addConnection'); - if (created) { - if (document) { - await vscode.window.showTextDocument(document, { preview: false }); - } - return created; - } + }, + { + label: '$(close) Cancel / Close', + description: 'Dismiss this menu', + cancel: true, + }, + ); + const pick = await vscode.window.showQuickPick(items, { + title: 'Simple DB — Select Connection for SQL File', + placeHolder: session + ? 'Choose a different connection for this SQL file' + : 'Choose the connection this SQL file should use', + ignoreFocusOut: false, + }); + if (!pick || pick.cancel) return null; + if (!pick.createConnection) return pick.profile; + + const created = await vscode.commands.executeCommand('simpleDb.addConnection'); + if (!created) return null; + if (document) { + await vscode.window.showTextDocument(document, { preview: false }); } + return created; } async ensureSession(document) { diff --git a/src/test/manifest.test.js b/src/test/manifest.test.js index 76e8b6a..7db507f 100644 --- a/src/test/manifest.test.js +++ b/src/test/manifest.test.js @@ -3,11 +3,31 @@ const manifest = require('../../package.json'); describe('extension manifest', () => { - it('ships 0.1.4 without the removed History feature', () => { - expect(manifest.version).toBe('0.1.4'); + it('ships 0.1.5 without the removed History feature', () => { + expect(manifest.version).toBe('0.1.5'); expect(JSON.stringify(manifest).toLowerCase()).not.toContain('history'); }); + it('exposes only Open Configuration in the Command Palette', () => { + const commandIds = manifest.contributes.commands.map((entry) => entry.command); + const hiddenIds = new Set( + manifest.contributes.menus.commandPalette + .filter((entry) => entry.when === 'false') + .map((entry) => entry.command), + ); + expect(commandIds.filter((commandId) => !hiddenIds.has(commandId))).toEqual([ + 'simpleDb.openConfiguration', + ]); + expect( + manifest.contributes.commands.find( + (command) => command.command === 'simpleDb.openConfiguration', + ), + ).toMatchObject({ + title: 'Open Configuration', + category: 'Simple DB', + }); + }); + it('puts connection selection next to query execution in SQL editors', () => { const editorTitle = manifest.contributes.menus['editor/title']; expect(editorTitle.map((entry) => entry.command)).toEqual([ @@ -29,6 +49,7 @@ describe('extension manifest', () => { it('keeps the SQL editor context menu focused on Simple DB daily actions', () => { const menu = manifest.contributes.menus['simpleDb.editorContextMenu']; expect(menu.map((entry) => entry.command)).toEqual([ + 'simpleDb.openConfiguration', 'simpleDb.changeEditorConnection', 'simpleDb.executeCurrent', 'simpleDb.executeDocument', diff --git a/src/test/resultViewHtml.test.js b/src/test/resultViewHtml.test.js index 63ba512..4c8fab6 100644 --- a/src/test/resultViewHtml.test.js +++ b/src/test/resultViewHtml.test.js @@ -21,7 +21,7 @@ describe('lower Results view', () => { const panel = packageJson.contributes.viewsContainers.panel; const views = packageJson.contributes.views.simpleDbResults; - expect(packageJson.version).toBe('0.1.4'); + expect(packageJson.version).toBe('0.1.5'); expect(panel).toContainEqual( expect.objectContaining({ id: 'simpleDbResults', title: 'Simple DB' }), ); diff --git a/src/ui/connectionForm.js b/src/ui/connectionForm.js index 6f2e856..38ff902 100644 --- a/src/ui/connectionForm.js +++ b/src/ui/connectionForm.js @@ -9,7 +9,7 @@ async function inputText(options) { prompt: options.prompt, value: options.value ?? '', password: options.password === true, - ignoreFocusOut: true, + ignoreFocusOut: false, validateInput: options.required ? (value) => (value.trim() ? undefined : 'This field is required.') : options.validateInput, @@ -22,14 +22,15 @@ async function chooseSqlitePath() { { label: '$(folder-opened) Open Existing Database', value: 'open' }, { label: '$(new-file) Create New Database', value: 'create' }, { label: '$(edit) Enter Path Manually', value: 'manual' }, + { label: '$(close) Cancel / Close', value: 'cancel' }, ], { title: 'Simple DB — SQLite Database', placeHolder: 'Choose the SQLite database file', - ignoreFocusOut: true, + ignoreFocusOut: false, }, ); - if (!mode) return undefined; + if (!mode || mode.value === 'cancel') return undefined; if (mode.value === 'open') { const selected = await vscode.window.showOpenDialog({ @@ -120,19 +121,22 @@ async function promptConnection(options = {}) { let engineId = options.engineId; if (!engineId) { const enginePick = await vscode.window.showQuickPick( - DATABASE_ENGINES.map((engine) => ({ - label: `$(database) ${engine.displayName}`, - description: - engine.defaultPort === null ? 'Local file' : `Default port ${engine.defaultPort}`, - value: engine.id, - })), + [ + ...DATABASE_ENGINES.map((engine) => ({ + label: `$(database) ${engine.displayName}`, + description: + engine.defaultPort === null ? 'Local file' : `Default port ${engine.defaultPort}`, + value: engine.id, + })), + { label: '$(close) Cancel / Close', value: 'cancel' }, + ], { title: 'Simple DB — Create Connection', placeHolder: 'Select a database engine', - ignoreFocusOut: true, + ignoreFocusOut: false, }, ); - if (!enginePick) return null; + if (!enginePick || enginePick.value === 'cancel') return null; engineId = enginePick.value; } diff --git a/src/views/sqlNavigationProvider.js b/src/views/sqlNavigationProvider.js index b1a3fb0..b028c2e 100644 --- a/src/views/sqlNavigationProvider.js +++ b/src/views/sqlNavigationProvider.js @@ -235,7 +235,7 @@ class SqlNavigationProvider { const pick = await vscode.window.showQuickPick(items, { title, placeHolder: 'Multiple database objects or overloads match this reference', - ignoreFocusOut: true, + ignoreFocusOut: false, }); if (pick) await this._openLocation(pick.location); }