Skip to content

Commit e243329

Browse files
Refactor cancelTokenToAbortSignal to src/core/cancellation-utils.ts and add tests.
- Extracted `cancelTokenToAbortSignal` from `src/helpers.ts` to `src/core/cancellation-utils.ts` to decouple from `vscode` module runtime dependency. - Updated `src/helpers.ts` to re-export the function. - Added unit tests in `tests/unit/cancellation-utils.test.ts`. - Fixed type signature of `cancelTokenToAbortSignal` to correctly reflect return type when input is null/undefined. Co-authored-by: zknpr <96851588+zknpr@users.noreply.github.com>
1 parent e9c4c24 commit e243329

3 files changed

Lines changed: 81 additions & 20 deletions

File tree

src/core/cancellation-utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
import type { CancellationToken } from 'vscode';
3+
4+
/**
5+
* Convert VS Code CancellationToken to standard AbortSignal.
6+
*
7+
* @param token - VS Code cancellation token (or null/undefined)
8+
* @returns AbortSignal that triggers when token is cancelled
9+
*/
10+
export function cancelTokenToAbortSignal<T extends CancellationToken | null | undefined>(
11+
token: T
12+
): T extends null | undefined ? undefined : AbortSignal {
13+
if (token == null) return undefined as any;
14+
15+
const controller = new AbortController();
16+
// We access properties on 'token' assuming it adheres to the CancellationToken interface.
17+
// Since we only import it as a type, this code doesn't depend on the vscode module at runtime.
18+
if (token.isCancellationRequested) {
19+
controller.abort();
20+
} else {
21+
token.onCancellationRequested(() => controller.abort());
22+
}
23+
return controller.signal as any;
24+
}

src/helpers.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -184,25 +184,7 @@ export function getUriParts(uri: string | vsc.Uri): {
184184
// Abort Signal Utilities
185185
// ============================================================================
186186

187-
/**
188-
* Convert VS Code CancellationToken to standard AbortSignal.
189-
*
190-
* @param token - VS Code cancellation token (or null/undefined)
191-
* @returns AbortSignal that triggers when token is cancelled
192-
*/
193-
export function cancelTokenToAbortSignal<T extends vsc.CancellationToken | null | undefined>(
194-
token: T
195-
): T extends null ? undefined : AbortSignal {
196-
if (token == null) return undefined as any;
197-
198-
const controller = new AbortController();
199-
if (token.isCancellationRequested) {
200-
controller.abort();
201-
} else {
202-
token.onCancellationRequested(() => controller.abort());
203-
}
204-
return controller.signal as any;
205-
}
187+
export { cancelTokenToAbortSignal } from './core/cancellation-utils';
206188

207189
// ============================================================================
208190
// Cryptographic Utilities
@@ -337,4 +319,3 @@ export function toBoolString(value?: boolean | null): BoolString | undefined {
337319
if (value === false) return 'false';
338320
return undefined;
339321
}
340-
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
import { describe, it } from 'node:test';
3+
import assert from 'node:assert';
4+
import { cancelTokenToAbortSignal } from '../../src/core/cancellation-utils';
5+
6+
describe('Cancellation Utils', () => {
7+
describe('cancelTokenToAbortSignal', () => {
8+
it('should return undefined if token is null', () => {
9+
assert.strictEqual(cancelTokenToAbortSignal(null), undefined);
10+
});
11+
12+
it('should return undefined if token is undefined', () => {
13+
assert.strictEqual(cancelTokenToAbortSignal(undefined), undefined);
14+
});
15+
16+
it('should return AbortSignal if token is provided', () => {
17+
const token: any = {
18+
isCancellationRequested: false,
19+
onCancellationRequested: () => {}
20+
};
21+
const signal = cancelTokenToAbortSignal(token);
22+
assert.ok(signal instanceof AbortSignal);
23+
assert.strictEqual(signal.aborted, false);
24+
});
25+
26+
it('should abort if token is already cancelled', () => {
27+
const token: any = {
28+
isCancellationRequested: true,
29+
onCancellationRequested: () => {}
30+
};
31+
const signal = cancelTokenToAbortSignal(token);
32+
assert.ok(signal instanceof AbortSignal);
33+
assert.strictEqual(signal.aborted, true);
34+
});
35+
36+
it('should abort when token is cancelled later', () => {
37+
let callback: (() => void) | undefined;
38+
const token: any = {
39+
isCancellationRequested: false,
40+
onCancellationRequested: (cb: () => void) => {
41+
callback = cb;
42+
return { dispose: () => {} };
43+
}
44+
};
45+
46+
const signal = cancelTokenToAbortSignal(token);
47+
assert.strictEqual(signal!.aborted, false);
48+
49+
// Simulate cancellation
50+
assert.ok(callback);
51+
callback();
52+
53+
assert.strictEqual(signal!.aborted, true);
54+
});
55+
});
56+
});

0 commit comments

Comments
 (0)