From 61e461e31adc2c8f6806437690301fcc28f5321b Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Tue, 27 May 2025 21:10:45 -0500 Subject: [PATCH] initial push --- README.md | 4 +- angular.json | 116 +++++++ app.js | 73 +++++ main.js | 86 ++++++ package.json | 57 ++++ preload.js | 14 + public/lsp-servers-config.json | 290 ++++++++++++++++++ public/newton.png | Bin 0 -> 8966 bytes src/app/app.component.css | 0 src/app/app.component.html | 4 + src/app/app.component.ts | 43 +++ src/app/app.config.server.ts | 11 + src/app/app.config.ts | 9 + src/app/app.routes.ts | 3 + src/app/common/configs/editor.config.ts | 28 ++ src/app/common/configs/keybinding-newton.js | 142 +++++++++ src/app/common/configs/keybindings.config.ts | 146 +++++++++ src/app/common/directives/dnd.directive.ts | 46 +++ .../common/services/editor/editors.service.ts | 22 ++ .../services/editor/tabs/tabs.service.ts | 22 ++ src/app/common/types/file.type.ts | 9 + src/app/common/types/service-message.type.ts | 5 + src/app/editor/ace/ace-editor.component.css | 23 ++ src/app/editor/ace/ace-editor.component.html | 2 + src/app/editor/ace/ace-editor.component.ts | 108 +++++++ src/app/editor/ace/webworker.ts | 165 ++++++++++ src/app/editor/editors.component.css | 3 + src/app/editor/editors.component.html | 6 + src/app/editor/editors.component.ts | 150 +++++++++ src/app/editor/tabs/tab/tab.component.css | 32 ++ src/app/editor/tabs/tab/tab.component.html | 4 + src/app/editor/tabs/tab/tab.component.ts | 30 ++ src/app/editor/tabs/tabs.component.css | 0 src/app/editor/tabs/tabs.component.html | 2 + src/app/editor/tabs/tabs.component.ts | 57 ++++ src/assets/css/ace-overrides.css | 14 + src/assets/css/styles.css | 116 +++++++ src/index.html | 17 + src/main.ts | 9 + src/polyfills.ts | 11 + src/styles.scss | 1 + tsconfig.app.json | 14 + tsconfig.json | 59 ++++ tsconfig.spec.json | 14 + 44 files changed, 1964 insertions(+), 3 deletions(-) create mode 100644 angular.json create mode 100644 app.js create mode 100644 main.js create mode 100644 package.json create mode 100644 preload.js create mode 100644 public/lsp-servers-config.json create mode 100644 public/newton.png create mode 100644 src/app/app.component.css create mode 100644 src/app/app.component.html create mode 100644 src/app/app.component.ts create mode 100644 src/app/app.config.server.ts create mode 100644 src/app/app.config.ts create mode 100644 src/app/app.routes.ts create mode 100644 src/app/common/configs/editor.config.ts create mode 100644 src/app/common/configs/keybinding-newton.js create mode 100644 src/app/common/configs/keybindings.config.ts create mode 100644 src/app/common/directives/dnd.directive.ts create mode 100644 src/app/common/services/editor/editors.service.ts create mode 100644 src/app/common/services/editor/tabs/tabs.service.ts create mode 100644 src/app/common/types/file.type.ts create mode 100644 src/app/common/types/service-message.type.ts create mode 100644 src/app/editor/ace/ace-editor.component.css create mode 100644 src/app/editor/ace/ace-editor.component.html create mode 100644 src/app/editor/ace/ace-editor.component.ts create mode 100644 src/app/editor/ace/webworker.ts create mode 100644 src/app/editor/editors.component.css create mode 100644 src/app/editor/editors.component.html create mode 100644 src/app/editor/editors.component.ts create mode 100644 src/app/editor/tabs/tab/tab.component.css create mode 100644 src/app/editor/tabs/tab/tab.component.html create mode 100644 src/app/editor/tabs/tab/tab.component.ts create mode 100644 src/app/editor/tabs/tabs.component.css create mode 100644 src/app/editor/tabs/tabs.component.html create mode 100644 src/app/editor/tabs/tabs.component.ts create mode 100644 src/assets/css/ace-overrides.css create mode 100644 src/assets/css/styles.css create mode 100644 src/index.html create mode 100644 src/main.ts create mode 100644 src/polyfills.ts create mode 100644 src/styles.scss create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.spec.json diff --git a/README.md b/README.md index 5b16fb5..cd4a134 100644 --- a/README.md +++ b/README.md @@ -1,3 +1 @@ -# Newtan - -Newton editor written with Angular + Electron \ No newline at end of file +# Newton diff --git a/angular.json b/angular.json new file mode 100644 index 0000000..09fb881 --- /dev/null +++ b/angular.json @@ -0,0 +1,116 @@ +{ + "$schema":"./node_modules/@angular/cli/lib/config/schema.json", + "version":1, + "newProjectRoot":"", + "cli":{ + "analytics":false + }, + "projects":{ + "App":{ + "projectType":"application", + "root":"", + "sourceRoot":"src", + "prefix":"app", + "architect":{ + "build":{ + "builder":"@angular-devkit/build-angular:browser", + "options":{ + "outputPath":"dist/app", + "index":"src/index.html", + "main":"src/main.ts", + "polyfills":[ + "zone.js" + ], + "tsConfig":"tsconfig.app.json", + "assets":[ + { + "glob":"**/*", + "input":"public", + "output":"resources" + }, + { + "glob":"**/*", + "input":"node_modules/ace-builds/src-noconflict", + "output":"ace" + } + ], + "stylePreprocessorOptions":{ + }, + "styles":[ + "node_modules/bootstrap/scss/bootstrap.scss", + "node_modules/bootstrap-icons/font/bootstrap-icons.css", + "src/assets/css/styles.css", + "src/assets/css/ace-overrides.css" + ], + "scripts":[ + ], + "optimization": false + }, + "configurations":{ + "production":{ + "budgets":[ + { + "type":"initial", + "maximumWarning":"5MB", + "maximumError":"50MB" + }, + { + "type":"anyComponentStyle", + "maximumWarning":"4kB", + "maximumError":"8kB" + } + ], + "optimization":false + }, + "development":{ + "outputHashing": "all", + "sourceMap": true, + "extractCss": true, + "namedChunks": true, + "extractLicenses": true, + "vendorChunk": false, + } + }, + "defaultConfiguration":"production" + }, + "serve":{ + "builder":"@angular-devkit/build-angular:dev-server", + "configurations":{ + "production":{ + "buildTarget":"App:build:production" + }, + "development":{ + "buildTarget":"App:build:development" + } + }, + "defaultConfiguration":"development" + }, + "extract-i18n":{ + "builder":"@angular-devkit/build-angular:extract-i18n" + }, + "test":{ + "builder":"@angular-devkit/build-angular:karma", + "options":{ + "polyfills":[ + "zone.js", + "zone.js/testing" + ], + "tsConfig":"tsconfig.spec.json", + "assets":[ + { + "glob":"**/*", + "input":"public" + } + ], + "styles":[ + "src/styles.css" + ], + "scripts":[ + + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..c106438 --- /dev/null +++ b/app.js @@ -0,0 +1,73 @@ +const { BrowserWindow } = require('electron'); +const path = require('node:path'); +const fs = require('node:fs'); +const os = require('os') + + + +// const BASE_PATH = 'dist/app/browser'; +const BASE_PATH = 'dist/app'; +const ICON_PATH = `${BASE_PATH}/resources/newton.png`; +const LSP_CONFIG_PATH = `${BASE_PATH}/resources/lsp-servers-config.json`; +let args = []; + + + +const createWindow = (startType = "build", debug = false, args = []) => { + this.args = args; + + const win = new BrowserWindow({ + width: 800, + height: 600, + transparent: true, + icon: path.join(__dirname, ICON_PATH), + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + contextIsolation: true, + enableRemoteModule: false, + } + }); + +// win.removeMenu() + + if (debug == true) { + win.webContents.openDevTools(); + } + + if (startType === "build") { + win.loadFile( + path.join(__dirname, `${BASE_PATH}/index.html`) + ); + } + + if (startType === "ng-serve") { + win.loadURL('http://localhost:4200'); + } +} + +const getFileContents = (_path, useRelativePath = false) => { + try { + if (!useRelativePath) { + return fs.readFileSync(_path, 'utf8'); + } else { + return fs.readFileSync(path.join(__dirname, _path), 'utf8'); + } + } catch(err) { + return `{"message": {"type": "error", "text": "Error: Could not read ${_path}"}}`; + } +} + +const getLspConfigData = () => { + const config = getFileContents(LSP_CONFIG_PATH, true); + return config.replaceAll("{user.home}", os.homedir()); +} + + + +module.exports = { + newton: { + createWindow: createWindow, + getFileContents: getFileContents, + getLspConfigData: getLspConfigData + } +}; \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..8a5f258 --- /dev/null +++ b/main.js @@ -0,0 +1,86 @@ +try { + require('electron-reloader')(module) +} catch {} + + + +const { app, ipcMain } = require('electron'); +const { newton } = require('./app'); +const path = require('node:path'); +const fs = require('node:fs'); + + + +let startType = ""; +let isDebug = false; +let args = []; + + + +const loadArgs = () => { + console.log("\n\nStart KArgs:"); + const hasStartAs = app.commandLine.hasSwitch("start-as"); + if (hasStartAs) { + startType = app.commandLine.getSwitchValue("start-as"); + console.log("Has start-as switch..."); + console.log(startType); + } + + const hasDebug = app.commandLine.hasSwitch("app-debug"); + if (hasDebug) { + isDebug = app.commandLine.getSwitchValue("app-debug"); + console.log("Has app-debug switch..."); + console.log(isDebug); + } + + console.log("\n\nStart VArgs:"); + args = process.argv.slice(4); // remove up to --start-as ... + args.forEach((val, index, array) => { + console.log(index + ': ' + val); + console.log(); + }); + +} + +const loadHandlers = () => { + ipcMain.handle('getLspConfigData', (eve) => newton.getLspConfigData()); + ipcMain.handle('getFileContents', (eve, file) => newton.getFileContents(file)); +} + +app.whenReady().then(() => { + loadArgs(); + loadHandlers(); + newton.createWindow(startType, isDebug, args); +}) + +app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + newton.createWindow(startType, isDebug, args); + }; +}); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit() + }; +}) + +process.on('uncaughtException', (error) => { + if (error.message != null) { + console.log(error.message); + } + + if (error.stack != null) { + console.log(error.stack); + } +}); + +process.on('unhandledRejection', function(error = {}) { + if (error.message != null) { + console.log(error.message); + } + + if (error.stack != null) { + console.log(error.stack); + } +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..b59b86f --- /dev/null +++ b/package.json @@ -0,0 +1,57 @@ +{ + "name": "Newton Editor", + "version": "0.0.1", + "description": "A uniquly simple quasi-IDE for hardcore developers.", + "main": "main.js", + "scripts": { + "app": "ng build --base-href ./ && electron . --trace-warnings --start-as=build", + "electron-build": "electron . --trace-warnings --start-as=build", + "electron-concurrently": "concurrently 'ng serve' 'electron . --trace-warnings --start-as=ng-serve'", + "ng-serve": "ng serve", + "ng-build": "ng build", + "ng-watch-build": "ng build --watch --configuration development", + "ng-test": "ng test", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "private": true, + "author": "ITDominator", + "license": "GPL-2.0-only", + "dependencies": { + "@angular/cli": "19.2.9", + "@angular/common": "19.2.0", + "@angular/compiler": "19.2.0", + "@angular/core": "19.2.0", + "@angular/forms": "19.2.0", + "@angular/platform-browser": "19.2.0", + "@angular/platform-browser-dynamic": "19.2.0", + "@angular/platform-server": "19.2.0", + "@angular/router": "19.2.0", + "ace-builds": "1.41.0", + "ace-linters": "1.5.3", + "bootstrap": "5.3.6", + "bootstrap-icons": "1.12.1", + "express": "4.18.2", + "rxjs": "7.8.0", + "tslib": "2.3.0", + "uuid": "11.1.0", + "zone.js": "0.15.0" + }, + "devDependencies": { + "@angular-devkit/build-angular": "19.2.8", + "@angular/cli": "19.2.8", + "@angular/compiler-cli": "19.2.0", + "@types/express": "4.17.17", + "@types/jasmine": "5.1.0", + "@types/node": "18.18.0", + "concurrently": "9.1.2", + "electron": "36.2.0", + "electron-reloader": "1.2.3", + "jasmine-core": "5.6.0", + "karma": "6.4.0", + "karma-chrome-launcher": "3.2.0", + "karma-coverage": "2.2.0", + "karma-jasmine": "5.1.0", + "karma-jasmine-html-reporter": "2.1.0", + "typescript": "5.7.2" + } +} \ No newline at end of file diff --git a/preload.js b/preload.js new file mode 100644 index 0000000..208ad77 --- /dev/null +++ b/preload.js @@ -0,0 +1,14 @@ +const { contextBridge, ipcRenderer, webUtils } = require('electron') + + +contextBridge.exposeInMainWorld('electron', { + node: () => process.versions.node, + chrome: () => process.versions.chrome, + electron: () => process.versions.electron, +}); + +contextBridge.exposeInMainWorld('fs', { + getLspConfigData: () => ipcRenderer.invoke("getLspConfigData"), + getFileContents: (file) => ipcRenderer.invoke("getFileContents", file), + getPathForFile: (file) => webUtils.getPathForFile(file) +}); diff --git a/public/lsp-servers-config.json b/public/lsp-servers-config.json new file mode 100644 index 0000000..d92b98c --- /dev/null +++ b/public/lsp-servers-config.json @@ -0,0 +1,290 @@ +{ + "java": { + "info": "https://download.eclipse.org/jdtls/", + "info-init-options": "https://github.com/eclipse-jdtls/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line", + "info-import-build": "https://www.javahotchocolate.com/tutorials/build-path.html", + "info-external-class-paths": "https://github.com/eclipse-jdtls/eclipse.jdt.ls/issues/3291", + "link": "https://download.eclipse.org/jdtls/milestones/?d", + "command": "lsp-ws-proxy --listen 4114 -- jdtls", + "alt-command": "lsp-ws-proxy -- jdtls", + "alt-command2": "java-language-server", + "socket": "ws://127.0.0.1:4114/?name=jdtls", + "alt-socket": "ws://127.0.0.1:3030/?name=java-language-server", + "initialization-options": { + "bundles": [ + "intellicode-core.jar" + ], + "workspaceFolders": [ + "file://" + ], + "extendedClientCapabilities": { + "classFileContentsSupport": true, + "executeClientCommandSupport": true + }, + "settings": { + "java": { + "autobuild": { + "enabled": false + }, + "completion": { + "enabled": true, + "importOrder": [ + "java", + "javax", + "org", + "com" + ] + }, + "configuration": { + "maven": { + "userSettings": "{user.home}/.config/jdtls/settings.xml", + "globalSettings": "{user.home}/.config/jdtls/settings.xml" + }, + "runtimes": [ + { + "name": "JavaSE-17", + "path": "/usr/lib/jvm/default-runtime", + "javadoc": "https://docs.oracle.com/en/java/javase/17/docs/api/", + "default": true + } + ] + }, + "classPath": [ + "{user.home}/.config/jdtls/m2/repository/**/*-sources.jar", + "lib/**/*-sources.jar" + ], + "docPath": [ + "{user.home}/.config/jdtls/m2/repository/**/*-javadoc.jar", + "lib/**/*-javadoc.jar" + ], + "silentNotification": true, + "project": { + "encoding": "ignore", + "outputPath": "bin", + "referencedLibraries": [ + "lib/**/*.jar", + "{user.home}/.config/jdtls/m2/repository/**/*.jar" + ], + "importOnFirstTimeStartup": "automatic", + "importHint": true, + "resourceFilters": [ + "node_modules", + "\\.git" + ], + "sourcePaths": [ + "src", + "{user.home}/.config/jdtls/m2/repository/**/*.jar" + ] + }, + "sources": { + "organizeImports": { + "starThreshold": 99, + "staticStarThreshold": 99 + } + }, + "imports": { + "gradle": { + "wrapper": { + "checksums": [] + } + } + }, + "import": { + "maven": { + "enabled": true, + "offline": { + "enabled": false + }, + "disableTestClasspathFlag": false + }, + "gradle": { + "enabled": true, + "wrapper": { + "enabled": true + }, + "version": "", + "home": "abs(static/gradle-7.3.3)", + "java": { + "home": "abs(static/launch_jres/17.0.6-linux-x86_64)" + }, + "offline": { + "enabled": false + }, + "arguments": [], + "jvmArguments": [], + "user": { + "home": "" + }, + "annotationProcessing": { + "enabled": true + } + }, + "exclusions": [ + "**/node_modules/**", + "**/.metadata/**", + "**/archetype-resources/**", + "**/META-INF/maven/**" + ], + "generatesMetadataFilesAtProjectRoot": false + }, + "maven": { + "downloadSources": true, + "updateSnapshots": true + }, + "signatureHelp": { + "enabled": true, + "description": { + "enabled": true + } + }, + "implementationsCodeLens": { + "enabled": true + } + } + } + } + }, + "python": { + "info": "https://github.com/python-lsp/python-lsp-server", + "command": "lsp-ws-proxy -- pylsp", + "alt-command": "pylsp", + "alt-command2": "lsp-ws-proxy --listen 4114 -- pylsp", + "alt-command3": "pylsp --ws --port 4114", + "socket": "ws://127.0.0.1:9999/?name=pylsp", + "initialization-options": { + "pyls": { + "plugins": { + "pycodestyle": { + "enabled": false + }, + "pydocstyle": { + "enabled": false + }, + "pyflakes": { + "enabled": false + }, + "pylint": { + "enabled": false + }, + "mccabe": { + "enabled": false + } + } + }, + "pylsp": { + "plugins": { + "pycodestyle": { + "enabled": false + }, + "pydocstyle": { + "enabled": false + }, + "pyflakes": { + "enabled": false + }, + "pylint": { + "enabled": false + }, + "mccabe": { + "enabled": false + }, + "ruff": true, + "pylsp_rope": { + "rename": true + }, + "rope_rename": { + "enabled": true + }, + "rope_autoimport": { + "enabled": true + }, + "rope_completion": { + "enabled": true, + "eager": true + }, + "jedi_rename": { + "enabled": true + }, + "jedi_completion": { + "enabled": true, + "include_class_objects": true, + "include_function_objects": true, + "fuzzy": true + }, + "jedi":{ + "extra_paths": [ + "{user.home}/Portable_Apps/py-venvs/pylsp-venv/venv/lib/python3.10/site-packages" + ], + "root_dir": "" + } + } + } + } + }, + "python - jedi-language-server": { + "hidden": true, + "info": "https://pypi.org/project/jedi-language-server/", + "command": "jedi-language-server", + "alt-command": "lsp-ws-proxy --listen 3030 -- jedi-language-server", + "socket": "ws://127.0.0.1:3030/?name=jedi-language-server", + "initialization-options": { + "jediSettings": { + "autoImportModules": [], + "caseInsensitiveCompletion": true, + "debug": false + }, + "completion": { + "disableSnippets": false, + "resolveEagerly": false, + "ignorePatterns": [] + }, + "markupKindPreferred": "markdown", + "workspace": { + "extraPaths": [ + "{user.home}/Portable_Apps/py-venvs/pylsp-venv/venv/lib/python3.10/site-packages" + ], + "environmentPath": "{user.home}/Portable_Apps/py-venvs/gtk-apps-venv/venv/bin/python", + "symbols": { + "ignoreFolders": [".nox", ".tox", ".venv", "__pycache__", "venv"], + "maxSymbols": 20 + } + } + } + }, + "cpp": { + "info": "https://clangd.llvm.org/", + "command": "lsp-ws-proxy -- clangd", + "alt-command": "clangd", + "socket": "ws://127.0.0.1:3030/?name=clangd", + "initialization-options": {} + }, + "sh": { + "info": "", + "command": "", + "alt-command": "", + "socket": "ws://127.0.0.1:3030/?name=shell", + "initialization-options": {} + }, + "go": { + "info": "https://pkg.go.dev/golang.org/x/tools/gopls#section-readme", + "command": "lsp-ws-proxy -- gopls", + "alt-command": "gopls", + "socket": "ws://127.0.0.1:3030/?name=gopls", + "initialization-options": {} + }, + "lua": { + "info": "https://github.com/LuaLS/lua-language-server", + "command": "lsp-ws-proxy -- lua-language-server", + "alt-command": "lua-language-server", + "socket": "ws://127.0.0.1:3030/?name=gopls", + "initialization-options": {} + }, + "c": { + "hidden": true, + "info": "https://clangd.llvm.org/", + "command": "lsp-ws-proxy -- clangd", + "alt-command": "clangd", + "socket": "ws://127.0.0.1:3030/?name=clangd", + "initialization-options": {} + } +} \ No newline at end of file diff --git a/public/newton.png b/public/newton.png new file mode 100644 index 0000000000000000000000000000000000000000..30ad12abd671958590acb2937f511bc576fcfb88 GIT binary patch literal 8966 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 hAnd`k#gn=NCLy#tD8l^q2<0apps#JLRjJ_=@n1 + + + \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts new file mode 100644 index 0000000..5248832 --- /dev/null +++ b/src/app/app.component.ts @@ -0,0 +1,43 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; + +import { TabsComponent } from './editor/tabs/tabs.component'; +import { EditorsComponent } from './editor/editors.component'; + + + +declare global { + interface Window { + electron: { + node: () => Promise, + chrome: () => Promise, + electron: () => Promise, + }, + fs: { + getLspConfigData: () => Promise, + getFileContents: (arg0: any) => Promise, + getPathForFile: any, + } + } +} + + + +@Component({ + selector: 'app-root', + imports: [ + TabsComponent, + EditorsComponent + ], + templateUrl: './app.component.html', + styleUrl: './app.component.css', + host: { + 'class': 'row' + } +}) +export class AppComponent { + title = 'Newton'; + + constructor() {} + +} \ No newline at end of file diff --git a/src/app/app.config.server.ts b/src/app/app.config.server.ts new file mode 100644 index 0000000..3514d3a --- /dev/null +++ b/src/app/app.config.server.ts @@ -0,0 +1,11 @@ +import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; +import { provideServerRendering } from '@angular/platform-server'; +import { appConfig } from './app.config'; + +const serverConfig: ApplicationConfig = { + providers: [ + provideServerRendering(), + ] +}; + +export const config = mergeApplicationConfig(appConfig, serverConfig); diff --git a/src/app/app.config.ts b/src/app/app.config.ts new file mode 100644 index 0000000..a9af518 --- /dev/null +++ b/src/app/app.config.ts @@ -0,0 +1,9 @@ +import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; + +import { routes } from './app.routes'; +import { provideClientHydration, withEventReplay } from '@angular/platform-browser'; + +export const appConfig: ApplicationConfig = { + providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration(withEventReplay())] +}; diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts new file mode 100644 index 0000000..dc39edb --- /dev/null +++ b/src/app/app.routes.ts @@ -0,0 +1,3 @@ +import { Routes } from '@angular/router'; + +export const routes: Routes = []; diff --git a/src/app/common/configs/editor.config.ts b/src/app/common/configs/editor.config.ts new file mode 100644 index 0000000..42722aa --- /dev/null +++ b/src/app/common/configs/editor.config.ts @@ -0,0 +1,28 @@ +import { Keybindings } from './keybindings.config'; + +export const EditorSettings: any = { + // BASE_PATH: 'https:cdnjs.cloudflare.com/ajax/libs/ace/1.40.1/', + BASE_PATH: 'ace', + KEYBINDINGS: Keybindings, + CONFIG: { + behavioursEnabled: true, + fontSize: "12px", + theme: "ace/theme/one_dark", + printMarginColumn: 80, + enableBasicAutocompletion: true, + enableLiveAutocompletion: true, + enableSnippets: true, + highlightActiveLine: true, + enableMultiselect: true, + tabSize: 4, + useSoftTabs: true, + tooltipFollowsMouse: true, + wrapBehavioursEnabled: false, + scrollPastEnd: 0.5, + mergeUndoDeltas: false, + showGutter: true, + customScrollbar: true, + navigateWithinSoftTabs: true, + scrollSpeed: 5 + } +}; \ No newline at end of file diff --git a/src/app/common/configs/keybinding-newton.js b/src/app/common/configs/keybinding-newton.js new file mode 100644 index 0000000..2a69344 --- /dev/null +++ b/src/app/common/configs/keybinding-newton.js @@ -0,0 +1,142 @@ +const editorCommands = [ + { + name: "showSettingsMenu", + bindKey: {win: "Ctrl-Shift-m", mac: "Ctrl-Shift-m"}, + exec: function(editor) { + ace.config.loadModule("ace/ext/settings_menu", function(module) { + module.init(editor); + editor.showSettingsMenu(); + }) + } + }, { + name: "showKeyboardShortcuts", + bindKey: {win: "ctrl-shift-h", mac: "command-shift-h"}, + exec: function(editor) { + ace.config.loadModule("ace/ext/keybinding_menu", function(module) { + module.init(editor); + editor.showKeyboardShortcuts(); + }) + } + }, { + name: "openCommandPalette2", + bindKey: {linux: "Command-Shift-/|F1", win: "Ctrl-Shift-/|F1"}, + exec: function(editor) { + editor.execCommand("openCommandPalette"); + } + }, { + name: "showLSPManager", + bindKey: {win: "ctrl-m", mac: "command-m"}, + exec: function(editor) { + $('#lsp-modal').modal("toggle"); + } + }, { + name: "search", + bindKey: {win: "ctrl-f", mac: "ctrl-f"}, + exec: function(editor) { + blockHigherNewtonEvePropigation = true; + searchReplace.toggleShow(); + }, + readOnly: true + }, { + name: "openFile", + bindKey: {win: "ctrl-o", mac: "ctrl-o"}, + exec: function(editor) { + fpath = aceSessions[currentSession]["fpath"] + sendMessage("open_file", "", "", fpath, ""); + }, + readOnly: true + }, { + name: "saveSession", + bindKey: {win: "ctrl-s", mac: "ctrl-s"}, + exec: function(editor) { + saveSession(currentSession); + }, + readOnly: true + }, { + name: "newSession", + bindKey: {win: "ctrl-t", mac: "ctrl-t"}, + exec: function(editor) { + newSession(); + }, + readOnly: true + }, { + name: "closeSession", + bindKey: {win: "ctrl-w", mac: "ctrl-w"}, + exec: function(editor) { + closeSession(currentSession); + }, + readOnly: true + }, { + name: "toggleLineHighlight", + bindKey: {win: "ctrl-h", mac: "ctrl-h"}, + exec: function(editor) { + toggleLineHighlight(); + }, + readOnly: true + }, { + name: "gotoDefinition", + bindKey: {win: "ctrl-g", mac: "ctrl-g"}, + exec: function(editor) { + console.log("Goto stub..."); + // lspProvider.$messageController.postMessage(Message(), callback); + }, + readOnly: true + }, { + name: "movelinesUp", + bindKey: {win: "ctrl-up", mac: "ctrl-up"}, + exec: function(editor) { + editor.execCommand("movelinesup"); + }, + readOnly: true + }, { + name: "movelinesDown", + bindKey: {win: "ctrl-down", mac: "ctrl-down"}, + exec: function(editor) { + editor.execCommand("movelinesdown"); + }, + readOnly: true + }, { + name: "tgglTopMainMenubar", + bindKey: {win: "ctrl-0", mac: "ctrl-0"}, + exec: function(editor) { + sendMessage("tggl_top_main_menubar", "", "", "", ""); + }, + readOnly: true + }, { + name: "zoomIn", + bindKey: {win: "ctrl-=", mac: "ctrl-="}, + exec: function(editor) { + zoomIn(); + }, + readOnly: true + }, { + name: "zoomOut", + bindKey: {win: "ctrl--", mac: "ctrl--"}, + exec: function(editor) { + zoomOut(); + }, + readOnly: true + }, { + name: "scrollUp", + bindKey: {win: "alt-up", mac: "alt-up"}, + exec: function(editor) { + editor.execCommand("scrollup"); + }, + readOnly: true + }, { + name: "scrollDown", + bindKey: {win: "alt-down", mac: "alt-down"}, + exec: function(editor) { + editor.execCommand("scrolldown"); + }, + readOnly: true + }, { + name: "launhLSP", + bindKey: {win: "ctrl-l", mac: "ctrl-l"}, + exec: function(editor) { + loadLSPManager(); + }, + readOnly: true + } + +]; \ No newline at end of file diff --git a/src/app/common/configs/keybindings.config.ts b/src/app/common/configs/keybindings.config.ts new file mode 100644 index 0000000..057e4cd --- /dev/null +++ b/src/app/common/configs/keybindings.config.ts @@ -0,0 +1,146 @@ +export const Keybindings: Array = [ +// export const Keybindings: any = [ + // { + // name: "showSettingsMenu", + // bindKey: {win: "Ctrl-Shift-m", mac: "Ctrl-Shift-m"}, + // exec: () => { + // ace.config.loadModule("ace/ext/settings_menu", function(module) { + // module.init(editor); + // editor.showSettingsMenu(); + // }) + // } + // }, { + // name: "showKeyboardShortcuts", + // bindKey: {win: "ctrl-shift-h", mac: "command-shift-h"}, + // exec: () => { + // ace.config.loadModule("ace/ext/keybinding_menu", function(module) { + // module.init(editor); + // editor.showKeyboardShortcuts(); + // }) + // } + // }, { + // name: "openCommandPalette2", + // bindKey: {linux: "Command-Shift-/|F1", win: "Ctrl-Shift-/|F1"}, + // exec: () => { + // editor.execCommand("openCommandPalette"); + // } + // }, { + // name: "showLSPManager", + // bindKey: {win: "ctrl-m", mac: "command-m"}, + // exec: () => { + // $('//lsp-modal').modal("toggle"); + // } + // }, { + { + name: "search", + bindKey: {win: "ctrl-f", mac: "ctrl-f"}, + exec: () => { + // blockHigherNewtonEvePropigation = true; + // searchReplace.toggleShow(); + console.log("Search"); + }, + readOnly: true + } + // }, { + // name: "openFile", + // bindKey: {win: "ctrl-o", mac: "ctrl-o"}, + // exec: () => { + // fpath = aceSessions[currentSession]["fpath"] + // sendMessage("open_file", "", "", fpath, ""); + // }, + // readOnly: true + // }, { + // name: "saveSession", + // bindKey: {win: "ctrl-s", mac: "ctrl-s"}, + // exec: () => { + // saveSession(currentSession); + // }, + // readOnly: true + // }, { + // name: "newSession", + // bindKey: {win: "ctrl-t", mac: "ctrl-t"}, + // exec: () => { + // newSession(); + // }, + // readOnly: true + // }, { + // name: "closeSession", + // bindKey: {win: "ctrl-w", mac: "ctrl-w"}, + // exec: () => { + // closeSession(currentSession); + // }, + // readOnly: true + // }, { + // name: "toggleLineHighlight", + // bindKey: {win: "ctrl-h", mac: "ctrl-h"}, + // exec: () => { + // toggleLineHighlight(); + // }, + // readOnly: true + // }, { + // name: "gotoDefinition", + // bindKey: {win: "ctrl-g", mac: "ctrl-g"}, + // exec: () => { + // console.log("Goto stub..."); + // lspProvider.$messageController.postMessage(Message(), callback); + // }, + // readOnly: true + // }, { + // name: "movelinesUp", + // bindKey: {win: "ctrl-up", mac: "ctrl-up"}, + // exec: () => { + // editor.execCommand("movelinesup"); + // }, + // readOnly: true + // }, { + // name: "movelinesDown", + // bindKey: {win: "ctrl-down", mac: "ctrl-down"}, + // exec: () => { + // editor.execCommand("movelinesdown"); + // }, + // readOnly: true + // }, { + // name: "tgglTopMainMenubar", + // bindKey: {win: "ctrl-0", mac: "ctrl-0"}, + // exec: () => { + // sendMessage("tggl_top_main_menubar", "", "", "", ""); + // }, + // readOnly: true + // }, { + // name: "zoomIn", + // bindKey: {win: "ctrl-=", mac: "ctrl-="}, + // exec: () => { + // zoomIn(); + // }, + // readOnly: true + // }, { + // name: "zoomOut", + // bindKey: {win: "ctrl--", mac: "ctrl--"}, + // exec: () => { + // zoomOut(); + // }, + // readOnly: true + // }, { + // name: "scrollUp", + // bindKey: {win: "alt-up", mac: "alt-up"}, + // exec: () => { + // editor.execCommand("scrollup"); + // }, + // readOnly: true + // }, { + // name: "scrollDown", + // bindKey: {win: "alt-down", mac: "alt-down"}, + // exec: () => { + // editor.execCommand("scrolldown"); + // }, + // readOnly: true + // }, { + // name: "launhLSP", + // bindKey: {win: "ctrl-l", mac: "ctrl-l"}, + // exec: () => { + // loadLSPManager(); + // }, + // readOnly: true + // } + +]; \ No newline at end of file diff --git a/src/app/common/directives/dnd.directive.ts b/src/app/common/directives/dnd.directive.ts new file mode 100644 index 0000000..c227396 --- /dev/null +++ b/src/app/common/directives/dnd.directive.ts @@ -0,0 +1,46 @@ +import { + Directive, + Output, + Input, + EventEmitter, + HostBinding, + HostListener +} from '@angular/core'; + +import { NewtonFile } from '../types/file.type'; + + +@Directive({ + selector: '[dropzone]' +}) +export class DndDirective { + @HostBinding('class.fileover') fileOver!: boolean; + @Output() fileDropped = new EventEmitter(); + + @HostListener('dragover', ['$event']) onDragOver(evt: any) { + evt.preventDefault(); + evt.stopPropagation(); + + this.fileOver = true; + } + + @HostListener('dragleave', ['$event']) public onDragLeave(evt: any) { + evt.preventDefault(); + evt.stopPropagation(); + + this.fileOver = false; + } + + @HostListener('drop', ['$event']) public ondrop(evt: any) { + evt.preventDefault(); + evt.stopPropagation(); + + this.fileOver = false; + let files: Array = evt.dataTransfer.files; + + if (files.length == 0) return; + + this.fileDropped.emit(files); + } + +} \ No newline at end of file diff --git a/src/app/common/services/editor/editors.service.ts b/src/app/common/services/editor/editors.service.ts new file mode 100644 index 0000000..87bd0f7 --- /dev/null +++ b/src/app/common/services/editor/editors.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, ReplaySubject, Observable } from 'rxjs'; + +import { ServiceMessage } from '../../types/service-message.type'; + + +@Injectable({ + providedIn: 'root' +}) +export class EditorsService { + private dataSubject: ReplaySubject = new ReplaySubject(1); + + constructor() {} + + setData(data: ServiceMessage): void { + this.dataSubject.next(data); + } + + getData$(): Observable { + return this.dataSubject.asObservable(); + } +} \ No newline at end of file diff --git a/src/app/common/services/editor/tabs/tabs.service.ts b/src/app/common/services/editor/tabs/tabs.service.ts new file mode 100644 index 0000000..e26a214 --- /dev/null +++ b/src/app/common/services/editor/tabs/tabs.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, ReplaySubject, Observable } from 'rxjs'; + +import { ServiceMessage } from '../../../types/service-message.type'; + + +@Injectable({ + providedIn: 'root' +}) +export class TabsService { + private dataSubject: ReplaySubject = new ReplaySubject(1); + + constructor() {} + + setData(data: ServiceMessage): void { + this.dataSubject.next(data); + } + + getData$(): Observable { + return this.dataSubject.asObservable(); + } +} \ No newline at end of file diff --git a/src/app/common/types/file.type.ts b/src/app/common/types/file.type.ts new file mode 100644 index 0000000..aa167f0 --- /dev/null +++ b/src/app/common/types/file.type.ts @@ -0,0 +1,9 @@ +import { EditSession } from 'ace-builds'; + + +export interface NewtonFile extends File { + fname: string, + path: string, + hash: string, + session: EditSession +} \ No newline at end of file diff --git a/src/app/common/types/service-message.type.ts b/src/app/common/types/service-message.type.ts new file mode 100644 index 0000000..794a74d --- /dev/null +++ b/src/app/common/types/service-message.type.ts @@ -0,0 +1,5 @@ +export class ServiceMessage { + action: string = "none"; + message: string = ""; + uuid!: string; +} \ No newline at end of file diff --git a/src/app/editor/ace/ace-editor.component.css b/src/app/editor/ace/ace-editor.component.css new file mode 100644 index 0000000..5156172 --- /dev/null +++ b/src/app/editor/ace/ace-editor.component.css @@ -0,0 +1,23 @@ +/* +.editor { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.editor { + height: 100vh; + width: auto; +} +*/ + + +.editor { + position: relative; + height: 100%; + width: 100%; +} + + diff --git a/src/app/editor/ace/ace-editor.component.html b/src/app/editor/ace/ace-editor.component.html new file mode 100644 index 0000000..a086340 --- /dev/null +++ b/src/app/editor/ace/ace-editor.component.html @@ -0,0 +1,2 @@ +
+
diff --git a/src/app/editor/ace/ace-editor.component.ts b/src/app/editor/ace/ace-editor.component.ts new file mode 100644 index 0000000..cc16bbc --- /dev/null +++ b/src/app/editor/ace/ace-editor.component.ts @@ -0,0 +1,108 @@ +import { Component, ElementRef, ViewChild, Input } from '@angular/core'; + +import { AceLanguageClient, LanguageClientConfig } from 'ace-linters/build/ace-language-client'; + +// Import Ace and its modes/themes so that `ace` global is defined +import * as ace from 'ace-builds/src-noconflict/ace'; +import 'ace-builds/src-noconflict/theme-one_dark'; +import { LanguageProvider } from "ace-linters"; + +import { EditorSettings } from "../../common/configs/editor.config"; + +import { EditorsService } from '../../common/services/editor/editors.service'; +import { ServiceMessage } from '../../common/types/service-message.type'; + + + +@Component({ + selector: 'ace-editor', + standalone: true, + imports: [ + ], + templateUrl: './ace-editor.component.html', + styleUrl: './ace-editor.component.css', + host: { + 'class': 'col', + '(click)': 'onClick($event)' + } +}) +export class AceEditorComponent { + + @Input() editorSettings!: typeof EditorSettings; + @ViewChild('editor') editorElm!: ElementRef; + editor!: any; + uuid!: string; + lspConfigData!: {}; + + + constructor(private editorsService: EditorsService) { + } + + + public ngAfterViewInit(): void { + this.loadAce(); + this.loadLanguageProviders(); + } + + public loadAce(): void { + ace.config.set('basePath', this.editorSettings.BASE_PATH); + + this.editor = ace.edit( this.editorElm.nativeElement ); + this.editor.setOptions( this.editorSettings.CONFIG ); +// this.editor.commands.addCommands( this.editorSettings.KEYBINDINGS ); + } + + protected onClick(event: any) { + let message = new ServiceMessage(); + message.action = "set-editor"; + message.message = this.uuid; + message.uuid = this.uuid; + + this.editorsService.setData(message); + } + + public loadLanguageProviders(): void { + let languageProvider = this.getLanguageProviderWithClientServers(); +// let languageProvider = this.getLanguageProviderWithWebWorker(); + languageProvider.registerEditor(this.editor); + + } + + + + public getLanguageProviderWithClientServers() { + let _initializationOptions = {}; + + if (Object.keys(this.lspConfigData).length !== 0) { +// _initializationOptions = this.lspConfigData[ this.editor.session.getMode() ]["initialization-options"]; + _initializationOptions = this.lspConfigData[ "python" ]["initialization-options"]; +// _initializationOptions = this.lspConfigData[ "java" ]["initialization-options"]; +// _initializationOptions = this.lspConfigData[ "cpp" ]["initialization-options"]; + console.log(_initializationOptions.toString()); + } + + let servers: LanguageClientConfig[] = [ + { + module: () => import("ace-linters/build/language-client"), + modes: "python", +// modes: "java", +// modes: "cpp", + type: "socket", + socket: new WebSocket("ws://127.0.0.1:9999/python"), +// socket: new WebSocket("ws://127.0.0.1:9999/?name=clangd"), +// socket: new WebSocket("ws://127.0.0.1:9999/?name=jdtls"), +// socket: new WebSocket("ws://127.0.0.1:9999/?name=pylsp"), +// initializationOptions: _initializationOptions + } + ]; + + return AceLanguageClient.for(servers); + } + + + public getLanguageProviderWithWebWorker() { + let worker = new Worker(new URL('./webworker.js', import.meta.url)); + return LanguageProvider.create(worker); + } + +} \ No newline at end of file diff --git a/src/app/editor/ace/webworker.ts b/src/app/editor/ace/webworker.ts new file mode 100644 index 0000000..530b356 --- /dev/null +++ b/src/app/editor/ace/webworker.ts @@ -0,0 +1,165 @@ +import { ServiceManager } from "ace-linters/build/service-manager"; + +let manager = new ServiceManager(self); + +manager.registerService("html", { + features: {signatureHelp: false}, + module: () => import("ace-linters/build/html-service"), + className: "HtmlService", + modes: "html" +}); + +manager.registerService("css", { + features: {signatureHelp: false}, + module: () => import("ace-linters/build/css-service"), + className: "CssService", + modes: "css" +}); + +manager.registerService("less", { + features: {signatureHelp: false}, + module: () => import("ace-linters/build/css-service"), + className: "CssService", + modes: "less" +}); + +manager.registerService("scss", { + features: {signatureHelp: false}, + module: () => import("ace-linters/build/css-service"), + className: "CssService", + modes: "scss" +}); + +manager.registerService("json", { + features: {signatureHelp: false, documentHighlight: false}, + module: () => import("ace-linters/build/json-service"), + className: "JsonService", + modes: "json", +}); + +manager.registerService("json5", { + features: {signatureHelp: false, documentHighlight: false}, + module: () => import("ace-linters/build/json-service"), + className: "JsonService", + modes: "json5", +}); + +manager.registerService("typescript", { + module: () => import("ace-linters/build/typescript-service"), + className: "TypescriptService", + modes: "typescript|tsx|javascript|jsx", +}); + +manager.registerService("yaml", { + features: {signatureHelp: false, documentHighlight: false}, + module: () => import("ace-linters/build/yaml-service"), + className: "YamlService", + modes: "yaml", +}); + +manager.registerService("xml", { + features: {completion: false, completionResolve: false, diagnostics: true, format: false, hover: false, documentHighlight: false, signatureHelp: false}, + module: () => import("ace-linters/build/xml-service"), + className: "XmlService", + modes: "xml", +}); + +manager.registerService("php", { + features: {completion: false, completionResolve: false, diagnostics: true, format: false, hover: false, documentHighlight: false, signatureHelp: false}, + module: () => import("ace-linters/build/php-service"), + className: "PhpService", + modes: "php" +}); + +manager.registerService("javascript", { + features: {completion: false, completionResolve: false, diagnostics: true, format: false, hover: false, documentHighlight: false, signatureHelp: false}, + module: () => import("ace-linters/build/javascript-service"), + className: "JavascriptService", + modes: "javascript", +}); + + +manager.registerServer("pythonls", { + module: () => import("ace-linters/build/language-client"), + modes: "python", + type: "socket", + socket: new WebSocket("ws://127.0.0.1:9999/?name=pylsp") +}); + + + +/* + + +manager.registerService("clang", { + module: () => import("ace-clang-linter/build/ace-clang-linter"), + className: "AceClangLinter", + modes: "c_cpp", +}); + +manager.registerService("lua", { + features: {completion: false, completionResolve: false, diagnostics: true, format: true, hover: false, documentHighlight: false, signatureHelp: false}, + module: () => import("ace-lua-linter/build/ace-lua-linter"), + className: "AceLuaLinter", + modes: "lua", +}); + +manager.registerService("mysql", { + module: () => import("ace-sql-linter/build/mysql-service"), + className: "MySQLService", + modes: "mysql", +}); + +manager.registerService("zig", { + module: () => import("ace-zig-linter/build/ace-zig-linter"), + className: "AceZigLinter", + modes: "zig", +}); + +manager.registerService("python", { + features: {completion: false, completionResolve: false, diagnostics: true, format: true, hover: false, documentHighlight: false, signatureHelp: false}, + module: () => import("ace-python-ruff-linter/build/python-service"), + className: "PythonService", + modes: "python", +}); + +manager.registerServer("svelte", { + module: () => import("ace-linters/build/language-client"), + modes: "html", + type: "socket", + socket: new WebSocket("ws://127.0.0.1:3030/svelte") +}); + +manager.registerServer("astro", { + module: () => import("ace-linters/build/language-client"), + modes: "astro", + type: "socket", + socket: new WebSocket("ws://127.0.0.1:3030/astro"), + initializationOptions: { + typescript: { + tsdk: "node_modules/typescript/lib", //path to typescript server + } + } +}); + +manager.registerServer("go", { + module: () => import("ace-linters/build/language-client"), + modes: "golang", + type: "socket", + socket: new WebSocket("ws://127.0.0.1:3030/go") +}); + +manager.registerService("dart", { + module: () => import("ace-dart-linter/build/ace-dart-linter"), + className: "AceDartLinter", + modes: "dart", +}); + +manager.registerService("golang", { + module: () => import("ace-go-linter/build/ace-go-linter"), + className: "AceGoLinter", + modes: "go", +}); + + +*/ \ No newline at end of file diff --git a/src/app/editor/editors.component.css b/src/app/editor/editors.component.css new file mode 100644 index 0000000..aa8d03d --- /dev/null +++ b/src/app/editor/editors.component.css @@ -0,0 +1,3 @@ +.dropzone { + height: 80vh; +} diff --git a/src/app/editor/editors.component.html b/src/app/editor/editors.component.html new file mode 100644 index 0000000..296fcfd --- /dev/null +++ b/src/app/editor/editors.component.html @@ -0,0 +1,6 @@ +
+
+ + +
+
\ No newline at end of file diff --git a/src/app/editor/editors.component.ts b/src/app/editor/editors.component.ts new file mode 100644 index 0000000..fc302ea --- /dev/null +++ b/src/app/editor/editors.component.ts @@ -0,0 +1,150 @@ +import { Component, ElementRef, ViewChild, TemplateRef, ComponentRef, ViewContainerRef } from '@angular/core'; +import { Subject, takeUntil } from 'rxjs'; +import * as uuid from 'uuid'; + +import { EditSession } from 'ace-builds'; +import { getModeForPath } from 'ace-builds/src-noconflict/ext-modelist'; + +import { AceEditorComponent } from "./ace/ace-editor.component"; +import { EditorsService } from '../common/services/editor/editors.service'; +import { TabsService } from '../common/services/editor/tabs/tabs.service'; + +import { DndDirective } from '../common/directives/dnd.directive'; +import { NewtonFile } from '../common/types/file.type'; +import { ServiceMessage } from '../common/types/service-message.type'; +import { EditorSettings } from "../common/configs/editor.config"; + + + +@Component({ + selector: 'editors', + standalone: true, + imports: [ + DndDirective + ], + templateUrl: './editors.component.html', + styleUrl: './editors.component.css', + host: { + 'class': 'row' + } +}) +export class EditorsComponent { + private unsubscribe = new Subject(); + + @ViewChild('containerRef', {read: ViewContainerRef}) containerRef!: ViewContainerRef; + editors: Map>; + editorSettings: typeof EditorSettings; + files: Map; + activeEditor!: string; + lspConfigData!: any; + + + constructor( + private editorsService: EditorsService, + private tabsService: TabsService + ) { + this.editorSettings = EditorSettings; + this.editors = new Map>(); + this.files = new Map(); + } + + + public ngAfterViewInit(): void { + this.editorsService.getData$().pipe( + takeUntil(this.unsubscribe) + ).subscribe((data: ServiceMessage) => { + if (data.action === "set-editor") + this.activeEditor = data.uuid; + }); + + this.getLspConfigData().then((lspConfigData: string) => { + this.lspConfigData = JSON.parse(lspConfigData); + + if (this.lspConfigData["message"]) { + console.log( + "Warning: LSP this.lspConfigData is a 'message'", + this.lspConfigData + ); + this.lspConfigData = {}; + } + + let editor = this.createEditor(); + this.activeEditor = editor.instance.uuid; + this.createEditor(); + }) + } + + ngOnDestroy() { + this.unsubscribe.next(); + this.unsubscribe.complete(); + } + + private getLspConfigData(): Promise { + return window.fs.getLspConfigData(); + } + + private createEditor() { + const component = this.containerRef.createComponent(AceEditorComponent); + component.instance.editorSettings = this.editorSettings; + component.instance.uuid = uuid.v4(); + component.instance.lspConfigData = this.lspConfigData; + this.editors.set(component.instance.uuid, component) + + return component; + } + + protected onFileDropped(event: any) { + this.loadFilesList(event).then((session: EditSession | undefined | null) => { + if ( !session ) return; + + let editor = this.editors.get(this.activeEditor)?.instance.editor; + editor?.setSession(session); + }); + } + + private async loadFilesList(files: Array): Promise { + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const path = window.fs.getPathForFile(file); + + if (!file || !path) continue; + if ( this.files.get(path) ) continue; + + await this.addFile(path, file); + this.addTab(file); + } + + return files[ files.length - 1 ].session; + } + + private async addFile(path: string, file: NewtonFile) { + try { + let pathParts = path.split("/"); + file.fname = pathParts[ pathParts.length - 1 ]; + file.path = path; + file.hash = btoa(file.path); + let data = await window.fs.getFileContents(file.path); + file.session = new EditSession(data); + + file.session.setMode( + getModeForPath( file.path ).mode + ); + + this.files.set(file.path, file); + } catch (error) { + console.log( + `---- Error ----\nPath: ${path}\nMessage: ${error}` + ); + } + } + + private async addTab(file: NewtonFile) { + let message = new ServiceMessage(); + message.action = "create-tab"; + message.message = file.fname; + message.uuid = file.hash; + + this.tabsService.setData(message); + } + +} \ No newline at end of file diff --git a/src/app/editor/tabs/tab/tab.component.css b/src/app/editor/tabs/tab/tab.component.css new file mode 100644 index 0000000..42ebab4 --- /dev/null +++ b/src/app/editor/tabs/tab/tab.component.css @@ -0,0 +1,32 @@ +.tab, +.title, +.close-button { + color: rgba(255, 255, 255, 0.64); +} + + +/* +.tab { + float: left; + clear: left; + display: flow; +} +*/ + + +.title { + margin-left: 2em; + margin-right: 2em; +} + +.close-button { + background: rgba(116, 0, 0, 0.64); + border-style: solid; + border-color: rgba(0, 0, 0, 0.64); + border-width: 1px; + border-radius: 0em 1em 0em 0em; +} + +.close-button:hover { + background: rgba(256, 0, 0, 0.64); +} \ No newline at end of file diff --git a/src/app/editor/tabs/tab/tab.component.html b/src/app/editor/tabs/tab/tab.component.html new file mode 100644 index 0000000..10ea72c --- /dev/null +++ b/src/app/editor/tabs/tab/tab.component.html @@ -0,0 +1,4 @@ + + {{title}} + + \ No newline at end of file diff --git a/src/app/editor/tabs/tab/tab.component.ts b/src/app/editor/tabs/tab/tab.component.ts new file mode 100644 index 0000000..9fef6a9 --- /dev/null +++ b/src/app/editor/tabs/tab/tab.component.ts @@ -0,0 +1,30 @@ +import { Component } from '@angular/core'; + + + +@Component({ + selector: 'tab', + standalone: true, + imports: [ + ], + templateUrl: './tab.component.html', + styleUrl: './tab.component.css', + host: { + 'class': 'col' +// 'class': 'col tab' + } +}) +export class TabComponent { + + title: string; + + + constructor() { + this.title = "[NO TITLE]"; + } + + ngOnDestroy() { + } + + +} \ No newline at end of file diff --git a/src/app/editor/tabs/tabs.component.css b/src/app/editor/tabs/tabs.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/editor/tabs/tabs.component.html b/src/app/editor/tabs/tabs.component.html new file mode 100644 index 0000000..c72e44d --- /dev/null +++ b/src/app/editor/tabs/tabs.component.html @@ -0,0 +1,2 @@ + + diff --git a/src/app/editor/tabs/tabs.component.ts b/src/app/editor/tabs/tabs.component.ts new file mode 100644 index 0000000..6f8f172 --- /dev/null +++ b/src/app/editor/tabs/tabs.component.ts @@ -0,0 +1,57 @@ +import { Component, ElementRef, ViewChild, TemplateRef, ComponentRef, ViewContainerRef } from '@angular/core'; +import { Subject, takeUntil } from 'rxjs'; + +import { TabComponent } from './tab/tab.component'; +import { TabsService } from '../../common/services/editor/tabs/tabs.service'; + +import { ServiceMessage } from '../../common/types/service-message.type'; + + + +@Component({ + selector: 'tabs', + standalone: true, + imports: [ + ], + templateUrl: './tabs.component.html', + styleUrl: './tabs.component.css', + host: { + 'class': 'tabs scroller' + } +}) +export class TabsComponent { + private unsubscribe = new Subject(); + + @ViewChild('containerRef', {read: ViewContainerRef}) containerRef!: ViewContainerRef; + tabs: Map>; + activeTab!: string; + + + constructor(private tabsService: TabsService) { + this.tabs = new Map>(); + } + + + public ngAfterViewInit(): void { + this.tabsService.getData$().pipe( + takeUntil(this.unsubscribe) + ).subscribe((data: ServiceMessage) => { + if (data.action === "create-tab") + this.createTab(data.message, data.uuid); + }); + } + + ngOnDestroy() { + this.unsubscribe.next(); + this.unsubscribe.complete(); + } + + private createTab(title: string, uuid: string) { + const component = this.containerRef.createComponent(TabComponent); + component.instance.title = title; + this.tabs.set(uuid, component) + + return component; + } + +} \ No newline at end of file diff --git a/src/assets/css/ace-overrides.css b/src/assets/css/ace-overrides.css new file mode 100644 index 0000000..8ed89de --- /dev/null +++ b/src/assets/css/ace-overrides.css @@ -0,0 +1,14 @@ +/* TAGS */ + + +/* IDs */ + + +/* CLASSES */ +.ace_editor, .ace_gutter { + background-color: rgba(0, 0, 0, 0.0) !important; +} + +.ace_autocomplete { + background-color: #25282c !important; +} \ No newline at end of file diff --git a/src/assets/css/styles.css b/src/assets/css/styles.css new file mode 100644 index 0000000..3f9f12c --- /dev/null +++ b/src/assets/css/styles.css @@ -0,0 +1,116 @@ +/* TAGS */ + +html { +/* + background-color: rgba(64, 64, 64, 0.64); + background-color: rgb(40, 44, 52); +*/ + background-color: rgba(40, 44, 52, 0.64); + color: rgba(255, 255, 255, 0.64); +} + +body { + background-color: rgba(64, 64, 64, 0.0) +} + + +/* IDs */ + + +/* CLASSES */ + +.tabs { + display: flex; + overflow: auto; + margin-bottom: 0.5em; + padding-bottom: 0.4em; + padding-top: 0.4em; +} + + +/* +.tab { + float: left; + clear: left; + display: contents; +} +*/ + +.tab { + display: flex; + overflow: auto; + float: left; + margin-right: 0.5em; + + border-radius: 1em 1em 0em 0em; + + border-top-style: solid; + border-top-color: #ffffff64; + border-top-width: 2px; + + border-left-style: solid; + border-left-color: #ffffff64; + border-left-width: 2px; + + border-right-style: solid; + border-right-color: #ffffff64; + border-right-width: 2px; + +} + + + + + +.scroller { +/* + -webkit-scrollbar-color: #00000084 #ffffff64; + scrollbar-color: #00000084 #ffffff64; +*/ + -webkit-scrollbar-color: #00000084 #ffffff06; + -webkit-scrollbar-width: thin; + scrollbar-color: #00000084 #ffffff06; + scrollbar-width: thin; +} + +.noselect { + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Old versions of Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently + supported by Chrome, Edge, Opera and Firefox */ +} + +.page-alert-zone-container { + position: sticky; + top: 0em; + z-index: 2; +} + + + +.searching, +.search-success, +.search-fail { + border-style: solid; + color: rgba(125, 125, 125, 1) !important; +} + +.searching { + border-color: rgba(0, 225, 225, 0.64) !important; +} +.search-success { + background: rgba(136, 204, 39, 0.12) !important; + border-color: rgba(136, 204, 39, 1) !important; +} +.search-fail { + background: rgba(170, 18, 18, 0.12) !important; + border-color: rgba(200, 18, 18, 1) !important; +} + +/* Other message text colors */ +.error { color: rgb(170, 18, 18); } +.warning { color: rgb(255, 168, 0); } +.success { color: rgb(136, 204, 39); } \ No newline at end of file diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..8714d3d --- /dev/null +++ b/src/index.html @@ -0,0 +1,17 @@ + + + + + Newton + + + + + + +
+ +
+ + + \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..1eee1b4 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,9 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; + +import { AppComponent } from './app/app.component'; + + +bootstrapApplication(AppComponent, appConfig).catch( + (err) => console.error(err) +); diff --git a/src/polyfills.ts b/src/polyfills.ts new file mode 100644 index 0000000..75b392d --- /dev/null +++ b/src/polyfills.ts @@ -0,0 +1,11 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + */ + +/*************************************************************************************************** + * Zone JS is required by Angular itself. + */ +import 'zone.js'; // Included with Angular CLI. + +// If you need other polyfills, add them here. \ No newline at end of file diff --git a/src/styles.scss b/src/styles.scss new file mode 100644 index 0000000..90d4ee0 --- /dev/null +++ b/src/styles.scss @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..374cc9d --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,14 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bb36a82 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,59 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ + +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "downlevelIteration": true, + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "useDefineForClassFields": false, + "moduleResolution": "node", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "sourceMap": true, + "declaration": false, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true + } +} + + + +/* + +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "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 + } +} + +*/ \ No newline at end of file diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 0000000..be7e9da --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,14 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +}