diff --git a/package-lock.json b/package-lock.json index f3db529f..ec756446 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-db2i", - "version": "1.11.0", + "version": "1.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-db2i", - "version": "1.11.0", + "version": "1.12.0", "dependencies": { "@ibm/mapepire-js": "^0.5.0", "@octokit/rest": "^21.1.1", diff --git a/package.json b/package.json index 45eecc49..6a0b65ea 100644 --- a/package.json +++ b/package.json @@ -135,6 +135,26 @@ } } }, + { + "id": "vscode-db2i.codegen", + "title": "Code generation", + "properties": { + "vscode-db2i.codegen.rpgSymbolicNameSource": { + "type": "string", + "order": 0, + "description": "Descriptor to use for RPG symbolic names in data structure", + "default": "Name", + "enum": [ + "Name", + "Label" + ], + "enumDescriptions": [ + "Use the column name", + "Use the column label\n'Extended metadata' must be set to true in the JDBC configuration" + ] + } + } + }, { "id": "vscode-db2i.visualExplain", "title": "Visual Explain", diff --git a/src/views/results/codegen.test.ts b/src/views/results/codegen.test.ts index 375c8ac6..a3e858f4 100644 --- a/src/views/results/codegen.test.ts +++ b/src/views/results/codegen.test.ts @@ -1,7 +1,35 @@ import { assert, expect, test } from 'vitest' -import { columnToRpgDefinition, queryResultToRpgDs } from './codegen'; +import { columnToRpgDefinition, columnToRpgFieldName, queryResultToRpgDs } from './codegen'; import { QueryResult } from '@ibm/mapepire-js'; +test('Column to RPG symbol', () => { + let name; + + name = columnToRpgFieldName({display_size: 0, label: 'änderungs- benutzer ', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('anderungs_benutzer'); + + name = columnToRpgFieldName({display_size: 0, label: 'änderungs- benutzer ', name: 'ANDBEN', type: 'CHAR', precision: 10, scale: 0}, 'Name'); + expect(name).toBe('andben'); + + name = columnToRpgFieldName({display_size: 0, label: 'Cust.number....:', name: 'CUSNUM', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('cust_number'); + + name = columnToRpgFieldName({display_size: 0, label: 'Cust. name.... : ', name: 'CUSNAM', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('cust_name'); + + name = columnToRpgFieldName({display_size: 0, label: 'Country:', name: 'C1', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('country'); + + name = columnToRpgFieldName({display_size: 0, label: 'På bærtur', name: 'PB1', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('paa_baertur'); + + name = columnToRpgFieldName({display_size: 0, label: 'öäß', name: 'ABCD', type: 'CHAR', precision: 10, scale: 0}, 'Label'); + expect(name).toBe('oas'); + + name = columnToRpgFieldName({display_size: 0, label: '', name: '0001', type: 'INTEGER', precision: 0, scale: 0}, 'Name'); + expect(name).toBe('col0001'); +}); + test('Column to RPG definition', () => { let rpgdef; diff --git a/src/views/results/codegen.ts b/src/views/results/codegen.ts index 9b488059..b90ceb75 100644 --- a/src/views/results/codegen.ts +++ b/src/views/results/codegen.ts @@ -1,15 +1,36 @@ import { ColumnMetaData, QueryResult } from "@ibm/mapepire-js"; -export function queryResultToRpgDs(result: QueryResult) : string { +export function queryResultToRpgDs(result: QueryResult, source: string = 'Name') : string { let content = `dcl-ds row_t qualified template;\n`; for (let i = 0; i < result.metadata.column_count; i++) { - const name = `${isNaN(+result.metadata.columns[i].label.charAt(0)) ? '' : 'col'}${result.metadata.columns[i].label.toLowerCase()}` + const name = columnToRpgFieldName(result.metadata.columns[i], source); content += ` ${name} ${columnToRpgDefinition(result.metadata.columns[i])};\n`; } content += `end-ds;\n`; return content; } +export function columnToRpgFieldName(column: ColumnMetaData, source: string = 'Name') : string { + let name = source === 'Label' ? column.label.toLowerCase().trim() : column.name.toLowerCase().trim(); + name = name.replace(/\u00fc/g, "u") // ü -> u + .replace(/\u00e4/g, "a") // ä -> a + .replace(/\u00f6/g, "o") // ö -> o + .replace(/\u00df/g, "s") // sharp s/Eszett -> s + .replace(/\u00e6/g, "ae") // æ -> ae + .replace(/\u00f8/g, "oe") // ø -> oe + .replace(/\u00e5/g, "aa") // å -> aa + .replace(/[ .:]+$/g, "") // remove trailing space, "." and ":" + .replace(/[.]/g, "_") // "." between words to underscore + .replace(/\s+/g, "_") // remaining whitespaces to underscore + .replace(/[^a-zA-Z0-9_]/g, "") // remove non-alphanumeric chars + .replace(/\_+/i, "_") // replace multiple underscores with single underscore + .trim(); + if (!isNaN(+name.charAt(0))) { + name = `col` + name; + } + return name; +} + export function columnToRpgDefinition(column: ColumnMetaData) : string { switch (column.type) { case `NUMERIC`: diff --git a/src/views/results/contributes.json b/src/views/results/contributes.json index 9f4f529d..9cbb4c69 100644 --- a/src/views/results/contributes.json +++ b/src/views/results/contributes.json @@ -25,6 +25,26 @@ "default": false } } + }, + { + "id": "vscode-db2i.codegen", + "title": "Code generation", + "properties": { + "vscode-db2i.codegen.rpgSymbolicNameSource": { + "type": "string", + "order": 0, + "description": "Descriptor to use for RPG symbolic names in data structure", + "default": "Name", + "enum": [ + "Name", + "Label" + ], + "enumDescriptions": [ + "Use the column name", + "Use the column label\n'Extended metadata' must be set to true in the JDBC configuration" + ] + } + } } ], "views": { diff --git a/src/views/results/index.ts b/src/views/results/index.ts index 82837aa1..28160a5e 100644 --- a/src/views/results/index.ts +++ b/src/views/results/index.ts @@ -18,6 +18,7 @@ import { updateStatusBar } from "../jobManager/statusBar"; import { DbCache } from "../../language/providers/logic/cache"; import { ExplainType } from "../../connection/types"; import { queryResultToRpgDs } from "./codegen"; +import Configuration from "../../configuration"; export type StatementQualifier = "statement" | "update" | "explain" | "onlyexplain" | "json" | "csv" | "cl" | "sql" | "rpg"; @@ -390,7 +391,7 @@ async function runHandler(options?: StatementInfo) { let content = `**free\n\n` + `// statement: ${statementDetail.content}\n\n` + `// Row data structure\n` - + queryResultToRpgDs(result); + + queryResultToRpgDs(result, Configuration.get(`codegen.rpgSymbolicNameSource`)); const textDoc = await vscode.workspace.openTextDocument({ language: 'rpgle', content }); await vscode.window.showTextDocument(textDoc); chosenView.setLoadingText(`RPG data structure generated.`, false);