9.3 KiB
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
- 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<u32> for FileIdeFrom<FileId> for u32.
- Padronizar uso cross-crate:
prometeu-compiler/frontends/pbs/ast: trocarNodeIdlocal paraprometeu_analysis::NodeId.prometeu-compiler/analysis/symbols: trocarSymbolIdlocal paraprometeu_analysis::SymbolId.prometeu-compiler/analysis/types: trocarTypeIdlocal paraprometeu_analysis::TypeId.- Onde houver
usize/u32cru representando file/module/symbol/type/node: substituir.
-
Trocar
Symbol.module: u32→ModuleId. -
Interner (
NameId):
- Garantir que o interner existente retorna
NameIddo módulo unificado. - Se existirem
NameIdduplicados 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 -qno workspace. -
Teste unitário novo em
prometeu-analysis:ids_are_repr_transparent_and_hashable()(checasize_of::<FileId>() == 4etc.).
-
Teste de compilação indireto: build de
prometeu-compilersem warnings de tipos duplicados.
Notas de implementação
- Evitar circular dependency:
prometeu-analysisdeve ser “baixo nível”. Se o compiler já depende dele, ok. - Se
prometeu-analysisnã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(comFileId)prometeu-compiler::common::spans::Span(comfile_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::Spano span canônico do projeto. - Remover/aposentar
prometeu-compiler::common::spans::Span. - Garantir que todo span carregue
FileId, e nãousize.
Escopo / Mudanças
- Definir
Spancanônico (se já existe, reforçar):
-
pub struct Span { pub file: FileId, pub start: u32, pub end: u32 } -
start/endem bytes (u32),endexclusivo. -
Helpers:
Span::new(file, start, end)Span::len()Span::contains(byte)
- Migrar compiler para usar Span canônico:
- Parser: todos os nós AST devem carregar spans canônicos.
- Diagnostics:
Diagnostic.spandeve ser canônico. - Resolver/Symbols:
Symbol.decl_spandeve ser canônico. - RefIndex: deve usar
Spancanônico.
- Matar o
file_id: usize:
- Onde havia
usize, trocar porFileId. - Nas arenas indexadas por
Vec, converter no ponto de acesso:file.0 as usize.
- Adapters temporários (se necessário)
- Se houver muitos pontos que esperam o Span antigo, criar
type OldSpan = Spanpor 1 PR (somente dentro do compiler), e remover no fim da PR.
Testes de aceite
-
cargo test -qno workspace. -
Teste novo:
span_end_is_exclusive()diagnostic_span_is_valid_for_file()(validaend>=starteend<=text.len()em um fixture simples).
Critérios de “done”
- Não existe mais
prometeu-compiler::common::spans::Span(ou estádeprecatede sem uso). - Qualquer
Spando 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:
Spanem bytes.- O índice baseado no conteúdo atual do arquivo.
Escopo / Mudanças
- Introduzir
TextIndexemprometeu-analysis(ouprometeu-lspse você quiser limitar ao LSP; mas recomendo emanalysispois será útil para debug map e tooling):
-
Arquivo sugerido:
crates/prometeu-analysis/src/text_index.rs -
Estrutura:
line_starts: Vec<u32>(byte offsets)line_utf16_lens: Vec<u32>(opcional cache)
- API mínima:
TextIndex::new(text: &str) -> Selffn byte_to_lsp(&self, byte: u32) -> (u32 /*line*/, u32 /*utf16_col*/)fn lsp_to_byte(&self, line: u32, utf16_col: u32) -> u32
- Algoritmo
-
line_startscalculado por varredura de\n. -
Para conversão de col:
-
pegar o slice da linha (
&text[line_start..line_end]) -
iterar
char_indices(), acumulando:byte_poseutf16_count += ch.len_utf16()
-
parar quando:
byte_pos >= target_byte(byte_to_lsp)utf16_count >= target_utf16(lsp_to_byte)
-
- 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.
- Integração
- Por enquanto, não mexer no LSP server.
- Apenas oferecer API em
analysispara 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
ProjectIdcomo chave em vez de string.
Escopo / Mudanças
-
Definir
ProjectId(u32)(já definido na PR-R1) e agora adotar. -
Criar um registry estável (no analysis/resolver layer):
-
ProjectRegistry:by_name: HashMap<ProjectKey, ProjectId>projects: Vec<ProjectMeta>
-
ProjectKeypode ser:{ name: SmolStr, version: Option<SmolStr> }ou{ name, version }
- Ajustar estruturas existentes para carregar
ProjectId
-
ModuleRef/ModulePath/ResolvedModuledevem apontar paraProjectId. -
symbols.jsonwriter/reader:- Manter
project: "sdk"no JSON (formato externo), mas internamente mapear paraProjectId.
- Manter
- 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
idaos lugares críticos (resolver, module index, symbols export)
- manter
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
Stringpara identificar projeto no core; usarProjectId. - Persistência (symbols.json) continua legível e compatível.
Ordem recomendada de merge (para minimizar conflitos)
- PR-R1 (IDs)
- PR-R2 (Span)
- PR-R3 (TextIndex)
- 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 testpassa