diff --git a/newton/fs.js b/newton/fs.js index ba635a3..e704cb0 100644 --- a/newton/fs.js +++ b/newton/fs.js @@ -5,13 +5,14 @@ const os = require('os'); const chokidar = require('chokidar'); -const HOME_DIR = os.homedir(); -const BASE_PATH = '../build/app'; -const CONFIG_PATH = path.join(HOME_DIR, "/.config/newton/"); -const SETTINGS_CONFIG_PATH = path.join(CONFIG_PATH, "/settings.json"); -const LSP_CONFIG_PATH = path.join(BASE_PATH, "/resources/lsp-servers-config.json"); -let window = null; -let watcher = null; +const HOME_DIR = os.homedir(); +const BASE_PATH = '../build/app'; +const CONFIG_PATH = path.join(HOME_DIR, "/.config/newton/"); +const SETTINGS_CONFIG_PATH = path.join(CONFIG_PATH, "/settings.json"); +const LSP_CONFIG_PATH = path.join(BASE_PATH, "/resources/lsp-servers-config.json"); +let window = null; +let watcher = null; +let skipWatchChangeUpdateOnce = false; @@ -63,21 +64,39 @@ const saveSettingsConfigData = (data) => { } const saveFile = (fpath, content) => { + skipWatchChangeUpdateOnce = true; + fs.writeFile(fpath, content, (err) => { - if (!err) return - console.error("An error ocurred writing to the file " + err.message); + if (err) { + console.error("An error ocurred writing to the file " + err.message); + return; + } + + let parentDir = path.dirname(fpath); + let watchers = watcher.getWatched(); + let targetDir = watchers[parentDir]; + if ( + targetDir && !targetDir.includes( path.basename(fpath) ) + ) { + skipWatchChangeUpdateOnce = false; + watcher.add(fpath); + window.webContents.send('update-file-path', fpath); + } + + try { + window.webContents.send('file-saved', fpath); + } catch(e) {} }); } const saveFileAs = (content) => { dialog.showSaveDialog().then((response) => { if (response.canceled) { - console.log("You didn't save the file"); + console.debug("You didn't save the file"); return; } saveFile(response.filePath, content); - watcher.add(response.filePath); }); } @@ -111,7 +130,7 @@ const openFiles = (startPath) => { } ).then((response) => { if (response.canceled) { - console.log("Canceled file(s) open request..."); + console.debug("Canceled file(s) open request..."); return; } @@ -124,6 +143,11 @@ const loadFilesWatcher = () => { watcher = chokidar.watch([], {}); watcher.on('change', (fpath) => { + if (skipWatchChangeUpdateOnce) { + skipWatchChangeUpdateOnce = false; + return; + } + console.debug("File (changed) : ", fpath); window.webContents.send('file-changed', fpath); }).on('unlink', (fpath) => { @@ -133,7 +157,7 @@ const loadFilesWatcher = () => { } const unwatchFile = async (fpath) => { - console.log("File (unwatch) : ", fpath); + console.debug("File (unwatch) : ", fpath); await watcher.unwatch(fpath); } diff --git a/newton/preload.js b/newton/preload.js index 5646dfe..5af2649 100644 --- a/newton/preload.js +++ b/newton/preload.js @@ -22,6 +22,8 @@ contextBridge.exposeInMainWorld('fs', { closeFile: (path) => ipcRenderer.invoke("closeFile", path), getPathForFile: (file) => webUtils.getPathForFile(file), onLoadFiles: (callback) => ipcRenderer.on('load-files', (_event, paths) => callback(paths)), + onUpdateFilePath: (callback) => ipcRenderer.on('update-file-path', (_event, paths) => callback(paths)), + onSavedFile: (callback) => ipcRenderer.on('file-saved', (_event, path) => callback(path)), onChangedFile: (callback) => ipcRenderer.on('file-changed', (_event, path) => callback(path)), onDeletedFile: (callback) => ipcRenderer.on('file-deleted', (_event, path) => callback(path)), }); \ No newline at end of file diff --git a/src/app/common/services/editor/editors.service.ts b/src/app/common/services/editor/editors.service.ts index 22f398e..e84728b 100644 --- a/src/app/common/services/editor/editors.service.ts +++ b/src/app/common/services/editor/editors.service.ts @@ -1,5 +1,5 @@ import { ComponentRef, Injectable } from '@angular/core'; -import { BehaviorSubject, ReplaySubject, Observable } from 'rxjs'; +import { ReplaySubject, Observable } from 'rxjs'; import { NewtonEditorComponent } from "../../../editor/newton-editor/newton-editor.component"; diff --git a/src/app/common/services/editor/files.service.ts b/src/app/common/services/editor/files.service.ts index 6af20f9..f16d6a2 100644 --- a/src/app/common/services/editor/files.service.ts +++ b/src/app/common/services/editor/files.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import { ReplaySubject, Observable } from 'rxjs'; import { EditSession } from 'ace-builds'; import { getModeForPath } from 'ace-builds/src-noconflict/ext-modelist'; @@ -14,6 +15,8 @@ import { ServiceMessage } from '../../types/service-message.type'; providedIn: 'root' }) export class FilesService { + private messageSubject: ReplaySubject = new ReplaySubject(1); + files: Map; @@ -38,7 +41,13 @@ export class FilesService { this.files.set(file.path, file); } + sendMessage(data: ServiceMessage): void { + this.messageSubject.next(data); + } + getMessage$(): Observable { + return this.messageSubject.asObservable(); + } async loadFilesList(files: Array): Promise { for (let i = 0; i < files.length; i++) { diff --git a/src/app/editor/editors.component.ts b/src/app/editor/editors.component.ts index 55cac4f..66f7cb7 100644 --- a/src/app/editor/editors.component.ts +++ b/src/app/editor/editors.component.ts @@ -4,6 +4,7 @@ import { Subject, takeUntil } from 'rxjs'; import { NewtonEditorComponent } from "./newton-editor/newton-editor.component"; import { FilesModalComponent } from "./modals/files-modal.component"; import { EditorsService } from '../common/services/editor/editors.service'; +import { TabsService } from '../common/services/editor/tabs/tabs.service'; import { FilesService } from '../common/services/editor/files.service'; import { DndDirective } from '../common/directives/dnd.directive'; @@ -34,6 +35,7 @@ export class EditorsComponent { constructor( private editorsService: EditorsService, + private tabsService: TabsService, private filesService: FilesService ) { } @@ -57,17 +59,17 @@ export class EditorsComponent { this.editorsService.getMessage$().pipe( takeUntil(this.unsubscribe) ).subscribe((message: ServiceMessage) => { - if (message.action == "select-left-editor") { + if (message.action === "select-left-editor") { let editorComponent = this.editorsService.get(message.editorUUID); if (!editorComponent.leftSiblingUUID) return; let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID); siblingComponent.editor.focus() - } else if (message.action == "select-right-editor") { + } else if (message.action === "select-right-editor") { let editorComponent = this.editorsService.get(message.editorUUID); if (!editorComponent.rightSiblingUUID) return; let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID); siblingComponent.editor.focus() - } else if (message.action == "move-session-left") { + } else if (message.action === "move-session-left") { let editorComponent = this.editorsService.get(message.editorUUID); if (!editorComponent.leftSiblingUUID) return; let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID); @@ -78,7 +80,7 @@ export class EditorsComponent { siblingComponent.editor.setSession(session); editorComponent.newBuffer(); siblingComponent.editor.focus() - } else if (message.action == "move-session-right") { + } else if (message.action === "move-session-right") { let editorComponent = this.editorsService.get(message.editorUUID); if (!editorComponent.rightSiblingUUID) return; let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID); @@ -89,18 +91,18 @@ export class EditorsComponent { siblingComponent.editor.setSession(session); editorComponent.newBuffer(); siblingComponent.editor.focus() - } else if (message.action == "set-active-editor") { + } else if (message.action === "set-active-editor") { this.editorsService.get(this.activeEditor).removeActiveStyling(); this.activeEditor = message.editorUUID; this.editorsService.get(this.activeEditor).addActiveStyling(); - } else if (message.action == "set-tab-to-editor") { + } else if (message.action === "set-tab-to-editor") { let file = this.filesService.get(message.filePath); let editorComponent = this.getActiveEditorComponent(); let editor = editorComponent.editor; editorComponent.activeFile = file; editor.setSession(file.session); - } else if (message.action == "close-tab") { + } else if (message.action === "close-tab") { let file = this.filesService.get(message.filePath); let editors = this.editorsService.getEditorsAsArray(); @@ -135,11 +137,33 @@ export class EditorsComponent { }); window.fs.onChangedFile(async (path: string) => { - console.log(path); + let message = new ServiceMessage(); + message.action = "file-changed"; + message.filePath = path; + this.tabsService.sendMessage(message); }); window.fs.onDeletedFile(async (path: string) => { + let message = new ServiceMessage(); + message.action = "file-deleted"; + message.filePath = path; + + this.tabsService.sendMessage(message); + this.filesService.sendMessage(message); + }); + + window.fs.onSavedFile(async (path: string) => { + let message = new ServiceMessage(); + message.action = "file-saved"; + message.filePath = path; + + this.tabsService.sendMessage(message); + }); + + window.fs.onUpdateFilePath(async (path: string) => { console.log(path); + // this.tabsService.sendMessage(message); + // this.filesService.sendMessage(message); }); window.main.onMenuActions(async (action: string) => { diff --git a/src/app/editor/newton-editor/newton-editor.component.ts b/src/app/editor/newton-editor/newton-editor.component.ts index d7505a4..04803a2 100644 --- a/src/app/editor/newton-editor/newton-editor.component.ts +++ b/src/app/editor/newton-editor/newton-editor.component.ts @@ -12,6 +12,7 @@ import "ace-builds/src-noconflict/theme-dracula"; import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.service'; import { FilesModalService } from '../../common/services/editor/modals/files-modal.service'; import { LSPService } from '../../common/services/lsp.service'; +import { TabsService } from '../../common/services/editor/tabs/tabs.service'; import { EditorsService } from '../../common/services/editor/editors.service'; import { NewtonEditorBase } from './newton-editor.base'; @@ -38,6 +39,7 @@ export class NewtonEditorComponent extends NewtonEditorBase { private infoBarService: InfoBarService, private editorsService: EditorsService, private lspService: LSPService, + private tabsService: TabsService, private filesModalService: FilesModalService ) { super(); @@ -124,6 +126,13 @@ export class NewtonEditorComponent extends NewtonEditorBase { this.editorsService.sendMessage(message); }); + this.editor.on("change", () => { + let message = new ServiceMessage(); + message.action = "file-changed"; + message.filePath = this.activeFile.path; + this.tabsService.sendMessage(message); + }); + this.editor.on("changeSession", (session) => { this.lspService.registerEditor(this.editor); this.updateInfoBar(); diff --git a/src/app/editor/tabs/tabs.component.css b/src/app/editor/tabs/tabs.component.css index 3a10c2a..99bd6d0 100644 --- a/src/app/editor/tabs/tabs.component.css +++ b/src/app/editor/tabs/tabs.component.css @@ -19,13 +19,25 @@ border-right-style: solid; border-right-color: #ffffff64; border-right-width: 2px; +} +.active-tab { + background-color: rgba(255, 255, 255, 0.46); + color: rgba(255, 255, 255, 0.8); } .tab:hover { cursor: pointer; } +.file-changed { + color: rgba(255, 168, 0, 0.64); +} + +.file-deleted { + color: rgba(255, 0, 0, 0.64); +} + .title { margin-left: 2em; margin-right: 2em; diff --git a/src/app/editor/tabs/tabs.component.ts b/src/app/editor/tabs/tabs.component.ts index 9cf8f51..3a41154 100644 --- a/src/app/editor/tabs/tabs.component.ts +++ b/src/app/editor/tabs/tabs.component.ts @@ -42,9 +42,21 @@ export class TabsComponent { public ngAfterViewInit(): void { this.tabsService.getMessage$().pipe( takeUntil(this.unsubscribe) - ).subscribe((data: ServiceMessage) => { - if (data.action === "create-tab") { - this.createTab(data.fileName, data.fileUUID, data.filePath); + ).subscribe((message: ServiceMessage) => { + if (message.action === "create-tab") { + this.createTab(message.fileName, message.fileUUID, message.filePath); + } else if (message.action === "file-changed") { + let elm = document.querySelectorAll(`[title="${message.filePath}"]`)[1]; + elm.classList.add("file-changed"); + elm.classList.remove("file-deleted"); + } else if (message.action === "file-deleted") { + let elm = document.querySelectorAll(`[title="${message.filePath}"]`)[1]; + elm.classList.add("file-deleted"); + elm.classList.remove("file-changed"); + } else if (message.action === "file-saved") { + let elm = document.querySelectorAll(`[title="${message.filePath}"]`)[1]; + elm.classList.remove("file-deleted"); + elm.classList.remove("file-changed"); } }); } diff --git a/src/polyfills.ts b/src/polyfills.ts index 2ee3027..ffeaf57 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -32,6 +32,8 @@ declare global { closeFile: (arg0: any) => Promise, getPathForFile: any, onLoadFiles: (arg0: any) => Promise, + onUpdateFilePath: (arg0: any) => Promise, + onSavedFile: (arg0: any) => Promise, onChangedFile: (arg0: any) => Promise, onDeletedFile: (arg0: any) => Promise, }