## PR-R1 — IDs padronizados (newtypes) em um único lugar **Branch:** `pr-r1-ids-newtypes` ### Briefing Hoje existem IDs espalhados entre crates (`FileId`, `NameId`, `NodeId`, `SymbolId`, `TypeId`) e alguns campos ainda usam `u32`/`usize` cru (ex.: `Symbol.module: u32`). Para LSP, precisamos de IDs consistentes para indexação, caches, spans e cross-crate APIs. ### Alvo Centralizar e padronizar os seguintes IDs (newtypes): * `FileId(u32)` * `NodeId(u32)` * `NameId(u32)` * `SymbolId(u32)` * `TypeId(u32)` * `ModuleId(u32)` * `ProjectId(u32)` *(ver PR-R4 para adoção total; aqui é apenas definição + plumbing mínimo se necessário)* **Definição única** em `prometeu-analysis` (ou um crate novo `prometeu-ids`, se você preferir isolar): * Arquivo sugerido: `crates/prometeu-analysis/src/ids.rs` * Exportar via `pub mod ids; pub use ids::*;` ### Escopo / Mudanças 1. **Criar o módulo de IDs** com: * `#[repr(transparent)] pub struct FileId(pub u32);` etc. * `Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug`. * Helpers: * `impl FileId { pub const INVALID: FileId = FileId(u32::MAX); }` (opcional) * `impl From for FileId` e `From for u32`. 2. **Padronizar uso cross-crate**: * `prometeu-compiler/frontends/pbs/ast`: trocar `NodeId` local para `prometeu_analysis::NodeId`. * `prometeu-compiler/analysis/symbols`: trocar `SymbolId` local para `prometeu_analysis::SymbolId`. * `prometeu-compiler/analysis/types`: trocar `TypeId` local para `prometeu_analysis::TypeId`. * Onde houver `usize`/`u32` cru representando file/module/symbol/type/node: substituir. 3. **Trocar `Symbol.module: u32` → `ModuleId`**. 4. **Interner (`NameId`)**: * Garantir que o interner existente retorna `NameId` do módulo unificado. * Se existirem `NameId` duplicados em crates diferentes, remover e apontar para o único. ### Regras de compatibilidade (para não quebrar tudo de uma vez) * Se algum ponto ainda depende de `usize`, oferecer funções auxiliares **temporárias**: * `fn as_usize(self) -> usize` (somente se realmente necessário) * Preferir converter na borda (ex.: índices de `Vec`). ### Testes de aceite * `cargo test -q` no workspace. * Teste unitário novo em `prometeu-analysis`: * `ids_are_repr_transparent_and_hashable()` (checa `size_of::() == 4` etc.). * Teste de compilação indireto: build de `prometeu-compiler` sem warnings de tipos duplicados. ### Notas de implementação * Evitar circular dependency: `prometeu-analysis` deve ser “baixo nível”. Se o compiler já depende dele, ok. * Se `prometeu-analysis` não puder depender do compiler (não deve), manter IDs neutros e reutilizáveis. --- ## PR-R2 — Span unificado + FileId consistente em todo pipeline **Branch:** `pr-r2-span-unify` ### Briefing Hoje existem dois tipos de `Span`: * `prometeu-analysis::Span` (com `FileId`) * `prometeu-compiler::common::spans::Span` (com `file_id: usize`) Para LSP, diagnostics/definition/symbols precisam de um único modelo de span para conversão consistente para `Location/Range`. A spec aponta spans como **byte offsets**, `end` exclusivo, e file id deve ser estável. (PBS Implementation Spec / Diagnostic specs) ### Alvo * Tornar `prometeu-analysis::Span` o **span canônico** do projeto. * Remover/aposentar `prometeu-compiler::common::spans::Span`. * Garantir que **todo span carregue `FileId`**, e não `usize`. ### Escopo / Mudanças 1. **Definir `Span` canônico** (se já existe, reforçar): * `pub struct Span { pub file: FileId, pub start: u32, pub end: u32 }` * `start/end` em bytes (u32), `end` exclusivo. * Helpers: * `Span::new(file, start, end)` * `Span::len()` * `Span::contains(byte)` 2. **Migrar compiler para usar Span canônico**: * Parser: todos os nós AST devem carregar spans canônicos. * Diagnostics: `Diagnostic.span` deve ser canônico. * Resolver/Symbols: `Symbol.decl_span` deve ser canônico. * RefIndex: deve usar `Span` canônico. 3. **Matar o `file_id: usize`**: * Onde havia `usize`, trocar por `FileId`. * Nas arenas indexadas por `Vec`, converter no ponto de acesso: `file.0 as usize`. 4. **Adapters temporários (se necessário)** * Se houver muitos pontos que esperam o Span antigo, criar `type OldSpan = Span` por 1 PR (somente dentro do compiler), e remover no fim da PR. ### Testes de aceite * `cargo test -q` no workspace. * Teste novo: * `span_end_is_exclusive()` * `diagnostic_span_is_valid_for_file()` (valida `end>=start` e `end<=text.len()` em um fixture simples). ### Critérios de “done” * Não existe mais `prometeu-compiler::common::spans::Span` (ou está `deprecated` e sem uso). * Qualquer `Span` do pipeline é `prometeu-analysis::Span`. --- ## PR-R3 — TextIndex/LineIndex correto para LSP (UTF-16) + conversões **Branch:** `pr-r3-text-index-utf16` ### Briefing O LSP usa `Position.character` em **UTF-16 code units** (não bytes). Hoje o `LineIndex` calcula coluna como *byte offset* na linha. Em arquivos com Unicode (acentos), diagnostics e goto definition ficam desalinhados. Queremos: * Manter o core do compilador em **byte offsets** (spec). * Converter **somente na borda** (LSP e ferramentas). ### Alvo Criar um índice de texto (por arquivo) que suporte: * `byte_offset -> (line, utf16_col)` * `(line, utf16_col) -> byte_offset` E manter: * `Span` em bytes. * O índice baseado no **conteúdo atual** do arquivo. ### Escopo / Mudanças 1. Introduzir `TextIndex` em `prometeu-analysis` (ou `prometeu-lsp` se você quiser limitar ao LSP; mas recomendo em `analysis` pois será útil para debug map e tooling): * Arquivo sugerido: `crates/prometeu-analysis/src/text_index.rs` * Estrutura: * `line_starts: Vec` (byte offsets) * `line_utf16_lens: Vec` (opcional cache) 2. API mínima: * `TextIndex::new(text: &str) -> Self` * `fn byte_to_lsp(&self, byte: u32) -> (u32 /*line*/, u32 /*utf16_col*/)` * `fn lsp_to_byte(&self, line: u32, utf16_col: u32) -> u32` 3. Algoritmo * `line_starts` calculado por varredura de `\n`. * Para conversão de col: * pegar o slice da linha (`&text[line_start..line_end]`) * iterar `char_indices()`, acumulando: * `byte_pos` e `utf16_count += ch.len_utf16()` * parar quando: * `byte_pos >= target_byte` (byte_to_lsp) * `utf16_count >= target_utf16` (lsp_to_byte) 4. Testes fortes com Unicode * Casos: `"aé🙂b"` (emoji e acento). * Validar round-trip: * `byte == lsp_to_byte(byte_to_lsp(byte))` para bytes em fronteira de char. 5. Integração * Por enquanto, **não** mexer no LSP server. * Apenas oferecer API em `analysis` para o LSP consumir na PR-08. ### Testes de aceite * `cargo test -q`. * Testes novos em `prometeu-analysis`: * `text_index_ascii_roundtrip()` * `text_index_unicode_roundtrip_utf16()` --- ## PR-R4 — ProjectId padronizado + modelagem de Project/Module estável **Branch:** `pr-r4-project-id` ### Briefing Hoje o resolver trabalha com `Project { name, version }` e o `symbols.json` contém projects e símbolos agrupados por projeto. Para LSP e para incremental analysis, queremos IDs estáveis e leves para: * mapear `uri -> FileId -> (ProjectId, ModuleId)` * armazenar caches por projeto * suportar workspace com múltiplos projetos no futuro Você pediu explicitamente incluir `ProjectId(u32)` nesta série. ### Alvo Introduzir `ProjectId(u32)` e plugar no modelo de resolução/linking: * Cada projeto carregado/descoberto no workspace recebe `ProjectId`. * Mapas centrais usam `ProjectId` como chave em vez de string. ### Escopo / Mudanças 1. Definir `ProjectId(u32)` (já definido na PR-R1) e agora **adotar**. 2. Criar um registry estável (no analysis/resolver layer): * `ProjectRegistry`: * `by_name: HashMap` * `projects: Vec` * `ProjectKey` pode ser: * `{ name: SmolStr, version: Option }` ou `{ name, version }` 3. Ajustar estruturas existentes para carregar `ProjectId` * `ModuleRef` / `ModulePath` / `ResolvedModule` devem apontar para `ProjectId`. * `symbols.json` writer/reader: * Manter `project: "sdk"` no JSON (formato externo), mas internamente mapear para `ProjectId`. 4. Integração mínima (sem LSP ainda) * `AnalysisDb` (ou equivalente) deve conseguir responder: * `fn project_for_file(file: FileId) -> ProjectId` ### Estratégia para não explodir o diff * Não reescrever o mundo: * manter `ProjectMeta { id: ProjectId, name, version }` * adicionar `id` aos lugares críticos (resolver, module index, symbols export) ### Testes de aceite * `cargo test -q`. * Teste novo: * `project_registry_stable_ids_for_same_key()` * `symbols_json_roundtrip_preserves_project_grouping()` (se houver infra de roundtrip) ### Critérios de “done” * Nenhum mapa central chaveado por `String` para identificar projeto no core; usar `ProjectId`. * Persistência (symbols.json) continua legível e compatível. --- # Ordem recomendada de merge (para minimizar conflitos) 1. PR-R1 (IDs) 2. PR-R2 (Span) 3. PR-R3 (TextIndex) 4. PR-R4 (ProjectId) > Depois disso, a PR-08 (LSP MVP) fica bem menor: o LSP só consome `Span` + `TextIndex` + IDs. --- # Checklist global (pré-PR-08) * [ ] IDs unificados e usados em todos os crates * [ ] Span único, sempre com `FileId`, e offsets em bytes * [ ] TextIndex com conversão UTF-16 confiável (testado) * [ ] ProjectId adotado no resolver/modelo de projeto * [ ] Workspace compila e `cargo test` passa