|
| 1 | +# Concepts |
| 2 | + |
| 3 | +Understanding a few core concepts makes the rest of FineCode straightforward. |
| 4 | + |
| 5 | +## Action |
| 6 | + |
| 7 | +An **Action** is a named operation — `lint`, `format`, `build_artifact`, etc. It is defined as a Python class that declares the types of its payload, execution context, and result: |
| 8 | + |
| 9 | +```python |
| 10 | +class LintAction(code_action.Action[LintRunPayload, LintRunContext, LintRunResult]): |
| 11 | + PAYLOAD_TYPE = LintRunPayload |
| 12 | + RUN_CONTEXT_TYPE = LintRunContext |
| 13 | + RESULT_TYPE = LintRunResult |
| 14 | +``` |
| 15 | + |
| 16 | +Actions are identified by their **import path** (e.g. `finecode_extension_api.actions.lint.LintAction`), not by the name used in config. The config name is just a human-readable alias. |
| 17 | + |
| 18 | +Actions can be called from: |
| 19 | + |
| 20 | +- the CLI (`python -m finecode run lint`) |
| 21 | +- the IDE via the LSP server (diagnostics, code actions, formatting) |
| 22 | +- other handlers |
| 23 | + |
| 24 | +## ActionHandler |
| 25 | + |
| 26 | +An **ActionHandler** is a concrete implementation of an action. Multiple handlers can be registered for a single action. For example, the `lint` action might have handlers for ruff, flake8, and mypy — each independently checking the code. |
| 27 | + |
| 28 | +Each handler: |
| 29 | + |
| 30 | +- specifies which **virtual environment** (`env`) it runs in |
| 31 | +- declares its own **dependencies** (installed automatically by `prepare-envs`) |
| 32 | +- receives the action payload and returns a result |
| 33 | + |
| 34 | +### Sequential vs. concurrent execution |
| 35 | + |
| 36 | +Handlers for an action run either **sequentially** (default) or **concurrently**. |
| 37 | + |
| 38 | +```mermaid |
| 39 | +flowchart LR |
| 40 | + subgraph Sequential |
| 41 | + direction TB |
| 42 | + P1[payload] --> H1[handler 1] |
| 43 | + H1 -->|current_result| H2[handler 2] |
| 44 | + H2 -->|current_result| H3[handler 3] |
| 45 | + H3 --> R1[result] |
| 46 | + end |
| 47 | +
|
| 48 | + subgraph Concurrent |
| 49 | + direction TB |
| 50 | + P2[payload] --> H4[handler 1] |
| 51 | + P2 --> H5[handler 2] |
| 52 | + P2 --> H6[handler 3] |
| 53 | + H4 --> M[merge] |
| 54 | + H5 --> M |
| 55 | + H6 --> M |
| 56 | + M --> R2[result] |
| 57 | + end |
| 58 | +``` |
| 59 | + |
| 60 | +**Sequential mode** (default): handlers run one after another. Each handler can read the accumulated result so far via `context.current_result`. Useful when handlers depend on each other's output (e.g. formatter → save-to-disk). |
| 61 | + |
| 62 | +**Concurrent mode** (`run_handlers_concurrently: true`): all handlers run in parallel and results are merged afterward. Accessing `context.current_result` in concurrent mode raises `RuntimeError`. Useful for independent linters. |
| 63 | + |
| 64 | +## Preset |
| 65 | + |
| 66 | +A **Preset** is a Python package that bundles action and handler declarations into a reusable, distributable configuration. Users install a preset as a `dev_workspace` dependency and reference it in `pyproject.toml`: |
| 67 | + |
| 68 | +```toml |
| 69 | +[tool.finecode] |
| 70 | +presets = [{ source = "fine_python_recommended" }] |
| 71 | +``` |
| 72 | + |
| 73 | +A preset contains a `preset.toml` file that declares which handlers to activate for which actions. The user's `pyproject.toml` configuration is merged on top of the preset, giving the user full control to override, extend, or disable individual handlers. |
| 74 | + |
| 75 | +### Handler modes |
| 76 | + |
| 77 | +When configuring an action in `pyproject.toml`, you can control how your configuration relates to preset handlers: |
| 78 | + |
| 79 | +- **Default (additive):** your handlers are added to the preset's handlers. |
| 80 | +- **`handlers_mode = "replace"`:** your handler list completely replaces the preset's handlers for that action. |
| 81 | +- **`disabled = true` on a handler entry:** disables that specific inherited handler. |
| 82 | + |
| 83 | +## Project |
| 84 | + |
| 85 | +A **Project** is any directory containing a `pyproject.toml` with a `[tool.finecode]` section. FineCode discovers all projects under the workspace root automatically. |
| 86 | + |
| 87 | +A project may belong to a **workspace** — a directory containing multiple projects. FineCode handles multi-project workspaces transparently: running `python -m finecode run lint` from the workspace root runs lint in all projects that define it. |
| 88 | + |
| 89 | +## Workspace Manager and Extension Runner |
| 90 | + |
| 91 | +FineCode has two runtime components: |
| 92 | + |
| 93 | +### Workspace Manager (WM) |
| 94 | + |
| 95 | +The `finecode` package. It: |
| 96 | + |
| 97 | +- discovers projects and resolves merged configuration |
| 98 | +- manages virtual environments (`prepare-envs`) |
| 99 | +- exposes an **LSP server** to the IDE |
| 100 | +- delegates action execution to Extension Runners |
| 101 | + |
| 102 | +### Extension Runner (ER) |
| 103 | + |
| 104 | +The `finecode_extension_runner` package. It: |
| 105 | + |
| 106 | +- runs inside an isolated virtual environment (e.g. `.venvs/dev_no_runtime`) |
| 107 | +- imports and executes handler code |
| 108 | +- communicates results back to the WM via LSP/JSON-RPC |
| 109 | + |
| 110 | +The WM/ER split means handler dependencies never pollute the workspace Python environment. |
| 111 | + |
| 112 | +## Environments |
| 113 | + |
| 114 | +Each handler declares an `env` (e.g. `dev_no_runtime`, `runtime`). FineCode creates a separate virtualenv for each env name it encounters, and installs the handler's declared `dependencies` into it. `prepare-envs` automates this. |
| 115 | + |
| 116 | +```toml |
| 117 | +# Example: handler in pyproject.toml |
| 118 | +[[tool.finecode.action.lint.handlers]] |
| 119 | +name = "ruff" |
| 120 | +source = "fine_python_ruff.RuffLintFilesHandler" |
| 121 | +env = "dev_no_runtime" |
| 122 | +dependencies = ["fine_python_ruff~=0.2.0"] |
| 123 | +``` |
| 124 | + |
| 125 | +The env name is arbitrary — it's just a label FineCode uses to group handlers that share a virtualenv. |
0 commit comments