diff --git a/.github/workflows/package-extension.yml b/.github/workflows/package-extension.yml new file mode 100644 index 00000000..e15dc214 --- /dev/null +++ b/.github/workflows/package-extension.yml @@ -0,0 +1,29 @@ +name: Build VSIX + +on: + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + + - run: npm install + + - name: Compile extension/client only + run: tsc -p extension/client/tsconfig.json + + - name: Package VSIX + run: npx vsce package + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: rpgle-extension + path: '*.vsix' diff --git a/extension/client/src/configuration.ts b/extension/client/src/configuration.ts index 81714f2f..3d38f0ca 100644 --- a/extension/client/src/configuration.ts +++ b/extension/client/src/configuration.ts @@ -6,4 +6,5 @@ export function get(prop: string) { } export const RULER_ENABLED_BY_DEFAULT = `rulerEnabledByDefault`; +export const GLOBAL_LINT_CONFIG_PATH = `globalLintConfigPath`; export const projectFilesGlob = `**/*.{rpgle,RPGLE,sqlrpgle,SQLRPGLE,rpgleinc,RPGLEINC}`; \ No newline at end of file diff --git a/extension/client/src/extension.ts b/extension/client/src/extension.ts index e944a11c..d6badb04 100644 --- a/extension/client/src/extension.ts +++ b/extension/client/src/extension.ts @@ -37,14 +37,22 @@ export function activate(context: ExtensionContext) { // If the extension is launched in debug mode then the debug server options are used // Otherwise the run options are used - const serverOptions: ServerOptions = { - run: { module: serverModule, transport: TransportKind.ipc }, - debug: { - module: serverModule, - transport: TransportKind.ipc, - options: debugOptions - } - }; + const globalLintPath = workspace.getConfiguration('vscode-rpgle').get('globalLintConfigPath'); + const env = { ...process.env } as NodeJS.ProcessEnv; + if (globalLintPath) env.GLOBAL_LINT_CONFIG_PATH = globalLintPath; + + const serverOptions: ServerOptions = { + run: { + module: serverModule, + transport: TransportKind.ipc, + options: { env } + }, + debug: { + module: serverModule, + transport: TransportKind.ipc, + options: { ...debugOptions, env } + } + }; loadBase(); diff --git a/extension/client/src/linter.ts b/extension/client/src/linter.ts index 275cb6b1..11c2bbcc 100644 --- a/extension/client/src/linter.ts +++ b/extension/client/src/linter.ts @@ -1,6 +1,7 @@ import path = require('path'); import { commands, ExtensionContext, Uri, ViewColumn, window, workspace } from 'vscode'; import {getInstance} from './base'; +import * as Configuration from './configuration'; import {DEFAULT_SCHEMA} from "./schemas/linter" @@ -46,8 +47,34 @@ export function initialise(context: ExtensionContext) { } } else if (instance && instance.getConnection()) { - const connection = instance.getConnection(); - const content = instance.getContent(); + const connection = instance.getConnection(); + const content = instance.getContent(); + + let globalPath = Configuration.get(Configuration.GLOBAL_LINT_CONFIG_PATH); + + if (globalPath?.startsWith('/')) { + globalPath = globalPath.substring(1); + } + + if (globalPath) { + try { + const parts = connection.parserMemberPath(globalPath); + const existsRes = await connection.runCommand({ + command: `CHKOBJ OBJ(${parts.library}/${parts.file}) OBJTYPE(*FILE) MBR(${parts.name})`, + noLibList: true + }); + + if (existsRes.code === 0) { + await commands.executeCommand(`code-for-ibmi.openEditable`, globalPath); + } else { + window.showErrorMessage(`Global lint config does not exist at ${globalPath}.`); + } + } catch (e) { + console.log(e); + window.showErrorMessage(`Failed to open global lint configuration.`); + } + return; + } /** @type {"member"|"streamfile"} */ let type = `member`; @@ -117,8 +144,22 @@ export function initialise(context: ExtensionContext) { } else { window.showErrorMessage(`RPGLE linter config doesn't exist for this file. Would you like to create a default at ${configPath}?`, `Yes`, `No`).then (async (value) => { - if (value === `Yes`) { - const jsonString = JSON.stringify(DEFAULT_SCHEMA, null, 2); + if (value === `Yes`) { + let jsonString: string | undefined; + + if (type === `member`) { + const globalPath = Configuration.get(Configuration.GLOBAL_LINT_CONFIG_PATH); + if (globalPath) { + try { + const globalParts = connection.parserMemberPath(globalPath); + jsonString = await content.downloadMemberContent(globalParts.library, globalParts.file, globalParts.name); + } catch (e) { + console.log(`Failed to load global lint config: ${e}`); + } + } + } + + if (!jsonString) jsonString = JSON.stringify(DEFAULT_SCHEMA, null, 2); switch (type) { case `member`: @@ -184,4 +225,4 @@ function parseMemberUri(fullPath: string): {asp?: string, library?: string, file library: parts[parts.length - 3], asp: parts[parts.length - 4] } -}; \ No newline at end of file +}; diff --git a/extension/client/tsconfig.json b/extension/client/tsconfig.json index 3f865757..bdcbb38c 100644 --- a/extension/client/tsconfig.json +++ b/extension/client/tsconfig.json @@ -1,18 +1,24 @@ { - "extends": "../tsconfig.base.json", - "compilerOptions": { - "module": "commonjs", - "target": "es2020", - "outDir": "out", - "rootDir": "src", - "lib": [ "es2020" ], - "sourceMap": true, - "composite": true - }, - "include": [ - "src" - ], - "exclude": [ - "node_modules", ".vscode-test", "src/test" - ] -} \ No newline at end of file + "compilerOptions": { + "module": "commonjs", + "target": "es2020", + "outDir": "../../out/client", + "lib": ["ES2020"], + "sourceMap": true, + "strict": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "noUnusedLocals": false, + "moduleResolution": "node", + "types": [], + "typeRoots": ["node_modules/@types"] + }, + "include": ["src/**/*.ts"], + "exclude": [ + "**/node_modules", + "**/vscode-ibmi-types/**/*", + "**/node-ssh/**/*" + ] +} diff --git a/extension/server/src/providers/linter/index.ts b/extension/server/src/providers/linter/index.ts index 2c4afd50..d9045d6a 100644 --- a/extension/server/src/providers/linter/index.ts +++ b/extension/server/src/providers/linter/index.ts @@ -101,14 +101,29 @@ enum ResolvedState { let boundLintConfig: {[workingUri: string]: {resolved: ResolvedState, uri: string}} = {}; export async function getLintConfigUri(workingUri: string) { - const uri = URI.parse(workingUri); - let cleanString: string | undefined; - - const cached = boundLintConfig[workingUri]; - - if (cached) { - return cached.resolved === ResolvedState.Found ? cached.uri : undefined; - } + const uri = URI.parse(workingUri); + let cleanString: string | undefined; + + const cached = boundLintConfig[workingUri]; + + if (cached) { + return cached.resolved === ResolvedState.Found ? cached.uri : undefined; + } + + if (uri.scheme === `member`) { + const globalPath = process.env.GLOBAL_LINT_CONFIG_PATH; + if (globalPath) { + cleanString = URI.from({ scheme: `member`, path: globalPath }).toString(); + cleanString = await validateUri(cleanString, `member`); + if (cleanString) { + boundLintConfig[workingUri] = { + resolved: ResolvedState.Found, + uri: cleanString + }; + return cleanString; + } + } + } switch (uri.scheme) { case `member`: diff --git a/package-lock.json b/package-lock.json index 25a9e01d..3d34c1f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-rpgle", - "version": "0.31.1", + "version": "0.32.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-rpgle", - "version": "0.31.1", + "version": "0.32.1", "hasInstallScript": true, "license": "MIT", "devDependencies": { diff --git a/package.json b/package.json index a0ba4cfe..deaec13f 100644 --- a/package.json +++ b/package.json @@ -36,13 +36,18 @@ "configuration": { "title": "RPGLE language tools", "properties": { - "vscode-rpgle.rulerEnabledByDefault": { - "type": "boolean", - "default": true, - "description": "Whether to show the fixed-format ruler by default." - } - } - }, + "vscode-rpgle.rulerEnabledByDefault": { + "type": "boolean", + "default": true, + "description": "Whether to show the fixed-format ruler by default." + }, + "vscode-rpgle.globalLintConfigPath": { + "type": "string", + "default": "", + "description": "QSYS.LIB path for global RPGLINT.JSON." + } + } + }, "snippets": [ { "path": "./schemas/rpgle.code-snippets", diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..206276d3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2020", + "outDir": "../../out/client", + "lib": ["ES2020"], + "sourceMap": true, + "strict": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "noUnusedLocals": false, + "moduleResolution": "node", + "forceConsistentCasingInFileNames": false + }, + "include": ["src/**/*.ts"], + "exclude": ["**/node_modules"] +} diff --git a/vscode-rpgle-0.32.1.vsix b/vscode-rpgle-0.32.1.vsix new file mode 100644 index 00000000..a6c9a3fd Binary files /dev/null and b/vscode-rpgle-0.32.1.vsix differ diff --git a/vscode-rpgle-0.32.3.vsix b/vscode-rpgle-0.32.3.vsix new file mode 100644 index 00000000..8c6e2d71 Binary files /dev/null and b/vscode-rpgle-0.32.3.vsix differ