Skip to content

[#19] Added inline ghost-text autocomplete to text input.#34

Open
AlexSkrypnyk wants to merge 3 commits into
mainfrom
feature/19-ghost-text
Open

[#19] Added inline ghost-text autocomplete to text input.#34
AlexSkrypnyk wants to merge 3 commits into
mainfrom
feature/19-ghost-text

Conversation

@AlexSkrypnyk

Copy link
Copy Markdown
Member

Closes #19

Summary

Adds inline ghost-text autocomplete to the text widget: as the user types, the single best-matching candidate is shown dimmed after the caret and accepted with Tab or Right-arrow (when the caret sits at the end of the line).
The completion source is set via a new FieldBuilder::complete(), taking either a static list of strings or a closure over the answers collected so far.
Matching is a Unicode-aware (mbstring) case-insensitive prefix match; ghost-text styling is driven by a new ThemeInterface::ghost() theme atom and is suppressed entirely in no-ANSI mode.
Two design choices were made autonomously and are worth a second look from the reviewer: matching is case-insensitive prefix, where accepting canonicalises the typed buffer to the candidate's own case, and the builder method is named complete().
Headless collection is unaffected, since completion only lives in the widget/factory/interactive layer.

Changes

  • Action::Complete - new action bound to Tab in the Text field key-scope (DefaultKeyMap); Right-arrow also accepts the completion when the caret is at the end of the buffer, otherwise it just moves the caret as before
  • TextWidget - bestMatch(), ghostSuffix() and applyCompletion() implement the match/render/accept logic, using mb_strtolower()/mb_strlen()/mb_substr() so multi-byte candidates fold and split correctly
  • Field / FieldBuilder - new completion property (a list<string>|\Closure) plus FieldBuilder::complete() to set it
  • WidgetFactory - completionsFor() resolves the completion source, calling a closure with the answers collected so far and coercing the result to a list<string> so a mistyped source degrades to no completion rather than erroring; create() gains an $answers parameter
  • PanelController::openEditor() - now passes the live $this->values through to WidgetFactory::create() so a closure-based completion source can see prior answers
  • DefaultTheme::ghost() / ThemeInterface::ghost() - new dimmed-gray theme atom for the ghost suffix, returning an empty string when colour is off
  • Tests across TextWidgetTest, WidgetFactoryTest, FormTest, KeyMapTest and ThemeTest cover matching, accepting, rendering, Unicode folding, closure wiring, and the no-colour suppression path
  • README - documents complete() under the Text section with a static-list example and a closure-over-answers example

Before / After

Before: plain text input                    After: inline ghost-text autocomplete

┌───────────────────────────────┐           ┌───────────────────────────────┐
│ Repository                     │           │ Repository                     │
│ acm█                           │           │ acm█e-site                     │
└───────────────────────────────┘           └───────────────────────────────┘
                                                       ^^^^^^^ dimmed gray,
  no hint of what to type next                accepted with Tab or Right-arrow
                                               at end of line; typed text is
                                               untouched until accepted

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8f0bedcf-80ed-4632-af98-a4e4b690761b

📥 Commits

Reviewing files that changed from the base of the PR and between 8cd42c8 and b487b6f.

📒 Files selected for processing (15)
  • README.md
  • src/Builder/FieldBuilder.php
  • src/Config/Field.php
  • src/Input/Action.php
  • src/Input/DefaultKeyMap.php
  • src/Render/PanelController.php
  • src/Theme/DefaultTheme.php
  • src/Theme/ThemeInterface.php
  • src/Widget/TextWidget.php
  • src/Widget/WidgetFactory.php
  • tests/phpunit/Unit/Builder/FormTest.php
  • tests/phpunit/Unit/Input/KeyMapTest.php
  • tests/phpunit/Unit/Theme/ThemeTest.php
  • tests/phpunit/Unit/Widget/TextWidgetTest.php
  • tests/phpunit/Unit/Widget/WidgetFactoryTest.php
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/19-ghost-text

Comment @coderabbitai help to get the list of available commands.

@AlexSkrypnyk AlexSkrypnyk added the A4 Board worker 4 label Jul 10, 2026
@github-actions

Copy link
Copy Markdown
Code Coverage Report:
  2026-07-10 23:33:11

 Summary:
  Classes: 92.86% (65/70)
  Methods: 98.20% (490/499)
  Lines:   99.53% (2105/2115)

DrevOps\Tui\Answers\Answer
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Answers\Answers
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 20/ 20)
DrevOps\Tui\Answers\SummaryFormatter
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 20/ 20)
DrevOps\Tui\Builder\FieldBuilder
  Methods: 100.00% (30/30)   Lines: 100.00% ( 99/ 99)
DrevOps\Tui\Builder\Form
  Methods:  93.33% (14/15)   Lines:  98.44% ( 63/ 64)
DrevOps\Tui\Builder\PanelBuilder
  Methods: 100.00% (19/19)   Lines: 100.00% ( 31/ 31)
DrevOps\Tui\Condition\CompositeCondition
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 19/ 19)
DrevOps\Tui\Condition\Condition
  Methods: 100.00% (10/10)   Lines: 100.00% ( 38/ 38)
DrevOps\Tui\Config\Config
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 17/ 17)
DrevOps\Tui\Config\Field
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 30/ 30)
DrevOps\Tui\Config\FieldType
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  2/  2)
DrevOps\Tui\Config\NumberBounds
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 20/ 20)
DrevOps\Tui\Config\Option
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  9/  9)
DrevOps\Tui\Config\Panel
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Derive\Derive
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 14/ 14)
DrevOps\Tui\Derive\Deriver
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 13/ 13)
DrevOps\Tui\Derive\Transform
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  7/  7)
DrevOps\Tui\Discovery\AbstractDiscover
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Discovery\Dotenv
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 19/ 19)
DrevOps\Tui\Discovery\JsonValue
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 16/ 16)
DrevOps\Tui\Discovery\PathExists
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% (  3/  3)
DrevOps\Tui\Discovery\Scan
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 20/ 20)
DrevOps\Tui\Engine\Engine
  Methods: 100.00% (12/12)   Lines: 100.00% ( 95/ 95)
DrevOps\Tui\Handler\HandlerRegistry
  Methods:  85.71% ( 6/ 7)   Lines:  95.45% ( 21/ 22)
DrevOps\Tui\Input\ArrayKeyStream
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 12/ 12)
DrevOps\Tui\Input\Binding
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Input\DefaultKeyMap
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% ( 34/ 34)
DrevOps\Tui\Input\Key
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% (  7/  7)
DrevOps\Tui\Input\KeyMap
  Methods: 100.00% (12/12)   Lines: 100.00% ( 65/ 65)
DrevOps\Tui\Input\KeyMapManager
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  8/  8)
DrevOps\Tui\Input\KeyParser
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 68/ 68)
DrevOps\Tui\Input\Scope
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 11/ 11)
DrevOps\Tui\Input\ScopedKeyMap
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% (  4/  4)
DrevOps\Tui\Input\VimKeyMap
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  8/  8)
DrevOps\Tui\Render\Ansi
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Render\Box
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 15/ 15)
DrevOps\Tui\Render\ExternalEditor
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 27/ 27)
DrevOps\Tui\Render\Navigator
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 15/ 15)
DrevOps\Tui\Render\PanelController
  Methods: 100.00% (16/16)   Lines: 100.00% ( 95/ 95)
DrevOps\Tui\Render\Scroller
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 12/ 12)
DrevOps\Tui\Render\Terminal
  Methods: 100.00% (12/12)   Lines: 100.00% ( 35/ 35)
DrevOps\Tui\Render\TerminalControl
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% (  9/  9)
DrevOps\Tui\Render\Viewport
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Resolver\InputResolver
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 27/ 27)
DrevOps\Tui\Schema\AgentHelp
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 25/ 25)
DrevOps\Tui\Schema\SchemaGenerator
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 30/ 30)
DrevOps\Tui\Schema\SchemaValidator
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 37/ 37)
DrevOps\Tui\Theme\DefaultTheme
  Methods:  92.54% (62/67)   Lines:  97.27% (214/220)
DrevOps\Tui\Theme\ThemeManager
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% (  8/  8)
DrevOps\Tui\Tui
  Methods: 100.00% (12/12)   Lines: 100.00% ( 26/ 26)
DrevOps\Tui\Widget\AbstractWidget
  Methods: 100.00% (13/13)   Lines: 100.00% ( 24/ 24)
DrevOps\Tui\Widget\ChoiceListTrait
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 21/ 21)
DrevOps\Tui\Widget\ConfirmWidget
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 22/ 22)
DrevOps\Tui\Widget\FilePickerWidget
  Methods: 100.00% (30/30)   Lines: 100.00% (187/187)
DrevOps\Tui\Widget\MultiSearchWidget
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)
DrevOps\Tui\Widget\MultiSelectWidget
  Methods: 100.00% (11/11)   Lines: 100.00% ( 86/ 86)
DrevOps\Tui\Widget\NumberWidget
  Methods: 100.00% ( 9/ 9)   Lines: 100.00% ( 37/ 37)
DrevOps\Tui\Widget\PasswordDisplay
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  5/  5)
DrevOps\Tui\Widget\PasswordWidget
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 44/ 44)
DrevOps\Tui\Widget\PauseWidget
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 10/ 10)
DrevOps\Tui\Widget\SearchWidget
  Methods: 100.00% ( 6/ 6)   Lines: 100.00% ( 46/ 46)
DrevOps\Tui\Widget\SelectWidget
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 29/ 29)
DrevOps\Tui\Widget\SuggestWidget
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 38/ 38)
DrevOps\Tui\Widget\TextWidget
  Methods: 100.00% (11/11)   Lines: 100.00% ( 53/ 53)
DrevOps\Tui\Widget\TextareaWidget
  Methods: 100.00% ( 8/ 8)   Lines: 100.00% ( 52/ 52)
DrevOps\Tui\Widget\ToggleWidget
  Methods: 100.00% ( 7/ 7)   Lines: 100.00% ( 31/ 31)
DrevOps\Tui\Widget\WidgetFactory
  Methods: 100.00% ( 5/ 5)   Lines: 100.00% ( 36/ 36)
DrevOps\Tui\Widget\WidgetRunner
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  5/  5)

@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.52%. Comparing base (8cd42c8) to head (b487b6f).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #34   +/-   ##
=======================================
  Coverage   99.51%   99.52%           
=======================================
  Files          70       70           
  Lines        2082     2115   +33     
=======================================
+ Hits         2072     2105   +33     
  Misses         10       10           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A4 Board worker 4

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add inline ghost-text autocomplete to text input

1 participant