diff --git a/CHANGELOG.md b/CHANGELOG.md index b462934..e316084 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Change Log +### [v3.1.0](https://github.com/drizzy/code-make/releases/tag/v3.1.0) + +> 03 March 2025 + +### Features + +- **Added a new function** to stop the program when required. +- **Improved Status Bar Updates**: The extension now ensures that the status bar shows the correct button based on the program's current state (compile, run, stop) without unnecessary delays. +- **Optimized Start/Stop Button Logic**: The "Run" button is now only visible when there is a valid project and the program is not currently running, while the "Stop" button appears when the program is actively running. +- **Optimized Process Check**: The extension now checks the process state every few seconds, ensuring the buttons reflect the actual status without introducing unnecessary performance overhead. + ### [v3.0.0](https://github.com/drizzy/code-make/releases/tag/v3.0.0) > 15 February 2025 diff --git a/package.json b/package.json index 0a3d184..d3fd163 100755 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "code-make", "displayName": "Code Make", "description": "Simplify C, C++, Go, and Java project creation and compilation with Make.", - "version": "3.0.0", + "version": "3.1.0", "publisher": "drizzy", "license": "MIT", "icon": "icons/logo.png", @@ -32,19 +32,15 @@ "tags": [ "C", "C++", - "cpp", "Go", - "Golang", "java", - "javac", - "run", - "compiler", - "build" + "make" ], "activationEvents": [ "onStartupFinished", "onCommand:code-make-create.run", - "onCommand:code-make-start.run" + "onCommand:code-make-start.run", + "onCommand:code-make-stop.run" ], "main": "./out/src/extension.js", "scripts": { diff --git a/src/CodeManager.ts b/src/CodeManager.ts deleted file mode 100755 index 666dffe..0000000 --- a/src/CodeManager.ts +++ /dev/null @@ -1,335 +0,0 @@ -import * as vscode from 'vscode'; -import * as os from 'os'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as SBar from './interface'; -import * as c from './templates/c'; -import * as cpp from './templates/cpp'; -import * as go from './templates/go'; -import * as java from './templates/java'; -import * as misc from './templates/misc'; - -export class CodeManage { - private _terminal: vscode.Terminal; - private _folderPath: string; - private _items: SBar.StatusBarItems; - private _watcher: vscode.FileSystemWatcher | null; - - constructor() { - this._terminal = vscode.window.createTerminal({ - name: 'Code Make', - hideFromUser: true, - }); - this._folderPath = this.getFolderPath(); - this._items = { - create: vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right), - start: vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right), - }; - this._watcher = null; - - this.setupFileSystemWatcher(); - this.updateStatusBar(); - } - - public async create(language: string = 'cpp') { - try { - if (!this._folderPath) { - return vscode.window.showErrorMessage('Please open a project folder first'); - } - - if (language === 'cpp') { - const folders = [ - 'assets', - 'docs', - 'include/app', - 'include/core', - 'include/utils', - 'lib', - 'scripts', - 'src/app', - 'src/core', - 'src/utils', - 'test', - ]; - - folders.forEach((folder) => this.createFolder(this._folderPath, folder)); - - this.createFile(path.join(this._folderPath, 'src', 'main.cpp'), cpp.mainCPP); - this.createFile(path.join(this._folderPath, 'Makefile'), cpp.makeCPP); - - } else if (language === 'c') { - const folders = [ - 'assets', - 'docs', - 'include/app', - 'include/core', - 'include/utils', - 'lib', - 'scripts', - 'src/app', - 'src/core', - 'src/utils', - 'test', - ]; - - folders.forEach((folder) => this.createFolder(this._folderPath, folder)); - - this.createFile(path.join(this._folderPath, 'src', 'main.c'), c.mainC); - this.createFile(path.join(this._folderPath, 'Makefile'), c.makeC); - - } else if (language === 'go') { - const folders = [ - 'assets', - 'api', - 'cmd', - 'configs', - 'docs', - 'internal/app', - 'pkg/utils', - 'scripts', - 'test', - ]; - - folders.forEach((folder) => this.createFolder(this._folderPath, folder)); - - this.createFile(path.join(this._folderPath, 'main.go'), go.mainGo); - this.createFile(path.join(this._folderPath, 'Makefile'), go.makeGo); - - const moduleName = await vscode.window.showInputBox({ - prompt: 'Enter the Go module name (e.g., github.com/username/project)', - placeHolder: 'github.com/username/project', - }); - - if (moduleName) { - this.terminal(`go mod init ${moduleName}`); - } else { - vscode.window.showErrorMessage('Go module name is required.'); - return; - } - - } else if (language === 'java') { - const folders = [ - 'assets', - 'docs', - 'src/com/example', - 'lib', - 'scripts', - 'test', - ]; - - folders.forEach((folder) => this.createFolder(this._folderPath, folder)); - - this.createFile(path.join(this._folderPath, 'src', 'com', 'example', 'Main.java'), java.mainJava); - this.createFile(path.join(this._folderPath, 'Makefile'), java.makeJava); - } - - this.createFile(path.join(this._folderPath, '.gitignore'), misc.gitignore); - this.createFile(path.join(this._folderPath, 'README.md'), misc.readme); - - vscode.window.showInformationMessage(`Project created successfully for ${language}!`); - } catch (e) { - console.log('An error occurred.', e); - vscode.window.showErrorMessage('Failed to create project.'); - } - } - - private build() { - try { - - if (!this._folderPath) { - return vscode.window.showErrorMessage('No project folder is open.'); - } - - const mainC = this.findFileRecursive(path.join(this._folderPath, 'src'), 'main.c'); - const mainCpp = this.findFileRecursive(path.join(this._folderPath, 'src'), 'main.cpp'); - const mainGo = this.findFileRecursive(this._folderPath, 'main.go'); - const mainJava = this.findFileRecursive(path.join(this._folderPath, 'src'), 'Main.java'); - - const isCProject = mainC && fs.existsSync(path.join(this._folderPath, 'Makefile')); - const isCppProject = mainCpp && fs.existsSync(path.join(this._folderPath, 'Makefile')); - const isJavaProject = mainJava && fs.existsSync(path.join(this._folderPath, 'Makefile')); - const isGoProject = - mainGo && - fs.existsSync(path.join(this._folderPath, 'go.mod')) && - fs.existsSync(path.join(this._folderPath, 'Makefile')); - - if (isCProject) { - this.terminal('make'); - } else if (isCppProject) { - this.terminal('make'); - } else if (isGoProject) { - this.terminal('make'); - } else if (isJavaProject) { - this.terminal('make'); - } else { - return vscode.window.showErrorMessage('No valid project found.'); - } - - } catch (e) { - console.log('An error occurred.', e); - vscode.window.showErrorMessage('Failed to build the project.'); - } - } - - public start() { - - if (!this._folderPath) { - return; - } - - const binPath: string = path.join(path.resolve(this._folderPath), 'build'); - try { - const binStats = fs.statSync(binPath); - if (!binStats.isDirectory()) { - this.build(); - } else { - const files: string[] = fs.readdirSync(binPath); - if (files.length === 0) { - this.build(); - } else { - this.terminal('make run'); - } - } - } catch (e) { - this.build(); - } - } - - private terminal(command: string) { - try { - - if (this._terminal.exitStatus) { - this._terminal = vscode.window.createTerminal({ - name: 'Code Make', - hideFromUser: true, - }); - } - - this._terminal.show(); - - const isWindows = os.platform() === "win32"; - - const shellPath = vscode.env.shell.toLowerCase(); - - let clearCommand; - if (isWindows) { - if (shellPath.includes("git") && shellPath.includes("bash.exe")) { - clearCommand = "clear"; - } else { - clearCommand = "cls"; - } - } else { - clearCommand = "clear"; - } - - this._terminal.sendText(`${clearCommand}`); - this._terminal.sendText(`${command}`); - - } catch (error) { - vscode.window.showErrorMessage(`Terminal error: ${error instanceof Error ? error.message : error}`); - } - } - - private updateStatusBar() { - if (!this._folderPath) { - this._items.create.hide(); - this._items.start.hide(); - return; - } - - const isCProject = - this.findFileRecursive(path.join(this._folderPath, 'src'), 'main.c') && - fs.existsSync(path.join(this._folderPath, 'Makefile')); - - const isCppProject = - this.findFileRecursive(path.join(this._folderPath, 'src'), 'main.cpp') && - fs.existsSync(path.join(this._folderPath, 'Makefile')); - - const isGoProject = - fs.existsSync(path.join(this._folderPath, 'go.mod')) && - fs.existsSync(path.join(this._folderPath, 'Makefile')) && - this.findFileRecursive(this._folderPath, 'main.go'); - - const isJavaProject = - this.findFileRecursive(path.join(this._folderPath, 'src'), 'Main.java') && - fs.existsSync(path.join(this._folderPath, 'Makefile')); - - const isProjectValid = isCppProject || isCProject || isGoProject || isJavaProject; - - this._items.create.text = `$(gear) Compile`; - this._items.create.tooltip = 'Create Project'; - this._items.create.command = 'code-make-create.run'; - this._items.create.color = '#FF79C6'; - - this._items.start.text = `$(play) Run`; - this._items.start.tooltip = 'Run Project'; - this._items.start.command = 'code-make-start.run'; - this._items.start.color = '#89D185'; - - if (!isProjectValid) { - this._items.create.show(); - this._items.start.hide(); - } else { - this._items.start.show(); - this._items.create.hide(); - } - } - - private setupFileSystemWatcher() { - this._watcher = vscode.workspace.createFileSystemWatcher('**/*', false, false, false); - this._watcher.onDidChange(this.updateStatusBar.bind(this)); - this._watcher.onDidCreate(this.updateStatusBar.bind(this)); - this._watcher.onDidDelete(this.updateStatusBar.bind(this)); - } - - private createFolder(folderPath: string, folderName: string) { - const fullPath: string = path.join(folderPath, folderName); - - if (!fs.existsSync(fullPath)) { - try { - fs.mkdirSync(fullPath, { recursive: true }); - } catch (error) { - vscode.window.showErrorMessage(`Failed to create folder: ${error instanceof Error ? error.message : error}`); - } - } - } - - private async createFile(filePath: string, templatePath: string) { - if (!fs.existsSync(filePath)) { - try { - await fs.promises.writeFile(filePath, templatePath, 'utf8'); - } catch (error) { - vscode.window.showErrorMessage(`Failed to create file: ${error instanceof Error ? error.message : error}`); - } - } - } - - private getFolderPath(): string { - if (!vscode.workspace || !vscode.workspace.workspaceFolders) { - return ''; - } - return vscode.workspace.workspaceFolders[0].uri.fsPath; - } - - private findFileRecursive(startPath: string, filename: string): boolean { - if (!fs.existsSync(startPath)) { - return false; - } - - const files = fs.readdirSync(startPath); - for (const file of files) { - const fullPath = path.join(startPath, file); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - if (this.findFileRecursive(fullPath, filename)) { - return true; - } - } else if (file === filename) { - return true; - } - } - return false; - } - -} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index a293d26..d6f0a3e 100755 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,14 +1,14 @@ import * as vscode from 'vscode'; -import { CodeManage } from './CodeManager'; +import { MakefileManager } from './manager/MakefileManager'; /** * It registers the commands that are defined in the `package.json` file * @param context - This is the context of the extension. */ export function activate(context: vscode.ExtensionContext) { - const manager = new CodeManage(); + const manager = new MakefileManager(); - const createProject: vscode.Disposable = vscode.commands.registerCommand('code-make-create.run', async () => { + const create: vscode.Disposable = vscode.commands.registerCommand('code-make-create.run', async () => { const selectedLanguage = await vscode.window.showQuickPick( ['C', 'C++', 'Go', 'Java'], @@ -42,7 +42,11 @@ export function activate(context: vscode.ExtensionContext) { manager.start(); }); - context.subscriptions.push(createProject, start); + const stop: vscode.Disposable = vscode.commands.registerCommand('code-make-stop.run', () => { + manager.stop(); + }); + + context.subscriptions.push(create, start, stop); } /** diff --git a/src/interface.ts b/src/interface.ts deleted file mode 100755 index f701ca6..0000000 --- a/src/interface.ts +++ /dev/null @@ -1,6 +0,0 @@ -import * as vscode from 'vscode'; - -export interface StatusBarItems { - create: vscode.StatusBarItem; - start: vscode.StatusBarItem; -} \ No newline at end of file diff --git a/src/manager/ConfigManager.ts b/src/manager/ConfigManager.ts new file mode 100644 index 0000000..1ffa55e --- /dev/null +++ b/src/manager/ConfigManager.ts @@ -0,0 +1,63 @@ +import * as vscode from 'vscode'; +import * as c from '../templates/c'; +import * as cpp from '../templates/cpp'; +import * as go from '../templates/go'; +import * as java from '../templates/java'; +import * as misc from '../templates/misc'; +import { Language, LanguageConfig } from '../utils/types'; + +export class ConfigManager { + + public configureLanguage(language: Language): LanguageConfig | undefined { + const config: Record = { + c: { + folders: c.folders, + mainFilePath: 'src/main.c', + mainFileContent: c.main, + makefileContent: c.makefile, + gitignore: misc.gitignore, + readme: misc.readme, + }, + cpp: { + folders: cpp.folders, + mainFilePath: 'src/main.cpp', + mainFileContent: cpp.main, + makefileContent: cpp.makefile, + gitignore: misc.gitignore, + readme: misc.readme, + }, + go: { + folders: go.folders, + mainFilePath: 'cmd/app/main.go', + mainFileContent: go.main, + makefileContent: go.makefile, + gitignore: misc.gitignore, + readme: misc.readme, + }, + java: { + folders: java.folders, + mainFilePath: 'src/main/java/com/example/Main.java', + mainFileContent: java.main, + makefileContent: java.makefile, + gitignore: misc.gitignore, + readme: misc.readme, + }, + }; + + return config[language]; + } + + public configureButton( + item: vscode.StatusBarItem, + text: string, + tooltip: string, + command: string, + color: string + ) { + item.text = text; + item.tooltip = tooltip; + item.command = command; + item.color = color; + } + +} \ No newline at end of file diff --git a/src/manager/FileManager.ts b/src/manager/FileManager.ts new file mode 100644 index 0000000..46bfc3b --- /dev/null +++ b/src/manager/FileManager.ts @@ -0,0 +1,92 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; + +export class FileManager { + private _folderPath: string; + + constructor() { + this._folderPath = this.getFolderPath() || ''; + } + + public get folderPath(): string { + return this._folderPath; + } + + public getFolderPath(): string { + if (!vscode.workspace || !vscode.workspace.workspaceFolders) { + return ''; + } + return vscode.workspace.workspaceFolders[0].uri.fsPath; + } + + public createFolder(folderPath: string, folderName: string) { + const fullPath: string = path.join(folderPath, folderName); + + if (!fs.existsSync(fullPath)) { + try { + fs.mkdirSync(fullPath, { recursive: true }); + } catch (error) { + vscode.window.showErrorMessage(`Failed to create folder: ${error instanceof Error ? error.message : error}`); + } + } + } + + public async createFile(filePath: string, templatePath: string) { + if (!fs.existsSync(filePath)) { + try { + const content = fs.existsSync(templatePath) ? await fs.promises.readFile(templatePath, 'utf8') : templatePath; + await fs.promises.writeFile(filePath, content, 'utf8'); + } catch (error) { + vscode.window.showErrorMessage(`Failed to create file: ${error instanceof Error ? error.message : error}`); + } + } + } + + public findFileRecursive(startPath: string, filename: string): boolean { + if (!fs.existsSync(startPath)) {return false;} + + const files = fs.readdirSync(startPath); + if (files.length === 0) {return false;} + + for (const file of files) { + const fullPath = path.join(startPath, file); + const stat = fs.statSync(fullPath); + + if (stat.isDirectory()) { + if (this.findFileRecursive(fullPath, filename)) {return true;} + } else if (file === filename) { + return true; + } + } + return false; + } + + public getProgramNameFromMakefile(): string | null { + const makefilePath = path.join(this._folderPath, 'Makefile'); + + if (!fs.existsSync(makefilePath)) { + vscode.window.showWarningMessage('Makefile not found in the workspace.'); + return null; + } + + const makefileContent = fs.readFileSync(makefilePath, 'utf-8'); + + const targetRegex = /TARGET\s*[:]?\s*=\s*(\S+)/; + const mainClassRegex = /MAIN_CLASS\s*[:]?\s*=\s*(\S+)/; + + let match = makefileContent.match(targetRegex); + if (match && match[1]) { + return match[1]; + } + + match = makefileContent.match(mainClassRegex); + if (match && match[1]) { + return match[1]; + } + + vscode.window.showWarningMessage('Neither TARGET nor MAIN_CLASS variables were found in Makefile.'); + return null; + } + +} \ No newline at end of file diff --git a/src/manager/MakefileManager.ts b/src/manager/MakefileManager.ts new file mode 100755 index 0000000..8025f7a --- /dev/null +++ b/src/manager/MakefileManager.ts @@ -0,0 +1,105 @@ +import * as vscode from 'vscode'; +import * as path from 'path'; +import { FileManager } from '../manager/FileManager'; +import { ConfigManager } from '../manager/ConfigManager'; +import { TerminalManager } from '../manager/TerminalManager'; +import { WatcherManager } from '../manager/WatcherManager'; +import { Language } from '../utils/types'; + +export class MakefileManager { + private _file: FileManager; + private _config: ConfigManager; + private _terminal: TerminalManager; + private _watcher: WatcherManager; + private _isRunning: boolean = false; + + constructor() { + this._file = new FileManager(); + this._config = new ConfigManager(); + this._terminal = new TerminalManager(); + this._watcher = new WatcherManager(); + + this._watcher.setupFileSystemWatcher(); + this._watcher.updateStatusBar(); + } + + public async create(language: Language = 'c') { + try { + + if (!this._file.folderPath) { + return vscode.window.showErrorMessage('Please open a project folder first'); + } + + const languageConfig = this._config.configureLanguage(language); + if (!languageConfig) { + return vscode.window.showErrorMessage('Unsupported language'); + } + + languageConfig.folders.forEach((folder) => this._file.createFolder(this._file.folderPath, folder)); + + this._file.createFile(path.join(this._file.folderPath, languageConfig.mainFilePath), languageConfig.mainFileContent); + this._file.createFile(path.join(this._file.folderPath, 'Makefile'), languageConfig.makefileContent); + + if (language === 'go') { + const moduleName = await vscode.window.showInputBox({ + prompt: 'Enter the Go module name (e.g., github.com/username/project)', + placeHolder: 'github.com/username/project', + }); + + if (moduleName) { + this._terminal.runCommand(`go mod init ${moduleName}`); + setTimeout(() => { + this._watcher.updateStatusBar(); + }, 2000); + } else { + vscode.window.showErrorMessage('Go module name is required.'); + return; + } + } + + this._file.createFile(path.join(this._file.folderPath, '.gitignore'), languageConfig.gitignore); + this._file.createFile(path.join(this._file.folderPath, 'README.md'), languageConfig.readme); + + vscode.window.showInformationMessage(`Project created successfully for ${language}!`); + + } catch (e) { + console.log('An error occurred.', e); + vscode.window.showErrorMessage('Failed to create project.'); + } + } + + public async start() { + + if (!this._file.folderPath) { + vscode.window.showErrorMessage('Please open a project folder first.'); + return; + } + + if (this._watcher.recompile) { + this._terminal.runCommand('make'); + this._watcher.recompile = false; + } + + this._isRunning = true; + this._watcher.updateStatusBar(); + this._terminal.runCommand('make run'); + } + + public stop() { + if (!this._isRunning) { + return; + } + + this._isRunning = false; + + try { + if (this._terminal) { + this._terminal.dispose; + } + this._watcher.updateStatusBar(); + } catch (e) { + vscode.window.showErrorMessage('Failed to stop project.'); + } + } + +} \ No newline at end of file diff --git a/src/manager/ProcessManager.ts b/src/manager/ProcessManager.ts new file mode 100644 index 0000000..77ce73b --- /dev/null +++ b/src/manager/ProcessManager.ts @@ -0,0 +1,84 @@ +import * as vscode from 'vscode'; +import * as process from 'child_process'; +import * as os from 'os'; +import { StatusBarItems } from '../utils/types'; + +export class ProcessManager { + private _items: StatusBarItems; + private _isRunningCheckInterval: NodeJS.Timeout | null = null; + private _isProcessRunning: boolean = false; + + constructor() { + this._items = { + create: vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right), + start: vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right), + stop: vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right), + }; + } + + public get items(): StatusBarItems { + return this._items; + } + + public get runningP(): boolean { + return this._isProcessRunning; + } + + private isProcessRunning(processName: string): boolean { + + try { + + const isWindows = os.platform() === "win32"; + + let command = isWindows ? `tasklist /FI "IMAGENAME eq ${processName}.exe"` : `ps aux`; + + const output = process.execSync(command, { stdio: 'pipe' }).toString(); + + const lines = output.split('\n'); + for (const line of lines) { + if (line.includes(processName) && !line.includes('grep')) { + return true; + } + } + return false; + } catch (e) { + return false; + } + } + + public monitorProcess(processName: string) { + + if (!this._isProcessRunning) { + this.toggleProcessButtons(this.isProcessRunning(processName)); + } + + if (this._isRunningCheckInterval) { + clearInterval(this._isRunningCheckInterval); + } + + this._isRunningCheckInterval = setInterval(() => { + const isRunning = this.isProcessRunning(processName); + if (isRunning !== this._isProcessRunning) { + this.toggleProcessButtons(isRunning); + } + }, 1000); + } + + private toggleProcessButtons(isRunning: boolean) { + if (isRunning) { + this._items.stop.show(); + this._items.start.hide(); + }else { + this._items.start.show(); + this._items.stop.hide(); + } + this._isProcessRunning = isRunning; + } + + public hideAllButtons() { + this._items.create.hide(); + this._items.start.hide(); + this._items.stop.hide(); + } + +} \ No newline at end of file diff --git a/src/manager/TerminalManager.ts b/src/manager/TerminalManager.ts new file mode 100644 index 0000000..d4b822d --- /dev/null +++ b/src/manager/TerminalManager.ts @@ -0,0 +1,47 @@ +import * as vscode from 'vscode'; +import * as os from 'os'; + +export class TerminalManager { + + private _terminal: vscode.Terminal | undefined; + + public async runCommand(command: string) { + + if (!this._terminal || this._terminal.exitStatus !== undefined) { + this._terminal = vscode.window.createTerminal({ + name: 'CodeMake', + hideFromUser: true, + }); + } + + const shellPath = vscode.env.shell.toLowerCase(); + const isWindows = os.platform() === 'win32'; + + let clearCommand: string; + + if (isWindows) { + if (shellPath.includes('git') && shellPath.includes('bash.exe')) { + clearCommand = 'clear'; + } else { + clearCommand = 'cls'; + } + } else { + clearCommand = 'clear'; + } + + this._terminal.sendText(clearCommand); + + this._terminal.show(); + + this._terminal.sendText(command); + + } + + public get dispose() { + if (this._terminal) { + return this._terminal.dispose(); + } + return undefined; + } + +} diff --git a/src/manager/WatcherManager.ts b/src/manager/WatcherManager.ts new file mode 100644 index 0000000..516e36c --- /dev/null +++ b/src/manager/WatcherManager.ts @@ -0,0 +1,116 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import { FileManager } from './FileManager'; +import { ConfigManager } from './ConfigManager'; +import { ProcessManager } from './ProcessManager'; + +export class WatcherManager { + private _file: FileManager; + private _config: ConfigManager; + private _process: ProcessManager; + private _watcher: vscode.FileSystemWatcher[] = []; + private _recompile: boolean = false; + + constructor() { + this._file = new FileManager(); + this._config = new ConfigManager(); + this._process = new ProcessManager(); + } + + public set recompile(value: boolean) { + this._recompile = value; + } + + public get recompile(): boolean { + return this._recompile; + } + + public updateStatusBar() { + + if (!this._file.folderPath) { + this._process.hideAllButtons(); + return; + } + + const projectConfigs = [ + { type: 'C', dir: 'src', file: 'main.c', extra: ['Makefile'] }, + { type: 'C++', dir: 'src', file: 'main.cpp', extra: ['Makefile'] }, + { type: 'Go', dir: 'cmd', file: 'main.go', extra: ['go.mod', 'Makefile'] }, + { type: 'Java', dir: 'src', file: 'Main.java', extra: ['Makefile'] }, + ]; + + const isProjectValid = projectConfigs.some(({ dir, file, extra }) => { + const mainFileExists = this._file.findFileRecursive(path.join(this._file.folderPath!, dir), file); + const extraFilesExist = extra.every(e => fs.existsSync(path.join(this._file.folderPath!, e))); + return mainFileExists && extraFilesExist; + }); + + this._config.configureButton(this._process.items.create, '$(gear) Compile', 'Create Project', 'code-make-create.run', '#FF79C6'); + this._config.configureButton(this._process.items.start, '$(play) Run', 'Run Project', 'code-make-start.run', this._recompile ? '#FFD700' : '#89D185'); + this._config.configureButton(this._process.items.stop, '$(stop) Stop', 'Stop Project', 'code-make-stop.run', '#FF5555'); + + if (!isProjectValid) { + this._process.items.create.show(); + this._process.items.start.hide(); + this._process.items.stop.hide(); + return; + } + + this._process.items.create.hide(); + this._process.items.start.show(); + this._process.items.stop.hide(); + + const programName = this._file.getProgramNameFromMakefile(); + if (!programName) { + vscode.window.showErrorMessage('Could not get program name from Makefile.'); + return; + } + + const isWindows = os.platform() === "win32"; + const processName = isWindows ? programName.replace(/\.exe$/, '') : programName; + + this._process.monitorProcess(processName); + } + + public setupFileSystemWatcher() { + const sourcePatterns = [ + '**/*build', + '**/*.a', + '**/*.c', + '**/*.cpp', + '**/*.h', + '**/*.hpp', + '**/*.go', + '**/*.java', + '**/*.jar', + '**/*.sh', + '**/*Makefile', + ]; + + sourcePatterns.forEach(pattern => { + const watcher = vscode.workspace.createFileSystemWatcher(pattern); + watcher.onDidChange(() => this.handleFileChange()); + watcher.onDidCreate(() => this.handleFileChange()); + watcher.onDidDelete(() => this.handleFileChange()); + this._watcher.push(watcher); + }); + } + + private handleFileChange() { + if (!this._process.runningP) { + this.markForRecompile(); + } + } + + private markForRecompile() { + this._recompile = true; + this.updateStatusBar(); + } + + public dispose() { + this._watcher.forEach(watcher => watcher.dispose()); + } + +} \ No newline at end of file diff --git a/src/templates/c.ts b/src/templates/c.ts index 7aff59b..fba555f 100755 --- a/src/templates/c.ts +++ b/src/templates/c.ts @@ -1,4 +1,22 @@ -export const makeC = `# ================================ +export const folders = [ +'include/app', +'include/core', +'include/utils', +'lib', +'scripts', +'src/app', +'src/core', +'src/utils', +'test',]; + +export const main = `#include + +int main(int argc, char *argv[]) { + printf("Hello, World from C!\\n"); + return 0; +}`; + +export const makefile = `# ================================ # Makefile for C project # Cross-platform compatibility # ================================ @@ -8,29 +26,29 @@ TARGET := my-c-program # Detect OS ifeq ($(OS),Windows_NT) - TARGET := $(TARGET).exe - DETECTED_OS := Windows + TARGET := $(TARGET).exe + DETECTED_OS := Windows else DETECTED_OS := Unix endif # Comandos de sistema según el entorno ifeq ($(DETECTED_OS),Windows) - SHELL = cmd.exe - MKDIR = if not exist "$(subst /,,$1)" mkdir "$(subst /,,$1)" - RMDIR = if exist "$(subst /,,$1)" rmdir /s /q "$(subst /,,$1)" - RM = if exist "$(subst /,,$1)" del /f /q + SHELL = cmd.exe + MKDIR = if not exist "$(subst /,,$1)" mkdir "$(subst /,,$1)" + RMDIR = if exist "$(subst /,,$1)" rmdir /s /q "$(subst /,,$1)" + RM = if exist "$(subst /,,$1)" del /f /q else - SHELL = /bin/sh - PRINTF = printf - MKDIR = mkdir -p $1 - RMDIR = rm -rf $1 - RM = rm -f $1 - - CYAN := \\033[36m - GREEN := \\033[32m - RED := \\033[31m - RESET := \\033[0m + SHELL = /bin/sh + PRINTF = printf + MKDIR = mkdir -p $1 + RMDIR = rm -rf $1 + RM = rm -f $1 + + CYAN := \\033[36m + GREEN := \\033[32m + RED := \\033[31m + RESET := \\033[0m endif # Compiler @@ -49,17 +67,22 @@ SRCS := $(wildcard $(SRC)/*.c) $(wildcard $(SRC)/**/*.c) HDRS := $(wildcard $(INCLUDE)/*.h) $(wildcard $(INCLUDE)/**/*.h) OBJS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SRCS)) -# Check if pkg-config is available -NULL_DEVICE := $(if $(ComSpec),NUL,/dev/null) +# Detect system and set NULL_DEVICE correctly +ifeq ($(OS),Windows_NT) + NULL_DEVICE := NUL +else + NULL_DEVICE := /dev/null +endif +# Configure PKG_CONFIG PKG_CONFIG := $(shell command -v pkg-config 2>$(NULL_DEVICE)) ifneq ($(PKG_CONFIG),) - PKG_FLAGS := $(shell pkg-config --cflags gtk4 2>$(NULL_DEVICE)) - PKG_LIBS := $(shell pkg-config --libs gtk4 2>$(NULL_DEVICE)) + PKG_FLAGS := $(shell pkg-config --cflags gtk4 2>$(NULL_DEVICE)) + PKG_LIBS := $(shell pkg-config --libs gtk4 2>$(NULL_DEVICE)) else - PKG_FLAGS := - PKG_LIBS := + PKG_FLAGS := + PKG_LIBS := endif # Find static libraries in lib/ @@ -92,7 +115,7 @@ ifeq ($(DETECTED_OS),Windows) @type nul > $(COMPILE_FLAG) else @if [ ! -f $(COMPILE_FLAG) ]; then \\ - $(PRINTF) "$(GREEN)Build complete!$(RESET)\\n"; \\ + $(PRINTF) "$(GREEN)Build complete!$(RESET)\\n"; \\ touch $(COMPILE_FLAG); \\ fi endif @@ -104,7 +127,7 @@ ifeq ($(DETECTED_OS),Windows) @if not exist $(COMPILE_FLAG) powershell -Command "Write-Host -NoNewline 'Compiling: ' -ForegroundColor Cyan; Write-Host '$<' -ForegroundColor Green" else @if [ ! -f $(COMPILE_FLAG) ]; then \\ - $(PRINTF) "$(CYAN)Compiling: $(GREEN)$<$(RESET)\\n"; \\ + $(PRINTF) "$(CYAN)Compiling: $(GREEN)$<$(RESET)\\n"; \\ fi endif @$(CC) $(CFLAGS) -c $< -o $@ @@ -133,11 +156,4 @@ ifeq ($(DETECTED_OS),Windows) @powershell -Command "Write-Host -NoNewline 'Clean completed! ' -ForegroundColor Green; Write-Host '' -ForegroundColor White" else @$(PRINTF) "$(GREEN)Clean completed!$(RESET)\\n"; -endif`; - -export const mainC = `#include - -int main(int argc, char *argv[]) { - printf("Hello, World from C!\\n"); - return 0; -}`; \ No newline at end of file +endif`; \ No newline at end of file diff --git a/src/templates/cpp.ts b/src/templates/cpp.ts index e28e7ca..37fe2a6 100755 --- a/src/templates/cpp.ts +++ b/src/templates/cpp.ts @@ -1,4 +1,24 @@ -export const makeCPP =`# ================================ +export const folders = [ +'include/app', +'include/core', +'include/utils', +'lib', +'scripts', +'src/app', +'src/core', +'src/utils', +'test',]; + +export const main =`#include + +using namespace std; + +int main(int argc, char *argv[]){ + cout << "Hello, World from C++!" << endl; + return 0; +}`; + +export const makefile =`# ================================ # Makefile for C++ project # Cross-platform compatibility # ================================ @@ -8,29 +28,29 @@ TARGET := my-cpp-program # Detect OS ifeq ($(OS),Windows_NT) - TARGET := $(TARGET).exe - DETECTED_OS := Windows + TARGET := $(TARGET).exe + DETECTED_OS := Windows else - DETECTED_OS := Unix + DETECTED_OS := Unix endif # Comandos de sistema según el entorno ifeq ($(DETECTED_OS),Windows) - SHELL = cmd.exe - MKDIR = if not exist "$(subst /,,$1)" mkdir "$(subst /,,$1)" - RMDIR = if exist "$(subst /,,$1)" rmdir /s /q "$(subst /,,$1)" - RM = if exist "$(subst /,,$1)" del /f /q + SHELL = cmd.exe + MKDIR = if not exist "$(subst /,,$1)" mkdir "$(subst /,,$1)" + RMDIR = if exist "$(subst /,,$1)" rmdir /s /q "$(subst /,,$1)" + RM = if exist "$(subst /,,$1)" del /f /q else - SHELL = /bin/sh - PRINTF = printf - MKDIR = mkdir -p $1 - RMDIR = rm -rf $1 - RM = rm -f $1 - - CYAN := \\033[36m - GREEN := \\033[32m - RED := \\033[31m - RESET := \\033[0m + SHELL = /bin/sh + PRINTF = printf + MKDIR = mkdir -p $1 + RMDIR = rm -rf $1 + RM = rm -f $1 + + CYAN := \\033[36m + GREEN := \\033[32m + RED := \\033[31m + RESET := \\033[0m endif # Compiler @@ -49,17 +69,22 @@ SRCS := $(wildcard $(SRC)/*.cpp) $(wildcard $(SRC)/**/*.cpp) HDRS := $(wildcard $(INCLUDE)/*.h) $(wildcard $(INCLUDE)/**/*.h) OBJS := $(patsubst $(SRC)/%.cpp, $(OBJ)/%.o, $(SRCS)) -# Check if pkg-config is available -NULL_DEVICE := $(if $(ComSpec),NUL,/dev/null) +# Detect system and set NULL_DEVICE correctly +ifeq ($(OS),Windows_NT) + NULL_DEVICE := NUL +else + NULL_DEVICE := /dev/null +endif +# Configure PKG_CONFIG PKG_CONFIG := $(shell command -v pkg-config 2>$(NULL_DEVICE)) ifneq ($(PKG_CONFIG),) - PKG_FLAGS := $(shell pkg-config --cflags gtkmm-4.0 2>$(NULL_DEVICE)) - PKG_LIBS := $(shell pkg-config --libs gtkmm-4.0 2>$(NULL_DEVICE)) + PKG_FLAGS := $(shell pkg-config --cflags gtkmm-4.0 2>$(NULL_DEVICE)) + PKG_LIBS := $(shell pkg-config --libs gtkmm-4.0 2>$(NULL_DEVICE)) else - PKG_FLAGS := - PKG_LIBS := + PKG_FLAGS := + PKG_LIBS := endif # Find static libraries in lib/ @@ -92,7 +117,7 @@ ifeq ($(DETECTED_OS),Windows) @type nul > $(COMPILE_FLAG) else @if [ ! -f $(COMPILE_FLAG) ]; then \\ - $(PRINTF) "$(GREEN)Build complete!$(RESET)\\n"; \\ + $(PRINTF) "$(GREEN)Build complete!$(RESET)\\n"; \\ touch $(COMPILE_FLAG); \\ fi endif @@ -104,7 +129,7 @@ ifeq ($(DETECTED_OS),Windows) @if not exist $(COMPILE_FLAG) powershell -Command "Write-Host -NoNewline 'Compiling: ' -ForegroundColor Cyan; Write-Host '$<' -ForegroundColor Green" else @if [ ! -f $(COMPILE_FLAG) ]; then \\ - $(PRINTF) "$(CYAN)Compiling: $(GREEN)$<$(RESET)\\n"; \\ + $(PRINTF) "$(CYAN)Compiling: $(GREEN)$<$(RESET)\\n"; \\ fi endif @$(CXX) $(CXXFLAGS) -c $< -o $@ @@ -133,14 +158,4 @@ ifeq ($(DETECTED_OS),Windows) @powershell -Command "Write-Host -NoNewline 'Clean completed! ' -ForegroundColor Green; Write-Host '' -ForegroundColor White" else @$(PRINTF) "$(GREEN)Clean completed!$(RESET)\\n"; -endif`; - -export const mainCPP =`#include - -using namespace std; - -int main(int argc, char *argv[]) -{ - cout << "Hello, World from C++!" << endl; - return 0; -}`; \ No newline at end of file +endif`; \ No newline at end of file diff --git a/src/templates/go.ts b/src/templates/go.ts index 1aab1ac..be8d32d 100755 --- a/src/templates/go.ts +++ b/src/templates/go.ts @@ -1,4 +1,23 @@ -export const makeGo = `# ================================ +export const folders = [ +'cmd/app', +'config', +'internal/app', +'internal/middleware', +'internal/database', +'internal/routes', +'pkg/utils', +'scripts', +'test',]; + +export const main = `package main + +import "fmt" + +func main() { + fmt.Println("Hello, World from Golang!") +}`; + +export const makefile = `# ================================ # Makefile for Golang project # Cross-platform compatibility # ================================ @@ -8,34 +27,40 @@ TARGET := my-go-program # Detect OS ifeq ($(OS),Windows_NT) - TARGET := $(TARGET).exe - DETECTED_OS := Windows + TARGET := $(TARGET).exe + DETECTED_OS := Windows else - DETECTED_OS := Unix + DETECTED_OS := Unix endif # System commands ifeq ($(DETECTED_OS),Windows) - SHELL = cmd.exe - MKDIR = if not exist "$(subst /,,$1)" mkdir "$(subst /,,$1)" - RMDIR = if exist "$(subst /,,$1)" rmdir /s /q "$(subst /,,$1)" - RM = if exist "$(subst /,,$1)" del /f /q + SHELL = cmd.exe + MKDIR = if not exist "$(subst /,,$1)" mkdir "$(subst /,,$1)" + RMDIR = if exist "$(subst /,,$1)" rmdir /s /q "$(subst /,,$1)" + RM = if exist "$(subst /,,$1)" del /f /q else - SHELL = /bin/sh - MKDIR = mkdir -p $1 - RMDIR = rm -rf $1 - RM = rm -f $1 + SHELL = /bin/sh + MKDIR = mkdir -p $1 + RMDIR = rm -rf $1 + RM = rm -f $1 - CYAN := \\033[36m - GREEN := \\033[32m - RED := \\033[31m - RESET := \\033[0m + CYAN := \\033[36m + GREEN := \\033[32m + RED := \\033[31m + RESET := \\033[0m endif # Directories +CMD_DIR := cmd/app BIN := build COMPILE_FLAG := $(BIN)/.compiled +# Commands +BUILD_CMD := go build -o $(BIN)/$(TARGET) ./$(CMD_DIR) +RUN_CMD := $(BIN)/$(TARGET) +CLEAN_CMD := $(call RMDIR,$(BIN)) + # Rule to build the Go project .PHONY: build build: @@ -47,13 +72,13 @@ else printf "$(CYAN)Building Go program...$(RESET)\\n"; \\ fi endif - @go build -o $(BIN)/$(TARGET) . + @$(BUILD_CMD) ifeq ($(DETECTED_OS),Windows) @if not exist $(COMPILE_FLAG) powershell -Command "Write-Host -NoNewline 'Build complete! ' -ForegroundColor Green; Write-Host '' -ForegroundColor White" @type nul > $(COMPILE_FLAG) else @if [ ! -f $(COMPILE_FLAG) ]; then \\ - printf "$(GREEN)Build complete!$(RESET)\\n"; \\ + printf "$(GREEN)Build complete!$(RESET)\\n"; \\ touch $(COMPILE_FLAG); \\ fi endif @@ -62,11 +87,11 @@ endif .PHONY: run run: build ifeq ($(DETECTED_OS),Windows) - @powershell -Command "Write-Host -NoNewline 'Running: ' -ForegroundColor Cyan; Write-Host '$(BIN)/$(TARGET)' -ForegroundColor Green" + @powershell -Command "Write-Host -NoNewline 'Running: ' -ForegroundColor Cyan; Write-Host '$(RUN_CMD)' -ForegroundColor Green" else - @printf "$(CYAN)Running: $(GREEN)$(BIN)/$(TARGET)$(RESET)\\n"; + @printf "$(CYAN)Running: $(GREEN)$(RUN_CMD)$(RESET)\\n"; endif - @$(BIN)/$(TARGET) + @$(RUN_CMD) # Rule to clean compiled files .PHONY: clean @@ -76,17 +101,9 @@ ifeq ($(DETECTED_OS),Windows) else @printf "$(RED)Cleaning build files...$(RESET)\\n"; endif - @$(call RMDIR,$(BIN)) + @$(CLEAN_CMD) ifeq ($(DETECTED_OS),Windows) @powershell -Command "Write-Host -NoNewline 'Clean completed! ' -ForegroundColor Green; Write-Host '' -ForegroundColor White" else @printf "$(GREEN)Clean completed!$(RESET)\\n"; -endif`; - -export const mainGo = `package main - -import "fmt" - -func main() { - fmt.Println("Hello, World from Golang!") -}`; \ No newline at end of file +endif`; \ No newline at end of file diff --git a/src/templates/java.ts b/src/templates/java.ts index 6c4f854..8831cce 100755 --- a/src/templates/java.ts +++ b/src/templates/java.ts @@ -1,40 +1,54 @@ -export const makeJava = `# ================================ +export const folders = [ +'src/main/java/com/example', +'src/main/resources', +'src/test/java/com/example', +'lib', +'scripts',]; + +export const main = `package com.example; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello, World from Java!"); + } +}`; + +export const makefile = `# ================================ # Makefile for Java project # Cross-platform compatibility # ================================ -# Main class (cambiar según el nombre real del archivo con main()) +# change according to the name of the file to be executed MAIN_CLASS := com.example.Main # Detect OS ifeq ($(OS),Windows_NT) - TARGET := $(TARGET).exe - DETECTED_OS := Windows + DETECTED_OS := Windows else - DETECTED_OS := Unix + DETECTED_OS := Unix endif # Detect OS and set correct file extensions and commands ifeq ($(OS),Windows_NT) - SHELL = cmd.exe - MKDIR = if not exist "$(subst /,,$1)" mkdir "$(subst /,,$1)" - RMDIR = if exist "$(subst /,,$1)" rmdir /s /q "$(subst /,,$1)" - RM = if exist "$(subst /,,$1)" del /f /q - FIND = cmd /c "for /r src %%i in (*.java) do @echo %%~fi" - CLASSPATH_SEP = ; + SHELL = cmd.exe + MKDIR = if not exist "$(subst /,,$1)" mkdir "$(subst /,,$1)" + RMDIR = if exist "$(subst /,,$1)" rmdir /s /q "$(subst /,,$1)" + RM = if exist "$(subst /,,$1)" del /f /q + FIND = cmd /c "for /r src %%i in (*.java) do @echo %%~fi" + CLASSPATH_SEP = ; else - PRINTF = printf - SHELL = /bin/sh - MKDIR = mkdir -p $1 - RMDIR = rm -rf $1 - RM = rm -f $1 - FIND = find src -name "*.java" - CLASSPATH_SEP = : + PRINTF = printf + SHELL = /bin/sh + MKDIR = mkdir -p $1 + RMDIR = rm -rf $1 + RM = rm -f $1 + FIND = find src -name "*.java" + CLASSPATH_SEP = : RED := \\033[31m - GREEN := \\033[32m - CYAN := \\033[36m - RESET := \\033[0m + GREEN := \\033[32m + CYAN := \\033[36m + RESET := \\033[0m endif # Compiler and runner @@ -101,12 +115,4 @@ ifeq ($(DETECTED_OS),Windows) @powershell -Command "Write-Host -NoNewline 'Clean completed. ' -ForegroundColor Green; Write-Host '' -ForegroundColor White" else @$(PRINTF) "$(GREEN)Clean completed.$(RESET)\\n" -endif`; - -export const mainJava = `package com.example; - -public class Main { - public static void main(String[] args) { - System.out.println("Hello, World from Java!"); - } -}`; \ No newline at end of file +endif`; \ No newline at end of file diff --git a/src/utils/types.ts b/src/utils/types.ts new file mode 100755 index 0000000..4b34425 --- /dev/null +++ b/src/utils/types.ts @@ -0,0 +1,18 @@ +import * as vscode from 'vscode'; + +export type Language = 'c' | 'cpp' | 'go' | 'java'; + +export interface StatusBarItems { + create: vscode.StatusBarItem; + start: vscode.StatusBarItem; + stop: vscode.StatusBarItem; +} + +export interface LanguageConfig { + folders: string[]; + mainFilePath: string; + mainFileContent: string; + makefileContent: string; + gitignore: string; + readme: string +} \ No newline at end of file