@tui-sandbox/library 11.11.1 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/browser/assets/index-CYUPHpRk.css +1 -0
  2. package/dist/browser/assets/index-DPQpUaDL.js +9 -0
  3. package/dist/browser/index.html +2 -2
  4. package/dist/src/browser/neovim-client.d.ts +2 -0
  5. package/dist/src/browser/neovim-client.js +2 -0
  6. package/dist/src/browser/neovim-client.js.map +1 -1
  7. package/dist/src/client/index.d.ts +1 -0
  8. package/dist/src/client/neovim-terminal-client.d.ts +0 -2
  9. package/dist/src/client/neovim-terminal-client.js +0 -2
  10. package/dist/src/client/neovim-terminal-client.js.map +1 -1
  11. package/dist/src/client/startTerminal.d.ts +0 -2
  12. package/dist/src/client/startTerminal.js +0 -2
  13. package/dist/src/client/startTerminal.js.map +1 -1
  14. package/dist/src/client/terminal-terminal-client.d.ts +0 -1
  15. package/dist/src/client/terminal-terminal-client.js +0 -1
  16. package/dist/src/client/terminal-terminal-client.js.map +1 -1
  17. package/dist/src/server/cypress-support/contents.js +9 -11
  18. package/dist/src/server/cypress-support/contents.js.map +1 -1
  19. package/dist/src/server/index.d.ts +4 -0
  20. package/dist/src/server/index.js.map +1 -1
  21. package/dist/tsconfig.tsbuildinfo +1 -1
  22. package/package.json +35 -9
  23. package/CHANGELOG.md +0 -933
  24. package/dist/browser/assets/index-BQzArJW3.js +0 -9
  25. package/dist/browser/assets/index-hkmOP7rU.css +0 -1
  26. package/index.html +0 -11
  27. package/src/browser/neovim-client.ts +0 -126
  28. package/src/client/MyNeovimConfigModification.test.ts +0 -25
  29. package/src/client/MyNeovimConfigModification.ts +0 -8
  30. package/src/client/color-utilities.test.ts +0 -10
  31. package/src/client/color-utilities.ts +0 -9
  32. package/src/client/cypress-assertions.ts +0 -35
  33. package/src/client/index.ts +0 -4
  34. package/src/client/neovim-terminal-client.ts +0 -130
  35. package/src/client/public/DejaVuSansMNerdFontMono-Regular.ttf +0 -0
  36. package/src/client/startTerminal.ts +0 -97
  37. package/src/client/style.css +0 -32
  38. package/src/client/terminal-config.ts +0 -25
  39. package/src/client/terminal-terminal-client.ts +0 -106
  40. package/src/client/validateMouseEvent.ts +0 -22
  41. package/src/scripts/commands/commandRun.ts +0 -68
  42. package/src/scripts/commands/commandTuiNeovimExec.ts +0 -36
  43. package/src/scripts/commands/commandTuiNeovimPrepare.ts +0 -12
  44. package/src/scripts/commands/commandTuiStart.ts +0 -36
  45. package/src/scripts/parseArguments.test.ts +0 -28
  46. package/src/scripts/parseArguments.ts +0 -66
  47. package/src/scripts/resolveConfig.test.ts +0 -78
  48. package/src/scripts/resolveTuiConfig.ts +0 -65
  49. package/src/scripts/tui.ts +0 -77
  50. package/src/server/TestServer.ts +0 -73
  51. package/src/server/applications/neovim/NeovimApplication.ts +0 -236
  52. package/src/server/applications/neovim/NeovimJavascriptApiClient.test.ts +0 -48
  53. package/src/server/applications/neovim/NeovimJavascriptApiClient.ts +0 -68
  54. package/src/server/applications/neovim/api.ts +0 -257
  55. package/src/server/applications/neovim/environment/TempDirectory.ts +0 -18
  56. package/src/server/applications/neovim/environment/createTempDir.test.ts +0 -29
  57. package/src/server/applications/neovim/environment/createTempDir.ts +0 -91
  58. package/src/server/applications/neovim/neovimRouter.ts +0 -100
  59. package/src/server/applications/neovim/prepareNewTestDirectory.test.ts +0 -32
  60. package/src/server/applications/neovim/prepareNewTestDirectory.ts +0 -21
  61. package/src/server/applications/terminal/TerminalTestApplication.ts +0 -101
  62. package/src/server/applications/terminal/api.ts +0 -89
  63. package/src/server/applications/terminal/runBlockingShellCommand.test.ts +0 -41
  64. package/src/server/applications/terminal/runBlockingShellCommand.ts +0 -64
  65. package/src/server/applications/terminal/terminalRouter.ts +0 -47
  66. package/src/server/blockingCommandInputSchema.test.ts +0 -24
  67. package/src/server/blockingCommandInputSchema.ts +0 -27
  68. package/src/server/config.test.ts +0 -7
  69. package/src/server/config.ts +0 -18
  70. package/src/server/connection/trpc.ts +0 -3
  71. package/src/server/cypress-support/contents.ts +0 -245
  72. package/src/server/cypress-support/createCypressSupportFile.test.ts +0 -69
  73. package/src/server/cypress-support/createCypressSupportFile.ts +0 -56
  74. package/src/server/dirtree/index.test.ts +0 -352
  75. package/src/server/dirtree/index.ts +0 -144
  76. package/src/server/dirtree/json-to-zod.ts +0 -60
  77. package/src/server/index.ts +0 -6
  78. package/src/server/server.ts +0 -34
  79. package/src/server/types.ts +0 -98
  80. package/src/server/updateTestdirectorySchemaFile.test.ts +0 -49
  81. package/src/server/updateTestdirectorySchemaFile.ts +0 -71
  82. package/src/server/utilities/DisposableSingleApplication.test.ts +0 -92
  83. package/src/server/utilities/DisposableSingleApplication.ts +0 -49
  84. package/src/server/utilities/Lazy.ts +0 -16
  85. package/src/server/utilities/TerminalApplication.ts +0 -100
  86. package/src/server/utilities/generator.test.ts +0 -50
  87. package/src/server/utilities/generator.ts +0 -12
  88. package/src/server/utilities/tabId.ts +0 -4
  89. package/src/server/utilities/timeout.ts +0 -3
  90. package/src/server/utilities/timeoutable.ts +0 -19
  91. package/tsconfig.json +0 -31
  92. package/vite.config.js +0 -27
@@ -1,236 +0,0 @@
1
- import assert from "assert"
2
- import { exec } from "child_process"
3
- import EventEmitter from "events"
4
- import { access } from "fs/promises"
5
- import type { NeovimClient as NeovimApiClient } from "neovim"
6
- import { tmpdir } from "os"
7
- import path, { join } from "path"
8
- import { debuglog } from "util"
9
- import type { TestDirectory, TestEnvironmentCommonEnvironmentVariables } from "../../types.js"
10
- import { DisposableSingleApplication } from "../../utilities/DisposableSingleApplication.js"
11
- import type { Lazy } from "../../utilities/Lazy.js"
12
- import { TerminalApplication } from "../../utilities/TerminalApplication.js"
13
- import { connectNeovimApi } from "./NeovimJavascriptApiClient.js"
14
-
15
- const log = debuglog("tui-sandbox-neovim-application")
16
-
17
- /*
18
-
19
- Usage:
20
- nvim [options] [file ...]
21
-
22
- Options:
23
- --cmd <cmd> Execute <cmd> before any config
24
- +<cmd>, -c <cmd> Execute <cmd> after config and first file
25
- -l <script> [args...] Execute Lua <script> (with optional args)
26
- -S <session> Source <session> after loading the first file
27
- -s <scriptin> Read Normal mode commands from <scriptin>
28
- -u <config> Use this config file
29
-
30
- -d Diff mode
31
- -es, -Es Silent (batch) mode
32
- -h, --help Print this help message
33
- -i <shada> Use this shada file
34
- -n No swap file, use memory only
35
- -o[N] Open N windows (default: one per file)
36
- -O[N] Open N vertical windows (default: one per file)
37
- -p[N] Open N tab pages (default: one per file)
38
- -R Read-only (view) mode
39
- -v, --version Print version information
40
- -V[N][file] Verbose [level][file]
41
-
42
- -- Only file names after this
43
- --api-info Write msgpack-encoded API metadata to stdout
44
- --clean "Factory defaults" (skip user config and plugins, shada)
45
- --embed Use stdin/stdout as a msgpack-rpc channel
46
- --headless Don't start a user interface
47
- --listen <address> Serve RPC API from this address
48
- --remote[-subcommand] Execute commands remotely on a server
49
- --server <address> Connect to this Nvim server
50
- --startuptime <file> Write startup timing messages to <file>
51
-
52
- See ":help startup-options" for all options.
53
-
54
- NVIM v0.11.2
55
- Build type: Release
56
- LuaJIT 2.1.1741730670
57
- Run "nvim -V1 -v" for more info
58
-
59
- */
60
-
61
- export type StdoutOrStderrMessage = "stdout"
62
-
63
- export type StartNeovimGenericArguments = {
64
- filename: string | { openInVerticalSplits: string[] }
65
- startupScriptModifications?: string[]
66
-
67
- /** Executes the given command with --headless -c <command> -c qa */
68
- headlessCmd?: string
69
-
70
- /**
71
- * This variable controls the sub-directory that Nvim will read from (and
72
- * auto-create) in each of the base directories.
73
- *
74
- * For example, setting $NVIM_APPNAME to "foo" before starting will cause
75
- * Nvim to look for configuration files in $XDG_CONFIG_HOME/foo instead of
76
- * $XDG_CONFIG_HOME/nvim. $NVIM_APPNAME must be a name, such as "foo", or a
77
- * relative path, such as "foo/bar".
78
- *
79
- * https://neovim.io/doc/user/starting.html#_nvim_appname
80
- */
81
- NVIM_APPNAME?: string
82
-
83
- /** Additions to the environment variables for the Neovim process. These
84
- * override any already existing environment variables. */
85
- additionalEnvironmentVariables?: Record<string, string> | undefined
86
- }
87
-
88
- export type TerminalDimensions = { cols: number; rows: number }
89
-
90
- type ResettableState = {
91
- testDirectory: TestDirectory
92
- NVIM_APPNAME: string | undefined
93
- socketPath: string
94
- client: Lazy<Promise<NeovimApiClient>>
95
- }
96
-
97
- export class NeovimApplication implements AsyncDisposable {
98
- public state: ResettableState | undefined
99
- public readonly events: EventEmitter
100
-
101
- public constructor(
102
- private readonly testEnvironmentPath: string,
103
- public readonly application: DisposableSingleApplication = new DisposableSingleApplication()
104
- ) {
105
- this.events = new EventEmitter()
106
- }
107
-
108
- /**
109
- * Kill the current application and start a new one with the given arguments.
110
- */
111
- public async startNextAndKillCurrent(
112
- testDirectory: TestDirectory,
113
- startArgs: StartNeovimGenericArguments,
114
- terminalDimensions: TerminalDimensions
115
- ): Promise<void> {
116
- await this[Symbol.asyncDispose]()
117
- assert(
118
- this.state === undefined,
119
- "NeovimApplication state should be undefined after disposing so that no previous state is reused."
120
- )
121
-
122
- const neovimArguments: string[] = []
123
-
124
- if (startArgs.startupScriptModifications) {
125
- for (const modification of startArgs.startupScriptModifications) {
126
- let file = path.join(testDirectory.rootPathAbsolute, "config-modifications", modification)
127
- try {
128
- await access(file)
129
- } catch (e) {
130
- throw new Error(`startupScriptModifications file does not exist: ${file}. Error: ${String(e)}`)
131
- }
132
-
133
- file = file.replaceAll("'", "\\'")
134
- neovimArguments.push("-c", `lua dofile('${file}')`)
135
- }
136
- }
137
-
138
- if (typeof startArgs.filename === "string") {
139
- const file = path.join(testDirectory.rootPathAbsolute, startArgs.filename)
140
- neovimArguments.push(file)
141
- } else if (startArgs.filename.openInVerticalSplits.length > 0) {
142
- // `-O[N]` Open N vertical windows (default: one per file)
143
- neovimArguments.push("-O")
144
-
145
- for (const file of startArgs.filename.openInVerticalSplits) {
146
- const filePath = path.join(testDirectory.rootPathAbsolute, file)
147
- neovimArguments.push(filePath)
148
- }
149
- }
150
-
151
- if (startArgs.headlessCmd) {
152
- // NOTE: update the doc comment above if this changes
153
- neovimArguments.push("--headless")
154
- neovimArguments.push("-c", startArgs.headlessCmd)
155
- neovimArguments.push("-c", "qa")
156
- }
157
-
158
- const id = Math.random().toString().slice(2, 8)
159
- const socketPath = `${tmpdir()}/tui-sandbox-nvim-socket-${id}`
160
- neovimArguments.push("--listen", socketPath)
161
-
162
- const stdout = this.events
163
-
164
- await this.application.startNextAndKillCurrent(async () => {
165
- const env = NeovimApplication.getEnvironmentVariables(
166
- testDirectory,
167
- startArgs.NVIM_APPNAME,
168
- startArgs.additionalEnvironmentVariables
169
- )
170
- return TerminalApplication.start({
171
- command: "nvim",
172
- args: neovimArguments,
173
-
174
- cwd: this.testEnvironmentPath,
175
- env,
176
- dimensions: terminalDimensions,
177
-
178
- onStdoutOrStderr(data) {
179
- data satisfies string
180
- stdout.emit("stdout" satisfies StdoutOrStderrMessage, data)
181
- },
182
- })
183
- })
184
-
185
- const processId = this.application.processId()
186
- assert(processId !== undefined, "Neovim was started without a process ID. This is a bug - please open an issue.")
187
-
188
- this.state = {
189
- testDirectory,
190
- socketPath,
191
- NVIM_APPNAME: startArgs.NVIM_APPNAME,
192
- client: connectNeovimApi(socketPath),
193
- }
194
-
195
- log(`🚀 Started Neovim instance ${processId} (NVIM_APPNAME: ${startArgs.NVIM_APPNAME})`)
196
- }
197
-
198
- public static getEnvironmentVariables(
199
- testDirectory: TestDirectory,
200
- NVIM_APPNAME: string | undefined,
201
- additionalEnvironmentVariables?: Record<string, string>
202
- ): NodeJS.ProcessEnv {
203
- return {
204
- ...process.env,
205
- ...({
206
- HOME: testDirectory.rootPathAbsolute,
207
- XDG_CONFIG_HOME: join(testDirectory.rootPathAbsolute, ".config"),
208
- XDG_DATA_HOME: join(testDirectory.testEnvironmentPath, ".repro", "data"),
209
- TUI_SANDBOX_TEST_ENVIRONMENT_PATH: testDirectory.testEnvironmentPath,
210
- } satisfies TestEnvironmentCommonEnvironmentVariables),
211
- NVIM_APPNAME: NVIM_APPNAME ?? "nvim",
212
-
213
- ...additionalEnvironmentVariables,
214
- }
215
- }
216
-
217
- async [Symbol.asyncDispose](): Promise<void> {
218
- await this.application[Symbol.asyncDispose]()
219
-
220
- if (!this.state) return
221
-
222
- exec(`rm -rf ${this.state.testDirectory.rootPathAbsolute}`)
223
-
224
- try {
225
- await access(this.state.socketPath)
226
- // this is probably not dangerous, but I'm not sure why it sometimes
227
- // happens. It's better to report it than to hide it.
228
- log(`Socket file ${this.state.socketPath} should have been removed by neovim when it exited.`)
229
- return
230
- } catch {
231
- // all good
232
- } finally {
233
- this.state = undefined
234
- }
235
- }
236
- }
@@ -1,48 +0,0 @@
1
- import { access } from "fs/promises"
2
- import { attach } from "neovim"
3
- import { afterEach, beforeEach, expect, it, vi } from "vitest"
4
- import type { PollingInterval } from "./NeovimJavascriptApiClient.js"
5
- import { connectNeovimApi } from "./NeovimJavascriptApiClient.js"
6
-
7
- vi.mock("neovim")
8
- vi.mock("fs/promises")
9
-
10
- const mocked = {
11
- attach: vi.mocked(attach),
12
- access: vi.mocked(access),
13
- log: vi.spyOn(console, "log").mockImplementation(() => {
14
- //
15
- }),
16
- }
17
- const pollingInterval = 100 satisfies PollingInterval
18
-
19
- beforeEach(() => {
20
- vi.useFakeTimers()
21
- })
22
-
23
- afterEach(() => {
24
- vi.useRealTimers()
25
- })
26
-
27
- it("is lazy - does not connect right away", async () => {
28
- mocked.access.mockRejectedValue(new Error("no such file or directory"))
29
- connectNeovimApi("foosocket")
30
-
31
- vi.advanceTimersByTime(pollingInterval)
32
- expect(mocked.attach).not.toHaveBeenCalled()
33
- })
34
-
35
- it("connects right away if the socket file is already there", async () => {
36
- mocked.access.mockResolvedValue(undefined)
37
- const lazyClient = connectNeovimApi("foosocket")
38
- await lazyClient.get()
39
-
40
- vi.advanceTimersByTime(pollingInterval)
41
- expect(mocked.attach).toHaveBeenCalledWith({
42
- socket: "foosocket",
43
- options: {
44
- logger: expect.any(Object) as never,
45
- },
46
- } satisfies Partial<Parameters<typeof attach>[0]>)
47
- expect(mocked.attach).toHaveBeenCalledTimes(1)
48
- })
@@ -1,68 +0,0 @@
1
- import { access } from "fs/promises"
2
- import type { NeovimClient as NeovimApiClient } from "neovim"
3
- import { attach } from "neovim"
4
- import { createLogger, format, transports } from "winston"
5
- import { Lazy } from "../../utilities/Lazy.js"
6
-
7
- // const log = debuglog("tui-sandbox.neovim.NeovimJavascriptApiClient")
8
-
9
- export type NeovimJavascriptApiClient = NeovimApiClient
10
-
11
- export type PollingInterval = 100
12
-
13
- export function connectNeovimApi(socketPath: string): Lazy<Promise<NeovimJavascriptApiClient>> {
14
- // When a custom logger is provided to attach(), we can hide debug level log
15
- // messages
16
- //
17
- // https://github.com/neovim/node-client/blob/e0568e32e0fc8837ad900146bfd5ca27b9416235/README.md#logging
18
- const logger = createLogger({
19
- level: "warn",
20
-
21
- transports: [
22
- new transports.Console({
23
- format: format.combine(
24
- format(info => {
25
- if ((info.message as string).startsWith("failed request to")) {
26
- // This is logged when neovim is started with --embed, which we don't use.
27
- // It's not a problem, so hide it.
28
- //
29
- // Returning false removes this log entry
30
- return false
31
- }
32
-
33
- return info
34
- })(),
35
- format.colorize(),
36
- format.simple()
37
- ),
38
- }),
39
- ],
40
- })
41
-
42
- // it takes about 100ms for the socket file to be created - best make this
43
- // Lazy so that we don't wait for it unnecessarily.
44
- return new Lazy(async () => {
45
- for (let i = 0; i < 100; i++) {
46
- try {
47
- await access(socketPath)
48
- // log(`socket file ${socketPath} created after at attempt ${i + 1}`)
49
- break
50
- } catch {
51
- // log(`polling for socket file ${socketPath} to be created (attempt ${i + 1})`)
52
- await new Promise(resolve => setTimeout(resolve, 100 satisfies PollingInterval))
53
- }
54
- }
55
-
56
- // Prevent neovim node client from monkey patching console.log. It runs in
57
- // a separate process entirely, so there doesn't seem to be any benefit to
58
- // this.
59
- process.env["ALLOW_CONSOLE"] = "1"
60
-
61
- return attach({
62
- socket: socketPath,
63
- options: {
64
- logger,
65
- },
66
- })
67
- })
68
- }
@@ -1,257 +0,0 @@
1
- import assert from "assert"
2
- import { access } from "fs/promises"
3
- import path from "path"
4
- import { debuglog } from "util"
5
- import type { BlockingCommandInput } from "../../blockingCommandInputSchema.js"
6
- import type {
7
- BlockingShellCommandOutput,
8
- RunExCommandOutput,
9
- RunLuaCodeOutput,
10
- StartNeovimGenericArguments,
11
- TestDirectory,
12
- } from "../../types.js"
13
- import type { TestServerConfig } from "../../updateTestdirectorySchemaFile.js"
14
- import { convertEventEmitterToAsyncGenerator } from "../../utilities/generator.js"
15
- import { Lazy } from "../../utilities/Lazy.js"
16
- import type { TabId } from "../../utilities/tabId.js"
17
- import { timeout } from "../../utilities/timeout.js"
18
- import { executeBlockingShellCommand } from "../terminal/runBlockingShellCommand.js"
19
- import type { StdoutOrStderrMessage, TerminalDimensions } from "./NeovimApplication.js"
20
- import { NeovimApplication } from "./NeovimApplication.js"
21
- import type { ExCommandInput, LuaCodeInput } from "./neovimRouter.js"
22
- import { prepareNewTestDirectory } from "./prepareNewTestDirectory.js"
23
-
24
- const neovims = new Map<TabId["tabId"], NeovimApplication>()
25
- const resources: Lazy<AsyncDisposableStack> = new Lazy(() => {
26
- return new AsyncDisposableStack()
27
- })
28
-
29
- const log = debuglog("tui-sandbox.neovim.api")
30
-
31
- export async function installDependencies(NVIM_APPNAME: string | undefined, config: TestServerConfig): Promise<void> {
32
- await using app = new NeovimApplication(config.directories.testEnvironmentPath)
33
- const testDirectory = await prepareNewTestDirectory(config)
34
- const prepareFilePath = path.join(testDirectory.rootPathAbsolute, ".config", NVIM_APPNAME ?? "nvim", "prepare.lua")
35
- try {
36
- await access(prepareFilePath)
37
- } catch {
38
- // show the output here because it's typically shown in the console before
39
- // the tests start. It's also sensitive to outside changes.
40
- //
41
- // eslint-disable-next-line no-restricted-properties
42
- console.log(
43
- `Neovim prepareFilePath does not exist: ${prepareFilePath}. If you want to run a prepare script before starting the tests, create it.`
44
- )
45
- return
46
- }
47
-
48
- // eslint-disable-next-line no-restricted-properties
49
- console.log(`🚀 Running Neovim prepareFilePath ${prepareFilePath}...`)
50
-
51
- let output = ""
52
- app.events.on("stdout" satisfies StdoutOrStderrMessage, data => {
53
- assert(data)
54
- assert(typeof data === "string")
55
- output += data
56
- })
57
- await app.startNextAndKillCurrent(
58
- testDirectory,
59
- {
60
- filename: "empty.txt",
61
- headlessCmd: `lua dofile("${prepareFilePath}")`,
62
- NVIM_APPNAME,
63
- },
64
- { cols: 80, rows: 24 }
65
- )
66
- await app.application.untilExit()
67
- // eslint-disable-next-line no-restricted-properties
68
- console.log(`🚀 Neovim installDependencies output:`)
69
- // eslint-disable-next-line no-restricted-properties
70
- console.log(output)
71
- }
72
-
73
- export async function initializeStdout(
74
- options: { client: TabId },
75
- signal: AbortSignal | undefined,
76
- testEnvironmentPath: string
77
- ): Promise<AsyncGenerator<string, void, unknown>> {
78
- const tabId = options.client.tabId
79
- const neovim = neovims.get(tabId) ?? new NeovimApplication(testEnvironmentPath)
80
- if (neovims.get(tabId) === undefined) {
81
- neovims.set(tabId, neovim)
82
- resources.get().use(neovim)
83
- }
84
-
85
- const stdout = convertEventEmitterToAsyncGenerator(neovim.events, "stdout" satisfies StdoutOrStderrMessage)
86
- if (signal) {
87
- signal.addEventListener("abort", () => {
88
- void neovim[Symbol.asyncDispose]().finally(() => {
89
- neovims.delete(tabId)
90
- })
91
- })
92
- }
93
-
94
- return stdout
95
- }
96
-
97
- export async function start(
98
- options: StartNeovimGenericArguments,
99
- terminalDimensions: TerminalDimensions,
100
- tabId: TabId,
101
- config: TestServerConfig
102
- ): Promise<TestDirectory> {
103
- const neovim = neovims.get(tabId.tabId)
104
- assert(neovim, `Neovim instance not found for client id ${tabId.tabId}`)
105
-
106
- const testDirectory = await prepareNewTestDirectory(config)
107
- await neovim.startNextAndKillCurrent(testDirectory, options, terminalDimensions)
108
-
109
- return testDirectory
110
- }
111
-
112
- export async function sendStdin(options: { tabId: TabId; data: string }): Promise<void> {
113
- const neovim = neovims.get(options.tabId.tabId)
114
- assert(
115
- neovim !== undefined,
116
- `Neovim instance for clientId not found - cannot send stdin. Maybe it's not started yet?`
117
- )
118
- assert(
119
- neovim.application,
120
- `Neovim application not found for client id ${options.tabId.tabId}. Maybe it's not started yet?`
121
- )
122
-
123
- await neovim.application.write(options.data)
124
- }
125
-
126
- export async function runBlockingShellCommand(
127
- signal: AbortSignal | undefined,
128
- input: BlockingCommandInput,
129
- allowFailure: boolean
130
- ): Promise<BlockingShellCommandOutput> {
131
- const neovim = neovims.get(input.tabId.tabId)
132
- assert(
133
- neovim !== undefined,
134
- `Neovim instance for clientId not found - cannot run blocking shell command. Maybe neovim's not started yet?`
135
- )
136
- const testDirectory = neovim.state?.testDirectory
137
- assert(testDirectory, `Test directory not found for client id ${input.tabId.tabId}. Maybe neovim's not started yet?`)
138
-
139
- const env = NeovimApplication.getEnvironmentVariables(testDirectory, neovim.state?.NVIM_APPNAME, input.envOverrides)
140
- return executeBlockingShellCommand(testDirectory, input, signal, allowFailure, env)
141
- }
142
-
143
- export async function runLuaCode(options: LuaCodeInput): Promise<RunLuaCodeOutput> {
144
- const neovim = neovims.get(options.tabId.tabId)
145
- assert(
146
- neovim !== undefined,
147
- `Neovim instance for clientId not found - cannot run Lua code. Maybe neovim's not started yet?`
148
- )
149
- assert(
150
- neovim.application,
151
- `Neovim application not found for client id ${options.tabId.tabId}. Maybe it's not started yet?`
152
- )
153
-
154
- const api = await neovim.state?.client.get()
155
- if (!api) {
156
- throw new Error(`Neovim API not available for client id ${options.tabId.tabId}. Maybe it's not started yet?`)
157
- }
158
-
159
- log(`Neovim ${neovim.application.processId()} running Lua code: ${options.luaCode}`)
160
- try {
161
- const value = await api.lua(options.luaCode)
162
- return { value }
163
- } catch (e) {
164
- console.warn(`Error running Lua code: ${options.luaCode}`, e)
165
- throw new Error(`Error running Lua code: ${options.luaCode}`, { cause: e })
166
- }
167
- }
168
-
169
- export type PollLuaCodeInput = {
170
- luaAssertion: string
171
- tabId: TabId
172
- }
173
-
174
- export async function waitForLuaCode(
175
- options: PollLuaCodeInput,
176
- signal: AbortSignal | undefined
177
- ): Promise<RunLuaCodeOutput> {
178
- const neovim = neovims.get(options.tabId.tabId)
179
- assert(
180
- neovim !== undefined,
181
- `Neovim instance for clientId not found - cannot pollLuaCode. Maybe neovim's not started yet?`
182
- )
183
- assert(
184
- neovim.application,
185
- `Neovim application not found for client id ${options.tabId.tabId}. Maybe it's not started yet?`
186
- )
187
-
188
- const api = await neovim.state?.client.get()
189
- if (!api) {
190
- throw new Error(`Neovim API not available for client id ${options.tabId.tabId}. Maybe it's not started yet?`)
191
- }
192
-
193
- log(`Neovim ${neovim.application.processId()} polling Lua code: ${options.luaAssertion}`)
194
-
195
- let running: boolean = true
196
- signal?.addEventListener("abort", () => {
197
- log(`Polling Lua code: '${options.luaAssertion}' was aborted via signal`)
198
- running = false
199
- })
200
-
201
- const failureMessages = new Set<string>()
202
- const reportFailure = () => {
203
- console.warn(`Polling Lua code: '${options.luaAssertion}' failed. Failure messages:`, failureMessages)
204
- }
205
-
206
- const maxIterations = 100
207
- for (let iteration = 1; iteration <= maxIterations; iteration++) {
208
- if (!running) {
209
- reportFailure()
210
- throw new Error(`Polling Lua code: '${options.luaAssertion}' was aborted after ${iteration} iterations`)
211
- }
212
-
213
- try {
214
- const value = await api.lua(options.luaAssertion)
215
- log(`Lua code assertion passed: ${options.luaAssertion} (iteration ${iteration})`)
216
-
217
- return { value }
218
- } catch (e) {
219
- failureMessages.add(String(e))
220
- await timeout(100)
221
- }
222
- }
223
-
224
- reportFailure()
225
- throw new Error(
226
- `Polling Lua code: '${options.luaAssertion}' always raised an error after ${maxIterations} iterations`
227
- )
228
- }
229
-
230
- export async function runExCommand(options: ExCommandInput): Promise<RunExCommandOutput> {
231
- const neovim = neovims.get(options.tabId.tabId)
232
- assert(
233
- neovim !== undefined,
234
- `Neovim instance for clientId not found - cannot runExCommand. Maybe neovim's not started yet?`
235
- )
236
- assert(
237
- neovim.application,
238
- `Neovim application not found for client id ${options.tabId.tabId}. Maybe it's not started yet?`
239
- )
240
-
241
- const api = await neovim.state?.client.get()
242
- if (!api) {
243
- throw new Error(`Neovim API not available for client id ${options.tabId.tabId}. Maybe it's not started yet?`)
244
- }
245
-
246
- log(`Neovim ${neovim.application.processId()} running Ex command: ${options.command}`)
247
- try {
248
- const output = await api.commandOutput(options.command)
249
- if (options.log) {
250
- console.info(`:${options.command} output: ${output}`)
251
- }
252
- return { value: output }
253
- } catch (e) {
254
- console.warn(`Error running Ex command: ${options.command}`, e)
255
- throw new Error(`Error running Ex command: ${options.command}`, { cause: e })
256
- }
257
- }
@@ -1,18 +0,0 @@
1
- import fs from "fs"
2
- import nodePath from "path"
3
-
4
- export type TestTempDirPrefix = "test-temp-dir-"
5
-
6
- export class TempDirectory implements Disposable {
7
- private constructor(public readonly path: string) {}
8
-
9
- public static create(): TempDirectory {
10
- const tmp = fs.mkdtempSync("test-temp-dir-" satisfies TestTempDirPrefix)
11
- const absolutePath = nodePath.resolve(tmp)
12
- return new TempDirectory(absolutePath)
13
- }
14
-
15
- [Symbol.dispose](): void {
16
- fs.rmdirSync(this.path, { recursive: true, maxRetries: 5 })
17
- }
18
- }
@@ -1,29 +0,0 @@
1
- import nodePath from "path"
2
- import type { PartialDeep } from "type-fest"
3
- import { expect, it, vi } from "vitest"
4
- import { createDefaultConfig } from "../../../config.js"
5
- import type { TestServerConfig } from "../../../updateTestdirectorySchemaFile.js"
6
- import type { TestDirsPath } from "./createTempDir.js"
7
- import { createTempDir } from "./createTempDir.js"
8
- import type { TestTempDirPrefix } from "./TempDirectory.js"
9
- import { TempDirectory } from "./TempDirectory.js"
10
-
11
- vi.spyOn(console, "log").mockImplementation(vi.fn())
12
-
13
- it("should create a temp dir with no contents", async () => {
14
- // typically the user will want to have contents, but this should not be an error
15
- using dir = TempDirectory.create()
16
- const result = await createTempDir({
17
- ...createDefaultConfig("cwd", {}),
18
- directories: {
19
- testEnvironmentPath: dir.path,
20
- outputFilePath: nodePath.join(dir.path, "MyTestDirectory.ts"),
21
- latestSymlinkName: "latest",
22
- },
23
- } satisfies PartialDeep<TestServerConfig>)
24
-
25
- expect(result.contents).toEqual({})
26
- expect(result.testEnvironmentPath).toEqual(dir.path)
27
- expect(result.testEnvironmentPath.includes("test-temp-dir-" satisfies TestTempDirPrefix)).toBeTruthy()
28
- expect(result.testEnvironmentPathRelative.includes("testdirs" satisfies TestDirsPath)).toBeTruthy()
29
- })