Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
53 changes: 53 additions & 0 deletions src/api/IBMiContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,59 @@ export default class IBMiContent {
return items;
}

/**
* Get path info
* @param path
* @return IFSFile
*/
async getFileInfo(path: string): Promise<IFSFile> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are going to need some new test cases for this method.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add some test cases...

const { 'stat': STAT } = this.ibmi.remoteFeatures;

let item: IFSFile = { type: 'streamfile', name: '', path: '' };
let fileInfoResult: CommandResult;

if (STAT) {
fileInfoResult = (await this.ibmi.sendCommand({
command: `${STAT} --dereference --printf="%A\t%h\t%U\t%G\t%s\t%Y\t%n\n" ${Tools.escapePath(path)}`
}));

if (fileInfoResult.stdout !== '') {
const fileInfo = fileInfoResult.stdout;

let auth: string, hardLinks: string, owner: string, group: string, size: string, modified: string, name: string;
[auth, hardLinks, owner, group, size, modified, name] = fileInfo.split(`\t`);

if (name !== `..` && name !== `.`) {
const type = (auth.startsWith(`d`) ? `directory` : `streamfile`);
item = {
type: type,
name: name,
path: path,
size: Number(size),
modified: new Date(Number(modified) * 1000),
owner: owner
};
};
}
} else {
fileInfoResult = (await this.ibmi.sendCommand({
command: `${this.ibmi.remoteFeatures.ls} -a -p -L ${Tools.escapePath(path)}`
}));

if (fileInfoResult.stdout !== '') {
const fileInfo = fileInfoResult.stdout;
const type = (fileInfo.endsWith(`/`) ? `directory` : `streamfile`);
item = {
type: type,
name: (type === `directory` ? fileInfo.substring(0, fileInfo.length - 1) : fileInfo),
path: path
};
}
}

return item;
}

async memberResolve(member: string, files: QsysPath[]): Promise<IBMiMember | undefined> {
const inAmerican = (s: string) => { return this.ibmi.sysNameInAmerican(s) };
const inLocal = (s: string) => { return this.ibmi.sysNameInLocal(s) };
Expand Down
46 changes: 27 additions & 19 deletions src/api/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export namespace Search {
export async function searchMembers(connection: IBMi, library: string, sourceFile: string, searchTerm: string, members: string|IBMiMember[], readOnly?: boolean,): Promise<SearchResults> {
const config = connection.getConfig();
const content = connection.getContent();
const infoComponent = connection.getComponent<GetMemberInfo>(GetMemberInfo.ID);

if (connection && config && content) {
let detailedMembers: IBMiMember[]|undefined;
Expand Down Expand Up @@ -47,26 +48,24 @@ export namespace Search {
const [lib, spf] = dir.split(`/`);
return detailedMembers!.some(member => member.name === name && member.library === lib && member.file === spf);
});
}

} else {
// Else, we need to fetch the member info for each hit so we can display the correct extension
const infoComponent = connection?.getComponent<GetMemberInfo>(GetMemberInfo.ID);
const memberInfos: IBMiMember[] = hits.map(hit => {
const { name, dir } = path.parse(hit.path);
const [library, file] = dir.split(`/`);

return {
name,
library,
file,
extension: ``
};
});
// We need to fetch the member info for each hit so we can display the correct extension and info.
const memberInfos: IBMiMember[] = hits.map(hit => {
const { name, dir } = path.parse(hit.path);
const [library, file] = dir.split(`/`);

detailedMembers = await infoComponent?.getMultipleMemberInfo(connection, memberInfos);
}
return {
name,
library,
file,
extension: ``
};
});

// Then fix the extensions in the hit
detailedMembers = await infoComponent?.getMultipleMemberInfo(connection, memberInfos);

// Add extension and member info to the hit.
for (const hit of hits) {
const { name, dir } = path.parse(hit.path);
const [lib, spf] = dir.split(`/`);
Expand All @@ -75,6 +74,7 @@ export namespace Search {

if (foundMember) {
hit.path = connection.sysNameInLocal(`${asp ? `${asp}/` : ``}${lib}/${spf}/${name}.${foundMember.extension}`);
hit.member = foundMember;
}
}

Expand Down Expand Up @@ -110,9 +110,13 @@ export namespace Search {
});

if (grepRes.code == 0) {
const hits = parseGrepOutput(grepRes.stdout);
for (var i = 0; i < hits.length; i++) {
hits[i].file = await connection.content.getFileInfo(hits[i].path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this been testing with a large hits array length? I wonder if getFileInfo might be nicely suited to accept an array as parameter and then call stat with multiple paths? Is that possible?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has not been tested, but both stat and ls supports multiple filenames, so I will change the function to accept an array of filenames and return an array of files info.

};
return {
term: searchTerm,
hits: parseGrepOutput(grepRes.stdout)
hits: hits
}
}
} else {
Expand Down Expand Up @@ -141,9 +145,13 @@ export namespace Search {
});

if (findRes.code == 0 && findRes.stdout) {
const hits = parseFindOutput(findRes.stdout);
for (var i = 0; i < hits.length; i++) {
hits[i].file = await connection.content.getFileInfo(hits[i].path)
};
return {
term: findTerm,
hits: parseFindOutput(findRes.stdout)
hits: hits
}
}
} else {
Expand Down
12 changes: 10 additions & 2 deletions src/api/components/getMemberInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ export class GetMemberInfo implements IBMiComponent {
async getMemberInfo(connection: IBMi, library: string, sourceFile: string, member: string): Promise<IBMiMember | undefined> {
const config = connection.config!;
const tempLib = config.tempLibrary;
const statement = `select * from table(${tempLib}.${this.procedureName}('${library}', '${sourceFile}', '${member}'))`;
const statement = ``.concat(`select Library, File, Member, Attr, Extension`,
` , extract(epoch from (CREATED))*1000 as CREATED`,
` , extract(epoch from (CHANGED))*1000 as CHANGED`,
` , Description, isSource`,
` from table(${tempLib}.${this.procedureName}('${library}', '${sourceFile}', '${member}'))`);

let results: Tools.DB2Row[] = [];
if (config.enableSQL) {
Expand Down Expand Up @@ -88,7 +92,11 @@ export class GetMemberInfo implements IBMiComponent {
const config = connection.config!;
const tempLib = config.tempLibrary;
const statement = members
.map(member => `select * from table(${tempLib}.${this.procedureName}('${member.library}', '${member.file}', '${member.name}'))`)
.map(member => ``.concat(`select Library, File, Member, Attr, Extension`,
` , extract(epoch from (CREATED))*1000 as CREATED`,
` , extract(epoch from (CHANGED))*1000 as CHANGED`,
` , Description, isSource`,
` from table(${tempLib}.${this.procedureName}('${member.library}', '${member.file}', '${member.name}'))`))
.join(' union all ');

let results: Tools.DB2Row[] = [];
Expand Down
6 changes: 4 additions & 2 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export interface IBMiObject extends QsysPath {
owner?: string
}

export interface IBMiMember {
export type IBMiMember = {
library: string
file: string
name: string
Expand All @@ -105,7 +105,7 @@ export interface IBMiMember {
changed?: Date
}

export interface IFSFile {
export type IFSFile = {
type: "directory" | "streamfile"
name: string
path: string
Expand Down Expand Up @@ -175,6 +175,8 @@ export type SearchHit = {
lines: SearchHitLine[]
readonly?: boolean
label?: string
file?: IFSFile
member?: IBMiMember
}

export type SearchHitLine = {
Expand Down
5 changes: 4 additions & 1 deletion src/ui/views/searchView.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'path';
import vscode from "vscode";
import { VscodeTools } from "../Tools";
import { DefaultOpenMode, SearchHit, SearchHitLine, SearchResults, WithPath } from "../../typings";

export function initializeSearchView(context: vscode.ExtensionContext) {
Expand Down Expand Up @@ -78,7 +79,9 @@ class HitSource extends vscode.TreeItem implements WithPath {
this.iconPath = vscode.ThemeIcon.File;
this.path = result.path;
this._readonly = result.readonly;
this.tooltip = result.path;
this.tooltip = result.file ? VscodeTools.ifsFileToToolTip(this.path, result.file) :
result.member? VscodeTools.memberToToolTip(this.path, result.member) :
result.path;

if (hits) {
this.description = `${hits} hit${hits === 1 ? `` : `s`}`;
Expand Down
Loading