37 Commits

Author SHA1 Message Date
9aae679d21 Adding images to README file 2026-03-21 15:41:33 -05:00
d179e1c42e WIP to handle outside of electron build 2025-12-10 06:56:24 +00:00
da4e87d3cd WIP new websocket setup; build setup changes; types cleanup 2025-12-10 04:37:26 +00:00
f1e3557db1 Correcting angular.json file styles field 2025-12-02 20:26:23 -06:00
dd969302cf Upgrading glob and rimraf; downgrading electron-builder; reverting font additions 2025-12-02 20:23:36 -06:00
21cf5c794a Adding fonts 2025-12-02 18:11:29 -06:00
a4765952bf Adding pre-dynamic load of css and js 2025-11-27 23:01:55 -06:00
de5184ce22 Added show and focus on ipc load-file 2025-11-23 16:11:04 -06:00
de0802cb2c removing WIP terminal 2025-11-23 15:49:48 -06:00
c4ab66141c package.json dependency movement to dev deps; updated build field too 2025-11-23 15:11:21 -06:00
6bedb69909 Improved search-replace component logic by disabing buttons when no query; displaying found/not found status; displaying too long query; adding ctrl-up and down key bindig on query field to god prev-next instance found 2025-11-23 01:13:58 -06:00
8b47ff3919 Fixing file edit status; modified build options 2025-11-17 19:10:09 -06:00
fd40340d97 Removed logging entries; added tab bottom border 2025-11-10 22:20:05 -06:00
ea134caf2b Added tab highlighting and scroll into view logic 2025-11-08 17:24:40 -06:00
88b38370e1 added context menu for lsp modal field; cleaned up context menu css locations 2025-11-08 13:32:23 -06:00
ae6c21f8ad Added right click options to tabs bar 2025-11-08 01:20:38 -06:00
f94a8ca26c Moving to 'marked' for markdown parsing; added reference dependencies for future consideration 2025-10-07 19:29:55 -05:00
ed89f34d40 WIP terminal integration 2025-09-04 00:26:22 -05:00
73f25aae1c Cleaning up type asignment 2025-08-23 14:58:23 -05:00
3c3a5d2f50 Removed empty constructors 2025-08-23 14:20:37 -05:00
d44e7d4e51 Moved loading of subscribers to constructors; improved subscriber destruction pattern; code quality improvements 2025-08-23 14:07:19 -05:00
60289953ec Update ace-linters; WIP color tokenizer 2025-08-11 19:22:06 -05:00
df5f2d481a Various LSP or LSP tangental changes 2025-08-09 15:57:07 -05:00
2ede33f3c2 Setting context menu bg color; aligning minimap scroll styling; adding wrap mode for minimap to remove hscroll bar 2025-07-26 01:28:03 -05:00
822d778008 WIP context menu added; upgraded ace-linters; integrated auto session register 2025-07-26 00:43:45 -05:00
90c8c9b3ee Wiring lsp config editor to update lspManagerService.lspConfigDataStr; removed redundant check; update lsp config 2025-07-19 02:13:52 -05:00
79adb86d25 Fixed editors.component close-tab when 1 file remains 2025-07-16 23:39:00 -05:00
a0914e8096 Refactored editors.component and tabs.service 2025-07-15 23:11:37 -05:00
a4d602f98a Added mouse scroll zoom 2025-07-15 21:21:14 -05:00
d6f766753c Update close file logic to select prior if exists; bug fix in LSP regarding editor and assignment 2025-07-15 19:46:17 -05:00
ae60905eb4 Adding quit to menus 2025-07-14 23:42:51 -05:00
080cc22841 Making IPC port kwarg configurable; improving kwarhs filter out 2025-07-14 23:00:36 -05:00
c5fdab59f6 Bumping ace-linters to fix desync issue 2025-07-14 21:33:20 -05:00
ab17e48338 Changing cursor color and blink settings 2025-07-13 20:55:17 -05:00
ec841445ed WIP adding tree-sitter and language bindings 2025-07-13 20:13:35 -05:00
41401f7257 Re-mapping java LSP socket port 2025-07-13 15:10:15 -05:00
766e70d766 WIP lsp-manager effort 2 2025-07-13 14:41:46 -05:00
73 changed files with 5672 additions and 688 deletions

View File

@@ -1 +1,17 @@
# Newton # Newton
### Images
![1 Newton default view.](images/pic1.png)
![2 Newton split pane view.](images/pic2.png)
![3 Newton search and replace shown.](images/pic3.png)
![4 Newton as transparent with youtube playing below it.](images/pic4.png)
### Additional Tools
* [Fzf](https://github.com/junegunn/fzf)
* [bat (cat but has highlighting)](https://github.com/sharkdp/bat)
* [ripgrep (very fast grep alternative)](https://github.com/BurntSushi/ripgrep/tree/master)
`
sudo pacman -Sy bat ripgrep fzf
`

View File

@@ -43,12 +43,11 @@
"styles":[ "styles":[
"node_modules/bootstrap/scss/bootstrap.scss", "node_modules/bootstrap/scss/bootstrap.scss",
"node_modules/bootstrap-icons/font/bootstrap-icons.css", "node_modules/bootstrap-icons/font/bootstrap-icons.css",
"src/assets/css/overrides.css",
"src/assets/css/styles.css", "src/assets/css/styles.css",
"src/assets/css/overrides.css"
"src/assets/css/ace-overrides.css" "src/assets/css/ace-overrides.css"
], ],
"scripts":[ "scripts":[
"src/libs/showdown.min.js"
], ],
"optimization": true "optimization": true
}, },

BIN
images/pic1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 KiB

BIN
images/pic2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
images/pic3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
images/pic4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 KiB

View File

@@ -5,6 +5,7 @@ const { menu } = require('./menu');
const { systemTray } = require('./system-tray'); const { systemTray } = require('./system-tray');
const { argsParser } = require('./args-parser'); const { argsParser } = require('./args-parser');
const { settingsManager } = require('./settings-manager'); const { settingsManager } = require('./settings-manager');
const { terminal } = require('./terminal');
const { newtonFs } = require('./fs'); const { newtonFs } = require('./fs');
const { newtonIPC } = require('./ipc'); const { newtonIPC } = require('./ipc');
@@ -53,8 +54,23 @@ const createWindow = (startType = "build", debug = false, args = []) => {
window.webContents.send('load-files', args); window.webContents.send('load-files', args);
}); });
window.webContents.on('did-finish-load', () => {
const cssFiles = [
path.join(newtonFs.CONFIG_PATH, 'override.css')
];
const jsFiles = [
// path.join(newtonFs.CONFIG_PATH, 'scripts', 'script1.js'),
// path.join(newtonFs.CONFIG_PATH, 'scripts', 'script2.js')
];
cssFiles.forEach(cssFile => newtonFs.readAndInjectCSS(window, cssFile));
jsFiles.forEach(jsFile => newtonFs.readAndInjectJS(window, jsFile));
});
menu.load(window); menu.load(window);
systemTray.load(menu.menuStruct); systemTray.load(menu.menuStruct);
terminal.load(window);
// window.setAutoHideMenuBar(true) // window.setAutoHideMenuBar(true)

View File

@@ -2,6 +2,7 @@ const { app } = require('electron');
let startType = "build"; let startType = "build";
let ipcPort = "4563";
let isDebug = false; let isDebug = false;
let args = []; let args = [];
@@ -17,6 +18,13 @@ const loadKWArgs = () => {
console.log(startType); console.log(startType);
} }
const hasIpcPort = app.commandLine.hasSwitch("ipc-port");
if (hasIpcPort) {
ipcPort = app.commandLine.getSwitchValue("ipc-port");
console.log("Has ipc-port switch...");
console.log(ipcPort);
}
const hasDebug = app.commandLine.hasSwitch("app-debug"); const hasDebug = app.commandLine.hasSwitch("app-debug");
if (hasDebug) { if (hasDebug) {
isDebug = app.commandLine.getSwitchValue("app-debug"); isDebug = app.commandLine.getSwitchValue("app-debug");
@@ -25,38 +33,29 @@ const loadKWArgs = () => {
} }
} }
const loadVArgs = () => { const filterOutLaunchAndKWArgs = () => {
console.log("\n\nStart VArgs:");
if ( if (
process.argv[0].endsWith("electron") process.argv[0].endsWith("electron")
) { ) {
process.argv = process.argv.slice(2); process.argv = process.argv.slice(2);
} }
if ( do {
process.argv = process.argv.slice(1);
} while (
process.argv.length > 0 &&
(
process.argv[0].endsWith("/newton") || process.argv[0].endsWith("/newton") ||
process.argv[0].endsWith(".AppImage") process.argv[0].endsWith(".AppImage") ||
) {
process.argv = process.argv.slice(1);
}
if ( process.argv.length > 0 && (
process.argv[0].includes("--trace-warnings") || process.argv[0].includes("--trace-warnings") ||
process.argv[0].includes("--start-as") process.argv[0].includes("--start-as") ||
process.argv[0].includes("--ipc-port")
) )
) { );
process.argv = process.argv.slice(1);
}
if ( process.argv.length > 0 && (
process.argv[0].includes("--trace-warnings") ||
process.argv[0].includes("--start-as")
)
) {
process.argv = process.argv.slice(1);
} }
const loadVArgs = () => {
console.log("\n\nStart VArgs:");
args = process.argv; args = process.argv;
args.forEach((val, index, array) => { args.forEach((val, index, array) => {
console.log(index + ': ' + val); console.log(index + ': ' + val);
@@ -67,6 +66,7 @@ const loadVArgs = () => {
const loadArgs = () => { const loadArgs = () => {
loadKWArgs(); loadKWArgs();
filterOutLaunchAndKWArgs();
loadVArgs(); loadVArgs();
} }
@@ -83,6 +83,10 @@ const getDebugMode = () => {
return isDebug; return isDebug;
} }
const getIpcPort = () => {
return ipcPort;
}
module.exports = { module.exports = {
argsParser: { argsParser: {
@@ -90,5 +94,6 @@ module.exports = {
getArgs: getArgs, getArgs: getArgs,
getStartType: getStartType, getStartType: getStartType,
getDebugMode: getDebugMode, getDebugMode: getDebugMode,
getIpcPort: getIpcPort,
} }
}; };

View File

@@ -100,6 +100,25 @@ const saveFileAs = () => {
}); });
} }
const chooseFolder = () => {
return dialog.showOpenDialog(
{
title: "Choose Folder:",
defaultPath: HOME_DIR,
properties: [
'openDirectory'
]
}
).then((response) => {
if (response.canceled) {
console.debug("Canceled folder selection...");
return "";
}
return response.filePaths[0];
});
}
const openFiles = (startPath) => { const openFiles = (startPath) => {
dialog.showOpenDialog( dialog.showOpenDialog(
{ {
@@ -166,11 +185,35 @@ const closeFile = (fpath) => {
unwatchFile(fpath); unwatchFile(fpath);
} }
const readAndInjectCSS = (window, filePath) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error(`Error reading CSS file: ${filePath}`, err);
return;
}
window.webContents.insertCSS(data);
});
}
const readAndInjectJS = (window, filePath) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error(`Error reading JS file: ${filePath}`, err);
return;
}
window.webContents.executeJavaScript(data);
});
}
module.exports = { module.exports = {
newtonFs: { newtonFs: {
CONFIG_PATH: CONFIG_PATH,
setWindow: setWindow, setWindow: setWindow,
chooseFolder: chooseFolder,
openFiles: openFiles, openFiles: openFiles,
saveFile: saveFile, saveFile: saveFile,
saveFileAs: saveFileAs, saveFileAs: saveFileAs,
@@ -180,6 +223,8 @@ module.exports = {
getLspConfigData: getLspConfigData, getLspConfigData: getLspConfigData,
getSettingsConfigData: getSettingsConfigData, getSettingsConfigData: getSettingsConfigData,
saveSettingsConfigData: saveSettingsConfigData, saveSettingsConfigData: saveSettingsConfigData,
readAndInjectJS: readAndInjectJS,
readAndInjectCSS: readAndInjectCSS,
loadFilesWatcher: loadFilesWatcher, loadFilesWatcher: loadFilesWatcher,
unwatchFile: unwatchFile, unwatchFile: unwatchFile,
} }

View File

@@ -8,14 +8,19 @@ const fetch = require('electron-fetch').default
const IPC_SERVER_IP = "127.0.0.1"; const IPC_SERVER_IP = "127.0.0.1";
let window = null; let window = null;
let ipcServer = null; let ipcServer = null;
let ipcServerPort = "4563"; let ipcServerPort = "";
let ipcServerURL = `http://${IPC_SERVER_IP}:${ipcServerPort}`; let ipcServerURL = "";
const setWindow = (win) => { const setWindow = (win) => {
window = win; window = win;
} }
const configure = (ipcPort) => {
ipcServerPort = ipcPort;
ipcServerURL = `http://${IPC_SERVER_IP}:${ipcServerPort}`;
}
const loadIPCServer = (fpath) => { const loadIPCServer = (fpath) => {
const app = express(); const app = express();
@@ -37,6 +42,9 @@ const loadIPCServer = (fpath) => {
console.debug("Load File(s) : ", req.body); console.debug("Load File(s) : ", req.body);
window.webContents.send('load-files', req.body); window.webContents.send('load-files', req.body);
window.show();
window.focus();
res.status(200).send(''); res.status(200).send('');
}); });
@@ -47,7 +55,8 @@ const loadIPCServer = (fpath) => {
} }
const isIPCServerUp = async () => { const isIPCServerUp = async () => {
const response = await fetch(`${ipcServerURL}/is-up`).catch((err) => { const response = await fetch(`${ipcServerURL}/is-up`)
.catch((err) => {
console.debug("IPCServer (status) : Not up; okay to start."); console.debug("IPCServer (status) : Not up; okay to start.");
return { return {
text: () => { text: () => {
@@ -73,9 +82,10 @@ const sendFilesToIPC = async (files) => {
module.exports = { module.exports = {
newtonIPC: { newtonIPC: {
setWindow: setWindow, configure: configure,
loadIPCServer: loadIPCServer, loadIPCServer: loadIPCServer,
isIPCServerUp: isIPCServerUp, isIPCServerUp: isIPCServerUp,
sendFilesToIPC: sendFilesToIPC, sendFilesToIPC: sendFilesToIPC,
setWindow: setWindow
} }
}; };

View File

@@ -72,13 +72,15 @@ const loadHandlers = () => {
ipcMain.handle('openFiles', (eve, startPath) => newton.fs.openFiles(startPath)); ipcMain.handle('openFiles', (eve, startPath) => newton.fs.openFiles(startPath));
ipcMain.handle('saveFile', (eve, path, content) => newton.fs.saveFile(path, content)); ipcMain.handle('saveFile', (eve, path, content) => newton.fs.saveFile(path, content));
ipcMain.handle('closeFile', (eve, path) => newton.fs.closeFile(path)); ipcMain.handle('closeFile', (eve, path) => newton.fs.closeFile(path));
ipcMain.handle('saveFileAs', (eve, content) => newton.fs.saveFileAs(content)); ipcMain.handle('saveFileAs', (eve) => newton.fs.saveFileAs());
ipcMain.handle('chooseFolder', (eve) => newton.fs.chooseFolder());
} }
app.whenReady().then(async () => { app.whenReady().then(async () => {
loadProcessSignalHandlers(); loadProcessSignalHandlers();
newton.args.loadArgs(); newton.args.loadArgs();
newton.ipc.configure( newton.args.getIpcPort() );
if ( !await newton.ipc.isIPCServerUp() ) { if ( !await newton.ipc.isIPCServerUp() ) {
newton.ipc.loadIPCServer(); newton.ipc.loadIPCServer();
} else { } else {

View File

@@ -22,6 +22,9 @@ const load = (win) => {
}, { }, {
label: 'Terminal', label: 'Terminal',
click: () => {} click: () => {}
}, {
label: "Quit",
click: () => win.webContents.send('menu-actions', "quit")
} }
] ]
}, { }, {

View File

@@ -9,6 +9,7 @@ contextBridge.exposeInMainWorld('electron', {
contextBridge.exposeInMainWorld('main', { contextBridge.exposeInMainWorld('main', {
onMenuActions: (callback) => ipcRenderer.on('menu-actions', (_event, action) => callback(action)), onMenuActions: (callback) => ipcRenderer.on('menu-actions', (_event, action) => callback(action)),
onTerminalActions: (callback) => ipcRenderer.on('terminal-actions', (_event, action) => callback(action)),
quit: () => ipcRenderer.invoke("quit"), quit: () => ipcRenderer.invoke("quit"),
toggleFullScreen: () => ipcRenderer.invoke("toggleFullScreen"), toggleFullScreen: () => ipcRenderer.invoke("toggleFullScreen"),
}); });
@@ -19,6 +20,7 @@ contextBridge.exposeInMainWorld('fs', {
openFiles: (startPath) => ipcRenderer.invoke("openFiles", startPath), openFiles: (startPath) => ipcRenderer.invoke("openFiles", startPath),
saveFile: (path, content) => ipcRenderer.invoke("saveFile", path, content), saveFile: (path, content) => ipcRenderer.invoke("saveFile", path, content),
saveFileAs: () => ipcRenderer.invoke("saveFileAs"), saveFileAs: () => ipcRenderer.invoke("saveFileAs"),
chooseFolder: () => ipcRenderer.invoke("chooseFolder"),
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)),

View File

@@ -33,6 +33,9 @@ const load = (win) => {
}, { }, {
label: 'Help', label: 'Help',
click: () => win.webContents.send('menu-actions', "show-about") click: () => win.webContents.send('menu-actions', "show-about")
}, {
label: 'Quit',
click: () => win.webContents.send('menu-actions', "quit")
} }
]; ];

35
newton/terminal.js Normal file
View File

@@ -0,0 +1,35 @@
const { ipcMain } = require('electron');
// const pty = require('node-pty');
const os = require("os");
const shell = "win32" === os.platform() ? "powershell.exe" : "bash";
const load = (win) => {
// const ptyProcess = pty.spawn(shell, [], {
// name: "xterm-color",
// cols: 172,
// rows: 256,
// cwd: process.env.HOME,
// env: process.env
// });
// ptyProcess.on('data', function(data) {
// win.webContents.send("terminal-actions", data);
// });
// ipcMain.on("terminal-keystroke", (event, key) => {
// ptyProcess.write(key);
// });
}
module.exports = {
terminal: {
load: load
}
};

View File

@@ -9,15 +9,18 @@
"main": "newton/main.js", "main": "newton/main.js",
"private": true, "private": true,
"scripts": { "scripts": {
"app": "ng build --base-href ./ && electron . --trace-warnings --start-as=build", "app": "ng build --base-href ./ && electron . --trace-warnings --start-as=build --ipc-port=4588",
"electron-start": "electron . --trace-warnings --start-as=build", "electron-start": "electron . --trace-warnings --start-as=build --ipc-port=4588",
"electron-pack": "ng build --base-href ./ && electron-builder --dir", "electron-pack": "ng build --base-href ./ && electron-builder --dir",
"electron-dist": "ng build --base-href ./ && electron-builder", "electron-dist": "ng build --base-href ./ && electron-builder",
"electron-dist-linux": "ng build --base-href ./ && electron-builder --linux deb zip AppImage", "electron-dist-zip-linux": "ng build --base-href ./ && electron-builder --linux zip",
"electron-dist-deb-linux": "ng build --base-href ./ && electron-builder --linux deb",
"electron-dist-appimage-linux": "ng build --base-href ./ && electron-builder --linux AppImage",
"electron-dist-all-linux": "ng build --base-href ./ && electron-builder --linux deb zip AppImage",
"electron-dist-all": "ng build --base-href ./ && electron-builder -mwl", "electron-dist-all": "ng build --base-href ./ && electron-builder -mwl",
"electron-concurrently": "concurrently 'ng serve' 'electron . --trace-warnings --start-as=ng-serve'", "electron-concurrently": "concurrently 'ng serve' 'electron . --trace-warnings --start-as=ng-serve'",
"ng-serve": "ng serve", "ng-serve": "ng serve",
"ng-build": "ng build", "ng-build": "ng build --base-href ./",
"ng-watch-build": "ng build --watch --configuration development", "ng-watch-build": "ng build --watch --configuration development",
"ng-test": "ng test", "ng-test": "ng test",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
@@ -27,10 +30,7 @@
"icon": "./icos/", "icon": "./icos/",
"files": [ "files": [
"newton/", "newton/",
"build/", "build/"
"!node_modules/ace-builds/",
"!node_modules/web-streams-polyfill/",
"!node_modules/@angular/"
], ],
"mac": { "mac": {
"category": "public.app-category.developer-tools" "category": "public.app-category.developer-tools"
@@ -44,43 +44,70 @@
"maintainer": "ITDominator" "maintainer": "ITDominator"
} }
}, },
"postinstall": "electron-builder install-app-deps", "overrides": {
"glob": "9.0.0",
"rimraf": "4.3.1"
},
"dependencies": { "dependencies": {
"@angular/cdk": "19.2.0",
"@angular/common": "19.2.0",
"@angular/core": "19.2.0",
"@angular/forms": "19.2.0",
"@angular/platform-browser": "19.2.0",
"ace-builds": "1.43.0",
"ace-diff": "3.0.3", "ace-diff": "3.0.3",
"ace-linters": "1.7.0", "ace-layout": "1.5.0",
"ace-linters": "1.8.3",
"bootstrap": "5.3.6", "bootstrap": "5.3.6",
"bootstrap-icons": "1.12.1", "bootstrap-icons": "1.12.1",
"chokidar": "4.0.3", "chokidar": "4.0.3",
"electron-fetch": "1.9.1", "electron-fetch": "1.9.1",
"express": "4.18.2", "express": "4.18.2",
"node-fetch": "3.3.2", "marked": "16.4.0",
"rxjs": "7.8.0",
"socket.io": "4.8.1", "socket.io": "4.8.1",
"uuid": "11.1.0", "uuid": "11.1.0",
"zone.js": "0.15.0" "zone.js": "0.15.0"
}, },
"devDependencies": { "devDependencies": {
"ace-builds": "1.43.0",
"@angular-devkit/build-angular": "19.2.8", "@angular-devkit/build-angular": "19.2.8",
"@angular/cdk": "19.2.0",
"@angular/common": "19.2.0",
"@angular/core": "19.2.0",
"@angular/cli": "19.2.8", "@angular/cli": "19.2.8",
"@angular/compiler-cli": "19.2.0", "@angular/compiler-cli": "19.2.0",
"@angular/forms": "19.2.0",
"@angular/platform-browser": "19.2.0",
"@types/express": "4.17.17", "@types/express": "4.17.17",
"@types/jasmine": "5.1.0", "@types/jasmine": "5.1.0",
"@types/node": "18.18.0", "@types/node": "18.18.0",
"concurrently": "9.1.2", "concurrently": "9.1.2",
"electron": "36.2.0", "electron": "36.2.0",
"electron-builder": "26.0.12", "@electron/remote": "2.1.2",
"electron-builder": "22.7.0",
"jasmine-core": "5.6.0", "jasmine-core": "5.6.0",
"jimp": "1.6.0",
"karma": "6.4.0", "karma": "6.4.0",
"karma-chrome-launcher": "3.2.0", "karma-chrome-launcher": "3.2.0",
"karma-coverage": "2.2.0", "karma-coverage": "2.2.0",
"karma-jasmine": "5.1.0", "karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.1.0", "karma-jasmine-html-reporter": "2.1.0",
"nanoevents": "9.1.0",
"rxjs": "7.8.0",
"tree-sitter": "0.21.1",
"tree-sitter-bash": "0.23.2",
"tree-sitter-c": "0.23.1",
"tree-sitter-cli": "0.25.8",
"tree-sitter-cpp": "0.23.4",
"tree-sitter-css": "0.23.0",
"tree-sitter-go": "0.23.4",
"tree-sitter-html": "0.23.2",
"tree-sitter-java": "0.23.5",
"tree-sitter-javascript": "0.23.1",
"tree-sitter-json": "0.24.8",
"tree-sitter-lua": "2.1.3",
"tree-sitter-php": "0.23.12",
"tree-sitter-python": "0.23.2",
"tree-sitter-r": "0.0.1-security",
"tree-sitter-sql": "0.1.0",
"tree-sitter-sqlite": "0.0.1-security",
"tree-sitter-toml": "0.5.1",
"tree-sitter-typescript": "0.23.2",
"tree-sitter-yaml": "0.5.0",
"tslib": "2.3.0", "tslib": "2.3.0",
"typescript": "5.7.2" "typescript": "5.7.2"
} }

View File

@@ -8,62 +8,78 @@
"command": "lsp-ws-proxy --listen 4114 -- jdtls", "command": "lsp-ws-proxy --listen 4114 -- jdtls",
"alt-command": "lsp-ws-proxy -- jdtls", "alt-command": "lsp-ws-proxy -- jdtls",
"alt-command2": "java-language-server", "alt-command2": "java-language-server",
"socket": "ws://127.0.0.1:4114/?name=jdtls", "socket": "ws://127.0.0.1:9999/java",
"alt-socket": "ws://127.0.0.1:3030/?name=java-language-server", "socket-two": "ws://127.0.0.1:9999/?name=jdtls",
"alt-socket": "ws://127.0.0.1:9999/?name=java-language-server",
"initialization-options": { "initialization-options": {
"bundles": [ "bundles": [
"intellicode-core.jar"
], ],
"workspaceFolders": [ "workspaceFolders": [
"file://" "file://{workspace.folder}"
], ],
"extendedClientCapabilities": { "extendedClientCapabilities": {
"classFileContentsSupport": true, "classFileContentsSupport": true,
"executeClientCommandSupport": true "executeClientCommandSupport": false
}, },
"settings": { "settings": {
"java": { "java": {
"autobuild": { "autobuild": {
"enabled": false "enabled": true
}, },
"completion": { "jdt": {
"enabled": true, "ls": {
"importOrder": [ "javac": {
"java", "enabled": true
"javax", },
"org", "java": {
"com" "home": "{user.home}/Portable_Apps/sdks/javasdk/jdk-22.0.2"
] },
"lombokSupport": {
"enabled": true
},
"protobufSupport":{
"enabled": true
},
"androidSupport": {
"enabled": true
}
}
}, },
"configuration": { "configuration": {
"updateBuildConfiguration": "automatic",
"maven": { "maven": {
"userSettings": "{user.home}/.config/jdtls/settings.xml", "userSettings": "{user.home}/.config/lsps/jdtls/settings.xml",
"globalSettings": "{user.home}/.config/jdtls/settings.xml" "globalSettings": "{user.home}/.config/lsps/jdtls/settings.xml"
}, },
"runtimes": [ "runtimes": [
{ {
"name": "JavaSE-17", "name": "JavaSE-17",
"path": "/usr/lib/jvm/default-runtime", "path": "/usr/lib/jvm/java-17-openjdk",
"javadoc": "https://docs.oracle.com/en/java/javase/17/docs/api/", "javadoc": "https://docs.oracle.com/en/java/javase/17/docs/api/",
"default": false
},
{
"name": "JavaSE-22",
"path": "{user.home}/Portable_Apps/sdks/javasdk/jdk-22.0.2",
"javadoc": "https://docs.oracle.com/en/java/javase/22/docs/api/",
"default": true "default": true
} }
] ]
}, },
"classPath": [ "classPath": [
"{user.home}/.config/jdtls/m2/repository/**/*-sources.jar", "{user.home}/.config/lsps/jdtls/m2/repository/**/*-sources.jar",
"lib/**/*-sources.jar" "lib/**/*-sources.jar"
], ],
"docPath": [ "docPath": [
"{user.home}/.config/jdtls/m2/repository/**/*-javadoc.jar", "{user.home}/.config/lsps/jdtls/m2/repository/**/*-javadoc.jar",
"lib/**/*-javadoc.jar" "lib/**/*-javadoc.jar"
], ],
"silentNotification": true,
"project": { "project": {
"encoding": "ignore", "encoding": "ignore",
"outputPath": "bin", "outputPath": "bin",
"referencedLibraries": [ "referencedLibraries": [
"lib/**/*.jar", "{user.home}/.config/lsps/jdtls/m2/repository/**/*.jar",
"{user.home}/.config/jdtls/m2/repository/**/*.jar" "lib/**/*.jar"
], ],
"importOnFirstTimeStartup": "automatic", "importOnFirstTimeStartup": "automatic",
"importHint": true, "importHint": true,
@@ -73,7 +89,7 @@
], ],
"sourcePaths": [ "sourcePaths": [
"src", "src",
"{user.home}/.config/jdtls/m2/repository/**/*.jar" "{user.home}/.config/lsps/jdtls/m2/repository/**/*.jar"
] ]
}, },
"sources": { "sources": {
@@ -103,9 +119,9 @@
"enabled": true "enabled": true
}, },
"version": "", "version": "",
"home": "abs(static/gradle-7.3.3)", "home": "{user.home}/Portable_Apps/sdks/gradle/gradle-9.0.0",
"java": { "java": {
"home": "abs(static/launch_jres/17.0.6-linux-x86_64)" "home": "{user.home}/Portable_Apps/sdks/javasdk/jdk-22.0.2"
}, },
"offline": { "offline": {
"enabled": false "enabled": false
@@ -131,14 +147,65 @@
"downloadSources": true, "downloadSources": true,
"updateSnapshots": true "updateSnapshots": true
}, },
"silentNotification": true,
"contentProvider": {
"preferred": "fernflower"
},
"signatureHelp": { "signatureHelp": {
"enabled": true, "enabled": true,
"description": { "description": {
"enabled": true "enabled": true
} }
}, },
"completion": {
"enabled": true,
"matchCase": "firstletter",
"maxResults": 25,
"guessMethodArguments": true,
"lazyResolveTextEdit": {
"enabled": true
},
"postfix": {
"enabled": true
},
"favoriteStaticMembers": [
"org.junit.Assert.*",
"org.junit.Assume.*",
"org.junit.jupiter.api.Assertions.*",
"org.junit.jupiter.api.Assumptions.*",
"org.junit.jupiter.api.DynamicContainer.*",
"org.junit.jupiter.api.DynamicTest.*"
],
"importOrder": [
"#",
"java",
"javax",
"org",
"com"
]
},
"references": {
"includeAccessors": true,
"includeDecompiledSources": true
},
"codeGeneration": {
"toString": {
"template": "${object.className}{${member.name()}=${member.value}, ${otherMembers}}"
},
"insertionLocation": "afterCursor",
"useBlocks": true
},
"implementationsCodeLens": { "implementationsCodeLens": {
"enabled": true "enabled": true
},
"referencesCodeLens": {
"enabled": true
},
"progressReports": {
"enabled": false
},
"saveActions": {
"organizeImports": true
} }
} }
} }
@@ -150,53 +217,39 @@
"alt-command": "pylsp", "alt-command": "pylsp",
"alt-command2": "lsp-ws-proxy --listen 4114 -- pylsp", "alt-command2": "lsp-ws-proxy --listen 4114 -- pylsp",
"alt-command3": "pylsp --ws --port 4114", "alt-command3": "pylsp --ws --port 4114",
"socket": "ws://127.0.0.1:9999/?name=pylsp", "socket": "ws://127.0.0.1:9999/python",
"socket-two": "ws://127.0.0.1:9999/?name=pylsp",
"initialization-options": { "initialization-options": {
"pyls": {
"plugins": {
"pycodestyle": {
"enabled": false
},
"pydocstyle": {
"enabled": false
},
"pyflakes": {
"enabled": false
},
"pylint": {
"enabled": false
},
"mccabe": {
"enabled": false
}
}
},
"pylsp": { "pylsp": {
"plugins": { "rope": {
"pycodestyle": { "ropeFolder": "{user.home}/.config/lsps/ropeproject"
"enabled": false
}, },
"pydocstyle": { "plugins": {
"ruff": {
"enabled": true,
"extendSelect": ["I"],
"lineLength": 80
},
"pycodestyle": {
"enabled": false "enabled": false
}, },
"pyflakes": { "pyflakes": {
"enabled": false "enabled": false
}, },
"pylint": { "pylint": {
"enabled": false "enabled": true
}, },
"mccabe": { "mccabe": {
"enabled": false "enabled": false
}, },
"ruff": true,
"pylsp_rope": { "pylsp_rope": {
"rename": true "rename": false
}, },
"rope_rename": { "rope_rename": {
"enabled": true "enabled": false
}, },
"rope_autoimport": { "rope_autoimport": {
"enabled": true "enabled": false
}, },
"rope_completion": { "rope_completion": {
"enabled": false, "enabled": false,
@@ -209,13 +262,13 @@
"enabled": true, "enabled": true,
"include_class_objects": true, "include_class_objects": true,
"include_function_objects": true, "include_function_objects": true,
"fuzzy": true "fuzzy": false
}, },
"jedi": { "jedi": {
"root_dir": "file://{workspace.folder}",
"extra_paths": [ "extra_paths": [
"{user.home}/Portable_Apps/py-venvs/pylsp-venv/venv/lib/python3.10/site-packages" "{user.home}/Portable_Apps/py-venvs/pylsp-venv/venv/lib/python3.10/site-packages"
], ]
"root_dir": ""
} }
} }
} }
@@ -226,7 +279,8 @@
"info": "https://pypi.org/project/jedi-language-server/", "info": "https://pypi.org/project/jedi-language-server/",
"command": "jedi-language-server", "command": "jedi-language-server",
"alt-command": "lsp-ws-proxy --listen 3030 -- jedi-language-server", "alt-command": "lsp-ws-proxy --listen 3030 -- jedi-language-server",
"socket": "ws://127.0.0.1:3030/?name=jedi-language-server", "socket": "ws://127.0.0.1:9999/python",
"socket-two": "ws://127.0.0.1:9999/?name=jedi-language-server",
"initialization-options": { "initialization-options": {
"jediSettings": { "jediSettings": {
"autoImportModules": [], "autoImportModules": [],
@@ -245,7 +299,13 @@
], ],
"environmentPath": "{user.home}/Portable_Apps/py-venvs/gtk-apps-venv/venv/bin/python", "environmentPath": "{user.home}/Portable_Apps/py-venvs/gtk-apps-venv/venv/bin/python",
"symbols": { "symbols": {
"ignoreFolders": [".nox", ".tox", ".venv", "__pycache__", "venv"], "ignoreFolders": [
".nox",
".tox",
".venv",
"__pycache__",
"venv"
],
"maxSymbols": 20 "maxSymbols": 20
} }
} }
@@ -255,28 +315,8 @@
"info": "https://clangd.llvm.org/", "info": "https://clangd.llvm.org/",
"command": "lsp-ws-proxy -- clangd", "command": "lsp-ws-proxy -- clangd",
"alt-command": "clangd", "alt-command": "clangd",
"socket": "ws://127.0.0.1:3030/?name=clangd", "socket": "ws://127.0.0.1:9999/cpp",
"initialization-options": {} "socket-two": "ws://127.0.0.1:9999/?name=clangd",
},
"sh": {
"info": "",
"command": "",
"alt-command": "",
"socket": "ws://127.0.0.1:3030/?name=shell",
"initialization-options": {}
},
"go": {
"info": "https://pkg.go.dev/golang.org/x/tools/gopls#section-readme",
"command": "lsp-ws-proxy -- gopls",
"alt-command": "gopls",
"socket": "ws://127.0.0.1:3030/?name=gopls",
"initialization-options": {}
},
"lua": {
"info": "https://github.com/LuaLS/lua-language-server",
"command": "lsp-ws-proxy -- lua-language-server",
"alt-command": "lua-language-server",
"socket": "ws://127.0.0.1:3030/?name=gopls",
"initialization-options": {} "initialization-options": {}
}, },
"c": { "c": {
@@ -284,7 +324,40 @@
"info": "https://clangd.llvm.org/", "info": "https://clangd.llvm.org/",
"command": "lsp-ws-proxy -- clangd", "command": "lsp-ws-proxy -- clangd",
"alt-command": "clangd", "alt-command": "clangd",
"socket": "ws://127.0.0.1:3030/?name=clangd", "socket": "ws://127.0.0.1:9999/c",
"socket-two": "ws://127.0.0.1:9999/?name=clangd",
"initialization-options": {}
},
"go": {
"info": "https://pkg.go.dev/golang.org/x/tools/gopls#section-readme",
"command": "lsp-ws-proxy -- gopls",
"alt-command": "gopls",
"socket": "ws://127.0.0.1:9999/go",
"socket-two": "ws://127.0.0.1:9999/?name=gopls",
"initialization-options": {}
},
"typescript": {
"info": "https://github.com/typescript-language-server/typescript-language-server",
"command": "lsp-ws-proxy -- typescript-language-server",
"alt-command": "typescript-language-server --stdio",
"socket": "ws://127.0.0.1:9999/typescript",
"socket-two": "ws://127.0.0.1:9999/?name=ts",
"initialization-options": {}
},
"sh": {
"info": "",
"command": "",
"alt-command": "",
"socket": "ws://127.0.0.1:9999/bash",
"socket-two": "ws://127.0.0.1:9999/?name=shell",
"initialization-options": {}
},
"lua": {
"info": "https://github.com/LuaLS/lua-language-server",
"command": "lsp-ws-proxy -- lua-language-server",
"alt-command": "lua-language-server",
"socket": "ws://127.0.0.1:9999/lua",
"socket-two": "ws://127.0.0.1:9999/?name=lua",
"initialization-options": {} "initialization-options": {}
} }
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,7 @@
<div class="col"> <div class="col">
<info-bar></info-bar> <info-bar></info-bar>
<tabs></tabs> <tabs></tabs>
<hr class="tabs-bar-underline"/>
<editors></editors> <editors></editors>
<search-replace></search-replace> <search-replace></search-replace>
<markdown-preview></markdown-preview> <markdown-preview></markdown-preview>

View File

@@ -1,4 +1,6 @@
import { Component } from '@angular/core'; import { Component, inject } from '@angular/core';
import { WebsocketService } from './common/services/websocket.service';
import { InfoBarComponent } from './editor/info-bar/info-bar.component'; import { InfoBarComponent } from './editor/info-bar/info-bar.component';
import { TabsComponent } from './editor/tabs/tabs.component'; import { TabsComponent } from './editor/tabs/tabs.component';
@@ -28,6 +30,69 @@ import { LspManagerComponent } from "./editor/lsp-manager/lsp-manager.component"
export class AppComponent { export class AppComponent {
title = 'Newton'; title = 'Newton';
constructor() {} protected ws: WebsocketService = inject(WebsocketService);
constructor() {
this.checkIfNotElectronMode();
}
ngOnInit() {}
checkIfNotElectronMode() {
if (
window.electron ||
window.main ||
window.fs
) { return; }
this.setupWebsocket();
this.setupWindowBindings();
}
setupWindowBindings() {
window.electron ??= {
node: () => { return "" },
chrome: () => { return "" },
electron: () => { return "" },
};
window.main ??= {
onMenuActions: () => {},
onTerminalActions: () => {},
quit: () => {},
toggleFullScreen: () => {},
};
window.fs ??= {
getLspConfigData: () => {
return new Promise((resolve, reject) => {
resolve("{}");
});
},
getFileContents: () => {},
openFiles: () => {},
saveFile: () => {},
saveFileAs: () => {},
chooseFolder: () => {},
closeFile: () => {},
getPathForFile: () => {},
onLoadFiles: () => {},
onUpdateFilePath: () => {},
onSavedFile: () => {},
onChangedFile: () => {},
onDeletedFile: () => {},
};
}
setupWebsocket() {
// TODO: Set with dynamic address and port
this.ws.connect('ws://localhost:7272').subscribe(msg => {
console.log(msg);
console.log(window.fs);
// this.ws.send("{ 'text': 'Hello server!' }");
});
}
} }

View File

@@ -1,7 +1,11 @@
import { Component, inject } from "@angular/core"; import {
Component,
DestroyRef,
inject
} from "@angular/core";
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { Subject, takeUntil } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import * as bootstrap from "bootstrap"; import * as bootstrap from "bootstrap";
@@ -25,17 +29,18 @@ import 'ace-diff/dist/ace-diff-dark.min.css';
} }
}) })
export class DiffModalComponent { export class DiffModalComponent {
readonly #destroyRef: DestroyRef = inject(DestroyRef);
diffModal!: bootstrap.Modal; diffModal!: bootstrap.Modal;
constructor() { constructor() {
this.loadSubscribers();
} }
private ngAfterViewInit(): void { private ngAfterViewInit(): void {
this.loadDiffView(); this.loadDiffView();
this.loadSubscribers();
} }
private loadDiffView() { private loadDiffView() {

View File

@@ -37,6 +37,11 @@ export const Keybindings: Array<{}> = [
bindKey: {win: "ctrl-r", mac: "ctrl-r"}, bindKey: {win: "ctrl-r", mac: "ctrl-r"},
readOnly: false readOnly: false
}, { }, {
name: "terminalPopup",
bindKey: {win: "ctrl-shift-.", mac: "ctrl-shift-."},
readOnly: false
}, {
name: "newFile", name: "newFile",
bindKey: {win: "ctrl-t", mac: "ctrl-t"}, bindKey: {win: "ctrl-t", mac: "ctrl-t"},
service: "editorsService", service: "editorsService",

View File

@@ -0,0 +1,5 @@
export abstract class ButtonMap {
static LEFT: number = 0;
static MIDDLE: number = 1;
static RIGHT: number = 2;
}

View File

@@ -16,7 +16,7 @@ import { NewtonFile } from '../types/file.type';
}) })
export class DndDirective { export class DndDirective {
@HostBinding('class.fileover') fileOver!: boolean; @HostBinding('class.fileover') fileOver!: boolean;
@Output() fileDropped = new EventEmitter<any>(); @Output() fileDropped: EventEmitter<any> = new EventEmitter();
@HostListener('dragover', ['$event']) @HostListener('dragover', ['$event'])
onDragOver(evt: any) { onDragOver(evt: any) {

View File

@@ -11,11 +11,11 @@ import {
selector: '[draggable-item]' selector: '[draggable-item]'
}) })
export class DraggableDirective { export class DraggableDirective {
@Output() dragStart = new EventEmitter<PointerEvent>(); @Output() dragStart: EventEmitter<PointerEvent> = new EventEmitter();
@Output() dragMove = new EventEmitter<PointerEvent>(); @Output() dragMove: EventEmitter<PointerEvent> = new EventEmitter();
@Output() dragEnd = new EventEmitter<PointerEvent>(); @Output() dragEnd: EventEmitter<PointerEvent> = new EventEmitter();
private dragging = false; private dragging: boolean = false;
selected: any; selected: any;

View File

@@ -11,9 +11,9 @@ import {
selector: '[pane-handle]' selector: '[pane-handle]'
}) })
export class PaneHandleDirective { export class PaneHandleDirective {
@Output() dragStart = new EventEmitter<PointerEvent>(); @Output() dragStart: EventEmitter<PointerEvent> = new EventEmitter();
@Output() dragMove = new EventEmitter<PointerEvent>(); @Output() dragMove: EventEmitter<PointerEvent> = new EventEmitter();
@Output() dragEnd = new EventEmitter<PointerEvent>(); @Output() dragEnd: EventEmitter<PointerEvent> = new EventEmitter();
private dragging: boolean = false; private dragging: boolean = false;
private isHrPane: boolean = false; private isHrPane: boolean = false;
@@ -31,7 +31,7 @@ export class PaneHandleDirective {
!target.classList.contains("hr-pane-handle") && !target.classList.contains("hr-pane-handle") &&
!target.classList.contains("vr-pane-handle") !target.classList.contains("vr-pane-handle")
) { ) {
console.log("Must have 'hr-pane-handle' or 'vr-pane-handle' in classList!"); console.error("Must have 'hr-pane-handle' or 'vr-pane-handle' in classList!");
return; return;
} }

View File

@@ -0,0 +1,64 @@
import { Injectable } from '@angular/core';
import * as ace from "ace-builds/src-min-noconflict/ace";
@Injectable({
providedIn: 'root'
})
export class ColorTokenizerService {
readonly #RULES: {} = {
start: [
{ token: "hex3", regex: "#[A-Fa-f0-9]{3}(?![A-Fa-f0-9])" },
{ token: "hex6", regex: "#[A-Fa-f0-9]{6}(?![A-Fa-f0-9])" },
{ token: "hex8", regex: "#[A-Fa-f0-9]{8}(?![A-Fa-f0-9])" },
{
token: "rgb",
regex: /rgb\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)/
},
{
token: "rgba",
regex: /rgba\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*(?:0(?:\.\d+)?|1(?:\.0+)?)\s*\)/
}
]
};
tokenizer!: any;
cssLines: {} = {};
constructor() {
Object.freeze(this.#RULES)
const Tokenizer = ace.require("ace/tokenizer").Tokenizer;
this.tokenizer = new Tokenizer(this.#RULES);
}
public async parse(data: string) {
const lines = data.split("\n");
for (let i = 0; i < lines.length; i++) {
const token = this.parseLine( lines[i] );
if (!token) continue;
this.cssLines[i] = token;
this.cssLines[i]["hash"] = btoa(
token["value"]
);
}
// console.log(this.cssLines);
}
public parseLine(line: string): {} | null {
const tokens = this.tokenizer.getLineTokens(line, "start").tokens;
for (let i = 0; i < tokens.length; i++) {
if ("text" === tokens[i]["type"]) continue;
return tokens[i];
}
return null;
}
}

View File

@@ -15,21 +15,15 @@ import { EditorType } from '../../types/editor.type';
providedIn: 'root' providedIn: 'root'
}) })
export class EditorsService { export class EditorsService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1); private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
editors: Map<string, CodeViewComponent>; editors: Map<string, CodeViewComponent> = new Map();
editorSettings: typeof EditorSettings; editorSettings: typeof EditorSettings = EditorSettings;
activeEditor!: string; activeEditor: string = "";
miniMapView!: CodeViewComponent; miniMapView!: CodeViewComponent;
constructor() {
this.editorSettings = EditorSettings;
this.editors = new Map<string, CodeViewComponent>();
}
public getEditorsAsArray(): CodeViewComponent[] { public getEditorsAsArray(): CodeViewComponent[] {
return [...this.editors.values()]; return [...this.editors.values()];
} }

View File

@@ -8,14 +8,11 @@ import { ServiceMessage } from '../../../types/service-message.type';
providedIn: 'root' providedIn: 'root'
}) })
export class InfoBarService { export class InfoBarService {
private dataSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1); private dataSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
private fpathSubject: ReplaySubject<string> = new ReplaySubject<string>(1); private fpathSubject: ReplaySubject<string> = new ReplaySubject(1);
private cursorPosSubject: ReplaySubject<any> = new ReplaySubject<any>(1); private cursorPosSubject: ReplaySubject<any> = new ReplaySubject(1);
private encodeingSubject: ReplaySubject<string> = new ReplaySubject<string>(1); private encodeingSubject: ReplaySubject<string> = new ReplaySubject(1);
private ftypeSubject: ReplaySubject<string> = new ReplaySubject<string>(1); private ftypeSubject: ReplaySubject<string> = new ReplaySubject(1);
constructor() {}
public setData(data: ServiceMessage): void { public setData(data: ServiceMessage): void {

View File

@@ -1,9 +1,15 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs'; import { ReplaySubject, Observable } from 'rxjs';
import { CompletionProvider } from "ace-builds/src-min-noconflict/ace";
import { CommandBarTooltip } from "ace-builds/src-min-noconflict/ext-command_bar";
import { InlineAutocomplete } from "ace-builds/src-min-noconflict/ext-inline_autocomplete";
import { AceLanguageClient, LanguageClientConfig } from 'ace-linters/build/ace-language-client'; import { AceLanguageClient, LanguageClientConfig } from 'ace-linters/build/ace-language-client';
import { LanguageProvider } from "ace-linters"; import { LanguageProvider } from "ace-linters";
import { ServiceMessage } from '../../../types/service-message.type'; import { ServiceMessage } from '../../../types/service-message.type';
@@ -12,81 +18,156 @@ import { ServiceMessage } from '../../../types/service-message.type';
providedIn: 'root' providedIn: 'root'
}) })
export class LspManagerService { export class LspManagerService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1); private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
lspConfigData!: {}; workspaceFolder: string = "";
lspConfigDataStr: string = "";
languageProviders: {} = {}; languageProviders: {} = {};
constructor() {
}
public loadLspConfigData(): Promise<string | void> { public loadLspConfigData(): Promise<string | void> {
return this.getLspConfigData().then((lspConfigData: string) => { return this.getLspConfigData().then((lspConfigData: string) => {
this.lspConfigData = JSON.parse(lspConfigData); this.lspConfigDataStr = lspConfigData;
if (this.lspConfigData["message"]) {
console.log(
"Warning: LSP this.lspConfigData is a 'message'",
this.lspConfigData
);
this.lspConfigData = {};
}
return lspConfigData; return lspConfigData;
}); });
} }
public registerEditor(editor: any): void { public registerEditorToLSPClient(editor: any) {
let modeParts = editor.getSession()["$modeId"].split("/"); let mode = this.getMode(editor.session);
let mode = modeParts[ modeParts.length - 1 ];
if ( !this.languageProviders[mode] ) { this.languageProviders[mode]?.registerEditor(
this.languageProviders[mode] = this.getLanguageProviderWithClientServer(mode); editor,
} editor.session.lspConfig
);
this.languageProviders[mode].registerEditor(editor);
} }
private getLspConfigData(): Promise<string> { private getLspConfigData(): Promise<string> {
return window.fs.getLspConfigData(); return window?.fs.getLspConfigData();
} }
private getLanguageProviderWithClientServer(mode: string) { private parseAndReturnLSPConfigData(): {} {
let _initializationOptions = {}; let configData = JSON.parse(
this.lspConfigDataStr.replaceAll("{workspace.folder}", this.workspaceFolder)
);
if ( Object.keys(this.lspConfigData).length !== 0 && this.lspConfigData[mode] ) { if (configData["message"]) {
_initializationOptions = this.lspConfigData[mode]["initialization-options"]; console.warn(
"Warning: LSP this.lspConfigDataStr is a 'message'",
this.lspConfigDataStr
);
configData = {};
} }
let servers: LanguageClientConfig[] = [ return configData;
}
private getInitializationOptions(mode: string, configData: {}): {} {
let initializationOptions = {};
if ( Object.keys(configData).length !== 0 && configData[mode] ) {
initializationOptions = configData[mode]["initialization-options"];
}
return initializationOptions;
}
public createLanguageProviderWithClientServer(mode: string): LanguageProvider {
if ( this.languageProviders[mode] ) return;
let servers: LanguageClientConfig[] = [];
try {
let lspConfigData = this.parseAndReturnLSPConfigData();
let initializationOptions = this.getInitializationOptions(mode, lspConfigData);
servers = [
{ {
module: () => import("ace-linters/build/language-client"), module: () => import("ace-linters/build/language-client"),
modes: mode, modes: mode,
type: "socket", type: "socket",
socket: new WebSocket(`ws://127.0.0.1:9999/${mode}`), socket: new WebSocket( lspConfigData[mode]["socket"] ),
// socket: new WebSocket("ws://127.0.0.1:9999/?name=pylsp"), initializationOptions: initializationOptions
initializationOptions: _initializationOptions
} }
]; ];
} catch(error) {
console.error(
"Error: Language Server could not be loaded OR doesn't exist in Newton-LSP config setup...",
);
return AceLanguageClient.for(servers); return;
} }
private getLanguageProviderWithWebWorker() { this.languageProviders[mode] = AceLanguageClient.for(
servers,
{
workspacePath: this.workspaceFolder,
functionality: {
hover: true,
completion: {
overwriteCompleters: true,
lspCompleterOptions: {
triggerCharacters: {
add: [
" ",
".",
"@"
]
}
}
},
// inlineCompletion: {
// overwriteCompleters: true
// },
completionResolve: true,
format: true,
documentHighlights: true,
signatureHelp: true,
semanticTokens: true,
codeActions: true
},
// aceComponents: {
// InlineAutocomplete,
// CommandBarTooltip,
// CompletionProvider
// },
manualSessionControl: true
}
);
return this.languageProviders[mode];
}
public closeLanguageProviderWithClientServer(mode: string): LanguageProvider {
if ( !this.languageProviders[mode] ) return;
let connection = this.languageProviders[mode];
delete this.languageProviders[mode];
connection.closeConnection();
}
private getLanguageProviderWithWebWorker(): LanguageProvider {
let worker = new Worker(new URL('./webworker.js', import.meta.url)); let worker = new Worker(new URL('./webworker.js', import.meta.url));
return LanguageProvider.create(worker); return LanguageProvider.create(worker);
} }
protected setSessionFilePath(session: any, mode: string = "", filePath: string = "") { public registerSession(editor: any) {
if ( !session || !mode || !filePath || !this.languageProviders[mode] ) return; let mode = this.getMode(editor.session);
this.languageProviders[mode].setSessionFilePath(session, filePath); if ( !this.languageProviders[mode] ) return;
this.languageProviders[mode].registerSession(
editor.session,
editor,
editor.session.lspConfig
);
} }
protected closeDocument(session: any, mode: string) { public getMode(session: any): string {
if ( !session || !mode || !this.languageProviders[mode] ) return; return session.getMode()["$id"].replace("ace/mode/", "");
}
public closeDocument(session: any) {
if ( !session ) return;
let mode = this.getMode(session);
if ( !this.languageProviders[mode] ) return;
this.languageProviders[mode].closeDocument(session); this.languageProviders[mode].closeDocument(session);
} }

View File

@@ -9,11 +9,7 @@ import { ServiceMessage } from '../../../types/service-message.type';
providedIn: 'root' providedIn: 'root'
}) })
export class MarkdownPreviewService { export class MarkdownPreviewService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1); private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
constructor() {
}
public sendMessage(data: ServiceMessage): void { public sendMessage(data: ServiceMessage): void {

View File

@@ -11,10 +11,6 @@ export class FilesModalService {
private addFileSubject: ReplaySubject<string> = new ReplaySubject<string>(1); private addFileSubject: ReplaySubject<string> = new ReplaySubject<string>(1);
constructor() {
}
public showFilesModal(): void { public showFilesModal(): void {
this.showFilesModalSubject.next(null); this.showFilesModalSubject.next(null);
} }

View File

@@ -12,10 +12,6 @@ export class SearchReplaceService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1); private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
constructor() {
}
public sendMessage(data: ServiceMessage): void { public sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data); this.messageSubject.next(data);
} }

View File

@@ -11,15 +11,13 @@ import { ServiceMessage } from '../../../types/service-message.type';
providedIn: 'root' providedIn: 'root'
}) })
export class TabsService { export class TabsService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1); private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
private editorsService: EditorsService = inject(EditorsService); private editorsService: EditorsService = inject(EditorsService);
tabs: any[] = []; tabs: any[] = [];
newIndex: number = -1; newIndex: number = -1;
constructor() {}
public push(tabData: {}): void { public push(tabData: {}): void {
this.tabs.push(tabData); this.tabs.push(tabData);
@@ -45,45 +43,21 @@ export class TabsService {
} }
public getLeftSiblingTab(fpath: string): string { public getLeftSiblingTab(fpath: string): string {
let size = this.tabs.length; if (this.tabs.length === 0 ) return;
let i = 0;
for (; i < size; i++) { let i = this.tabs.indexOf(fpath);
if (this.tabs[i].path == fpath) {
break;
}
}
if ( !(size > 1) ) { (i === 0) ? i = this.tabs.length - 1 : i -= 1;
return ""; return this.tabs[i].path;
}
if ( i === 0 ) {
return this.tabs[i + 1].path;
}
return this.tabs[i - 1].path;
} }
public getRightSiblingTab(fpath: string): string { public getRightSiblingTab(fpath: string): string {
let size = this.tabs.length; if (this.tabs.length === 0 ) return;
let i = 0;
for (; i < size; i++) { let i = this.tabs.indexOf(fpath);
if (this.tabs[i].path == fpath) {
break;
}
}
if ( !(size > 1) ) { (i === (this.tabs.length - 1)) ? i = 0 : i += 1;
return ""; return this.tabs[i].path;
}
if ( i === (size - 1) ) {
return this.tabs[i - 1].path;
}
return this.tabs[i + 1].path;
} }
public setNewTargetIndex(fpath: string): void { public setNewTargetIndex(fpath: string): void {

View File

@@ -5,6 +5,7 @@ import { EditSession, UndoManager } from 'ace-builds';
import { getModeForPath } from 'ace-builds/src-noconflict/ext-modelist'; import { getModeForPath } from 'ace-builds/src-noconflict/ext-modelist';
import { TabsService } from './editor/tabs/tabs.service'; import { TabsService } from './editor/tabs/tabs.service';
import { ColorTokenizerService } from './color-tokenizer.service';
import { NewtonFile } from '../types/file.type'; import { NewtonFile } from '../types/file.type';
import { ServiceMessage } from '../types/service-message.type'; import { ServiceMessage } from '../types/service-message.type';
@@ -15,16 +16,11 @@ 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); private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
private tabsService: TabsService = inject(TabsService); private tabsService: TabsService = inject(TabsService);
files: Map<string, NewtonFile>; files: Map<string, NewtonFile> = new Map();
constructor() {
this.files = new Map<string, NewtonFile>();
}
public get(path: string): NewtonFile { public get(path: string): NewtonFile {
@@ -39,9 +35,27 @@ export class FilesService {
return [...this.files.values()]; return [...this.files.values()];
} }
public delete(file: NewtonFile) { public getPreviousFile(path: string): NewtonFile {
let paths = this.getAllPaths();
if (paths.length === 0 ) return;
let i = paths.indexOf(path);
(i === 0) ? i = paths.length - 1 : i -= 1;
return this.files.get( paths[i] );
}
public getNextFile(path: string): NewtonFile {
let paths = this.getAllPaths();
if (paths.length === 0 ) return;
let i = paths.indexOf(path);
(i === (paths.length - 1)) ? i = 0 : i += 1;
return this.files.get( paths[i] );
}
public unset(file: NewtonFile) {
file.session.destroy(); file.session.destroy();
window.fs.closeFile(file.path); window?.fs.closeFile(file.path);
this.files.delete(file.path); this.files.delete(file.path);
} }
@@ -62,7 +76,7 @@ export class FilesService {
): Promise<NewtonFile | undefined | null> { ): Promise<NewtonFile | undefined | null> {
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
const file = files[i]; const file = files[i];
const path = window.fs.getPathForFile(file); const path = window?.fs.getPathForFile(file);
if (!file || !path) continue; if (!file || !path) continue;
if ( this.files.get(path) ) continue; if ( this.files.get(path) ) continue;
@@ -87,15 +101,22 @@ export class FilesService {
file.hash = btoa(file.path); file.hash = btoa(file.path);
if (loadFileContents) if (loadFileContents)
data = await window.fs.getFileContents(file.path); data = await window?.fs.getFileContents(file.path);
file.session = new EditSession(data); file.session = new EditSession(data);
file.session["id"] = path;
file.session.setUndoManager( new UndoManager() ); file.session.setUndoManager( new UndoManager() );
file.session.setMode( getModeForPath( file.path ).mode ); file.session.setMode( getModeForPath( file.path ).mode );
file.session["lspConfig"] = {
filePath: path,
joinWorkspaceURI: false
}
file.session["colorTokenizer"] = new ColorTokenizerService();
file.session["colorTokenizer"].parse(data);
this.files.set(file.path, file); this.files.set(file.path, file);
} catch (error) { } catch (error) {
console.log( console.error(
`---- Error ----\nPath: ${path}\nMessage: ${error}` `---- Error ----\nPath: ${path}\nMessage: ${error}`
); );
} }

View File

@@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class WebsocketService {
private socket$!: WebSocketSubject<any>;
connect(url: string): Observable<any> {
if (!this.socket$ || this.socket$.closed) {
this.socket$ = webSocket(
{
url,
deserializer: msg => msg.data
}
);
}
return this.socket$.asObservable();
}
send(message: any) {
if (this.socket$) {
this.socket$.next(message);
}
}
close() {
if (this.socket$) {
this.socket$.complete();
}
}
}

View File

@@ -1,4 +1,10 @@
import { Directive, ElementRef, Input, ViewChild, inject } from '@angular/core'; import {
Directive,
ElementRef,
Input,
ViewChild,
inject
} from '@angular/core';
import * as uuid from 'uuid'; import * as uuid from 'uuid';
import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.service'; import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.service';
@@ -46,9 +52,8 @@ export class CodeViewBase {
public debounceId: number = -1; public debounceId: number = -1;
public debounceWait: number = 800; public debounceWait: number = 800;
@ViewChild('contextMenu') contextMenu!: ElementRef;
constructor() { public showContextMenu: boolean = false;
}
public selectLeftEditor() { public selectLeftEditor() {
@@ -182,7 +187,7 @@ export class CodeViewBase {
} }
public toggleFullScreen() { public toggleFullScreen() {
window.main.toggleFullScreen(); window?.main.toggleFullScreen();
} }
public setAsReadOnly() { public setAsReadOnly() {
@@ -200,8 +205,8 @@ export class CodeViewBase {
this.editor.setHighlightGutterLine(false); this.editor.setHighlightGutterLine(false);
this.editor.setShowFoldWidgets(false); this.editor.setShowFoldWidgets(false);
this.editor.setShowPrintMargin(false); this.editor.setShowPrintMargin(false);
this.editor.session.setUseWrapMode(true);
this.editorElm.nativeElement.parentElement.classList.remove("scroller");
this.editorElm.nativeElement.parentElement.classList.add("col-1"); this.editorElm.nativeElement.parentElement.classList.add("col-1");
this.editorElm.nativeElement.parentElement.classList.add("zero-margin-padding"); this.editorElm.nativeElement.parentElement.classList.add("zero-margin-padding");
@@ -295,27 +300,30 @@ export class CodeViewBase {
startDir = pathParts.join( '/' ); startDir = pathParts.join( '/' );
} }
window.fs.openFiles(startDir); window?.fs.openFiles(startDir);
} }
protected saveFile() { protected saveFile() {
if (!this.activeFile) { if (!this.activeFile) {
this.saveFileAs(); this.saveFileAs();
this.activeFile.session.getUndoManager().markClean();
return; return;
} }
const text = this.activeFile.session.getValue(); const text = this.activeFile.session.getValue();
window.fs.saveFile(this.activeFile.path, text); window?.fs.saveFile(this.activeFile.path, text);
this.activeFile.session.getUndoManager().markClean();
} }
protected saveFileAs() { protected saveFileAs() {
window.fs.saveFileAs().then((path: string) => { window?.fs.saveFileAs().then((path: string) => {
if (!path) return; if (!path) return;
let file: NewtonFile = new File([""], path, {}); let file: NewtonFile = new File([""], path, {});
const text = this.editor.session.getValue(); const text = this.editor.session.getValue();
window.fs.saveFile(path, text); window?.fs.saveFile(path, text);
this.filesService.addFile( this.filesService.addFile(
path, path,
file, file,
@@ -356,6 +364,6 @@ export class CodeViewBase {
} }
private quit() { private quit() {
window.main.quit(); window?.main.quit();
} }
} }

View File

@@ -1,19 +1,3 @@
/*
.editor {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.editor {
height: 100vh;
width: auto;
}
*/
.editor { .editor {
position: relative; position: relative;
height: 100%; height: 100%;

View File

@@ -1,2 +1,15 @@
<div class="editor" #editor > <div class="editor" #editor >
</div> </div>
<ul #contextMenu
class="contextMenu"
[hidden]="!showContextMenu"
(blur)="hideContextMenu()"
(click)="contextMenuClicked($event)"
>
<li command="cutText">Cut</li>
<li command="copyText">Copy</li>
<li command="pasteText">Paste</li>
<hr/>
<li command="prettyJSON">Prettify JSON</li>
</ul>

View File

@@ -2,6 +2,10 @@ import { Component } from "@angular/core";
// Import Ace and its modes/themes so that `ace` global is defined // Import Ace and its modes/themes so that `ace` global is defined
import * as ace from "ace-builds/src-min-noconflict/ace"; import * as ace from "ace-builds/src-min-noconflict/ace";
// Note: https://github.com/mkslanc/ace-linters/blob/c286d85c558530aa1b0597d02108bc782abd4736/packages/demo/file-api-websockets/client.ts#L27
// import { AceLayout, Box, TabManager, Button, dom, AceTreeWrapper, FileSystemWeb, Pane, AceEditor, Tab } from "ace-layout";
import "ace-builds/src-min-noconflict/ext-settings_menu"; import "ace-builds/src-min-noconflict/ext-settings_menu";
import "ace-builds/src-min-noconflict/ext-keybinding_menu"; import "ace-builds/src-min-noconflict/ext-keybinding_menu";
import "ace-builds/src-min-noconflict/ext-command_bar"; import "ace-builds/src-min-noconflict/ext-command_bar";
@@ -13,11 +17,15 @@ import "ace-builds/src-min-noconflict/ext-language_tools";
// import "ace-builds/src-min-noconflict/theme-penguins_in_space"; // import "ace-builds/src-min-noconflict/theme-penguins_in_space";
import "ace-builds/src-min-noconflict/theme-gruvbox"; import "ace-builds/src-min-noconflict/theme-gruvbox";
// https://www.npmjs.com/package/web-tree-sitter
// import { Language, Parser } from 'web-tree-sitter';
import { CodeViewBase } from './view.base'; import { CodeViewBase } from './view.base';
import { NewtonFile } from '../../common/types/file.type'; import { NewtonFile } from '../../common/types/file.type';
import { EditorType } from '../../common/types/editor.type'; import { EditorType } from '../../common/types/editor.type';
import { ServiceMessage } from '../../common/types/service-message.type'; import { ServiceMessage } from '../../common/types/service-message.type';
import { ButtonMap } from '../../common/constants/button.map';
@@ -38,6 +46,20 @@ export class CodeViewComponent extends CodeViewBase {
constructor() { constructor() {
super(); super();
// const { Parser } = window.TreeSitter;
// const { Parser } = TreeSitter;
// console.log(treeSitter);
// treeSitter.Parser.init().then(() => {
// console.log("Parser ready...");
// });
// const parser = new Parser();
// const JavaScript = await Language.load('/path/to/tree-sitter-javascript.wasm');
// Language.load('resources/wasm/tree-sitter-javascript.wasm').then((language) => {
// console.log(language);
// });
this.aceApi = ace; this.aceApi = ace;
} }
@@ -59,8 +81,8 @@ export class CodeViewComponent extends CodeViewBase {
this.editor = ace.edit( this.editorElm.nativeElement ); this.editor = ace.edit( this.editorElm.nativeElement );
this.editor.setOptions( this.editorSettings.CONFIG ); this.editor.setOptions( this.editorSettings.CONFIG );
if (this.isDefault) {
this.editorsService.set(this.uuid, this); this.editorsService.set(this.uuid, this);
if (this.isDefault) {
this.editorsService.setActiveEditor(this.uuid); this.editorsService.setActiveEditor(this.uuid);
this.addActiveStyling(); this.addActiveStyling();
this.editor.focus(); this.editor.focus();
@@ -120,7 +142,7 @@ export class CodeViewComponent extends CodeViewBase {
private loadNewtonEventBindings(): void { private loadNewtonEventBindings(): void {
// Note: https://ajaxorg.github.io/ace-api-docs/interfaces/ace.Ace.EditorEvents.html // Note: https://ajaxorg.github.io/ace-api-docs/interfaces/ace.Ace.EditorEvents.html
this.editor.on("focus", (e) => { this.editor.on("focus", (event) => {
let message = new ServiceMessage(); let message = new ServiceMessage();
message.action = "set-active-editor"; message.action = "set-active-editor";
message.editorUUID = this.uuid; message.editorUUID = this.uuid;
@@ -128,20 +150,45 @@ export class CodeViewComponent extends CodeViewBase {
this.editorsService.sendMessage(message); this.editorsService.sendMessage(message);
this.searchReplaceService.sendMessage(message); this.searchReplaceService.sendMessage(message);
this.editorsService.sendMessage(message);
message = new ServiceMessage(); message = new ServiceMessage();
message.action = "set-active-editor"; message.action = "set-active-editor";
message.rawData = this; message.rawData = this;
this.lspManagerService.sendMessage(message);
this.markdownPreviewService.sendMessage(message); this.markdownPreviewService.sendMessage(message);
message = new ServiceMessage();
message.action = "highlight-active-tab";
message.filePath = this.activeFile?.path;
this.tabsService.sendMessage(message);
this.updateInfoBar(); this.updateInfoBar();
}); });
this.editor.on("click", () => { this.editor.on("click", (event) => {
this.updateInfoBar(); this.updateInfoBar();
}); });
this.editor.addEventListener("mousedown", (event) => {
if (ButtonMap.LEFT === event.domEvent.button) {
this.showContextMenu = false;
} else if (ButtonMap.RIGHT === event.domEvent.button) {
let menuElm = this.contextMenu.nativeElement;
let pageX = event.domEvent.pageX;
let pageY = event.domEvent.pageY;
const origin = {
left: pageX + 5,
top: pageY - 5
};
menuElm.style.left = `${origin.left}px`;
menuElm.style.top = `${origin.top}px`;
this.showContextMenu = true;
}
});
this.editor.on("input", () => { this.editor.on("input", () => {
this.updateInfoBar(); this.updateInfoBar();
}); });
@@ -161,25 +208,71 @@ export class CodeViewComponent extends CodeViewBase {
} }
}); });
this.editor.on("mousewheel", (event) => {
if (event.domEvent.ctrlKey && event.domEvent.deltaY < 0) {
event.preventDefault();
event.stopPropagation();
this.zoomIn();
} else if (event.domEvent.ctrlKey && event.domEvent.deltaY > 0) {
event.preventDefault();
event.stopPropagation();
this.zoomOut();
}
});
this.editor.on("change", () => { this.editor.on("change", () => {
if (this.debounceId) { clearTimeout(this.debounceId); } if (this.debounceId) { clearTimeout(this.debounceId); }
this.setDebounceTimeout(); this.setDebounceTimeout();
if (!this.activeFile) return; if (!this.activeFile) return;
const isClean = this.activeFile.session.getUndoManager().isClean();
const hasUndo = this.activeFile.session.getUndoManager().hasUndo();
let message = new ServiceMessage(); let message = new ServiceMessage();
message.action = "file-changed"; message.action = (!isClean && hasUndo) ? "file-changed" : "file-unmodified";
message.filePath = this.activeFile.path; message.filePath = this.activeFile.path;
this.tabsService.sendMessage(message); this.tabsService.sendMessage(message);
}); });
this.editor.on("changeSession", (session) => { this.editor.on("changeSession", (session) => {
let message = new ServiceMessage();
message.action = "editor-update";
message.rawData = this;
this.lspManagerService.sendMessage(message);
message = new ServiceMessage();
message.action = "highlight-active-tab";
message.filePath = this.activeFile?.path;
this.tabsService.sendMessage(message);
this.updateInfoBar(); this.updateInfoBar();
}); });
} }
public prettyJSON() {
let data = JSON.parse(this.editor.session.getValue());
this.editor.session.setValue(
JSON.stringify(data, null, 4)
);
}
public hideContextMenu() {
this.showContextMenu = false;
}
public contextMenuClicked(event: any) {
this.showContextMenu = false;
const command = event.target.getAttribute("command");
const args = event.target.getAttribute("args");
if (!command) return;
this[command]( (args) ? args : null );
}
public assignSession(file: NewtonFile) { public assignSession(file: NewtonFile) {
if (!file) return; if (!file) return;
@@ -192,8 +285,15 @@ export class CodeViewComponent extends CodeViewBase {
this.activeFile = file; this.activeFile = file;
let session = this.aceApi.createEditSession(file.session.getValue()); let session = this.aceApi.createEditSession(file.session.getValue());
session["$config"] = {
"lsp": {
"filePath": file.path
}
}
session.setMode( file.session.getMode()["$id"] ); session.setMode( file.session.getMode()["$id"] );
session.setUseWrapMode(true);
this.editor.setSession(session); this.editor.setSession(session);
} }

View File

@@ -7,4 +7,4 @@
<code-view [mode]="'mini-map'"></code-view> <code-view [mode]="'mini-map'"></code-view>
</div> </div>
<div> </div>

View File

@@ -1,5 +1,5 @@
import { Component, inject } from '@angular/core'; import { Component, DestroyRef, inject } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
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 { TabsService } from '../common/services/editor/tabs/tabs.service';
@@ -29,7 +29,7 @@ import { ServiceMessage } from '../common/types/service-message.type';
} }
}) })
export class EditorsComponent { export class EditorsComponent {
private unsubscribe: Subject<void> = new Subject(); readonly #destroyRef: DestroyRef = inject(DestroyRef);
private editorsService: EditorsService = inject(EditorsService); private editorsService: EditorsService = inject(EditorsService);
private tabsService: TabsService = inject(TabsService); private tabsService: TabsService = inject(TabsService);
@@ -37,167 +37,46 @@ export class EditorsComponent {
constructor() { constructor() {
}
private ngAfterViewInit(): void {
this.loadSubscribers(); this.loadSubscribers();
this.loadMainSubscribers(); this.loadMainSubscribers();
} }
private ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
private loadSubscribers() { private loadSubscribers() {
this.editorsService.getMessage$().pipe( this.editorsService.getMessage$().pipe(
takeUntil(this.unsubscribe) takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => { ).subscribe((message: ServiceMessage) => {
if (message.action === "select-left-editor") { switch ( message.action ) {
let editorComponent = this.editorsService.get(message.editorUUID); case "select-left-editor":
if (!editorComponent.leftSiblingUUID) return; this.selectLeftEditor(message);
let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID); break;
siblingComponent.editor.focus() case "select-right-editor":
} else if (message.action === "select-right-editor") { this.selectRightEditor(message);
let editorComponent = this.editorsService.get(message.editorUUID); break;
if (!editorComponent.rightSiblingUUID) return; case "move-session-left":
let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID); this.moveSessionLeft(message);
siblingComponent.editor.focus() break;
} else if (message.action === "move-session-left") { case "move-session-right":
let editorComponent = this.editorsService.get(message.editorUUID); this.moveSessionRight(message);
if (!editorComponent.leftSiblingUUID) return; break;
case "set-active-editor":
let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID); this.setActiveEditor(message);
let session = editorComponent.editor.getSession(); break;
let siblingSession = siblingComponent.editor.getSession(); case "set-tab-to-editor":
this.setTabToEditor(message);
if (session == siblingSession) return; break;
case "close-tab":
siblingComponent.assignSession(editorComponent.activeFile); this.closeTab(message);
break;
let targetPath = this.tabsService.getRightSiblingTab( default:
editorComponent.activeFile.path break;
)
if (targetPath) {
editorComponent.assignSession(
this.filesService.get(targetPath)
);
} else {
editorComponent.newFile();
} }
siblingComponent.editor.focus()
} 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);
let session = editorComponent.editor.getSession();
let siblingSession = siblingComponent.editor.getSession();
if (session == siblingSession) return;
siblingComponent.assignSession(editorComponent.activeFile);
let targetPath = this.tabsService.getRightSiblingTab(
editorComponent.activeFile.path
)
if (targetPath) {
editorComponent.assignSession(
this.filesService.get(targetPath)
);
} else {
editorComponent.newFile();
}
siblingComponent.editor.focus()
} else if (message.action === "set-active-editor") {
this.editorsService.getActiveEditorComponent().removeActiveStyling();
this.editorsService.setActiveEditor(message.editorUUID);
this.editorsService.getActiveEditorComponent().addActiveStyling();
} else if (message.action === "set-tab-to-editor") {
let file = this.filesService.get(message.filePath);
let editorComponent = this.editorsService.getActiveEditorComponent();
let editor = editorComponent.editor;
editorComponent.assignSession(file);
this.editorsService.miniMapView.cloneSession(file);
} else if (message.action === "close-tab") {
let activeComponent = this.editorsService.getActiveEditorComponent();
let editors = this.editorsService.getEditorsAsArray();
let file = this.filesService.get(message.filePath);
for (let i = 0; i < editors.length; i++) {
let editorComponent = editors[i];
if (editorComponent.editor.session == file.session) {
if (activeComponent == editorComponent) {
this.editorsService.miniMapView.newFile();
}
editorComponent.newFile();
}
}
this.filesService.delete(file);
}
}); });
} }
private loadMainSubscribers() { private loadMainSubscribers() {
window.fs.onLoadFiles(async (paths: []) => { window?.main.onMenuActions(async (action: string) => {
for (let i = 0; i < paths.length; i++) {
let file = new File([], "") as NewtonFile;
if ( this.filesService.get(paths[i]) ) continue;
await this.filesService.addFile(paths[i], file);
this.filesService.addTab(file);
}
let path = paths[ paths.length - 1 ];
let file = this.filesService.get(path);
this.editorsService.setSession(file);
});
window.fs.onChangedFile(async (path: string, data: string) => {
let file = this.filesService.get(path);
file.session.setValue(data);
// Note: fake 'save' event to not show as changed iven external save happened...
let message = new ServiceMessage();
message.action = "file-saved";
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) => {
let editorComponent = this.editorsService.getActiveEditorComponent(); let editorComponent = this.editorsService.getActiveEditorComponent();
let editor = editorComponent.editor; let editor = editorComponent.editor;
@@ -232,10 +111,65 @@ export class EditorsComponent {
editor.showSettingsMenu(); editor.showSettingsMenu();
case "show-about": case "show-about":
break; break;
case "quit":
window?.main.quit();
break;
default: default:
editor.execCommand(action); editor.execCommand(action);
break;
} }
}); });
window?.fs.onLoadFiles(async (paths: []) => {
for (let i = 0; i < paths.length; i++) {
let file = new File([], "") as NewtonFile;
if ( this.filesService.get(paths[i]) ) continue;
await this.filesService.addFile(paths[i], file);
this.filesService.addTab(file);
}
let path = paths[ paths.length - 1 ];
let file = this.filesService.get(path);
this.editorsService.setSession(file);
});
window?.fs.onChangedFile(async (path: string, data: string) => {
let file = this.filesService.get(path);
file.session.setValue(data);
// Note: fake 'save' event to not show as changed iven external save happened...
let message = new ServiceMessage();
message.action = "file-saved";
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("TODO (onUpdateFilePath) :", path);
// this.tabsService.sendMessage(message);
// this.filesService.sendMessage(message);
});
} }
protected onFileDropped(files: any) { protected onFileDropped(files: any) {
@@ -248,4 +182,104 @@ export class EditorsComponent {
}); });
} }
private selectLeftEditor(message: ServiceMessage) {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.leftSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID);
siblingComponent.editor.focus();
}
private selectRightEditor(message: ServiceMessage) {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.rightSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID);
siblingComponent.editor.focus();
}
private moveSessionLeft(message: ServiceMessage) {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.leftSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID);
this.moveSession("left", editorComponent, siblingComponent);
}
private moveSessionRight(message: ServiceMessage) {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.rightSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID);
this.moveSession("right", editorComponent, siblingComponent);
}
private moveSession(
direction: string,
editorComponent: CodeViewComponent,
siblingComponent: CodeViewComponent
) {
let session = editorComponent.editor.getSession();
let siblingSession = siblingComponent.editor.getSession();
if (session == siblingSession) return;
let targetPath: string = this.tabsService.getRightSiblingTab(
editorComponent.activeFile.path
);
siblingComponent.assignSession(editorComponent.activeFile);
if (targetPath) {
editorComponent.assignSession(
this.filesService.get(targetPath)
);
} else {
editorComponent.newFile();
}
siblingComponent.editor.focus()
}
private setActiveEditor(message: ServiceMessage) {
this.editorsService.getActiveEditorComponent().removeActiveStyling();
this.editorsService.setActiveEditor(message.editorUUID);
this.editorsService.getActiveEditorComponent().addActiveStyling();
}
private setTabToEditor(message: ServiceMessage) {
let file = this.filesService.get(message.filePath);
let editorComponent = this.editorsService.getActiveEditorComponent();
let editor = editorComponent.editor;
editorComponent.assignSession(file);
this.editorsService.miniMapView.cloneSession(file);
}
private closeTab(message: ServiceMessage) {
let activeComponent = this.editorsService.getActiveEditorComponent();
let editors = this.editorsService.getEditorsAsArray();
let file = this.filesService.get(message.filePath);
for (let i = 0; i < editors.length; i++) {
let editorComponent = editors[i];
if (editorComponent.editor.session !== file.session) continue;
let targetFile = this.filesService.getPreviousFile(file.path)
if (targetFile && (targetFile.path !== message.filePath)) {
editorComponent.assignSession(targetFile);
if (activeComponent == editorComponent) {
this.editorsService.miniMapView.cloneSession(targetFile);
}
} else {
editorComponent.newFile();
if (activeComponent == editorComponent) {
this.editorsService.miniMapView.newFile();
}
}
}
activeComponent.lspManagerService.closeDocument(file.session);
this.filesService.unset(file);
}
} }

View File

@@ -1,5 +1,5 @@
import { Component, inject } from '@angular/core'; import { Component, DestroyRef, inject } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.service'; import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.service';
@@ -17,7 +17,7 @@ import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.s
} }
}) })
export class InfoBarComponent { export class InfoBarComponent {
private unsubscribe: Subject<void> = new Subject(); readonly #destroyRef: DestroyRef = inject(DestroyRef);
private infoBarService: InfoBarService = inject(InfoBarService); private infoBarService: InfoBarService = inject(InfoBarService);
@@ -28,18 +28,14 @@ export class InfoBarComponent {
ftype: string = ""; ftype: string = "";
constructor() {} constructor() {
private ngAfterViewInit(): void {
this.loadSubscribers(); this.loadSubscribers();
} }
private loadSubscribers() { private loadSubscribers() {
this.infoBarService.updateInfoBarFPath$().pipe( this.infoBarService.updateInfoBarFPath$().pipe(
takeUntil(this.unsubscribe) takeUntilDestroyed(this.#destroyRef)
).subscribe((fpath: string) => { ).subscribe((fpath: string) => {
this.fpath = fpath; this.fpath = fpath;
let _path = fpath; let _path = fpath;
@@ -52,19 +48,19 @@ export class InfoBarComponent {
}); });
this.infoBarService.updateInfoBarCursorPos$().pipe( this.infoBarService.updateInfoBarCursorPos$().pipe(
takeUntil(this.unsubscribe) takeUntilDestroyed(this.#destroyRef)
).subscribe((cursorPos: any) => { ).subscribe((cursorPos: any) => {
this.cursorPos = `${cursorPos.row + 1}:${cursorPos.column}`; this.cursorPos = `${cursorPos.row + 1}:${cursorPos.column}`;
}); });
this.infoBarService.updateInfoBarEncodeing$().pipe( this.infoBarService.updateInfoBarEncodeing$().pipe(
takeUntil(this.unsubscribe) takeUntilDestroyed(this.#destroyRef)
).subscribe((encodeing: string) => { ).subscribe((encodeing: string) => {
this.encodeing = encodeing; this.encodeing = encodeing;
}); });
this.infoBarService.updateInfoBarFType$().pipe( this.infoBarService.updateInfoBarFType$().pipe(
takeUntil(this.unsubscribe) takeUntilDestroyed(this.#destroyRef)
).subscribe((ftype: string) => { ).subscribe((ftype: string) => {
let mode = ftype.split("/"); let mode = ftype.split("/");
this.ftype = mode[ mode.length - 1 ]; this.ftype = mode[ mode.length - 1 ];

View File

@@ -2,3 +2,10 @@
display: grid; display: grid;
min-height: 25em; min-height: 25em;
} }
.clear-left-padding {
padding-left: 0px;
}
.clear-right-padding {
padding-right: 0px;
}

View File

@@ -2,26 +2,76 @@
<div class="row mt-2 mb-3"> <div class="row mt-2 mb-3">
<div class="col"> <div class="col clear-right-padding">
<div class="input-group-sm"> <div class="input-group-sm"
<input class="form-control" placeholder="Project Path..." /> (mouseup)="handleActionMouseUp($event)"
>
<label class="form-control" [innerText]="lspManagerService.workspaceFolder || 'Project Path...'">
</label>
</div> </div>
</div> </div>
<div class="col col-auto clear-left-padding">
<button class="btn btn-sm btn-dark" (click)="clearWorkspaceFolder()">x</button>
</div>
<div class="col col-auto"> <div class="col col-auto">
<div class="input-group-sm"> <div class="input-group-sm">
<button class="btn btn-sm btn-dark">Choose Directory</button> <button class="btn btn-sm btn-dark" (click)="setWorkspaceFolder()">Workspace Folder</button>
<button class="btn btn-sm btn-danger ms-5" (click)="hideLspManager()">X</button>
</div> </div>
</div> </div>
</div> </div>
<div class="row mt-2 md-2">
<div class="col">
LSP Configs:
</div>
</div>
<div class="row">
<div class="col">
<code-view #lspEditorComponent [mode]="'standalone'" class="lsp-config-text"></code-view>
</div>
</div>
<div class="row mt-2 md-2">
<div class="col col-sm" [hidden]="!lspManagerService.workspaceFolder">
<button class="btn btn-sm btn-dark" (click)="createLanguageClient()">Create Language Client</button>
<button class="btn btn-sm btn-dark" (click)="closeLanguageClient()">Close Language Client</button>
</div>
<div class="col">
Target Editor: <label [innerText]="editor?.id || '...'"></label>
</div>
<div class="col-sm" [hidden]="!lspManagerService.workspaceFolder">
<button class="btn btn-sm btn-dark"
(click)="registerEditorToLanguageClient()">
Register Editor To LSP
</button>
</div>
<div class="col">
Active Editor Session:
</div>
</div>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<code-view #editorComponent [mode]="'standalone'" class="lsp-config-text"></code-view> <code-view #sessionEditorComponent [mode]="'read-only'" class="lsp-config-text"></code-view>
</div> </div>
</div> </div>
</div> </div>
<ul #contextMenu
class="contextMenu"
[hidden]="!showContextMenu"
(blur)="hideContextMenu"
(click)="contextMenuClicked($event)"
>
<li command="pasteText">Paste</li>
</ul>

View File

@@ -1,5 +1,12 @@
import { Component, ElementRef, HostBinding, ViewChild, inject } from '@angular/core'; import {
import { Subject, takeUntil } from 'rxjs'; Component,
ChangeDetectorRef,
DestroyRef,
ElementRef,
HostBinding,
ViewChild,
inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { LspManagerService } from '../../common/services/editor/lsp-manager/lsp-manager.service'; import { LspManagerService } from '../../common/services/editor/lsp-manager/lsp-manager.service';
@@ -7,6 +14,8 @@ import { CodeViewComponent } from '../code-view/view.component';
import { ServiceMessage } from '../../common/types/service-message.type'; import { ServiceMessage } from '../../common/types/service-message.type';
import { ButtonMap } from '../../common/constants/button.map';
@Component({ @Component({
@@ -23,66 +32,175 @@ import { ServiceMessage } from '../../common/types/service-message.type';
} }
}) })
export class LspManagerComponent { export class LspManagerComponent {
private unsubscribe: Subject<void> = new Subject(); readonly #destroyRef = inject(DestroyRef);
private changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
private lspManagerService: LspManagerService = inject(LspManagerService); lspManagerService: LspManagerService = inject(LspManagerService);
@HostBinding("class.hidden") isHidden: boolean = true; @HostBinding("class.hidden") isHidden: boolean = true;
@ViewChild('editorComponent') editorComponent!: CodeViewComponent; @ViewChild('lspEditorComponent') lspEditorComponent!: CodeViewComponent;
lspTextEditor!: any; @ViewChild('sessionEditorComponent') sessionEditorComponent!: CodeViewComponent;
private editor: any; lspTextEditor: any;
innerEditor: any;
editor: any;
activeFile: any;
@ViewChild('contextMenu') contextMenu!: ElementRef;
public showContextMenu: boolean = false;
constructor() { constructor() {
this.loadSubscribers();
} }
private ngAfterViewInit(): void { private ngAfterViewInit(): void {
this.lspTextEditor = this.editorComponent.editor; this.mapEditorsAndLoadConfig();
}
private mapEditorsAndLoadConfig() {
this.lspTextEditor = this.lspEditorComponent.editor;
this.innerEditor = this.sessionEditorComponent.editor;
this.lspTextEditor.on("input", () => {
this.lspManagerService.lspConfigDataStr =
this.lspTextEditor.session.getValue();
});
this.lspManagerService.loadLspConfigData().then((lspConfigData) => { this.lspManagerService.loadLspConfigData().then((lspConfigData) => {
this.lspTextEditor.session.setMode("ace/mode/json"); this.lspTextEditor.session.setMode("ace/mode/json");
this.lspTextEditor.session.setValue(lspConfigData); this.lspTextEditor.session.setValue(lspConfigData);
}); });
this.loadSubscribers();
}
private ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
} }
private loadSubscribers() { private loadSubscribers() {
this.lspManagerService.getMessage$().pipe( this.lspManagerService.getMessage$().pipe(
takeUntil(this.unsubscribe) takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => { ).subscribe((message: ServiceMessage) => {
if (message.action === "toggle-lsp-manager") { if (message.action === "toggle-lsp-manager") {
this.toggleLspManager(message); this.toggleLspManager(message);
} else if (message.action === "set-active-editor") { } else if (message.action === "set-active-editor") {
this.setActiveEditor(message); this.setActiveEditor(message);
} else if (message.action === "editor-update") {
this.editorUpdate(message);
} else if (message.action === "close-file") {
this.closeFile(message);
} }
}); });
} }
public hideLspManager() { protected handleActionMouseUp(event: any): void {
this.isHidden = true; if (ButtonMap.LEFT === event.button) return;
this.editor.focus();
let target = event.target;
let menuElm = this.contextMenu.nativeElement;
let pageX = event.clientX;
let pageY = event.clientY;
const origin = {
left: pageX + 5,
top: pageY - 5
};
menuElm.style.left = `${origin.left}px`;
menuElm.style.top = `${origin.top}px`;
this.showContextMenu = true;
} }
public hideContextMenu() {
this.showContextMenu = false;
}
public contextMenuClicked(event: any) {
this.showContextMenu = false;
const command = event.target.getAttribute("command");
const args = event.target.getAttribute("args");
if (!command) return;
this[command]( (args) ? args : null );
}
public pasteText() {
navigator.clipboard.readText().then((pasteText) => {
if (pasteText.includes("\n") || !pasteText.startsWith("/")) return;
this.lspManagerService.workspaceFolder = pasteText;
});
}
public clearWorkspaceFolder() {
this.lspManagerService.workspaceFolder = "";
}
public setWorkspaceFolder() {
window?.fs.chooseFolder().then((folder: string) => {
if (!folder) return;
this.lspManagerService.workspaceFolder = folder;
});
}
public createLanguageClient() {
let mode = this.lspManagerService.getMode(this.editor.session);
this.lspManagerService.createLanguageProviderWithClientServer(mode);
}
public closeLanguageClient() {
let mode = this.lspManagerService.getMode(this.editor.session);
this.lspManagerService.closeLanguageProviderWithClientServer(mode);
}
public registerEditorToLanguageClient() {
this.lspManagerService.registerEditorToLSPClient(this.editor);
}
public globalLspManagerKeyHandler(event: any) { public globalLspManagerKeyHandler(event: any) {
if (event.ctrlKey && event.shiftKey && event.key === "l") { if (event.ctrlKey && event.shiftKey && event.key === "l") {
this.hideLspManager(); this.hideLspManager();
} }
} }
public hideLspManager() {
this.isHidden = true;
this.editor.focus();
}
private toggleLspManager(message: ServiceMessage) { private toggleLspManager(message: ServiceMessage) {
this.isHidden = !this.isHidden; this.isHidden = !this.isHidden;
if (this.isHidden) return;
// Note: hack for issue with setActiveEditor TODO
setTimeout(() => {
this.innerEditor.setSession(this.editor.getSession());
}, 10);
} }
private setActiveEditor(message: ServiceMessage) { private setActiveEditor(message: ServiceMessage) {
this.editor = message.rawData; this.editor = message.rawData.editor;
this.activeFile = message.rawData.activeFile;
// TODO: figure out why this doesn't update the session consistently...
// It seems maybe bound to visible state as change detector ref didn't help either.
// this.innerEditor.setSession(this.editor.session);
}
private editorUpdate(message: ServiceMessage) {
if (
!this.editor ||
!message.rawData.activeFile
) return;
this.editor.setSession(message.rawData.editor.getSession())
this.activeFile = message.rawData.activeFile;
this.lspManagerService.registerSession(this.editor);
}
private closeFile(message: ServiceMessage) {
this.lspManagerService.closeDocument(message.rawData);
} }
} }

View File

@@ -1,5 +1,12 @@
import { Component, HostBinding, inject } from '@angular/core'; import {
import { Subject, takeUntil } from 'rxjs'; Component,
DestroyRef,
HostBinding,
inject
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { marked } from 'marked';
import { MarkdownPreviewService } from '../../common/services/editor/markdown-preview/markdown-preview.service'; import { MarkdownPreviewService } from '../../common/services/editor/markdown-preview/markdown-preview.service';
@@ -19,12 +26,12 @@ import { ServiceMessage } from '../../common/types/service-message.type';
} }
}) })
export class MarkdownPreviewComponent { export class MarkdownPreviewComponent {
private unsubscribe: Subject<void> = new Subject(); readonly #destroyRef: DestroyRef = inject(DestroyRef);
private markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService); private markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService);
@HostBinding("class.hidden") isHidden: boolean = true; @HostBinding("class.hidden") isHidden: boolean = true;
converter: any = new showdown.Converter(); converter: any = marked;
defaultHtml: string = "<h1>NOT a Markdown file...</h1>" defaultHtml: string = "<h1>NOT a Markdown file...</h1>"
bodyHtml: string = ""; bodyHtml: string = "";
@@ -32,26 +39,22 @@ export class MarkdownPreviewComponent {
constructor() { constructor() {
}
private ngAfterViewInit(): void {
this.loadSubscribers(); this.loadSubscribers();
} }
private ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
private loadSubscribers() { private loadSubscribers() {
this.markdownPreviewService.getMessage$().pipe( this.markdownPreviewService.getMessage$().pipe(
takeUntil(this.unsubscribe) takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => { ).subscribe((message: ServiceMessage) => {
if (message.action === "toggle-markdown-preview") { switch ( message.action ) {
case "toggle-markdown-preview":
this.toggleMarkdownPreview(message); this.toggleMarkdownPreview(message);
} else if (message.action === "set-active-editor") { break;
case "set-active-editor":
this.setActiveEditor(message); this.setActiveEditor(message);
break;
default:
break;
} }
}); });
} }
@@ -61,7 +64,7 @@ export class MarkdownPreviewComponent {
setTimeout(() => { setTimeout(() => {
this.updatePreview(); this.updatePreview();
}, 200); }, 10);
} }
private setActiveEditor(message: ServiceMessage) { private setActiveEditor(message: ServiceMessage) {
@@ -84,7 +87,7 @@ export class MarkdownPreviewComponent {
let mdStr = this.editorComponent.editor.session.getValue(); let mdStr = this.editorComponent.editor.session.getValue();
let pathParts = this.editorComponent.activeFile.path.split("/"); let pathParts = this.editorComponent.activeFile.path.split("/");
let basePath = "file://" + pathParts.slice(0, -1).join("/"); let basePath = "file://" + pathParts.slice(0, -1).join("/");
this.bodyHtml = this.converter.makeHtml( this.bodyHtml = this.converter.parse(
mdStr.replaceAll("](images", `](${basePath}/images`) mdStr.replaceAll("](images", `](${basePath}/images`)
.replaceAll("](imgs", `](${basePath}/imgs`) .replaceAll("](imgs", `](${basePath}/imgs`)
.replaceAll("](pictures", `](${basePath}/pictures`) .replaceAll("](pictures", `](${basePath}/pictures`)

View File

@@ -1,7 +1,21 @@
<div class="col"> <div class="col">
<div class="row"> <div class="row">
<div class="col col-3"> <div class="col col-3">
<label id="find-status-lbl">Find in Current File</label> @if (isQueryLong) {
<label id="find-status-lbl">
<b class="error">Query exceeds 80 characters...</b>
</label>
} @else if (isQueryNotFound) {
<label id="find-status-lbl">
<b class="warning">Query not found...</b>
</label>
} @else if (query && !isQueryLong && !isQueryNotFound) {
<label id="find-status-lbl">Found in current file:
<b class="success">{{totalCount}}</b>
</label>
} @else {
<label id="find-status-lbl">Find in Current File:</label>
}
</div> </div>
<div class="col col-4"> <div class="col col-4">
@@ -45,15 +59,27 @@
id="find-entry" id="find-entry"
class="form-control" class="form-control"
type="search" type="search"
(keyup)="searchForString()" (focus)="searchForString()"
(keyup)="findEntryKeyUpHandler($event)"
(input)="searchForString()"
placeholder="Find in current file..." placeholder="Find in current file..."
aria-label="Find in current file..." aria-label="Find in current file..."
/> />
</div> </div>
</div> </div>
<div class="col col-auto"> <div class="col col-auto">
<button id="find-btn" class="width-8em btn btn-sm btn-dark" (click)="findNextEntry()">Find</button> <button
<button id="find-all-btn" class="width-8em btn btn-sm btn-dark" (click)="findAllEntries()">Find All</button> [disabled]="!query || isQueryLong || isQueryNotFound"
id="find-btn"
class="width-8em btn btn-sm btn-dark"
(click)="findNextEntry()">Find
</button>
<button
[disabled]="!query || isQueryLong || isQueryNotFound"
id="find-all-btn"
class="width-8em btn btn-sm btn-dark"
(click)="findAllEntries()">Find All
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -68,15 +94,25 @@
id="replace-entry" id="replace-entry"
class="form-control" class="form-control"
type="search" type="search"
(keyup)="replaceEntry($event)" (keyup.enter)="replaceEntry($event)"
title="Replace in current file..." title="Replace in current file..."
placeholder="Replace in current file..." placeholder="Replace in current file..."
/> />
</div> </div>
</div> </div>
<div class="col col-auto"> <div class="col col-auto">
<button id="replace-btn" class="width-8em btn btn-sm btn-dark" (click)="replaceEntry($event)">Replace</button> <button
<button id="replace-all-btn" class="width-8em btn btn-sm btn-dark" (click)="replaceAll()">Replace All</button> [disabled]="!query || isQueryLong || isQueryNotFound"
id="replace-btn"
class="width-8em btn btn-sm btn-dark"
(click)="replaceEntry($event)">Replace
</button>
<button
[disabled]="!query || isQueryLong || isQueryNotFound"
id="replace-all-btn"
class="width-8em btn btn-sm btn-dark"
(click)="replaceAll()">Replace All
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,5 +1,13 @@
import { Component, ElementRef, HostBinding, Input, ViewChild, inject } from '@angular/core'; import {
import { Subject, takeUntil } from 'rxjs'; Component,
DestroyRef,
ElementRef,
HostBinding,
Input,
ViewChild,
inject
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { SearchReplaceService } from '../../common/services/editor/search-replace/search-replace.service'; import { SearchReplaceService } from '../../common/services/editor/search-replace/search-replace.service';
@@ -20,7 +28,7 @@ import { ServiceMessage } from '../../common/types/service-message.type';
} }
}) })
export class SearchReplaceComponent { export class SearchReplaceComponent {
private unsubscribe: Subject<void> = new Subject(); readonly #destroyRef: DestroyRef = inject(DestroyRef);
private searchReplaceService: SearchReplaceService = inject(SearchReplaceService); private searchReplaceService: SearchReplaceService = inject(SearchReplaceService);
@@ -28,15 +36,19 @@ export class SearchReplaceComponent {
@ViewChild('findEntryElm') findEntryElm!: ElementRef; @ViewChild('findEntryElm') findEntryElm!: ElementRef;
@ViewChild('replaceEntryElm') replaceEntryElm!: ElementRef; @ViewChild('replaceEntryElm') replaceEntryElm!: ElementRef;
@Input() query: string = "";
@Input() findOptions: string = "";
@Input() isQueryLong: boolean = false;
@Input() isQueryNotFound: boolean = false;
@Input() totalCount: number = 0;
private editor!: any; private editor!: any;
@Input() findOptions: string = "";
private useWholeWordSearch: boolean = false; private useWholeWordSearch: boolean = false;
private searchOnlyInSelection: boolean = false; private searchOnlyInSelection: boolean = false;
private useCaseSensitive: boolean = false; private useCaseSensitive: boolean = false;
private useRegex: boolean = false; private useRegex: boolean = false;
private selection: string = ""; private selection: string = "";
private query: string = "";
private toStr: string = ""; private toStr: string = "";
private isBackwards: boolean = false; private isBackwards: boolean = false;
private isWrap: boolean = true; private isWrap: boolean = true;
@@ -45,26 +57,22 @@ export class SearchReplaceComponent {
constructor() { constructor() {
}
private ngAfterViewInit(): void {
this.loadSubscribers(); this.loadSubscribers();
} }
private ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
private loadSubscribers() { private loadSubscribers() {
this.searchReplaceService.getMessage$().pipe( this.searchReplaceService.getMessage$().pipe(
takeUntil(this.unsubscribe) takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => { ).subscribe((message: ServiceMessage) => {
if (message.action === "toggle-search-replace") { switch ( message.action ) {
case "toggle-search-replace":
this.toggleSearchReplace(message); this.toggleSearchReplace(message);
} else if (message.action === "set-active-editor") { break;
case "set-active-editor":
this.setActiveEditor(message); this.setActiveEditor(message);
break;
default:
break;
} }
}); });
} }
@@ -175,17 +183,25 @@ export class SearchReplaceComponent {
this.findOptions = findOptionsStr; this.findOptions = findOptionsStr;
} }
public findPreviousEntry() {
this.editor.findPrevious();
}
public findNextEntry() { public findNextEntry() {
this.editor.findNext(); this.editor.findNext();
} }
public findEntryKeyUpHandler(event: KeyboardEvent) {
if (!event.ctrlKey || !this.query) return;
if (event.key === "ArrowUp") this.findPreviousEntry();
if (event.key === "ArrowDown") this.findNextEntry();
}
public findAllEntries() { public findAllEntries() {
this.query = this.findEntryElm.nativeElement.value; this.query = this.findEntryElm.nativeElement.value;
if (!this.query) return; this.totalCount = this.editor.findAll(this.query, {
let totalCount = this.editor.findAll(this.query, {
backwards: this.isBackwards, backwards: this.isBackwards,
wrap: this.isWrap, wrap: this.isWrap,
caseSensitive: this.useCaseSensitive, caseSensitive: this.useCaseSensitive,
@@ -193,25 +209,19 @@ export class SearchReplaceComponent {
regExp: this.useRegex, regExp: this.useRegex,
range: this.searchOnlyInSelection range: this.searchOnlyInSelection
}); });
if (this.totalCount === 0) this.isQueryNotFound = true;
} }
public findPreviousEntry() { public replaceEntry(event: KeyboardEvent) {
this.editor.findPrevious(); if (this.isQueryLong || this.isQueryNotFound) return;
}
public replaceEntry(event: any) {
if (event instanceof KeyboardEvent) {
if (event.key !== "Enter") {
return;
}
}
let fromStr = this.findEntryElm.nativeElement.value; let fromStr = this.findEntryElm.nativeElement.value;
let toStr = this.replaceEntryElm.nativeElement.value; let toStr = this.replaceEntryElm.nativeElement.value;
if (!fromStr) return; if (!fromStr) return;
let totalCount = this.editor.replace(toStr, fromStr, { this.editor.replace(toStr, fromStr, {
backwards: this.isBackwards, backwards: this.isBackwards,
wrap: this.isWrap, wrap: this.isWrap,
caseSensitive: this.useCaseSensitive, caseSensitive: this.useCaseSensitive,
@@ -225,12 +235,14 @@ export class SearchReplaceComponent {
} }
public replaceAll() { public replaceAll() {
if (this.isQueryLong || this.isQueryNotFound) return;
let fromStr = this.findEntryElm.nativeElement.value; let fromStr = this.findEntryElm.nativeElement.value;
let toStr = this.replaceEntryElm.nativeElement.value; let toStr = this.replaceEntryElm.nativeElement.value;
if (!fromStr) return; if (!fromStr) return;
let totalCount = this.editor.replaceAll(toStr, fromStr, { this.editor.replaceAll(toStr, fromStr, {
backwards: this.isBackwards, backwards: this.isBackwards,
wrap: this.isWrap, wrap: this.isWrap,
caseSensitive: this.useCaseSensitive, caseSensitive: this.useCaseSensitive,
@@ -238,23 +250,27 @@ export class SearchReplaceComponent {
regExp: this.useRegex, regExp: this.useRegex,
range: this.searchOnlyInSelection range: this.searchOnlyInSelection
}); });
this.isQueryNotFound = true;
} }
public searchForString() { public searchForString() {
if (event instanceof KeyboardEvent) { if (this.searchTimeoutId) { clearTimeout(this.searchTimeoutId); }
if (event.key !== "Enter") {
return;
}
}
this.query = this.findEntryElm.nativeElement.value; this.query = this.findEntryElm.nativeElement.value;
if (!this.query) return; if (!this.query) {
this.isQueryLong = false;
this.isQueryNotFound = false;
if (this.searchTimeoutId) { clearTimeout(this.searchTimeoutId); } return;
}
this.isQueryLong = (this.query.length > 80);
if (this.isQueryLong) return;
this.searchTimeoutId = setTimeout(() => { this.searchTimeoutId = setTimeout(() => {
let totalCount = this.editor.find(this.query, { this.totalCount = this.editor.findAll(this.query, {
backwards: this.isBackwards, backwards: this.isBackwards,
wrap: this.isWrap, wrap: this.isWrap,
caseSensitive: this.useCaseSensitive, caseSensitive: this.useCaseSensitive,
@@ -262,6 +278,8 @@ export class SearchReplaceComponent {
regExp: this.useRegex, regExp: this.useRegex,
range: this.searchOnlyInSelection range: this.searchOnlyInSelection
}); });
this.isQueryNotFound = (this.totalCount === 0);
}, this.searchTimeout); }, this.searchTimeout);
} }

View File

@@ -22,7 +22,7 @@
} }
.active-tab { .active-tab {
background-color: rgba(255, 255, 255, 0.46); background-color: rgba(144, 144, 144, 0.64);
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
} }
@@ -42,6 +42,7 @@
margin-left: 2em; margin-left: 2em;
margin-right: 2em; margin-right: 2em;
font-size: 4em; font-size: 4em;
align-self: center;
} }
.close-button { .close-button {

View File

@@ -3,7 +3,8 @@
cdkDropListLockAxis="x" cdkDropListLockAxis="x"
cdkDropListOrientation="horizontal" cdkDropListOrientation="horizontal"
(cdkDropListDropped)="dropped($event)" (cdkDropListDropped)="dropped($event)"
(click)="handleAction($event)" (mousedown)="handleActionMouseDown($event)"
(click)="handleActionClick($event)"
class="display-contents" class="display-contents"
> >
@@ -20,3 +21,15 @@
</div> </div>
</div> </div>
<ul #contextMenu
class="contextMenu"
[hidden]="!showContextMenu"
(blur)="hideContextMenu()"
(click)="contextMenuClicked($event)"
>
<li command="close">Close</li>
<li command="closeAll">Close All</li>
<li command="closeAllLeft">Close All Left</li>
<li command="closeAllRight">Close All Right</li>
</ul>

View File

@@ -1,12 +1,21 @@
import { Component, ChangeDetectorRef, inject } from '@angular/core'; import {
Component,
ChangeDetectorRef,
DestroyRef,
ElementRef,
ViewChild,
inject
} from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CdkDrag, CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop'; import { CdkDrag, CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop';
import { Subject, takeUntil } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { TabsService } from '../../common/services/editor/tabs/tabs.service'; import { TabsService } from '../../common/services/editor/tabs/tabs.service';
import { ServiceMessage } from '../../common/types/service-message.type'; import { ServiceMessage } from '../../common/types/service-message.type';
import { ButtonMap } from '../../common/constants/button.map';
@Component({ @Component({
@@ -24,68 +33,129 @@ import { ServiceMessage } from '../../common/types/service-message.type';
} }
}) })
export class TabsComponent { export class TabsComponent {
private unsubscribe: Subject<void> = new Subject(); readonly #destroyRef = inject(DestroyRef);
private tabsService: TabsService = inject(TabsService); private tabsService: TabsService = inject(TabsService);
private changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef); private changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
@ViewChild('contextMenu') contextMenu!: ElementRef;
public showContextMenu: boolean = false;
tabs: any[] = this.tabsService.tabs; tabs: any[] = this.tabsService.tabs;
targetEvent!: any;
activeTab!: any;
constructor() { constructor() {
}
private ngAfterViewInit(): void {
this.loadSubscribers(); this.loadSubscribers();
} }
private ngOnDestroy(): void {
this.unsubscribe.next();
this.unsubscribe.complete();
}
private loadSubscribers() { private loadSubscribers() {
this.tabsService.getMessage$().pipe( this.tabsService.getMessage$().pipe(
takeUntil(this.unsubscribe) takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => { ).subscribe((message: ServiceMessage) => {
if (message.action === "create-tab") { let elm = document.querySelector(`.tab[title="${message.filePath}"]`);
switch ( message.action ) {
case "create-tab":
this.createTab(message.fileName, message.fileUUID, message.filePath); this.createTab(message.fileName, message.fileUUID, message.filePath);
} else if (message.action === "file-changed") { break;
let elm = document.querySelectorAll(`[title="${message.filePath}"]`)[1]; case "file-unmodified":
elm.classList.remove("file-changed");
break;
case "file-changed":
elm.classList.add("file-changed"); elm.classList.add("file-changed");
elm.classList.remove("file-deleted"); elm.classList.remove("file-deleted");
} else if (message.action === "file-deleted") { break;
let elm = document.querySelectorAll(`[title="${message.filePath}"]`)[1]; case "file-deleted":
elm.classList.add("file-deleted"); elm.classList.add("file-deleted");
elm.classList.remove("file-changed"); elm.classList.remove("file-changed");
} else if (message.action === "file-saved") { break;
let elm = document.querySelectorAll(`[title="${message.filePath}"]`)[1]; case "file-saved":
elm.classList.remove("file-deleted"); elm.classList.remove("file-deleted");
elm.classList.remove("file-changed"); elm.classList.remove("file-changed");
break;
case "highlight-active-tab":
if (!elm) {
if (this.activeTab) {
this.activeTab.classList.remove("active-tab")
this.activeTab = elm;
} }
break;
};
if (this.activeTab) {
if (
this.activeTab.getAttribute("title") == elm.getAttribute("title")
) {
break;
}
this.activeTab.classList.remove("active-tab")
}
this.activeTab = elm;
elm.classList.add("active-tab");
elm.scrollIntoView(
{
behavior: "smooth",
block: "center",
inline: "center"
}
);
break;
default:
break;
}
}); });
} }
protected handleAction(event: any): void { protected handleActionClick(event: any): void {
if (ButtonMap.RIGHT === event.button) return;
let target = event.target; let target = event.target;
if ( target.classList.contains("tab") ) { this.showContextMenu = false;
this.tabsService.sendEditorsServiceAMessage( this.processTargetEvent(event);
"set-tab-to-editor",
event.srcElement.getAttribute("title")
);
} else if ( target.classList.contains("title") ) {
this.tabsService.sendEditorsServiceAMessage(
"set-tab-to-editor",
event.srcElement.parentElement.getAttribute("title")
);
} else if ( target.classList.contains("close-button") ) {
this.tabsService.closeTab(
event.srcElement.parentElement.getAttribute("title")
);
} }
protected handleActionMouseDown(event: any): void {
if (ButtonMap.LEFT === event.button) return;
let target = event.target;
let menuElm = this.contextMenu.nativeElement;
let pageX = event.clientX;
let pageY = event.clientY;
const origin = {
left: pageX + 5,
top: pageY - 5
};
menuElm.style.left = `${origin.left}px`;
menuElm.style.top = `${origin.top}px`;
this.targetEvent = event;
this.showContextMenu = true;
}
public hideContextMenu() {
this.showContextMenu = false;
}
public contextMenuClicked(event: any) {
this.showContextMenu = false;
const command = event.target.getAttribute("command");
const args = event.target.getAttribute("args");
if (!command) return;
this[command]( (args) ? args : null );
} }
public createTab(title: string, uuid: string, path: string): void { public createTab(title: string, uuid: string, path: string): void {
@@ -112,4 +182,87 @@ export class TabsComponent {
this.tabsService.move(event.previousIndex); this.tabsService.move(event.previousIndex);
} }
private close(event: any): void {
this.tabsService.closeTab(
this.targetEvent.srcElement.parentElement.getAttribute("title")
);
}
private closeAll(event: any): void {
let elm = this.targetEvent.srcElement.parentElement;
let startElm = elm;
// clear right
while (elm) {
elm = elm.nextSibling;
if (!elm || elm.nodeType == 8) continue;
this.tabsService.closeTab( elm.getAttribute("title") );
}
// clear left
elm = startElm;
while (elm) {
elm = elm.previousSibling;
if (!elm || elm.nodeType == 8) continue;
this.tabsService.closeTab( elm.getAttribute("title") );
}
// clear initial target
elm = startElm;
this.tabsService.closeTab( elm.getAttribute("title") );
}
private closeAllLeft(event: any): void {
let elm = this.targetEvent.srcElement.parentElement;
// clear left
while (elm) {
elm = elm.previousSibling;
if (!elm || elm.nodeType == 8) continue;
this.tabsService.closeTab( elm.getAttribute("title") );
}
}
private closeAllRight(event: any): void {
let elm = this.targetEvent.srcElement.parentElement;
// clear right
while (elm) {
elm = elm.nextSibling;
if (!elm || elm.nodeType == 8) continue;
this.tabsService.closeTab( elm.getAttribute("title") );
}
}
private processTargetEvent(event: any): void {
let target = event.target;
if ( target.classList.contains("tab") ) {
let fpath = event.srcElement.getAttribute("title")
this.tabsService.sendEditorsServiceAMessage("set-tab-to-editor", fpath);
// this.updateActiveTabHighlight(fpath);
} else if ( target.classList.contains("title") ) {
let fpath = event.srcElement.parentElement.getAttribute("title")
this.tabsService.sendEditorsServiceAMessage("set-tab-to-editor", fpath);
// this.updateActiveTabHighlight(fpath);
} else if ( target.classList.contains("close-button") ) {
this.tabsService.closeTab(
event.srcElement.parentElement.getAttribute("title")
);
}
}
private updateActiveTabHighlight(fpath: string): void {
let message = new ServiceMessage();
message.action = "highlight-active-tab";
message.filePath = fpath;
this.tabsService.sendMessage(message);
}
} }

View File

@@ -61,3 +61,15 @@
.ace_sb-h { .ace_sb-h {
width: 0.8em !important; width: 0.8em !important;
} }
.ace_cursor {
color: rgba(249, 148, 6, 0.64);
animation: blinker 1s linear infinite;
}
@keyframes blinker {
50% {
opacity: 0;
}
}

View File

@@ -10,6 +10,10 @@ body {
overflow: hidden; overflow: hidden;
} }
ul, li {
list-style: none;
}
/* IDs */ /* IDs */

View File

@@ -57,6 +57,13 @@
margin-right: 0.2em; margin-right: 0.2em;
} }
.tabs-bar-underline {
border-color: #fff;
margin-top: -.8em;
margin-left: -.2em;
border-width: 0.2em;
}
.hr-pane-handle, .hr-pane-handle,
.vr-pane-handle { .vr-pane-handle {
border: 2px dashed lightblue; border: 2px dashed lightblue;
@@ -72,6 +79,28 @@
.contextMenu {
z-index: 999;
overflow: auto;
position: absolute;
min-width: 2em;
max-width: 8em;
padding: 0.2em;
top: 0em;
right: 0em;
background-color: gray;
}
.contextMenu li:hover {
background-color: rgba(0, 124, 0, 0.64);
cursor: pointer;
}
.contextMenu li {
padding: 0em 0.2em;
}
.zero-margin-padding { .zero-margin-padding {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;

File diff suppressed because one or more lines are too long

3978
src/libs/tree-sitter.js Normal file

File diff suppressed because it is too large Load Diff

BIN
src/libs/tree-sitter.wasm Executable file

Binary file not shown.

View File

@@ -11,31 +11,35 @@
import 'zone.js'; // Included with Angular CLI. import 'zone.js'; // Included with Angular CLI.
// Note: Is set to 'any' b/c of desire to set 'render'
// side if running outside of electron mode.
declare global { declare global {
interface Window { interface Window {
electron: { electron: {
node: () => Promise<string>, node: any,
chrome: () => Promise<string>, chrome: any,
electron: () => Promise<string>, electron: any,
}, },
main: { main: {
onMenuActions: (arg0: any) => Promise<string>, onMenuActions: any,
onTerminalActions: any,
quit: any, quit: any,
toggleFullScreen: any, toggleFullScreen: any,
}, },
fs: { fs: {
getLspConfigData: () => Promise<string>, getLspConfigData: any,
getFileContents: (arg0: any) => Promise<string>, getFileContents: any,
openFiles: (arg0) => Promise<string>, openFiles: any,
saveFile: (arg0: any, arg1: any) => Promise<string>, saveFile: any,
saveFileAs: () => Promise<string>, saveFileAs: any,
closeFile: (arg0: any) => Promise<string>, chooseFolder: any,
closeFile: any,
getPathForFile: any, getPathForFile: any,
onLoadFiles: (arg0: any) => Promise<string>, onLoadFiles: any,
onUpdateFilePath: (arg0: any) => Promise<string>, onUpdateFilePath: any,
onSavedFile: (arg0: any) => Promise<string>, onSavedFile: any,
onChangedFile: (arg0: any) => Promise<string>, onChangedFile: any,
onDeletedFile: (arg0: any) => Promise<string>, onDeletedFile: any,
} }
} }
} }

1
src/typings.d.ts vendored
View File

@@ -1 +0,0 @@
declare var showdown: any;

View File

@@ -16,50 +16,6 @@
"declaration": false, "declaration": false,
"skipLibCheck": true, "skipLibCheck": true,
"strict": false, "strict": false,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true
},
"includes": [
"src/typings.d.ts"
]
} }
/*
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./build/app",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"esModuleInterop": true,
"sourceMap": true,
"declaration": false,
"experimentalDecorators": true,
"moduleResolution": "bundler",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"useDefineForClassFields": false,
"lib": [
"ES2022",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
},
"includes": [
"src/typings.d.ts"
]
} }
*/