From dde7b541be40d4fbbdf80a1d6fe01eaee809ebd9 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Tue, 23 Jul 2024 10:19:45 -0400 Subject: [PATCH 1/8] Remove SEU colour support Signed-off-by: worksofliam --- package.json | 5 - src/instantiate.ts | 6 - src/languages/general/SEUColorProvider.ts | 80 -------- src/languages/general/SEUColors.ts | 222 ---------------------- 4 files changed, 313 deletions(-) delete mode 100644 src/languages/general/SEUColorProvider.ts delete mode 100644 src/languages/general/SEUColors.ts diff --git a/package.json b/package.json index 43c14d4e4..87b715740 100644 --- a/package.json +++ b/package.json @@ -416,11 +416,6 @@ "default": true, "description": "If enabled, will log spool files from command executed. You can find it under Output for 'IBM i Compile Log'." }, - "code-for-ibmi.showSeuColors": { - "type": "boolean", - "default": false, - "description": "If enabled, will colourise lines like SEU. Only supports source members. Requires restart if changed." - }, "code-for-ibmi.clearOutputEveryTime": { "type": "boolean", "default": true, diff --git a/src/instantiate.ts b/src/instantiate.ts index c635ea346..3c7efe6d0 100644 --- a/src/instantiate.ts +++ b/src/instantiate.ts @@ -12,7 +12,6 @@ import { refreshDiagnosticsFromServer } from './api/errors/diagnostics'; import { setupGitEventHandler } from './api/local/git'; import { GetMemberInfo } from './components/getMemberInfo'; import { QSysFS, getUriFromPath, parseFSOptions } from "./filesystems/qsys/QSysFs"; -import { SEUColorProvider } from "./languages/general/SEUColorProvider"; import { t } from './locale'; import { Action, BrowserItem, DeploymentMethod, MemberItem, OpenEditableOptions, WithPath } from "./typings"; import { ActionsUI } from './webviews/actions'; @@ -788,11 +787,6 @@ export async function loadAllofExtension(context: vscode.ExtensionContext) { }) ); - // Color provider - if (GlobalConfiguration.get(`showSeuColors`)) { - SEUColorProvider.intitialize(context); - } - // Register git events based on workspace folders if (vscode.workspace.workspaceFolders) { setupGitEventHandler(context); diff --git a/src/languages/general/SEUColorProvider.ts b/src/languages/general/SEUColorProvider.ts deleted file mode 100644 index 1d08b6381..000000000 --- a/src/languages/general/SEUColorProvider.ts +++ /dev/null @@ -1,80 +0,0 @@ -import vscode from 'vscode'; - -import { SEUColors } from './SEUColors'; - -const hidden = vscode.window.createTextEditorDecorationType({ - letterSpacing: `-1em`, - opacity: `0`, -}); - -export namespace SEUColorProvider { - let _timeout: NodeJS.Timeout; - - export function intitialize(context: vscode.ExtensionContext) { - context.subscriptions.push( - vscode.workspace.onDidChangeTextDocument(event => { - clearTimeout(_timeout); - _timeout = setTimeout(() => { - refreshDocumentColors(event.document); - }, 2000); - }), - - vscode.window.onDidChangeActiveTextEditor(event => { - if (event?.document) { - refreshDocumentColors(event.document); - } - }) - - ); - } - - function refreshDocumentColors(document: vscode.TextDocument) { - if (document.uri.scheme === `member`) { - // This should only work for members. - // We don't want to support this everywhere because it's ugly. - - const activeEditor = vscode.window.activeTextEditor; - if (document.uri.path === activeEditor?.document.uri.path) { - const hiddenDecorations : vscode.DecorationOptions[] = []; - const colorDecorations : Record = {}; - - // Set up the arrays - SEUColors.forEach(name => { - colorDecorations[name] = []; - }); - - // Find the lines and the bytes and all that... - for (let lineIndex = 0; lineIndex < document.lineCount; lineIndex++) { - const line = document.lineAt(lineIndex); - - const lineBytes = Buffer.from(line.text); - - SEUColors.forEach((name, definition) => { - const byteIndex = lineBytes.indexOf(definition.bytes); - if (byteIndex >= 0) { - colorDecorations[name].push({ - range: new vscode.Range(lineIndex, byteIndex + definition.bytes.length - 1, lineIndex, line.text.length) - }); - - hiddenDecorations.push({ - range: new vscode.Range(lineIndex, byteIndex, lineIndex, byteIndex + 1), - renderOptions: { - after: { - contentText: ``.padEnd(definition.bytes.length), - } - } - }); - } - }) - } - - // Then set the decorations - SEUColors.forEach((name, definition) => { - activeEditor.setDecorations(definition.decoration, colorDecorations[name]); - }); - - activeEditor.setDecorations(hidden, hiddenDecorations); - } - } - } -} \ No newline at end of file diff --git a/src/languages/general/SEUColors.ts b/src/languages/general/SEUColors.ts deleted file mode 100644 index fa2130087..000000000 --- a/src/languages/general/SEUColors.ts +++ /dev/null @@ -1,222 +0,0 @@ -import vscode from 'vscode'; - -export namespace SEUColors { - interface Color { - bytes: Buffer - decoration: vscode.TextEditorDecorationType - } - - const ColorDefinitions: Record = { - blue: { - bytes: Buffer.from([194, 154]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#3565cc`, - rangeBehavior: vscode.DecorationRangeBehavior.ClosedOpen, - }) - }, - blue_ri: { - bytes: Buffer.from([194, 155]), - decoration: vscode.window.createTextEditorDecorationType({ - backgroundColor: `#3565cc`, - color: `white`, - rangeBehavior: vscode.DecorationRangeBehavior.ClosedOpen, - }) - }, - blue_ul: { - bytes: Buffer.from([194, 158]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#3565cc`, - textDecoration: `; border-bottom: 1px solid #3565cc;` - }) - }, - green: { - bytes: Buffer.from([194, 128]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#28d15d`, - }) - }, - green_ri: { - bytes: Buffer.from([194, 129]), - decoration: vscode.window.createTextEditorDecorationType({ - backgroundColor: `#28d15d`, - color: `black`, - }) - }, - green_ul: { - bytes: Buffer.from([194, 132]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#28d15d`, - textDecoration: `; border-bottom: 1px solid #28d15d;` - }) - }, - green_ul_ri: { - bytes: Buffer.from([7]), - decoration: vscode.window.createTextEditorDecorationType({ - backgroundColor: `#28d15d`, - color: `black`, - textDecoration: `; border-bottom: 1px solid black;` - }) - }, - pink: { - bytes: Buffer.from([194, 152]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#cf259c`, - }) - }, - pink_ri: { - bytes: Buffer.from([194, 153]), - decoration: vscode.window.createTextEditorDecorationType({ - backgroundColor: `#cf259c`, - color: `white`, - }) - }, - pink_ul: { - bytes: Buffer.from([20]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#cf259c`, - textDecoration: `; border-bottom: 1px solid #cf259c;` - }) - }, - pink_ul_ri: { - bytes: Buffer.from([21]), - decoration: vscode.window.createTextEditorDecorationType({ - backgroundColor: `#cf259c`, - color: `white`, - textDecoration: `; border-bottom: 1px solid white;` - }) - }, - red: { - bytes: Buffer.from([194, 136]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#cf2331`, - }) - }, - // red_bl and red are the same - red_bl: { - bytes: Buffer.from([194, 138]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#cf2331`, - }) - }, - red_ri: { - bytes: Buffer.from([194, 137]), - decoration: vscode.window.createTextEditorDecorationType({ - backgroundColor: `#cf2331`, - color: `white`, - }), - }, - // red_ri_bl and red_ri are the same - red_ri_bl: { - bytes: Buffer.from([194, 139]), - decoration: vscode.window.createTextEditorDecorationType({ - backgroundColor: `#cf2331`, - color: `white`, - }), - }, - red_ul: { - bytes: Buffer.from([194, 140]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#cf2331`, - textDecoration: `; border-bottom: 1px solid #cf2331;` - }) - }, - red_ri_ul: { - bytes: Buffer.from([5]), - decoration: vscode.window.createTextEditorDecorationType({ - backgroundColor: `#cf2331`, - color: `white`, - textDecoration: `; border-bottom: 1px solid white;` - }), - }, - turquoise: { - bytes: Buffer.from([194, 144]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#22c4d6`, - }) - }, - turquoise_ri: { - bytes: Buffer.from([194, 145]), - decoration: vscode.window.createTextEditorDecorationType({ - backgroundColor: `#22c4d6`, - color: `black`, - }) - }, - turquoise_ul: { - bytes: Buffer.from([194, 148]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#22c4d6`, - textDecoration: `; border-bottom: 1px solid #22c4d6;` - }) - }, - // turquoise_ul_ri is the same as turquoise_ri - turquoise_ul_ri: { - bytes: Buffer.from([194, 149]), - decoration: vscode.window.createTextEditorDecorationType({ - backgroundColor: `#22c4d6`, - color: `black`, - textDecoration: `; border-bottom: 1px solid black;` - }) - }, - white: { - bytes: Buffer.from([194, 130]), - decoration: vscode.window.createTextEditorDecorationType({ - light: { - color: `#000000`, - }, - dark: { - color: `#ffffff`, - } - }) - }, - white_ri: { - bytes: Buffer.from([194, 131]), - decoration: vscode.window.createTextEditorDecorationType({ - light: { - color: `#ffffff`, - backgroundColor: `#000000`, - }, - dark: { - color: `#000000`, - backgroundColor: `#ffffff`, - } - }) - }, - white_ul: { - bytes: Buffer.from([23]), - decoration: vscode.window.createTextEditorDecorationType({ - light: { - color: `#000000`, - textDecoration: `; border-bottom: 1px solid #000000;` - }, - dark: { - color: `#ffffff`, - textDecoration: `; border-bottom: 1px solid #ffffff;` - } - }) - }, - yellow: { - bytes: Buffer.from([22]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#f4c842`, - }) - }, - yellow_ri: { - bytes: Buffer.from([194, 147]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#000000`, - backgroundColor: `#f4c842` - }) - }, - yellow_ul: { - bytes: Buffer.from([194, 150]), - decoration: vscode.window.createTextEditorDecorationType({ - color: `#f4c842`, - textDecoration: `; border-bottom: 1px solid #f4c842;` - }) - } - }; - - export function forEach(cb: (name: string, color: Color) => void) { - Object.entries(ColorDefinitions).forEach(c => cb(c[0], c[1])); - } -} \ No newline at end of file From 885fe09c2da1bfa7b1a35ff8fb64fcb26c30f1ac Mon Sep 17 00:00:00 2001 From: worksofliam Date: Tue, 23 Jul 2024 10:22:55 -0400 Subject: [PATCH 2/8] Remove SQL saving logic to support SEU colours Signed-off-by: worksofliam --- src/filesystems/qsys/extendedContent.ts | 31 +++++-------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/src/filesystems/qsys/extendedContent.ts b/src/filesystems/qsys/extendedContent.ts index 477bd3154..4ba3a531f 100644 --- a/src/filesystems/qsys/extendedContent.ts +++ b/src/filesystems/qsys/extendedContent.ts @@ -10,10 +10,6 @@ const writeFileAsync = util.promisify(fs.writeFile); const DEFAULT_RECORD_LENGTH = 80; -// Translate x'25' to x'2F' and back, or x'25' will become x'0A' (linefeed)! -const SEU_GREEN_UL_RI = `x'25'`; -const SEU_GREEN_UL_RI_temp = `x'2F'`; - export class ExtendedIBMiContent { constructor(readonly sourceDateHandler: SourceDateHandler) { @@ -36,7 +32,6 @@ export class ExtendedIBMiContent { spf = connection.upperCaseName(spf); mbr = connection.upperCaseName(mbr); - const sourceColourSupport = GlobalConfiguration.get(`showSeuColors`); const tempLib = config.tempLibrary; const alias = getAliasName(lib, spf, mbr); const aliasPath = `${tempLib}.${alias}`; @@ -50,15 +45,9 @@ export class ExtendedIBMiContent { this.sourceDateHandler.recordLengths.set(alias, recordLength); } - let rows; - if (sourceColourSupport) - rows = await content.runSQL( - `select srcdat, rtrim(translate(srcdta, ${SEU_GREEN_UL_RI_temp}, ${SEU_GREEN_UL_RI})) as srcdta from ${aliasPath}` - ); - else - rows = await content.runSQL( - `select srcdat, srcdta from ${aliasPath}` - ); + let rows = await content.runSQL( + `select srcdat, srcdta from ${aliasPath}` + ); if (rows.length === 0) { rows.push({ @@ -130,7 +119,6 @@ export class ExtendedIBMiContent { const client = connection.client; const tempRmt = connection.getTempRemote(lib + spf + mbr); if (tempRmt) { - const sourceColourSupport = GlobalConfiguration.get(`showSeuColors`); const tmpobj = await tmpFile(); const sourceData = body.split(`\n`); @@ -146,16 +134,9 @@ export class ExtendedIBMiContent { sourceData[i] = sourceData[i].substring(0, recordLength); } - // We only want to do the translate when source colours at enabled. - // For large sources, translate adds a bunch of time to the saving process. - if (sourceColourSupport) - rows.push( - `(${sequence}, ${sourceDates[i] ? sourceDates[i].padEnd(6, `0`) : `0`}, translate('${escapeString(sourceData[i])}', ${SEU_GREEN_UL_RI}, ${SEU_GREEN_UL_RI_temp}))`, - ); - else - rows.push( - `(${sequence}, ${sourceDates[i] ? sourceDates[i].padEnd(6, `0`) : `0`}, '${escapeString(sourceData[i])}')`, - ); + rows.push( + `(${sequence}, ${sourceDates[i] ? sourceDates[i].padEnd(6, `0`) : `0`}, '${escapeString(sourceData[i])}')`, + ); } From db2562c0ec3522c3a01a143808ffde3d6cdd0788 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Fri, 2 Aug 2024 18:31:10 -0400 Subject: [PATCH 3/8] Initial work for bad codes cleanup Signed-off-by: worksofliam --- package.json | 5 ++++ src/api/Configuration.ts | 4 ++- src/instantiate.ts | 4 +++ src/languages/colour.ts | 63 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/languages/colour.ts diff --git a/package.json b/package.json index 87b715740..b4029f4eb 100644 --- a/package.json +++ b/package.json @@ -378,6 +378,11 @@ "type": "boolean", "default": false, "description": "Tells the debug service to send more data to the client. Only useful for debugging issues in the service." + }, + "autoFixInvalidCharacters": { + "type": "boolean", + "default": false, + "description": "Automatically fix invalid characters in source members when opened." } } } diff --git a/src/api/Configuration.ts b/src/api/Configuration.ts index 4dffbf84c..90cfe8b4d 100644 --- a/src/api/Configuration.ts +++ b/src/api/Configuration.ts @@ -134,6 +134,7 @@ export namespace ConnectionConfiguration { protectedPaths: string[]; showHiddenFiles: boolean; lastDownloadLocation:string; + autoFixInvalidCharacters?: boolean; [name: string]: any; } @@ -213,7 +214,8 @@ export namespace ConnectionConfiguration { defaultDeploymentMethod: parameters.defaultDeploymentMethod || ``, protectedPaths: (parameters.protectedPaths || []), showHiddenFiles: (parameters.showHiddenFiles === true || parameters.showHiddenFiles === undefined), - lastDownloadLocation: (parameters.lastDownloadLocation || os.homedir()) + lastDownloadLocation: (parameters.lastDownloadLocation || os.homedir()), + autoFixInvalidCharacters: (parameters.autoFixInvalidCharacters === true) } } diff --git a/src/instantiate.ts b/src/instantiate.ts index 3c7efe6d0..72785e7a5 100644 --- a/src/instantiate.ts +++ b/src/instantiate.ts @@ -16,6 +16,7 @@ import { t } from './locale'; import { Action, BrowserItem, DeploymentMethod, MemberItem, OpenEditableOptions, WithPath } from "./typings"; import { ActionsUI } from './webviews/actions'; import { VariablesUI } from "./webviews/variables"; +import { initialiseColourChecker } from './languages/colour'; export let instance: Instance; @@ -787,6 +788,9 @@ export async function loadAllofExtension(context: vscode.ExtensionContext) { }) ); + // Colour fixer + initialiseColourChecker(context); + // Register git events based on workspace folders if (vscode.workspace.workspaceFolders) { setupGitEventHandler(context); diff --git a/src/languages/colour.ts b/src/languages/colour.ts new file mode 100644 index 000000000..0c5ab8146 --- /dev/null +++ b/src/languages/colour.ts @@ -0,0 +1,63 @@ +import { ExtensionContext, Range, window, workspace, WorkspaceEdit } from "vscode"; +import { instance } from "../instantiate"; + +const NEW_LINE_NUMBERS = [10, 13]; + +export function initialiseColourChecker(context: ExtensionContext) { + context.subscriptions.push( + workspace.onDidOpenTextDocument(async (document) => { + if (document.uri.scheme === `member`) { + const content = document.getText(); + let hasInvalidCharacters = false; + for (let i = 0; i < content.length; i++) { + if (content.charCodeAt(i) < 32 && !NEW_LINE_NUMBERS.includes(content.charCodeAt(i))) { + hasInvalidCharacters = true; + break; + } + } + + const shouldFix = await shouldInitiateCleanup(); + + if (shouldFix) { + const fixedContent = replaceInvalidCharacters(content); + const edit = new WorkspaceEdit(); + edit.replace(document.uri, new Range(0, 0, document.lineCount, 0), fixedContent); + } + } + }) + ) +} + +async function shouldInitiateCleanup() { + const config = instance.getConfig() + + if (config?.autoFixInvalidCharacters) { + return true; + } + + const chosen = await window.showInformationMessage(`This member contains invalid characters. Would you like to clean it up?`, `Yes`, `Always`, `No`); + + if (chosen === `No`) { + return false; + } + + if (chosen === `Always` && config) { + config.autoFixInvalidCharacters = true; + await instance.setConfig(config); + } + + return true; +} + +function replaceInvalidCharacters(content: string) { + const chars = content.split(``); + + // return content.replace(/[\x00-\x1F]/g, ``); // This almost works, but we want to keep line feed / carriage return + for (let i = 0; i < content.length; i++) { + if (content.charCodeAt(i) < 32 && !NEW_LINE_NUMBERS.includes(content.charCodeAt(i))) { + chars[i] = ` `; + } + } + + return chars.join(``); +} \ No newline at end of file From 1a6e6c5c76eecac0df43a6dc879bc63f466a1f13 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Fri, 2 Aug 2024 18:40:29 -0400 Subject: [PATCH 4/8] Add new option to UI Signed-off-by: worksofliam --- src/api/Configuration.ts | 2 +- src/webviews/settings/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api/Configuration.ts b/src/api/Configuration.ts index 90cfe8b4d..ff7a2a343 100644 --- a/src/api/Configuration.ts +++ b/src/api/Configuration.ts @@ -134,7 +134,7 @@ export namespace ConnectionConfiguration { protectedPaths: string[]; showHiddenFiles: boolean; lastDownloadLocation:string; - autoFixInvalidCharacters?: boolean; + autoFixInvalidCharacters: boolean; [name: string]: any; } diff --git a/src/webviews/settings/index.ts b/src/webviews/settings/index.ts index 7694d390d..85f23cb34 100644 --- a/src/webviews/settings/index.ts +++ b/src/webviews/settings/index.ts @@ -78,6 +78,7 @@ export class SettingsUI { sourceTab .addInput(`sourceASP`, `Source ASP`, `If source files live within a specific ASP, please specify it here. Leave blank otherwise. You can ignore this if you have access to QSYS2.ASP_INFO as Code for IBM i will fetch ASP information automatically.`, { default: config.sourceASP }) .addInput(`sourceFileCCSID`, `Source file CCSID`, `The CCSID of source files on your system. You should only change this setting from *FILE if you have a source file that is 65535 - otherwise use *FILE. Note that this config is used to fetch all members. If you have any source files using 65535, you have bigger problems.`, { default: config.sourceFileCCSID, minlength: 1, maxlength: 5 }) + .addCheckbox(`autoFixInvalidCharacters`, `Member cleanup`, `Legacy editors used to support invalid ASCII/unicode printable characters and sometimes members still contain them. Enable this to automatically cleanup members as they are opened.`, config.autoFixInvalidCharacters) .addHorizontalRule() .addCheckbox(`enableSourceDates`, `Enable Source Dates`, `When enabled, source dates will be retained and updated when editing source members. Requires restart when changed.`, config.enableSourceDates) .addSelect(`sourceDateMode`, `Source date tracking mode`, [ From 3c96575dab61254b059c39ba51e49d3bbc8a94de Mon Sep 17 00:00:00 2001 From: worksofliam Date: Fri, 2 Aug 2024 18:40:41 -0400 Subject: [PATCH 5/8] Correct validation of opened members Signed-off-by: worksofliam --- src/languages/colour.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/languages/colour.ts b/src/languages/colour.ts index 0c5ab8146..58474fa0b 100644 --- a/src/languages/colour.ts +++ b/src/languages/colour.ts @@ -6,7 +6,7 @@ const NEW_LINE_NUMBERS = [10, 13]; export function initialiseColourChecker(context: ExtensionContext) { context.subscriptions.push( workspace.onDidOpenTextDocument(async (document) => { - if (document.uri.scheme === `member`) { + if (document.uri.scheme === `member` && !document.isClosed) { const content = document.getText(); let hasInvalidCharacters = false; for (let i = 0; i < content.length; i++) { @@ -16,12 +16,14 @@ export function initialiseColourChecker(context: ExtensionContext) { } } - const shouldFix = await shouldInitiateCleanup(); + if (hasInvalidCharacters) { + const shouldFix = await shouldInitiateCleanup(); - if (shouldFix) { - const fixedContent = replaceInvalidCharacters(content); - const edit = new WorkspaceEdit(); - edit.replace(document.uri, new Range(0, 0, document.lineCount, 0), fixedContent); + if (shouldFix) { + const fixedContent = replaceInvalidCharacters(content); + const edit = new WorkspaceEdit(); + edit.replace(document.uri, new Range(0, 0, document.lineCount, 0), fixedContent); + } } } }) From f720047e7f13f31b25f92022ac458b73444aa7ec Mon Sep 17 00:00:00 2001 From: worksofliam Date: Tue, 6 Aug 2024 12:22:15 -0400 Subject: [PATCH 6/8] Further improvements to cleanup logic Signed-off-by: worksofliam --- src/languages/colour.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/languages/colour.ts b/src/languages/colour.ts index 58474fa0b..ec9722a90 100644 --- a/src/languages/colour.ts +++ b/src/languages/colour.ts @@ -10,7 +10,7 @@ export function initialiseColourChecker(context: ExtensionContext) { const content = document.getText(); let hasInvalidCharacters = false; for (let i = 0; i < content.length; i++) { - if (content.charCodeAt(i) < 32 && !NEW_LINE_NUMBERS.includes(content.charCodeAt(i))) { + if (replaceCharCode(content.charCodeAt(i))) { hasInvalidCharacters = true; break; } @@ -23,6 +23,7 @@ export function initialiseColourChecker(context: ExtensionContext) { const fixedContent = replaceInvalidCharacters(content); const edit = new WorkspaceEdit(); edit.replace(document.uri, new Range(0, 0, document.lineCount, 0), fixedContent); + workspace.applyEdit(edit); } } } @@ -30,6 +31,13 @@ export function initialiseColourChecker(context: ExtensionContext) { ) } +function replaceCharCode(charCode: number) { + if ((charCode < 32 && !NEW_LINE_NUMBERS.includes(charCode)) || (charCode >= 128 && charCode <= 157)) { + return true; + } + return false; +} + async function shouldInitiateCleanup() { const config = instance.getConfig() @@ -56,7 +64,7 @@ function replaceInvalidCharacters(content: string) { // return content.replace(/[\x00-\x1F]/g, ``); // This almost works, but we want to keep line feed / carriage return for (let i = 0; i < content.length; i++) { - if (content.charCodeAt(i) < 32 && !NEW_LINE_NUMBERS.includes(content.charCodeAt(i))) { + if (replaceCharCode(content.charCodeAt(i))) { chars[i] = ` `; } } From 4d82b0b98e5bc3efbb404a297a9ab2dc03b67cad Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 2 Sep 2024 14:59:59 -0400 Subject: [PATCH 7/8] Locale support Signed-off-by: worksofliam --- src/languages/colour.ts | 12 +++++++++--- src/locale/ids/en.json | 4 +++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/languages/colour.ts b/src/languages/colour.ts index ec9722a90..a6270b075 100644 --- a/src/languages/colour.ts +++ b/src/languages/colour.ts @@ -1,5 +1,6 @@ import { ExtensionContext, Range, window, workspace, WorkspaceEdit } from "vscode"; import { instance } from "../instantiate"; +import { t } from "../locale"; const NEW_LINE_NUMBERS = [10, 13]; @@ -44,14 +45,19 @@ async function shouldInitiateCleanup() { if (config?.autoFixInvalidCharacters) { return true; } + + const always = t(`Always`); + const no = t(`No`); - const chosen = await window.showInformationMessage(`This member contains invalid characters. Would you like to clean it up?`, `Yes`, `Always`, `No`); + const chosen = await window.showInformationMessage( + t(`seuColours.warning`), + t(`Yes`), always, no); - if (chosen === `No`) { + if (chosen === no) { return false; } - if (chosen === `Always` && config) { + if (chosen === always && config) { config.autoFixInvalidCharacters = true; await instance.setConfig(config); } diff --git a/src/locale/ids/en.json b/src/locale/ids/en.json index d73988885..beb4ec1f8 100644 --- a/src/locale/ids/en.json +++ b/src/locale/ids/en.json @@ -423,6 +423,8 @@ "USER_DIRECTORY": "User directory", "username": "Username", "Yes": "Yes", + "Always": "Always", "debug.service.config.incomplete": "Incomplete configuration", - "debug.service.config.incomplete.detail": "Certificate needs to be regenerated" + "debug.service.config.incomplete.detail": "Certificate needs to be regenerated", + "seuColours.warning": "This member contains invalid characters. Would you like to clean it up?" } \ No newline at end of file From 849950a91149c312f7f259101c69ef72c5fa3c8a Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 2 Sep 2024 15:16:59 -0400 Subject: [PATCH 8/8] Test case for colour replacing Signed-off-by: worksofliam --- src/languages/colour.ts | 29 +++++----- src/testing/encoding.ts | 114 ++++++++++++++++++++++++++++++---------- 2 files changed, 102 insertions(+), 41 deletions(-) diff --git a/src/languages/colour.ts b/src/languages/colour.ts index a6270b075..640f5b8f4 100644 --- a/src/languages/colour.ts +++ b/src/languages/colour.ts @@ -9,16 +9,10 @@ export function initialiseColourChecker(context: ExtensionContext) { workspace.onDidOpenTextDocument(async (document) => { if (document.uri.scheme === `member` && !document.isClosed) { const content = document.getText(); - let hasInvalidCharacters = false; - for (let i = 0; i < content.length; i++) { - if (replaceCharCode(content.charCodeAt(i))) { - hasInvalidCharacters = true; - break; - } - } + let doWork = hasInvalidCharacters(content); - if (hasInvalidCharacters) { - const shouldFix = await shouldInitiateCleanup(); + if (doWork) { + const shouldFix = await askUserToStart(); if (shouldFix) { const fixedContent = replaceInvalidCharacters(content); @@ -32,14 +26,23 @@ export function initialiseColourChecker(context: ExtensionContext) { ) } -function replaceCharCode(charCode: number) { +export function hasInvalidCharacters(content: string) { + for (let i = 0; i < content.length; i++) { + if (shouldReplaceCharCode(content.charCodeAt(i))) { + return true; + } + } + return false; +} + +function shouldReplaceCharCode(charCode: number) { if ((charCode < 32 && !NEW_LINE_NUMBERS.includes(charCode)) || (charCode >= 128 && charCode <= 157)) { return true; } return false; } -async function shouldInitiateCleanup() { +async function askUserToStart() { const config = instance.getConfig() if (config?.autoFixInvalidCharacters) { @@ -65,12 +68,12 @@ async function shouldInitiateCleanup() { return true; } -function replaceInvalidCharacters(content: string) { +export function replaceInvalidCharacters(content: string) { const chars = content.split(``); // return content.replace(/[\x00-\x1F]/g, ``); // This almost works, but we want to keep line feed / carriage return for (let i = 0; i < content.length; i++) { - if (replaceCharCode(content.charCodeAt(i))) { + if (shouldReplaceCharCode(content.charCodeAt(i))) { chars[i] = ` `; } } diff --git a/src/testing/encoding.ts b/src/testing/encoding.ts index fe3fa8083..3da5c0f70 100644 --- a/src/testing/encoding.ts +++ b/src/testing/encoding.ts @@ -7,6 +7,7 @@ import { Tools } from "../api/Tools"; import { instance } from "../instantiate"; import { CommandResult } from "../typings"; import { getMemberUri } from "../filesystems/qsys/QSysFs"; +import { hasInvalidCharacters, replaceInvalidCharacters } from "../languages/colour"; const contents = { '37': [`Hello world`], @@ -27,43 +28,100 @@ export const EncodingSuite: TestSuite = { assert.ok(config.enableSourceDates, `Source dates must be enabled for this test.`); }, - tests: Object.keys(contents).map(ccsid => { - return { - name: `Encoding ${ccsid}`, test: async () => { - const connection = instance.getConnection(); - const config = instance.getConfig()!; + tests: [ + ...Object.keys(contents).map(ccsid => { + return { + name: `Encoding ${ccsid}`, test: async () => { + const connection = instance.getConnection(); + const config = instance.getConfig()!; - const oldLines = contents[ccsid as keyof typeof contents]; - const lines = oldLines.join(`\n`); + const oldLines = contents[ccsid as keyof typeof contents]; + const lines = oldLines.join(`\n`); - const tempLib = config!.tempLibrary; + const tempLib = config!.tempLibrary; - const file = `TEST${ccsid}`; + const file = `TEST${ccsid}`; - await connection!.runCommand({ command: `CRTSRCPF FILE(${tempLib}/${file}) RCDLEN(112) CCSID(${ccsid})`, noLibList: true }); - await connection!.runCommand({ command: `ADDPFM FILE(${tempLib}/${file}) MBR(THEMEMBER) SRCTYPE(TXT)`, noLibList: true }); + await connection!.runCommand({ command: `CRTSRCPF FILE(${tempLib}/${file}) RCDLEN(112) CCSID(${ccsid})`, noLibList: true }); + await connection!.runCommand({ command: `ADDPFM FILE(${tempLib}/${file}) MBR(THEMEMBER) SRCTYPE(TXT)`, noLibList: true }); - const theBadOneUri = getMemberUri({ library: tempLib, file, name: `THEMEMBER`, extension: `TXT` }); + const theBadOneUri = getMemberUri({ library: tempLib, file, name: `THEMEMBER`, extension: `TXT` }); - await workspace.fs.readFile(theBadOneUri); + await workspace.fs.readFile(theBadOneUri); - await workspace.fs.writeFile(theBadOneUri, Buffer.from(lines, `utf8`)); + await workspace.fs.writeFile(theBadOneUri, Buffer.from(lines, `utf8`)); - const memberContentBuf = await workspace.fs.readFile(theBadOneUri); - let fileContent = new TextDecoder().decode(memberContentBuf); - - if (rtlEncodings.includes(ccsid)) { - const newLines = fileContent.split(`\n`); - - assert.strictEqual(newLines.length, 2); - assert.ok(newLines[1].startsWith(` `)); // RTL + const memberContentBuf = await workspace.fs.readFile(theBadOneUri); + let fileContent = new TextDecoder().decode(memberContentBuf); - assert.strictEqual(newLines[0].trim(), oldLines[0]); - assert.strictEqual(newLines[1].trim(), oldLines[1]); - } else { - assert.deepStrictEqual(fileContent, lines); + if (rtlEncodings.includes(ccsid)) { + const newLines = fileContent.split(`\n`); + + assert.strictEqual(newLines.length, 2); + assert.ok(newLines[1].startsWith(` `)); // RTL + + assert.strictEqual(newLines[0].trim(), oldLines[0]); + assert.strictEqual(newLines[1].trim(), oldLines[1]); + } else { + assert.deepStrictEqual(fileContent, lines); + } } } - } - }) + }), + {name: `Colour fix test`, test: async () => { + const connection = instance.getConnection(); + const config = instance.getConfig()!; + + const tempLib = config!.tempLibrary; + const file = `COLOURS`; + const member = `THEMEMBER`; + + await connection!.runCommand({ command: `CRTSRCPF FILE(${tempLib}/${file}) RCDLEN(112)`, noLibList: true }); + await connection!.runCommand({ command: `ADDPFM FILE(${tempLib}/${file}) MBR(${member}) SRCTYPE(TXT)`, noLibList: true }); + + const aliasName = `${tempLib}.test_${file}_${member}`; + await connection?.runSQL(`CREATE OR REPLACE ALIAS ${aliasName} for "${tempLib}"."${file}"("${member}")`); + + try { + await connection?.runSQL(`delete from ${aliasName}`); + } catch (e) {} + + const lines = [ + `insert into ${aliasName} (srcseq, srcdat, srcdta)`, + `values `, + ` (01.00, 240805, ' // This illustrates 5250 attribute bytes.'),`, + ` (02.00, 240805, ' '),`, + // `--(05.00, 240805, ' C*' concat x'XX' concat 'X''XX''' concat x'404020' concat 'X''XX'' GRN RI UL BL CS ND'),`, + ` (03.00, 240805, ' C*' concat x'20' concat 'X''20''' concat x'404020' concat 'X''20'' GRN '),`, + ` (04.00, 240805, ' C*' concat x'24' concat 'X''24''' concat x'404020' concat 'X''24'' GRN UL '),`, + ` (05.00, 240805, ' C*' concat x'25' concat 'X''25''' concat x'404020' concat 'X''25'' GRN RI UL '),`, + ` (06.00, 240805, ' C*' concat x'2E' concat 'X''2E''' concat x'404020' concat 'X''2E'' RED UL BL '),`, + ` (07.00, 240805, ' C*' concat x'2F' concat 'X''2F''' concat x'404020' concat 'X''2F'' RED ND'),`, + ` (08.00, 240805, ' C*' concat x'30' concat 'X''30''' concat x'404020' concat 'X''30'' TRQ CS '),`, + ` (09.00, 240805, ' C*' concat x'36' concat 'X''36''' concat x'404020' concat 'X''36'' YLW CS '),`, + ` (10.00, 240805, ' C*' concat x'37' concat 'X''37''' concat x'404020' concat 'X''37'' YLW CS ND'),`, + ` (11.00, 240805, ' C*' concat x'3D' concat 'X''3D''' concat x'404020' concat 'X''3D'' PNK RI UL '),`, + ` (12.00, 240805, ' C*' concat x'3E' concat 'X''3E''' concat x'404020' concat 'X''3E'' BLU UL '),`, + ` (13.00, 240805, ' C*' concat x'3F' concat 'X''3F''' concat x'404020' concat 'X''3F'' revert to default '),`, + ` (14.00, 240805, ' '),`, + ` (15.00, 240805, ' *inlr = *on;'),`, + ` (16.00, 240805, ' return;')`, + ]; + + await connection?.runSQL(lines.join(` `)); + + const theBadOneUri = getMemberUri({ library: tempLib, file, name: member, extension: `TXT` }); + + const memberContentBuf = await workspace.fs.readFile(theBadOneUri); + const fileContent = new TextDecoder().decode(memberContentBuf); + + assert.ok(hasInvalidCharacters(fileContent)); + + const newContent = replaceInvalidCharacters(fileContent); + + assert.notStrictEqual(newContent, fileContent); + + assert.ok(hasInvalidCharacters(newContent)); + }} + ] };