Skip to content
Merged
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
26 changes: 15 additions & 11 deletions packages/lsp/src/server/pyright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

import type { LSPServerInfo } from './types'
import { spawn } from 'node:child_process'
import process from 'node:process'
import { attachLSPProcessHandlers, log, nearestRoot } from './utils'

export const PYRIGHT_VERSION = '1.1.414'

export const PyrightServer: LSPServerInfo = {
id: 'pyright',
extensions: ['.py', '.pyi'],
Expand All @@ -16,20 +19,21 @@ export const PyrightServer: LSPServerInfo = {
'pyrightconfig.json',
]),
async spawn(root) {
const pyright = Bun.which('pyright-langserver')
// Read PATH now rather than the copy Bun captured at startup
const pyright = Bun.which('pyright-langserver', { PATH: process.env.PATH ?? '' })
if (!pyright) {
// Try via bunx/npx
try {
const proc = spawn('bunx', ['pyright-langserver', '--stdio'], {
cwd: root,
})
attachLSPProcessHandlers(proc, 'pyright')
return { process: proc }
}
catch (err) {
log.error({ err }, 'Failed to spawn pyright-langserver via bunx')
// pyright-langserver is a bin of the pyright package, not a package itself.
// spawn reports a missing bunx only as an async 'error' event, so check for it first.
const bunx = Bun.which('bunx', { PATH: process.env.PATH ?? '' })
Comment thread
amondnet marked this conversation as resolved.
if (!bunx) {
log.warn({ root }, 'pyright not found. Install with: npm install pyright')
return undefined
}
const proc = spawn(bunx, ['-p', `pyright@${PYRIGHT_VERSION}`, 'pyright-langserver', '--stdio'], {
cwd: root,
})
attachLSPProcessHandlers(proc, 'pyright')
return { process: proc }
}

const proc = spawn(pyright, ['--stdio'], {
Expand Down
30 changes: 30 additions & 0 deletions packages/lsp/test/unit/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,36 @@ describe.skipIf(process.platform === 'win32')('BiomeServer fallback', () => {
})
})

describe.skipIf(process.platform === 'win32')('PyrightServer fallback', () => {
test('falls back to bunx with a pinned pyright when pyright-langserver is not installed', async () => {
const invoked = await spawnServerWith(PyrightServer, { bunx: recorder('bunx') })
expect(invoked).toBe('bunx -p pyright@1.1.414 pyright-langserver --stdio')
})

test('prefers pyright-langserver on PATH over bunx', async () => {
const invoked = await spawnServerWith(PyrightServer, { 'pyright-langserver': recorder('path'), 'bunx': recorder('bunx') })
expect(invoked).toBe('path --stdio')
})

test('returns undefined with the install hint when bunx is missing', async () => {
const emptyDir = await fs.mkdtemp(path.join(os.tmpdir(), 'lsp-fallback-empty-'))
const savedPath = process.env.PATH
const warn = spyOn(log, 'warn')
try {
process.env.PATH = emptyDir
const handle = await PyrightServer.spawn(emptyDir)
process.env.PATH = savedPath
expect(handle).toBeUndefined()
expect(warn).toHaveBeenCalledWith(expect.anything(), 'pyright not found. Install with: npm install pyright')
}
finally {
warn.mockRestore()
process.env.PATH = savedPath
await fs.rm(emptyDir, { recursive: true, force: true })
}
})
})

describe('VueServer', () => {
test('has correct id', () => {
expect(VueServer.id).toBe('vue')
Expand Down
Loading