Skip to content
Merged
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
24 changes: 15 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Compatibility and upgrade notes

- Raised the minimum supported VS Code version to **1.101.0** (Node.js 22). (#1850)

## 3.2.1 - 2026-10-24

### Improvements and bug fixes
Expand Down Expand Up @@ -257,38 +263,38 @@ same user/workspace scope).

### Features

* feat: change default of r.lsp.multiServer to false
- feat: change default of r.lsp.multiServer to false

**Full Changelog**: <https://github.com/REditorSupport/vscode-R/compare/v2.8.7...v2.8.8>

## 2.8.7 - 2026-03-15

### Bug Fixes

* fix: correct r.term and r.path setting names in error message
- fix: correct r.term and r.path setting names in error message

### Features

* feat: support multi-root workspaces in single-server mode
- feat: support multi-root workspaces in single-server mode

### Other

* Allow bracketedPaste on win32 platform ([#1631](https://github.com/REditorSupport/vscode-R/issues/1631))
* feat: default to single language server for multi-root workspaces ([#1682](https://github.com/REditorSupport/vscode-R/issues/1682))
- Allow bracketedPaste on win32 platform ([#1631](https://github.com/REditorSupport/vscode-R/issues/1631))
- feat: default to single language server for multi-root workspaces ([#1682](https://github.com/REditorSupport/vscode-R/issues/1682))

**Full Changelog**: <https://github.com/REditorSupport/vscode-R/compare/v2.8.6...v2.8.7>

## 2.8.6 - 2025-05-31

### Other

* Syntax update and bump to 2.8.6 ([#1605](https://github.com/REditorSupport/vscode-R/issues/1605))
* Show sidebar icon only when extension is active ([#1579](https://github.com/REditorSupport/vscode-R/issues/1579))
* Move R and R markdown syntaxes to vscode-R-syntax ([#1606](https://github.com/REditorSupport/vscode-R/issues/1606))
- Syntax update and bump to 2.8.6 ([#1605](https://github.com/REditorSupport/vscode-R/issues/1605))
- Show sidebar icon only when extension is active ([#1579](https://github.com/REditorSupport/vscode-R/issues/1579))
- Move R and R markdown syntaxes to vscode-R-syntax ([#1606](https://github.com/REditorSupport/vscode-R/issues/1606))

### Refactor

* refactor: restructure files ([#1613](https://github.com/REditorSupport/vscode-R/issues/1613))
- refactor: restructure files ([#1613](https://github.com/REditorSupport/vscode-R/issues/1613))

**Full Changelog**: <https://github.com/REditorSupport/vscode-R/compare/v2.8.5...v2.8.6>

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ for other changes and upgrade notes.
install.packages("languageserver")
```

3. Install the stable release of this extension from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=reditorsupport.r) or the [Open VSX Registry](https://open-vsx.org/extension/reditorsupport/r). This release requires VS Code 1.110 or later.
3. Install the stable release of this extension from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=reditorsupport.r) or the [Open VSX Registry](https://open-vsx.org/extension/reditorsupport/r). This release requires VS Code 1.101.0 or later.

```sh
code --install-extension REditorSupport.r
Expand Down
1 change: 1 addition & 0 deletions esbuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ async function main() {
sourcemap: !production,
sourcesContent: false,
platform: 'node',
target: 'node22',
outfile: 'dist/extension.js',
external: ['vscode', 'utf-8-validate', 'bufferutil'],
logLevel: 'info',
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"R Markdown"
],
"engines": {
"vscode": "^1.98.2"
"vscode": "^1.101.0"
},
"activationEvents": [
"workspaceContains:**/*.{rproj,Rproj,r,R,rd,Rd,rmd,Rmd}",
Expand Down Expand Up @@ -2619,7 +2619,7 @@
"@types/mocha": "^8.2.3",
"@types/node": "^22.20.5",
"@types/sinon": "^10.0.20",
"@types/vscode": "~1.94.0",
"@types/vscode": "~1.101.0",
"@types/winreg": "^1.2.36",
"@vscode/test-cli": "^0.0.15",
"@vscode/test-electron": "^3.1.0",
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions src/plotViewer/httpgdClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,7 @@ export class HttpgdClient {
}

private openWebSocket(): void {
// TODO: Remove this missing-WebSocket compatibility path when the minimum
// extension host provides Node 22. Keep polling for connection failures.
if (typeof globalThis.WebSocket !== 'function' || !this.controller || this.socket) {
if (!this.controller || this.socket) {
return;
}
this.retryAt = Date.now() + this.retryInterval;
Expand Down
30 changes: 5 additions & 25 deletions src/test/node/httpgdClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ suite('Internal httpgd client', () => {
let remote: HttpgdState;
let ids: string[];
let requests: { path: string; token?: string }[];
let websocketDescriptor: PropertyDescriptor | undefined;
let held: ServerResponse[];
let holdPlots: boolean;
let invalidPlots: boolean;
Expand All @@ -62,12 +61,7 @@ suite('Internal httpgd client', () => {
holdPlots = invalidPlots = false;
notifications = [];
TestWebSocket.instances = [];
websocketDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'WebSocket');
Object.defineProperty(globalThis, 'WebSocket', {
value: undefined,
configurable: true,
writable: true,
});
globalThis.WebSocket = TestWebSocket as unknown as typeof WebSocket;
server = createServer((request, response) => {
const url = new URL(request.url ?? '/', origin);
requests.push({
Expand Down Expand Up @@ -122,11 +116,7 @@ suite('Internal httpgd client', () => {
});
teardown(async () => {
client.disconnect();
if (websocketDescriptor) {
Object.defineProperty(globalThis, 'WebSocket', websocketDescriptor);
} else {
Reflect.deleteProperty(globalThis, 'WebSocket');
}
globalThis.WebSocket = nativeWebSocket;
for (const response of held) {
response.destroy();
}
Expand Down Expand Up @@ -161,9 +151,7 @@ suite('Internal httpgd client', () => {
assert.deepStrictEqual(notifications, [['1'], []]);
});
test('polls a server on port 10080, including when native WebSocket rejects it', async () => {
if (typeof nativeWebSocket === 'function') {
globalThis.WebSocket = nativeWebSocket;
}
globalThis.WebSocket = nativeWebSocket;
await new Promise<void>((resolve, reject) =>
server.close((error) => (error ? reject(error) : resolve())),
);
Expand All @@ -186,7 +174,7 @@ suite('Internal httpgd client', () => {
await until(() => client.getPlots()[0]?.id === '2');
assert.deepStrictEqual(await client.getPlotBytes({ id: '2', renderer: 'png' }), binary);
});
test('polls without WebSocket and only fetches plots after a state change', async () => {
test('polls when WebSocket cannot connect and only fetches changed plots', async () => {
await client.connect();
await until(() => requests.filter((request) => request.path === '/state').length >= 2);
assert.strictEqual(requests.filter((request) => request.path === '/plots').length, 1);
Expand All @@ -196,7 +184,6 @@ suite('Internal httpgd client', () => {
assert.deepStrictEqual(notifications, [['1'], ['1', '2']]);
});
test('uses native WebSocket notifications and falls back after a disconnect', async () => {
globalThis.WebSocket = TestWebSocket as unknown as typeof WebSocket;
await client.connect();
const socket = TestWebSocket.instances[0];
assert.strictEqual(socket.url.protocol, 'ws:');
Expand All @@ -215,12 +202,7 @@ suite('Internal httpgd client', () => {
TestWebSocket.instances[1].open();
await until(() => requests.filter((request) => request.path === '/plots').length >= 5);
});
test('receives notifications through an actual native WebSocket connection', async function () {
if (typeof nativeWebSocket !== 'function') {
this.skip();
return;
}

test('receives notifications through an actual native WebSocket connection', async () => {
const handshakeTimeoutMs = 5000;
const testOrigin = origin;
const actualServer = server;
Expand Down Expand Up @@ -401,15 +383,13 @@ suite('Internal httpgd client', () => {
}
});
test('falls back when WebSocket cannot open', async () => {
globalThis.WebSocket = TestWebSocket as unknown as typeof WebSocket;
await client.connect();
await until(() => TestWebSocket.instances[0].readyState === 3);
ids = ['2'];
remote = { ...remote, upid: 2 };
await until(() => client.getPlots()[0]?.id === '2');
});
test('serializes plot refreshes and retains updates received during a request', async () => {
globalThis.WebSocket = TestWebSocket as unknown as typeof WebSocket;
await client.connect();
const socket = TestWebSocket.instances[0];
socket.open();
Expand Down
Loading