import { Component } from "@angular/core"; // Import Ace and its modes/themes so that `ace` global is defined import * as ace from "ace-builds/src-min-noconflict/ace"; // Note: https://github.com/mkslanc/ace-linters/blob/c286d85c558530aa1b0597d02108bc782abd4736/packages/demo/file-api-websockets/client.ts#L27 // import { AceLayout, Box, TabManager, Button, dom, AceTreeWrapper, FileSystemWeb, Pane, AceEditor, Tab } from "ace-layout"; import "ace-builds/src-min-noconflict/ext-settings_menu"; import "ace-builds/src-min-noconflict/ext-keybinding_menu"; import "ace-builds/src-min-noconflict/ext-command_bar"; import "ace-builds/src-min-noconflict/ext-prompt"; import "ace-builds/src-min-noconflict/ext-code_lens"; // import "ace-builds/src-min-noconflict/ext-searchbox"; import "ace-builds/src-min-noconflict/ext-language_tools"; // import "ace-builds/src-min-noconflict/theme-one_dark"; // import "ace-builds/src-min-noconflict/theme-penguins_in_space"; import "ace-builds/src-min-noconflict/theme-gruvbox"; // https://www.npmjs.com/package/web-tree-sitter // import { Language, Parser } from 'web-tree-sitter'; import { CodeViewBase } from './view.base'; import { NewtonFile } from '../../common/types/file.type'; import { EditorType } from '../../common/types/editor.type'; import { ServiceMessage } from '../../common/types/service-message.type'; import { ButtonMap } from '../../common/constants/button.map'; @Component({ selector: 'code-view', standalone: true, imports: [ ], templateUrl: './view.component.html', styleUrl: './view.component.css', host: { 'class': 'col zero-margin-padding scroller' } }) export class CodeViewComponent extends CodeViewBase { constructor() { super(); // const { Parser } = window.TreeSitter; // const { Parser } = TreeSitter; // console.log(treeSitter); // treeSitter.Parser.init().then(() => { // console.log("Parser ready..."); // }); // const parser = new Parser(); // const JavaScript = await Language.load('/path/to/tree-sitter-javascript.wasm'); // Language.load('resources/wasm/tree-sitter-javascript.wasm').then((language) => { // console.log(language); // }); this.aceApi = ace; } private ngAfterViewInit(): void { this.loadAce(); } private loadAce(): void { this.configAceAndBindToElement() this.setupRequestedMode(); } private configAceAndBindToElement(): void { this.editorSettings = this.editorsService.editorSettings; ace.config.set('basePath', this.editorSettings.BASE_PATH); this.editor = ace.edit( this.editorElm.nativeElement ); this.editor.setOptions( this.editorSettings.CONFIG ); this.editorsService.set(this.uuid, this); if (this.isDefault) { this.editorsService.setActiveEditor(this.uuid); this.addActiveStyling(); this.editor.focus(); } } private setupRequestedMode() { switch(this.mode) { case EditorType.Standalone: // Note: Ace editor without any additional Newton setup... break; case EditorType.MiniMap: this.setAsMiniMapView(); break; case EditorType.ReadOnly: this.setAsReadOnly(); break; default: this.loadNewtonKeyBindings(); this.loadNewtonEventBindings(); break; } } private loadNewtonKeyBindings(): void { let keyBindings = []; for (let i = 0; i < this.editorSettings.KEYBINDINGS.length; i++) { let keyBinding = this.editorSettings.KEYBINDINGS[i]; keyBindings.push( { name: keyBinding.name, bindKey: keyBinding.bindKey, exec: (keyBinding.name && keyBinding?.service) ? () => ( this[keyBinding?.service][keyBinding.name]() ) : (this[keyBinding.name]) ? () => ( this[keyBinding.name]() ) : () => ( console.log( `Name: ${keyBinding.name}, is not mapping to a method OR mapping to a Service: ${keyBinding?.service} and Name: ${keyBinding.name}.` ) ) , readOnly: keyBinding.readOnly } ); } this.editor.commands.addCommands( keyBindings ); } private loadNewtonEventBindings(): void { // Note: https://ajaxorg.github.io/ace-api-docs/interfaces/ace.Ace.EditorEvents.html this.editor.on("focus", (e) => { let message = new ServiceMessage(); message.action = "set-active-editor"; message.editorUUID = this.uuid; message.rawData = this.editor; this.editorsService.sendMessage(message); this.searchReplaceService.sendMessage(message); message = new ServiceMessage(); message.action = "set-active-editor"; message.rawData = this; this.lspManagerService.sendMessage(message); this.markdownPreviewService.sendMessage(message); this.updateInfoBar(); }); this.editor.on("click", (event) => { 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.updateInfoBar(); }); this.editor.on("keyboardActivity", (e) => { switch(e.command.name) { case "golineup": case "golinedown": case "gotoleft": case "gotoright": this.infoBarService.setInfoBarCursorPos( this.editor.getCursorPosition() ); break; default: break; } }); 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", () => { if (this.debounceId) { clearTimeout(this.debounceId); } this.setDebounceTimeout(); if (!this.activeFile) return; let message = new ServiceMessage(); message.action = "file-changed"; message.filePath = this.activeFile.path; this.tabsService.sendMessage(message); }); this.editor.on("changeSession", (session) => { let message = new ServiceMessage(); message.action = "editor-update"; message.rawData = this; this.lspManagerService.sendMessage(message); 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) { if (!file) return; this.activeFile = file; this.editor.setSession(file.session); } public cloneSession(file: NewtonFile) { if (!file) return; this.activeFile = file; let session = this.aceApi.createEditSession(file.session.getValue()); session["$config"] = { "lsp": { "filePath": file.path } } session.setMode( file.session.getMode()["$id"] ); this.editor.setSession(session); } private setDebounceTimeout(timeout: number = null) { this.debounceId = setTimeout(() => { this.editorsService.miniMapView.editor.session.setValue( this.editor.session.getValue() ); this.debounceId = -1; }, (timeout) ? timeout : this.debounceWait); } }