WIP lsp-manager effort 2
This commit is contained in:
@@ -31,7 +31,7 @@ export class PaneHandleDirective {
|
||||
!target.classList.contains("hr-pane-handle") &&
|
||||
!target.classList.contains("vr-pane-handle")
|
||||
) {
|
||||
console.log("Must have 'hr-pane-handle' or 'vr-pane-handle' in classList!");
|
||||
console.error("Must have 'hr-pane-handle' or 'vr-pane-handle' in classList!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -14,8 +14,9 @@ import { ServiceMessage } from '../../../types/service-message.type';
|
||||
export class LspManagerService {
|
||||
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
|
||||
|
||||
lspConfigData!: {};
|
||||
languageProviders: {} = {};
|
||||
workspaceFolder: string = "";
|
||||
lspConfigDataStr: string = "";
|
||||
languageProviders: {} = {};
|
||||
|
||||
|
||||
constructor() {
|
||||
@@ -24,69 +25,103 @@ export class LspManagerService {
|
||||
|
||||
public loadLspConfigData(): Promise<string | void> {
|
||||
return 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 = {};
|
||||
}
|
||||
|
||||
this.lspConfigDataStr = lspConfigData;
|
||||
return lspConfigData;
|
||||
});
|
||||
}
|
||||
|
||||
public registerEditor(editor: any): void {
|
||||
let modeParts = editor.getSession()["$modeId"].split("/");
|
||||
let mode = modeParts[ modeParts.length - 1 ];
|
||||
public registerEditorToLSPClient(editor: any) {
|
||||
let mode = this.getMode(editor.session);
|
||||
|
||||
if ( !this.languageProviders[mode] ) {
|
||||
this.languageProviders[mode] = this.getLanguageProviderWithClientServer(mode);
|
||||
if ( this.languageProviders[mode] ) {
|
||||
this.languageProviders[mode].registerEditor(editor);
|
||||
return;
|
||||
}
|
||||
|
||||
this.languageProviders[mode].registerEditor(editor);
|
||||
this.languageProviders[mode]?.registerEditor(editor);
|
||||
}
|
||||
|
||||
private getLspConfigData(): Promise<string> {
|
||||
return window.fs.getLspConfigData();
|
||||
}
|
||||
|
||||
private getLanguageProviderWithClientServer(mode: string) {
|
||||
let _initializationOptions = {};
|
||||
private parseAndReturnLSPConfigData(): {} {
|
||||
let configData = JSON.parse(
|
||||
this.lspConfigDataStr.replaceAll("{workspace.folder}", this.workspaceFolder)
|
||||
);
|
||||
|
||||
if ( Object.keys(this.lspConfigData).length !== 0 && this.lspConfigData[mode] ) {
|
||||
_initializationOptions = this.lspConfigData[mode]["initialization-options"];
|
||||
if (configData["message"]) {
|
||||
console.warn(
|
||||
"Warning: LSP this.lspConfigDataStr is a 'message'",
|
||||
this.lspConfigDataStr
|
||||
);
|
||||
|
||||
configData = {};
|
||||
}
|
||||
|
||||
let servers: LanguageClientConfig[] = [
|
||||
{
|
||||
module: () => import("ace-linters/build/language-client"),
|
||||
modes: mode,
|
||||
type: "socket",
|
||||
socket: new WebSocket(`ws://127.0.0.1:9999/${mode}`),
|
||||
// socket: new WebSocket("ws://127.0.0.1:9999/?name=pylsp"),
|
||||
initializationOptions: _initializationOptions
|
||||
}
|
||||
];
|
||||
|
||||
return AceLanguageClient.for(servers);
|
||||
return configData;
|
||||
}
|
||||
|
||||
private getLanguageProviderWithWebWorker() {
|
||||
private getInitializationOptions(mode: string, configData: {}): {} {
|
||||
let _initializationOptions = {};
|
||||
|
||||
if ( Object.keys(configData).length !== 0 && configData[mode] ) {
|
||||
_initializationOptions = configData[mode]["initialization-options"];
|
||||
}
|
||||
|
||||
return _initializationOptions;
|
||||
}
|
||||
|
||||
public createLanguageProviderWithClientServer(mode: string): LanguageProvider {
|
||||
if ( this.languageProviders[mode] ) return;
|
||||
let servers: LanguageClientConfig[] = [];
|
||||
|
||||
try {
|
||||
let configData = this.parseAndReturnLSPConfigData();
|
||||
let _initializationOptions = this.getInitializationOptions(mode, configData);
|
||||
servers = [
|
||||
{
|
||||
module: () => import("ace-linters/build/language-client"),
|
||||
modes: mode,
|
||||
type: "socket",
|
||||
socket: new WebSocket( configData[mode]["socket"] ),
|
||||
initializationOptions: _initializationOptions
|
||||
}
|
||||
];
|
||||
} catch(error) {
|
||||
console.error(
|
||||
"Error: Language Server could not be loaded OR doesn't exist in Newton-LSP config setup...",
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.languageProviders[mode] = AceLanguageClient.for(servers);
|
||||
// this.languageProviders[mode].requireFilePath = true;
|
||||
this.languageProviders[mode].changeWorkspaceFolder(this.workspaceFolder);
|
||||
return this.languageProviders[mode];
|
||||
}
|
||||
|
||||
private getLanguageProviderWithWebWorker(): LanguageProvider {
|
||||
let worker = new Worker(new URL('./webworker.js', import.meta.url));
|
||||
return LanguageProvider.create(worker);
|
||||
}
|
||||
|
||||
protected setSessionFilePath(session: any, mode: string = "", filePath: string = "") {
|
||||
if ( !session || !mode || !filePath || !this.languageProviders[mode] ) return;
|
||||
public setSessionFilePath(session: any, filePath: string = "") {
|
||||
if ( !session || !filePath ) return;
|
||||
let mode = this.getMode(session);
|
||||
if ( !this.languageProviders[mode] ) return;
|
||||
this.languageProviders[mode].setSessionFilePath(session, filePath);
|
||||
}
|
||||
|
||||
protected closeDocument(session: any, mode: string) {
|
||||
if ( !session || !mode || !this.languageProviders[mode] ) return;
|
||||
public getMode(session: any): string {
|
||||
return session.getMode()["$id"].replace("ace/mode/", "");
|
||||
}
|
||||
|
||||
public closeDocument(session: any) {
|
||||
if ( !session ) return;
|
||||
let mode = this.getMode(session);
|
||||
if ( !this.languageProviders[mode] ) return;
|
||||
this.languageProviders[mode].closeDocument(session);
|
||||
}
|
||||
|
||||
|
@@ -89,13 +89,14 @@ export class FilesService {
|
||||
if (loadFileContents)
|
||||
data = await window.fs.getFileContents(file.path);
|
||||
|
||||
file.session = new EditSession(data);
|
||||
file.session = new EditSession(data);
|
||||
file.session["id"] = path;
|
||||
file.session.setUndoManager( new UndoManager() );
|
||||
file.session.setMode( getModeForPath( file.path ).mode );
|
||||
|
||||
this.files.set(file.path, file);
|
||||
} catch (error) {
|
||||
console.log(
|
||||
console.error(
|
||||
`---- Error ----\nPath: ${path}\nMessage: ${error}`
|
||||
);
|
||||
}
|
||||
|
@@ -2,4 +2,4 @@ export abstract class EditorType {
|
||||
static MiniMap: string = "mini-map";
|
||||
static ReadOnly: string = "read-only";
|
||||
static Standalone: string = "standalone";
|
||||
}
|
||||
}
|
@@ -2,6 +2,10 @@ 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";
|
||||
@@ -59,8 +63,8 @@ export class CodeViewComponent extends CodeViewBase {
|
||||
this.editor = ace.edit( this.editorElm.nativeElement );
|
||||
this.editor.setOptions( this.editorSettings.CONFIG );
|
||||
|
||||
this.editorsService.set(this.uuid, this);
|
||||
if (this.isDefault) {
|
||||
this.editorsService.set(this.uuid, this);
|
||||
this.editorsService.setActiveEditor(this.uuid);
|
||||
this.addActiveStyling();
|
||||
this.editor.focus();
|
||||
@@ -128,11 +132,11 @@ export class CodeViewComponent extends CodeViewBase {
|
||||
|
||||
this.editorsService.sendMessage(message);
|
||||
this.searchReplaceService.sendMessage(message);
|
||||
this.editorsService.sendMessage(message);
|
||||
|
||||
message = new ServiceMessage();
|
||||
message.action = "set-active-editor";
|
||||
message.rawData = this;
|
||||
this.lspManagerService.sendMessage(message);
|
||||
this.markdownPreviewService.sendMessage(message);
|
||||
|
||||
this.updateInfoBar();
|
||||
@@ -175,6 +179,12 @@ export class CodeViewComponent extends CodeViewBase {
|
||||
});
|
||||
|
||||
this.editor.on("changeSession", (session) => {
|
||||
let message = new ServiceMessage();
|
||||
message.action = "editor-update";
|
||||
message.rawData = this;
|
||||
|
||||
this.lspManagerService.sendMessage(message);
|
||||
|
||||
this.updateInfoBar();
|
||||
});
|
||||
}
|
||||
|
@@ -139,6 +139,7 @@ export class EditorsComponent {
|
||||
}
|
||||
}
|
||||
|
||||
activeComponent.lspManagerService.closeDocument(file.session);
|
||||
this.filesService.delete(file);
|
||||
}
|
||||
|
||||
@@ -192,7 +193,7 @@ export class EditorsComponent {
|
||||
});
|
||||
|
||||
window.fs.onUpdateFilePath(async (path: string) => {
|
||||
console.log(path);
|
||||
console.log("TODO (onUpdateFilePath) :", path);
|
||||
// this.tabsService.sendMessage(message);
|
||||
// this.filesService.sendMessage(message);
|
||||
});
|
||||
|
@@ -1,4 +1,11 @@
|
||||
.lsp-config-text {
|
||||
display: grid;
|
||||
min-height: 25em;
|
||||
}
|
||||
}
|
||||
|
||||
.clear-left-padding {
|
||||
padding-left: 0px;
|
||||
}
|
||||
.clear-right-padding {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
@@ -2,24 +2,63 @@
|
||||
|
||||
<div class="row mt-2 mb-3">
|
||||
|
||||
<div class="col">
|
||||
<div class="col clear-right-padding">
|
||||
<div class="input-group-sm">
|
||||
<input class="form-control" placeholder="Project Path..." />
|
||||
<label class="form-control" [innerText]="lspManagerService.workspaceFolder || 'Project Path...'">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col col-auto clear-left-padding">
|
||||
<button class="btn btn-sm btn-dark" (click)="clearWorkspaceFolder()">x</button>
|
||||
</div>
|
||||
|
||||
<div class="col col-auto">
|
||||
<div class="input-group-sm">
|
||||
<button class="btn btn-sm btn-dark">Choose Directory</button>
|
||||
<button class="btn btn-sm btn-dark" (click)="setWorkspaceFolder()">Workspace Folder</button>
|
||||
<button class="btn btn-sm btn-danger ms-5" (click)="hideLspManager()">X</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row mt-2 md-2">
|
||||
<div class="col">
|
||||
LSP Configs:
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<code-view #lspEditorComponent [mode]="'standalone'" class="lsp-config-text"></code-view>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2 md-2">
|
||||
<div class="col col-sm" [hidden]="!lspManagerService.workspaceFolder">
|
||||
<button class="btn btn-sm btn-dark" (click)="createLanguageClient()">Create Language Client</button>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
Target Editor: <label [innerText]="editor?.id || '...'"></label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm" [hidden]="!lspManagerService.workspaceFolder">
|
||||
<button class="btn btn-sm btn-dark"
|
||||
(click)="registerEditorToLanguageClient()">
|
||||
Register Editor To LSP
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
Active Editor Session:
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
<code-view #editorComponent [mode]="'standalone'" class="lsp-config-text"></code-view>
|
||||
<code-view #sessionEditorComponent [mode]="'read-only'" class="lsp-config-text"></code-view>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, ElementRef, HostBinding, ViewChild, inject } from '@angular/core';
|
||||
import { Component, ChangeDetectorRef, ElementRef, HostBinding, ViewChild, inject } from '@angular/core';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
|
||||
import { LspManagerService } from '../../common/services/editor/lsp-manager/lsp-manager.service';
|
||||
@@ -23,14 +23,20 @@ import { ServiceMessage } from '../../common/types/service-message.type';
|
||||
}
|
||||
})
|
||||
export class LspManagerComponent {
|
||||
private unsubscribe: Subject<void> = new Subject();
|
||||
private unsubscribe: Subject<void> = new Subject();
|
||||
private changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
|
||||
|
||||
private lspManagerService: LspManagerService = inject(LspManagerService);
|
||||
lspManagerService: LspManagerService = inject(LspManagerService);
|
||||
|
||||
@HostBinding("class.hidden") isHidden: boolean = true;
|
||||
@ViewChild('editorComponent') editorComponent!: CodeViewComponent;
|
||||
lspTextEditor!: any;
|
||||
private editor: any;
|
||||
@ViewChild('lspEditorComponent') lspEditorComponent!: CodeViewComponent;
|
||||
@ViewChild('sessionEditorComponent') sessionEditorComponent!: CodeViewComponent;
|
||||
lspTextEditor: any;
|
||||
innerEditor: any;
|
||||
editor: any;
|
||||
activeFile: any;
|
||||
|
||||
|
||||
|
||||
|
||||
constructor() {
|
||||
@@ -38,13 +44,7 @@ export class LspManagerComponent {
|
||||
|
||||
|
||||
private ngAfterViewInit(): void {
|
||||
this.lspTextEditor = this.editorComponent.editor;
|
||||
|
||||
this.lspManagerService.loadLspConfigData().then((lspConfigData) => {
|
||||
this.lspTextEditor.session.setMode("ace/mode/json");
|
||||
this.lspTextEditor.session.setValue(lspConfigData);
|
||||
});
|
||||
|
||||
this.mapEditorsAndLoadConfig();
|
||||
this.loadSubscribers();
|
||||
}
|
||||
|
||||
@@ -53,6 +53,16 @@ export class LspManagerComponent {
|
||||
this.unsubscribe.complete();
|
||||
}
|
||||
|
||||
private mapEditorsAndLoadConfig() {
|
||||
this.lspTextEditor = this.lspEditorComponent.editor;
|
||||
this.innerEditor = this.sessionEditorComponent.editor;
|
||||
|
||||
this.lspManagerService.loadLspConfigData().then((lspConfigData) => {
|
||||
this.lspTextEditor.session.setMode("ace/mode/json");
|
||||
this.lspTextEditor.session.setValue(lspConfigData);
|
||||
});
|
||||
}
|
||||
|
||||
private loadSubscribers() {
|
||||
this.lspManagerService.getMessage$().pipe(
|
||||
takeUntil(this.unsubscribe)
|
||||
@@ -61,28 +71,90 @@ export class LspManagerComponent {
|
||||
this.toggleLspManager(message);
|
||||
} else if (message.action === "set-active-editor") {
|
||||
this.setActiveEditor(message);
|
||||
} else if (message.action === "editor-update") {
|
||||
this.editorUpdate(message);
|
||||
} else if (message.action === "close-file") {
|
||||
this.closeFile(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public hideLspManager() {
|
||||
this.isHidden = true;
|
||||
this.editor.focus();
|
||||
public clearWorkspaceFolder() {
|
||||
this.lspManagerService.workspaceFolder = "";
|
||||
}
|
||||
|
||||
public setWorkspaceFolder() {
|
||||
window.fs.chooseFolder().then((folder: string) => {
|
||||
if (!folder) return;
|
||||
|
||||
this.lspManagerService.workspaceFolder = folder;
|
||||
});
|
||||
}
|
||||
|
||||
public createLanguageClient() {
|
||||
let mode = this.lspManagerService.getMode(this.editor.session);
|
||||
this.lspManagerService.createLanguageProviderWithClientServer(mode);
|
||||
}
|
||||
|
||||
public registerEditorToLanguageClient() {
|
||||
this.lspManagerService.registerEditorToLSPClient(this.editor);
|
||||
/*
|
||||
this.lspManagerService.setSessionFilePath(
|
||||
this.editor.session,
|
||||
this.activeFile.path
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
public globalLspManagerKeyHandler(event: any) {
|
||||
if (event.ctrlKey && event.shiftKey && event.key === "l") {
|
||||
this.hideLspManager();
|
||||
}
|
||||
}
|
||||
|
||||
public hideLspManager() {
|
||||
this.isHidden = true;
|
||||
this.editor.focus();
|
||||
}
|
||||
|
||||
private toggleLspManager(message: ServiceMessage) {
|
||||
this.isHidden = !this.isHidden;
|
||||
|
||||
if (this.isHidden) return;
|
||||
|
||||
// Note: hack for issue with setActiveEditor TODO
|
||||
setTimeout(() => {
|
||||
this.innerEditor.setSession(this.editor.getSession());
|
||||
}, 10);
|
||||
}
|
||||
|
||||
private setActiveEditor(message: ServiceMessage) {
|
||||
this.editor = message.rawData;
|
||||
this.editor = message.rawData.editor;
|
||||
this.activeFile = message.rawData.activeFile;
|
||||
|
||||
// TODO: figure out why this doesn't update the session consistently...
|
||||
// It seems maybe bound to visible state as change detector ref didn't help either.
|
||||
// this.innerEditor.setSession(this.editor.session);
|
||||
}
|
||||
|
||||
private editorUpdate(message: ServiceMessage) {
|
||||
if (!message.rawData.activeFile) return;
|
||||
|
||||
this.editor.setSession(message.rawData.editor.getSession())
|
||||
this.activeFile = message.rawData.activeFile;
|
||||
|
||||
/*
|
||||
this.lspManagerService.setSessionFilePath(
|
||||
this.editor.session,
|
||||
this.activeFile.path
|
||||
);
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
private closeFile(message: ServiceMessage) {
|
||||
this.lspManagerService.closeDocument(message.rawData);
|
||||
}
|
||||
|
||||
}
|
@@ -61,7 +61,7 @@ export class MarkdownPreviewComponent {
|
||||
|
||||
setTimeout(() => {
|
||||
this.updatePreview();
|
||||
}, 200);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
private setActiveEditor(message: ServiceMessage) {
|
||||
|
@@ -29,6 +29,7 @@ declare global {
|
||||
openFiles: (arg0) => Promise<string>,
|
||||
saveFile: (arg0: any, arg1: any) => Promise<string>,
|
||||
saveFileAs: () => Promise<string>,
|
||||
chooseFolder: () => Promise<string>,
|
||||
closeFile: (arg0: any) => Promise<string>,
|
||||
getPathForFile: any,
|
||||
onLoadFiles: (arg0: any) => Promise<string>,
|
||||
|
Reference in New Issue
Block a user