Skip to content
This repository was archived by the owner on Sep 28, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading