Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>RefactoryNew to Visual Studio Code? Get it now.
Refactory

Refactory

mark-ku

| (0) | Free
Convention-aware refactorings for C# and React/TypeScript — one keystroke, your project's conventions.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Refactory

Convention-aware refactorings for C# and React/TypeScript in VS Code.

Status: early but real. 23 actions, 187 tests (58 xunit for the C# engine, 108 for the TypeScript engine, 21 running inside an actual VS Code). Not on the Marketplace yet — build a VSIX with .\pack.ps1.

What this is — and what it deliberately is not

VS Code already ships extract-function, move-to-new-file, organize-imports and rename. JetBrains ships an official ReSharper extension for generic C# refactoring. Re-implementing any of that would produce a worse copy.

This extension does the three things nobody serves:

  1. Convention-aware generation — put the file where your repo actually puts it, with your copyright header, your XML docs, your this. qualification and your BOM, so the result passes your analyzers instead of flooding review.
  2. Solution-shaped multi-file operations — DI registration, interface ↔ implementation sync, controller version clones.
  3. React/Next semantics — 'use client' propagation, extract component and extract hook into real files, alias-aware imports.

Plus one Refactor This menu that lists the built-in refactorings alongside ours, so "we don't rebuild what exists" is visible rather than merely claimed.


One keystroke, everything applicable

Ctrl+Alt+Shift+T — unbound on every platform, so nothing of yours is stolen.

Refactor This menu

Ours are grouped under Refactory; VS Code's and the language servers' own actions stay right there under Built-in refactorings. The menu opens immediately and streams the slower ones in.

The same actions also nest correctly in the native refactor menu (Ctrl+.), in the groups they belong to — not in a parallel menu of their own:

Native refactor menu


React: extract a component, keep the conventions

Select JSX, extract. Props are inferred from what the selection captures, key moves to the call site, and the name is edited in place — type once and the interface, the function and the props type all update together:

Extract Component result

The new-file variant carries 'use client' across when the source had it, brings only the imports the markup actually uses, and exports a module-level constant rather than duplicating it — a copied object literal is a different object, which quietly breaks reference equality downstream.

If a captured value's type cannot be derived from the file, the refactoring refuses and names it. A plausible-but-wrong type compiles, which makes it exactly the corruption no later gate would catch.

C#: edits that look like their neighbours

C# refactor menu

Adding an injected dependency means a using in sorted position, a field, its XML doc, a constructor parameter, a <param> tag at the matching position, and an assignment. Style is measured from the class being edited — underscore or not, this. or not, documented fields or not:

C# refactor preview

Two refusals matter more than the generation itself:

  • A manual new Foo(...) call site means we would have to invent an argument. Injecting null! to keep it compiling is precisely the corruption this tool exists to prevent.
  • Whether the type is registered in DI is never implied to have been checked. An unregistered dependency compiles perfectly and throws at application start, invisible to every type-based verifier.

What ships today

React / TypeScript

Refactoring Needs
Convert import to alias path / to relative path one file
Extract JSX into a component (same file, with in-place naming) one file
Extract JSX into a new component file one file
Extract a custom hook (same file, or into your _hooks/ folder) one file
Convert const X: FC<Props> to a function declaration one file
Wrap JSX in a fragment / a conditional / a .map one file
Inline variable one file
'use client' boundary diagnostics + quick fixes (Next.js) one file
Move a declaration to its own file, repointing every importer import graph
Safe delete a declaration import graph
Rename a component prop, including every JSX call site import graph

C#

Refactoring Needs
Add injected dependency (six coordinated edits) one file
Add missing ConfigureAwait(false) one file
Document a member (copying the interface's wording when there is one) declaration index
Implement an interface member in every implementation declaration index
Extract interface into the folder your repo actually uses declaration index
Register a service in the DI container declaration index
Clone a controller into the next API version declaration index

Plus the Refactor This menu, which lists all of the above alongside everything VS Code and the language servers already offer.

Nothing here loads a TypeScript program or a Roslyn solution. The editor already holds one of each; doubling that is how an extension becomes the thing users blame for a slow VS Code. Cross-file questions are answered by two cheap indexes instead:

  • a declaration index for C# — a parallel parse of top-level declarations, no MetadataReference, no MSBuild — which answers "who implements this" in under a second instead of after a 20–60 second solution load;
  • a lex-only import graph for TypeScript, which answers "which files mention this file" without a type checker.

The limits of that choice are surfaced, not hidden. An implementation that inherits an interface indirectly is not found, and the refusal says so; an import statement whose bindings could not be read cheaply is left alone, and the plan reports how many.

Safety

Every refactoring is a pure function producing a serializable plan, which then passes a fixed pipeline before anything touches disk:

  • Preconditions — never "offer and hope". Three states only: available, available-with-warnings (preview forced), or unavailable with a reason you can read.
  • Optimistic concurrency — WorkspaceEdit has no version field, so we built one. If a file changed while we were analysing, the edit aborts before touching disk. Stale offsets are never rebased onto changed text.
  • Post-apply verification — a WorkspaceEdit containing file creations is documented as not all-or-nothing, so creations are ordered first and content hashes are re-checked afterwards.
  • One undo stop — verified in the real editor, including the created file.

See docs/M0-spike-report.md for the editor capabilities this rests on and how each was verified.


Layout

Path Role
packages/extension VS Code host. The only package allowed to import vscode.
packages/core-ts TS/React engine. Must not import vscode or touch the filesystem — enforced by a test, not a convention.
server-dotnet Roslyn sidecar, JSON-RPC over stdio. Lazy: never spawned for a TypeScript-only session.
fixtures/ Golden fixtures, test workspaces and the screenshot demo project.
scripts/screenshots.ps1 Captures the screenshots above from a real editor.

Development

npm install
npm run build          # tsc --noEmit + esbuild bundle
npm run test:unit      # vitest — no editor, milliseconds
npm run test:cs        # xunit — the C# engine
npm run test:int       # @vscode/test-cli — downloads VS Code on first run
npm test               # all three
.\pack.ps1 -Bump       # version bump + dotnet publish + vsce package + install

F5 launches an Extension Development Host. Three configurations are provided: an empty workspace, a React repo and a C# solution.

Developer commands

Set "refactory.developerMode": true to expose them.

  • Why Is Nothing Offered Here? — structured diagnosis of the caret position: which tsconfig governs the file, which aliases were detected, what the engine saw. This is the most common question a refactoring tool has to answer.
  • Dump Available Code Actions Here — everything every provider offers at the cursor. Run it in a .cs file with C# Dev Kit on and in a .tsx file to keep the "don't rebuild what exists" table honest as the SDKs move.
  • Run UX Capability Spike — probes the editor capabilities the design depends on and writes a report.

Screenshots

.\scripts\screenshots.ps1

Captures with PrintWindow(PW_RENDERFULLCONTENT), which reads the window's own content. That is a privacy decision, not a technical one: anything that grabs a screen rectangle captures whatever is actually on screen if the target window is not on top.

License

MIT

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft