Skip to content

Commit 9634750

Browse files
committed
adding filewatch from lsp
1 parent 52aeb70 commit 9634750

File tree

7 files changed

+182
-71
lines changed

7 files changed

+182
-71
lines changed

client/src/client/client.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function setupClient(context: vscode.ExtensionContext) {
4040
],
4141
synchronize: {
4242
// Notify the server about file changes to '.clientrc files contained in the workspace
43-
fileEvents: vscode.workspace.createFileSystemWatcher("**/.clientrc"),
43+
fileEvents: vscode.workspace.createFileSystemWatcher("**/*.{mcfunction,json,jsonc}"),
4444
},
4545
middleware: {
4646
resolveCodeLens: resolveCodeLens,
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { TextDocument } from "./text-document";
2+
3+
export class CacheDocuments {
4+
private _documents: Map<string, TextDocument>;
5+
6+
constructor() {
7+
this._documents = new Map<string, TextDocument>();
8+
}
9+
10+
get(uri: string): TextDocument | undefined {
11+
return this._documents.get(uri);
12+
}
13+
set(uri: string, document: TextDocument) {
14+
this._documents.set(uri, document);
15+
return this;
16+
}
17+
delete(uri: string) {
18+
return this._documents.delete(uri);
19+
}
20+
/**
21+
* Only add the document to the cache if the document already has the document
22+
* @param uri
23+
* @param document
24+
* @returns
25+
*/
26+
update(uri: string, document: TextDocument) {
27+
if (this._documents.has(uri)) {
28+
this._documents.set(uri, document);
29+
}
30+
31+
return this;
32+
}
33+
clear() {
34+
this._documents.clear();
35+
return this;
36+
}
37+
}

server/src/lib/lsp/documents/event.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { FileChangeType, FileEvent } from "vscode-languageserver-protocol";
2+
import { TextDocument } from "./text-document";
3+
4+
export interface DocumentEvent extends FileEvent {
5+
document: TextDocument | undefined;
6+
}
7+
8+
export interface DocumenterGetter {
9+
get(uri: string): TextDocument | undefined;
10+
}
11+
12+
export class LazyDocumentEvent implements DocumentEvent {
13+
private _document: TextDocument | undefined;
14+
private _uri: string;
15+
private _type: FileChangeType;
16+
private _getter: DocumenterGetter;
17+
18+
constructor(getter: DocumenterGetter, uri: string, type: FileChangeType) {
19+
this._getter = getter;
20+
this._uri = uri;
21+
this._type = type;
22+
this._document = undefined;
23+
}
24+
get uri(): string {
25+
return this._uri;
26+
}
27+
get type(): FileChangeType {
28+
return this._type;
29+
}
30+
31+
get document(): TextDocument | undefined {
32+
if (this._document) return this._document;
33+
34+
this._document = this._getter.get(this._uri);
35+
return this._document;
36+
}
37+
}

server/src/lib/lsp/documents/io.ts

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import * as fs from "fs";
1111
* @returns The contents of the file or undefined when an error occured
1212
*/
1313
export function readDocument(uri: URI, logger: IExtendedLogger): string | undefined {
14+
logger.debug("loading document manually", uri);
15+
1416
try {
1517
switch (uri.scheme) {
1618
case "file":

server/src/lib/lsp/documents/manager.ts

+96-31
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
import {
22
CancellationToken,
33
Connection,
4-
TextDocuments,
5-
TextDocumentSyncKind
4+
DidChangeWatchedFilesParams,
5+
FileChangeType,
6+
TextDocumentSyncKind,
67
} from "vscode-languageserver";
7-
import { FileOperationFilter } from "vscode-languageserver-protocol/lib/common/protocol.fileOperations";
8+
import {
9+
DidChangeTextDocumentParams,
10+
DidCloseTextDocumentParams,
11+
DidOpenTextDocumentParams,
12+
DidSaveTextDocumentParams,
13+
Disposable,
14+
Emitter,
15+
} from "vscode-languageserver-protocol";
816
import { URI } from "vscode-uri";
917
import { Processor } from "../../util";
1018
import { ExtensionContext } from "../extension";
@@ -19,45 +27,40 @@ import { identifyDocument } from "./languageId";
1927
import { TextDocument } from "./text-document";
2028

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

2334
export type ContentType = string | vscode.TextDocument | undefined;
24-
export type IDocumentManager = Pick<
25-
DocumentManager,
26-
"get" | "forEach" | "onDidChangeContent" | "onDidClose" | "onDidOpen" | "onDidSave"
27-
>;
28-
29-
export class DocumentManager
30-
extends BaseService
31-
implements
32-
Partial<IService>,
33-
Pick<TextDocuments<TextDocument>, "onDidChangeContent" | "onDidClose" | "onDidOpen" | "onDidSave">
34-
{
35+
export type IDocumentManager = Pick<DocumentManager, "get" | "forEach" | "onDeleted" | "onCreated" | "onChanged">;
36+
37+
export class DocumentManager extends BaseService implements Partial<IService> {
3538
public readonly name: string = "documents";
36-
private _documents: TextDocuments<TextDocument>;
39+
private _cachedDocuments: CacheDocuments;
3740
private _factory: TextDocumentFactory;
41+
private _onDeleted: Emitter<DocumentEvent>;
42+
private _onCreated: Emitter<DocumentEvent>;
43+
private _onChanged: Emitter<DocumentEvent>;
3844

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

4248
this._factory = new TextDocumentFactory(logger, extension);
43-
this._documents = new TextDocuments(this._factory);
44-
}
49+
this._cachedDocuments = new CacheDocuments();
4550

46-
/** @inheritdoc */
47-
get onDidOpen() {
48-
return this._documents.onDidOpen;
51+
this._onDeleted = new Emitter();
52+
this._onCreated = new Emitter();
53+
this._onChanged = new Emitter();
4954
}
50-
/** @inheritdoc */
51-
get onDidChangeContent() {
52-
return this._documents.onDidChangeContent;
55+
56+
get onDeleted() {
57+
return this._onDeleted.event;
5358
}
54-
/** @inheritdoc */
55-
get onDidClose() {
56-
return this._documents.onDidClose;
59+
get onCreated() {
60+
return this._onCreated.event;
5761
}
58-
/** @inheritdoc */
59-
get onDidSave() {
60-
return this._documents.onDidSave;
62+
get onChanged() {
63+
return this._onChanged.event;
6164
}
6265

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

8487
setupHandlers(connection: Connection): void {
85-
this._documents.listen(connection);
88+
this.addDisposable(
89+
connection.onDidChangeWatchedFiles(this._onDidChangeWatchedFiles.bind(this)),
90+
connection.onDidSaveTextDocument(this._handleSave.bind(this)),
91+
connection.onDidCloseTextDocument(this._handleClose.bind(this)),
92+
connection.onDidOpenTextDocument(this._handleOpen.bind(this)),
93+
connection.onDidChangeTextDocument(this._handleChanged.bind(this))
94+
);
95+
}
96+
97+
private async _onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams) {
98+
const changes = params.changes.map((m) => new LazyDocumentEvent(this, m.uri, m.type));
99+
100+
// Delete, then create, finally changed
101+
const disposables: Partial<Disposable>[] = [
102+
...changes.filter((c) => c.type === FileChangeType.Deleted).map((c) => this._processDeleted(c)),
103+
...changes.filter((c) => c.type === FileChangeType.Created).map((c) => this._processCreated(c)),
104+
...changes.filter((c) => c.type === FileChangeType.Changed).map((c) => this._processChanged(c)),
105+
];
106+
107+
// Cleanup
108+
disposables.forEach((d) => d?.dispose?.call(d));
109+
}
110+
111+
private _processDeleted(event: LazyDocumentEvent) {
112+
this.logger.debug("received file watch delete event", event);
113+
114+
this._cachedDocuments.delete(event.uri);
115+
return this._onDeleted.fire(Object.freeze(event));
116+
}
117+
private _processCreated(event: LazyDocumentEvent) {
118+
this.logger.debug("received file watch create event", event);
119+
120+
return this._onCreated.fire(Object.freeze(event));
121+
}
122+
private _processChanged(event: LazyDocumentEvent) {
123+
this.logger.debug("received file watch change event", event);
124+
125+
return this._onChanged.fire(Object.freeze(event));
126+
}
127+
private _handleChanged(params: DidChangeTextDocumentParams) {
128+
if (params.contentChanges.length === 0) return;
129+
this.logger.debug("received changed event", params);
130+
131+
let doc = this._cachedDocuments.get(params.textDocument.uri);
132+
if (doc) {
133+
doc = this._factory.update(doc, params.contentChanges, params.textDocument.version);
134+
this._cachedDocuments.set(doc.uri, doc);
135+
}
136+
}
137+
private _handleOpen(params: DidOpenTextDocumentParams) {
138+
this.logger.debug("received open event", params);
139+
140+
const td = params.textDocument;
141+
const doc = this._factory.create(td.uri, td.languageId, td.version, td.text);
142+
this._cachedDocuments.set(doc.uri, doc);
143+
}
144+
private _handleClose(params: DidCloseTextDocumentParams) {
145+
this.logger.debug("received close event", params);
146+
147+
return this._cachedDocuments.delete(params.textDocument.uri);
148+
}
149+
private _handleSave(params: DidSaveTextDocumentParams) {
150+
this.logger.debug("received saved event", params);
86151
}
87152

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

109-
const doc = this._documents.get(u.toString());
174+
const doc = this._cachedDocuments.get(u.toString()) || this._cachedDocuments.get(uri);
110175
if (doc) return this._factory.extend(doc);
111176

112177
const text = readDocument(u, this.logger);

server/src/lib/lsp/process/document-processor.ts

+8-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
import { Connection, TextDocumentChangeEvent } from "vscode-languageserver";
2-
import {
3-
CreateFilesParams,
4-
DeleteFilesParams,
5-
RenameFilesParams
6-
} from "vscode-languageserver-protocol";
1+
import { CreateFilesParams, DeleteFilesParams, RenameFilesParams } from "vscode-languageserver-protocol";
72
import { Glob } from "../../files/glob";
83
import { DiagnoserService } from "../diagnostics/service";
4+
import { DocumentEvent } from "../documents/event";
95
import { ContentType } from "../documents/manager";
106
import { TextDocument } from "../documents/text-document";
117
import { ExtensionContext } from "../extension";
@@ -25,24 +21,16 @@ export class DocumentProcessor extends BaseService implements Partial<IService>
2521
onInitialize(): void {
2622
//provides diagnostics and such
2723
const { documents } = this.extension;
28-
documents.onDidOpen(this.onDocumentChanged.bind(this));
29-
documents.onDidSave(this.onDocumentChanged.bind(this));
30-
}
3124

32-
setupHandlers(connection: Connection): void {
33-
this.addDisposable(
34-
connection.workspace.onDidCreateFiles(this.onDidCreateFiles.bind(this)),
35-
connection.workspace.onDidDeleteFiles(this.onDidDeleteFiles.bind(this)),
36-
connection.workspace.onDidRenameFiles(this.onDidRenameFiles.bind(this))
37-
);
25+
this.addDisposable(documents.onChanged(this.onDocumentChanged.bind(this)));
3826
}
3927

40-
private onDocumentChanged(e: TextDocumentChangeEvent<TextDocument>) {
41-
const doc = this.extension.documents.get(e.document.uri, e.document, e.document.languageId);
42-
if (doc === undefined) return;
28+
private onDocumentChanged(e: DocumentEvent) {
29+
const document = e.document;
30+
if (document === undefined) return;
4331

44-
this.process(doc);
45-
return this.diagnose(doc)
32+
this.process(document);
33+
return this.diagnose(document);
4634
}
4735

4836
get(uri: string): TextDocument;

server/src/lib/lsp/process/workspace-processor.ts

+1-19
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import { Languages } from "@blockception/shared";
21
import { Pack } from "bc-minecraft-bedrock-project";
32
import {
43
CancellationToken,
54
Connection,
6-
TextDocumentChangeEvent,
75
WorkspaceFolder,
86
WorkspaceFoldersChangeEvent
97
} from "vscode-languageserver";
108
import { Processor, Tokens } from "../../util";
11-
import { TextDocument } from "../documents/text-document";
129
import { ExtensionContext } from "../extension";
1310
import { IExtendedLogger } from "../logger/logger";
1411
import { BaseService } from "../services/base";
@@ -26,28 +23,13 @@ export class WorkspaceProcessor extends BaseService implements Partial<IService>
2623
}
2724

2825
onInitialize(): void {
29-
//provides diagnostics and such
30-
const documents = this.extension.documents;
31-
documents.onDidSave(this.onDocumentChanged.bind(this));
26+
setImmediate(() => this.traverse());
3227
}
3328

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

38-
/**
39-
* Watch for project files being update that might changes settings for the workspace
40-
* @param e
41-
* @returns
42-
*/
43-
private async onDocumentChanged(e: TextDocumentChangeEvent<TextDocument>): Promise<void> {
44-
if (this.extension.state.workspaces.traversed === false) return;
45-
const { document } = e;
46-
47-
if (document.languageId === Languages.McProjectIdentifier) {
48-
return this.traverse();
49-
}
50-
}
5133

5234
/**
5335
* The event that is called when any workspaces are added / removed

0 commit comments

Comments
 (0)