From fedd5fea895d19d5289313c9646f60fde17a821f Mon Sep 17 00:00:00 2001 From: fernandoescolar Date: Sun, 24 Nov 2024 20:02:04 +0100 Subject: [PATCH] read slnx format #309 --- src/OmnisharpIntegrationService.ts | 5 ++ src/SolutionFinder.ts | 8 +-- src/core/Solutions/SolutionFactory.ts | 7 ++ src/core/Solutions/model.ts | 2 +- src/core/Solutions/slnx/Slnx.ts | 65 +++++++++++++++++++ src/core/Utilities/Utilities.ts | 14 ++-- src/extensions/dialogs/openSolutionFile.ts | 2 +- .../decorators/CodeDecoratorController.ts | 4 +- 8 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 src/core/Solutions/slnx/Slnx.ts diff --git a/src/OmnisharpIntegrationService.ts b/src/OmnisharpIntegrationService.ts index bb85b33..eab6ff1 100644 --- a/src/OmnisharpIntegrationService.ts +++ b/src/OmnisharpIntegrationService.ts @@ -23,6 +23,7 @@ interface EventStream { const CSHARP_EXTENSION_ID = 'ms-dotnettools.csharp'; const SELECT_SOLUTION_EVENT_TYPE = 4; const SOLUTION_EXTENSION = '.sln'; +const SOLUTIONX_EXTENSION = '.slnx'; export class OmnisharpIntegrationService extends vscode.Disposable { @@ -72,6 +73,10 @@ export class OmnisharpIntegrationService extends vscode.Disposable { const e = new SolutionSelected(solutionPath); this.eventAggregator.publish(e); } + if (solutionPath.toLocaleLowerCase().endsWith(SOLUTIONX_EXTENSION)) { + const e = new SolutionSelected(solutionPath); + this.eventAggregator.publish(e); + } } } } diff --git a/src/SolutionFinder.ts b/src/SolutionFinder.ts index ac19771..6c1805b 100644 --- a/src/SolutionFinder.ts +++ b/src/SolutionFinder.ts @@ -23,7 +23,7 @@ export class SolutionFinder { public isWorkspaceSolutionFile(filePath: string): boolean { return this.workspaceRoots.indexOf(path.dirname(filePath)) >= 0 - && filePath.endsWith('.sln'); + && (filePath.endsWith('.sln') || filePath.endsWith('.slnx')); } public async findSolutions(): Promise { @@ -35,7 +35,7 @@ export class SolutionFinder { if (config.getOpenSolutionsInRootFolder()) { for (let i = 0; i < this.workspaceRoots.length; i++) { - const paths = await Utilities.searchFilesInDir(this.workspaceRoots[i], '.sln'); + const paths = await Utilities.searchFilesInDir(this.workspaceRoots[i], ['.sln', '.slnx']); paths.forEach(p => solutionPaths.push({ root: this.workspaceRoots[i], sln: p })); } } @@ -44,7 +44,7 @@ export class SolutionFinder { let altFolders = config.getAlternativeSolutionFolders(); for (let i = 0; i < altFolders.length; i++) { for (let j = 0; j < this.workspaceRoots.length; j++) { - const paths = await Utilities.searchFilesInDir(path.join(this.workspaceRoots[j], altFolders[i]), '.sln'); + const paths = await Utilities.searchFilesInDir(path.join(this.workspaceRoots[j], altFolders[i]), ['.sln', '.slnx']); paths.forEach(p => solutionPaths.push({ root: this.workspaceRoots[j], sln: p })); } } @@ -52,7 +52,7 @@ export class SolutionFinder { if (config.getOpenSolutionsInFoldersAndSubfolders()) { for (let i = 0; i < this.workspaceRoots.length; i++) { - const paths = await Utilities.searchFilesInDir(this.workspaceRoots[i], '.sln', true); + const paths = await Utilities.searchFilesInDir(this.workspaceRoots[i], ['.sln', '.slnx'], true); paths.forEach(p => solutionPaths.push({ root: this.workspaceRoots[i], sln: p })); } } diff --git a/src/core/Solutions/SolutionFactory.ts b/src/core/Solutions/SolutionFactory.ts index 35d3c3b..7de087c 100644 --- a/src/core/Solutions/SolutionFactory.ts +++ b/src/core/Solutions/SolutionFactory.ts @@ -1,6 +1,7 @@ import * as fs from "@extensions/fs"; import { Solution } from './model'; import { SlnLoader } from "./SlnLoader"; +import { SlnxSolution } from "./slnx/Slnx"; export class SolutionFactory { public static async load(path: string): Promise { @@ -12,6 +13,12 @@ export class SolutionFactory { return SlnLoader.load(path); } + if (path.endsWith(".slnx")) { + const s = new SlnxSolution(); + await s.load(path); + return s; + } + return new Solution(); } } diff --git a/src/core/Solutions/model.ts b/src/core/Solutions/model.ts index c1a31b9..5cab3de 100644 --- a/src/core/Solutions/model.ts +++ b/src/core/Solutions/model.ts @@ -18,7 +18,7 @@ class SolutionObject { } class SolutionParentObject extends SolutionObject { - protected readonly items: SolutionItem[] = []; + protected items: SolutionItem[] = []; public addItem(item: SolutionItem): void { this.items.push(item); diff --git a/src/core/Solutions/slnx/Slnx.ts b/src/core/Solutions/slnx/Slnx.ts new file mode 100644 index 0000000..bc11e17 --- /dev/null +++ b/src/core/Solutions/slnx/Slnx.ts @@ -0,0 +1,65 @@ +import * as path from "@extensions/path"; +import * as fs from "@extensions/fs"; +import * as xml from "@extensions/xml"; +import { Solution, SolutionFolder, SolutionItem, SolutionProject, SolutionProjectType, SolutionType } from "../model"; +import { v4 as uuidv4 } from "uuid"; + +export class SlnxSolution extends Solution { + private document: xml.XmlElement | undefined; + + constructor() { + super(); + this.type = SolutionType.Slnx; + } + + public async load(filepath: string): Promise { + const content = await fs.readFile(filepath); + this.document = await xml.parseToJson(content); + this.fullPath = filepath; + this.folderPath = path.dirname(filepath); + this.name = path.basename(filepath); + this.refresh(); + } + + public refresh(): void { + if (!this.document) { + return; + } + + this.items = []; + if (this.document.elements.length === 1) { + this.document.elements[0].elements.forEach((child: { name: string; }) => { + if (child.name === 'Folder') { + this.addItem(this.createFolder(child)); + } else if (child.name === 'Project') { + this.addItem(this.createProject(child)); + } + }); + } + } + + private createProject(child: any) : SolutionItem { + const project = new SolutionProject(uuidv4()); + const projectPath = child.attributes.Path.replace(/\\/g, path.sep).trim(); + project.fullPath = path.join(this.folderPath, projectPath); + project.name = path.basename(projectPath, path.extname(projectPath)); + project.type = SolutionProjectType.default; + return project; + } + + private createFolder(child: any) { + const folder = new SolutionFolder(uuidv4()); + folder.name = child.attributes.Name.replace(/^[\/\\]+|[\/\\]+$/g, ''); + folder.fullPath = path.join(this.folderPath, folder.name); + + child.elements.forEach((child: { name: string; }) => { + if (child.name === 'Project') { + folder.addItem(this.createProject(child)) + } else if (child.name === 'Folder') { + folder.addItem(this.createFolder(child)); + } + }); + + return folder; + } +} \ No newline at end of file diff --git a/src/core/Utilities/Utilities.ts b/src/core/Utilities/Utilities.ts index dfd2a2d..600ad64 100644 --- a/src/core/Utilities/Utilities.ts +++ b/src/core/Utilities/Utilities.ts @@ -2,7 +2,7 @@ import * as path from "@extensions/path"; import * as fs from "@extensions/fs"; import { DirectorySearchResult } from "./DirectorySearchResult"; -export async function searchFilesInDir(startPath:string, extension: string, recursive: boolean = false) : Promise { +export async function searchFilesInDir(startPath:string, extensions: string[], recursive: boolean = false) : Promise { if (!(await fs.exists(startPath))) { return []; } @@ -11,14 +11,18 @@ export async function searchFilesInDir(startPath:string, extension: string, recu let files = await fs.readdir(startPath); for (let i = 0; i < files.length; i++) { let filename = path.join(startPath, files[i]); - if (filename.endsWith(extension)) { - result.push(filename); - } + extensions.some(ext => { + if (filename.endsWith(ext)) { + result.push(filename); + return true; + } + return false; + }); if (recursive) { let isDirectory = await fs.isDirectory(filename); if (isDirectory) { - let subresult = await searchFilesInDir(filename, extension, recursive); + let subresult = await searchFilesInDir(filename, extensions, recursive); result = result.concat(subresult); } } diff --git a/src/extensions/dialogs/openSolutionFile.ts b/src/extensions/dialogs/openSolutionFile.ts index cbb782c..7c332dc 100644 --- a/src/extensions/dialogs/openSolutionFile.ts +++ b/src/extensions/dialogs/openSolutionFile.ts @@ -5,7 +5,7 @@ export async function openSolutionFile(label: string): Promise 0) { diff --git a/src/language/decorators/CodeDecoratorController.ts b/src/language/decorators/CodeDecoratorController.ts index 420b3d7..88cb9b9 100644 --- a/src/language/decorators/CodeDecoratorController.ts +++ b/src/language/decorators/CodeDecoratorController.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode"; import { ICodeDecorator } from "./ICodeDecorator"; export class CodeDecoratorController { - private timeout: NodeJS.Timer | undefined = undefined; + private timeout: NodeJS.Timer | number | undefined = undefined; constructor(private readonly context: vscode.ExtensionContext, private readonly decorators: ICodeDecorator[]) { } @@ -41,7 +41,7 @@ export class CodeDecoratorController { private triggerUpdateDecorations(activeEditor: vscode.TextEditor, throttle = false) { if (this.timeout) { - clearTimeout(this.timeout); + clearTimeout(this.timeout as number); this.timeout = undefined; }