Start Debugging

Wave-IDE em 2026: o encanamento mínimo de Roslyn por trás de uma IDE WinForms no .NET 10

Wave-IDE mostra que WinForms e Roslyn no .NET 10 já bastam para construir uma IDE C# funcional. Aqui está o encanamento mínimo para análise incremental, autocompletar e diagnósticos.

Um post no r/csharp compartilhou “Wave”, uma IDE em WinForms construída como projeto pessoal de C#, com o repo linkado direto na thread. É um bom lembrete de que em um .NET 9 e .NET 10 modernos ainda dá para construir tooling de desktop sério com tecnologia “chata”: WinForms para UI, Roslyn para os serviços de linguagem e um pouco de disciplina em torno de atualizações incrementais.

Fontes: thread no Reddit e o repo linkado fmooij/Wave-IDE.

”IDE” começa por um workspace, não por painéis dockáveis

Se você tira a pintura da UI, as responsabilidades centrais são:

No .NET 10 com C# 14, o Roslyn te dá o motor da linguagem, mas ele não vai te salvar se você reabrir a solution a cada edição.

Mantenha um único snapshot da solution e atualize documentos de forma incremental

Esse esqueleto carrega a solution uma vez e depois atualiza o texto de um Document em memória. A partir daí, consulta diagnósticos e itens de autocompletar. É intencionalmente mínimo, mas mostra o formato necessário para o loop do editor.

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Completion;

public sealed class RoslynServices
{
    private readonly MSBuildWorkspace _workspace = MSBuildWorkspace.Create();
    private Solution? _solution;

    public async Task LoadSolutionAsync(string slnPath, CancellationToken ct)
        => _solution = await _workspace.OpenSolutionAsync(slnPath, cancellationToken: ct);

    public async Task<(Diagnostic[] diagnostics, CompletionItem[] items)> AnalyzeAsync(
        DocumentId docId,
        string newText,
        int caretPosition,
        CancellationToken ct)
    {
        if (_solution is null) throw new InvalidOperationException("Solution not loaded.");

        var doc = _solution.GetDocument(docId) ?? throw new InvalidOperationException("Missing document.");
        doc = doc.WithText(SourceText.From(newText));
        _solution = doc.Project.Solution;

        var compilation = await doc.Project.GetCompilationAsync(ct);
        var diags = compilation?
            .GetDiagnostics(ct)
            .Where(d => d.Location.IsInSource)
            .ToArray() ?? Array.Empty<Diagnostic>();

        var completion = CompletionService.GetService(doc);
        var items = completion is null
            ? Array.Empty<CompletionItem>()
            : (await completion.GetCompletionsAsync(doc, caretPosition, cancellationToken: ct))?.Items.ToArray()
              ?? Array.Empty<CompletionItem>();

        return (diags, items);
    }
}

Se você guarda o DocumentId por aba aberta, isso vira a espinha dorsal: faça debounce das teclas (por exemplo 150-250ms), chame AnalyzeAsync e depois renderize diagnósticos e autocompletar na UI do seu editor.

A primeira armadilha de escala é o stutter, não a corretude

A fase do “funciona” é fácil. A fase do “parece responsivo” é onde a maioria das IDEs caseiras empaca. Duas regras importam:

Se você quer construir uma IDE no .NET 10, Roslyn é a alavanca. WinForms é só a camada de transporte para pixels e cliques. O nível de qualidade está em o seu loop de edição continuar incremental sob pressão.

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Voltar