Skip to content

feat: add floating window support #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

krishna-bala
Copy link

@krishna-bala krishna-bala commented May 19, 2025

Summary

  • Adds support for displaying claude-code terminal in a floating window
  • Adds comprehensive configuration options for float position, size, and border
  • Preserves existing split window functionality
  • Includes documentation and examples for floating window configuration
  • Includes extensive tests for both float and split window modes

Test plan

  • Open claude-code with floating window: :ClaudeCode (with position = "float")
  • Verify floating window appears at configured position with correct size
  • Test percentage-based sizing (e.g., width = "80%", height = "60%")
  • Test centered positioning (row = "center", col = "center")
  • Test different border styles (rounded, single, double, etc.)
  • Verify toggling still works correctly with floating windows
  • Confirm all existing tests pass
  • Test that split window mode still works when float is disabled
  • Verify documentation examples work as expected

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for displaying Claude Code in floating windows with customizable size, position, and border styles.
  • Documentation

    • Updated user guides and configuration examples to document floating window support, including detailed options and usage examples.
  • Tests

    • Introduced comprehensive tests to verify floating window configuration, validation, and terminal behavior.

krishna-bala and others added 3 commits May 18, 2025 16:55
Adds comprehensive floating window support with configurable dimensions,
position, and border styles.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Adds documentation for floating window support with configurable dimensions,
position, and border styles. Includes percentage-based sizing and
automatic centering options.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Copy link

coderabbitai bot commented May 19, 2025

Walkthrough

The changes introduce comprehensive support for floating windows in the Claude Code Neovim plugin. Documentation, configuration schema, window creation logic, and test suites are all updated to support and validate a new position = "float" option, with customizable size, position, and border styles for floating terminal windows.

Changes

File(s) Change Summary
CHANGELOG.md, README.md, doc/claude-code.txt Documentation updated to describe new floating window support, configuration options, and usage examples.
lua/claude-code/config.lua Configuration schema and validation extended for position = "float" with nested float options for size, position, border, and relative anchor. Default values and validation logic enhanced accordingly.
lua/claude-code/terminal.lua Floating window creation logic added, including helper functions for dimension/position calculation and integration into the main toggle function. Existing split logic refactored to accommodate floating windows.
tests/spec/config_spec.lua New test cases added to verify parsing and preservation of float-specific window configuration options, including defaulting behavior.
tests/spec/config_validation_spec.lua Additional tests for validating window.float configuration, ensuring type and value correctness, and fallback to defaults on failure.
tests/spec/terminal_spec.lua New test suite for floating window behavior, covering creation, dimension calculations, centering, and buffer reuse logic.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ClaudeCodePlugin
    participant Neovim

    User->>ClaudeCodePlugin: Toggle Claude Code terminal
    ClaudeCodePlugin->>ClaudeCodePlugin: Parse config (check window.position)
    alt window.position == "float"
        ClaudeCodePlugin->>Neovim: Create scratch buffer (if needed)
        ClaudeCodePlugin->>Neovim: Open floating window with config (size, position, border)
        ClaudeCodePlugin->>Neovim: Set buffer/window options
        ClaudeCodePlugin->>Neovim: Run terminal command in buffer
    else window.position == "left"/"right"/"top"/"bottom"
        ClaudeCodePlugin->>Neovim: Open split window
        ClaudeCodePlugin->>Neovim: Set buffer/window options
        ClaudeCodePlugin->>Neovim: Run terminal command in buffer
    end
Loading

Poem

A window floats, a rabbit grins,
Configured wide or tall as whims.
Borders rounded, shadows cast,
Claude Code’s terminal floats at last!
With tests and docs all up to date,
This bunny’s coding feels first-rate.
🐇✨

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (9)
doc/claude-code.txt (1)

94-108: Clarify the documented position values

position = "vertical" is accepted by the implementation, but many Neovim users are accustomed to "vsplit" (which is also supported by create_split). It may be worth mentioning both aliases to avoid confusion when users copy the example verbatim.

Additionally, consider noting that "botright vsplit" / "topleft split" may be provided verbatim when finer-grained control is desired, because create_split() passes through any string containing “split”.

tests/spec/config_validation_spec.lua (3)

35-43: Test only the reverted field, not the full fallback

The assertion verifies that window.position reverts to its default when the float table is invalid – good.
To protect against future regressions, also assert that window.float has been reset (or at least that it is a table). Otherwise, an implementation could return { position = default, float = 'invalid' } and still pass.


45-56: Duplicate logic across similar tests

These three tests differ only by the field under test (width, relative, border) but each rewrites the entire test body. Consider using a parameterised helper such as:

local function expect_invalid_float(field, value)
  local cfg = vim.deepcopy(config.default_config)
  cfg.window.position = 'float'
  cfg.window.float[field] = value
  local result = config.parse_config(cfg, true)
  assert.are.equal(config.default_config.window.position, result.window.position)
end

This reduces duplication and makes it trivial to add more invalid-field checks later.


71-83: Border validation could also assert revert of border

Same remark as earlier: ensure the invalid border does not “bleed through” into the parsed config.

tests/spec/terminal_spec.lua (2)

350-372: Assertion misses window style guarantees

You already assert relative and border. To fully vet the float config, also assert calculated width, height, row, col for the numeric case so that later refactors can’t silently change the layout.


391-393: Hard-coded expected sizes risk drift

If default editor size constants change (e.g., you decide to mock 160×50 later), these magic numbers will break. Consider:

assert.are.equal(math.floor(120 * 0.8), nvim_open_win_config.width)
assert.are.equal(math.floor(40  * 0.5), nvim_open_win_config.height)

This makes intent explicit and independent of the stubbed values.

lua/claude-code/terminal.lua (3)

38-45: Clamp computed positions inside the screen

row/col values can currently exceed the screen bounds (e.g., 110%).
Consider math.max(0, math.min(pos, max_value - window_size)) to keep the float visible.


90-96: create_split mutates state before size calculation

is_vertical looks for substrings 'vsplit' or 'vertical'. If the user passes 'botright vsplit', the subsequent position .. ' split' duplication produces 'botright vsplit split'.
Simplify: detect if position already contains 'split' (any flavour) and otherwise prepend 'split'/vsplit.


215-242: Duplicated git-root command construction

Both floating & split paths build nearly identical pushd … && … && popd strings. Extract into a small helper to avoid divergence and ease unit testing.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between a021225 and b2e2df2.

📒 Files selected for processing (8)
  • CHANGELOG.md (1 hunks)
  • README.md (3 hunks)
  • doc/claude-code.txt (1 hunks)
  • lua/claude-code/config.lua (4 hunks)
  • lua/claude-code/terminal.lua (2 hunks)
  • tests/spec/config_spec.lua (1 hunks)
  • tests/spec/config_validation_spec.lua (1 hunks)
  • tests/spec/terminal_spec.lua (1 hunks)
🔇 Additional comments (14)
lua/claude-code/config.lua (5)

12-12: LGTM! Clear documentation update.

The documentation for the window position has been correctly updated to include the new "float" option, which aligns with the implementation.


17-23: Comprehensive documentation for float parameters.

All the floating window configuration options are well-documented with clear parameter descriptions. This provides good guidance for users who want to customize their floating windows.


77-85: Well-structured default floating window configuration.

The default configuration for floating windows is comprehensive and sensible:

  • 80% width and height provides good visibility without taking the entire screen
  • Center positioning is intuitive for most users
  • The rounded border style is aesthetically pleasing and consistent with modern UI practices

162-225: Thorough validation for floating window configuration.

The validation logic is comprehensive and robust:

  • Conditionally validating float configuration only when position is "float"
  • Proper handling of both numeric and percentage dimensions
  • Validation of relative positioning options
  • Comprehensive border style validation
  • Flexible row/col validation supporting numbers, "center", and percentage strings

342-345: Good fallback to default float configuration.

This logic ensures that when a user sets position = "float" but doesn't provide float-specific configuration, the plugin will use sensible defaults rather than failing. This improves the user experience by making the feature work out-of-the-box with minimal configuration.

CHANGELOG.md (1)

12-13: Clear and concise changelog entries.

The changelog entries effectively communicate the new floating window feature and its configuration options. This is placed correctly in the "Unreleased" section, following the Keep a Changelog format.

README.md (3)

36-36: Updated features list to include floating windows.

The features list now correctly mentions the support for floating windows, which is consistent with the implementation.


100-114: Comprehensive configuration documentation.

The default configuration example has been updated to include the floating window options with clear comments explaining each setting. This provides users with a complete reference of all available options.


212-230: Excellent floating window example.

The dedicated example for floating window configuration is clear and illustrative. It shows users exactly how to customize the floating window with practical values and explains what each setting does. This will help users quickly adopt this new feature.

tests/spec/config_spec.lua (4)

57-77: Good test coverage for basic float configuration.

This test thoroughly verifies that the config parser correctly handles the floating window configuration with numeric dimensions. It checks all essential properties including width, height, relative positioning, and border style.


79-95: Proper testing of percentage-based dimensions.

This test verifies that percentage strings (e.g., "80%") are correctly parsed and preserved in the configuration. This is important since percentage-based sizing is a key feature for responsive floating windows.


97-115: Validates center positioning functionality.

The test confirms that 'center' values for row and column positioning are correctly handled by the configuration parser, which is essential for the centered floating window use case.


117-135: Verifies default float configuration behavior.

This test confirms that when a user specifies position = "float" without providing explicit float configuration, the system correctly applies the default floating window settings. This ensures the feature works out-of-the-box with minimal configuration.

tests/spec/terminal_spec.lua (1)

418-438: nvim_create_buf_called flag may mis-report

nvim_create_buf_called is set only when the mock nvim_create_buf is executed.
If terminal.toggle re-enters the early-return path before reaching the mock (e.g., because buffer validation changes), the test would pass even though no window opened. To ensure correctness:

  1. Assert that nvim_open_win_called == true (already done); and
  2. Assert that nvim_create_buf_called == false after first ensuring the code path should indeed reuse the buffer.

This guards against future logic regressions.

Preserve existing vim.o fields when setting columns and lines in tests
instead of replacing the entire table. This prevents potential issues
where tests might fail if code under test accesses other vim.o fields.

Addresses CodeRabbitAI feedback on floating window PR
- Add explicit assertions for window dimensions and position
- Use dynamic calculations instead of hardcoded expected values
- Add test case for out-of-bounds dimensions to ensure graceful handling
- Makes tests more maintainable and comprehensive

Addresses CodeRabbitAI feedback on floating window PR
- Extract git root command building into helper function
- Add proper shell escaping using vim.fn.shellescape()
- Prevents errors when git root contains spaces
- Eliminates code duplication between float and split paths

Addresses CodeRabbitAI feedback on floating window PR
- Add nil check for dimensions with 80% default fallback
- Clamp calculated positions to ensure window stays visible on screen
- Prevents floating windows from appearing partially off-screen
- Makes configuration more forgiving with sensible defaults

Addresses CodeRabbitAI feedback on floating window PR
- Use appropriate split command (vsplit/split) based on direction
- Avoid appending 'split' to commands that already contain it
- Update comment to accurately reflect the logic
- Prevents 'vsplit split' duplication issue

Addresses CodeRabbitAI feedback on floating window PR
- Validate buftype is 'terminal' before reusing buffer in create_float
- Check terminal job is still running in toggle function
- Reset stored buffer reference when buffer is no longer valid
- Prevents errors from reusing invalid or non-terminal buffers
- Uses pcall for safe access to terminal_job_id variable

Addresses CodeRabbitAI feedback on floating window PR
- Add proper mocks for nvim_buf_get_option and nvim_buf_get_var
- Mock jobwait to simulate running terminal jobs
- Update git root test to expect shell-escaped paths
- Make nvim_buf_is_valid check for positive buffer IDs
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
lua/claude-code/terminal.lua (2)

257-258: Consider applying the same command validation in the split window path

Similar to the floating window path, consider validating the terminal command here as well.


53-95: 🛠️ Refactor suggestion

Enhancement needed for terminal job verification in buffer reuse

While the function validates that an existing buffer is still a terminal by checking its buftype, it doesn't verify if the terminal job is still running. This could lead to displaying a terminated terminal.

  -- Validate existing buffer is still a terminal
  local buftype = vim.api.nvim_buf_get_option(bufnr, 'buftype')
- if buftype ~= 'terminal' then
+  local terminal_job_id = nil
+  pcall(function()
+    terminal_job_id = vim.api.nvim_buf_get_var(bufnr, 'terminal_job_id')
+  end)
+  local is_valid_terminal = buftype == 'terminal' and terminal_job_id and vim.fn.jobwait({terminal_job_id}, 0)[1] == -1
+  if not is_valid_terminal then
    -- Buffer exists but is no longer a terminal, create a new one
    bufnr = vim.api.nvim_create_buf(false, true) -- unlisted, scratch
  end
🧹 Nitpick comments (3)
lua/claude-code/terminal.lua (3)

216-251: Consider extracting common window configuration logic to reduce duplication

The floating window and split window paths have duplicated logic for setting window options like hiding numbers and signcolumn. Consider extracting this into a helper function.

+ --- Configure common window options
+ --- @param win_id number Window ID to configure
+ --- @param config table Plugin configuration
+ --- @private
+ local function configure_window_options(win_id, config)
+   if config.window.hide_numbers then
+     vim.api.nvim_win_set_option(win_id, 'number', false)
+     vim.api.nvim_win_set_option(win_id, 'relativenumber', false)
+   end
+   
+   if config.window.hide_signcolumn then
+     vim.api.nvim_win_set_option(win_id, 'signcolumn', 'no')
+   end
+ end

  -- In the floating window path:
- if config.window.hide_numbers then
-   vim.api.nvim_win_set_option(win_id, 'number', false)
-   vim.api.nvim_win_set_option(win_id, 'relativenumber', false)
- end
- 
- if config.window.hide_signcolumn then
-   vim.api.nvim_win_set_option(win_id, 'signcolumn', 'no')
- end
+ configure_window_options(win_id, config)

  -- And similarly in the split window path

252-269: Consider using API calls consistently instead of mixing vim.cmd and API functions

The split window path uses vim.cmd while the floating window path uses direct API calls. Consider using API calls consistently for better maintainability.

- vim.cmd 'setlocal nonumber norelativenumber'
+ vim.api.nvim_win_set_option(0, 'number', false)
+ vim.api.nvim_win_set_option(0, 'relativenumber', false)

- vim.cmd 'setlocal signcolumn=no'
+ vim.api.nvim_win_set_option(0, 'signcolumn', 'no')

229-231: Consider validating that the terminal command is not empty

The code runs termopen directly with the command without validating it. Consider adding a check to ensure the command is not empty to avoid potential errors.

- vim.fn.termopen(cmd)
+ if cmd and cmd ~= "" then
+   vim.fn.termopen(cmd)
+ else
+   vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {"Error: No command specified"})
+ end
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between b2e2df2 and 6d88958.

📒 Files selected for processing (2)
  • lua/claude-code/terminal.lua (3 hunks)
  • tests/spec/terminal_spec.lua (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/spec/terminal_spec.lua
🔇 Additional comments (5)
lua/claude-code/terminal.lua (5)

18-31: Well-implemented dimension calculation with safe defaults

The calculation functions provide a robust implementation to handle different input formats (numbers, percentages) with appropriate defaults. The nil-safe default is especially helpful.


33-51: Good position calculation with window edge handling

The position calculation correctly handles "center" alignment and percentage-based positions, with proper clamping to keep windows within editor boundaries. This prevents windows from being positioned offscreen.


97-112: Good implementation of git root directory handling with proper path escaping

The function properly quotes git paths with shellescape to handle spaces in paths, fixing the issue mentioned in previous reviews.


120-136: Clean implementation for handling both floating and split windows

The function has a clear separation of concerns between floating and split windows, and correctly preserves the existing split behavior while adding float support.


180-194: Proper validation of terminal buffer status

The code correctly validates if the buffer is still a valid terminal and resets it if necessary, which prevents issues with stale or invalid terminal buffers.

@greggh
Copy link
Owner

greggh commented May 19, 2025

Hi @krishna-bala, thanks for the awesome PR. Any chance you have time to do those last few suggestions from coderabbit? i would actually just accept this and do them myself later, but I won't have time for a few days.

@krishna-bala
Copy link
Author

Sure! Might take a few days as well but should be able to get to it soon.

@krishna-bala
Copy link
Author

Closing this PR as it's a bit stale - I'll take another stab at it soon if still relevant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants