Skip to content

Commit 8531e4e

Browse files
committed
clangd improvements
- define file system endpoints - clangd LS uses message port for communication - Use wtm new ComChannelEndpoints for handling async communication of message channels or workers - worker transfers files to client via message channel - clangd example: list open files below editor - Prototype: File system related code added to monaco-languageclient/fs
1 parent f4fd13a commit 8531e4e

26 files changed

+7375
-247
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Click [here](https://www.typefox.io/blog/teaching-the-language-server-protocol-t
2727
- [Python Language client and pyright language server example (Location)](#python-language-client-and-pyright-language-server-example-location)
2828
- [Groovy Language client and language server example (Location)](#groovy-language-client-and-language-server-example-location)
2929
- [Java Language client and language server example (Location)](#java-language-client-and-language-server-example-location)
30+
- [Cpp / Clangd (Location)](#cpp--clangd-location)
3031
- [Langium grammar DSL (Location)](#langium-grammar-dsl-location)
3132
- [Statemachine DSL (created with Langium) (Location)](#statemachine-dsl-created-with-langium-location)
3233
- [bare monaco-languageclient (Location)](#bare-monaco-languageclient-location)
@@ -138,6 +139,10 @@ The **java-client** contains the [monaco-editor-wrapper app](./packages/examples
138139

139140
Langium examples (here client and server communicate via `vscode-languageserver-protocol/browser` instead of a web socket used in the three examples above
140141

142+
#### Cpp / Clangd ([Location](./packages/examples/src/clangd))
143+
144+
It contains both the [language client](./packages/examples/src/clangd/client/main.ts) and the [langauge server (web worker)](./packages/examples/src/clangd/worker/clangd-server.ts). The clangd language server is compiled to wasm so it can be executed in the browser.
145+
141146
#### Langium grammar DSL ([Location](./packages/examples/src/langium/langium-dsl))
142147

143148
It contains both the [language client](./packages/examples/src/langium/langium-dsl/wrapperLangium.ts) and the [langauge server (web worker)](./packages/examples/src/langium/langium-dsl/worker/langium-server.ts). Here you can chose beforehand if the wrapper should be started in classic or extended mode.

package-lock.json

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/client/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ All notable changes to this npm module are documented in this file.
44

55
## [9.0.0-next.5] - 2024-10-23
66

7+
- Prototype: File system endpoint.
78
- Added `createUrl` to `monaco-languageclient/tools`. Moved it here from `monaco-editor-wrapper`.
89
- Updated to eslint 9
910
- Support all arguments for monaco-vscode-api `initialize` [#756](https://github.com/TypeFox/monaco-languageclient/pull/756)

packages/client/package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
"./vscode/services": {
3232
"types": "./lib/vscode/index.d.ts",
3333
"default": "./lib/vscode/index.js"
34+
},
35+
"./fs": {
36+
"types": "./lib/fs/index.d.ts",
37+
"default": "./lib/fs/index.js"
3438
}
3539
},
3640
"typesVersions": {
@@ -43,6 +47,9 @@
4347
],
4448
"vscode/services": [
4549
"lib/vscode/index"
50+
],
51+
"fs": [
52+
"lib/fs/index"
4653
]
4754
}
4855
},

packages/client/src/fs/definitions.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) 2024 TypeFox and others.
3+
* Licensed under the MIT License. See LICENSE in the package root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
6+
import { Logger } from 'monaco-languageclient/tools';
7+
8+
export interface FileReadRequest {
9+
resourceUri: string
10+
}
11+
12+
export type FileReadResultStatus = 'success' | 'denied';
13+
14+
export interface FileReadRequestResult {
15+
status: FileReadResultStatus
16+
content: string | ArrayBuffer
17+
}
18+
19+
export interface FileUpdate {
20+
resourceUri: string
21+
content: string | ArrayBuffer
22+
}
23+
24+
export type FileUpdateResultStatus = 'equal' | 'updated' | 'created' | 'denied';
25+
26+
export interface FileUpdateResult {
27+
status: FileUpdateResultStatus
28+
message?: string
29+
}
30+
31+
export interface DirectoryListingRequest {
32+
directoryUri: string
33+
}
34+
35+
export interface DirectoryListingRequestResult {
36+
files: string[]
37+
}
38+
39+
export type StatsRequestType = 'directory' | 'file';
40+
41+
export interface StatsRequest {
42+
type: StatsRequestType,
43+
resourceUri: string
44+
}
45+
46+
export interface StatsRequestResult {
47+
type: StatsRequestType
48+
size: number
49+
name: string
50+
mtime: number
51+
}
52+
53+
export enum EndpointType {
54+
DRIVER,
55+
FOLLOWER,
56+
LOCAL,
57+
EMPTY
58+
}
59+
60+
export interface FileSystemCapabilities {
61+
62+
/**
63+
* Get a text file content
64+
* @param params the resourceUri of the file
65+
* @returns The ReadFileResult containing the content of the file
66+
*/
67+
readFile(params: FileReadRequest): Promise<FileReadRequestResult>
68+
69+
/**
70+
* Save a file on the filesystem
71+
* @param params the resourceUri and the content of the file
72+
* @returns The FileUpdateResult containing the result of the operation and an optional message
73+
*/
74+
writeFile(params: FileUpdate): Promise<FileUpdateResult>;
75+
76+
/**
77+
* The implementation has to decide if the file at given uri at need to be updated
78+
* @param params the resourceUri and the content of the file
79+
* @returns The FileUpdateResult containing the result of the operation and an optional message
80+
*/
81+
syncFile(params: FileUpdate): Promise<FileUpdateResult>;
82+
83+
/**
84+
* Get file stats on a given file
85+
* @param params the resourceUri and if a file or a directory is requested
86+
*/
87+
getFileStats(params: StatsRequest): Promise<StatsRequestResult>
88+
89+
/**
90+
* List the files of a directory
91+
* @param resourceUri the Uri of the directory
92+
*/
93+
listFiles(params: DirectoryListingRequest): Promise<DirectoryListingRequestResult>
94+
95+
}
96+
97+
/**
98+
* Defines the APT for a file system endpoint
99+
*/
100+
export interface FileSystemEndpoint extends FileSystemCapabilities {
101+
102+
/**
103+
* Whatever can't be handled in the constructor should be done here
104+
*/
105+
init?(): void;
106+
107+
/**
108+
* Set an optional logger
109+
* @param logger the logger implemenation
110+
*/
111+
setLogger?(logger: Logger): void;
112+
113+
/**
114+
* Get the type of the client
115+
*/
116+
getEndpointType(): EndpointType;
117+
118+
/**
119+
* Provide info about the file system
120+
*/
121+
getFileSystemInfo(): string;
122+
123+
/**
124+
* Signal readiness
125+
*/
126+
ready?(): void;
127+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) 2024 TypeFox and others.
3+
* Licensed under the MIT License. See LICENSE in the package root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
6+
import { Logger } from 'monaco-languageclient/tools';
7+
import { DirectoryListingRequest, DirectoryListingRequestResult, EndpointType, FileReadRequest, FileReadRequestResult, FileSystemEndpoint, FileUpdate, FileUpdateResult, StatsRequest, StatsRequestResult } from '../definitions.js';
8+
9+
export class EmptyFileSystemEndpoint implements FileSystemEndpoint {
10+
11+
private endpointType: EndpointType;
12+
private logger?: Logger;
13+
14+
constructor(endpointType: EndpointType) {
15+
this.endpointType = endpointType;
16+
}
17+
18+
init(): void { }
19+
20+
getFileSystemInfo(): string {
21+
return 'This file system performs no operations.';
22+
}
23+
24+
setLogger(logger: Logger): void {
25+
this.logger = logger;
26+
}
27+
28+
getEndpointType(): EndpointType {
29+
return this.endpointType;
30+
}
31+
32+
readFile(params: FileReadRequest): Promise<FileReadRequestResult> {
33+
this.logger?.info(`Reading file: ${params.resourceUri}`);
34+
return Promise.resolve({
35+
status: 'denied',
36+
content: ''
37+
});
38+
}
39+
40+
writeFile(params: FileUpdate): Promise<FileUpdateResult> {
41+
this.logger?.info(`Writing file: ${params.resourceUri}`);
42+
return Promise.resolve({ status: 'denied' });
43+
}
44+
45+
syncFile(params: FileUpdate): Promise<FileUpdateResult> {
46+
this.logger?.info(`Syncing file: ${params.resourceUri}`);
47+
return Promise.resolve({ status: 'denied' });
48+
}
49+
50+
getFileStats(params: StatsRequest): Promise<StatsRequestResult> {
51+
this.logger?.info(`Getting file stats for: "${params.resourceUri}" (${params.type})`);
52+
return Promise.reject('No stats available.');
53+
}
54+
55+
listFiles(params: DirectoryListingRequest): Promise<DirectoryListingRequestResult> {
56+
this.logger?.info(`Listing files for directory: "${params.directoryUri}"`);
57+
return Promise.reject('No file listing possible.');
58+
}
59+
60+
}

packages/client/src/fs/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) 2024 TypeFox and others.
3+
* Licensed under the MIT License. See LICENSE in the package root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
6+
export * from './definitions.js';
7+
export * from './endpoints/defaultEndpoint.js';
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) 2024 TypeFox and others.
3+
* Licensed under the MIT License. See LICENSE in the package root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
6+
import { describe, expect, test } from 'vitest';
7+
import { EmptyFileSystemEndpoint, EndpointType } from 'monaco-languageclient/fs';
8+
9+
describe('EmptyFileSystemEndpoint Tests', () => {
10+
11+
const endpoint = new EmptyFileSystemEndpoint(EndpointType.EMPTY);
12+
13+
test('readFile', async () => {
14+
const result = await endpoint.readFile({ resourceUri: '/tmp/test.js' });
15+
expect(result).toEqual({
16+
status: 'denied',
17+
content: ''
18+
});
19+
});
20+
21+
test('writeFile', async () => {
22+
const result = await endpoint.writeFile({
23+
resourceUri: '/tmp/test.js',
24+
content: 'console.log("Hello World!");'
25+
});
26+
expect(result).toEqual({
27+
status: 'denied'
28+
});
29+
});
30+
31+
test('syncFile', async () => {
32+
const result = await endpoint.syncFile({
33+
resourceUri: '/tmp/test.js',
34+
content: 'console.log("Hello World!");'
35+
});
36+
expect(result).toEqual({
37+
status: 'denied'
38+
});
39+
});
40+
41+
test('getFileStats', async () => {
42+
expect(async () => {
43+
await endpoint.getFileStats({
44+
type: 'file',
45+
resourceUri: '/tmp/test.js'
46+
});
47+
}).rejects.toThrowError('No stats available.');
48+
});
49+
50+
test('listFiles', async () => {
51+
expect(async () => {
52+
await endpoint.listFiles({
53+
directoryUri: '/tmp'
54+
});
55+
}).rejects.toThrowError('No file listing possible.');
56+
});
57+
58+
});

packages/examples/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
All notable changes to this npm module are documented in this file.
44

5+
## [2024.10.5] - 2024-10-2x
6+
7+
- Added clangd example.
8+
59
## [2024.10.4] - 2024-10-23
610

711
- Updated to `[email protected]`, `[email protected]` and `@typefox/[email protected]`.

packages/examples/clangd.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010

1111
<body>
1212
<h2>Cpp Language Client & Clangd Language Server (Worker/Wasm)</h2>
13-
This example has been derived from: <a href="https://github.com/guyutongxue/clangd-in-browser">clangd-in-browser</a><br>
13+
<b>Heads up:</b> This is a prototype and still evolving.<br>
14+
The clangd language server worker has been derived from: <a href="https://github.com/guyutongxue/clangd-in-browser">clangd-in-browser</a><br>
1415
<button type="button" id="button-start">Start</button>
15-
<button type="button" id="button-dispose">Dispose</button>
1616
<div id="monaco-editor-root" style="width:800px;height:600px;border:1px solid grey"></div>
17+
<label for="openFiles">Select open file:</label>
18+
<select name="openFiles" id="openFiles">
19+
</select>
1720
<script type="module">
1821
import { runClangdWrapper } from "./src/clangd/client/main.ts";
1922

packages/examples/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,13 @@
8989
"vscode-languageserver": "~9.0.1",
9090
"vscode-uri": "~3.0.8",
9191
"vscode-ws-jsonrpc": "~3.3.2",
92-
"ws": "~8.18.0"
92+
"ws": "~8.18.0",
93+
"wtd-core": "~4.0.1"
9394
},
9495
"devDependencies": {
9596
"@types/express": "~5.0.0",
9697
"@types/ws": "~8.5.12",
98+
"@types/emscripten": "~1.39.13",
9799
"langium-cli": "~3.2.0",
98100
"ts-node": "~10.9.1",
99101
"vscode-languageserver-types": "~3.17.5"

0 commit comments

Comments
 (0)