From b654ec0fed6d6c4569c4b66b6bfc3345001a0908 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Wed, 18 Sep 2024 15:06:55 -0400 Subject: [PATCH 1/3] Initial work for peek for objects Signed-off-by: worksofliam --- src/database/schemas.ts | 19 ++++++- src/language/providers/index.ts | 4 +- src/language/providers/peekProvider.ts | 73 ++++++++++++++++++++++++++ src/views/schemaBrowser/index.ts | 20 +------ 4 files changed, 96 insertions(+), 20 deletions(-) create mode 100644 src/language/providers/peekProvider.ts diff --git a/src/database/schemas.ts b/src/database/schemas.ts index 6da419d4..a5a45438 100644 --- a/src/database/schemas.ts +++ b/src/database/schemas.ts @@ -1,4 +1,5 @@ +import { spec } from "node:test/reporters"; import { getInstance } from "../base"; import { JobManager } from "../config"; @@ -15,6 +16,22 @@ const typeMap = { export const AllSQLTypes: SQLType[] = ["tables", "views", "aliases", "constraints", "functions", "variables", "indexes", "procedures", "sequences", "packages", "triggers", "types", "logicals"]; +export const InternalTypes: {[t: string]: string} = { + "tables": `table`, + "views": `view`, + "aliases": `alias`, + "constraints": `constraint`, + "functions": `function`, + "variables": `variable`, + "indexes": `index`, + "procedures": `procedure`, + "sequences": `sequence`, + "packages": `package`, + "triggers": `trigger`, + "types": `type`, + "logicals": `logical` +} + export const SQL_ESCAPE_CHAR = `\\`; type BasicColumnType = string|number; @@ -211,7 +228,7 @@ export default class Schemas { schema: object.BASE_SCHEMA || undefined, name: object.BASE_OBJ || undefined } - })); + } as BasicSQLObject)); } /** diff --git a/src/language/providers/index.ts b/src/language/providers/index.ts index 07a54872..cf463170 100644 --- a/src/language/providers/index.ts +++ b/src/language/providers/index.ts @@ -2,6 +2,7 @@ import { completionProvider } from "./completionProvider"; import { formatProvider } from "./formatProvider"; import { hoverProvider, openProvider } from "./hoverProvider"; import { signatureProvider } from "./parameterProvider"; +import { peekProvider } from "./peekProvider"; export function languageInit() { let functionality = []; @@ -11,7 +12,8 @@ export function languageInit() { formatProvider, signatureProvider, hoverProvider, - openProvider + openProvider, + peekProvider ); return functionality; diff --git a/src/language/providers/peekProvider.ts b/src/language/providers/peekProvider.ts new file mode 100644 index 00000000..7882fa00 --- /dev/null +++ b/src/language/providers/peekProvider.ts @@ -0,0 +1,73 @@ +import { env, Hover, languages, MarkdownString, workspace } from "vscode"; +import { getSqlDocument } from "./logic/parse"; +import { DbCache, LookupResult, RoutineDetail } from "./logic/cache"; +import { JobManager } from "../../config"; +import Statement from "../../database/statement"; +import { getParmAttributes, prepareParamType } from "./logic/completion"; +import { StatementType } from "../sql/types"; +import { remoteAssistIsEnabled } from "./logic/available"; +import { getPositionData } from "./logic/callable"; +import { CallableSignature } from "../../database/callable"; +import Schemas, { AllSQLTypes, InternalTypes, SQLType } from "../../database/schemas"; + +const standardObjects: SQLType[] = AllSQLTypes.filter(type => ![`functions`, `procedures`].includes(type)); + +export const peekProvider = languages.registerDefinitionProvider({ language: `sql` }, { + async provideDefinition(document, position, token) { + if (!remoteAssistIsEnabled()) return; + console.log(`peekProvider`); + + const defaultSchema = getDefaultSchema(); + const sqlDoc = getSqlDocument(document); + const offset = document.offsetAt(position); + + const tokAt = sqlDoc.getTokenByOffset(offset); + const statementAt = sqlDoc.getStatementByOffset(offset); + + if (statementAt) { + const refs = statementAt.getObjectReferences(); + const possibleNames = refs.map(ref => ref.object.name).filter(name => name); + + const ref = refs.find(ref => ref.tokens[0].range.start && offset <= ref.tokens[ref.tokens.length - 1].range.end); + + if (ref) { + const name = Statement.noQuotes(Statement.delimName(ref.object.name, true)); + const schema = Statement.noQuotes(Statement.delimName(ref.object.schema || defaultSchema, true)); + + let types: SQLType[] = standardObjects; + + if (ref.isUDTF) { + types = [`functions`]; + } else if (statementAt.type === StatementType.Call) { + types = [`procedures`]; + } + + const possibleObjects = await Schemas.getObjects(schema, types, {filter: name}); + + if (possibleObjects.length) { + const lines: string[] = []; + for (const obj of possibleObjects) { + const type = InternalTypes[obj.type]; + if (type) { + const contents = await Schemas.generateSQL(obj.schema, obj.name, type.toUpperCase()); + lines.push(contents, ``, ``); + } + } + + const document = await workspace.openTextDocument({ content: lines.join(`\n`), language: `sql` }); + + return { + uri: document.uri, + range: document.lineAt(0).range, + }; + } + + } + } + } +}); + +const getDefaultSchema = (): string => { + const currentJob = JobManager.getSelection(); + return currentJob && currentJob.job.options.libraries[0] ? currentJob.job.options.libraries[0] : `QGPL`; +} \ No newline at end of file diff --git a/src/views/schemaBrowser/index.ts b/src/views/schemaBrowser/index.ts index 4503ced3..fb8d45e4 100644 --- a/src/views/schemaBrowser/index.ts +++ b/src/views/schemaBrowser/index.ts @@ -1,7 +1,7 @@ import { ThemeIcon, TreeItem } from "vscode" import * as vscode from "vscode" -import Schemas, { AllSQLTypes, SQL_ESCAPE_CHAR, SQLType } from "../../database/schemas"; +import Schemas, { AllSQLTypes, InternalTypes, SQL_ESCAPE_CHAR, SQLType } from "../../database/schemas"; import Table from "../../database/table"; import { getInstance, loadBase } from "../../base"; @@ -12,22 +12,6 @@ import Statement from "../../database/statement"; import { copyUI } from "./copyUI"; import { getAdvisedIndexesStatement, getIndexesStatement, getMTIStatement } from "./statements"; -const viewItem = { - "tables": `table`, - "views": `view`, - "aliases": `alias`, - "constraints": `constraint`, - "functions": `function`, - "variables": `variable`, - "indexes": `index`, - "procedures": `procedure`, - "sequences": `sequence`, - "packages": `package`, - "triggers": `trigger`, - "types": `type`, - "logicals": `logical` -} - const itemIcons = { "table": `split-horizontal`, "procedure": `run`, @@ -552,7 +536,7 @@ class SQLObject extends vscode.TreeItem { } constructor(item: BasicSQLObject) { - const type = viewItem[item.type]; + const type = InternalTypes[item.type]; super(Statement.prettyName(item.name), Types[type] ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None); this.contextValue = type; From 9f5ea2dc46290bba4304594545afd67af176a3b0 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Tue, 3 Dec 2024 15:03:01 -0500 Subject: [PATCH 2/3] Slim down definition and use formatter Signed-off-by: worksofliam --- src/database/schemas.ts | 25 ++++++++++++++++++++----- src/language/providers/peekProvider.ts | 6 +++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/database/schemas.ts b/src/database/schemas.ts index a5a45438..4e9b8ef5 100644 --- a/src/database/schemas.ts +++ b/src/database/schemas.ts @@ -1,5 +1,4 @@ -import { spec } from "node:test/reporters"; import { getInstance } from "../base"; import { JobManager } from "../config"; @@ -235,10 +234,26 @@ export default class Schemas { * @param schema Not user input * @param object Not user input */ - static async generateSQL(schema: string, object: string, internalType: string): Promise { - const lines = await JobManager.runSQL<{ SRCDTA: string }>([ - `CALL QSYS2.GENERATE_SQL(?, ?, ?, CREATE_OR_REPLACE_OPTION => '1', PRIVILEGES_OPTION => '0')` - ].join(` `), { parameters: [object, schema, internalType] }); + static async generateSQL(schema: string, object: string, internalType: string, basic?: boolean): Promise { + let statement = `CALL QSYS2.GENERATE_SQL(?, ?, ?, CREATE_OR_REPLACE_OPTION => '1', PRIVILEGES_OPTION => '0')`; + + if (basic) { + let options: string[] = [ + `CREATE_OR_REPLACE_OPTION => '0'`, + `PRIVILEGES_OPTION => '0'`, + `COMMENT_OPTION => '0'`, + `LABEL_OPTION => '0'`, + `HEADER_OPTION => '0'`, + `TRIGGER_OPTION => '0'`, + `CONSTRAINT_OPTION => '0'`, + // `PRIVILEGES_OPTION => '0'`, + // `ACTIVATE_ACCESS_CONTROL_OPTION => '0'`, + `MASK_AND_PERMISSION_OPTION => '0'`, + ]; + statement = `CALL QSYS2.GENERATE_SQL(?, ?, ?, ${options.join(`, `)})`; + } + + const lines = await JobManager.runSQL<{ SRCDTA: string }>(statement, { parameters: [object, schema, internalType] }); const generatedStatement = lines.map(line => line.SRCDTA).join(`\n`); diff --git a/src/language/providers/peekProvider.ts b/src/language/providers/peekProvider.ts index 7882fa00..9b171a82 100644 --- a/src/language/providers/peekProvider.ts +++ b/src/language/providers/peekProvider.ts @@ -45,12 +45,12 @@ export const peekProvider = languages.registerDefinitionProvider({ language: `sq const possibleObjects = await Schemas.getObjects(schema, types, {filter: name}); if (possibleObjects.length) { - const lines: string[] = []; + const lines: string[] = [`-- Condensed version of the object definition`]; for (const obj of possibleObjects) { const type = InternalTypes[obj.type]; if (type) { - const contents = await Schemas.generateSQL(obj.schema, obj.name, type.toUpperCase()); - lines.push(contents, ``, ``); + const contents = await Schemas.generateSQL(obj.schema, obj.name, type.toUpperCase(), true); + lines.push(Statement.format(contents), ``, ``); } } From d55a23baccb315f14ab3a27530b66441a36d802a Mon Sep 17 00:00:00 2001 From: worksofliam Date: Tue, 3 Dec 2024 15:05:06 -0500 Subject: [PATCH 3/3] Fix invalid comment references Signed-off-by: worksofliam --- .github/workflows/webpack_ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/webpack_ci.yaml b/.github/workflows/webpack_ci.yaml index 1156ea8a..61bff11a 100644 --- a/.github/workflows/webpack_ci.yaml +++ b/.github/workflows/webpack_ci.yaml @@ -57,7 +57,7 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: '👋 A new build is available for this PR based on ${{ github.event.pull_request.head.sha }}.\n * [Download here.](https://github.com/codefori/vscode-ibmi/actions/runs/${{ github.run_id }})\n* [Read more about how to test](https://github.com/codefori/vscode-ibmi/blob/master/.github/pr_testing_template.md)' + body: '👋 A new build is available for this PR based on ${{ github.event.pull_request.head.sha }}.\n * [Download here.](https://github.com/codefori/vscode-db2i/actions/runs/${{ github.run_id }})\n* [Read more about how to test](https://github.com/codefori/vscode-db2i/blob/master/.github/pr_testing_template.md)' }) - name: Update comment @@ -69,5 +69,5 @@ jobs: body: | 👋 A new build is available for this PR based on ${{ github.event.pull_request.head.sha }}. - * [Download here.](https://github.com/codefori/vscode-ibmi/actions/runs/${{ github.run_id }}) + * [Download here.](https://github.com/codefori/vscode-db2i/actions/runs/${{ github.run_id }}) * [Read more about how to test](https://github.com/codefori/vscode-ibmi/blob/master/.github/pr_testing_template.md) \ No newline at end of file