Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions extension/client/src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ export function get<T>(prop: string) {
}

export const RULER_ENABLED_BY_DEFAULT = `rulerEnabledByDefault`;
export const GLOBAL_LINT_CONFIG_PATH = `globalLintConfigPath`;
export const LINT_IGNORE_LIBRARIES = `lintIgnoreLibraries`;
export const projectFilesGlob = `**/*.{rpgle,RPGLE,sqlrpgle,SQLRPGLE,rpgleinc,RPGLEINC}`;
14 changes: 14 additions & 0 deletions extension/client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ export function activate(context: ExtensionContext) {
}
};

// The global configuration path for linter config file
const globalConfig = workspace.getConfiguration(`vscode-rpgle`);
const globalLintPath = globalConfig.get<string>(`globalLintConfigPath`);
const lintIgnoreLibraries = globalConfig.get<string>(`lintIgnoreLibraries`);
const env = { ...process.env } as NodeJS.ProcessEnv;

if (globalLintPath) {
env.GLOBAL_LINT_CONFIG_PATH = globalLintPath;
}

if (lintIgnoreLibraries) {
env.LINT_IGNORE_LIBRARIES = lintIgnoreLibraries;
}

loadBase();

// Options to control the language client
Expand Down
8 changes: 4 additions & 4 deletions extension/client/src/language/serverReferences.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ConnectionConfig, IBMiMember } from "@halcyontech/vscode-ibmi-types";
import IBMi from "@halcyontech/vscode-ibmi-types/api/IBMi";
import { commands, Definition, DocumentSymbol, languages, Location, ProgressLocation, Range, SymbolInformation, SymbolKind, TextDocument, Uri, window, workspace } from "vscode";
import { getInstance } from "../base";
import IBMi from "@halcyontech/vscode-ibmi-types/api/IBMi";
import { ConnectionConfig, IBMiMember } from "@halcyontech/vscode-ibmi-types";

export function getServerSymbolProvider() {
let latestFetch: ExportInfo[]|undefined;
Expand All @@ -18,7 +18,7 @@ export function getServerSymbolProvider() {

if (instance && instance.getConnection()) {
const connection = instance.getConnection();
const config = connection.config! //TODO in vscode-ibmi 3.0.0 - change to getConfig()
const config = connection.getConfig();

let member: IBMiMember|undefined;

Expand Down Expand Up @@ -71,7 +71,7 @@ export function getServerImplementationProvider() {

if (connection) {
const word = document.getText(document.getWordRangeAtPosition(position));
const config = connection.getConfig() //TODO in vscode-ibmi 3.0.0 - change to getConfig()
const config = connection.getConfig();

const uriPath = document.uri.path;
const member = connection.parserMemberPath(uriPath);
Expand Down
178 changes: 91 additions & 87 deletions extension/client/src/linter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import path = require('path');
import { commands, ExtensionContext, Uri, ViewColumn, window, workspace } from 'vscode';
import {getInstance} from './base';
import { getInstance } from './base';

import {DEFAULT_SCHEMA} from "./schemas/linter"
import { DEFAULT_SCHEMA } from "./schemas/linter"

const GLOBAL_LINT_CONFIG_PATH = `/etc/vscode/rpglint.json`;

export function initialise(context: ExtensionContext) {
context.subscriptions.push(
Expand All @@ -13,25 +15,19 @@ export function initialise(context: ExtensionContext) {
let exists = false;

if (editor && ![`member`, `streamfile`].includes(editor.document.uri.scheme)) {
// Local workspace file � existing behaviour unchanged
const workspaces = workspace.workspaceFolders;
if (workspaces && workspaces.length > 0) {
const linter = await workspace.findFiles(`**/.vscode/rpglint.json`, `**/.git`, 1);
let uri;
if (linter && linter.length > 0) {
uri = linter[0];

console.log(`Uri path: ${JSON.stringify(uri)}`);

} else {
console.log(`String path: ${path.join(workspaces[0].uri.fsPath, `.vscode`, `rpglint.json`)}`);

uri = Uri.from({
scheme: `file`,
path: path.join(workspaces[0].uri.fsPath, `.vscode`, `rpglint.json`)
});

console.log(`Creating Uri path: ${JSON.stringify(uri)}`);

await workspace.fs.writeFile(
uri,
Buffer.from(JSON.stringify(DEFAULT_SCHEMA, null, 2), `utf8`)
Expand All @@ -49,100 +45,109 @@ export function initialise(context: ExtensionContext) {
const connection = instance.getConnection();
const content = instance.getContent();

/** @type {"member"|"streamfile"} */
let type = `member`;
let configPath: string | undefined;

if (filter && filter.description) {
// Bad way to get the library for the filter ..
const library: string = (filter.description.split(`/`)[0]).toLocaleUpperCase();

if (library.includes(`*`)) {
window.showErrorMessage(`Cannot show lint config for a library filter.`);
return;
}

configPath = `${library}/VSCODE/RPGLINT.JSON`;

exists = (await connection.runCommand({
command: `QSYS/CHKOBJ OBJ(${library}/VSCODE) OBJTYPE(*FILE) MBR(RPGLINT)`,
noLibList: true
})).code === 0;

} else if (editor) {
//@ts-ignore
type = editor.document.uri.scheme;
// Check if global lint config is enabled
const useGlobal = workspace.getConfiguration(`vscode-rpgle`).get<boolean>(`useGlobalLintConfig`, false);

console.log(`Uri remote path: ${JSON.stringify(editor.document.uri)}`);

switch (type) {
case `member`:
const memberPath = parseMemberUri(editor.document.uri.path);
const cleanString = [
memberPath.library,
`VSCODE`,
`RPGLINT.JSON`
].join(`/`);

const memberUri = Uri.from({
scheme: `member`,
path: cleanString
});
if (useGlobal) {
// Global IFS mode � use /etc/vscode/rpglint.json
const globalExists = await content.testStreamFile(GLOBAL_LINT_CONFIG_PATH, `r`);

configPath = memberUri.path;

exists = (await connection.runCommand({
command: `QSYS/CHKOBJ OBJ(${memberPath.library!.toLocaleUpperCase()}/VSCODE) OBJTYPE(*FILE) MBR(RPGLINT)`,
noLibList: true
})).code === 0;
break;
if (globalExists) {
await commands.executeCommand(`code-for-ibmi.openEditable`, GLOBAL_LINT_CONFIG_PATH);
} else {
const answer = await window.showInformationMessage(
`Global lint config does not exist at ${GLOBAL_LINT_CONFIG_PATH}. Would you like to create it?`,
`Yes`, `No`
);

case `streamfile`:
const config = instance.getConfig();
if (config.homeDirectory) {
configPath = path.posix.join(config.homeDirectory, `.vscode`, `rpglint.json`)
exists = await content.testStreamFile(configPath, `r`);
if (answer === `Yes`) {
const jsonString = JSON.stringify(DEFAULT_SCHEMA, null, 2);
try {
await content.writeStreamfile(GLOBAL_LINT_CONFIG_PATH, jsonString);
await commands.executeCommand(`code-for-ibmi.openEditable`, GLOBAL_LINT_CONFIG_PATH);
} catch (e) {
console.log(e);
window.showErrorMessage(`Failed to create global lint config at ${GLOBAL_LINT_CONFIG_PATH}. Ensure /etc/vscode/ directory exists.`);
}
break;
}
}
} else {
window.showErrorMessage(`No active editor found.`);
}

if (configPath) {
console.log(`Current path: ${configPath}`);

if (exists) {
await commands.executeCommand(`code-for-ibmi.openEditable`, configPath);
// Per-library mode � existing behaviour
let type: `member` | `streamfile` = `member`;
let configPath: string | undefined;

if (filter && filter.description) {
const library: string = (filter.description.split(`/`)[0]).toLocaleUpperCase();

if (library.includes(`*`)) {
window.showErrorMessage(`Cannot show lint config for a library filter.`);
return;
}

configPath = `/${library}/VSCODE/RPGLINT.JSON`;

exists = (await connection.runCommand({
command: `QSYS/CHKOBJ OBJ(${library}/VSCODE) OBJTYPE(*FILE) MBR(RPGLINT)`,
noLibList: true
})).code === 0;

} else if (editor) {
//@ts-ignore
type = editor.document.uri.scheme;

switch (type) {
case `member`:
const memberPath = parseMemberUri(editor.document.uri.path);
const library = memberPath.library!.toLocaleUpperCase();

configPath = `/${library}/VSCODE/RPGLINT.JSON`;

exists = (await connection.runCommand({
command: `QSYS/CHKOBJ OBJ(${library}/VSCODE) OBJTYPE(*FILE) MBR(RPGLINT)`,
noLibList: true
})).code === 0;
break;

case `streamfile`:
const config = instance.getConfig();
if (config.homeDirectory) {
configPath = path.posix.join(config.homeDirectory, `.vscode`, `rpglint.json`)
exists = await content.testStreamFile(configPath, `r`);
}
break;
}
} 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) => {
window.showErrorMessage(`No active editor found.`);
}

if (configPath) {
if (exists) {
await commands.executeCommand(`code-for-ibmi.openEditable`, configPath);
} else {
window.showErrorMessage(
`RPGLE linter config doesn't exist. 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);

switch (type) {
case `member`:
if (configPath) {
const memberPath = configPath.split(`/`);
const pathParts = configPath.split(`/`);
// pathParts = ['', 'LIBRARY', 'VSCODE', 'RPGLINT.JSON']
const lib = pathParts[1];

// Will not crash, even if it fails
await connection.runCommand(
{
'command': `QSYS/CRTSRCPF FILE(${memberPath[0]}/VSCODE) RCDLEN(112)`
}
{ 'command': `QSYS/CRTSRCPF FILE(${lib}/VSCODE) RCDLEN(112)` }
);

// Will not crash, even if it fails
await connection.runCommand(
{
command: `QSYS/ADDPFM FILE(${memberPath[0]}/VSCODE) MBR(RPGLINT) SRCTYPE(JSON)`
}
{ command: `QSYS/ADDPFM FILE(${lib}/VSCODE) MBR(RPGLINT) SRCTYPE(JSON)` }
);

try {
console.log(`Member path: ${[memberPath[0], `VSCODE`, `RPGLINT`].join(`/`)}`);

await content.uploadMemberContent(undefined, memberPath[0], `VSCODE`, `RPGLINT`, jsonString);
await content.uploadMemberContent(undefined, lib, `VSCODE`, `RPGLINT`, jsonString);
await commands.executeCommand(`code-for-ibmi.openEditable`, configPath);
} catch (e) {
console.log(e);
Expand All @@ -152,8 +157,6 @@ export function initialise(context: ExtensionContext) {
break;

case `streamfile`:
console.log(`IFS path: ${configPath}`);

try {
await content.writeStreamfile(configPath, jsonString);
await commands.executeCommand(`code-for-ibmi.openEditable`, configPath);
Expand All @@ -165,9 +168,10 @@ export function initialise(context: ExtensionContext) {
}
}
});
}
} else {
window.showErrorMessage(`No lint config path for this file. File must either be a member or a streamfile on the host IBM i.`);
}
} else {
window.showErrorMessage(`No lint config path for this file. File must either be a member or a streamfile on the host IBM i.`);
}
} else {
window.showErrorMessage(`Not connected to a system.`);
Expand All @@ -176,7 +180,7 @@ export function initialise(context: ExtensionContext) {
)
}

function parseMemberUri(fullPath: string): {asp?: string, library?: string, file?: string, name: string} {
function parseMemberUri(fullPath: string): { asp?: string, library?: string, file?: string, name: string } {
const parts = fullPath.split(`/`).map(s => s.split(`,`)).flat().filter(s => s.length >= 1);
return {
name: path.parse(parts[parts.length - 1]).name,
Expand Down
Loading