From 2af5b5803f91fb77b0fe3e2216af5ba758337911 Mon Sep 17 00:00:00 2001 From: Christian Jorgensen Date: Wed, 19 Feb 2025 00:17:03 +0100 Subject: [PATCH 1/4] Add tooltip to IFS files in search results --- src/api/IBMiContent.ts | 53 ++++++++++++++++++++++++++++++++++++++ src/api/Search.ts | 6 ++++- src/api/types.ts | 3 ++- src/ui/views/searchView.ts | 3 ++- 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/api/IBMiContent.ts b/src/api/IBMiContent.ts index 3a33f906a..c55e7daa6 100644 --- a/src/api/IBMiContent.ts +++ b/src/api/IBMiContent.ts @@ -787,6 +787,59 @@ export default class IBMiContent { return items; } + /** + * Get path info + * @param path + * @return IFSFile + */ + async getFileInfo(path: string): Promise { + 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 { const inAmerican = (s: string) => { return this.ibmi.sysNameInAmerican(s) }; const inLocal = (s: string) => { return this.ibmi.sysNameInLocal(s) }; diff --git a/src/api/Search.ts b/src/api/Search.ts index 4a87cd31c..0283d00f2 100644 --- a/src/api/Search.ts +++ b/src/api/Search.ts @@ -121,9 +121,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) + }; return { term: searchTerm, - hits: parseGrepOutput(grepRes.stdout) + hits: hits } } } else { diff --git a/src/api/types.ts b/src/api/types.ts index 4ea2b04f3..a1ba537f5 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -105,7 +105,7 @@ export interface IBMiMember { changed?: Date } -export interface IFSFile { +export type IFSFile = { type: "directory" | "streamfile" name: string path: string @@ -175,6 +175,7 @@ export type SearchHit = { lines: SearchHitLine[] readonly?: boolean label?: string + file?: IFSFile } export type SearchHitLine = { diff --git a/src/ui/views/searchView.ts b/src/ui/views/searchView.ts index d5cf072d3..f3dfaa59c 100644 --- a/src/ui/views/searchView.ts +++ b/src/ui/views/searchView.ts @@ -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) { @@ -78,7 +79,7 @@ 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.path; if (hits) { this.description = `${hits} hit${hits === 1 ? `` : `s`}`; From 0843250790655640144212f114a8fc85cd885386 Mon Sep 17 00:00:00 2001 From: Christian Jorgensen Date: Wed, 19 Feb 2025 08:08:53 +0100 Subject: [PATCH 2/4] Add tooltip to IFS files in find results --- src/api/Search.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/api/Search.ts b/src/api/Search.ts index 0283d00f2..705e7ad0d 100644 --- a/src/api/Search.ts +++ b/src/api/Search.ts @@ -156,9 +156,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 { From 958de1f52b3ff552911da786a861ec4d1f69ac47 Mon Sep 17 00:00:00 2001 From: Christian Jorgensen Date: Wed, 19 Feb 2025 09:35:26 +0100 Subject: [PATCH 3/4] Add tooltip to members in search results --- src/api/Search.ts | 34 +++++++++++++++++----------------- src/api/types.ts | 3 ++- src/ui/views/searchView.ts | 4 +++- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/api/Search.ts b/src/api/Search.ts index 705e7ad0d..fb84ba03a 100644 --- a/src/api/Search.ts +++ b/src/api/Search.ts @@ -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 { const config = connection.getConfig(); const content = connection.getContent(); + const infoComponent = connection.getComponent(GetMemberInfo.ID); if (connection && config && content) { let detailedMembers: IBMiMember[]|undefined; @@ -58,26 +59,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.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: `` + }; + }); + + detailedMembers = await infoComponent?.getMultipleMemberInfo(connection, memberInfos); - // Then fix the extensions in the hit + // 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(`/`); @@ -86,6 +85,7 @@ export namespace Search { if (foundMember) { hit.path = connection.sysNameInLocal(`${lib}/${spf}/${name}.${foundMember.extension}`); + hit.member = foundMember; } } diff --git a/src/api/types.ts b/src/api/types.ts index a1ba537f5..522a922cd 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -92,7 +92,7 @@ export interface IBMiObject extends QsysPath { owner?: string } -export interface IBMiMember { +export type IBMiMember = { library: string file: string name: string @@ -176,6 +176,7 @@ export type SearchHit = { readonly?: boolean label?: string file?: IFSFile + member?: IBMiMember } export type SearchHitLine = { diff --git a/src/ui/views/searchView.ts b/src/ui/views/searchView.ts index f3dfaa59c..fe6cb3ae2 100644 --- a/src/ui/views/searchView.ts +++ b/src/ui/views/searchView.ts @@ -79,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.file ? VscodeTools.ifsFileToToolTip(this.path, result.file) : 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`}`; From 128cbbf9a1dbf98839dee2280daa2107fd35a5cd Mon Sep 17 00:00:00 2001 From: Christian Jorgensen Date: Wed, 19 Feb 2025 14:34:01 +0100 Subject: [PATCH 4/4] Fix invalid dates from member info functions --- src/api/components/getMemberInfo.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/api/components/getMemberInfo.ts b/src/api/components/getMemberInfo.ts index 69f9a7cb7..1340e1910 100644 --- a/src/api/components/getMemberInfo.ts +++ b/src/api/components/getMemberInfo.ts @@ -56,7 +56,11 @@ export class GetMemberInfo implements IBMiComponent { async getMemberInfo(connection: IBMi, library: string, sourceFile: string, member: string): Promise { 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) { @@ -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[] = [];