Visual Studio Code (VS Code) hat sich als einer der beliebtesten Code-Editoren für Entwickler auf der ganzen Welt etabliert. Seine schlanke Oberfläche, leistungsstarke Funktionen und die enorme Erweiterbarkeit machen ihn zu einem unverzichtbaren Werkzeug. Doch was, wenn Sie eine spezifische Funktion benötigen, die noch niemand implementiert hat? Oder Sie möchten Ihre eigenen Tools und Arbeitsabläufe direkt in Ihren Editor integrieren? Dann ist die Entwicklung einer eigenen VS Code Extension genau das Richtige für Sie! Und das Beste daran: Als C#-Entwickler müssen Sie Ihre geliebte Sprache nicht einmal verlassen, um die Logik für Ihre Erweiterung zu schreiben.
Dieser umfassende Leitfaden führt Sie Schritt für Schritt durch den Prozess, eine eigene VS Code Extension zu erstellen, wobei der Fokus darauf liegt, wie Sie Ihre C#-Expertise nutzen können, um die Kernlogik zu implementieren. Wir zeigen Ihnen, wie Sie von der ersten Idee bis zum fertigen Code gelangen und Ihre Erweiterung sogar im Visual Studio Marketplace veröffentlichen.
Warum eine VS Code Extension entwickeln?
Es gibt unzählige Gründe, warum Sie eine eigene Erweiterung in Betracht ziehen sollten:
- Produktivität steigern: Automatisieren Sie wiederkehrende Aufgaben oder integrieren Sie benutzerdefinierte Code-Generatoren.
- Spezifische Bedürfnisse abdecken: Erstellen Sie Werkzeuge, die perfekt auf Ihre Nische, Ihr Team oder Ihr Projekt zugeschnitten sind.
- Wissen teilen: Veröffentlichen Sie nützliche Helfer oder Sprachunterstützung für Ihre Community.
- Lernkurve: Tauchen Sie tief in die Welt der Editor-Erweiterung und der Cross-Plattform-Entwicklung ein.
- Integration eigener Tools: Wenn Sie bereits ein leistungsstarkes C#-Tool besitzen, können Sie es nahtlos in VS Code integrieren.
Was ist eine VS Code Extension?
Eine VS Code Extension ist im Wesentlichen ein kleines Programm, das die Funktionalität von VS Code erweitert. Erweiterungen können fast alles tun: Neue Befehle hinzufügen, benutzerdefinierte Views und WebViews bereitstellen, Syntax-Highlighting für neue Sprachen definieren, Debugger integrieren oder sogar bestehende Features ändern. Die Oberfläche und die „Klebstoff”-Logik einer VS Code Extension werden traditionell mit TypeScript oder JavaScript geschrieben, da sie in einer Node.js-Umgebung laufen. Doch keine Sorge: Für die eigentliche, anspruchsvolle Logik können wir C# und .NET nutzen!
Die Brücke: C# und VS Code – Wie funktioniert das?
Da VS Code Extensions in einer Node.js-Umgebung ausgeführt werden, können sie nicht direkt C#-Code ausführen. Die Lösung besteht darin, Ihre C#-Logik in einer separaten Anwendung (z.B. einer .NET Core Konsolenanwendung) zu implementieren, die dann von Ihrer TypeScript-basierten VS Code Extension gestartet und mit ihr kommuniziert wird. Dies ermöglicht eine leistungsstarke Arbeitsteilung: TypeScript kümmert sich um die Benutzeroberfläche und die Interaktion mit VS Code, während C# die schwere Rechenarbeit, Datenverarbeitung oder spezifische Logik übernimmt.
Es gibt hauptsächlich zwei Ansätze, um diese Kommunikation zu realisieren:
Option 1: Der Language Server Protocol (LSP) Ansatz
Das Language Server Protocol (LSP) ist ein von Microsoft entwickeltes, offenes, JSON-RPC-basiertes Protokoll, das die Kommunikation zwischen einem Editor (Client) und einem Server (Language Server) standardisiert. Der Language Server bietet sprachspezifische Features wie Auto-Vervollständigung, Go-to-Definition, Refactoring, Syntaxfehler-Überprüfung (Diagnostics) und vieles mehr. Viele moderne Editoren, darunter VS Code, unterstützen LSP.
Vorteile für C#-Entwickler: Mit LSP können Sie einen Language Server vollständig in C# schreiben. Dieser Server kann dann als separates Programm von Ihrer VS Code Extension gestartet werden und bietet alle erweiterten Sprachfeatures für die von Ihnen unterstützte Sprache oder Dateityp. Dies ist die bevorzugte Methode, wenn Ihre Erweiterung umfangreiche Code-Analyse oder sprachspezifische Funktionen benötigt.
Option 2: Einfache Prozesskommunikation
Für einfachere Aufgaben, die keine volle LSP-Implementierung erfordern, können Sie eine C#-Konsolenanwendung schreiben, die über Standard-Input/Output (stdio) oder Named Pipes mit der VS Code Extension kommuniziert. Die Extension startet die C#-Anwendung als Unterprozess, sendet Anfragen (z.B. als JSON-Strings) über stdin und empfängt Antworten über stdout.
Vorteile: Dieser Ansatz ist einfacher zu implementieren, wenn Sie nur bestimmte Befehle ausführen oder Daten von einem C#-Dienst abrufen müssen, ohne die Komplexität eines vollständigen Language Servers.
Für diesen Artikel konzentrieren wir uns auf den LSP-Ansatz, da er die leistungsstärkste und „integrierteste” Art und Weise ist, C# in VS Code Extensions zu nutzen, um wirklich beeindruckende Features zu schaffen.
Voraussetzungen für die Entwicklung
Bevor wir loslegen, stellen Sie sicher, dass Sie folgende Tools installiert haben:
- Node.js und npm: Für die Ausführung der Extension-Generatoren und das Paketmanagement.
- Visual Studio Code: Die Entwicklungsumgebung selbst.
- .NET SDK: Für die Entwicklung Ihrer C#-Komponente.
- Yeoman und VS Code Extension Generator: Diese Tools vereinfachen die Einrichtung eines neuen Erweiterungsprojekts. Installieren Sie sie global mit:
npm install -g yo generator-code
Schritt 1: Projektinitialisierung – Die VS Code Extension (TypeScript/JavaScript)
Beginnen wir mit der Erstellung des Frontend-Teils Ihrer Extension:
- Öffnen Sie Ihr Terminal/Ihre Eingabeaufforderung.
- Führen Sie den Generator aus:
yo code
- Beantworten Sie die Fragen:
- What type of extension do you want to create? Wählen Sie `New Language Server Extension`. Dies bereitet das Projekt bereits für LSP vor.
- What’s the name of your extension? (z.B. `MyCSharpExtension`)
- What’s the identifier of your extension? (Standardwert übernehmen)
- What’s the description of your extension? (Beliebige Beschreibung)
- Enable TypeScript check and linting? (Ja)
- Initialize a git repository? (Ja)
- Öffnen Sie den generierten Ordner in VS Code:
code MyCSharpExtension
Sie sehen nun eine Projektstruktur, die bereits alles Notwendige für eine Language Server Extension enthält. Die wichtigsten Dateien sind:
package.json
: Das Manifest Ihrer Extension. Es definiert Metadaten, Aktivierungsereignisse (wann die Extension geladen wird) und Beiträge (Befehle, Ansichten, etc.).src/extension.ts
: Der Haupteinstiegspunkt Ihrer Extension. Hier wird die Logik für die Aktivierung und Deaktivierung der Extension sowie die Verbindung zum Language Server hergestellt.client/src/extension.ts
: Der Client-Teil Ihrer LSP-Extension, der den Language Server startet und mit ihm kommuniziert.server/src/server.ts
: Eine Platzhalter-Implementierung für einen Language Server in TypeScript (diesen werden wir durch unsere C#-Implementierung ersetzen).
Schritt 2: Die C# Logik – Ihr Language Server
Jetzt kommt der Teil, in dem Ihre C#-Fähigkeiten glänzen! Erstellen Sie ein neues .NET-Projekt in einem separaten Ordner (z.B. MyCSharpExtensionServer
) neben Ihrem Extension-Projekt. Es ist wichtig, dass dieses Projekt eine eigenständige ausführbare Datei erzeugt.
- Erstellen Sie einen neuen Ordner neben Ihrem Client-Ordner:
mkdir MyCSharpExtensionServer
- Navigieren Sie in diesen Ordner:
cd MyCSharpExtensionServer
- Erstellen Sie ein neues Konsolenprojekt:
dotnet new console
- Fügen Sie das NuGet-Paket für den Language Server hinzu:
dotnet add package Microsoft.LanguageServer.Protocol
und möglicherweiseStreamJsonRpc
für die Implementierung.
In Ihrer Program.cs
-Datei können Sie nun Ihren Language Server implementieren. Eine grundlegende Struktur könnte so aussehen:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StreamJsonRpc; // Notwendig für die Implementierung
// Hier implementieren Sie Ihre eigene Language Server Logik
// Dies ist ein stark vereinfachtes Beispiel
public class MyLanguageServer
{
private JsonRpc _rpc;
private ILogger _logger;
public MyLanguageServer(Stream input, Stream output, ILogger logger)
{
_logger = logger;
_rpc = new JsonRpc(output, input, this);
_rpc.StartListening();
}
// Beispielmethode, die vom Client aufgerufen werden kann
public async Task<string> SayHello(string name)
{
_logger.LogInformation($"Received SayHello request for: {name}");
await Task.Delay(100); // Simulate some work
return $"Hello from C# Language Server, {name}!";
}
// Weitere LSP-Methoden (initialize, textDocument/didOpen, etc.) würden hier implementiert
// Sie würden die Microsoft.LanguageServer.Protocol-Typen nutzen
public async Task WaitForExit()
{
await _rpc.Completion;
}
}
class Program
{
static async Task Main(string[] args)
{
// Ein einfacher Logger
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole().SetMinimumLevel(LogLevel.Debug);
});
var logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("C# Language Server Starting...");
// LSP kommuniziert über Standard-Input/Output
using var stdin = Console.OpenStandardInput();
using var stdout = Console.OpenStandardOutput();
// Hier würden Sie Ihren eigentlichen LSP-Server initialisieren
// Das ist eine stark vereinfachte Version ohne volle LSP-Klassen
var server = new MyLanguageServer(stdin, stdout, logger);
await server.WaitForExit();
logger.LogInformation("C# Language Server Exiting.");
}
}
Um eine vollständige LSP-Implementierung zu erstellen, würden Sie die Klassen und Interfaces aus dem Microsoft.LanguageServer.Protocol
-Paket nutzen, wie LanguageServer
, InitializeParams
, TextDocumentSyncKind
usw. Es gibt viele Ressourcen und Beispiele online, die detaillierter auf die Implementierung eines vollständigen C#-Language-Servers eingehen. Das obige Beispiel zeigt jedoch die grundlegende Kommunikationsbrücke.
Schritt 3: Den C# Server aus der VS Code Extension starten
Nun müssen wir die VS Code Extension (TypeScript) dazu bringen, Ihren C#-Language Server zu starten und mit ihm zu kommunizieren. Gehen Sie zurück in Ihr VS Code Extension Projekt (MyCSharpExtension
).
Bearbeiten Sie die Datei client/src/extension.ts
(oder src/extension.ts
, je nachdem, wie Ihr Generator sie benannt hat).
- Löschen Sie den Inhalt der
server/
-Ordner (da wir einen eigenen C#-Server verwenden). - Passen Sie den Pfad zu Ihrem C#-Server an:
Zuerst müssen Sie Ihr C#-Projekt kompilieren. Wechseln Sie im Terminal in den Ordner
MyCSharpExtensionServer
und führen Siedotnet publish -c Release -r win-x64 --self-contained true /p:PublishSingleFile=true
(oder die entsprechende Runtime für Ihr OS) aus. Dies erstellt eine eigenständige ausführbare Datei.Passen Sie dann in
client/src/extension.ts
den Pfad an, um Ihr kompiliertes C#-Programm zu starten:import * as path from 'path'; import { workspace, ExtensionContext } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node'; let client: LanguageClient; export function activate(context: ExtensionContext) { // Der Pfad zu Ihrer C#-Anwendung (aus dem publish-Ordner) // Passen Sie diesen Pfad an Ihr Betriebssystem und Ihren Ordnernamen an // Für Windows könnte es '.\..\MyCSharpExtensionServer\bin\Release\net8.0\win-x64\publish\MyCSharpExtensionServer.exe' sein // Für Linux/macOS '.\..\MyCSharpExtensionServer\bin\Release\net8.0\osx-x64\publish\MyCSharpExtensionServer' const serverExecutable = path.join(__dirname, '..', '..', '..', 'MyCSharpExtensionServer', 'bin', 'Release', 'net8.0', 'win-x64', 'publish', 'MyCSharpExtensionServer.exe'); // Beispielpfad! // Konfiguration für den Language Server const serverOptions: ServerOptions = { run: { command: serverExecutable, transport: TransportKind.stdio }, debug: { command: serverExecutable, transport: TransportKind.stdio, args: ['--debug'] } // Optional: Debug-Argumente }; // Optionen für den Language Client const clientOptions: LanguageClientOptions = { documentSelector: [{ scheme: 'file', language: 'plaintext' }], // Welche Dateien Ihr Server verarbeitet (Beispiel: Textdateien) synchronize: { fileEvents: workspace.createFileSystemWatcher('**/.clientrc') } }; // Erstellen des Language Clients client = new LanguageClient( 'myCSharpLanguageServer', 'My C# Language Server', serverOptions, clientOptions ); // Starten des Clients und somit auch des Servers client.start(); } export function deactivate(): Thenable<void> | undefined { if (!client) { return undefined; } return client.stop(); }
Wichtiger Hinweis zum Pfad: Der Pfad serverExecutable
muss korrekt auf die ausführbare Datei Ihres C#-Servers verweisen, relativ zum Kompilat Ihrer TypeScript-Extension. Es ist oft eine gute Praxis, den C#-Server vor dem Verpacken der Extension ins `out`-Verzeichnis zu kopieren oder ihn im `package.json` als Asset anzugeben.
Schritt 4: Entwicklung und Debugging
Das Debugging einer Extension, die einen externen Prozess startet, erfordert ein wenig mehr Aufmerksamkeit.
- Debugging der TypeScript-Extension: Öffnen Sie Ihr Extension-Projekt in VS Code. Drücken Sie
F5
, um die Extension in einem neuen „Extension Development Host” Fenster zu starten. Fehler im TypeScript-Code werden in der Debug-Konsole des Haupt-VS Code Fensters angezeigt. - Debugging des C#-Language Servers:
- Stellen Sie sicher, dass Ihr C#-Server im Debug-Modus kompiliert wurde.
- Sie können Debug-Meldungen direkt in die Konsole des C#-Servers schreiben (
Console.WriteLine
oder besser noch mitILogger
), die dann im „Output”-Fenster des Extension Development Host sichtbar werden (wenn der Transportstdio
ist). - Für echtes Schritt-für-Schritt-Debugging können Sie einen C#-Debugger (z.B. den C# Debugger in VS Code) an den laufenden C#-Prozess anhängen. Dies geschieht in der Regel, indem Sie im „Extension Development Host”-Fenster (im „Run and Debug”-Panel) „Attach to Process” auswählen und den Prozess Ihres C#-Servers finden. Dies erfordert oft, dass Ihr C#-Server eine kurze Pause einlegt oder auf ein Signal wartet, bevor er vollständig startet, damit Sie Zeit zum Anhängen haben.
Schritt 5: Testen Ihrer Extension
VS Code Extensions können mit dem Mocha Test-Framework getestet werden. Der yo code
Generator hat bereits einen Ordner src/test
erstellt. Hier können Sie Integrationstests schreiben, die Ihre Befehle ausführen und die Ergebnisse überprüfen.
Schritt 6: Veröffentlichung Ihrer Extension im Visual Studio Marketplace
Sobald Ihre Extension stabil ist und alle gewünschten Funktionen bietet, können Sie sie im Visual Studio Marketplace veröffentlichen, damit andere sie nutzen können.
- Erstellen Sie einen Publisher: Gehen Sie zu marketplace.visualstudio.com/manage und erstellen Sie einen neuen Publisher.
- Installieren Sie das `vsce`-Tool:
npm install -g vsce
- Generieren Sie ein Persönliches Zugriffstoken (PAT): Gehen Sie zu dev.azure.com/your-organization/_usersSettings/tokens (ersetzen Sie
your-organization
durch Ihren Publisher-Namen) und generieren Sie ein PAT mit „Full access” für „Marketplace (all access)”. - Melden Sie sich an: Führen Sie in Ihrem Extension-Stammverzeichnis aus:
vsce login your-publisher-name
und geben Sie Ihr PAT ein. - Verpacken Sie Ihre Extension: Bevor Sie verpacken, stellen Sie sicher, dass Ihr C#-Server in den relevanten Build-Ordner kopiert wird. Am einfachsten ist es, wenn das `dotnet publish`-Kommando direkt in das `out`-Verzeichnis des VS Code Extensions-Projekts kopiert, oder Sie den C# Server direkt in Ihrem `package.json` unter `files` als Asset angeben. Dann führen Sie aus:
vsce package
. Dies erstellt eine.vsix
-Datei. - Veröffentlichen Sie:
vsce publish
.
Best Practices und Tipps
- Performance: Achten Sie darauf, dass Ihr C#-Server schnell startet und reagiert, um ein flüssiges Benutzererlebnis zu gewährleisten.
- Fehlerbehandlung: Implementieren Sie robuste Fehlerbehandlung in sowohl Ihrer C#- als auch Ihrer TypeScript-Logik. Protokollieren Sie Fehler sorgfältig.
- Benutzererfahrung: Bieten Sie sinnvolle Fortschrittsanzeigen für langlaufende Operationen.
- Dokumentation: Eine gute
README.md
ist entscheidend für den Erfolg Ihrer Extension. Beschreiben Sie Funktionen, Installation und Nutzung. - Versionierung: Folgen Sie dem SemVer-Standard für die Versionierung Ihrer Extension.
- Open Source: Viele erfolgreiche Extensions sind Open Source. Erwägen Sie, Ihren Code auf GitHub zu teilen.
Fazit
Die Entwicklung einer VS Code Extension mit C# und .NET öffnet Ihnen die Tür zu einer völlig neuen Ebene der Produktivität und Individualisierung. Obwohl die Oberfläche der Extensions in TypeScript geschrieben ist, können Sie die mächtige Logik und die umfangreiche Bibliothek von .NET für die Kernfunktionen nutzen. Egal, ob Sie einen fortschrittlichen Language Server, ein Code-Analyse-Tool oder eine einfache Automatisierung entwickeln möchten – der Weg von der Idee zum Code ist klar definiert. Experimentieren Sie, lernen Sie und schaffen Sie innovative Werkzeuge, die die Arbeitsweise von Entwicklern verändern können. Ihre nächste großartige C#-basierte VS Code Extension wartet darauf, entwickelt zu werden!