diff --git a/packages/neovim/src/testUtil.ts b/packages/neovim/src/testUtil.ts index 9cbd8c05..b3168e92 100644 --- a/packages/neovim/src/testUtil.ts +++ b/packages/neovim/src/testUtil.ts @@ -10,7 +10,7 @@ import { getLogger } from './utils/logger'; export function findNvimOrFail() { const minVersion = '0.9.5'; - const found = findNvim({ minVersion }); + const found = findNvim({ minVersion, firstMatch: true }); if (found.matches.length === 0) { throw new Error(`nvim ${minVersion} not found`); } diff --git a/packages/neovim/src/utils/findNvim.test.ts b/packages/neovim/src/utils/findNvim.test.ts index e647d7f7..fdea43d8 100644 --- a/packages/neovim/src/utils/findNvim.test.ts +++ b/packages/neovim/src/utils/findNvim.test.ts @@ -149,13 +149,13 @@ describe('findNvim', () => { }); }); - it('searches in additional custom paths', () => { + it('tries locations or commands given by `cmds`', () => { const customPaths = [ join(process.cwd(), 'package.json'), '/custom/path/to/nvim', '/another/custom/path', - ].map(normalizePath); - const nvimRes = findNvim({ paths: customPaths }); + ].map(s => [ normalizePath(s) ]); + const nvimRes = findNvim({ cmds: customPaths }); expect(nvimRes.matches.length).toBeGreaterThanOrEqual(1); diff --git a/packages/neovim/src/utils/findNvim.ts b/packages/neovim/src/utils/findNvim.ts index abca031e..5f9099fc 100644 --- a/packages/neovim/src/utils/findNvim.ts +++ b/packages/neovim/src/utils/findNvim.ts @@ -3,8 +3,13 @@ import { join, delimiter, normalize } from 'node:path'; import { constants, existsSync, accessSync } from 'node:fs'; export type NvimVersion = { - /** Path to `nvim` executable. */ + /** + * @deprecated + * Path to `nvim` executable. + */ readonly path: string; + /** Nvim location or invocation command. */ + readonly cmd: string[]; /** Nvim version, or undefined if there was an error. */ readonly nvimVersion?: string; /** Nvim build type, or undefined if there was an error. */ @@ -36,16 +41,22 @@ export type FindNvimOptions = { */ readonly firstMatch?: boolean; /** - * (Optional) Additional specific file paths to check for Nvim executables. - * These paths will be checked before searching `dirs`. - * Useful for allowing users to specify exact Nvim executable locations. + * (Optional) Specific commands that (potentially) invoke Nvim and can receive arbitrary args. + * Checked before searching `dirs`. Useful for checking a user-configured Nvim location or + * unconventional wrappers such as Windows WSL. * - * Example: ['/usr/local/bin/nvim', '/opt/homebrew/bin/nvim'] + * Example: + * ``` + * cmds: [ + * ['/usr/bin/env', 'nvim'], + * ['/opt/homebrew/bin/nvim'], + * ], + * ``` */ - readonly paths?: string[]; + readonly cmds?: string[][]; /** * (Optional) Additional directories to search for Nvim executables. - * These directories will be searched after checking `paths` + * These directories will be searched after checking `cmds` * but before searching `$PATH` and other default locations. * Useful for including non-standard installation directories. * @@ -148,48 +159,47 @@ function normalizePath(path: string): string { } function getPlatformSearchDirs(): Set { - const paths = new Set(); + const dirs = new Set(); const { PATH, USERPROFILE, LOCALAPPDATA, PROGRAMFILES, HOME } = process.env; - PATH?.split(delimiter).forEach(p => paths.add(normalizePath(p))); + PATH?.split(delimiter).forEach(p => dirs.add(normalizePath(p))); - // Add common Neovim installation paths not always in the system's PATH. + // Add common Nvim locations which may not be in the system $PATH. if (windows) { - // Scoop common install location + // Scoop install location. if (USERPROFILE) { - paths.add(normalizePath(`${USERPROFILE}/scoop/shims`)); + dirs.add(normalizePath(`${USERPROFILE}/scoop/shims`)); } - paths.add(normalizePath('C:/ProgramData/scoop/shims')); + dirs.add(normalizePath('C:/ProgramData/scoop/shims')); - // Winget common install location - // See https://github.com/microsoft/winget-cli/blob/master/doc/specs/%23182%20-%20Support%20for%20installation%20of%20portable%20standalone%20apps.md + // Winget install location. https://github.com/microsoft/winget-cli/blob/master/doc/specs/%23182%20-%20Support%20for%20installation%20of%20portable%20standalone%20apps.md if (LOCALAPPDATA) { - paths.add(normalizePath(`${LOCALAPPDATA}/Microsoft/WindowsApps`)); - paths.add(normalizePath(`${LOCALAPPDATA}/Microsoft/WinGet/Packages`)); + dirs.add(normalizePath(`${LOCALAPPDATA}/Microsoft/WindowsApps`)); + dirs.add(normalizePath(`${LOCALAPPDATA}/Microsoft/WinGet/Packages`)); } if (PROGRAMFILES) { - paths.add(normalizePath(`${PROGRAMFILES}/Neovim/bin`)); - paths.add(normalizePath(`${PROGRAMFILES} (x86)/Neovim/bin`)); - paths.add(normalizePath(`${PROGRAMFILES}/WinGet/Packages`)); - paths.add(normalizePath(`${PROGRAMFILES} (x86)/WinGet/Packages`)); + dirs.add(normalizePath(`${PROGRAMFILES}/Neovim/bin`)); + dirs.add(normalizePath(`${PROGRAMFILES} (x86)/Neovim/bin`)); + dirs.add(normalizePath(`${PROGRAMFILES}/WinGet/Packages`)); + dirs.add(normalizePath(`${PROGRAMFILES} (x86)/WinGet/Packages`)); } } else { - // Common paths for Unix-like systems + // Common locations for Unix-like systems. [ '/usr/local/bin', '/usr/bin', '/opt/homebrew/bin', '/home/linuxbrew/.linuxbrew/bin', '/snap/nvim/current/usr/bin', - ].forEach(p => paths.add(p)); + ].forEach(p => dirs.add(p)); if (HOME) { - paths.add(normalizePath(`${HOME}/bin`)); - paths.add(normalizePath(`${HOME}/.linuxbrew/bin`)); + dirs.add(normalizePath(`${HOME}/bin`)); + dirs.add(normalizePath(`${HOME}/.linuxbrew/bin`)); } } - return paths; + return dirs; } /** @@ -198,13 +208,13 @@ function getPlatformSearchDirs(): Set { * @param opt.minVersion See {@link FindNvimOptions.minVersion} * @param opt.orderBy See {@link FindNvimOptions.orderBy} * @param opt.firstMatch See {@link FindNvimOptions.firstMatch} - * @param opt.paths See {@link FindNvimOptions.paths} + * @param opt.cmds See {@link FindNvimOptions.cmds} * @param opt.dirs See {@link FindNvimOptions.dirs} */ export function findNvim(opt: FindNvimOptions = {}): Readonly { const platformDirs = getPlatformSearchDirs(); const nvimExecutable = windows ? 'nvim.exe' : 'nvim'; - const normalizedPathsFromUser = (opt.paths ?? []).map(normalizePath); + const normalizedPathsFromUser = (opt.cmds ?? []).map(a => normalizePath); const allPaths = new Set([ ...normalizedPathsFromUser,