1namespace ts { 2 describe("unittests:: services:: cancellableLanguageServiceOperations", () => { 3 const file = ` 4 function foo(): void; 5 function foo<T>(x: T): T; 6 function foo<T>(x?: T): T | void {} 7 foo(f); 8 `; 9 it("can cancel signature help mid-request", () => { 10 verifyOperationCancelledAfter(file, 4, service => // Two calls are top-level in services, one is the root type, and the second should be for the parameter type 11 service.getSignatureHelpItems("file.ts", file.lastIndexOf("f"), emptyOptions)!, r => assert.exists(r.items[0]) 12 ); 13 }); 14 15 it("can cancel find all references mid-request", () => { 16 verifyOperationCancelledAfter(file, 3, service => // Two calls are top-level in services, one is the root type 17 service.findReferences("file.ts", file.lastIndexOf("o"))!, r => assert.exists(r[0].definition) 18 ); 19 }); 20 21 it("can cancel quick info mid-request", () => { 22 verifyOperationCancelledAfter(file, 1, service => // The LS doesn't do any top-level checks on the token for quickinfo, so the first check is within the checker 23 service.getQuickInfoAtPosition("file.ts", file.lastIndexOf("o"))!, r => assert.exists(r.displayParts) 24 ); 25 }); 26 27 it("can cancel completion entry details mid-request", () => { 28 const options: FormatCodeSettings = { 29 indentSize: 4, 30 tabSize: 4, 31 newLineCharacter: "\n", 32 convertTabsToSpaces: true, 33 indentStyle: IndentStyle.Smart, 34 insertSpaceAfterConstructor: false, 35 insertSpaceAfterCommaDelimiter: true, 36 insertSpaceAfterSemicolonInForStatements: true, 37 insertSpaceBeforeAndAfterBinaryOperators: true, 38 insertSpaceAfterKeywordsInControlFlowStatements: true, 39 insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, 40 insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, 41 insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, 42 insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true, 43 insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, 44 insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false, 45 insertSpaceBeforeFunctionParenthesis: false, 46 placeOpenBraceOnNewLineForFunctions: false, 47 placeOpenBraceOnNewLineForControlBlocks: false, 48 }; 49 verifyOperationCancelledAfter(file, 1, service => // The LS doesn't do any top-level checks on the token for completion entry details, so the first check is within the checker 50 service.getCompletionEntryDetails("file.ts", file.lastIndexOf("f"), "foo", options, /*source*/ undefined, {}, /*data*/ undefined)!, r => assert.exists(r.displayParts) 51 ); 52 }); 53 54 it("can cancel suggestion diagnostics mid-request", () => { 55 verifyOperationCancelledAfter(file, 1, service => // The LS doesn't do any top-level checks on the token for suggestion diagnostics, so the first check is within the checker 56 service.getSuggestionDiagnostics("file.js"), r => assert.notEqual(r.length, 0), "file.js", "function foo() { let a = 10; }", { allowJs: true } 57 ); 58 }); 59 }); 60 61 function verifyOperationCancelledAfter<T>(content: string, cancelAfter: number, operation: (service: LanguageService) => T, validator: (arg: T) => void, fileName?: string, fileContent?: string, options?: CompilerOptions) { 62 let checks = 0; 63 const token: HostCancellationToken = { 64 isCancellationRequested() { 65 checks++; 66 const result = checks >= cancelAfter; 67 if (result) { 68 checks = -Infinity; // Cancel just once, then disable cancellation, effectively 69 } 70 return result; 71 } 72 }; 73 const adapter = new Harness.LanguageService.NativeLanguageServiceAdapter(token, options); 74 const host = adapter.getHost(); 75 host.addScript(fileName || "file.ts", fileContent || content, /*isRootFile*/ true); 76 const service = adapter.getLanguageService(); 77 assertCancelled(() => operation(service)); 78 validator(operation(service)); 79 } 80 81 /** 82 * We don't just use `assert.throws` because it doesn't validate instances for thrown objects which do not inherit from `Error` 83 */ 84 function assertCancelled(cb: () => void) { 85 let caught: any; 86 try { 87 cb(); 88 } 89 catch (e) { 90 caught = e; 91 } 92 assert.exists(caught, "Expected operation to be cancelled, but was not"); 93 assert.instanceOf(caught, OperationCanceledException); 94 } 95} 96