Compare commits
34 Commits
ec841445ed
...
release/ne
| Author | SHA1 | Date | |
|---|---|---|---|
| 9aae679d21 | |||
| d179e1c42e | |||
| da4e87d3cd | |||
| f1e3557db1 | |||
| dd969302cf | |||
| 21cf5c794a | |||
| a4765952bf | |||
| de5184ce22 | |||
| de0802cb2c | |||
| c4ab66141c | |||
| 6bedb69909 | |||
| 8b47ff3919 | |||
| fd40340d97 | |||
| ea134caf2b | |||
| 88b38370e1 | |||
| ae6c21f8ad | |||
| f94a8ca26c | |||
| ed89f34d40 | |||
| 73f25aae1c | |||
| 3c3a5d2f50 | |||
| d44e7d4e51 | |||
| 60289953ec | |||
| df5f2d481a | |||
| 2ede33f3c2 | |||
| 822d778008 | |||
| 90c8c9b3ee | |||
| 79adb86d25 | |||
| a0914e8096 | |||
| a4d602f98a | |||
| d6f766753c | |||
| ae60905eb4 | |||
| 080cc22841 | |||
| c5fdab59f6 | |||
| ab17e48338 |
16
README.md
16
README.md
@@ -1 +1,17 @@
|
|||||||
# Newton
|
# Newton
|
||||||
|
|
||||||
|
### Images
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### 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
|
||||||
|
`
|
||||||
|
|||||||
@@ -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
BIN
images/pic1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 913 KiB |
BIN
images/pic2.png
Normal file
BIN
images/pic2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
images/pic3.png
Normal file
BIN
images/pic3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
BIN
images/pic4.png
Normal file
BIN
images/pic4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 661 KiB |
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
const { app } = require('electron');
|
const { app } = require('electron');
|
||||||
|
|
||||||
|
|
||||||
let startType = "build";
|
let startType = "build";
|
||||||
let isDebug = false;
|
let ipcPort = "4563";
|
||||||
let args = [];
|
let isDebug = false;
|
||||||
|
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[0].endsWith("/newton") ||
|
|
||||||
process.argv[0].endsWith(".AppImage")
|
|
||||||
) {
|
|
||||||
process.argv = process.argv.slice(1);
|
process.argv = process.argv.slice(1);
|
||||||
}
|
} while (
|
||||||
|
process.argv.length > 0 &&
|
||||||
if ( process.argv.length > 0 && (
|
(
|
||||||
process.argv[0].includes("--trace-warnings") ||
|
process.argv[0].endsWith("/newton") ||
|
||||||
process.argv[0].includes("--start-as")
|
process.argv[0].endsWith(".AppImage") ||
|
||||||
|
process.argv[0].includes("--trace-warnings") ||
|
||||||
|
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,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
26
newton/fs.js
26
newton/fs.js
@@ -114,7 +114,6 @@ const chooseFolder = () => {
|
|||||||
console.debug("Canceled folder selection...");
|
console.debug("Canceled folder selection...");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
console.log(response)
|
|
||||||
|
|
||||||
return response.filePaths[0];
|
return response.filePaths[0];
|
||||||
});
|
});
|
||||||
@@ -186,10 +185,33 @@ 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,
|
chooseFolder: chooseFolder,
|
||||||
openFiles: openFiles,
|
openFiles: openFiles,
|
||||||
@@ -201,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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,17 @@ 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) => {
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -80,6 +80,7 @@ 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 {
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ const load = (win) => {
|
|||||||
}, {
|
}, {
|
||||||
label: 'Terminal',
|
label: 'Terminal',
|
||||||
click: () => {}
|
click: () => {}
|
||||||
|
}, {
|
||||||
|
label: "Quit",
|
||||||
|
click: () => win.webContents.send('menu-actions', "quit")
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@@ -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"),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
35
newton/terminal.js
Normal 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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
47
package.json
47
package.json
@@ -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,45 +44,50 @@
|
|||||||
"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-layout": "1.5.0",
|
"ace-layout": "1.5.0",
|
||||||
"ace-linters": "1.7.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",
|
||||||
"web-tree-sitter": "0.25.8",
|
|
||||||
"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": "0.21.1",
|
||||||
"tree-sitter-bash": "0.23.2",
|
"tree-sitter-bash": "0.23.2",
|
||||||
"tree-sitter-c": "0.23.1",
|
"tree-sitter-c": "0.23.1",
|
||||||
@@ -106,4 +111,4 @@
|
|||||||
"tslib": "2.3.0",
|
"tslib": "2.3.0",
|
||||||
"typescript": "5.7.2"
|
"typescript": "5.7.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,58 +13,73 @@
|
|||||||
"alt-socket": "ws://127.0.0.1:9999/?name=java-language-server",
|
"alt-socket": "ws://127.0.0.1:9999/?name=java-language-server",
|
||||||
"initialization-options": {
|
"initialization-options": {
|
||||||
"bundles": [
|
"bundles": [
|
||||||
"intellicode-core.jar"
|
|
||||||
],
|
],
|
||||||
"workspaceFolders": [
|
"workspaceFolders": [
|
||||||
"file://{workspace.folder}"
|
"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,
|
||||||
@@ -74,7 +89,7 @@
|
|||||||
],
|
],
|
||||||
"sourcePaths": [
|
"sourcePaths": [
|
||||||
"src",
|
"src",
|
||||||
"{user.home}/.config/jdtls/m2/repository/**/*.jar"
|
"{user.home}/.config/lsps/jdtls/m2/repository/**/*.jar"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"sources": {
|
"sources": {
|
||||||
@@ -104,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
|
||||||
@@ -132,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,51 +220,36 @@
|
|||||||
"socket": "ws://127.0.0.1:9999/python",
|
"socket": "ws://127.0.0.1:9999/python",
|
||||||
"socket-two": "ws://127.0.0.1:9999/?name=pylsp",
|
"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": {
|
||||||
|
"rope": {
|
||||||
|
"ropeFolder": "{user.home}/.config/lsps/ropeproject"
|
||||||
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"pycodestyle": {
|
"ruff": {
|
||||||
"enabled": false
|
"enabled": true,
|
||||||
|
"extendSelect": ["I"],
|
||||||
|
"lineLength": 80
|
||||||
},
|
},
|
||||||
"pydocstyle": {
|
"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,
|
||||||
@@ -213,7 +264,7 @@
|
|||||||
"include_function_objects": true,
|
"include_function_objects": true,
|
||||||
"fuzzy": false
|
"fuzzy": false
|
||||||
},
|
},
|
||||||
"jedi":{
|
"jedi": {
|
||||||
"root_dir": "file://{workspace.folder}",
|
"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"
|
||||||
@@ -248,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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!' }");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
5
src/app/common/constants/button.map.ts
Normal file
5
src/app/common/constants/button.map.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export abstract class ButtonMap {
|
||||||
|
static LEFT: number = 0;
|
||||||
|
static MIDDLE: number = 1;
|
||||||
|
static RIGHT: number = 2;
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
64
src/app/common/services/color-tokenizer.service.ts
Normal file
64
src/app/common/services/color-tokenizer.service.ts
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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()];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,17 +18,13 @@ 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);
|
||||||
|
|
||||||
workspaceFolder: string = "";
|
workspaceFolder: string = "";
|
||||||
lspConfigDataStr: 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.lspConfigDataStr = lspConfigData;
|
this.lspConfigDataStr = lspConfigData;
|
||||||
@@ -33,16 +35,14 @@ export class LspManagerService {
|
|||||||
public registerEditorToLSPClient(editor: any) {
|
public registerEditorToLSPClient(editor: any) {
|
||||||
let mode = this.getMode(editor.session);
|
let mode = this.getMode(editor.session);
|
||||||
|
|
||||||
if ( this.languageProviders[mode] ) {
|
this.languageProviders[mode]?.registerEditor(
|
||||||
this.languageProviders[mode].registerEditor(editor);
|
editor,
|
||||||
return;
|
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 parseAndReturnLSPConfigData(): {} {
|
private parseAndReturnLSPConfigData(): {} {
|
||||||
@@ -63,13 +63,13 @@ export class LspManagerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getInitializationOptions(mode: string, configData: {}): {} {
|
private getInitializationOptions(mode: string, configData: {}): {} {
|
||||||
let _initializationOptions = {};
|
let initializationOptions = {};
|
||||||
|
|
||||||
if ( Object.keys(configData).length !== 0 && configData[mode] ) {
|
if ( Object.keys(configData).length !== 0 && configData[mode] ) {
|
||||||
_initializationOptions = configData[mode]["initialization-options"];
|
initializationOptions = configData[mode]["initialization-options"];
|
||||||
}
|
}
|
||||||
|
|
||||||
return _initializationOptions;
|
return initializationOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createLanguageProviderWithClientServer(mode: string): LanguageProvider {
|
public createLanguageProviderWithClientServer(mode: string): LanguageProvider {
|
||||||
@@ -77,15 +77,15 @@ export class LspManagerService {
|
|||||||
let servers: LanguageClientConfig[] = [];
|
let servers: LanguageClientConfig[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let configData = this.parseAndReturnLSPConfigData();
|
let lspConfigData = this.parseAndReturnLSPConfigData();
|
||||||
let _initializationOptions = this.getInitializationOptions(mode, configData);
|
let initializationOptions = this.getInitializationOptions(mode, lspConfigData);
|
||||||
servers = [
|
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( configData[mode]["socket"] ),
|
socket: new WebSocket( lspConfigData[mode]["socket"] ),
|
||||||
initializationOptions: _initializationOptions
|
initializationOptions: initializationOptions
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
@@ -96,22 +96,68 @@ export class LspManagerService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.languageProviders[mode] = AceLanguageClient.for(servers);
|
this.languageProviders[mode] = AceLanguageClient.for(
|
||||||
// this.languageProviders[mode].requireFilePath = true;
|
servers,
|
||||||
this.languageProviders[mode].changeWorkspaceFolder(this.workspaceFolder);
|
{
|
||||||
|
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];
|
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 {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSessionFilePath(session: any, filePath: string = "") {
|
public registerSession(editor: any) {
|
||||||
if ( !session || !filePath ) return;
|
let mode = this.getMode(editor.session);
|
||||||
let mode = this.getMode(session);
|
|
||||||
if ( !this.languageProviders[mode] ) return;
|
if ( !this.languageProviders[mode] ) return;
|
||||||
this.languageProviders[mode].setSessionFilePath(session, filePath);
|
|
||||||
|
this.languageProviders[mode].registerSession(
|
||||||
|
editor.session,
|
||||||
|
editor,
|
||||||
|
editor.session.lspConfig
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMode(session: any): string {
|
public getMode(session: any): string {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ import { ReplaySubject, Observable } from 'rxjs';
|
|||||||
})
|
})
|
||||||
export class FilesModalService {
|
export class FilesModalService {
|
||||||
private showFilesModalSubject: ReplaySubject<null> = new ReplaySubject<null>(1);
|
private showFilesModalSubject: ReplaySubject<null> = new ReplaySubject<null>(1);
|
||||||
private addFileSubject: ReplaySubject<string> = new ReplaySubject<string>(1);
|
private addFileSubject: ReplaySubject<string> = new ReplaySubject<string>(1);
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public showFilesModal(): void {
|
public showFilesModal(): void {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,12 +101,18 @@ 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["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) {
|
||||||
|
|||||||
35
src/app/common/services/websocket.service.ts
Normal file
35
src/app/common/services/websocket.service.ts
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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%;
|
||||||
@@ -25,4 +9,4 @@
|
|||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: thin;
|
border-width: thin;
|
||||||
border-color: rgba(124, 124, 124, 1);
|
border-color: rgba(124, 124, 124, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -25,6 +25,7 @@ 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';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -141,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;
|
||||||
@@ -156,13 +157,38 @@ export class CodeViewComponent extends CodeViewBase {
|
|||||||
this.lspManagerService.sendMessage(message);
|
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();
|
||||||
});
|
});
|
||||||
@@ -182,17 +208,30 @@ 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) => {
|
||||||
@@ -202,11 +241,38 @@ export class CodeViewComponent extends CodeViewBase {
|
|||||||
|
|
||||||
this.lspManagerService.sendMessage(message);
|
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;
|
||||||
|
|
||||||
@@ -217,10 +283,17 @@ export class CodeViewComponent extends CodeViewBase {
|
|||||||
public cloneSession(file: NewtonFile) {
|
public cloneSession(file: NewtonFile) {
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,4 +307,4 @@ export class CodeViewComponent extends CodeViewBase {
|
|||||||
}, (timeout) ? timeout : this.debounceWait);
|
}, (timeout) ? timeout : this.debounceWait);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,4 @@
|
|||||||
<code-view [mode]="'mini-map'"></code-view>
|
<code-view [mode]="'mini-map'"></code-view>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
</div>
|
||||||
@@ -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,168 +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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
activeComponent.lspManagerService.closeDocument(file.session);
|
|
||||||
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("TODO (onUpdateFilePath) :", 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;
|
||||||
|
|
||||||
@@ -233,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) {
|
||||||
@@ -249,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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 ];
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
<div class="row mt-2 mb-3">
|
<div class="row mt-2 mb-3">
|
||||||
|
|
||||||
<div class="col clear-right-padding">
|
<div class="col clear-right-padding">
|
||||||
<div class="input-group-sm">
|
<div class="input-group-sm"
|
||||||
|
(mouseup)="handleActionMouseUp($event)"
|
||||||
|
>
|
||||||
<label class="form-control" [innerText]="lspManagerService.workspaceFolder || 'Project Path...'">
|
<label class="form-control" [innerText]="lspManagerService.workspaceFolder || 'Project Path...'">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,12 +39,12 @@
|
|||||||
<div class="row mt-2 md-2">
|
<div class="row mt-2 md-2">
|
||||||
<div class="col col-sm" [hidden]="!lspManagerService.workspaceFolder">
|
<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)="createLanguageClient()">Create Language Client</button>
|
||||||
|
<button class="btn btn-sm btn-dark" (click)="closeLanguageClient()">Close Language Client</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
Target Editor: <label [innerText]="editor?.id || '...'"></label>
|
Target Editor: <label [innerText]="editor?.id || '...'"></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm" [hidden]="!lspManagerService.workspaceFolder">
|
<div class="col-sm" [hidden]="!lspManagerService.workspaceFolder">
|
||||||
<button class="btn btn-sm btn-dark"
|
<button class="btn btn-sm btn-dark"
|
||||||
(click)="registerEditorToLanguageClient()">
|
(click)="registerEditorToLanguageClient()">
|
||||||
@@ -63,4 +65,13 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ul #contextMenu
|
||||||
|
class="contextMenu"
|
||||||
|
[hidden]="!showContextMenu"
|
||||||
|
(blur)="hideContextMenu"
|
||||||
|
(click)="contextMenuClicked($event)"
|
||||||
|
>
|
||||||
|
<li command="pasteText">Paste</li>
|
||||||
|
</ul>
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
import { Component, ChangeDetectorRef, 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,8 +32,8 @@ 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 changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
|
||||||
|
|
||||||
lspManagerService: LspManagerService = inject(LspManagerService);
|
lspManagerService: LspManagerService = inject(LspManagerService);
|
||||||
|
|
||||||
@@ -36,27 +45,27 @@ export class LspManagerComponent {
|
|||||||
editor: any;
|
editor: any;
|
||||||
activeFile: any;
|
activeFile: any;
|
||||||
|
|
||||||
|
@ViewChild('contextMenu') contextMenu!: ElementRef;
|
||||||
|
public showContextMenu: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private ngAfterViewInit(): void {
|
|
||||||
this.mapEditorsAndLoadConfig();
|
|
||||||
this.loadSubscribers();
|
this.loadSubscribers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ngOnDestroy() {
|
private ngAfterViewInit(): void {
|
||||||
this.unsubscribe.next();
|
this.mapEditorsAndLoadConfig();
|
||||||
this.unsubscribe.complete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapEditorsAndLoadConfig() {
|
private mapEditorsAndLoadConfig() {
|
||||||
this.lspTextEditor = this.lspEditorComponent.editor;
|
this.lspTextEditor = this.lspEditorComponent.editor;
|
||||||
this.innerEditor = this.sessionEditorComponent.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);
|
||||||
@@ -65,7 +74,7 @@ export class LspManagerComponent {
|
|||||||
|
|
||||||
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);
|
||||||
@@ -79,12 +88,53 @@ export class LspManagerComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected handleActionMouseUp(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.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() {
|
public clearWorkspaceFolder() {
|
||||||
this.lspManagerService.workspaceFolder = "";
|
this.lspManagerService.workspaceFolder = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public setWorkspaceFolder() {
|
public setWorkspaceFolder() {
|
||||||
window.fs.chooseFolder().then((folder: string) => {
|
window?.fs.chooseFolder().then((folder: string) => {
|
||||||
if (!folder) return;
|
if (!folder) return;
|
||||||
|
|
||||||
this.lspManagerService.workspaceFolder = folder;
|
this.lspManagerService.workspaceFolder = folder;
|
||||||
@@ -96,14 +146,13 @@ export class LspManagerComponent {
|
|||||||
this.lspManagerService.createLanguageProviderWithClientServer(mode);
|
this.lspManagerService.createLanguageProviderWithClientServer(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public closeLanguageClient() {
|
||||||
|
let mode = this.lspManagerService.getMode(this.editor.session);
|
||||||
|
this.lspManagerService.closeLanguageProviderWithClientServer(mode);
|
||||||
|
}
|
||||||
|
|
||||||
public registerEditorToLanguageClient() {
|
public registerEditorToLanguageClient() {
|
||||||
this.lspManagerService.registerEditorToLSPClient(this.editor);
|
this.lspManagerService.registerEditorToLSPClient(this.editor);
|
||||||
/*
|
|
||||||
this.lspManagerService.setSessionFilePath(
|
|
||||||
this.editor.session,
|
|
||||||
this.activeFile.path
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -130,8 +179,8 @@ export class LspManagerComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setActiveEditor(message: ServiceMessage) {
|
private setActiveEditor(message: ServiceMessage) {
|
||||||
this.editor = message.rawData.editor;
|
this.editor = message.rawData.editor;
|
||||||
this.activeFile = message.rawData.activeFile;
|
this.activeFile = message.rawData.activeFile;
|
||||||
|
|
||||||
// TODO: figure out why this doesn't update the session consistently...
|
// 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.
|
// It seems maybe bound to visible state as change detector ref didn't help either.
|
||||||
@@ -139,18 +188,15 @@ export class LspManagerComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private editorUpdate(message: ServiceMessage) {
|
private editorUpdate(message: ServiceMessage) {
|
||||||
if (!message.rawData.activeFile) return;
|
if (
|
||||||
|
!this.editor ||
|
||||||
|
!message.rawData.activeFile
|
||||||
|
) return;
|
||||||
|
|
||||||
this.editor.setSession(message.rawData.editor.getSession())
|
this.editor.setSession(message.rawData.editor.getSession())
|
||||||
this.activeFile = message.rawData.activeFile;
|
this.activeFile = message.rawData.activeFile;
|
||||||
|
|
||||||
/*
|
|
||||||
this.lspManagerService.setSessionFilePath(
|
|
||||||
this.editor.session,
|
|
||||||
this.activeFile.path
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
this.lspManagerService.registerSession(this.editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private closeFile(message: ServiceMessage) {
|
private closeFile(message: ServiceMessage) {
|
||||||
|
|||||||
@@ -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 ) {
|
||||||
this.toggleMarkdownPreview(message);
|
case "toggle-markdown-preview":
|
||||||
} else if (message.action === "set-active-editor") {
|
this.toggleMarkdownPreview(message);
|
||||||
this.setActiveEditor(message);
|
break;
|
||||||
|
case "set-active-editor":
|
||||||
|
this.setActiveEditor(message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -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`)
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 ) {
|
||||||
this.toggleSearchReplace(message);
|
case "toggle-search-replace":
|
||||||
} else if (message.action === "set-active-editor") {
|
this.toggleSearchReplace(message);
|
||||||
this.setActiveEditor(message);
|
break;
|
||||||
|
case "set-active-editor":
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -53,4 +54,4 @@
|
|||||||
|
|
||||||
.close-button:hover {
|
.close-button:hover {
|
||||||
background: rgba(256, 0, 0, 0.64);
|
background: rgba(256, 0, 0, 0.64);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
>
|
>
|
||||||
|
|
||||||
@@ -19,4 +20,16 @@
|
|||||||
<button class="close-button">X</button>
|
<button class="close-button">X</button>
|
||||||
</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>
|
||||||
|
|||||||
@@ -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}"]`);
|
||||||
this.createTab(message.fileName, message.fileUUID, message.filePath);
|
|
||||||
} else if (message.action === "file-changed") {
|
switch ( message.action ) {
|
||||||
let elm = document.querySelectorAll(`[title="${message.filePath}"]`)[1];
|
case "create-tab":
|
||||||
elm.classList.add("file-changed");
|
this.createTab(message.fileName, message.fileUUID, message.filePath);
|
||||||
elm.classList.remove("file-deleted");
|
break;
|
||||||
} else if (message.action === "file-deleted") {
|
case "file-unmodified":
|
||||||
let elm = document.querySelectorAll(`[title="${message.filePath}"]`)[1];
|
elm.classList.remove("file-changed");
|
||||||
elm.classList.add("file-deleted");
|
break;
|
||||||
elm.classList.remove("file-changed");
|
case "file-changed":
|
||||||
} else if (message.action === "file-saved") {
|
elm.classList.add("file-changed");
|
||||||
let elm = document.querySelectorAll(`[title="${message.filePath}"]`)[1];
|
elm.classList.remove("file-deleted");
|
||||||
elm.classList.remove("file-deleted");
|
break;
|
||||||
elm.classList.remove("file-changed");
|
case "file-deleted":
|
||||||
|
elm.classList.add("file-deleted");
|
||||||
|
elm.classList.remove("file-changed");
|
||||||
|
break;
|
||||||
|
case "file-saved":
|
||||||
|
elm.classList.remove("file-deleted");
|
||||||
|
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") ) {
|
protected handleActionMouseDown(event: any): void {
|
||||||
this.tabsService.sendEditorsServiceAMessage(
|
if (ButtonMap.LEFT === event.button) return;
|
||||||
"set-tab-to-editor",
|
|
||||||
event.srcElement.parentElement.getAttribute("title")
|
|
||||||
);
|
|
||||||
} else if ( target.classList.contains("close-button") ) {
|
|
||||||
this.tabsService.closeTab(
|
|
||||||
event.srcElement.parentElement.getAttribute("title")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ body {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul, li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* IDs */
|
/* IDs */
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
3
src/libs/showdown.min.js
vendored
3
src/libs/showdown.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -11,32 +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,
|
||||||
chooseFolder: () => Promise<string>,
|
chooseFolder: any,
|
||||||
closeFile: (arg0: any) => Promise<string>,
|
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
1
src/typings.d.ts
vendored
@@ -1 +0,0 @@
|
|||||||
declare var showdown: any;
|
|
||||||
@@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
Reference in New Issue
Block a user