Skip to content

Commit 8a2acfa

Browse files
committed
feat: added placeholder
when no project and no open workspace folder
1 parent 2f7b29c commit 8a2acfa

File tree

6 files changed

+85
-106
lines changed

6 files changed

+85
-106
lines changed

package.json

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"type": "git",
1414
"url": "https://github.com/woung717/backtest-manager-vscode.git"
1515
},
16-
"version": "0.1.3",
16+
"version": "0.1.4",
1717
"engines": {
1818
"vscode": "^1.98.0"
1919
},
@@ -138,6 +138,27 @@
138138
}
139139
]
140140
},
141+
"viewsWelcome": [
142+
{
143+
"view": "projectTreeView",
144+
"contents":
145+
"Welcome to the Backtest Manager! \nYou can create a new project by clicking the '+' button in the view title. \n [Create a Project](command:backtestManager.createNewProject)\n To run a backtest, open a Python file in your project and click the 'Run Backtest' button in the editor title.",
146+
"when": "workbenchState != empty"
147+
},
148+
{
149+
"view": "projectTreeView",
150+
"contents":
151+
"Workspace folder is not open. Please open a folder to use Backtest Manager. \n [Open Folder](command:workbench.action.files.openFolder)",
152+
"when": "workbenchState == empty"
153+
},
154+
{
155+
"view": "datasetTreeView",
156+
"contents":
157+
"Workspace folder is not open. Please open a folder to use Backtest Manager.",
158+
"when": "workbenchState == empty"
159+
}
160+
161+
],
141162
"menus": {
142163
"view/title": [
143164
{

src/database.ts

Lines changed: 51 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import * as path from 'path';
33
import * as zlib from 'zlib';
44
import vscode from 'vscode';
5-
import { promisify } from 'util';
65
import { Backtest } from './types';
76
import { ProjectInfo } from './types';
87

@@ -11,154 +10,120 @@ export class Database {
1110
private static instance: Database;
1211
private db: any;
1312
private dbPath = '';
14-
private initialized = false;
1513
private isLoaded = false;
1614
private readonly CONFIG_FILE_NAME: string = '.backtest-man';
17-
private readonly gzip = promisify(zlib.gzip);
18-
private readonly gunzip = promisify(zlib.gunzip);
1915

2016
private constructor() {}
2117

2218
public static getInstance(workspacePath: string): Database {
2319
if (!Database.instance) {
2420
Database.instance = new Database();
2521
Database.instance.initialize(workspacePath);
26-
} else if (!Database.instance.initialized) {
22+
} else if (!Database.instance.isLoaded) {
2723
Database.instance.initialize(workspacePath);
2824
}
29-
30-
if (!Database.instance.initialized) {
31-
throw new Error('Database could not be initialized.');
32-
}
33-
3425
return Database.instance;
3526
}
3627

37-
private async unzipIfNeeded(line: string): Promise<string> {
28+
private unzipIfNeeded(line: string): string | undefined {
3829
try {
3930
const buffer = Buffer.from(line, 'base64');
4031
if (buffer[0] === 0x1f && buffer[1] === 0x8b) {
41-
const decompressed = await this.gunzip(buffer);
42-
return decompressed.toString('utf-8');
32+
return zlib.gunzipSync(buffer).toString('utf-8');
4333
}
4434
return line;
4535
} catch (error) {
36+
vscode.window.showErrorMessage(`Error unzipping file: ${error}`);
4637
throw new Error(`Error unzipping file: ${error}`);
4738
}
4839
}
4940

50-
private async zip(line: string): Promise<string> {
41+
private zip(line: string): string {
5142
try {
52-
const compressed = await this.gzip(Buffer.from(line, 'utf-8'));
53-
return compressed.toString('base64');
43+
return zlib.gzipSync(Buffer.from(line, 'utf-8')).toString('base64');
5444
} catch (error) {
45+
vscode.window.showErrorMessage(`Error unzipping file: ${error}`);
5546
throw new Error(`Error zipping file: ${error}`);
5647
}
5748
}
5849

59-
private async initialize(workspacePath: string): Promise<void> {
60-
if (this.initialized) {
61-
return;
62-
}
63-
64-
if (!workspacePath) {
65-
this.initialized = false;
66-
throw new Error('Workspace path is required to initialize the database.');
67-
}
68-
50+
private initialize(workspacePath: string) {
6951
this.dbPath = path.join(workspacePath, this.CONFIG_FILE_NAME);
52+
this.db = new this.Datastore({
53+
filename: this.dbPath,
54+
beforeDeserialization: (line: string) => this.unzipIfNeeded(line),
55+
afterSerialization: (line: string) => this.zip(line)
56+
});
7057

7158
try {
72-
this.db = new this.Datastore({
73-
filename: this.dbPath,
74-
beforeDeserialization: async (line: string) => {
75-
return await this.unzipIfNeeded(line);
76-
},
77-
afterSerialization: async (line: string) => {
78-
return await this.zip(line);
79-
}
80-
});
81-
82-
this.initialized = true;
83-
} catch (error) {
84-
this.initialized = false;
85-
throw new Error(`Error initializing database at ${this.dbPath}: ${error instanceof Error ? error.message : String(error)}`);
86-
}
87-
88-
try {
89-
await vscode.workspace.fs.stat(vscode.Uri.file(this.dbPath));
59+
vscode.workspace.fs.stat(vscode.Uri.file(this.dbPath));
9060
this.enableStorage();
61+
this.isLoaded = true;
9162
} catch {
92-
console.log(`Database file not found at ${this.dbPath}.`);
63+
this.isLoaded = false;
9364
}
9465
}
9566

96-
private async enableStorage(): Promise<void> {
97-
if (!this.initialized) {
98-
return;
99-
}
100-
101-
await this.db.loadDatabaseAsync();
102-
await this.db.compactDatafileAsync();
103-
67+
private enableStorage(): void {
68+
this.db.loadDatabase();
69+
this.db.persistence.compactDatafile();
10470
this.db.persistence.setAutocompactionInterval(1000 * 60 * 10);
105-
106-
this.isLoaded = true;
10771
}
10872

10973
public isDatabaseLoaded(): boolean {
11074
return this.isLoaded;
11175
}
11276

113-
public async getProjects(): Promise<(ProjectInfo & { results: Backtest[] })[]> {
114-
return await this.db.findAsync({});
77+
public getProjects(): (ProjectInfo & { results: Backtest[] })[] {
78+
if (!this.isLoaded) {
79+
return [];
80+
}
81+
return this.db.find({});
11582
}
11683

117-
public async getProject(projectId: string): Promise<(ProjectInfo & { results: Backtest[] }) | undefined> {
118-
return await this.db.findOneAsync({ _id: projectId });
84+
public getProject(projectId: string): (ProjectInfo & { results: Backtest[] }) | undefined {
85+
return this.db.findOne({ _id: projectId });
11986
}
12087

121-
public async getProjectByName(projectName: string): Promise<(ProjectInfo & { results: Backtest[] }) | undefined> {
122-
return await this.db.findOneAsync({ name: projectName });
88+
public getProjectByName(projectName: string): (ProjectInfo & { results: Backtest[] }) | undefined {
89+
return this.db.findOne({ name: projectName });
12390
}
12491

125-
public async addProject(projectData: ProjectInfo): Promise<ProjectInfo> {
126-
await this.enableStorage();
127-
92+
public addProject(projectData: ProjectInfo): ProjectInfo {
93+
this.enableStorage();
12894
const projectToInsert = {
129-
...projectData,
130-
results: projectData.results || [], // Initialize if not present
131-
lastConfig: projectData.lastConfig || {}, // Initialize if not present
132-
created: projectData.created || new Date(), // Set if not present
133-
updated: projectData.updated || new Date(), // Set if not present
95+
...projectData,
96+
results: projectData.results || [],
97+
lastConfig: projectData.lastConfig || {},
98+
created: projectData.created || new Date(),
99+
updated: projectData.updated || new Date(),
134100
};
135-
const insertedProject = await this.db.insertAsync(projectToInsert);
136-
137-
return insertedProject as ProjectInfo; // Cast because NeDB types might be generic
101+
const insertedProject = this.db.insert(projectToInsert);
102+
return insertedProject as ProjectInfo;
138103
}
139104

140-
public async updateProject(projectId: string, updates: Partial<ProjectInfo>): Promise<void> {
141-
await this.db.updateAsync({ _id: projectId }, { $set: updates }, {});
105+
public updateProject(projectId: string, updates: Partial<ProjectInfo>): void {
106+
this.db.update({ _id: projectId }, { $set: updates }, {});
142107
}
143108

144-
public async updateLastConfig(projectId: string, config: any): Promise<void> {
145-
await this.db.updateAsync({ _id: projectId }, { $set: { lastConfig: config } }, {});
109+
public updateLastConfig(projectId: string, config: any): void {
110+
this.db.update({ _id: projectId }, { $set: { lastConfig: config } }, {});
146111
}
147112

148-
public async deleteProject(projectId: string): Promise<void> {
149-
const numRemoved = await this.db.removeAsync({ _id: projectId }, {});
113+
public deleteProject(projectId: string): void {
114+
const numRemoved = this.db.remove({ _id: projectId }, {});
150115
if (numRemoved === 0) {
151116
throw new Error(`Project not found: ${projectId}`);
152117
}
153118
}
154119

155-
public async getBacktestResults(projectId: string): Promise<Backtest[]> {
156-
const project = await this.getProject(projectId);
120+
public getBacktestResults(projectId: string): Backtest[] {
121+
const project = this.getProject(projectId);
157122
return project?.results || [];
158123
}
159124

160-
public async addBacktestResult(projectId: string, result: Backtest): Promise<void> {
161-
const numUpdated = await this.db.updateAsync(
125+
public addBacktestResult(projectId: string, result: Backtest): void {
126+
const numUpdated = this.db.update(
162127
{ _id: projectId },
163128
{ $push: { results: result } },
164129
{}
@@ -168,8 +133,8 @@ export class Database {
168133
}
169134
}
170135

171-
public async deleteBacktestResult(projectId: string, resultId: string): Promise<void> {
172-
const numUpdated = await this.db.updateAsync(
136+
public deleteBacktestResult(projectId: string, resultId: string): void {
137+
const numUpdated = this.db.update(
173138
{ _id: projectId },
174139
{ $pull: { results: { id: resultId } } },
175140
{}
@@ -179,7 +144,7 @@ export class Database {
179144
}
180145
}
181146

182-
public async saveDatabase(): Promise<void> {
183-
await this.db.persistence.compactDatafileAsync();
147+
public saveDatabase(): void {
148+
this.db.persistence.compactDatafile();
184149
}
185150
}

src/datasetTreeProvider.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ export class DatasetTreeProvider implements vscode.TreeDataProvider<DatasetTreeI
4343
constructor(
4444
private datasetService: IDatasetService, // Injected service
4545
) {
46-
// Initialize with loading state
47-
this.data = [new DatasetTreeItem('loading', 'Loading Datasets...', true)];
4846
// Trigger initial load
4947
this.refresh().catch(err => {
5048
console.error('Failed to load datasets:', err);

src/extension.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,10 @@ export function activate(context: vscode.ExtensionContext) {
4141
// Get workspace path
4242
const workspaceFolders = vscode.workspace.workspaceFolders;
4343
if (!workspaceFolders || workspaceFolders.length === 0) {
44-
vscode.window.showErrorMessage('Workspace folder is not open. Please open a folder to use Backtest Manager.');
4544
return;
4645
}
47-
const workspacePath = workspaceFolders[0].uri.fsPath;
4846

47+
const workspacePath = workspaceFolders[0].uri.fsPath;
4948
const database = Database.getInstance(workspacePath);
5049

5150
// Instantiate Services

src/projectTreeProvider.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,20 @@ export class ProjectTreeItem {
2222
}
2323

2424
export class ProjectTreeProvider implements vscode.TreeDataProvider<ProjectTreeItem> {
25-
// private db: Database; // Removed
2625
private data: ProjectTreeItem[] = [
27-
new ProjectTreeItem('loading', 'Loading Projects...', undefined, undefined, undefined, undefined)
2826
];
2927

3028
private _onDidChangeTreeData: vscode.EventEmitter<ProjectTreeItem | undefined | null | void> = new vscode.EventEmitter<ProjectTreeItem | undefined | null | void>();
3129
readonly onDidChangeTreeData: vscode.Event<ProjectTreeItem | undefined | null | void> = this._onDidChangeTreeData.event;
3230

33-
constructor(private projectService: IProjectService) { // Modified constructor
34-
// Trigger initial load async
31+
constructor(private projectService: IProjectService) {
3532
this.refresh().catch(error => {
3633
vscode.window.showErrorMessage(`Error during initial project load: ${error instanceof Error ? error.message : String(error)}`);
3734
});
3835
}
3936

4037
private async loadProjects(): Promise<void> {
4138
try {
42-
// projectService.getProjects() is expected to return ProjectInfo with 'results' (Backtest[]) populated.
4339
let projects = await this.projectService.getProjects();
4440

4541
projects = projects.sort((a, b) => a.name.localeCompare(b.name));
@@ -121,7 +117,7 @@ export class ProjectTreeProvider implements vscode.TreeDataProvider<ProjectTreeI
121117
if (element.contextValue === 'project' && element.projectInfo) {
122118
treeItem.tooltip = `Project: ${element.projectInfo.name}\nPath: ${element.projectInfo.path}\nEngine: ${element.projectInfo.engine}`;
123119
} else if (element.contextValue === 'backtestResult' && element.backtestResult) {
124-
treeItem.tooltip = `Backtest Result\nStrategy: ${element.backtestResult.strategy}\nTotal Return: ${(element.backtestResult.performance?.totalReturn * 100 || 0).toFixed(2)}%`;
120+
treeItem.tooltip = `Backtest Result\nTotal Return: ${(element.backtestResult.performance?.totalReturn * 100 || 0).toFixed(2)}%`;
125121
treeItem.command = {
126122
command: 'backtestManager.showBacktestResult',
127123
title: 'View Backtest Result',

src/services/projectService.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ export class ProjectService implements IProjectService {
2424
constructor(private database: Database, private workspaceRootPath: string) {} // Updated constructor
2525

2626
async getProjects(): Promise<ProjectInfo[]> {
27-
return await this.database.getProjects();
27+
return this.database.getProjects();
2828
}
2929

3030
async getProject(projectId: string): Promise<ProjectInfo | null> {
3131
if (!this.database.isDatabaseLoaded()) {
3232
return null;
3333
}
3434

35-
const project = await this.database.getProject(projectId);
35+
const project = this.database.getProject(projectId);
3636
return project || null; // NeDB findOne can return undefined, ensure null if not found
3737
}
3838

@@ -41,7 +41,7 @@ export class ProjectService implements IProjectService {
4141
return null;
4242
}
4343

44-
const project = await this.database.getProjectByName(projectName);
44+
const project = this.database.getProjectByName(projectName);
4545
return project || null; // Ensure null if not found
4646
}
4747

@@ -65,17 +65,17 @@ export class ProjectService implements IProjectService {
6565
lastConfig: {} // Initialize as empty object or specific default config structure
6666
};
6767

68-
return await this.database.addProject(newProjectForDb);
68+
return this.database.addProject(newProjectForDb);
6969
}
7070

7171
async updateProject(projectId: string, projectData: Partial<ProjectInfo>): Promise<ProjectInfo | null> {
72-
await this.database.updateProject(projectId, projectData);
72+
this.database.updateProject(projectId, projectData);
7373
return await this.getProject(projectId); // Fetch and return updated project
7474
}
7575

7676
async deleteProject(projectId: string): Promise<boolean> {
7777
try {
78-
await this.database.deleteProject(projectId);
78+
this.database.deleteProject(projectId);
7979
return true;
8080
} catch (error) {
8181
console.error(`Error deleting project ${projectId}:`, error);
@@ -84,17 +84,17 @@ export class ProjectService implements IProjectService {
8484
}
8585

8686
async addBacktestResult(projectId: string, backtestResult: Backtest): Promise<ProjectInfo | null> {
87-
await this.database.addBacktestResult(projectId, backtestResult);
87+
this.database.addBacktestResult(projectId, backtestResult);
8888
return await this.getProject(projectId);
8989
}
9090

9191
async deleteBacktestResult(projectId: string, backtestResultId: string): Promise<ProjectInfo | null> {
92-
await this.database.deleteBacktestResult(projectId, backtestResultId);
92+
this.database.deleteBacktestResult(projectId, backtestResultId);
9393
return await this.getProject(projectId);
9494
}
9595

9696
async updateLastConfig(projectId: string, config: any): Promise<ProjectInfo | null> {
97-
await this.database.updateLastConfig(projectId, config);
97+
this.database.updateLastConfig(projectId, config);
9898
return await this.getProject(projectId);
9999
}
100100

0 commit comments

Comments
 (0)