From 68744547d613107d9e834d8c9267f9cfe748f0a2 Mon Sep 17 00:00:00 2001 From: Marcello Urbani Date: Fri, 8 Nov 2024 16:50:07 +0000 Subject: [PATCH 1/8] format documents with abaplint --- browser.webpack.config.js | 35 ++++++++------- client/package-lock.json | 86 ++++++++++++++++++++++++++++++++++++ client/package.json | 5 ++- client/src/extension.ts | 3 +- client/src/integrations.ts | 89 ++++++++++++++++++++++++++++++++++++++ package.json | 1 + 6 files changed, 200 insertions(+), 19 deletions(-) create mode 100644 client/src/integrations.ts diff --git a/browser.webpack.config.js b/browser.webpack.config.js index 223ab2b6..52e1a500 100644 --- a/browser.webpack.config.js +++ b/browser.webpack.config.js @@ -23,15 +23,17 @@ const browserClientConfig = /** @type WebpackConfig */ { extensions: ['.ts', '.js'], // support ts-files and js-files alias: {}, fallback: { - "fs": false, - "path": require.resolve("path-browserify") - }, + "fs": false, + "path": require.resolve("path-browserify"), + "crypto": require.resolve("crypto-browserify"), + "vm": require.resolve("vm-browserify") + } }, - plugins: [ - new ProvidePlugin({ - Buffer: [require.resolve("buffer/"), "Buffer"], - }), - ], + plugins: [ + new ProvidePlugin({ + Buffer: [require.resolve("buffer/"), "Buffer"], + }), + ], module: { rules: [ { @@ -71,16 +73,17 @@ const browserServerConfig = /** @type WebpackConfig */ { mainFields: ['module', 'main'], extensions: ['.ts', '.js'], // support ts-files and js-files alias: { - glob: false, - }, + glob: false, + }, fallback: { "path": require.resolve("path-browserify"), - "crypto": require.resolve("crypto-browserify"), - util: false, - fs: false, - child_process: false, - os: false, - assert: false + "crypto": require.resolve("crypto-browserify"), + "vm": require.resolve("vm-browserify"), + util: false, + fs: false, + child_process: false, + os: false, + assert: false }, }, module: { diff --git a/client/package-lock.json b/client/package-lock.json index c95132b6..20a7d38a 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { + "@abaplint/core": "^2.113.42", "vscode-languageclient": "^9.0.1" }, "devDependencies": { @@ -19,6 +20,23 @@ "vscode": "^1.40.0" } }, + "node_modules/@abaplint/core": { + "version": "2.113.44", + "resolved": "https://registry.npmjs.org/@abaplint/core/-/core-2.113.44.tgz", + "integrity": "sha512-+EIaBOPFUNixv6aYrOQ/db5v/o8KKEzLFW8jvWISPBWoWt4u7iDLKeaouSdYpJuysRqFW7WxJdJOmiakc3ap5Q==", + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^4.5.0", + "json5": "^2.2.3", + "vscode-languageserver-types": "^3.17.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/larshp" + } + }, "node_modules/@types/vscode": { "version": "1.91.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.91.0.tgz", @@ -82,6 +100,28 @@ "ieee754": "^1.2.1" } }, + "node_modules/fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -102,6 +142,18 @@ } ] }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -138,6 +190,12 @@ "node": ">=10" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", @@ -180,6 +238,16 @@ } }, "dependencies": { + "@abaplint/core": { + "version": "2.113.44", + "resolved": "https://registry.npmjs.org/@abaplint/core/-/core-2.113.44.tgz", + "integrity": "sha512-+EIaBOPFUNixv6aYrOQ/db5v/o8KKEzLFW8jvWISPBWoWt4u7iDLKeaouSdYpJuysRqFW7WxJdJOmiakc3ap5Q==", + "requires": { + "fast-xml-parser": "^4.5.0", + "json5": "^2.2.3", + "vscode-languageserver-types": "^3.17.5" + } + }, "@types/vscode": { "version": "1.91.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.91.0.tgz", @@ -215,12 +283,25 @@ "ieee754": "^1.2.1" } }, + "fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "requires": { + "strnum": "^1.0.5" + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -245,6 +326,11 @@ "lru-cache": "^6.0.0" } }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, "vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", diff --git a/client/package.json b/client/package.json index 17905ebc..70abe532 100644 --- a/client/package.json +++ b/client/package.json @@ -17,10 +17,11 @@ "update-vscode": "vscode-install" }, "dependencies": { - "vscode-languageclient": "^9.0.1" + "vscode-languageclient": "^9.0.1", + "@abaplint/core": "^2.113.42" }, "devDependencies": { "@types/vscode": "^1.91.0", "buffer": "^6.0.3" } -} +} \ No newline at end of file diff --git a/client/src/extension.ts b/client/src/extension.ts index 707602c0..2d6bdb9a 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -9,6 +9,7 @@ import {Help} from "./help"; import {Config} from "./config"; import {Flows} from "./flows"; import {TestController} from "./test_controller"; +import {registerBitbucket} from "./integrations"; let client: BaseLanguageClient; let myStatusBarItem: vscode.StatusBarItem; @@ -122,7 +123,7 @@ export function activate(context: ExtensionContext) { highlight.highlightWritesResponse(data.ranges, data.uri); }); }); - + registerBitbucket(); // removed, TODO: what was this used for? // context.subscriptions.push(await client.start()); } diff --git a/client/src/integrations.ts b/client/src/integrations.ts new file mode 100644 index 00000000..92334980 --- /dev/null +++ b/client/src/integrations.ts @@ -0,0 +1,89 @@ +import {extensions, Uri, workspace} from "vscode"; +import {ABAPFile, ABAPObject, MemoryFile, Registry, PrettyPrinter, Config} from "@abaplint/core"; + +const ATLASCODEDIFF = "atlascode.bbpr"; +interface CodeNormalizer { + isRelevant: (u: Uri) => boolean; + normalize: (code: string, uri: Uri) => Promise; +} + +interface BitBucketApi { + registerCodeNormalizer:(n:CodeNormalizer)=>Disposable; +} +export function parseAbapFile( + name: string, + abap: string +): ABAPFile | undefined { + const reg = new Registry().addFile(new MemoryFile(name, abap)).parse(); + const objects = [...reg.getObjects()].filter(ABAPObject.is); + return objects[0]?.getABAPFiles()[0]; +} + +const getConfig = async ():Promise => { + const cfgfile = [...await workspace.findFiles("abaplint.json"), ...await workspace.findFiles("abaplint.json[c5]")]; + console.log(cfgfile); + for (const c of cfgfile) { + try { + const file = await workspace.fs.readFile(c); + return new Config(file.toString()); + } catch (error) { + console.log(error); + } + } + return Config.getDefault(); +}; + +const abapLintPrettyPrint = async (path: string, source: string) => { + const name = path.replace(/.*\//, ""); + const f = parseAbapFile(name, source); + const result = f && new PrettyPrinter(f, await getConfig()).run(); + if (source && !result) { + throw new Error(`Abaplint formatting failed for ${path}`); + } + return result || source; +}; + +const shouldNormalize = (u:Uri) => { + try { + const o = JSON.parse(u.query); + return !! o.normalized; + } catch (error) { + return false; + } +}; + +const extractname = (u:Uri) => { + if (u.scheme !== ATLASCODEDIFF) {return u.path;} + try { + const details = JSON.parse(u.query); + if (details.path && typeof details.path === "string") { + return details.path; + } + } catch (error) { + return u.fragment; + } +}; + +const norm = ():CodeNormalizer => { + return { + isRelevant:(u) => !!(u.fsPath.match(/\.abap$/) || u.fragment.match(/\.abap$/)), + normalize:async (code, uri) => { + if (!shouldNormalize(uri)) { + return code; + } + const name = extractname(uri); + return abapLintPrettyPrint(name, code); + }, + }; +}; + +export const registerBitbucket = async () => { + const ext = extensions.getExtension("atlassian.atlascode"); + if (!ext) { + return; + } + if (!ext.isActive) { + await ext.activate(); + } + ext.exports.registerCodeNormalizer(norm()); +}; diff --git a/package.json b/package.json index a1130a18..cb8e6f80 100644 --- a/package.json +++ b/package.json @@ -271,6 +271,7 @@ "path-browserify": "^1.0.1", "ts-loader": "^9.5.1", "typescript": "^5.6.3", + "vm-browserify": "^1.1.2", "webpack": "^5.96.1", "webpack-cli": "^5.1.4" } From 40a6e7a31ed58cb3818b2dafa897719145fd9e5f Mon Sep 17 00:00:00 2001 From: Marcello Urbani Date: Mon, 11 Nov 2024 17:20:01 +0000 Subject: [PATCH 2/8] fix format relevant issues --- client/src/integrations.ts | 99 +++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/client/src/integrations.ts b/client/src/integrations.ts index 92334980..112420ae 100644 --- a/client/src/integrations.ts +++ b/client/src/integrations.ts @@ -1,5 +1,5 @@ -import {extensions, Uri, workspace} from "vscode"; -import {ABAPFile, ABAPObject, MemoryFile, Registry, PrettyPrinter, Config} from "@abaplint/core"; +import {extensions, Uri} from "vscode"; +import {ABAPFile, ABAPObject, MemoryFile, Registry, PrettyPrinter, Config, IRegistry, RulesRunner, applyEditList, IConfig} from "@abaplint/core"; const ATLASCODEDIFF = "atlascode.bbpr"; interface CodeNormalizer { @@ -10,37 +10,102 @@ interface CodeNormalizer { interface BitBucketApi { registerCodeNormalizer:(n:CodeNormalizer)=>Disposable; } + +interface FileDetails { + file: ABAPFile + reg: IRegistry +} + export function parseAbapFile( name: string, abap: string -): ABAPFile | undefined { +):FileDetails | undefined { const reg = new Registry().addFile(new MemoryFile(name, abap)).parse(); const objects = [...reg.getObjects()].filter(ABAPObject.is); - return objects[0]?.getABAPFiles()[0]; + const file = objects[0]?.getABAPFiles()[0]; + if (file) {return {file, reg};}; + return; } const getConfig = async ():Promise => { - const cfgfile = [...await workspace.findFiles("abaplint.json"), ...await workspace.findFiles("abaplint.json[c5]")]; - console.log(cfgfile); - for (const c of cfgfile) { - try { - const file = await workspace.fs.readFile(c); - return new Config(file.toString()); - } catch (error) { - console.log(error); + const rules = { + align_pseudo_comments:{ + exclude: [], + severity: "Error", + }, + align_parameters:{ + exclude: [], + severity: "Error", + }, + align_type_expressions:{ + exclude: [], + severity: "Error", + }, + in_statement_indentation:{ + exclude: [], + severity: "Error", + blockStatements: 2, + ignoreExceptions: true, + }, + sequential_blank: { + lines: 4, + }, + contains_tab:{ + exclude: [], + severity: "Error", + spaces: 1, + }, + indentation:{ + exclude: [], + severity: "Error", + ignoreExceptions: true, + alignTryCatch: false, + selectionScreenBlockIndentation: false, + globalClassSkipFirst: false, + ignoreGlobalClassDefinition: false, + ignoreGlobalInterface: false, + }, + keyword_case: { + style: "lower", + ignoreExceptions: true, + ignoreLowerClassImplmentationStatement: true, + ignoreGlobalClassDefinition: false, + ignoreGlobalInterface: false, + ignoreFunctionModuleName: false, + }, + }; + return new Config(JSON.stringify({rules})); +}; + +const applyRules = (f:FileDetails, config:Config) => { + const objects = [...f.reg.getObjects()].filter(ABAPObject.is);; + const obj = objects[0]; + console.assert(obj && objects.length === 1 && obj.getFiles().length === 1); + for (const rule of config.getEnabledRules()) { + rule.initialize(f.reg); + const issues = new RulesRunner(f.reg).excludeIssues([...rule.run(obj)]); + const edits = issues.map(a => a.getDefaultFix()).filter(e => typeof e !== "undefined"); // TODO: check overlaps + if (edits.length) { + const changed = applyEditList(f.reg, edits); + if (changed) {console.log(changed);}; + f.reg.parse(); } } - return Config.getDefault(); + const file = obj.getABAPFiles()[0] || f.file; + return {...f, file}; }; const abapLintPrettyPrint = async (path: string, source: string) => { const name = path.replace(/.*\//, ""); const f = parseAbapFile(name, source); - const result = f && new PrettyPrinter(f, await getConfig()).run(); - if (source && !result) { - throw new Error(`Abaplint formatting failed for ${path}`); + if (f) { + const config = await getConfig(); + const fixed = await applyRules(f, config); + console.log(fixed); + const result = new PrettyPrinter(fixed.file, config).run(); + if (result) {return result;}; } - return result || source; + throw new Error(`Abaplint formatting failed for ${path}`); }; const shouldNormalize = (u:Uri) => { From 623164efa2210dc4241d0c6d123b1084fc1af105 Mon Sep 17 00:00:00 2001 From: Marcello Urbani Date: Wed, 13 Nov 2024 14:29:42 +0000 Subject: [PATCH 3/8] remove overlapping edits --- client/src/integrations.ts | 64 ++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/client/src/integrations.ts b/client/src/integrations.ts index 112420ae..e9134355 100644 --- a/client/src/integrations.ts +++ b/client/src/integrations.ts @@ -1,5 +1,5 @@ import {extensions, Uri} from "vscode"; -import {ABAPFile, ABAPObject, MemoryFile, Registry, PrettyPrinter, Config, IRegistry, RulesRunner, applyEditList, IConfig} from "@abaplint/core"; +import {ABAPFile, ABAPObject, MemoryFile, Registry, PrettyPrinter, Config, IRegistry, RulesRunner, applyEditList, IEdit} from "@abaplint/core"; const ATLASCODEDIFF = "atlascode.bbpr"; interface CodeNormalizer { @@ -77,19 +77,65 @@ const getConfig = async ():Promise => { return new Config(JSON.stringify({rules})); }; +const HasOverlaps = (edit1:IEdit, edit2:IEdit) => { + const files1 = new Set(Object.keys(edit1)); + for (const file of Object.keys(edit2).filter(x => files1.has(x))) { + for (const filedit1 of edit1[file]) { + for (const filedit2 of edit2[file]) { + if (filedit2.range.start.getRow() <= filedit1.range.start.getRow() + && filedit2.range.end.getRow() >= filedit1.range.start.getRow()) { + return true; + } + if (filedit2.range.start.getRow() <= filedit1.range.end.getRow() + && filedit2.range.end.getRow() >= filedit1.range.end.getRow()) { + return true; + } + } + } + } + return false; +}; + + +const removeOverlapping = (edits:IEdit[]) => { + return edits.filter((ed, i) => { + if (i <= 0) {return true;} + for (let idx = 0; idx < i; idx++) { + if (HasOverlaps(ed, edits[idx])) {return false;} + } + return true; + }); +}; + +type IRule = ReturnType[0] // expose hidden IRule interface +const applyRule = (reg:IRegistry, obj:ABAPObject, rule:IRule) => { + rule.initialize(reg); + const issues = new RulesRunner(reg).excludeIssues([...rule.run(obj)]); + const edits = issues + .map(a => a.getDefaultFix()) + .filter(e => typeof e !== "undefined"); + if (edits.length) { + const nonconflicting = removeOverlapping(edits); + const changed = applyEditList(reg, nonconflicting); + reg.parse(); + console.log(`${rule.getMetadata().title} ${nonconflicting.length} ${edits.length}`); + const needReapplying = !!changed.length && nonconflicting.length < edits.length; + return needReapplying; + } + return false; +}; + const applyRules = (f:FileDetails, config:Config) => { const objects = [...f.reg.getObjects()].filter(ABAPObject.is);; const obj = objects[0]; console.assert(obj && objects.length === 1 && obj.getFiles().length === 1); for (const rule of config.getEnabledRules()) { - rule.initialize(f.reg); - const issues = new RulesRunner(f.reg).excludeIssues([...rule.run(obj)]); - const edits = issues.map(a => a.getDefaultFix()).filter(e => typeof e !== "undefined"); // TODO: check overlaps - if (edits.length) { - const changed = applyEditList(f.reg, edits); - if (changed) {console.log(changed);}; - f.reg.parse(); - } + let needtoApply = true; + let count = 0; + while (needtoApply) { + needtoApply = applyRule(f.reg, obj, rule); + if (count++ > 0) {console.log(`${count} ${rule.getMetadata().title}`);}; + }; } const file = obj.getABAPFiles()[0] || f.file; return {...f, file}; From 3a18380f77373f08558a4753e1e0e09caaf7f95a Mon Sep 17 00:00:00 2001 From: Marcello Urbani Date: Wed, 13 Nov 2024 16:51:36 +0000 Subject: [PATCH 4/8] refactor --- client/package-lock.json | 86 ---------------- client/package.json | 3 +- client/src/extension.ts | 2 +- client/src/integrations.ts | 184 ++++++----------------------------- package.json | 19 +++- server/src/codenormalizer.ts | 147 ++++++++++++++++++++++++++++ server/src/server.ts | 11 +++ 7 files changed, 207 insertions(+), 245 deletions(-) create mode 100644 server/src/codenormalizer.ts diff --git a/client/package-lock.json b/client/package-lock.json index 20a7d38a..c95132b6 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,7 +9,6 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@abaplint/core": "^2.113.42", "vscode-languageclient": "^9.0.1" }, "devDependencies": { @@ -20,23 +19,6 @@ "vscode": "^1.40.0" } }, - "node_modules/@abaplint/core": { - "version": "2.113.44", - "resolved": "https://registry.npmjs.org/@abaplint/core/-/core-2.113.44.tgz", - "integrity": "sha512-+EIaBOPFUNixv6aYrOQ/db5v/o8KKEzLFW8jvWISPBWoWt4u7iDLKeaouSdYpJuysRqFW7WxJdJOmiakc3ap5Q==", - "license": "MIT", - "dependencies": { - "fast-xml-parser": "^4.5.0", - "json5": "^2.2.3", - "vscode-languageserver-types": "^3.17.5" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/larshp" - } - }, "node_modules/@types/vscode": { "version": "1.91.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.91.0.tgz", @@ -100,28 +82,6 @@ "ieee754": "^1.2.1" } }, - "node_modules/fast-xml-parser": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", - "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -142,18 +102,6 @@ } ] }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -190,12 +138,6 @@ "node": ">=10" } }, - "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "license": "MIT" - }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", @@ -238,16 +180,6 @@ } }, "dependencies": { - "@abaplint/core": { - "version": "2.113.44", - "resolved": "https://registry.npmjs.org/@abaplint/core/-/core-2.113.44.tgz", - "integrity": "sha512-+EIaBOPFUNixv6aYrOQ/db5v/o8KKEzLFW8jvWISPBWoWt4u7iDLKeaouSdYpJuysRqFW7WxJdJOmiakc3ap5Q==", - "requires": { - "fast-xml-parser": "^4.5.0", - "json5": "^2.2.3", - "vscode-languageserver-types": "^3.17.5" - } - }, "@types/vscode": { "version": "1.91.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.91.0.tgz", @@ -283,25 +215,12 @@ "ieee754": "^1.2.1" } }, - "fast-xml-parser": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", - "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", - "requires": { - "strnum": "^1.0.5" - } - }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -326,11 +245,6 @@ "lru-cache": "^6.0.0" } }, - "strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" - }, "vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", diff --git a/client/package.json b/client/package.json index 70abe532..6c1bd7b0 100644 --- a/client/package.json +++ b/client/package.json @@ -17,8 +17,7 @@ "update-vscode": "vscode-install" }, "dependencies": { - "vscode-languageclient": "^9.0.1", - "@abaplint/core": "^2.113.42" + "vscode-languageclient": "^9.0.1" }, "devDependencies": { "@types/vscode": "^1.91.0", diff --git a/client/src/extension.ts b/client/src/extension.ts index 2d6bdb9a..4af7a870 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -123,7 +123,7 @@ export function activate(context: ExtensionContext) { highlight.highlightWritesResponse(data.ranges, data.uri); }); }); - registerBitbucket(); + registerBitbucket(client); // removed, TODO: what was this used for? // context.subscriptions.push(await client.start()); } diff --git a/client/src/integrations.ts b/client/src/integrations.ts index e9134355..e71b69fc 100644 --- a/client/src/integrations.ts +++ b/client/src/integrations.ts @@ -1,5 +1,5 @@ -import {extensions, Uri} from "vscode"; -import {ABAPFile, ABAPObject, MemoryFile, Registry, PrettyPrinter, Config, IRegistry, RulesRunner, applyEditList, IEdit} from "@abaplint/core"; +import {extensions, Uri, workspace} from "vscode"; +import {BaseLanguageClient} from "vscode-languageclient"; const ATLASCODEDIFF = "atlascode.bbpr"; interface CodeNormalizer { @@ -11,149 +11,6 @@ interface BitBucketApi { registerCodeNormalizer:(n:CodeNormalizer)=>Disposable; } -interface FileDetails { - file: ABAPFile - reg: IRegistry -} - -export function parseAbapFile( - name: string, - abap: string -):FileDetails | undefined { - const reg = new Registry().addFile(new MemoryFile(name, abap)).parse(); - const objects = [...reg.getObjects()].filter(ABAPObject.is); - const file = objects[0]?.getABAPFiles()[0]; - if (file) {return {file, reg};}; - return; -} - -const getConfig = async ():Promise => { - const rules = { - align_pseudo_comments:{ - exclude: [], - severity: "Error", - }, - align_parameters:{ - exclude: [], - severity: "Error", - }, - align_type_expressions:{ - exclude: [], - severity: "Error", - }, - in_statement_indentation:{ - exclude: [], - severity: "Error", - blockStatements: 2, - ignoreExceptions: true, - }, - sequential_blank: { - lines: 4, - }, - contains_tab:{ - exclude: [], - severity: "Error", - spaces: 1, - }, - indentation:{ - exclude: [], - severity: "Error", - ignoreExceptions: true, - alignTryCatch: false, - selectionScreenBlockIndentation: false, - globalClassSkipFirst: false, - ignoreGlobalClassDefinition: false, - ignoreGlobalInterface: false, - }, - keyword_case: { - style: "lower", - ignoreExceptions: true, - ignoreLowerClassImplmentationStatement: true, - ignoreGlobalClassDefinition: false, - ignoreGlobalInterface: false, - ignoreFunctionModuleName: false, - }, - }; - return new Config(JSON.stringify({rules})); -}; - -const HasOverlaps = (edit1:IEdit, edit2:IEdit) => { - const files1 = new Set(Object.keys(edit1)); - for (const file of Object.keys(edit2).filter(x => files1.has(x))) { - for (const filedit1 of edit1[file]) { - for (const filedit2 of edit2[file]) { - if (filedit2.range.start.getRow() <= filedit1.range.start.getRow() - && filedit2.range.end.getRow() >= filedit1.range.start.getRow()) { - return true; - } - if (filedit2.range.start.getRow() <= filedit1.range.end.getRow() - && filedit2.range.end.getRow() >= filedit1.range.end.getRow()) { - return true; - } - } - } - } - return false; -}; - - -const removeOverlapping = (edits:IEdit[]) => { - return edits.filter((ed, i) => { - if (i <= 0) {return true;} - for (let idx = 0; idx < i; idx++) { - if (HasOverlaps(ed, edits[idx])) {return false;} - } - return true; - }); -}; - -type IRule = ReturnType[0] // expose hidden IRule interface -const applyRule = (reg:IRegistry, obj:ABAPObject, rule:IRule) => { - rule.initialize(reg); - const issues = new RulesRunner(reg).excludeIssues([...rule.run(obj)]); - const edits = issues - .map(a => a.getDefaultFix()) - .filter(e => typeof e !== "undefined"); - if (edits.length) { - const nonconflicting = removeOverlapping(edits); - const changed = applyEditList(reg, nonconflicting); - reg.parse(); - console.log(`${rule.getMetadata().title} ${nonconflicting.length} ${edits.length}`); - const needReapplying = !!changed.length && nonconflicting.length < edits.length; - return needReapplying; - } - return false; -}; - -const applyRules = (f:FileDetails, config:Config) => { - const objects = [...f.reg.getObjects()].filter(ABAPObject.is);; - const obj = objects[0]; - console.assert(obj && objects.length === 1 && obj.getFiles().length === 1); - for (const rule of config.getEnabledRules()) { - let needtoApply = true; - let count = 0; - while (needtoApply) { - needtoApply = applyRule(f.reg, obj, rule); - if (count++ > 0) {console.log(`${count} ${rule.getMetadata().title}`);}; - }; - } - const file = obj.getABAPFiles()[0] || f.file; - return {...f, file}; -}; - -const abapLintPrettyPrint = async (path: string, source: string) => { - const name = path.replace(/.*\//, ""); - const f = parseAbapFile(name, source); - if (f) { - const config = await getConfig(); - const fixed = await applyRules(f, config); - console.log(fixed); - const result = new PrettyPrinter(fixed.file, config).run(); - if (result) {return result;}; - } - throw new Error(`Abaplint formatting failed for ${path}`); -}; - const shouldNormalize = (u:Uri) => { try { const o = JSON.parse(u.query); @@ -174,21 +31,36 @@ const extractname = (u:Uri) => { return u.fragment; } }; - -const norm = ():CodeNormalizer => { +const normalizations = ["On by default", "Off by default", "deactivated"] as const; +type Normalization = (typeof normalizations[I]); +const getNormalization = (): Normalization => { + const n = workspace.getConfiguration("abaplint").get("codeNormalization") as any; + if (normalizations.includes(n)) {return n;} + return "Off by default"; +}; +const isAbap = (u:Uri) => !!(u.fsPath.match(/\.abap$/) || u.fragment.match(/\.abap$/)); +const norm = (client: BaseLanguageClient):CodeNormalizer => { + const normalization = getNormalization(); + const inverted = normalization === "On by default"; + const inactive = normalization === "deactivated"; return { - isRelevant:(u) => !!(u.fsPath.match(/\.abap$/) || u.fragment.match(/\.abap$/)), - normalize:async (code, uri) => { - if (!shouldNormalize(uri)) { - return code; + isRelevant:(u) => !inactive && isAbap(u), + normalize:async (source, uri) => { + if (inactive || !isAbap(uri) || (inverted === shouldNormalize(uri))) { + return source; + } + const path = extractname(uri); + try { + const formatted:string = await client.sendRequest("abaplint/normalize", {path, source}); + return formatted; + } catch (error) { + return source; } - const name = extractname(uri); - return abapLintPrettyPrint(name, code); }, }; }; -export const registerBitbucket = async () => { +export const registerBitbucket = async (client: BaseLanguageClient) => { const ext = extensions.getExtension("atlassian.atlascode"); if (!ext) { return; @@ -196,5 +68,7 @@ export const registerBitbucket = async () => { if (!ext.isActive) { await ext.activate(); } - ext.exports.registerCodeNormalizer(norm()); + if (ext.exports?.registerCodeNormalizer) { + ext.exports.registerCodeNormalizer(norm(client)); + } }; diff --git a/package.json b/package.json index cb8e6f80..758fb6b5 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ }, "activationEvents": [ "onLanguage:abap", - "onLanguage:abap_cds" + "onLanguage:abap_cds", + "onStartupFinished" ], "icon": "img/abaplint_icon.png", "main": "./out-native/extension", @@ -231,6 +232,22 @@ "description": "List of rules that should not be triggered on formatting" } } + }, + { + "order": 50, + "id": "codenormalization", + "title": "Diff code normalization for bitbucket", + "properties": { + "abaplint.codeNormalization": { + "type": "string", + "enum": [ + "On by default", + "Off by default", + "deactivated" + ], + "default": "Off by default" + } + } } ] }, diff --git a/server/src/codenormalizer.ts b/server/src/codenormalizer.ts new file mode 100644 index 00000000..f0d0185f --- /dev/null +++ b/server/src/codenormalizer.ts @@ -0,0 +1,147 @@ +import {ABAPFile, ABAPObject, MemoryFile, Registry, PrettyPrinter, Config, IRegistry, RulesRunner, applyEditList, IEdit} from "@abaplint/core"; + +interface FileDetails { + file: ABAPFile + reg: IRegistry +} + +function parseAbapFile( + name: string, + abap: string +):FileDetails | undefined { + const reg = new Registry().addFile(new MemoryFile(name, abap)).parse(); + const objects = [...reg.getObjects()].filter(ABAPObject.is); + const file = objects[0]?.getABAPFiles()[0]; + if (file) {return {file, reg};}; + return; +} + +const getConfig = ():Config => { + const rules = { + align_pseudo_comments:{ + exclude: [], + severity: "Error", + }, + align_parameters:{ + exclude: [], + severity: "Error", + }, + align_type_expressions:{ + exclude: [], + severity: "Error", + }, + in_statement_indentation:{ + exclude: [], + severity: "Error", + blockStatements: 2, + ignoreExceptions: true, + }, + sequential_blank: { + lines: 4, + }, + contains_tab:{ + exclude: [], + severity: "Error", + spaces: 1, + }, + indentation:{ + exclude: [], + severity: "Error", + ignoreExceptions: true, + alignTryCatch: false, + selectionScreenBlockIndentation: false, + globalClassSkipFirst: false, + ignoreGlobalClassDefinition: false, + ignoreGlobalInterface: false, + }, + keyword_case: { + style: "lower", + ignoreExceptions: true, + ignoreLowerClassImplmentationStatement: true, + ignoreGlobalClassDefinition: false, + ignoreGlobalInterface: false, + ignoreFunctionModuleName: false, + }, + }; + return new Config(JSON.stringify({rules})); +}; + +const HasOverlaps = (edit1:IEdit, edit2:IEdit) => { + const files1 = new Set(Object.keys(edit1)); + for (const file of Object.keys(edit2).filter(x => files1.has(x))) { + for (const filedit1 of edit1[file]) { + for (const filedit2 of edit2[file]) { + if (filedit2.range.start.getRow() <= filedit1.range.start.getRow() + && filedit2.range.end.getRow() >= filedit1.range.start.getRow()) { + return true; + } + if (filedit2.range.start.getRow() <= filedit1.range.end.getRow() + && filedit2.range.end.getRow() >= filedit1.range.end.getRow()) { + return true; + } + } + } + } + return false; +}; + +const removeOverlapping = (edits:IEdit[]) => { + return edits.filter((ed, i) => { + if (i <= 0) {return true;} + for (let idx = 0; idx < i; idx++) { + if (HasOverlaps(ed, edits[idx])) {return false;} + } + return true; + }); +}; + +type IRule = ReturnType[0] // expose hidden IRule interface +const applyRule = (reg:IRegistry, obj:ABAPObject, rule:IRule) => { + rule.initialize(reg); + const issues = new RulesRunner(reg).excludeIssues([...rule.run(obj)]); + const edits = issues + .map(a => a.getDefaultFix()) + .filter(e => typeof e !== "undefined"); + if (edits.length) { + const nonconflicting = removeOverlapping(edits); + const changed = applyEditList(reg, nonconflicting); + reg.parse(); + const needReapplying = !!changed.length && nonconflicting.length < edits.length; + return needReapplying; + } + return false; +}; + +const applyRules = (f:FileDetails, config:Config) => { + const objects = [...f.reg.getObjects()].filter(ABAPObject.is);; + const obj = objects[0]; + if (obj?.getFiles().length !== 1) {return f;} + for (const rule of config.getEnabledRules()) { + let needtoApply = true; + let count = 0; + while (needtoApply) { + needtoApply = count++ < 10 && applyRule(f.reg, obj, rule); + }; + } + const file = obj.getABAPFiles()[0] || f.file; + return {...f, file}; +}; + +let normalizer: (path: string, source: string) => Promise; + +export const getNormalizer = () => { + if (!normalizer) { + const config = getConfig(); + normalizer = async (path: string, source: string) => { + const name = path.replace(/.*\//, ""); + const f = parseAbapFile(name, source); + if (f) { + const fixed = await applyRules(f, config); + const result = new PrettyPrinter(fixed.file, config).run(); + if (result) {return result;}; + } + throw new Error(`Abaplint formatting failed for ${path}`); + }; + } + return normalizer; +}; \ No newline at end of file diff --git a/server/src/server.ts b/server/src/server.ts index 11241a8d..cec7d30a 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -5,6 +5,7 @@ import * as abaplint from "@abaplint/core"; import {TextDocument} from "vscode-languageserver-textdocument"; import {Handler} from "./handler"; import {FsProvider, FileOperations} from "./file_operations"; +import {getNormalizer} from "./codenormalizer"; let connection: LServer.Connection; if (fs.read === undefined) { @@ -282,5 +283,15 @@ connection.onRequest("abaplint/unittests/list/request", async () => { handler.onListUnitTests(); }); +connection.onRequest("abaplint/normalize", async (data) => { + try { + const {path, source} = data; + return await getNormalizer()(path, source); + } catch (error) { + connection.console.error("message" in error ? error.message : error); + return data?.source; + } +}); + documents.listen(connection); connection.listen(); From 43f53a7efc1ffcaf7b9ea2f275e6f4002f083926 Mon Sep 17 00:00:00 2001 From: Marcello Urbani Date: Fri, 15 Nov 2024 14:19:38 +0000 Subject: [PATCH 5/8] normalize regular diffs --- client/src/extension.ts | 11 +++++--- client/src/integrations.ts | 11 +++++--- client/src/normalize.ts | 52 ++++++++++++++++++++++++++++++++++++++ package.json | 11 ++++++++ 4 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 client/src/normalize.ts diff --git a/client/src/extension.ts b/client/src/extension.ts index 4af7a870..38d5db99 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -10,6 +10,7 @@ import {Config} from "./config"; import {Flows} from "./flows"; import {TestController} from "./test_controller"; import {registerBitbucket} from "./integrations"; +import {registerNormalizer} from "./normalize"; let client: BaseLanguageClient; let myStatusBarItem: vscode.StatusBarItem; @@ -17,6 +18,7 @@ let highlight: Highlight; let help: Help; let flows: Flows; let config: Config; +let disposeAll:()=>void|undefined; function registerAsFsProvider(client: BaseLanguageClient) { const toUri = (path: string) => Uri.file(path); @@ -44,6 +46,7 @@ function registerAsFsProvider(client: BaseLanguageClient) { } export function activate(context: ExtensionContext) { + disposeAll = () => context.subscriptions.forEach(async d => d.dispose()); myStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); myStatusBarItem.text = "abaplint"; myStatusBarItem.show(); @@ -123,14 +126,16 @@ export function activate(context: ExtensionContext) { highlight.highlightWritesResponse(data.ranges, data.uri); }); }); + registerNormalizer(context, client); registerBitbucket(client); -// removed, TODO: what was this used for? -// context.subscriptions.push(await client.start()); } export function deactivate(): Thenable | undefined { if (!client) { return undefined; } - return client.stop(); + const stop = client.stop().then(() => client.dispose()); + if (disposeAll) {disposeAll();} + return stop; } + diff --git a/client/src/integrations.ts b/client/src/integrations.ts index e71b69fc..a507571a 100644 --- a/client/src/integrations.ts +++ b/client/src/integrations.ts @@ -1,12 +1,13 @@ import {extensions, Uri, workspace} from "vscode"; import {BaseLanguageClient} from "vscode-languageclient"; -const ATLASCODEDIFF = "atlascode.bbpr"; -interface CodeNormalizer { +export const ATLASCODEDIFF = "atlascode.bbpr"; +export interface CodeNormalizer { isRelevant: (u: Uri) => boolean; normalize: (code: string, uri: Uri) => Promise; } +export let integrationIsActive:(u:Uri) => boolean = () => false; interface BitBucketApi { registerCodeNormalizer:(n:CodeNormalizer)=>Disposable; } @@ -39,7 +40,7 @@ const getNormalization = (): Normalization => { return "Off by default"; }; const isAbap = (u:Uri) => !!(u.fsPath.match(/\.abap$/) || u.fragment.match(/\.abap$/)); -const norm = (client: BaseLanguageClient):CodeNormalizer => { +export const getAbapCodeNormalizer = (client: BaseLanguageClient):CodeNormalizer => { const normalization = getNormalization(); const inverted = normalization === "On by default"; const inactive = normalization === "deactivated"; @@ -69,6 +70,8 @@ export const registerBitbucket = async (client: BaseLanguageClient) => { await ext.activate(); } if (ext.exports?.registerCodeNormalizer) { - ext.exports.registerCodeNormalizer(norm(client)); + const norm = getAbapCodeNormalizer(client); + integrationIsActive = (u) => u.scheme === ATLASCODEDIFF && norm.isRelevant(u); + ext.exports.registerCodeNormalizer(norm); } }; diff --git a/client/src/normalize.ts b/client/src/normalize.ts new file mode 100644 index 00000000..962aacf5 --- /dev/null +++ b/client/src/normalize.ts @@ -0,0 +1,52 @@ +import {commands, ExtensionContext, TabInputTextDiff, TextEditor, Uri, window, TextDocumentContentProvider, Event, workspace} from "vscode"; +import {BaseLanguageClient} from "vscode-languageclient"; +import {ATLASCODEDIFF, CodeNormalizer, getAbapCodeNormalizer, integrationIsActive} from "./integrations"; +const ABAPGITSCHEME = "abapgit.normalized"; + +class NormalizedProvider implements TextDocumentContentProvider { + private readonly normalizer: CodeNormalizer; + public constructor(client: BaseLanguageClient) { + this.normalizer = getAbapCodeNormalizer(client); + }; + public onDidChange?: Event | undefined; + public async provideTextDocumentContent(uri: Uri): Promise { + const origUri = uri.with(JSON.parse(uri.query)); + const raw = await workspace.fs.readFile(origUri); + return this.normalizer.normalize(raw.toString(), origUri); + } +} + +const shouldActivate = (e: TextEditor | undefined) => { + const curtab = window.tabGroups.activeTabGroup; + const uri = e?.document.uri; + const isdiff = curtab.activeTab?.input instanceof TabInputTextDiff; + if (!(isdiff && uri)) {return false;} + const relevant = + uri.path.match(/\.abap$/) || + uri.scheme === ATLASCODEDIFF && uri.fragment.match(/\.abap$/); + return relevant && !integrationIsActive(uri); +}; + +const activateNormalizer = (e: TextEditor | undefined) => { + commands.executeCommand("setContext", "abaplint.IsNormalizerEnabled", shouldActivate(e)); +}; + +const toggleUrlNormalizer = (u:Uri) => { + if (u.scheme === ABAPGITSCHEME) {return u.with(JSON.parse(u.query));}; + const query = JSON.stringify(u); + return u.with({scheme:ABAPGITSCHEME, query}); +}; + +const toggleNormalizer = () => { + const curtab = window.tabGroups.activeTabGroup.activeTab; + if (!(curtab?.input instanceof TabInputTextDiff)) {return;} + const {original, modified} = curtab.input; + return commands.executeCommand("vscode.diff", toggleUrlNormalizer(original), toggleUrlNormalizer(modified), curtab.label); +}; + +export const registerNormalizer = (context:ExtensionContext, client: BaseLanguageClient) => { + const onchg = window.onDidChangeActiveTextEditor(activateNormalizer); + const normalize = commands.registerCommand("abaplint.togglediffNormalize", toggleNormalizer); + const provider = workspace.registerTextDocumentContentProvider(ABAPGITSCHEME, new NormalizedProvider(client)); + context.subscriptions.push(onchg, normalize, provider); +}; \ No newline at end of file diff --git a/package.json b/package.json index 758fb6b5..eff90952 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,12 @@ "command": "abaplint.create.default-config", "title": "Create Default Config", "category": "abaplint" + }, + { + "command": "abaplint.togglediffNormalize", + "title": "Normalize files", + "icon": "$(law)", + "category": "abaplint" } ], "jsonValidation": [ @@ -118,6 +124,11 @@ "when": "editorLangId == 'abap'", "command": "abaplint.show", "group": "navigation" + }, + { + "command": "abaplint.togglediffNormalize", + "group": "navigation", + "when": "isInDiffEditor && abaplint.IsNormalizerEnabled" } ], "file/newFile": [ From 2d1f539b0f204ee54f784c034ca1289c144b117c Mon Sep 17 00:00:00 2001 From: Marcello Urbani Date: Fri, 15 Nov 2024 16:51:45 +0000 Subject: [PATCH 6/8] disable questionable activation event --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index eff90952..4df7a326 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,7 @@ }, "activationEvents": [ "onLanguage:abap", - "onLanguage:abap_cds", - "onStartupFinished" + "onLanguage:abap_cds" ], "icon": "img/abaplint_icon.png", "main": "./out-native/extension", From 32721695f9ca93249f6549c7316f3a29ee864f8e Mon Sep 17 00:00:00 2001 From: Marcello Urbani Date: Fri, 15 Nov 2024 16:58:59 +0000 Subject: [PATCH 7/8] comment on bb api --- client/src/integrations.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/src/integrations.ts b/client/src/integrations.ts index a507571a..dbcefe4e 100644 --- a/client/src/integrations.ts +++ b/client/src/integrations.ts @@ -61,6 +61,12 @@ export const getAbapCodeNormalizer = (client: BaseLanguageClient):CodeNormalizer }; }; +// registers a code formatter for bitbucket using an API which will probably never be merged +// for now it's available on my fork of atlascode: +// https://bitbucket.org/marcellourbani/atlascode/branch/issue-%235433-Add-hook-to-pretty-print-code-to-show-in-diff-in-atlascode +// allows to: +// - normalize the code by default +// - get bitbucket functionality (i.e. comments) to work after normalizing export const registerBitbucket = async (client: BaseLanguageClient) => { const ext = extensions.getExtension("atlassian.atlascode"); if (!ext) { From 3c7f3ecc065463caf9065bdc014774da58a3b160 Mon Sep 17 00:00:00 2001 From: Marcello Urbani Date: Fri, 15 Nov 2024 17:41:18 +0000 Subject: [PATCH 8/8] improved support for document providers --- client/src/normalize.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/client/src/normalize.ts b/client/src/normalize.ts index 962aacf5..e7ae614b 100644 --- a/client/src/normalize.ts +++ b/client/src/normalize.ts @@ -3,6 +3,12 @@ import {BaseLanguageClient} from "vscode-languageclient"; import {ATLASCODEDIFF, CodeNormalizer, getAbapCodeNormalizer, integrationIsActive} from "./integrations"; const ABAPGITSCHEME = "abapgit.normalized"; +const originalUri = (u:Uri) => { + if (u.scheme !== ABAPGITSCHEME) {return u;} + const {scheme, query} = JSON.parse(u.query); + return u.with({scheme, query}); +}; + class NormalizedProvider implements TextDocumentContentProvider { private readonly normalizer: CodeNormalizer; public constructor(client: BaseLanguageClient) { @@ -10,9 +16,10 @@ class NormalizedProvider implements TextDocumentContentProvider { }; public onDidChange?: Event | undefined; public async provideTextDocumentContent(uri: Uri): Promise { - const origUri = uri.with(JSON.parse(uri.query)); - const raw = await workspace.fs.readFile(origUri); - return this.normalizer.normalize(raw.toString(), origUri); + const origUri = originalUri(uri); + if (uri.scheme === origUri.scheme) {throw new Error("invalid URL"); }; + const raw = await workspace.openTextDocument(origUri); + return this.normalizer.normalize(raw.getText(), origUri); } } @@ -30,10 +37,9 @@ const shouldActivate = (e: TextEditor | undefined) => { const activateNormalizer = (e: TextEditor | undefined) => { commands.executeCommand("setContext", "abaplint.IsNormalizerEnabled", shouldActivate(e)); }; - const toggleUrlNormalizer = (u:Uri) => { - if (u.scheme === ABAPGITSCHEME) {return u.with(JSON.parse(u.query));}; - const query = JSON.stringify(u); + if (u.scheme === ABAPGITSCHEME) { return originalUri(u);}; + const query = JSON.stringify({scheme:u.scheme, query:u.query}); return u.with({scheme:ABAPGITSCHEME, query}); };