Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding filewatch from lsp #755

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
@@ -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,
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
@@ -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":
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";
@@ -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 {
@@ -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;
@@ -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);
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";
@@ -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;
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";
@@ -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

Unchanged files with check annotations Beta

this._onDiagnosingDone = new Emitter();
this._getFilesCache = new DataCache(DataCache.defaultTimespan);
this.documents.onDidSave(() => this._getFilesCache.clear());

Check failure on line 29 in server/src/lib/lsp/diagnostics/context.ts

GitHub Actions / 📦 Build Check

Property 'onDidSave' does not exist on type 'IDocumentManager'.

Check failure on line 29 in server/src/lib/lsp/diagnostics/context.ts

GitHub Actions / 📋 Unit Test / 📋 Test macOS-latest

Property 'onDidSave' does not exist on type 'IDocumentManager'.

Check failure on line 29 in server/src/lib/lsp/diagnostics/context.ts

GitHub Actions / 📋 Unit Test / 📋 Test ubuntu-latest

Property 'onDidSave' does not exist on type 'IDocumentManager'.

Check failure on line 29 in server/src/lib/lsp/diagnostics/context.ts

GitHub Actions / 📋 Unit Test / 📋 Test windows-latest

Property 'onDidSave' does not exist on type 'IDocumentManager'.
}
get onDiagnosingFinished() {