# How LSP Workspace Operations Function in oh-my-pi: Rename, References, and Code Actions

> Discover how oh-my-pi's LSP workspace operations handle renames, references, and code actions via a central dispatcher. Learn about symbol queries and preview modes.

- Repository: [Can Bölük/oh-my-pi](https://github.com/can1357/oh-my-pi)
- Tags: deep-dive
- Published: 2026-05-21

---

**The oh-my-pi coding agent implements LSP workspace operations through a centralized dispatcher in [`packages/coding-agent/src/lsp/index.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/lsp/index.ts), enabling file rename detection, symbol reference queries, and batched code actions with optional preview modes.**

The oh-my-pi (omp) coding agent provides comprehensive Language Server Protocol (LSP) integration to handle complex workspace-wide transformations. Located in `packages/coding-agent/src/lsp/`, the LSP module manages everything from file renames to code actions by communicating with configured language servers. Understanding how omp handles these **LSP workspace operations** reveals sophisticated batching and error handling mechanisms that ensure consistent, safe code transformations.

## Handling File and Directory Renames via workspace/willRenameFiles

When you invoke the `rename_file` tool, omp validates source and destination paths, then calls **`enumerateRenamePairs()`** to construct the list of `{oldUri, newUri}` pairs that language servers expect for a **`workspace/willRenameFiles`** request. This function, found at [line 48 of [`index.ts`](https://github.com/can1357/oh-my-pi/blob/main/index.ts)](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/lsp/index.ts#L48), handles both single files and directories.

For directory renames, omp walks every regular file within the source tree and builds parallel URIs under the new root. To prevent performance degradation, the enumeration caps at **`MAX_RENAME_PAIRS = 1000`** files. For each configured LSP server, the agent obtains (or creates) a client via `getOrCreateClient()` and dispatches the `workspace/willRenameFiles` request. The resulting `WorkspaceEdit` objects from all servers are merged, de-duplicated, and stored in the tool-call metadata.

### Preview Mode vs. Direct Application

The rename operation supports a configurable `apply` parameter. When set to `false`, the rename enters **preview mode**: the merge step builds a human-readable description (`Rename preview: … → …`) and the file system remains untouched. When `apply` is omitted or truthy, omp executes `fs.promises.rename()` on the source path(s) only after the LSP edits have been applied, ensuring symbol references remain synchronized with the new file structure.

```typescript
// Rename a single file (preview only)
await runtime.tool("rename_file", {
  file: "src/old.ts",
  new_name: "src/new.ts",
  apply: false,
});

// Rename a directory and apply LSP edits
await runtime.tool("rename_file", {
  file: "src/components",
  new_name: "src/ui",
  apply: true,
});

```

## Querying Symbol References and Definitions

The core LSP dispatcher lives in **[`LspTool.handleAction`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/lsp/index.ts#L1900)**. For actions such as `"references"`, `"definition"`, `"type_definition"`, and `"implementation"`, the agent builds a request payload using the document URI and cursor position, then invokes the appropriate LSP method (`textDocument/references`, `textDocument/definition`, etc.).

Results are transformed into user-friendly text blocks via **`formatLocationWithContext()`** (lines 23-34 of the same file). If no results are returned, the tool emits "No references found" rather than empty output.

```typescript
// Query for all references of a symbol
await runtime.tool("references", {
  file: "src/util.ts",
  line: 12,
  col: 1,
});

```

## Executing and Resolving Code Actions

When requesting a **code action** (`action: "code_action"`), the dispatcher sends a **`textDocument/codeAction`** request (see [line 2107 of [`index.ts`](https://github.com/can1357/oh-my-pi/blob/main/index.ts)](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/lsp/index.ts#L2107)). The raw list of `CodeAction` objects may contain *unresolved* actions containing only a title and kind.

The agent resolves these on-demand by calling **`textDocument/codeAction/resolve`** (line 2142) for each unresolved action. Resolved actions convert into `WorkspaceEdit` objects, merge with any pending LSP edits, and apply to the workspace when `apply` is true.

```typescript
// Request a code-action at a cursor
await runtime.tool("code_action", {
  file: "src/app.ts",
  line: 42,
  col: 5,
  kind: "quickfix",
});

```

## Batching Strategies for Atomic Workflows

To maintain workspace consistency, omp places LSP-related tools (`edit`, `write`, `rename_file`, `code_action`) in the **`LSP_BATCH_TOOLS`** set defined in [[`render-utils.ts`](https://github.com/can1357/oh-my-pi/blob/main/render-utils.ts)](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/tools/render-utils.ts#L752). When streaming tool calls, the agent delays execution and keeps earlier LSP-tool calls in the batch until the last LSP-tool is observed.

This batching guarantees that inter-dependent edits—such as a rename that triggers a code action—see a consistent view of the workspace. All edits accumulate and merge into a single atomic operation.

## Error Handling and Abort Signals

If a language server does not implement a particular method, **`isMethodNotFoundError()`** (lines 78-87 of [`index.ts`](https://github.com/can1357/oh-my-pi/blob/main/index.ts)) filters the error and records a user-facing note rather than aborting the entire operation. All LSP calls wrap their promises with `throwIfAborted(signal)`, ensuring that user-initiated aborts (e.g., `Ctrl-C`) stop the batch safely without leaving the workspace in a partial state.

## Summary

- **Rename enumeration** uses `enumerateRenamePairs()` to build URI mappings, capped at 1000 pairs to prevent performance issues during directory renames.
- **LSP workspace operations** are coordinated through `LspTool.handleAction()`, supporting references, definitions, and implementation queries with contextual formatting.
- **Code actions** are resolved via `textDocument/codeAction/resolve` before converting to workspace edits, ensuring complete transformation data.
- **Request batching** via `LSP_BATCH_TOOLS` ensures atomic execution of inter-dependent LSP operations.
- **Graceful degradation** occurs when servers lack method support, with `isMethodNotFoundError()` preventing cascading failures.

## Frequently Asked Questions

### How does oh-my-pi handle renaming directories containing thousands of files?

The `enumerateRenamePairs()` function imposes a hard limit of **`MAX_RENAME_PAIRS = 1000`** files when walking directory trees. If a directory exceeds this limit, omp processes only the first 1000 regular files to prevent memory exhaustion and LSP request timeout issues, ensuring stable performance even on large codebases.

### Can I preview LSP workspace edits before applying them to disk?

Yes. The `rename_file` and `code_action` tools accept an `apply` parameter. Setting `apply: false` enters preview mode, where omp generates a human-readable description of the proposed changes without executing `fs.promises.rename()` or writing file modifications. This allows verification of symbol renames and refactorings before committing changes.

### How does oh-my-pi manage multiple simultaneous LSP tool calls?

omp utilizes the **`LSP_BATCH_TOOLS`** set defined in [`packages/coding-agent/src/tools/render-utils.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/tools/render-utils.ts) to identify LSP-related operations. The system buffers these calls until all LSP tools in the current batch are collected, then dispatches them together. This ensures that dependent operations—such as a file rename followed by a code action—operate on a consistent workspace state.

### What happens when a language server doesn't support workspace/willRenameFiles?

If a server returns a method-not-found error, **`isMethodNotFoundError()`** catches the exception (lines 78-87 of [`index.ts`](https://github.com/can1357/oh-my-pi/blob/main/index.ts)) and records a note in the tool output rather than failing the entire rename operation. The file system rename proceeds normally, though without automatic import updates from that specific server.