Skip to content

Commit

Permalink
adding filewatch from lsp
Browse files Browse the repository at this point in the history
  • Loading branch information
DaanV2 committed Mar 2, 2025
1 parent 52aeb70 commit 9634750
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 71 deletions.
2 changes: 1 addition & 1 deletion client/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function setupClient(context: vscode.ExtensionContext) {
],
synchronize: {
// Notify the server about file changes to '.clientrc files contained in the workspace
fileEvents: vscode.workspace.createFileSystemWatcher("**/.clientrc"),
fileEvents: vscode.workspace.createFileSystemWatcher("**/*.{mcfunction,json,jsonc}"),
},
middleware: {
resolveCodeLens: resolveCodeLens,
Expand Down
37 changes: 37 additions & 0 deletions server/src/lib/lsp/documents/cached.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { TextDocument } from "./text-document";

export class CacheDocuments {
private _documents: Map<string, TextDocument>;

constructor() {
this._documents = new Map<string, TextDocument>();
}

get(uri: string): TextDocument | undefined {
return this._documents.get(uri);
}
set(uri: string, document: TextDocument) {
this._documents.set(uri, document);
return this;
}
delete(uri: string) {
return this._documents.delete(uri);
}
/**
* Only add the document to the cache if the document already has the document
* @param uri
* @param document
* @returns
*/
update(uri: string, document: TextDocument) {
if (this._documents.has(uri)) {
this._documents.set(uri, document);
}

return this;
}
clear() {
this._documents.clear();
return this;
}
}
37 changes: 37 additions & 0 deletions server/src/lib/lsp/documents/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { FileChangeType, FileEvent } from "vscode-languageserver-protocol";
import { TextDocument } from "./text-document";

export interface DocumentEvent extends FileEvent {
document: TextDocument | undefined;
}

export interface DocumenterGetter {
get(uri: string): TextDocument | undefined;
}

export class LazyDocumentEvent implements DocumentEvent {
private _document: TextDocument | undefined;
private _uri: string;
private _type: FileChangeType;
private _getter: DocumenterGetter;

constructor(getter: DocumenterGetter, uri: string, type: FileChangeType) {
this._getter = getter;
this._uri = uri;
this._type = type;
this._document = undefined;
}
get uri(): string {
return this._uri;
}
get type(): FileChangeType {
return this._type;
}

get document(): TextDocument | undefined {
if (this._document) return this._document;

this._document = this._getter.get(this._uri);
return this._document;
}
}
2 changes: 2 additions & 0 deletions server/src/lib/lsp/documents/io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import * as fs from "fs";
* @returns The contents of the file or undefined when an error occured
*/
export function readDocument(uri: URI, logger: IExtendedLogger): string | undefined {
logger.debug("loading document manually", uri);

try {
switch (uri.scheme) {
case "file":
Expand Down
127 changes: 96 additions & 31 deletions server/src/lib/lsp/documents/manager.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import {
CancellationToken,
Connection,
TextDocuments,
TextDocumentSyncKind
DidChangeWatchedFilesParams,
FileChangeType,
TextDocumentSyncKind,
} from "vscode-languageserver";
import { FileOperationFilter } from "vscode-languageserver-protocol/lib/common/protocol.fileOperations";
import {
DidChangeTextDocumentParams,
DidCloseTextDocumentParams,
DidOpenTextDocumentParams,
DidSaveTextDocumentParams,
Disposable,
Emitter,
} from "vscode-languageserver-protocol";
import { URI } from "vscode-uri";
import { Processor } from "../../util";
import { ExtensionContext } from "../extension";
Expand All @@ -19,45 +27,40 @@ import { identifyDocument } from "./languageId";
import { TextDocument } from "./text-document";

import * as vscode from "vscode-languageserver-textdocument";
import { FileOperationFilter } from "vscode-languageserver-protocol/lib/common/protocol.fileOperations";
import { DocumentEvent, LazyDocumentEvent } from "./event";
import { CacheDocuments } from "./cached";

export type ContentType = string | vscode.TextDocument | undefined;
export type IDocumentManager = Pick<
DocumentManager,
"get" | "forEach" | "onDidChangeContent" | "onDidClose" | "onDidOpen" | "onDidSave"
>;

export class DocumentManager
extends BaseService
implements
Partial<IService>,
Pick<TextDocuments<TextDocument>, "onDidChangeContent" | "onDidClose" | "onDidOpen" | "onDidSave">
{
export type IDocumentManager = Pick<DocumentManager, "get" | "forEach" | "onDeleted" | "onCreated" | "onChanged">;

export class DocumentManager extends BaseService implements Partial<IService> {
public readonly name: string = "documents";
private _documents: TextDocuments<TextDocument>;
private _cachedDocuments: CacheDocuments;
private _factory: TextDocumentFactory;
private _onDeleted: Emitter<DocumentEvent>;
private _onCreated: Emitter<DocumentEvent>;
private _onChanged: Emitter<DocumentEvent>;

constructor(logger: IExtendedLogger, extension: ExtensionContext) {
super(logger.withPrefix("[documents]"), extension);

this._factory = new TextDocumentFactory(logger, extension);
this._documents = new TextDocuments(this._factory);
}
this._cachedDocuments = new CacheDocuments();

/** @inheritdoc */
get onDidOpen() {
return this._documents.onDidOpen;
this._onDeleted = new Emitter();
this._onCreated = new Emitter();
this._onChanged = new Emitter();
}
/** @inheritdoc */
get onDidChangeContent() {
return this._documents.onDidChangeContent;

get onDeleted() {
return this._onDeleted.event;
}
/** @inheritdoc */
get onDidClose() {
return this._documents.onDidClose;
get onCreated() {
return this._onCreated.event;
}
/** @inheritdoc */
get onDidSave() {
return this._documents.onDidSave;
get onChanged() {
return this._onChanged.event;
}

onInitialize(capabilities: CapabilityBuilder): void {
Expand All @@ -82,7 +85,69 @@ export class DocumentManager
}

setupHandlers(connection: Connection): void {
this._documents.listen(connection);
this.addDisposable(
connection.onDidChangeWatchedFiles(this._onDidChangeWatchedFiles.bind(this)),
connection.onDidSaveTextDocument(this._handleSave.bind(this)),
connection.onDidCloseTextDocument(this._handleClose.bind(this)),
connection.onDidOpenTextDocument(this._handleOpen.bind(this)),
connection.onDidChangeTextDocument(this._handleChanged.bind(this))
);
}

private async _onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) {
const changes = params.changes.map((m) => new LazyDocumentEvent(this, m.uri, m.type));

// Delete, then create, finally changed
const disposables: Partial<Disposable>[] = [
...changes.filter((c) => c.type === FileChangeType.Deleted).map((c) => this._processDeleted(c)),
...changes.filter((c) => c.type === FileChangeType.Created).map((c) => this._processCreated(c)),
...changes.filter((c) => c.type === FileChangeType.Changed).map((c) => this._processChanged(c)),
];

// Cleanup
disposables.forEach((d) => d?.dispose?.call(d));
}

private _processDeleted(event: LazyDocumentEvent) {
this.logger.debug("received file watch delete event", event);

this._cachedDocuments.delete(event.uri);
return this._onDeleted.fire(Object.freeze(event));
}
private _processCreated(event: LazyDocumentEvent) {
this.logger.debug("received file watch create event", event);

return this._onCreated.fire(Object.freeze(event));
}
private _processChanged(event: LazyDocumentEvent) {
this.logger.debug("received file watch change event", event);

return this._onChanged.fire(Object.freeze(event));
}
private _handleChanged(params: DidChangeTextDocumentParams) {
if (params.contentChanges.length === 0) return;
this.logger.debug("received changed event", params);

let doc = this._cachedDocuments.get(params.textDocument.uri);
if (doc) {
doc = this._factory.update(doc, params.contentChanges, params.textDocument.version);
this._cachedDocuments.set(doc.uri, doc);
}
}
private _handleOpen(params: DidOpenTextDocumentParams) {
this.logger.debug("received open event", params);

const td = params.textDocument;
const doc = this._factory.create(td.uri, td.languageId, td.version, td.text);
this._cachedDocuments.set(doc.uri, doc);
}
private _handleClose(params: DidCloseTextDocumentParams) {
this.logger.debug("received close event", params);

return this._cachedDocuments.delete(params.textDocument.uri);
}
private _handleSave(params: DidSaveTextDocumentParams) {
this.logger.debug("received saved event", params);
}

get(uri: string): TextDocument | undefined;
Expand All @@ -106,7 +171,7 @@ export class DocumentManager
return this._factory.extend(content);
}

const doc = this._documents.get(u.toString());
const doc = this._cachedDocuments.get(u.toString()) || this._cachedDocuments.get(uri);
if (doc) return this._factory.extend(doc);

const text = readDocument(u, this.logger);
Expand Down
28 changes: 8 additions & 20 deletions server/src/lib/lsp/process/document-processor.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { Connection, TextDocumentChangeEvent } from "vscode-languageserver";
import {
CreateFilesParams,
DeleteFilesParams,
RenameFilesParams
} from "vscode-languageserver-protocol";
import { CreateFilesParams, DeleteFilesParams, RenameFilesParams } from "vscode-languageserver-protocol";
import { Glob } from "../../files/glob";
import { DiagnoserService } from "../diagnostics/service";
import { DocumentEvent } from "../documents/event";
import { ContentType } from "../documents/manager";
import { TextDocument } from "../documents/text-document";
import { ExtensionContext } from "../extension";
Expand All @@ -25,24 +21,16 @@ export class DocumentProcessor extends BaseService implements Partial<IService>
onInitialize(): void {
//provides diagnostics and such
const { documents } = this.extension;
documents.onDidOpen(this.onDocumentChanged.bind(this));
documents.onDidSave(this.onDocumentChanged.bind(this));
}

setupHandlers(connection: Connection): void {
this.addDisposable(
connection.workspace.onDidCreateFiles(this.onDidCreateFiles.bind(this)),
connection.workspace.onDidDeleteFiles(this.onDidDeleteFiles.bind(this)),
connection.workspace.onDidRenameFiles(this.onDidRenameFiles.bind(this))
);
this.addDisposable(documents.onChanged(this.onDocumentChanged.bind(this)));
}

private onDocumentChanged(e: TextDocumentChangeEvent<TextDocument>) {
const doc = this.extension.documents.get(e.document.uri, e.document, e.document.languageId);
if (doc === undefined) return;
private onDocumentChanged(e: DocumentEvent) {
const document = e.document;
if (document === undefined) return;

this.process(doc);
return this.diagnose(doc)
this.process(document);
return this.diagnose(document);
}

get(uri: string): TextDocument;
Expand Down
20 changes: 1 addition & 19 deletions server/src/lib/lsp/process/workspace-processor.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { Languages } from "@blockception/shared";
import { Pack } from "bc-minecraft-bedrock-project";
import {
CancellationToken,
Connection,
TextDocumentChangeEvent,
WorkspaceFolder,
WorkspaceFoldersChangeEvent
} from "vscode-languageserver";
import { Processor, Tokens } from "../../util";
import { TextDocument } from "../documents/text-document";
import { ExtensionContext } from "../extension";
import { IExtendedLogger } from "../logger/logger";
import { BaseService } from "../services/base";
Expand All @@ -26,28 +23,13 @@ export class WorkspaceProcessor extends BaseService implements Partial<IService>
}

onInitialize(): void {
//provides diagnostics and such
const documents = this.extension.documents;
documents.onDidSave(this.onDocumentChanged.bind(this));
setImmediate(() => this.traverse());
}

setupHandlers(connection: Connection): void {
this.addDisposable(connection.workspace.onDidChangeWorkspaceFolders(this.onWorkspaceFolderChanged.bind(this)));
}

/**
* Watch for project files being update that might changes settings for the workspace
* @param e
* @returns
*/
private async onDocumentChanged(e: TextDocumentChangeEvent<TextDocument>): Promise<void> {
if (this.extension.state.workspaces.traversed === false) return;
const { document } = e;

if (document.languageId === Languages.McProjectIdentifier) {
return this.traverse();
}
}

/**
* The event that is called when any workspaces are added / removed
Expand Down

0 comments on commit 9634750

Please sign in to comment.