From 703857091f62727b0262ebf4c56449da2dd55382 Mon Sep 17 00:00:00 2001 From: Chapman Pendery Date: Fri, 6 Oct 2023 21:02:42 -0700 Subject: [PATCH] feat: render suggestions in the UI Signed-off-by: Chapman Pendery --- src/commands/root.ts | 2 +- src/runtime/model.ts | 5 +++ src/runtime/runtime.ts | 3 +- src/runtime/suggestion.ts | 7 +--- src/ui/suggestions.tsx | 82 +++++++++++++-------------------------- src/ui/ui.tsx | 34 ++++++---------- tsconfig.json | 5 +-- 7 files changed, 49 insertions(+), 89 deletions(-) diff --git a/src/commands/root.ts b/src/commands/root.ts index 59ca538..f03ea4b 100644 --- a/src/commands/root.ts +++ b/src/commands/root.ts @@ -1,4 +1,4 @@ -import { render } from "@/ui/ui.js"; +import { render } from "../ui/ui.js"; export const action = () => { render(); diff --git a/src/runtime/model.ts b/src/runtime/model.ts index 87be41e..97c8fff 100644 --- a/src/runtime/model.ts +++ b/src/runtime/model.ts @@ -3,3 +3,8 @@ export type Suggestion = { description?: string; icon: string; }; + +export type SuggestionBlob = { + suggestions: Suggestion[]; + argumentDescription?: string; +}; diff --git a/src/runtime/runtime.ts b/src/runtime/runtime.ts index 3fe1f81..b8beec7 100644 --- a/src/runtime/runtime.ts +++ b/src/runtime/runtime.ts @@ -3,7 +3,8 @@ import speclist, { // @ts-ignore } from "@withfig/autocomplete/build/index.js"; import { parseCommand, CommandToken } from "./parser.js"; -import { SuggestionBlob, getArgDrivenRecommendation, getSubcommandDrivenRecommendation } from "./suggestion.js"; +import { getArgDrivenRecommendation, getSubcommandDrivenRecommendation } from "./suggestion.js"; +import { SuggestionBlob } from "./model.js"; const specSet: any = {}; (speclist as string[]).forEach((s) => { diff --git a/src/runtime/suggestion.ts b/src/runtime/suggestion.ts index 6e5511c..b369db3 100644 --- a/src/runtime/suggestion.ts +++ b/src/runtime/suggestion.ts @@ -1,7 +1,7 @@ import { CommandToken } from "./parser.js"; import { runGenerator } from "./generator.js"; import { runTemplates } from "./template.js"; -import { Suggestion } from "./model.js"; +import { Suggestion, SuggestionBlob } from "./model.js"; // TODO: support other suggestion attributes enum SuggestionIcons { @@ -89,11 +89,6 @@ function filter - {suggestions.map((suggestion, idx) => { - const bgColor = - idx === activeSuggestionIdx - ? ActiveSuggestionBackgroundColor - : undefined; + + + {suggestions.map((suggestion, idx) => { + const bgColor = idx === activeSuggestionIdx ? ActiveSuggestionBackgroundColor : undefined; - const rawName = suggestion.displayName ?? (suggestion.name || ""); - const name = - typeof rawName === "string" ? rawName : rawName.at(0) || ""; - if (name.length === 0) return <>; + const name = suggestion.name; + if (name.length === 0) return <>; - return ( - - - {name.padEnd(SuggestionWidth - BorderWidth, " ")} - - - ); - })} + return ( + + + {name.padEnd(SuggestionWidth - BorderWidth, " ")} + + + ); + })} + ); } @@ -57,39 +49,27 @@ export default function Suggestions({ suggestions, }: { leftPadding: number; - setActiveSuggestion: (_: Fig.Suggestion) => void; - suggestions: Fig.Suggestion[]; + setActiveSuggestion: (_: Suggestion) => void; + suggestions: Suggestion[]; }) { const [activeSuggestionIndex, setActiveSuggestionIndex] = useState(0); const [windowWidth, setWindowWidth] = useState(500); const page = Math.floor(activeSuggestionIndex / MaxSuggestions) + 1; - const pagedSuggestions = suggestions.filter( - (_, idx) => - idx < page * MaxSuggestions && idx >= (page - 1) * MaxSuggestions - ); + const pagedSuggestions = suggestions.filter((_, idx) => idx < page * MaxSuggestions && idx >= (page - 1) * MaxSuggestions); const activePagedSuggestionIndex = activeSuggestionIndex % MaxSuggestions; - const activeDescription = - pagedSuggestions.at(activePagedSuggestionIndex)?.description || ""; + const activeDescription = pagedSuggestions.at(activePagedSuggestionIndex)?.description || ""; // TODO: tweak this logic to be more accurate as it gives bad offsets on wrap const wrappedPadding = leftPadding % windowWidth; - const maxPadding = - activeDescription.length !== 0 - ? windowWidth - SuggestionWidth - DescriptionWidth - : windowWidth - SuggestionWidth; + const maxPadding = activeDescription.length !== 0 ? windowWidth - SuggestionWidth - DescriptionWidth : windowWidth - SuggestionWidth; const swapDescription = wrappedPadding > maxPadding; - const swappedPadding = swapDescription - ? Math.max(wrappedPadding - DescriptionWidth, 0) - : wrappedPadding; + const swappedPadding = swapDescription ? Math.max(wrappedPadding - DescriptionWidth, 0) : wrappedPadding; - const clampedLeftPadding = Math.min( - Math.min(wrappedPadding, swappedPadding), - maxPadding - ); + const clampedLeftPadding = Math.min(Math.min(wrappedPadding, swappedPadding), maxPadding); useInput((_, key) => { if (key.upArrow) { @@ -97,9 +77,7 @@ export default function Suggestions({ setActiveSuggestion(suggestions[activeSuggestionIndex]); } if (key.downArrow) { - setActiveSuggestionIndex( - Math.min(activeSuggestionIndex + 1, suggestions.length - 1) - ); + setActiveSuggestionIndex(Math.min(activeSuggestionIndex + 1, suggestions.length - 1)); setActiveSuggestion(suggestions[activeSuggestionIndex]); } }); @@ -119,17 +97,11 @@ export default function Suggestions({ {swapDescription ? ( <> - + ) : ( <> - + )} diff --git a/src/ui/ui.tsx b/src/ui/ui.tsx index da49f9a..49c6208 100644 --- a/src/ui/ui.tsx +++ b/src/ui/ui.tsx @@ -1,23 +1,19 @@ import React, { useCallback, useEffect, useState } from "react"; -import { - Text, - Box, - useInput, - render as inkRender, - measureElement, - DOMElement, -} from "ink"; +import { Text, Box, useInput, render as inkRender, measureElement, DOMElement } from "ink"; import wrapAnsi from "wrap-ansi"; -import { getSuggestions } from "@/runtime/runtime.js"; +import { getSuggestions } from "../runtime/runtime.js"; +import { Suggestion } from "../runtime/model.js"; import Cursor from "./cursor.js"; import Suggestions from "./suggestions.js"; +// TODO: fix issue where we are not rendering some rows of the suggestion UI + const Prompt = "> "; function UI() { const [command, setCommand] = useState(""); - const [activeSuggestion, setActiveSuggestion] = useState(); - const [suggestions, setSuggestions] = useState([]); + const [activeSuggestion, setActiveSuggestion] = useState(); + const [suggestions, setSuggestions] = useState([]); const [windowWidth, setWindowWidth] = useState(500); const leftPadding = getLeftPadding(windowWidth, command); @@ -29,7 +25,9 @@ function UI() { }, []); useEffect(() => { - setSuggestions(getSuggestions(command)); + getSuggestions(command).then((suggestions) => { + setSuggestions(suggestions?.suggestions ?? []); + }); }, [command]); useInput((input, key) => { @@ -51,11 +49,7 @@ function UI() { - + ); } @@ -71,9 +65,5 @@ function getLeftPadding(windowWidth: number, command: string) { hard: true, }); const lines = wrappedText.split("\n"); - return ( - (lines.length - 1) * windowWidth + - lines[lines.length - 1].length + - Prompt.length - ); + return (lines.length - 1) * windowWidth + lines[lines.length - 1].length + Prompt.length; } diff --git a/tsconfig.json b/tsconfig.json index e977d09..0ab261f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,9 +5,6 @@ "compilerOptions": { "jsx": "react", "outDir": "./build", - "types": ["@withfig/autocomplete-types", "jest"], - "paths": { - "@/*": ["./src/*"] - } + "types": ["@withfig/autocomplete-types", "jest"] } }