twoslash for C#

Rich, interactive C# code snippets
powered by Roslyn

Add hover tooltips, IntelliSense completions, and compile verification to your C# documentation. Zero runtime JavaScript. Built on the same compiler that powers Visual Studio.

var
List<Todo>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
todos
List<Todo>? todos
= new List
List<Todo>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
<
List<Todo>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
Todo
Todo
A task with a title and completion status.
>
List<Todo>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
{
new("Learn Glo#", true),
new("Build docs", false),
};
var pending
IEnumerable<Todo>? pending
= todos
List<Todo>? todos
.Where
IEnumerable<Todo> IEnumerable<Todo>.Where<Todo>(Func<Todo, bool> predicate)
Filters a sequence of values based on a predicate.
source — An IEnumerable`1 to filter.
predicate — A function to test each element for a condition.
An IEnumerable`1 that contains elements from the input sequence that satisfy the condition.
ArgumentNullException — source or predicate is .
(t
Todo t
=> !
IEnumerable<Todo> IEnumerable<Todo>.Where<Todo>(Func<Todo, bool> predicate)
Filters a sequence of values based on a predicate.
source — An IEnumerable`1 to filter.
predicate — A function to test each element for a condition.
An IEnumerable`1 that contains elements from the input sequence that satisfy the condition.
ArgumentNullException — source or predicate is .
t
Todo t
.Done
bool Todo.Done
);
IEnumerable<Todo>
Exposes the enumerator, which supports a simple iteration over a collection of a specified type.
Console
Console
Represents the standard input, output, and error streams for console applications. This class cannot be inherited.
.WriteLine
void Console.WriteLine(string? value)
Writes the specified string value, followed by the current line terminator, to the standard output stream.
value — The value to write.
IOException — An I/O error occurred.
($"Pending: {pending
IEnumerable<Todo>? pending
.Count
int IEnumerable<Todo>.Count<Todo>()
Returns the number of elements in a sequence.
source — A sequence that contains elements to be counted.
The number of elements in the input sequence.
ArgumentNullException — source is .
OverflowException — The number of elements in source is larger than MaxValue.
()}");
var todos = new List<Todo>
{
    new("Learn Glo#", true),
    new("Build docs", false),
};

var pending = todos.Where(t => !t.Done);
//^?

// @highlight
Console.WriteLine($"Pending: {pending.Count()}");
// @hide
/// <summary>A task with a title and completion status.</summary>
public record Todo(string Title, bool Done);

Compiler-verified

Every snippet is compiled by Roslyn. Catch errors in documentation before your readers do.

IDE-quality hovers

Full type signatures, XML doc comments, parameter info, and overload counts—just like Visual Studio.

Zero runtime JS

Tooltips use CSS anchor positioning. No JavaScript shipped to your readers—ever.

Everything you need for
beautiful C# documentation

^?

Hover queries

Place // ^? markers below any token to show its type signature and documentation inline. Readers see exactly what they'd see in their IDE.

var
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
items
List<int>? items
= new List
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
<
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
int
int
Represents a 32-bit signed integer.
>
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
{ 1, 2, 3 };
var
int
Represents a 32-bit signed integer.
count = items
List<int>? items
.Count
int List<int>.Count
Gets the number of elements contained in the List`1.
The number of elements contained in the List`1.
;
int count
var items = new List<int> { 1, 2, 3 };
var count = items.Count;
//  ^?
^?

XML documentation

Full extraction of <summary>, <param>, <returns>, <remarks>, and <exception> tags. Rendered in structured, readable popups.

var
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
items
List<int>? items
= new List<
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
int
int
Represents a 32-bit signed integer.
>
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
{ 1, 2, 3 };
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
var
int
Represents a 32-bit signed integer.
total
int total
= items
List<int>? items
.Sum
int IEnumerable<int>.Sum()
Computes the sum of a sequence of Int32 values.
source — A sequence of Int32 values to calculate the sum of.
The sum of the values in the sequence.
ArgumentNullException — source is .
OverflowException — The sum is larger than MaxValue.
();
var items = new List<int> { 1, 2, 3 };
//              ^?
var total = items.Sum();
@errors

Error assertions

Expect specific compiler errors with // @errors: CS1002. Verify error messages and severity. Use // @noErrors to assert clean compilation.

int
int
Represents a 32-bit signed integer.
x
int x
= "hello";
// @errors: CS0029
int x = "hello";
@noErrors

Compile verification

Assert your snippets compile cleanly with // @noErrors. Catch documentation bugs in CI before your readers find them.

var
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
items
List<int>? items
= new List
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
<
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
int
int
Represents a 32-bit signed integer.
>
List<int>
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
{ 1, 2, 3 };
var
int
Represents a 32-bit signed integer.
sum
int sum
= items
List<int>? items
.Sum
int IEnumerable<int>.Sum()
Computes the sum of a sequence of Int32 values.
source — A sequence of Int32 values to calculate the sum of.
The sum of the values in the sequence.
ArgumentNullException — source is .
OverflowException — The sum is larger than MaxValue.
();
Console
Console
Represents the standard input, output, and error streams for console applications. This class cannot be inherited.
.WriteLine
void Console.WriteLine(string? value)
Writes the specified string value, followed by the current line terminator, to the standard output stream.
value — The value to write.
IOException — An I/O error occurred.
($"Sum: {sum
int sum
}");
// @noErrors
var items = new List<int> { 1, 2, 3 };
var sum = items.Sum();
Console.WriteLine($"Sum: {sum}");
@highlight

Visual annotations

Highlight, focus, and diff lines with // @highlight, // @focus, and // @diff: +/-. Guide your reader's attention to what matters.

var config = LoadConfig();
var conn = config.GetConnection();
var db = new DbContext(conn);
var config = LoadConfig();
var conn = config.GetConnection();
// @highlight
var db = new DbContext(conn);
#region

Region extraction

Extract named #region blocks from full source files. The entire file compiles for accurate types, but only the region appears in output.

public string
string
Represents text as a sequence of UTF-16 code units.
FullName
string User.FullName
=> $"{First
string User.First
} {Last
string User.Last
}";
public class User {
  public string First { get; set; }
  public string Last { get; set; }
  #region FullName
  public string FullName
    => $"{First} {Last}";
  #endregion
}
---cut---

Hidden setup code

Hide boilerplate with // ---cut--- or // @hide/// @show. Setup code compiles but doesn't appear in the rendered snippet.

app
WebApplication? app
.MapGet
RouteHandlerBuilder IEndpointRouteBuilder.MapGet(string pattern, Delegate handler)
Adds a RouteEndpoint to the IEndpointRouteBuilder that matches HTTP GET requests for the specified pattern.
endpoints — The IEndpointRouteBuilder to add the route to.
pattern — The route pattern.
handler — The delegate executed when the endpoint is matched.
A RouteHandlerBuilder that can be used to further customize the endpoint.
("/", () => "Hello");
app
WebApplication? app
.Run
void WebApplication.Run(string? url = null)
Runs an application and blocks the calling thread until host shutdown.
url — The URL to listen to if the server hasn't been configured directly.
();
#:sdk Microsoft.NET.Sdk.Web
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// ---cut---
app.MapGet("/", () => "Hello");
app.Run();
#:pkg

File-based apps

On .NET 10+, use #:package and #:sdk directives directly in your code. No separate .csproj needed—your snippet is valid C# and Glo# input.

using Newtonsoft
Newtonsoft
.Json
Json
;
var
string
Represents text as a sequence of UTF-16 code units.
json
string? json
= JsonConvert
JsonConvert
Provides methods for converting between .NET types and JSON types.
.SerializeObject
string JsonConvert.SerializeObject(object? value)
Serializes the specified object to a JSON string.
value — The object to serialize.
A JSON string representation of the object.
(new { x
int <anonymous type: int x>.x
= 1 });
#:package Newtonsoft.Json@13.0.3
using Newtonsoft.Json;
var json = JsonConvert.SerializeObject(new { x = 1 });

Two-layer caching

In-process compilation context cache plus SHA256-keyed disk cache. Rebuilds are fast. Atomic writes handle concurrent processes safely.

Terminal window
$ glosharp verify samples/ --cache-dir .glosharp-cache
24 files verified (18 cached)
Completed in 1.2s

How it works

Glo# runs at build time. Your readers never download extra JavaScript.

1

Write C# with markers

Add // ^? markers to show hover info, // @errors to assert compiler errors, and directives like // @highlight to annotate visually.

2

Roslyn compiles & extracts

Glo# invokes the Roslyn compiler, resolves symbols, extracts type signatures, XML docs, and diagnostics. Everything is output as structured JSON.

3

Render with your tools

Use the Shiki transformer, Expressive Code plugin, or standalone HTML renderer. Hovers become CSS-positioned popups. No JavaScript required.

Works with your stack

First-class integrations for the most popular documentation tools.

Shiki transformer

@glosharp/shiki

Drop into any Shiki-powered pipeline. Works with Astro, VitePress, and any framework that uses Shiki for syntax highlighting.

import { transformerGloSharp } from '@glosharp/shiki'
const html = await codeToHtml(code, {
lang: 'csharp',
transformers: [transformerGloSharp({
project: './My.csproj'
})]
})

Expressive Code plugin

@glosharp/expressive-code

Full Expressive Code integration with auto-hover extraction. Every C# block gets rich tooltips automatically. Perfect for Starlight docs.

import { pluginGloSharp } from '@glosharp/expressive-code'
export default defineConfig({
integrations: [starlight({
expressiveCode: {
plugins: [pluginGloSharp({
project: './My.csproj'
})]
}
})]
})

Standalone HTML

glosharp CLI

Generate self-contained HTML with no external dependencies. Perfect for Hugo, Jekyll, or any static site. Just embed the output.

Terminal window
$ glosharp render snippet.cs \
--theme github-dark \
--standalone \
--output snippet.html

Node.js API

@glosharp/core

Programmatic access for custom integrations. Full TypeScript types. Build your own rendering pipeline or IDE extension.

import { createGloSharp } from '@glosharp/core'
const glosharp = createGloSharp({
project: './My.csproj'
})
const result = await glosharp.process({
code: 'var x = 42; // ^?'
})

Get started in minutes

Pick the integration that fits your workflow.

1. Install the .NET tool and npm packages

Terminal window
dotnet tool install --global GloSharp.Cli
npm install @glosharp/shiki

2. Configure your Astro Shiki setup

astro.config.mjs
import { transformerGloSharp } from '@glosharp/shiki'
export default defineConfig({
markdown: {
shikiConfig: {
transformers: [transformerGloSharp({
project: './samples/Samples.csproj'
})]
}
}
})

3. Write C# code blocks with markers

```csharp
var greeting = "Hello, world!";
greeting.Length;
// ^?
```

1. Install the .NET tool and npm packages

Terminal window
dotnet tool install --global GloSharp.Cli
npm install @glosharp/expressive-code

2. Configure the Expressive Code plugin

astro.config.mjs
import { pluginGloSharp } from '@glosharp/expressive-code'
export default defineConfig({
integrations: [starlight({
expressiveCode: {
plugins: [pluginGloSharp({
project: './samples/Samples.csproj'
})]
}
})]
})

3. Every C# code block is enhanced automatically

With auto-hover extraction, all C# blocks get rich tooltips without any markers. Add // ^? for persistent, always-visible hovers.

1. Install the .NET tool

Terminal window
dotnet tool install --global GloSharp.Cli

2. Process a file

Terminal window
# Output JSON with type info, hovers, errors
glosharp process snippet.cs --project ./My.csproj
# Generate standalone HTML
glosharp render snippet.cs --theme github-dark --standalone
# Process from stdin
echo 'var x = 42; // ^?' | glosharp process --stdin
# Scaffold a config file
glosharp init

1. Add Glo# to your CI pipeline

Terminal window
dotnet tool install --global GloSharp.Cli

2. Verify all snippets compile

Terminal window
# Fail the build if any snippet has unexpected errors
glosharp verify samples/ --project ./Samples.csproj
# With caching for faster CI runs
glosharp verify samples/ --cache-dir .glosharp-cache

Use // @errors: CS0029 to mark expected errors and // @noErrors to assert clean compilation. Unexpected errors will fail the build.

Marker reference

The full set of markers you can use in your C# snippets.

Query markers

// ^? Show type/hover info at this column
// ^| Show IntelliSense completions at this column

Error handling

// @errors: CS1002 Assert specific compiler errors are expected
// @noErrors Assert the snippet compiles cleanly
// @suppressErrors Suppress all or specific error codes

Visual annotations

// @highlight Highlight this line (or a range)
// @focus Focus on this line, dim others
// @diff: + Mark as added line in a diff view
// @diff: - Mark as removed line in a diff view

Visibility

// @hide / // @show Toggle line visibility
// ---cut--- Hide everything above this line
// @above-hidden Alias for ---cut---

Compiler control

// @nullable: enable Set nullable context
// @langVersion: 12 Set C# language version

File-based app directives .NET 10+

#:package Newtonsoft.Json Declare a NuGet package
#:sdk Microsoft.NET.Sdk.Web Set the SDK type
#:property Key=Value Set project properties

Make your C# documentation come alive

Glo# is open source and ready to use today.