WIP lsp-manager effort

This commit is contained in:
itdominator 2025-07-05 15:21:08 -05:00
parent 23d9bb24f2
commit 7b4529e8f3
14 changed files with 178 additions and 179 deletions

View File

@ -2,8 +2,8 @@
<info-bar></info-bar> <info-bar></info-bar>
<tabs></tabs> <tabs></tabs>
<editors></editors> <editors></editors>
<markdown-preview></markdown-preview>
<search-replace></search-replace> <search-replace></search-replace>
<markdown-preview></markdown-preview>
<files-modal></files-modal> <lsp-manager></lsp-manager>
</div> </div>

View File

@ -5,7 +5,7 @@ import { TabsComponent } from './editor/tabs/tabs.component';
import { EditorsComponent } from './editor/editors.component'; import { EditorsComponent } from './editor/editors.component';
import { SearchReplaceComponent } from "./editor/search-replace/search-replace.component"; import { SearchReplaceComponent } from "./editor/search-replace/search-replace.component";
import { MarkdownPreviewComponent } from "./editor/markdown-preview/markdown-preview.component"; import { MarkdownPreviewComponent } from "./editor/markdown-preview/markdown-preview.component";
import { FilesModalComponent } from "./common/components/modals/files/files-modal.component"; import { LspManagerComponent } from "./editor/lsp-manager/lsp-manager.component";
@ -17,7 +17,7 @@ import { FilesModalComponent } from "./common/components/modals/files/files-moda
EditorsComponent, EditorsComponent,
SearchReplaceComponent, SearchReplaceComponent,
MarkdownPreviewComponent, MarkdownPreviewComponent,
FilesModalComponent LspManagerComponent,
], ],
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrl: './app.component.css', styleUrl: './app.component.css',

View File

@ -1,16 +0,0 @@
.modal-column {
min-height: 24em;
max-height: 24em;
overflow: auto;
}
.close-button {
background: rgba(116, 0, 0, 0.64);
border-style: solid;
border-color: rgba(0, 0, 0, 0.64);
border-width: 1px;
}
.close-button:hover {
background: rgba(256, 0, 0, 0.64);
}

View File

@ -1,52 +0,0 @@
<div #filesModal
id="filesModal" class="modal fade" tabindex="-1" role="dialog"
>
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Files:</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col">
<div class="row">
<div class="col modal-column">
<div #filesList *ngFor="let file of files" class="row">
<div class="col-11 title"
title="{{file.path}}"
uuid="{{file.uuid}}"
path="{{file.path}}"
>
{{file.title}}
</div>
<div class="col-1 close-button">
X
</div>
</div>
</div>
</div>
<div class="row">
<input #filesSearch type="text" placeholder="Search..." />
</div>
</div>
<div class="col modal-column">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,85 +0,0 @@
import { Component, inject } from "@angular/core";
import { CommonModule } from "@angular/common";
import { Subject, takeUntil } from 'rxjs';
import * as bootstrap from "bootstrap";
import { FilesModalService } from "../../../services/editor/modals/files-modal.service";
import { TabsService } from '../../../services/editor/tabs/tabs.service';
import { ServiceMessage } from '../../../types/service-message.type';
@Component({
selector: 'files-modal',
standalone: true,
imports: [
CommonModule
],
templateUrl: './files-modal.component.html',
styleUrl: './files-modal.component.css',
host: {
'class': ''
}
})
export class FilesModalComponent {
private unsubscribe: Subject<void> = new Subject();
private filesModalService: FilesModalService = inject(FilesModalService);
private tabsService: TabsService = inject(TabsService);
filesModal!: bootstrap.Modal;
files: any[] = [];
constructor() {
}
private ngAfterViewInit(): void {
this.loadSubscribers();
}
private loadSubscribers() {
this.tabsService.getMessage$().pipe(
takeUntil(this.unsubscribe)
).subscribe((data: ServiceMessage) => {
if (data.action === "create-tab") {
this.createFileRow(data.fileName, data.fileUUID, data.filePath);
}
});
this.filesModalService.showFilesModalRequested$().pipe(
takeUntil(this.unsubscribe)
).subscribe(() => {
if (!this.filesModal) {
this.createModal();
}
this.showModal();
});
this.filesModalService.addFileToModalRequested$().pipe(
takeUntil(this.unsubscribe)
).subscribe((uuid: string) => {
if (!this.filesModal) {
this.createModal();
}
});
}
private createModal() {
this.filesModal = new bootstrap.Modal("#filesModal", {});
}
public createFileRow(title: string, uuid: string, path: string): void {
this.files.push({title: title, uuid: uuid, path: path})
}
public showModal() {
this.filesModal?.toggle();
}
}

View File

@ -15,18 +15,14 @@ export const Keybindings: Array<{}> = [
name: "openCommandPalette2", name: "openCommandPalette2",
bindKey: {linux: "command-shift-/|F1", win: "ctrl-shift-/|F1"}, bindKey: {linux: "command-shift-/|F1", win: "ctrl-shift-/|F1"},
readOnly: false readOnly: false
}, {
name: "showFilesModal",
bindKey: {win: "ctrl-shift-b", mac: "ctrl-shift-b"},
service: "filesModalService",
readOnly: false
}, { }, {
name: "showFilesList", name: "showFilesList",
bindKey: {win: "ctrl-b", mac: "ctrl-b"}, bindKey: {win: "ctrl-b", mac: "ctrl-b"},
readOnly: false readOnly: false
}, { }, {
name: "showLSPModal", name: "lspManagerPopup",
bindKey: {win: "ctrl-shift-l", mac: "ctrl-shift-l"}, bindKey: {win: "ctrl-shift-l", mac: "ctrl-shift-l"},
service: "",
readOnly: false readOnly: false
}, { }, {
name: "markdownPreviewPopup", name: "markdownPreviewPopup",

View File

@ -1,15 +1,19 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { BehaviorSubject, ReplaySubject, Observable } from 'rxjs'; import { ReplaySubject, Observable } from 'rxjs';
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';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class LSPService { export class LspManagerService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
lspConfigData!: {}; lspConfigData!: {};
languageProviders: {} = {}; languageProviders: {} = {};
@ -75,14 +79,22 @@ export class LSPService {
return LanguageProvider.create(worker); return LanguageProvider.create(worker);
} }
protected setSessionFilePath(session: any, mode: string = "", filePath: string = "") => { protected setSessionFilePath(session: any, mode: string = "", filePath: string = "") {
if ( !session || !mode || !filePath || !this.languageProviders[mode] ) return; if ( !session || !mode || !filePath || !this.languageProviders[mode] ) return;
this.languageProviders[mode].setSessionFilePath(session, filePath); this.languageProviders[mode].setSessionFilePath(session, filePath);
} }
protected closeDocument(session: any, mode: string) => { protected closeDocument(session: any, mode: string) {
if ( !session || !mode || !this.languageProviders[mode] ) return; if ( !session || !mode || !this.languageProviders[mode] ) return;
this.languageProviders[mode].closeDocument(session); this.languageProviders[mode].closeDocument(session);
} }
public sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data);
}
public getMessage$(): Observable<ServiceMessage> {
return this.messageSubject.asObservable();
}
} }

View File

@ -8,6 +8,7 @@ import { EditorsService } from '../../common/services/editor/editors.service';
import { FilesService } from '../../common/services/files.service'; import { FilesService } from '../../common/services/files.service';
import { SearchReplaceService } from '../../common/services/editor/search-replace/search-replace.service'; import { SearchReplaceService } from '../../common/services/editor/search-replace/search-replace.service';
import { MarkdownPreviewService } from '../../common/services/editor/markdown-preview/markdown-preview.service'; import { MarkdownPreviewService } from '../../common/services/editor/markdown-preview/markdown-preview.service';
import { LspManagerService } from '../../common/services/editor/lsp-manager/lsp-manager.service';
import { EditorSettings } from "../../common/configs/editor.config"; import { EditorSettings } from "../../common/configs/editor.config";
import { NewtonFile } from '../../common/types/file.type'; import { NewtonFile } from '../../common/types/file.type';
@ -31,6 +32,7 @@ export class CodeViewBase {
protected filesService: FilesService = inject(FilesService); protected filesService: FilesService = inject(FilesService);
protected searchReplaceService: SearchReplaceService = inject(SearchReplaceService); protected searchReplaceService: SearchReplaceService = inject(SearchReplaceService);
protected markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService); protected markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService);
protected lspManagerService: LspManagerService = inject(LspManagerService);
@ViewChild('editor') editorElm!: ElementRef; @ViewChild('editor') editorElm!: ElementRef;
@Input() editorSettings!: typeof EditorSettings; @Input() editorSettings!: typeof EditorSettings;
@ -97,6 +99,12 @@ export class CodeViewBase {
this.editor.showKeyboardShortcuts(); this.editor.showKeyboardShortcuts();
} }
public lspManagerPopup() {
let message = new ServiceMessage();
message.action = "toggle-lsp-manager";
this.lspManagerService.sendMessage(message);
}
public markdownPreviewPopup() { public markdownPreviewPopup() {
let message = new ServiceMessage(); let message = new ServiceMessage();
message.action = "toggle-markdown-preview"; message.action = "toggle-markdown-preview";

View File

@ -59,6 +59,12 @@ export class CodeViewComponent extends CodeViewBase {
return; return;
} }
let message = new ServiceMessage();
message.action = "register-editor";
message.rawData = this;
this.lspManagerService.sendMessage(message);
this.loadAceKeyBindings(); this.loadAceKeyBindings();
this.loadAceEventBindings(); this.loadAceEventBindings();
} }
@ -116,6 +122,7 @@ export class CodeViewComponent extends CodeViewBase {
this.editorsService.sendMessage(message); this.editorsService.sendMessage(message);
this.searchReplaceService.sendMessage(message); this.searchReplaceService.sendMessage(message);
this.editorsService.sendMessage(message);
message = new ServiceMessage(); message = new ServiceMessage();
message.action = "set-active-editor"; message.action = "set-active-editor";

View File

@ -0,0 +1,3 @@
.lsp-config-text {
min-height: 25em;
}

View File

@ -0,0 +1,27 @@
<div class="container-fluid">
<div class="row mt-2 mb-3">
<div class="col">
<div class="input-group-sm">
<input class="form-control" placeholder="Project Path..." />
</div>
</div>
<div class="col col-auto">
<div class="input-group-sm">
<button class="btn btn-sm btn-dark">Choose Directory</button>
</div>
</div>
</div>
<div class="row">
<div class="col">
<textarea #lspConfigText class="form-control form-control-sm lsp-config-text" placeholder="LSP Config..."> </textarea>
</div>
</div>
</div>

View File

@ -0,0 +1,87 @@
import { Component, ElementRef, HostBinding, ViewChild, inject } from '@angular/core';
import { Subject, takeUntil } from 'rxjs';
import { LspManagerService } from '../../common/services/editor/lsp-manager/lsp-manager.service';
import { ServiceMessage } from '../../common/types/service-message.type';
@Component({
selector: 'lsp-manager',
standalone: true,
imports: [
],
templateUrl: './lsp-manager.component.html',
styleUrl: './lsp-manager.component.css',
host: {
'class': 'lsp-manager',
"(keyup)": "globalLspManagerKeyHandler($event)"
}
})
export class LspManagerComponent {
private unsubscribe: Subject<void> = new Subject();
private lspManagerService: LspManagerService = inject(LspManagerService);
@HostBinding("class.hidden") isHidden: boolean = true;
@ViewChild('lspConfigText') lspConfigText!: ElementRef;
private editors: any = {};
private editor: any;
constructor() {
}
private ngAfterViewInit(): void {
this.lspConfigText.nativeElement.value = this.lspManagerService.lspConfigData;
this.loadSubscribers();
}
private ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
private loadSubscribers() {
this.lspManagerService.getMessage$().pipe(
takeUntil(this.unsubscribe)
).subscribe((message: ServiceMessage) => {
if (message.action === "toggle-lsp-manager") {
this.toggleLspManager(message);
} else if (message.action === "set-active-editor") {
this.setActiveEditor(message);
} else if (message.action === "register-editor") {
this.registerEditor(message);
}
});
}
public hideLspManager() {
this.isHidden = true;
this.editor.focus();
}
public globalLspManagerKeyHandler(event: any) {
if (event.ctrlKey && event.shiftKey && event.key === "l") {
this.hideLspManager();
}
}
private toggleLspManager(message: ServiceMessage) {
this.isHidden = !this.isHidden;
}
private setActiveEditor(message: ServiceMessage) {
this.editor = this.editors[message.editorUUID];
}
private registerEditor(message: ServiceMessage) {
let _editor = message.rawData;
this.editors[_editor.uuid] = _editor.editor;
}
}

View File

@ -6,28 +6,40 @@
/* CLASSES */ /* CLASSES */
.markdown-preview { .search-replace,
top: 2em; .markdown-preview,
bottom: 2em; .lsp-manager {
right: 2em;
z-index: 900;
position: fixed;
display: inline-block; display: inline-block;
width: 50vw; position: fixed;
overflow: auto;
background-color: rgba(64, 64, 64, 0.84); background-color: rgba(64, 64, 64, 0.84);
overflow: auto;
} }
.search-replace { .search-replace {
bottom: 2em; bottom: 2em;
left: 2em; left: 2em;
right: 2em; right: 2em;
z-index: 800; z-index: 900;
display: inline-block;
position: fixed;
background-color: rgba(64, 64, 64, 0.64);
} }
.markdown-preview {
top: 2em;
bottom: 2em;
right: 2em;
z-index: 700;
width: 50vw;
overflow: auto;
}
.lsp-manager {
top: 2em;
bottom: 2em;
left: 2em;
right: 2em;
z-index: 800;
}
.info-bar { .info-bar {
font-size: 0.8em; font-size: 0.8em;
color: rgba(255, 255, 255, 0.84); color: rgba(255, 255, 255, 0.84);