diff --git a/docs/roadmaps/lsp/LSP - base PRs.md b/docs/roadmaps/lsp/LSP - base PRs.md index e69de29b..e3f52eb9 100644 --- a/docs/roadmaps/lsp/LSP - base PRs.md +++ b/docs/roadmaps/lsp/LSP - base PRs.md @@ -0,0 +1,537 @@ +# PR — lsp-base (LSP MVP) + +**Branch:** `pr-08-lsp-mvp` + +## Briefing + +Queremos um **LSP mínimo funcional** que já permita trabalhar PBS no VSCode com: + +* erros aparecendo (diagnostics) +* navegação básica (goto definition) +* visão estrutural (document/workspace symbols) + +Regras-chave (MVP): + +* `didChange` será **full-text** +* rebuild será **coarse** (recompila o projeto inteiro) sempre que qualquer arquivo muda +* sem incremental analysis ainda +* comentários extensivos com exemplos se necessário e em inglês sempre + +Este PR não inclui semantic tokens nem completion (virão nos PRs seguintes). + +--- + +## Alvo (Features) + +### LSP endpoints + +* ✅ `initialize` +* ✅ `textDocument/didOpen` +* ✅ `textDocument/didChange` (FULL) +* ✅ `textDocument/didClose` +* ✅ `textDocument/documentSymbol` +* ✅ `workspace/symbol` +* ✅ `textDocument/definition` +* ✅ `textDocument/publishDiagnostics` + +### Modelo de build + +* `AnalysisDb` em memória +* `FileDb` (uri → texto) +* `rebuild()` recompila **workspace inteiro** e produz um snapshot + +--- + +## Design (como deve funcionar) + +### 1) AnalysisDb: estado e snapshot + +**Objetivo:** separar “estado de arquivos” de “resultado de análise”. + +Estruturas recomendadas: + +```rust +pub struct AnalysisDb { + pub file_db: FileDb, + pub revision: u64, + pub active_rebuild: Option, + pub last_good: Option, +} + +pub struct AnalysisSnapshot { + pub diagnostics: Vec, + pub symbols: SymbolIndex, // index por Project/Module (coarse) + pub node_to_symbol: NodeToSymbol, // para definition + pub def_index: DefIndex, // opcional se você já tiver + pub ast: AstArena, // opcional (mas útil para nodes) +} +``` + +> Observação: use os tipos reais do seu compiler. O importante é ter um “snapshot” único que o LSP consulta. + +### 2) Fluxo de rebuild + +* `didOpen/didChange`: + + * atualizar `file_db` com texto atual + * incrementar `revision` + * disparar `request_rebuild()` (coalescing) + +* `request_rebuild()`: + + * cancela rebuild anterior se houver + * agenda um rebuild assíncrono (tokio task) + * no fim, se não estiver cancelado e se `revision` não mudou, grava `last_good` e publica diagnostics + +### 3) Diagnostics + +* O compiler já deve produzir diagnostics com `Span { file, start, end }` em bytes. +* Para publicar, converter: + + * `Span` → `lsp_types::Range` via `TextIndex` (já existe do refactor) + +Regra: + +* Diagnóstico sem `Span` (ou span inválido) deve ser **ignorado** ou degradado para range 0..0. + +### 4) Definition + +Ao receber `textDocument/definition`: + +1. converter `Position` → byte offset (com `TextIndex`) +2. encontrar `NodeId` no ponto +3. resolver `NodeId -> SymbolId` via `node_to_symbol` +4. pegar `Symbol.decl_span` +5. converter `decl_span` → `Location` + +> Se `NodeId` não existir ou não resolver símbolo: retornar `None`. + +### 5) documentSymbol / workspaceSymbol + +* `documentSymbol`: filtrar símbolos cujo `decl_span.file` == arquivo da request. +* `workspaceSymbol`: busca textual simples (contains/case-insensitive) nos nomes de símbolos (coarse) e retorna top N. + +--- + +## Tarefas de implementação (checklist técnico) + +### Capabilities em `initialize` + +Declarar no server: + +* `textDocumentSync: Full` +* `definitionProvider: true` +* `documentSymbolProvider: true` +* `workspaceSymbolProvider: true` +* `diagnosticProvider`: use publishDiagnostics (push) + +### didOpen + +* upsert texto +* request_rebuild + +### didChange (Full) + +* receber texto completo do doc +* upsert texto +* request_rebuild + +### didClose + +* remover do file_db (ou marcar fechado) +* publicar diagnostics vazios para o arquivo fechado + +### Conversions + +* `uri <-> FileId`: FileDb precisa mapear URI para FileId estável. +* `Span -> Range`: usar `TextIndex` do texto atual do arquivo. +* `Position -> byte`: usar `TextIndex`. + +### Node lookup + +Você precisa de uma função no snapshot (ou util) tipo: + +* `fn node_at(file: FileId, byte: u32) -> Option` + +MVP aceitável: + +* se você ainda não tiver um índice de nodes por span, pode: + + * usar AST arena e fazer busca linear na árvore (aceitável no coarse MVP) + +--- + +## Test Harness (mínimo, mas real) + +### Opção A (preferida): tests com `tower-lsp` client in-process + +Criar um teste `tests/lsp_mvp.rs`: + +* sobe o backend LSP em memória +* envia: + + * `initialize` + * `didOpen` com um fixture PBS (2 arquivos) + * espera `publishDiagnostics` (pode interceptar via `Client` mock) + * chama `definition` em uma posição de uso e verifica que retorna `Location` do `decl_span` + +**Aceite:** + +* definition retorna arquivo correto +* range bate com span convertido + +### Opção B (mais simples): unit tests nos adaptadores + +Se o harness LSP demorar, pelo menos criar: + +* `span_to_range_uses_utf16()` +* `position_to_byte_roundtrip()` +* `definition_resolves_to_decl_span()` usando snapshot fake. + +--- + +## Checklist de aceite (obrigatório) + +* [ ] `cargo test -q` passa no workspace +* [ ] VSCode: abrir arquivo `.pbs` mostra diagnostics (pelo menos 1 erro sintático) +* [ ] VSCode: `Go to Definition` funciona para símbolo resolvido +* [ ] VSCode: Outline mostra `documentSymbol` +* [ ] Mudanças em um arquivo disparam rebuild e atualizam diagnostics +* [ ] Unicode: diagnostics não ficam “desalinhados” (teste manual com `ação`/emoji) + +--- + +## Fora de escopo (explicitamente) + +* semantic tokens +* completion +* references/rename +* hover/signatureHelp +* incremental analysis e cancelation avançada + +--- + +# PR — lsp-hightlight-base (Semantic Tokens — lexer-first) + +**Branch:** `pr-12a-lsp-semantic-lexer` + +## Briefing + +Queremos **highlight no VSCode via LSP**, sem depender de resolver e sem TextMate. + +Estratégia: + +* Implementar `textDocument/semanticTokens/full`. +* Gerar tokens **lexer-first**: comments/strings/numbers/keywords/identifiers. +* Converter spans (bytes) para LSP positions (UTF-16) usando `TextIndex`. +* comentários extensivos com exemplos se necessário e em inglês sempre + +Isso entrega um highlight “bom o suficiente” e muito estável, mesmo com arquivo com erro de parse. + +--- + +## Alvo (Features) + +* ✅ `textDocument/semanticTokens/full` +* ✅ `SemanticTokensLegend` consistente +* ✅ tokens derivados do lexer + +Opcional (não obrigatório neste PR): + +* `semanticTokens/range` +* `semanticTokens/full/delta` + +--- + +## Design + +### 1) Legend fixa + +Escolher um conjunto pequeno de token types: + +* `comment` +* `string` +* `number` +* `keyword` +* `operator` (opcional) +* `variable` (para identifiers genéricos) + +> Não inventar muitos tipos agora; fácil expandir depois. + +### 2) Fonte de tokens + +Implementar uma função no analysis/compiler layer (ou no lsp crate) que, dado: + +* `FileId` +* `text: &str` + retorna `Vec<(Span, TokenType, TokenModifiers)>`. + +**Importante:** + +* Spans são em bytes. +* Devem ser **não sobrepostos** e preferencialmente em ordem. + +### 3) Conversão para formato LSP (deltas) + +LSP semantic tokens usa encoding em deltas: + +* `deltaLine`, `deltaStartChar`, `length`, `tokenType`, `tokenModifiers` + +Algoritmo: + +1. Converter `Span.start` e `Span.end` em `(line, utf16_col)`. +2. Calcular `length` em UTF-16 units para o trecho (start..end). +3. Ordenar por `(line, col)`. +4. Emitir deltas. + +Regra: + +* Se `Span` cruza linhas, **quebrar** em múltiplos tokens por linha (MVP seguro). + +### 4) Robustez + +* Token inválido (end < start, ou fora do texto) deve ser ignorado. +* Se o arquivo não estiver no `FileDb`, retornar tokens vazios. + +--- + +## Tarefas de implementação + +### Server capabilities + +No `initialize`, anunciar: + +* `semanticTokensProvider` com: + + * `legend` + * `full: true` + * `range: false` (por enquanto) + +### Handler + +Implementar `semantic_tokens_full(params)`: + +* pegar `uri` +* buscar texto no `file_db` +* gerar tokens do lexer +* converter com `TextIndex` +* retornar `SemanticTokensResult::Tokens` + +### Lexer tokens + +Se você já tem lexer com spans: + +* mapear tokens para os tipos (keyword/string/comment/number/identifier) +* keywords: pode ser por enum do token ou por tabela + +Se o lexer não marca keyword vs ident: + +* fallback: parse por string e classifica keywords via `HashSet<&'static str>`. + +--- + +## Testes + +### Unit tests (obrigatórios) + +1. `semantic_tokens_legend_is_stable()` + +* garante que legend não muda sem intenção. + +2. `semantic_tokens_are_sorted_and_non_negative()` + +* gera tokens em um fixture com 2 linhas +* valida que deltas nunca ficam negativos e que ordem é válida. + +3. `semantic_tokens_unicode_length_utf16()` + +* texto com `ação🙂` +* valida que `length` corresponde a UTF-16 (emoji conta como 2). + +### Teste manual (aceite) + +* Abrir `.pbs` no VSCode +* Verificar: + + * strings e comentários coloridos + * keywords coloridas + * números coloridos + * identifiers coloridos (mesmo que genérico) + +--- + +## Checklist de aceite + +* [ ] `cargo test -q` passa +* [ ] VSCode: arquivos PBS ficam coloridos via LSP (sem TextMate) +* [ ] Unicode não quebra offsets +* [ ] Arquivo com erro de parse ainda tem highlight (lexer-first) + +--- + +## Fora de escopo + +* semantic tokens semântico (type vs fn vs var) — virá em `PR-12b` +* `range`/`delta` +* completion + +--- + +# PR — lsp-completion-base (Completion mínimo) + +**Branch:** `pr-11a-lsp-completion-min` + +## Briefing + +Queremos autocomplete **mínimo mas útil** para conseguir escrever SDK/ECS em PBS sem fricção. + +Princípio: + +* Não depende de scope facts, nem type facts. +* Usa somente: + + * keywords/builtins + * símbolos top-level do módulo atual + * exports/imports visíveis no projeto (coarse) + +Isso é suficiente para começar a programar e evoluir o LSP depois. + +* comentários extensivos com exemplos se necessário e em inglês sempre + +--- + +## Alvo (Features) + +* ✅ `textDocument/completion` +* ✅ `completionItem/resolve` (opcional; pode retornar item já completo) + +--- + +## Design + +### 1) Buckets e ordenação + +Retornar `CompletionList` com itens nesta prioridade: + +1. Keywords (`fn`, `let`, `mutate`, `declare`, `struct`, `storage`, `if`, `else`, `for`, `return`, etc.) +2. Builtins/funções globais (ex.: `alloc`, `box`, `unbox`, `range`, etc. — conforme spec do PBS) +3. Símbolos do módulo atual (top-level) +4. Símbolos “workspace visible” (exports/imports), limitado (ex.: top 200) + +**Regra de ranking simples:** + +* locals (não teremos) > módulo atual > workspace + +### 2) Contexto + +Para completion mínimo, só precisamos: + +* `uri` do documento +* `position` (para pegar prefixo) + +Prefixo: + +* converter `Position` -> byte +* extrair texto até o cursor e identificar o “token parcial” (regex simples `[A-Za-z_][A-Za-z0-9_]*$`) + +### 3) Fonte dos símbolos + +Usar o `AnalysisSnapshot` (produzido na PR-08) para expor: + +* `fn symbols_in_file(file: FileId) -> Vec` +* `fn symbols_in_module(project: ProjectId, module: ModuleId) -> Vec` +* `fn workspace_symbols() -> impl Iterator` + +MVP aceitável: + +* `workspace_symbols()` pode ser um vetor “flatten” pré-calculado no snapshot. + +### 4) CompletionItem + +Mapeamento para `CompletionItemKind`: + +* functions -> `FUNCTION` +* types -> `CLASS` (ou `STRUCT` se quiser) +* modules -> `MODULE` +* variables/constants -> `VARIABLE` / `CONSTANT` + +Campos: + +* `label`: nome +* `detail`: (opcional) `"fn"/"type"/"module"` +* `sortText`: prefixado para impor bucket (ex.: `"1_"`, `"2_"`) +* `filterText`: label + +--- + +## Tarefas de implementação + +### Capabilities + +No `initialize`: + +* `completionProvider: { resolveProvider: false, triggerCharacters: [".", ":"]? }` + +> Para completion mínimo, nem precisa trigger chars. Pode deixar default. + +### Handler `completion` + +* buscar texto e `TextIndex` +* calcular prefixo +* montar lista de candidatos por bucket +* filtrar por prefixo (case-sensitive ou insensitive; escolha e documente) +* limitar tamanho +* retornar `CompletionResponse::List` + +### Builtins/keywords + +Criar tabelas estáticas em `prometeu-lsp`: + +* `static KEYWORDS: &[&str] = ...` +* `static BUILTINS: &[&str] = ...` + +--- + +## Testes + +### Unit tests (obrigatórios) + +1. `completion_extracts_prefix()` + +* valida regex de prefixo + +2. `completion_includes_keywords()` + +* chama handler com texto vazio e posição 0 +* garante que `fn` aparece + +3. `completion_filters_by_prefix()` + +* prefixo `"ra"` deve sugerir `range` (se builtin) + +4. (se possível) `completion_includes_module_symbols()` + +* usando snapshot fake ou fixture compilado pela infra da PR-08 + +### Teste manual (aceite) + +* Abrir arquivo PBS e digitar `ra` -> aparece `range` +* Digitar nome de tipo/fn do SDK -> aparece suggestion + +--- + +## Checklist de aceite + +* [ ] `cargo test -q` passa +* [ ] VSCode: autocomplete sugere keywords e builtins +* [ ] VSCode: autocomplete sugere símbolos do módulo atual +* [ ] Não trava com rebuild coarse + +--- + +## Fora de escopo + +* locals em scope +* members (`obj.`) +* signatureHelp +* hover diff --git a/docs/roadmaps/lsp/LSP - roadmap remain.md b/docs/roadmaps/lsp/LSP - roadmap remain.md index e69de29b..f502768e 100644 --- a/docs/roadmaps/lsp/LSP - roadmap remain.md +++ b/docs/roadmaps/lsp/LSP - roadmap remain.md @@ -0,0 +1,219 @@ +# LSP Roadmap — do “base usable” até “LSP completo” + +> Este documento é o mapa incremental pós `lsp-base`, `lsp-hightlight-base`, `lsp-completion-base`. +> A ideia é manter o LSP evoluindo sem rework, enquanto SDK/ECS/Packer avançam em paralelo. + +--- + +## Estado atual (após os 3 PRs base) + +### ✅ Já temos + +* Diagnostics (publishDiagnostics) +* Definition (goto definition) +* DocumentSymbol / WorkspaceSymbol +* Semantic tokens (lexer-first) +* Completion mínimo (keywords/builtins/top-level/module/workspace) + +### ❌ Ainda não temos + +* references / rename +* hover / signatureHelp +* completion com locals em scope +* semantic tokens semântico (type vs fn vs var) +* incremental analysis real (por arquivo / cancelation) +* debug map pc→span integrado com traps +* code actions / formatting + +--- + +## Próximos PRs recomendados (ordem sugerida) + +## PR-09 — References + Rename (seguro) + +### Objetivo + +Habilitar `references`, `prepareRename`, `rename` com regras de segurança. + +### Pré-requisitos + +* `RefIndex` completo (SymbolId -> usos em spans) +* `NodeToSymbol` estável +* `TriviaIndex` (comments/strings) para não renomear spans proibidos + +### Regras de segurança + +* Não renomear símbolo não-resolvido +* Não renomear em comentário/string +* Renomear deve gerar `WorkspaceEdit` apenas para spans válidos + +### Testes + +* Fixture com 2 arquivos: rename atualiza todas as refs +* Fixture: rename em comentário não altera nada + +--- + +## PR-10 — Hover + SignatureHelp + +### Objetivo + +* `hover`: mostrar tipo + docstring (docstring opcional) +* `signatureHelp`: para call nodes + +### Pré-requisitos + +* Type facts básicos: `NodeId -> TypeId` +* Modelo de assinatura: para functions (params, retorno) + +### Testes + +* Hover em símbolo mostra tipo +* SignatureHelp em chamada mostra params + +--- + +## PR-11b — Completion com locals em scope + +### Objetivo + +Autocomplete útil para código real: + +* locals/params +* shadowing correto + +### Pré-requisitos + +* Binder/Scope facts: + + * `Position -> ScopeId` + * `ScopeId -> bindings (name -> SymbolId/TypeId)` + +### Testes + +* Dentro de bloco, sugere variável local +* Shadowing: sugere a mais interna + +--- + +## PR-12b — Semantic tokens resolver-backed (semântico) + +### Objetivo + +Melhorar highlight: + +* diferenciar `type` vs `function` vs `variable` vs `parameter` vs `module` + +### Pré-requisitos + +* `NodeAtPosition` confiável +* `NodeToSymbol` confiável +* `SymbolKind` consistente + +### Testes + +* Identificador de tipo recebe token `type` +* Função recebe token `function` + +--- + +## PR-13 — Formatting + Code Actions (opcional) + +### Objetivo + +* `textDocument/formatting` (nem que seja formatter simples/estável) +* code actions básicas: + + * organizar imports + * criar stub de função/struct + +### Pré-requisitos + +* AST pretty printer (ou formatter incremental) + +--- + +## PR-14 — Incremental analysis + cancelation (de verdade) + +### Objetivo + +Sair do rebuild coarse: + +* recompilar somente arquivo/módulo afetado +* cancelamento real de builds longos +* snapshots por versão + +### Pré-requisitos + +* Graph de dependências por módulo +* Cache de parse/resolve por arquivo +* `revision` por doc + +### Testes + +* Editar arquivo A não recompila projeto inteiro +* Cancel: digitar rápido não aplica resultados velhos + +--- + +## PR-15 — Debug map (pc→span) + integração com traps + +### Objetivo + +Quando a VM gerar trap/runtime error: + +* mapear `pc` para `Span` +* mostrar erro com localização exata + +### Pré-requisitos + +* SourceMap gerado no backend bytecode +* runtime expõe `pc`/contexto + +### Testes + +* programa que gera trap aponta para linha/col corretos + +--- + +# Backlog adicional (nice to have) + +* `workspace/didChangeWatchedFiles` (reagir a mudanças fora do editor) +* `textDocument/codeLens` (ex.: run test / run main) +* `textDocument/inlayHint` +* `workspace/diagnostic` pull-mode (se quiser) +* semanticTokens `range` e `delta` para performance + +--- + +## Interação com SDK/ECS/Packer + +### Com os 3 PRs base, você já consegue: + +* escrever SDK/ECS em PBS com feedback imediato +* navegar rapidamente pelo código +* manter arquivos grandes com highlight +* usar completion para nomes de APIs + +### Enquanto isso, em paralelo: + +* packer pode evoluir (não depende do LSP) +* quando packer estabilizar, podemos adicionar code actions/codelens para “build/pack/run” direto no VSCode + +--- + +## Definição de “LSP completo” (para Prometeu view-ready) + +Para chamar de "completo" (na sua visão de produto): + +* ✅ diagnostics +* ✅ definition +* ✅ symbols +* ✅ highlight semântico +* ✅ completion com scope + members +* ✅ hover + signatureHelp +* ✅ references + rename +* ✅ incremental + cancel +* ✅ debug map integrado com traps + +> A ordem acima é incremental por valor percebido e por dependências.