Feature: autocomplete commands#184
Conversation
| const currentValue = textarea.value | ||
| const cursorPosition = textarea.selectionStart || 0 | ||
|
|
||
| const lineStart = currentValue.lastIndexOf("\n", cursorPosition - 1) + 1 |
There was a problem hiding this comment.
this probably can be inlined on line 93
| } | ||
| } | ||
| break | ||
| case "Tab": |
There was a problem hiding this comment.
looks like Tab and Enter can be collapsed since their handlers are identical
case "Enter":
case "Tab": {
| const afterLine = currentValue.substring(lineEnd) | ||
| const newValue = beforeCommand + commandText + afterLine | ||
|
|
||
| textarea.value = newValue |
There was a problem hiding this comment.
from this point, this feels very off tbh — we have a react-state-managed textarea component and suddenly it's force-fed directly through DOM, and an event impersonating a user, while we have setState.
Essentially, we have a split-brain between the hook and component states
| navigateToLast, | ||
| } | ||
|
|
||
| return { state, actions } |
There was a problem hiding this comment.
-
This hook in general does way too many things in a intertwined way. Also, I cannot see how this hook can be reused by other components that don't do "send command", so a hook seems like a wrong abstraction.
-
Given that we're building a specialized component that does autocompletion of valkey commands, it seems that the right abstraction is still a component wrapping the textarea where the user types, the autocompletion dropdown, and contains the logic and the shared state (textarea.value, cursor position, commands matches)
-
Given the reactive nature of this flow, you had to add debouncing via setTimeouts and all that which makes the logic hard to follow, so I suggest leveraging the fact that we already have rxjs where these operators are OOB. You can create a subject and feed in onChange of textarea and pipe it to debounce, map with all the transforms and filter and in subscribe do setSuggestions
arseny-kostenko
left a comment
There was a problem hiding this comment.
Thanks for the work on this, but I can’t approve the PR in its current form.
Some feedback is in this comment https://github.com/valkey-io/valkey-admin/pull/184/files#r2700170446
Overall, the implementation lacks modularity and mixes too many responsibilities into single units (notably the autocomplete hook). UI state management, debouncing, matching logic, keyboard navigation, and direct DOM mutation are all tightly coupled, which makes the code hard to reason about, test, and evolve. It also ignores the existing design and tooling.
There’s a lot of imperative, step-by-step logic (“prepare variables now, use them later”) and manual DOM manipulation that fights React’s declarative model. As a result, the code reads as spaghetti rather than a composition of clear, well-defined pieces.
Before this can be merged, I’d like to see:
- Clear separation between text parsing/insertion logic, autocomplete state, and UI rendering.
- Removal of imperative DOM mutations (textarea.value, synthetic input events) in favour of controlled state
- Smaller, focused modules/hooks with single responsibilities — if it's a hook under /hooks directory, it's expected to be a reusable between components hook. Otherwise, if it's just a named function used inside a useEffect/useCallback — it should be close to the component that uses it.
- useEffect and useCallbacks need to be justified
I’m happy to review a refactor along those lines.
Signed-off-by: Allen Helton <allenheltondev@gmail.com>
|
@arseny-kostenko I really appreciate the thorough review and suggestions. I just pushed a major simplification following your requested guidelines 👍 |
|
Thanks @allenheltondev, this looks way better! I'll do a more thorough review next week. Meanwhile, could you leave some comments around text insertion util — how it works and etc. Thank you! |
| /** | ||
| * Calculate Levenshtein distance between two strings | ||
| */ | ||
| function levenshteinDistance(str1: string, str2: string): number { |
There was a problem hiding this comment.
let's switch to https://www.npmjs.com/package/fastest-levenshtein
this current Levenshtein implementation builds a full (n × m) matrix for every comparison, which is O(n x m) in both time and memory. Since this runs on each keystroke across multiple candidates, it creates unnecessary allocations and GC pressure.
fastest-levenshtein uses a Myers bit-parallel algorithm that avoids building the full matrix, so it's much faster on small and medium strings and has lower memory overhead, and is well-tested
Description
Adding an autocomplete/intellisense for the command page. Works for commands only (no resources), but does tell you the parameters required for the command. This is for issue #151
Change Visualization
Command page with autocomplete open
