Wiring file watch events WIP

This commit is contained in:
itdominator 2025-06-20 00:50:43 -05:00
parent 3a29e0dcad
commit 41f6ea5854
9 changed files with 119 additions and 25 deletions

View File

@ -5,13 +5,14 @@ const os = require('os');
const chokidar = require('chokidar'); const chokidar = require('chokidar');
const HOME_DIR = os.homedir(); const HOME_DIR = os.homedir();
const BASE_PATH = '../build/app'; const BASE_PATH = '../build/app';
const CONFIG_PATH = path.join(HOME_DIR, "/.config/newton/"); const CONFIG_PATH = path.join(HOME_DIR, "/.config/newton/");
const SETTINGS_CONFIG_PATH = path.join(CONFIG_PATH, "/settings.json"); const SETTINGS_CONFIG_PATH = path.join(CONFIG_PATH, "/settings.json");
const LSP_CONFIG_PATH = path.join(BASE_PATH, "/resources/lsp-servers-config.json"); const LSP_CONFIG_PATH = path.join(BASE_PATH, "/resources/lsp-servers-config.json");
let window = null; let window = null;
let watcher = null; let watcher = null;
let skipWatchChangeUpdateOnce = false;
@ -63,21 +64,39 @@ const saveSettingsConfigData = (data) => {
} }
const saveFile = (fpath, content) => { const saveFile = (fpath, content) => {
skipWatchChangeUpdateOnce = true;
fs.writeFile(fpath, content, (err) => { fs.writeFile(fpath, content, (err) => {
if (!err) return if (err) {
console.error("An error ocurred writing to the file " + err.message); 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) => { const saveFileAs = (content) => {
dialog.showSaveDialog().then((response) => { dialog.showSaveDialog().then((response) => {
if (response.canceled) { if (response.canceled) {
console.log("You didn't save the file"); console.debug("You didn't save the file");
return; return;
} }
saveFile(response.filePath, content); saveFile(response.filePath, content);
watcher.add(response.filePath);
}); });
} }
@ -111,7 +130,7 @@ const openFiles = (startPath) => {
} }
).then((response) => { ).then((response) => {
if (response.canceled) { if (response.canceled) {
console.log("Canceled file(s) open request..."); console.debug("Canceled file(s) open request...");
return; return;
} }
@ -124,6 +143,11 @@ const loadFilesWatcher = () => {
watcher = chokidar.watch([], {}); watcher = chokidar.watch([], {});
watcher.on('change', (fpath) => { watcher.on('change', (fpath) => {
if (skipWatchChangeUpdateOnce) {
skipWatchChangeUpdateOnce = false;
return;
}
console.debug("File (changed) : ", fpath); console.debug("File (changed) : ", fpath);
window.webContents.send('file-changed', fpath); window.webContents.send('file-changed', fpath);
}).on('unlink', (fpath) => { }).on('unlink', (fpath) => {
@ -133,7 +157,7 @@ const loadFilesWatcher = () => {
} }
const unwatchFile = async (fpath) => { const unwatchFile = async (fpath) => {
console.log("File (unwatch) : ", fpath); console.debug("File (unwatch) : ", fpath);
await watcher.unwatch(fpath); await watcher.unwatch(fpath);
} }

View File

@ -22,6 +22,8 @@ contextBridge.exposeInMainWorld('fs', {
closeFile: (path) => ipcRenderer.invoke("closeFile", path), closeFile: (path) => ipcRenderer.invoke("closeFile", path),
getPathForFile: (file) => webUtils.getPathForFile(file), getPathForFile: (file) => webUtils.getPathForFile(file),
onLoadFiles: (callback) => ipcRenderer.on('load-files', (_event, paths) => callback(paths)), 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)), onChangedFile: (callback) => ipcRenderer.on('file-changed', (_event, path) => callback(path)),
onDeletedFile: (callback) => ipcRenderer.on('file-deleted', (_event, path) => callback(path)), onDeletedFile: (callback) => ipcRenderer.on('file-deleted', (_event, path) => callback(path)),
}); });

View File

@ -1,5 +1,5 @@
import { ComponentRef, Injectable } from '@angular/core'; 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"; import { NewtonEditorComponent } from "../../../editor/newton-editor/newton-editor.component";

View File

@ -1,4 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs';
import { EditSession } from 'ace-builds'; import { EditSession } from 'ace-builds';
import { getModeForPath } from 'ace-builds/src-noconflict/ext-modelist'; import { getModeForPath } from 'ace-builds/src-noconflict/ext-modelist';
@ -14,6 +15,8 @@ import { ServiceMessage } from '../../types/service-message.type';
providedIn: 'root' providedIn: 'root'
}) })
export class FilesService { export class FilesService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
files: Map<string, NewtonFile>; files: Map<string, NewtonFile>;
@ -38,7 +41,13 @@ export class FilesService {
this.files.set(file.path, file); this.files.set(file.path, file);
} }
sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data);
}
getMessage$(): Observable<ServiceMessage> {
return this.messageSubject.asObservable();
}
async loadFilesList(files: Array<NewtonFile>): Promise<NewtonFile | undefined | null> { async loadFilesList(files: Array<NewtonFile>): Promise<NewtonFile | undefined | null> {
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {

View File

@ -4,6 +4,7 @@ import { Subject, takeUntil } from 'rxjs';
import { NewtonEditorComponent } from "./newton-editor/newton-editor.component"; import { NewtonEditorComponent } from "./newton-editor/newton-editor.component";
import { FilesModalComponent } from "./modals/files-modal.component"; import { FilesModalComponent } from "./modals/files-modal.component";
import { EditorsService } from '../common/services/editor/editors.service'; 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 { FilesService } from '../common/services/editor/files.service';
import { DndDirective } from '../common/directives/dnd.directive'; import { DndDirective } from '../common/directives/dnd.directive';
@ -34,6 +35,7 @@ export class EditorsComponent {
constructor( constructor(
private editorsService: EditorsService, private editorsService: EditorsService,
private tabsService: TabsService,
private filesService: FilesService private filesService: FilesService
) { ) {
} }
@ -57,17 +59,17 @@ export class EditorsComponent {
this.editorsService.getMessage$().pipe( this.editorsService.getMessage$().pipe(
takeUntil(this.unsubscribe) takeUntil(this.unsubscribe)
).subscribe((message: ServiceMessage) => { ).subscribe((message: ServiceMessage) => {
if (message.action == "select-left-editor") { if (message.action === "select-left-editor") {
let editorComponent = this.editorsService.get(message.editorUUID); let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.leftSiblingUUID) return; if (!editorComponent.leftSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID); let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID);
siblingComponent.editor.focus() siblingComponent.editor.focus()
} else if (message.action == "select-right-editor") { } else if (message.action === "select-right-editor") {
let editorComponent = this.editorsService.get(message.editorUUID); let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.rightSiblingUUID) return; if (!editorComponent.rightSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID); let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID);
siblingComponent.editor.focus() siblingComponent.editor.focus()
} else if (message.action == "move-session-left") { } else if (message.action === "move-session-left") {
let editorComponent = this.editorsService.get(message.editorUUID); let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.leftSiblingUUID) return; if (!editorComponent.leftSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID); let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID);
@ -78,7 +80,7 @@ export class EditorsComponent {
siblingComponent.editor.setSession(session); siblingComponent.editor.setSession(session);
editorComponent.newBuffer(); editorComponent.newBuffer();
siblingComponent.editor.focus() siblingComponent.editor.focus()
} else if (message.action == "move-session-right") { } else if (message.action === "move-session-right") {
let editorComponent = this.editorsService.get(message.editorUUID); let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.rightSiblingUUID) return; if (!editorComponent.rightSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID); let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID);
@ -89,18 +91,18 @@ export class EditorsComponent {
siblingComponent.editor.setSession(session); siblingComponent.editor.setSession(session);
editorComponent.newBuffer(); editorComponent.newBuffer();
siblingComponent.editor.focus() siblingComponent.editor.focus()
} else if (message.action == "set-active-editor") { } else if (message.action === "set-active-editor") {
this.editorsService.get(this.activeEditor).removeActiveStyling(); this.editorsService.get(this.activeEditor).removeActiveStyling();
this.activeEditor = message.editorUUID; this.activeEditor = message.editorUUID;
this.editorsService.get(this.activeEditor).addActiveStyling(); 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 file = this.filesService.get(message.filePath);
let editorComponent = this.getActiveEditorComponent(); let editorComponent = this.getActiveEditorComponent();
let editor = editorComponent.editor; let editor = editorComponent.editor;
editorComponent.activeFile = file; editorComponent.activeFile = file;
editor.setSession(file.session); editor.setSession(file.session);
} else if (message.action == "close-tab") { } else if (message.action === "close-tab") {
let file = this.filesService.get(message.filePath); let file = this.filesService.get(message.filePath);
let editors = this.editorsService.getEditorsAsArray(); let editors = this.editorsService.getEditorsAsArray();
@ -135,11 +137,33 @@ export class EditorsComponent {
}); });
window.fs.onChangedFile(async (path: string) => { 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) => { 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); console.log(path);
// this.tabsService.sendMessage(message);
// this.filesService.sendMessage(message);
}); });
window.main.onMenuActions(async (action: string) => { window.main.onMenuActions(async (action: string) => {

View File

@ -12,6 +12,7 @@ import "ace-builds/src-noconflict/theme-dracula";
import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.service'; import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.service';
import { FilesModalService } from '../../common/services/editor/modals/files-modal.service'; import { FilesModalService } from '../../common/services/editor/modals/files-modal.service';
import { LSPService } from '../../common/services/lsp.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 { EditorsService } from '../../common/services/editor/editors.service';
import { NewtonEditorBase } from './newton-editor.base'; import { NewtonEditorBase } from './newton-editor.base';
@ -38,6 +39,7 @@ export class NewtonEditorComponent extends NewtonEditorBase {
private infoBarService: InfoBarService, private infoBarService: InfoBarService,
private editorsService: EditorsService, private editorsService: EditorsService,
private lspService: LSPService, private lspService: LSPService,
private tabsService: TabsService,
private filesModalService: FilesModalService private filesModalService: FilesModalService
) { ) {
super(); super();
@ -124,6 +126,13 @@ export class NewtonEditorComponent extends NewtonEditorBase {
this.editorsService.sendMessage(message); 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.editor.on("changeSession", (session) => {
this.lspService.registerEditor(this.editor); this.lspService.registerEditor(this.editor);
this.updateInfoBar(); this.updateInfoBar();

View File

@ -19,13 +19,25 @@
border-right-style: solid; border-right-style: solid;
border-right-color: #ffffff64; border-right-color: #ffffff64;
border-right-width: 2px; border-right-width: 2px;
}
.active-tab {
background-color: rgba(255, 255, 255, 0.46);
color: rgba(255, 255, 255, 0.8);
} }
.tab:hover { .tab:hover {
cursor: pointer; cursor: pointer;
} }
.file-changed {
color: rgba(255, 168, 0, 0.64);
}
.file-deleted {
color: rgba(255, 0, 0, 0.64);
}
.title { .title {
margin-left: 2em; margin-left: 2em;
margin-right: 2em; margin-right: 2em;

View File

@ -42,9 +42,21 @@ export class TabsComponent {
public ngAfterViewInit(): void { public ngAfterViewInit(): void {
this.tabsService.getMessage$().pipe( this.tabsService.getMessage$().pipe(
takeUntil(this.unsubscribe) takeUntil(this.unsubscribe)
).subscribe((data: ServiceMessage) => { ).subscribe((message: ServiceMessage) => {
if (data.action === "create-tab") { if (message.action === "create-tab") {
this.createTab(data.fileName, data.fileUUID, data.filePath); 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");
} }
}); });
} }

View File

@ -32,6 +32,8 @@ declare global {
closeFile: (arg0: any) => Promise<string>, closeFile: (arg0: any) => Promise<string>,
getPathForFile: any, getPathForFile: any,
onLoadFiles: (arg0: any) => Promise<string>, onLoadFiles: (arg0: any) => Promise<string>,
onUpdateFilePath: (arg0: any) => Promise<string>,
onSavedFile: (arg0: any) => Promise<string>,
onChangedFile: (arg0: any) => Promise<string>, onChangedFile: (arg0: any) => Promise<string>,
onDeletedFile: (arg0: any) => Promise<string>, onDeletedFile: (arg0: any) => Promise<string>,
} }