# AGENTS.md Instructions for AI coding agents working on this repository. Read this file before changing code. The rules below are architectural requirements, not suggestions. ## Project shape | Path | Responsibility | | --- | --- | | `src/api.rs` | `AppApi` / `PanelApi` — the single control surface of the application | | `src/script.rs` | Lua engine; publishes `app`, `include`, and `require` | | `src/keymap.rs` | Key binding parsing (`KeyBinding`) and dispatch (`KeyMap`) | | `src/app.rs` | GPUI view, rendering, event wiring | | `src/editor.rs` | `Workspace`, documents, tabs | | `src/panel.rs` | `PanelState` for left/right/bottom panels | | `src/lsp.rs` | LSP server manager | | `src/constants.rs` | CLI arguments and script directory | | `init.lua` | Example startup script (also a usage reference) | | `DOCS.md` | User-facing Lua API documentation | ## 1. `AppApi` is the center of the application - `AppApi` (in `src/api.rs`) is the one object through which the application is controlled. All operations on documents, tabs, panels, the window, syntax highlighting, key bindings, and LSP go through it. - Shared state is held as `Rc>` handles (workspace, keymap, panels, LSP manager). The UI and `AppApi` must observe the *same* instances; never create a second copy of the state. - Rust code that needs to act on the application should call `AppApi::global()` (or an `AppApi` instance passed in) instead of mutating `Workspace`, `PanelState`, `KeyMap`, or `LspManager` directly. - Methods that are implementation details of `AppApi` stay private or are marked `#[lua(skip)]`. Do not add a second, parallel control path. ## 2. Every `AppApi` capability is available from Lua - Everything in the `impl AppApi` block annotated with `#[mlua::userdata_impl]` is automatically exposed as `app:method(...)`. - When adding functionality, add it to `AppApi` and expose it to Lua at the same time. An operation that a user can trigger in the UI must also be callable from a Lua script. - Use `#[lua(infallible)]` where the method cannot fail, and return `mlua::Result` / convert errors to `mlua::Error::RuntimeError` otherwise. - Generic Rust methods are not exported. Add a thin non-generic Lua wrapper, following the existing pattern: `save` / `lua_save`, `open` / `lua_open`, `set_active_text` / `lua_set_active_text`, `set_language` / `lua_set_language`. - Use `#[lua(name = "...")]` when the Lua name should differ from the Rust name (for example `active_tab_index` → `active_tab`). - Lua API naming: lower `snake_case`, tabs and indices are zero-based, panels are accessed as `app.left_panel`, `app.right_panel`, `app.bottom_panel`. ## 3. All UI actions and keyboard shortcuts go through the API ### UI actions - UI event handlers and rendered controls must invoke `AppApi` methods, not manipulate state directly. Examples: - window close button → `app:close()` (i.e. `AppApi::close`) - minimize button → `app:minimize()` - maximize / restore → `app:toggle_maximize()` or `app:set_maximized(bool)`; read the current state with `app:is_maximized()` - tab close button → `app:close_tab(index)` - "new tab" button → `app:open_tab()` - panel toggles → `app.right_panel:toggle()` and friends - Rendering code may *read* state (for example `workspace.borrow()` to draw the active document), but must not mutate it. Mutations belong to `AppApi`. - Window operations are queued as `WindowCommand`s and applied by `apply_window_commands`; keep this mechanism as the only way the UI and Lua request window changes. - If an external component (for example `gpui_kit::component::TitleBar`) performs an action internally, do not leave it as a separate control path: intercept the action or replace the component so that it calls `AppApi`. ### Keyboard shortcuts - Every key binding must be registered through `AppApi::map_key` / `KeyMap`. There must be no hard-coded shortcuts that scripts cannot inspect, override, or remove. - Existing hard-coded checks such as `is_save_shortcut` and `is_completion_shortcut` in `src/app.rs` are technical debt: when touching this area, move those defaults into the `KeyMap` mechanism so they can be rebound from Lua. - Default bindings should be installed through the same mechanism that scripts use (or a documented equivalent), so that `app:map_key(...)` can replace them. Rebinding the same chord must replace the previous handler. - Dispatch order matters: the key interceptor runs before key bindings, and `dispatch_keymap` is used for both key down and key up (`<...>` vs `!key`). Preserve this behavior. - Any `KeyMap` change must keep the documented chord syntax working: `a`, `1`, `!a`, ``, ``, ``, named keys (case-insensitive). - Editor text input itself is not a shortcut, but all app-level commands (save, close, panels, completion popup navigation, etc.) must be remappable. ## 4. Keep `DOCS.md` in sync `DOCS.md` is the source of truth for the Lua API. In the **same change** that modifies the public surface, update the docs: - added/removed/renamed `AppApi` or `PanelApi` methods; - changed arguments, return values, or error behavior; - new supported languages, LSP configuration fields, CLI flags, or `include`/`require` behavior; - changed default shortcuts or their syntax. Also update the usage comments in `init.lua` when they describe behavior that changed. Documentation must describe the code as it actually behaves — no aspirational APIs. ## Conventions - Keep changes focused and consistent with the existing style; comments and user-facing documentation are written in Russian. - Rust API docs use `///`; write them for every new public method. - Validate with `cargo fmt` and `cargo test`, then `cargo build` if behavior changed. Do not commit unless explicitly asked. ## Checklist Before finishing a change, verify: - [ ] The new behavior is implemented in `AppApi`, not in a UI handler. - [ ] The operation is callable from Lua and has a documented signature. - [ ] No new hard-coded keyboard shortcut bypasses `KeyMap`. - [ ] UI controls call the corresponding `AppApi` method. - [ ] `DOCS.md` (and `init.lua` comments if needed) are updated. - [ ] `cargo fmt` and `cargo test` pass (or explain why they could not run).