Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(findNvim)!: rename paths:string[] => cmds:string[][] #432

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/neovim/src/testUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`);
}
Expand Down
6 changes: 3 additions & 3 deletions packages/neovim/src/utils/findNvim.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
66 changes: 38 additions & 28 deletions packages/neovim/src/utils/findNvim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -148,48 +159,47 @@ function normalizePath(path: string): string {
}

function getPlatformSearchDirs(): Set<string> {
const paths = new Set<string>();
const dirs = new Set<string>();
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;
}

/**
Expand All @@ -198,13 +208,13 @@ function getPlatformSearchDirs(): Set<string> {
* @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<FindNvimResult> {
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<string>([
...normalizedPathsFromUser,
Expand Down
Loading