81 lines
2.2 KiB
TypeScript
81 lines
2.2 KiB
TypeScript
import { startTransition, useEffect, useRef, useState } from 'react'
|
|
|
|
import type { GatewayClient } from '../gatewayClient.js'
|
|
|
|
const TAB_PATH_RE = /((?:["']?(?:[A-Za-z]:[\\/]|\.{1,2}\/|~\/|\/|@|[^"'`\s]+\/))[^\s]*)$/
|
|
|
|
export function useCompletion(input: string, blocked: boolean, gw: GatewayClient) {
|
|
const [completions, setCompletions] = useState<{ text: string; display: string; meta: string }[]>([])
|
|
const [compIdx, setCompIdx] = useState(0)
|
|
const [compReplace, setCompReplace] = useState(0)
|
|
const ref = useRef('')
|
|
|
|
useEffect(() => {
|
|
if (blocked) {
|
|
if (completions.length) {
|
|
setCompletions([])
|
|
setCompIdx(0)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if (input === ref.current) {
|
|
return
|
|
}
|
|
|
|
ref.current = input
|
|
|
|
const isSlash = input.startsWith('/')
|
|
const pathWord = !isSlash ? (input.match(TAB_PATH_RE)?.[1] ?? null) : null
|
|
|
|
if (!isSlash && !pathWord) {
|
|
if (completions.length) {
|
|
setCompletions([])
|
|
setCompIdx(0)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
const t = setTimeout(() => {
|
|
if (ref.current !== input) {
|
|
return
|
|
}
|
|
|
|
const req = isSlash
|
|
? gw.request('complete.slash', { text: input })
|
|
: gw.request('complete.path', { word: pathWord })
|
|
|
|
req
|
|
.then((r: any) => {
|
|
if (ref.current !== input) {
|
|
return
|
|
}
|
|
|
|
startTransition(() => {
|
|
setCompletions(r?.items ?? [])
|
|
setCompIdx(0)
|
|
setCompReplace(isSlash ? (r?.replace_from ?? 1) : input.length - (pathWord?.length ?? 0))
|
|
})
|
|
})
|
|
.catch((e: unknown) => {
|
|
if (ref.current !== input) {
|
|
return
|
|
}
|
|
|
|
const meta = e instanceof Error && e.message ? e.message : 'unavailable'
|
|
startTransition(() => {
|
|
setCompletions([{ text: '', display: 'completion unavailable', meta }])
|
|
setCompIdx(0)
|
|
setCompReplace(isSlash ? 1 : input.length - (pathWord?.length ?? 0))
|
|
})
|
|
})
|
|
}, 60)
|
|
|
|
return () => clearTimeout(t)
|
|
}, [input, blocked, gw]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
return { completions, compIdx, setCompIdx, compReplace }
|
|
}
|