# Cursor inline edit: tips 46–60

URL: https://developertoolkit.ai/en/cursor-ide/build/inline-edit/
Description: Scope Cursor's inline edit with the selection, write instructions that land first time, chain small edits, and prove each one with types and tests.

Cursor's inline edit rewrites the selected code from a short natural-language instruction, or inserts new code at the cursor when nothing is selected. The selection sets the boundary of the change, so its scope decides the result as much as the wording does. Inline edit suits changes inside one file that a type check or a focused test can confirm.

This page is for **developers** who already use Cursor's agent and Tab but get coin-flip results from inline edit. You select a 60-line function, type "make this better", and get back a rewrite that renamed two variables, dropped a guard clause and added an import you never use. Then you read the whole diff line by line, which is slower than typing the change yourself. The fix is a tighter selection, an instruction that names one transformation and its limits, and a gate that checks the result for you.

**What this page covers**

The Cursor 3.x line, checked on 2026-09-26. Cursor's help lists Cmd+K for inline edit (checked 2026-10-12). Inline edit is usually bound to `Cmd+K` (macOS) or `Ctrl+K` (Windows, Linux), but shortcuts move between builds: search for "inline edit" in the Keyboard Shortcuts editor to find yours ([Cursor keyboard shortcuts](/en/cursor-ide/reference/keyboard-shortcuts/)).

## What you'll walk away with from inline edit

- A selection table that maps what you select to what inline edit may change and to the check that ends the edit.
- Seven copy-paste inline edit instructions: generate a helper, match a pattern in the file, convert a structure, add error handling, convert JavaScript to strict TypeScript, extract magic values, and write tests.
- A chaining routine for refactors: pin behaviour with a test, make one transformation per edit, and run a gate between edits.
- A four-signal check that confirms an inline edit without reading every line, and a table for when to switch to the agent.

## How does the selection shape an inline edit?

### Tip 46: Match the selection to the change you want

Inline edit treats the selection as its boundary: it rewrites what you selected and leaves the rest of the file alone. A selection that is larger than the change invites edits you did not ask for. A selection that is smaller than the change produces code that no longer fits its surroundings.

| What you select | What inline edit changes | Good for | The check that ends it |
| --- | --- | --- | --- |
| Nothing (cursor on an empty line) | Inserts new code at the cursor | A helper, a type, a small block described in a short instruction | Type check, then a test that calls it |
| One or two lines | Those lines | A condition, a return value, a single expression | Your own read of the lines, then the type checker |
| One whole function | The function body, and the signature only if you allow it | Error handling, a structural conversion, input validation | The function's unit tests |
| Several related functions | All of them as one unit | The same change applied consistently, such as adding logging to three handlers | Tests for each function, plus `git diff --stat` showing one file |

Select the smallest block that contains every line that must change. To change two lines in a 60-line function, select those two lines, not the function.

### Tip 47: Put the context in the selection or in the instruction

In practice inline edit works from the selection and the file around it. It does not reliably open a type or helper that lives in another file, so it guesses the shape and the guess compiles only by luck.

When the type sits in the same file, extend the selection upward to include it:

```typescript
// Select both the type and the function, then run inline edit.
type UserRole = 'admin' | 'editor' | 'viewer';

function getPermissions(role: UserRole): Permission[] {
  // ...
}
```

When the type lives elsewhere, state its shape in the instruction: "`User` has `id: string`, `email: string` and `role: UserRole`." If the change needs more than two such facts, the change spans files, and [Tip 60](#tip-60-switch-to-the-agent-when-the-change-leaves-the-selection) applies.

### Tip 48: Generate at an empty line for small, self-contained code

With the cursor on an empty line and nothing selected, inline edit writes new code at that position. It is lighter than opening the agent for a block you can describe in a short instruction. Treat inline edit as writing only at the cursor or inside the selection; creating files, editing other files and running commands are the agent's job.

**Copy-paste inline edit instruction: generate a typed helper**

```text
Write a generic TypeScript debounce(fn, waitMs) that returns a function with the same
parameter types as fn plus a cancel() method. A new call cancels the pending one.
Preserve `this` and the arguments of the last call. No external dependencies. Export
it.
```

Then write, or ask the agent to write, a test with fake timers that proves a second call within `waitMs` cancels the first. A generated helper with no test is unverified code.

## How do you write an inline edit instruction that lands the first time?

### Tip 49: Name one transformation and what must not change

Name the transformation, then name the invariants. The invariants stop the rewrite from spreading beyond the change you asked for.

| Vague | Specific |
| --- | --- |
| "Make this better" | "Return early for null, undefined and the empty string, throwing a `TypeError` that names the parameter. Do not change the success path." |
| "Optimise" | "Replace the nested loops with a `Set` lookup so the function is linear in the input size. Keep the output order unchanged." |
| "Fix the types" | "Add explicit types to every parameter and the return value. No `any`. Keep the function signature's parameter order." |

A useful default ending for any instruction is "Do not change the signature, the return type or any line outside the selection."

### Tip 50: Point at a pattern that already exists in the file

When the same file already shows the pattern you want, name it. Inline edit can read the rest of the file, so a named example beats a paragraph describing the pattern.

**Copy-paste inline edit instruction: copy a pattern from the same file**

```text
Add error handling to this handler that matches handleUserCreation above: the same
try/catch structure, the same logger.error call shape with the handler name and
request ID, and the same mapping from ValidationError to 400 and NotFoundError to 404.
Do not change the success path or the function signature.
```

If the pattern lives in another file, copy a short example of it into the instruction. A [project rule](/en/cursor-ide/quick-start/project-rules/) does not reach inline edit: Cursor's help says rules apply to Agent (Chat) only, not to Tab completion, Inline Edit or Bugbot PR reviews (checked 2026-10-12).

### Tip 51: Use "convert X to Y" for structural changes

A conversion gives inline edit a clear before and after, so it is the most dependable shape of instruction: callbacks to `async`/`await`, a `switch` to a lookup object, a class component to a function component with hooks, or a loop to `map` and `filter`. Always add the behaviour that must survive the conversion.

**Copy-paste inline edit instruction: convert callbacks to async/await**

```text
Convert this callback-based function to async/await. Keep the exported name and
every parameter except the trailing callback, which you remove; return a Promise
instead. Every error the callback version passed as its first argument must now
reject with the same Error object. Keep the retry loop and its three-attempt limit exactly as it is.
```

After the conversion, update the callers. They sit outside the selection, so this is the point where the agent, not inline edit, takes over.

## How do you chain inline edits through a refactor?

### Tip 52: Pin the behaviour with a test before the first edit

A chain of rewrites is only safe when something tells you behaviour did not move. For code without tests, ask the agent for characterization tests that record what the function does today, including the odd cases, before you touch it. [Characterization tests](/en/workflows/test/characterization-tests/) covers the technique in depth.

**Copy-paste agent prompt: pin behaviour before a refactor**

```text
Before I refactor calculateInvoiceTotal in src/billing/invoice.ts, write
characterization tests in src/billing/invoice.characterization.test.ts that record
what it returns today: an empty cart, one line, a discount above the subtotal, a
negative quantity, and a currency with no minor unit. Assert the current outputs even
where they look wrong, and mark those cases with a comment. Do not change invoice.ts.
Run npx vitest run src/billing/ and show me the passing output.
```

These tests are your oracle for the whole chain. If an edit turns them red, the edit changed behaviour. Reject it rather than edit the test.

### Tip 53: Make one transformation per edit, with a gate between edits

A single instruction to "add types, validation and error handling" produces one large diff that you can only check by reading it. Three edits produce three small diffs, each checked by a command.

1. Select the function and run inline edit: "Add explicit types to every parameter and the return value. No `any`."
2. Gate: `npx tsc --noEmit`. Accept only when it exits 0.
3. Run inline edit again: "Validate each parameter against the types above and throw a `TypeError` naming the parameter."
4. Gate: `npx vitest run src/billing/`. The characterization tests must still pass unchanged.
5. Run inline edit again: "Wrap the database call in try/catch and rethrow as `AppError`, preserving the cause."
6. Gate: the same test run, plus `npx eslint src/billing/invoice.ts`.
7. Commit. Each commit is then a checkpoint you can return to with Git, in addition to [Cursor's own checkpoints](/en/cursor-ide/build/checkpoints-branching/).

Order the chain so no step undoes an earlier one. "Add logging" followed by "remove all side effects" leaves incoherent code.

### Tip 54: Finish the agent's output with inline edit

The agent drafts a module quickly and misses house conventions in a few places. Fixing those places with inline edit is cheaper than another agent round-trip, because each fix is local and the rest of the draft stays put.

1. Let the agent finish, and let its own gate (tests and type check) pass first.
2. Select one function and run inline edit, for example: "Rename boolean parameters to start with `is` or `has`, and update their uses inside this function only."
3. Select a `throw new Error(...)` block and run inline edit: "Throw `AppError` from `@/lib/errors` with the same message and a `code` field."
4. Run the same gate the agent ran. A convention fix that breaks a test was not a convention fix.

## Which transformations suit inline edit best?

### Tip 55: Add error handling to happy-path code

**Copy-paste inline edit instruction: add error handling**

```text
Add error handling to this function. Catch only errors from the fetch call and the
JSON parse. Retry a network error once after 1 second. Throw a validation error
immediately with a message naming the bad field. Log unexpected errors with the
function name and rethrow them. Never swallow an error. Do not change the signature.
```

Check it with a test that makes the fetch fail once and then succeed, and a test that returns malformed JSON.

### Tip 56: Convert JavaScript to strict TypeScript

**Copy-paste inline edit instruction: JavaScript to strict TypeScript**

```text
Convert this code to TypeScript that passes strict mode. No `any`; use `unknown` and
narrow it where the input is untrusted. Declare an interface for every object
parameter and return value. Mark optional properties with `?`. Reuse types already
declared in this file instead of inventing new ones.
```

The gate is `npx tsc --noEmit` with `strict` on. A conversion that needs a new `// @ts-expect-error` hides a type problem rather than solving it; reject it.

### Tip 57: Extract magic numbers and strings

**Copy-paste inline edit instruction: extract magic values**

```text
Extract every magic number and string literal in the selection into named constants
declared directly above it, in SCREAMING_SNAKE_CASE. Group related constants. Add a
one-line comment only where the name does not explain the value. Behaviour must not
change: every constant keeps its exact current value.
```

This edit changes no behaviour, so the existing tests must pass with no edits to the tests.

### Tip 58: Write tests from the implementation, then prove they can fail

Inline edit only writes where the cursor is, so run it at an empty line in the test file, not in the source file, and paste the function's signature into the instruction so it knows what it is testing. Tests written from the implementation tend to assert what the code does, bugs included, so a passing run proves little until you see the tests fail.

**Copy-paste inline edit instruction: generate tests**

```text
Write Vitest tests for this function: <paste the signature, for example
export function parsePrice(input: string): number | null>. Cover the typical
case, the empty input, zero and boundary values, null or undefined arguments, and, if it is async, both the resolve
and the reject path. Name each test after the scenario, not the function. Assert
specific values, never toBeDefined() or toBeTruthy() alone.
```

Accept the tests, then break the function on purpose, for example by flipping a `<` to `<=`, and run the tests. At least one test must fail. Undo the break. A suite that stays green is decoration. [Test-driven development in Cursor](/en/cursor-ide/test/testing-tdd/) covers writing the tests first instead.

## How do you check an inline edit without reading every line?

### Tip 59: Use a four-signal check before you accept

Inline edit proposes its change as a diff you accept or reject. Read the diff for its shape, not line by line, and let commands judge its content:

1. **Scope.** The changed lines sit inside your selection. After you accept, `git diff --stat` lists one file with roughly the line count you expected (commit or stash first, or compare against the last commit, so the stat shows only this edit). Anything else means the selection or the instruction was too loose.
2. **Removals and imports.** Scan only the red lines and any new `import`. A dropped guard clause or an unused import shows up here in seconds.
3. **Types and lint.** `npx tsc --noEmit` and `npx eslint <file>` exit 0. Keep the type checker in watch mode with `npx tsc --noEmit --watch` so this signal arrives before you finish reading.
4. **Behaviour.** The tests for that module pass, and for a refactor, the characterization tests from [Tip 52](#tip-52-pin-the-behaviour-with-a-test-before-the-first-edit) pass unchanged.

You sign off the edit when all four are green and you commit it. The pull request adds a second gate: CI runs the full suite, and a reviewer or a review bot sees the combined diff. [Reading evidence instead of code](/en/ladder/evidence-not-diffs/) explains why this order of checks scales further than reading each diff.

**Stateful code needs a runtime check**

An edit to React hooks, closures or code with shared mutable state can pass the type check and still change behaviour at runtime. For those, run the component or integration tests before you move to the next edit, not after the chain.

### Tip 60: Switch to the agent when the change leaves the selection

| Signal | Stay with inline edit | Switch to the agent |
| --- | --- | --- |
| Files touched | One, inside a selection you can make | Two or more, or callers need updating |
| Context needed | In the selection, the same file, or two facts in the instruction | Types and helpers across the codebase |
| Proof | You run one command after accepting | The change needs a write, test and fix loop |
| Instruction length | One transformation plus its invariants, a short paragraph at most | Several transformations, or it reads like a plan |

When you switch, carry the same discipline into the prompt: the tests that define done and the commands that gate the result. [Agent chat workflows in Cursor](/en/cursor-ide/build/agent-chat/) shows that prompt shape, and [agent modes in Cursor](/en/cursor-ide/build/agent-modes-deep-dive/) covers when to plan first.

## When Cursor inline edits go wrong

| Symptom | Likely cause | Recovery |
| --- | --- | --- |
| The rewrite changed more than you asked for | The selection was larger than the change, or the instruction had no invariants | Reject, select only the lines that must change, and add "Do not change anything else" |
| The code uses a type or field that does not exist | The type lives in another file | Reject, state the type's shape in the instruction, or use the agent |
| The diff looks right but a test now fails | A behaviour change hidden in a refactor, often a reordered condition or a removed early return | Undo with the editor's Undo command or `git restore <file>`, then split the edit into smaller steps |
| Generated tests all pass, even against broken code | The tests restate the implementation | Break the code on purpose; rewrite any test that stays green with specific expected values |
| Each edit in a chain undoes the last one | Contradictory steps | Revert to the last green commit and reorder the chain so every step builds on the previous one |
| The same convention needs fixing in every edit | The convention exists only in your head, or in a rule that inline edit does not read | Write it into a [project rule](/en/cursor-ide/quick-start/project-rules/) for the agent, and paste a short example of it into each inline edit instruction (Cursor's help says rules do not apply to Inline Edit, checked 2026-10-12) |

If you already accepted several edits and the tests are red, do not ask for a fix on top. Return to the last commit where the gate passed and repeat the chain in smaller steps.

## Where to go next after inline edit

- Before this page: [Cursor core features, tips 16–30](/en/cursor-ide/build/core-features/), and [Cursor Tab autocomplete, tips 31–45](/en/cursor-ide/build/tab-autocomplete/), which hands a Tab scaffold to inline edit.
- Next: [agent chat workflows in Cursor](/en/cursor-ide/build/agent-chat/), for changes that span files and need a write, test and fix loop.
- For larger restructurings: [refactoring strategies in Cursor](/en/cursor-ide/build/refactoring-strategies/), and reusable instructions in [Cursor prompt templates](/en/cursor-ide/build/prompt-templates/).
- If you also run Claude Code beside Cursor: [IDE and CLI coordination](/en/claude-code/build/ide-coordination/) shows how to keep inline edits and the CLI from writing the same file.
