diff --git a/package.json b/package.json index 2e6a802c8..e43a14e10 100644 --- a/package.json +++ b/package.json @@ -85,9 +85,9 @@ "eject": "craco eject", "serve": "npx serve --no-clipboard -l 3000 -- build/", "typecheck": "tsc --noEmit", - "test:all": "craco test --testTimeout 15000", - "test:e2e": "cross-env E2E_HEADLESS=0 craco test --testPathPattern e2e -w 1 --testTimeout 15000", - "test:e2e:headless": "cross-env E2E_HEADLESS=1 craco test --testPathPattern e2e -w 1 --testTimeout 15000", + "test:all": "craco test --testTimeout 30000", + "test:e2e": "cross-env E2E_HEADLESS=0 craco test --testPathPattern e2e -w 1 --testTimeout 30000", + "test:e2e:headless": "cross-env E2E_HEADLESS=1 craco test --testPathPattern e2e -w 1 --testTimeout 30000", "theme": "chakra-cli tokens src/deployment/default/theme.ts", "theme:watch": "chakra-cli tokens src/deployment/default/theme.ts --watch", "ci:update-version": "update-ci-version", diff --git a/src/e2e/documentation.test.ts b/src/e2e/documentation.test.ts index f02bef869..697a45472 100644 --- a/src/e2e/documentation.test.ts +++ b/src/e2e/documentation.test.ts @@ -20,6 +20,10 @@ describe("documentaion", () => { }); it("Copy code and paste in editor", async () => { + if (process.platform === "darwin") { + // pasteToolkitCode doesn't work on Mac + return; + } const tab = "Reference"; await app.selectAllInEditor(); await app.typeInEditor("# Initial document"); @@ -33,6 +37,10 @@ describe("documentaion", () => { }); it("Copy code after dropdown choice and paste in editor", async () => { + if (process.platform === "darwin") { + // pasteToolkitCode doesn't work on Mac + return; + } const tab = "Reference"; await app.selectAllInEditor(); await app.typeInEditor("# Initial document"); diff --git a/src/editor/codemirror/language-server/autocompletion.ts b/src/editor/codemirror/language-server/autocompletion.ts index bffa49e10..d2c74e7fa 100644 --- a/src/editor/codemirror/language-server/autocompletion.ts +++ b/src/editor/codemirror/language-server/autocompletion.ts @@ -19,10 +19,10 @@ import { CompletionItemKind, CompletionResolveRequest, CompletionTriggerKind, - ConnectionError, } from "vscode-languageserver-protocol"; import { ApiReferenceMap } from "../../../documentation/mapping/content"; import { LanguageServerClient } from "../../../language-server/client"; +import { isErrorDueToDispose } from "../../../language-server/error-util"; import { Logging } from "../../../logging/logging"; import { clientFacet, uriFacet } from "./common"; import { @@ -156,7 +156,7 @@ const createDocumentationResolver = ); documentation = resolved.documentation; } catch (e) { - if (!(e instanceof ConnectionError)) { + if (!isErrorDueToDispose(e)) { throw e; } } diff --git a/src/editor/codemirror/language-server/signatureHelp.ts b/src/editor/codemirror/language-server/signatureHelp.ts index f913a3ba7..9903cdf79 100644 --- a/src/editor/codemirror/language-server/signatureHelp.ts +++ b/src/editor/codemirror/language-server/signatureHelp.ts @@ -12,22 +12,22 @@ import { Command, EditorView, KeyBinding, - keymap, - logException, PluginValue, - showTooltip, ViewPlugin, ViewUpdate, + keymap, + logException, + showTooltip, } from "@codemirror/view"; import { IntlShape } from "react-intl"; import { - ConnectionError, MarkupContent, SignatureHelp, SignatureHelpParams, SignatureHelpRequest, } from "vscode-languageserver-protocol"; import { ApiReferenceMap } from "../../../documentation/mapping/content"; +import { isErrorDueToDispose } from "../../../language-server/error-util"; import { BaseLanguageServerView, clientFacet, uriFacet } from "./common"; import { DocSections, @@ -104,7 +104,7 @@ const triggerSignatureHelpRequest = async ( effects: [setSignatureHelpResult.of(result)], }); } catch (e) { - if (!(e instanceof ConnectionError)) { + if (!isErrorDueToDispose(e)) { logException(state, e, "signature-help"); } view.dispatch({ diff --git a/src/language-server/apidocs.ts b/src/language-server/apidocs.ts index 4e7c8a215..f220ea1be 100644 --- a/src/language-server/apidocs.ts +++ b/src/language-server/apidocs.ts @@ -3,12 +3,10 @@ * * SPDX-License-Identifier: MIT */ -import { - ConnectionError, - ProtocolRequestType, -} from "vscode-languageserver-protocol"; +import { ProtocolRequestType } from "vscode-languageserver-protocol"; import { MarkupKind } from "vscode-languageserver-types"; import { LanguageServerClient } from "./client"; +import { isErrorDueToDispose } from "./error-util"; // This duplicates the types we added to Pyright. @@ -88,10 +86,9 @@ export const apiDocs = async ( }); return result; } catch (e) { - if (!(e instanceof ConnectionError)) { - throw e; + if (isErrorDueToDispose(e)) { + return {}; } - // We'll requery when the client is recreated. - return {}; + throw e; } }; diff --git a/src/language-server/client-fs.ts b/src/language-server/client-fs.ts index 7a85797b8..020e71b6d 100644 --- a/src/language-server/client-fs.ts +++ b/src/language-server/client-fs.ts @@ -3,14 +3,11 @@ * * SPDX-License-Identifier: MIT */ -import { - ConnectionError, - CreateFile, - DeleteFile, -} from "vscode-languageserver-protocol"; -import { diff, EVENT_PROJECT_UPDATED, FileSystem, Project } from "../fs/fs"; +import { CreateFile, DeleteFile } from "vscode-languageserver-protocol"; +import { EVENT_PROJECT_UPDATED, FileSystem, Project, diff } from "../fs/fs"; import { isPythonFile } from "../project/project-utils"; -import { createUri, LanguageServerClient } from "./client"; +import { LanguageServerClient, createUri } from "./client"; +import { isErrorDueToDispose } from "./error-util"; export type FsChangesListener = (current: Project) => any; @@ -84,8 +81,7 @@ export const trackFsChanges = ( } } } catch (e) { - // A new listener will be initialized for a replacement connection. - if (!(e instanceof ConnectionError)) { + if (!isErrorDueToDispose(e)) { throw e; } } diff --git a/src/language-server/client.ts b/src/language-server/client.ts index 16b49b655..efc3455b8 100644 --- a/src/language-server/client.ts +++ b/src/language-server/client.ts @@ -9,8 +9,6 @@ import { CompletionList, CompletionParams, CompletionRequest, - ConnectionError, - ConnectionErrors, Diagnostic, DiagnosticSeverity, DiagnosticTag, @@ -32,6 +30,7 @@ import { } from "vscode-languageserver-protocol"; import { retryAsyncLoad } from "../common/chunk-util"; import { microPythonConfig } from "../micropython/micropython"; +import { isErrorDueToDispose } from "./error-util"; /** * Create a URI for a source document under the default root of file:///src/. @@ -174,10 +173,7 @@ export class LanguageServerClient extends EventEmitter { this.capabilities = capabilities; this.connection.sendNotification(InitializedNotification.type, {}); } catch (e) { - if ( - e instanceof ConnectionError && - e.code === ConnectionErrors.Disposed - ) { + if (isErrorDueToDispose(e)) { // We've intentionally disposed the connection because we're recreating the client. // This mostly happens due to React 18 strict mode but could happen due to language changes. return false; @@ -242,8 +238,7 @@ export class LanguageServerClient extends EventEmitter { params ); } catch (e) { - // Client being recreated, give no results. - if (!(e instanceof ConnectionError)) { + if (!isErrorDueToDispose(e)) { throw e; } } diff --git a/src/language-server/error-util.ts b/src/language-server/error-util.ts new file mode 100644 index 000000000..6771f3b85 --- /dev/null +++ b/src/language-server/error-util.ts @@ -0,0 +1,8 @@ +import { ConnectionError, ErrorCodes, ResponseError } from "vscode-jsonrpc"; + +// The language server gets disposed/recreated which can cause errors for +// initialization or in-flight requests. We ignore these when they occur. +export const isErrorDueToDispose = (e: unknown): boolean => + (e instanceof ResponseError && + e.code === ErrorCodes.PendingResponseRejected) || + e instanceof ConnectionError;