# dotnet/vscode-csharp: the TypeScript shell around a Roslyn language server

> Microsoft's official C# extension for Visual Studio Code, built on the Language Server Protocol and shipped as a required dependency of C# Dev Kit.

**dotnet/vscode-csharp** — Official C# support for Visual Studio Code

- Repository: https://github.com/dotnet/vscode-csharp
- Stars: 3,069 · Forks: 736
- Language: TypeScript
- License: MIT
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/dotnet-vscode-csharp

## A client for someone else's compiler

The README describes the project in two sentences, and the second one is the whole architecture. This is a Visual Studio Code extension providing rich language support for C#, shipped along with C# Dev Kit, powered by a Language Server Protocol server, integrating with open source components like Roslyn and Razor to provide rich type information and a faster, more reliable C# experience.

So there are three layers. Roslyn is the C# compiler and language service, written in C#, maintained in its own repository. The LSP server wraps Roslyn in a protocol VS Code can talk to. This repository is the TypeScript side of that conversation, and everything else in it is editor surface: activation logic, the Razor TextMate grammar, the debugger adapters, the snippets directory, the localization bundles for thirteen languages, and the Azure Pipelines definitions that push all of it out.

The repository reflects that split in its file listing. TypeScript is the declared language, with src/, snippets/, esbuild.js, eslint.config.mjs, jest.config.ts and __mocks__/ for the test setup. Alongside that sit msbuild/, RuntimeLicenses/, Directory.Build.props and NuGet.config, which exist because parts of the payload are .NET components rather than JavaScript. The repository has 3,069 stars and 736 forks, is not archived, uses main as its default branch, and was last pushed on 2026-09-23. Topics are c-sharp, csharp, dotnet, omnisharp and vscode, which is a fair summary of the migration this project has been through.

## The version pins that define the stack

The package.json in this repository declares defaults for every downstream component the extension ships. That block is the fastest way to see what you actually get when you install it.

```json
  "capabilities": {
    "virtualWorkspaces": false,
    "untrustedWorkspaces": {
      "supported": "limited"
    }
  },
  "extensionKind": [
    "workspace"
  ],
  "defaults": {
    "roslyn": "5.12.0-1.26471.3",
    "omniSharp": "1.39.14",
    "razorOmnisharp": "7.0.0-preview.23363.1",
    "xamlTools": "18.10.12014.341",
    "testDiscovery": "11.0.59-g6f54c5"
  },
```

Read that as a compatibility statement. Roslyn 5.12 is the language service doing your IntelliSense and your code fixes. The OmniSharp pin at 1.39.14 is the older server that predates Roslyn based language support and is kept for legacy scenarios. The Razor OmniSharp pin is still marked preview, which is a detail worth knowing if you work in .cshtml or Razor components. xamlTools at 18.10 is what makes XAML editing work, and testDiscovery at 11.0.59 is the piece that surfaces tests in the test explorer.

Two capability declarations sit right above those pins and constrain where the extension will run. Virtual workspaces are disabled, so this extension does not function in a virtual filesystem such as a remote repository browser view. Untrusted workspaces are only limited supported. And extensionKind is workspace, which means the extension runs on the remote side when you are connected to a remote or container workspace rather than on your local machine. For anyone using dev containers, that last one is the behavior to expect.

## The legacy escape hatch, spelled out in the README

The README keeps a dedicated section on using OmniSharp, which tells you how contested this transition has been and how to back out of it if you need to.

The procedure is three steps. Set dotnet.server.useOmnisharp to true in the extension settings. Uninstall or disable C# Dev Kit, since it will otherwise reinstall and reconfigure the Roslyn path. Restart VS Code for the change to take effect.

There is a second, more specific note for projects that predate .NET 6 or are not solution based. In that case the README tells you to install a .NET Framework runtime and MSBuild tooling, set omnisharp.useModernNet to false, set dotnet.server.useOmnisharp to true, and uninstall or disable C# Dev Kit. On Windows that means .NET Framework plus the MSBuild Tools for Visual Studio 2022. On MacOS and Linux it means Mono with MSBuild.

None of this is hidden, which is a point in the project's favor. But it does mean the extension has two supported personalities, and the deciding factor is usually the target framework of the project you are opening rather than your own preference. If you maintain a mixed estate with some services on .NET Framework and some on current .NET, you will find yourself switching this setting more often than you would like.

## What the 2.160 release says about current work

The stable tag v2.160.4, published in September 2026, is the most useful release note in the set because it describes intent rather than a version bump. It opens by saying the update improves C# and Razor editing, project loading performance, debugging, MAUI Hot Reload, and solution management.

The language feature worth naming is file based apps, which now support the #:ref directive including syntax classification and completion. File based apps are the newer .NET model where a single .cs file runs as a program with dependencies declared in directives rather than a project file, and having #:ref classified and completed in the editor is what makes them pleasant to write in VS Code rather than only workable.

Refactoring work includes making Move Static Members Up available in headless editor environments, making Convert to Local Function work inside top level statements, and fixing source generators that use ForAttributeWithMetadataName so they recognize constructor targeted attributes on primary and record constructors. Completion now suggests types hidden by same named primary constructor parameters in static contexts, offers unimported types in cref documentation, and handles inherited AttributeUsage constraints correctly. Quick Info shows nullable annotations for delegates.

The prerelease tags show the churn behind those improvements. The 2.151.x line is mostly Roslyn bumps to 5.12.0 with fixes for spurious TypeScript diagnostics caused by Razor code, clamping LSP position character to the line end, IDE0002 for static abstract and virtual interface member access, and avoiding a full project reload when only a .cs file changes. The 2.147.x line trims LSP dispatch allocations and reduces LSP request handling allocations. The allocation work in particular is what turns a large solution from slow into usable, and it is not the kind of change that shows up in a feature list.

## Why the issue tracker is so large

The repository shows 679 open issues, and that number deserves an explanation rather than a reaction, because it is a structural property of the design.

An extension that wraps a compiler is a proxy for that compiler's behavior. When Roslyn moves, the extension inherits whatever changed, including cases where the language server is correct and the editor experience is not, cases where a Razor grammar disagrees with a Razor language server, and cases where a .NET SDK version on the machine produces a different project graph than the extension expects. Every one of those shows up in this repository's tracker rather than Roslyn's, because this is where users report them.

The release notes show the shape of that load. A single Roslyn bump brings along fixes for LSP position handling, allocation trimming, unsafe evolution diagnostics not being picked up by the IDE, errors logged while a load is being cancelled, nested and prefixed tag helpers throwing exceptions, and linked editing on empty tag pairs. Those are eight distinct user visible symptoms from what reads as one dependency update.

There is also the localization surface. The tree lists package.nls.json alongside German, Spanish, French, Italian, Japanese, Korean, Polish, Brazilian Portuguese, Russian, Turkish, Simplified Chinese and Traditional Chinese bundles, plus an l10n/ directory and a loc/ directory. Shipping an editor experience in thirteen languages adds translation, string extraction and format sensitivity issues on top of the engineering problems, and the build scripts reflect that with a dedicated l10nDevGenerateLocalizationBundle step wired into both compile and compileDev.

## Building and contributing to the extension

The build is an npm project with a .NET component attached, and the scripts section of package.json shows how the two halves are stitched together.

The compile script runs tsc against tsconfig.json, then npm run lint, then the localization bundle generation, then a Razor TextMate grammar compile step. compileDev is the same sequence. The ordering matters and is a fair summary of what can go wrong in a project like this: types first, lint second, generated resources third, grammar assets last, with a Jest configuration and codecov.yml alongside for the test side.

The repository also carries azure-pipelines.yml, azure-pipelines-official.yml and an azure-pipelines/ directory, which is how a project with both npm and MSBuild steps ships to the marketplace. NuGet.config and Directory.Build.props handle the .NET side of the payload, and the presence of ThirdPartyNotices.txt and RuntimeLicenses/ alongside the MIT LICENSE.txt reflects the split licensing the README describes: the extension source is MIT, while the C# extension itself is subject to separate license terms, and RuntimeLicenses/license.txt is the file that governs the shipped runtime components.

Contributions go through the .NET Foundation CLA, and the project is supported by the .NET Foundation. Bug reports follow the instructions in SUPPORT.md rather than the general issue template, and the README points people at the existing issues first so reactions can be used for prioritization.

## Conclusion

The important structural fact about this repository is that it is not where C# language intelligence lives. The intelligence is Roslyn, running as a separate language server process, and this extension is the TypeScript client that talks to it, plus the Razor grammar, the debugger plumbing, the localization bundles and the npm build. The package.json defaults block is the clearest single view of the whole stack, because it pins the Roslyn build, the OmniSharp build, the Razor OmniSharp build, the XAML tools version and the test discovery version that ship together. The README also recommends C# Dev Kit rather than the standalone extension, and C# Dev Kit in turn takes this extension as a required dependency, so the two ship as a pair even though this repository remains installable on its own. Two rough edges deserve a plan rather than a surprise. The 679 open issues are a direct consequence of shipping several language server versions through one extension, since a Roslyn bump can bring in diagnostics for Razor, LSP position clamping, static abstract interface members and IntelliSense edge cases all at once. And projects older than .NET 6 or outside solution based layouts need a different setup entirely, with OmniSharp re-enabled, C# Dev Kit uninstalled, and Mono with MSBuild on Mac and Linux. If you are starting fresh on a current .NET version, install C# Dev Kit and let the dependency chain resolve.

## FAQ

### Can you do C# in VS Code?

Yes. This extension is the official Microsoft C# support for Visual Studio Code, and it provides IntelliSense, navigation, refactoring and formatting and linting through a Language Server Protocol server powered by Roslyn. Install C# Dev Kit, which pulls this extension in as a required dependency, and open a folder containing a .csproj or .sln file so the extension activates.

### Which IDE is better for C#?

The repository does not make that comparison. What it does state is that this extension is a standalone option, while C# Dev Kit is the recommended install, and that C# Dev Kit installs this extension automatically as a required dependency. Projects targeting versions before .NET 6, or non solution based projects, need a different setup: OmniSharp re-enabled via dotnet.server.useOmnisharp, C# Dev Kit uninstalled, and MSBuild tooling or Mono with MSBuild installed.

### How do I switch the C# extension back to OmniSharp?

Set `dotnet.server.useOmnisharp` to true in the extension settings, uninstall or disable C# Dev Kit, and restart VS Code. For projects older than .NET 6 the README also asks you to set `omnisharp.useModernNet` to false and install MSBuild tooling on Windows or Mono with MSBuild on MacOS and Linux.

## Sources

- [dotnet/vscode-csharp on GitHub](https://github.com/dotnet/vscode-csharp)
- [Issues](https://github.com/dotnet/vscode-csharp/issues)
- [License: MIT](https://github.com/dotnet/vscode-csharp/blob/main/LICENSE)
- [README](https://github.com/dotnet/vscode-csharp/blob/main/README.md)
- [Releases](https://github.com/dotnet/vscode-csharp/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dotnet-vscode-csharp
