prepare prs
This commit is contained in:
parent
f9120e740b
commit
747ac81da2
@ -1,10 +1,39 @@
|
||||
# PRs for Junie — Compiler Dependency Resolution & Linking Pipeline
|
||||
## Visão de arquitetura alvo
|
||||
|
||||
> Goal: Move dependency resolution + linking orchestration into **prometeu_compiler** so that the compiler produces a **single fully-linked bytecode blob**, and the VM/runtime only **loads + executes**.
|
||||
### Camadas
|
||||
|
||||
## Non-goals (for this PR set)
|
||||
1. **FileDB**: texto, URI/path, line-index, snapshots.
|
||||
2. **Lexer/Parser**: produz **AstArena** (NodeId + spans por nó).
|
||||
3. **Binder/Resolver**: produz **SymbolArena** + índices (def/ref).
|
||||
4. **Typecheck**: produz **TypeArena** + facts (node→type, symbol→type).
|
||||
5. **Analysis Export**: `analysis.json` (full) + `symbols.json` (leve e estável).
|
||||
6. **LSP Server**: consome `AnalysisDb` e responde requests.
|
||||
|
||||
* No lockfile format (yet)
|
||||
* No registry (yet)
|
||||
* No advanced SAT solver: first iteration is deterministic and pragmatic
|
||||
* No incremental compilation (yet)
|
||||
### IDs padronizados (newtypes)
|
||||
|
||||
* `FileId(u32)`
|
||||
* `NodeId(u32)`
|
||||
* `NameId(u32)` (interner)
|
||||
* `SymbolId(u32)`
|
||||
* `TypeId(u32)`
|
||||
* `ProjectId(u32)`
|
||||
* `ModuleId(u32)`
|
||||
|
||||
### Invariantes
|
||||
|
||||
* AST é **imutável** após construção (normativo na spec PBS).
|
||||
* Nenhuma fase expõe referências diretas entre nós; apenas **IDs**.
|
||||
* IDs externos são aceitos, mas sempre passam por **validate/resolve** (API checked).
|
||||
|
||||
---
|
||||
|
||||
## Regras para Junie (para reduzir vai-e-volta)
|
||||
|
||||
1. Não “inventar design”. Se algo não estiver especificado, **criar TODO** e parar.
|
||||
2. Não mudar formatação/estilo fora do escopo.
|
||||
3. Todo novo tipo público precisa de doc-comment curta e exemplo.
|
||||
4. Toda mudança de JSON precisa:
|
||||
|
||||
* `schema_version` bump (se não for backward)
|
||||
* teste de snapshot
|
||||
5. Cada PR deve deixar `cargo test` verde.
|
||||
@ -0,0 +1,789 @@
|
||||
# Prometeu — PR Plan (Arena-Driven) para um LSP Completo
|
||||
|
||||
> **Meta do Nilton / Junie workflow**: PRs extremamente prescritivos. A Junie só implementa; você revisa o design **antes** do código.
|
||||
>
|
||||
> **Regra de ouro:** cada PR abaixo vem com:
|
||||
>
|
||||
> 1. Arquivos/alvos exatos
|
||||
> 2. Estruturas e assinaturas obrigatórias
|
||||
> 3. Esquemas JSON (quando aplicável)
|
||||
> 4. Testes (golden + unit)
|
||||
> 5. Critérios de aceite “binário” (passou/não passou)
|
||||
|
||||
---
|
||||
|
||||
## Estado atual (confirmado pelo repo)
|
||||
|
||||
Workspace (Archive.zip) tem crates:
|
||||
|
||||
* `prometeu/`
|
||||
* `prometeu-bytecode/`
|
||||
* `prometeu-core/`
|
||||
* `prometeu-compiler/` (bin `src/main.rs`, deps: `serde`, `serde_json`, `anyhow`, `clap`, e família `oxc_*`).
|
||||
|
||||
**Saída atual existente:** `symbols.json` (schema_version 0), exportando símbolos por projeto, com `decl_span` em `line/col` e paths absolutos. (Vamos evoluir isso no PR-07.)
|
||||
|
||||
---
|
||||
|
||||
# Templates prescritivos para PR (usar em TODAS)
|
||||
|
||||
## Template de descrição (copiar/colar)
|
||||
|
||||
* **Motivação:**
|
||||
* **Mudança de modelo de dados:**
|
||||
* **APIs novas / alteradas:**
|
||||
* **Arquivos tocados:**
|
||||
* **Testes adicionados/atualizados:**
|
||||
* **Riscos & rollback:**
|
||||
* **Checklist de aceite (binário):**
|
||||
|
||||
---
|
||||
|
||||
# PRs (detalhados)
|
||||
|
||||
## PR-00 — Infra: crates de análise + lsp (estrutura do workspace)
|
||||
|
||||
**Branch:** `pr-00-analysis-lsp-foundations`
|
||||
|
||||
### Decisão travada (LSP)
|
||||
|
||||
* **Biblioteca escolhida:** `tower-lsp`
|
||||
* **Motivo:** reduzir boilerplate, acelerar MVP e minimizar vai-e-volta. Cancelamento/incremental será feito **por cima** (tasks async + cancel tokens).
|
||||
|
||||
### Objetivo
|
||||
|
||||
Adicionar crates novas sem mexer no pipeline existente.
|
||||
|
||||
### Entregas obrigatórias
|
||||
|
||||
1. **Novo crate:** `prometeu-analysis/`
|
||||
|
||||
* `Cargo.toml` (library)
|
||||
* `src/lib.rs`
|
||||
2. **Novo crate:** `prometeu-lsp/`
|
||||
|
||||
* `Cargo.toml` (bin)
|
||||
* `src/main.rs`
|
||||
3. Workspace root: ajustar `Cargo.toml` (se existir no root) para incluir members.
|
||||
|
||||
### Dependências (fixas)
|
||||
|
||||
* `prometeu-analysis`: `serde`, `serde_json`
|
||||
* `prometeu-lsp`:
|
||||
|
||||
* `tower-lsp = "0.20"` (ou versão estável atual)
|
||||
* `tokio` (full)
|
||||
* `prometeu-analysis` (path)
|
||||
|
||||
### Esqueleto obrigatório (`prometeu-lsp/src/main.rs`)
|
||||
|
||||
```rust
|
||||
use tower_lsp::{LspService, Server};
|
||||
use tokio::sync::RwLock;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Default)]
|
||||
struct AnalysisDb {
|
||||
// FileDB, AstArena, SymbolArena, TypeArena, Diagnostics
|
||||
}
|
||||
|
||||
struct Backend {
|
||||
db: Arc<RwLock<AnalysisDb>>,
|
||||
}
|
||||
|
||||
#[tower_lsp::async_trait]
|
||||
impl tower_lsp::LanguageServer for Backend {
|
||||
async fn initialize(&self, _: tower_lsp::lsp_types::InitializeParams)
|
||||
-> tower_lsp::jsonrpc::Result<tower_lsp::lsp_types::InitializeResult> {
|
||||
Ok(tower_lsp::lsp_types::InitializeResult {
|
||||
capabilities: tower_lsp::lsp_types::ServerCapabilities {
|
||||
text_document_sync: Some(tower_lsp::lsp_types::TextDocumentSyncCapability::Kind(
|
||||
tower_lsp::lsp_types::TextDocumentSyncKind::FULL,
|
||||
)),
|
||||
// Declaramos capacidades desde já para evitar churn posterior.
|
||||
definition_provider: Some(true.into()),
|
||||
document_symbol_provider: Some(true.into()),
|
||||
workspace_symbol_provider: Some(true.into()),
|
||||
hover_provider: Some(true.into()),
|
||||
references_provider: Some(true.into()),
|
||||
rename_provider: Some(tower_lsp::lsp_types::OneOf::Left(true)),
|
||||
completion_provider: Some(tower_lsp::lsp_types::CompletionOptions {
|
||||
resolve_provider: Some(false),
|
||||
trigger_characters: Some(vec![".".into(), ":".into()]),
|
||||
..Default::default()
|
||||
}),
|
||||
semantic_tokens_provider: Some(
|
||||
tower_lsp::lsp_types::SemanticTokensServerCapabilities::SemanticTokensOptions(
|
||||
tower_lsp::lsp_types::SemanticTokensOptions {
|
||||
legend: tower_lsp::lsp_types::SemanticTokensLegend {
|
||||
// preenchido no PR-12
|
||||
token_types: vec![],
|
||||
token_modifiers: vec![],
|
||||
},
|
||||
full: Some(tower_lsp::lsp_types::SemanticTokensFullOptions::Bool(true)),
|
||||
range: None,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
async fn initialized(&self, _: tower_lsp::lsp_types::InitializedParams) {}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let stdin = tokio::io::stdin();
|
||||
let stdout = tokio::io::stdout();
|
||||
let (service, socket) = LspService::new(|_| Backend { db: Arc::new(RwLock::new(AnalysisDb::default())) });
|
||||
Server::new(stdin, stdout, socket).serve(service).await;
|
||||
}
|
||||
```
|
||||
|
||||
### Contrato exato do AnalysisDb (travado)
|
||||
|
||||
> **Regra:** o LSP nunca recalcula segurando `RwLock` por muito tempo. Toda análise pesada roda fora do lock e depois faz swap.
|
||||
|
||||
Criar arquivo: `prometeu-lsp/src/analysis_db.rs`
|
||||
|
||||
```rust
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use prometeu_analysis::FileDB;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AnalysisDb {
|
||||
pub file_db: FileDB,
|
||||
|
||||
// Os campos abaixo serão conectados conforme PR-03/04/05 (podem começar como None)
|
||||
// pub ast: Option<AstArena>,
|
||||
// pub symbols: Option<SymbolArena>,
|
||||
// pub types: Option<TypeArena>,
|
||||
// pub diagnostics: Vec<Diagnostic>,
|
||||
|
||||
/// Incrementa a cada rebuild concluído com sucesso
|
||||
pub revision: u64,
|
||||
|
||||
/// Cancel token do último rebuild em progresso (se houver)
|
||||
pub active_rebuild: Option<CancellationToken>,
|
||||
}
|
||||
|
||||
pub type SharedDb = Arc<RwLock<AnalysisDb>>;
|
||||
```
|
||||
|
||||
No `main.rs`, **substituir** o `AnalysisDb` local pelo import acima (ou manter local e delegar, mas sem duplicar).
|
||||
|
||||
### Modelo de cancelamento e coalescing (travado)
|
||||
|
||||
> Objetivo: quando chegam múltiplos `didChange` em sequência, cancelar o rebuild anterior e rodar apenas o último.
|
||||
|
||||
Adicionar dependência fixa em `prometeu-lsp/Cargo.toml`:
|
||||
|
||||
* `tokio-util`
|
||||
|
||||
Criar arquivo: `prometeu-lsp/src/rebuild.rs`
|
||||
|
||||
```rust
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use crate::analysis_db::SharedDb;
|
||||
|
||||
/// Solicita rebuild do projeto (coarse). Cancela rebuild anterior se em progresso.
|
||||
/// Implementação inicial: apenas cria task e retorna.
|
||||
pub async fn request_rebuild(db: SharedDb) {
|
||||
// 1) lock curto: cancelar token anterior e instalar token novo
|
||||
// 2) spawn task: roda análise fora do lock
|
||||
// 3) lock curto: se token não cancelado, swap estado + revision++
|
||||
}
|
||||
```
|
||||
|
||||
Regras obrigatórias:
|
||||
|
||||
1. `request_rebuild` deve:
|
||||
|
||||
* cancelar `active_rebuild` anterior (se existir)
|
||||
* instalar um token novo
|
||||
* `tokio::spawn` uma task de rebuild
|
||||
2. A task deve checar `token.is_cancelled()` em pontos seguros:
|
||||
|
||||
* antes de começar
|
||||
* após parsing
|
||||
* após resolver
|
||||
* após typecheck
|
||||
3. O lock (`RwLock`) deve ser segurado apenas:
|
||||
|
||||
* para trocar `active_rebuild`
|
||||
* para aplicar o resultado final
|
||||
|
||||
### Critérios de aceite
|
||||
|
||||
* `cargo build -p prometeu-lsp` ok
|
||||
* LSP inicializa em VS Code/Neovim sem crash
|
||||
* Nenhuma feature além do declarado
|
||||
|
||||
---
|
||||
|
||||
## Política fixa de cancelamento e rebuild (tower-lsp)
|
||||
|
||||
### Objetivo
|
||||
|
||||
Garantir que edições rápidas não gerem backlog de análises e evitar race conditions.
|
||||
|
||||
### Decisão travada
|
||||
|
||||
* Usar `tokio_util::sync::CancellationToken`
|
||||
* **Um token por rebuild**
|
||||
* Qualquer `didChange`:
|
||||
|
||||
1. Cancela o rebuild anterior
|
||||
2. Cria novo token
|
||||
3. Agenda nova task de análise
|
||||
|
||||
### Estruturas obrigatórias
|
||||
|
||||
```rust
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
struct AnalysisController {
|
||||
current: Option<CancellationToken>,
|
||||
}
|
||||
```
|
||||
|
||||
### Regras
|
||||
|
||||
* Nunca segurar `RwLock<AnalysisDb>` durante parsing/resolver/typecheck.
|
||||
* Fluxo correto:
|
||||
|
||||
1. Clonar snapshot dos textos (FileDB)
|
||||
2. Soltar lock
|
||||
3. Rodar análise pesada
|
||||
4. Re-adquirir lock e substituir estado
|
||||
* Tasks devem checar `token.is_cancelled()` entre fases (parse / bind / type).
|
||||
|
||||
### Critérios de aceite
|
||||
|
||||
* Digitar rapidamente não acumula tasks.
|
||||
* Apenas o último estado publica diagnostics.
|
||||
|
||||
---
|
||||
|
||||
## Contrato fechado do AnalysisDb (fonte de verdade do LSP)
|
||||
|
||||
### Objetivo
|
||||
|
||||
Eliminar ambiguidade sobre o que vive no estado compartilhado.
|
||||
|
||||
### Estrutura obrigatória
|
||||
|
||||
```rust
|
||||
#[derive(Default)]
|
||||
pub struct AnalysisDb {
|
||||
pub files: FileDB,
|
||||
pub ast: Option<AstArena>,
|
||||
pub symbols: Option<SymbolArena>,
|
||||
pub types: Option<TypeArena>,
|
||||
pub type_facts: Option<TypeFacts>,
|
||||
pub diagnostics: Vec<Diagnostic>,
|
||||
}
|
||||
```
|
||||
|
||||
### Regras
|
||||
|
||||
* `AnalysisDb` **nunca** armazena estado parcialmente válido.
|
||||
* Se uma fase falhar:
|
||||
|
||||
* `ast/symbols/types` podem ficar `None`
|
||||
* `diagnostics` deve estar preenchido
|
||||
* LSP handlers **não** rodam análise: apenas leem `AnalysisDb`.
|
||||
|
||||
### Leitura segura
|
||||
|
||||
* Todos os handlers (`definition`, `hover`, `references`, etc.) devem:
|
||||
|
||||
* adquirir `db.read()`
|
||||
* lidar com `Option::None` retornando `null` / resposta vazia
|
||||
|
||||
### Escrita segura
|
||||
|
||||
* Apenas a task de rebuild escreve no `AnalysisDb`.
|
||||
* Escrita sempre substitui o estado inteiro (swap lógico).
|
||||
|
||||
### Critérios de aceite
|
||||
|
||||
* Nenhum handler pode panic por `None`.
|
||||
* Rebuild substitui estado de forma atômica (do ponto de vista do LSP).
|
||||
|
||||
---
|
||||
|
||||
## PR-01 — FileDB + LineIndex (base do LSP e spans)
|
||||
|
||||
**Branch:** `pr-01-filedb`
|
||||
|
||||
### Objetivo
|
||||
|
||||
Criar uma base de arquivos com IDs estáveis na sessão e conversão offset<->(line,col).
|
||||
|
||||
### Arquivos / módulos
|
||||
|
||||
* `prometeu-analysis/src/file_db.rs`
|
||||
* `prometeu-analysis/src/span.rs`
|
||||
* `prometeu-analysis/src/ids.rs`
|
||||
* `prometeu-analysis/src/lib.rs` (re-exports)
|
||||
|
||||
### Estruturas obrigatórias
|
||||
|
||||
```rust
|
||||
// prometeu-analysis/src/ids.rs
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FileId(pub u32);
|
||||
|
||||
// prometeu-analysis/src/span.rs
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Span {
|
||||
pub file: FileId,
|
||||
pub start: u32, // byte offset
|
||||
pub end: u32, // byte offset, exclusive
|
||||
}
|
||||
|
||||
// prometeu-analysis/src/file_db.rs
|
||||
pub struct FileDB {
|
||||
// map uri->id, id->uri/text/line_index
|
||||
}
|
||||
|
||||
pub struct LineIndex {
|
||||
// stores line start offsets
|
||||
}
|
||||
```
|
||||
|
||||
### Regras
|
||||
|
||||
* Spans são **byte offsets UTF-8** (`start` inclusive, `end` exclusive) — alinhado ao Canonical Addenda.
|
||||
* `LineIndex` deve lidar com `
|
||||
` (LF). (CRLF pode ser normalizado na entrada.)
|
||||
|
||||
### APIs obrigatórias
|
||||
|
||||
```rust
|
||||
impl FileDB {
|
||||
pub fn upsert(&mut self, uri: &str, text: String) -> FileId;
|
||||
pub fn file_id(&self, uri: &str) -> Option<FileId>;
|
||||
pub fn uri(&self, id: FileId) -> &str;
|
||||
pub fn text(&self, id: FileId) -> &str;
|
||||
pub fn line_index(&self, id: FileId) -> &LineIndex;
|
||||
}
|
||||
|
||||
impl LineIndex {
|
||||
pub fn offset_to_line_col(&self, offset: u32) -> (u32, u32);
|
||||
pub fn line_col_to_offset(&self, line: u32, col: u32) -> Option<u32>;
|
||||
}
|
||||
```
|
||||
|
||||
### Testes obrigatórios
|
||||
|
||||
* `tests/file_db_line_index.rs` (unit)
|
||||
|
||||
* roundtrip offset->(l,c)->offset
|
||||
* bordas (start/end, EOF)
|
||||
|
||||
### Critérios de aceite
|
||||
|
||||
* Testes passam.
|
||||
* Nenhuma mudança no output do `prometeu-compiler` ainda.
|
||||
|
||||
---
|
||||
|
||||
## PR-02 — NameInterner (NameId) e eliminação de String no hot path
|
||||
|
||||
**Branch:** `pr-02-name-interner`
|
||||
|
||||
### Objetivo
|
||||
|
||||
Trocar identificadores em AST/resolver de `String` para `NameId`.
|
||||
|
||||
### Arquivos
|
||||
|
||||
* `prometeu-analysis/src/interner.rs`
|
||||
* Ajustar frontend PBS no `prometeu-compiler` (ver lista de toques abaixo).
|
||||
|
||||
### Estruturas obrigatórias
|
||||
|
||||
```rust
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct NameId(pub u32);
|
||||
|
||||
pub struct NameInterner {
|
||||
// bidirectional: string->id, id->string
|
||||
}
|
||||
```
|
||||
|
||||
### APIs obrigatórias
|
||||
|
||||
```rust
|
||||
impl NameInterner {
|
||||
pub fn new() -> Self;
|
||||
pub fn intern(&mut self, s: &str) -> NameId;
|
||||
pub fn resolve(&self, id: NameId) -> &str;
|
||||
}
|
||||
```
|
||||
|
||||
### Regras
|
||||
|
||||
* Interner é **session-local**.
|
||||
* `resolve` deve retornar `&str` estável (armazenar `String` internamente).
|
||||
|
||||
### Touch points no `prometeu-compiler`
|
||||
|
||||
* Onde hoje existe `String` como nome de símbolo, token de identifier, etc:
|
||||
|
||||
* AST nodes de `Ident`
|
||||
* Resolver scopes: `HashMap<NameId, _>`
|
||||
|
||||
### Testes
|
||||
|
||||
* Unit: intern/resolve, dedup
|
||||
* Golden: manter comportamento de resolver/diags
|
||||
|
||||
### Critérios de aceite
|
||||
|
||||
* Redução de `String` em estruturas hot (resolver)
|
||||
* Build e testes ok.
|
||||
|
||||
---
|
||||
|
||||
## PR-03 — AST Arena (NodeId) para PBS
|
||||
|
||||
**Branch:** `pr-03-ast-arena`
|
||||
|
||||
### Objetivo
|
||||
|
||||
Refatorar o AST PBS (atualmente recursivo) para uma arena `Vec` com `NodeId`.
|
||||
|
||||
### Alvos no repo
|
||||
|
||||
* `prometeu-compiler/src/frontends/pbs/*` (localizar AST/parser atuais)
|
||||
|
||||
### Estruturas obrigatórias
|
||||
|
||||
```rust
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct NodeId(pub u32);
|
||||
|
||||
pub struct AstArena {
|
||||
pub nodes: Vec<NodeKind>,
|
||||
pub spans: Vec<prometeu_analysis::Span>,
|
||||
pub roots: Vec<NodeId>,
|
||||
}
|
||||
|
||||
pub enum NodeKind {
|
||||
File { imports: Vec<NodeId>, decls: Vec<NodeId> },
|
||||
Import { /* ... */ },
|
||||
FnDecl { name: NameId, /* ... */, body: NodeId },
|
||||
// ... (conforme canonical AST do PBS)
|
||||
}
|
||||
```
|
||||
|
||||
### Regras
|
||||
|
||||
* `AstArena::push(kind, span) -> NodeId` sempre faz append.
|
||||
* `spans.len() == nodes.len()` sempre.
|
||||
|
||||
### APIs obrigatórias
|
||||
|
||||
```rust
|
||||
impl AstArena {
|
||||
pub fn push(&mut self, kind: NodeKind, span: Span) -> NodeId;
|
||||
pub fn kind(&self, id: NodeId) -> &NodeKind;
|
||||
pub fn span(&self, id: NodeId) -> Span;
|
||||
}
|
||||
```
|
||||
|
||||
### Testes obrigatórios
|
||||
|
||||
* Parser golden tests: comparar JSON canonical (se você já tem) ou snapshots equivalentes.
|
||||
* Unit: invariantes (push mantém alinhamento).
|
||||
|
||||
### Critérios de aceite
|
||||
|
||||
* Nenhum `Box<Node>` em AST PBS.
|
||||
* Resolver e typechecker passam a consumir NodeId.
|
||||
|
||||
---
|
||||
|
||||
## PR-04 — SymbolArena + índices (defs/refs) + node→symbol
|
||||
|
||||
**Branch:** `pr-04-symbol-arena-index`
|
||||
|
||||
### Objetivo
|
||||
|
||||
Criar `SymbolArena` e índices para features de LSP sem traversal pesado.
|
||||
|
||||
### Estruturas obrigatórias
|
||||
|
||||
```rust
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct SymbolId(pub u32);
|
||||
|
||||
pub enum SymbolKind { Type, Value, Service, Function /* etc */ }
|
||||
|
||||
pub struct Symbol {
|
||||
pub name: NameId,
|
||||
pub kind: SymbolKind,
|
||||
pub exported: bool,
|
||||
pub module: ModuleId,
|
||||
pub decl_span: Span,
|
||||
}
|
||||
|
||||
pub struct SymbolArena { pub symbols: Vec<Symbol> }
|
||||
|
||||
pub struct DefIndex { /* HashMap<(ModuleId, NameId, Namespace), SymbolId> */ }
|
||||
|
||||
pub struct RefIndex { /* Vec<Vec<Span>> by SymbolId */ }
|
||||
|
||||
pub struct NodeToSymbol { /* Vec<Option<SymbolId>> by NodeId */ }
|
||||
```
|
||||
|
||||
### APIs obrigatórias
|
||||
|
||||
* `insert_symbol(...) -> SymbolId` (falha se duplicate, com diag E_RESOLVE_DUPLICATE_SYMBOL)
|
||||
* `record_ref(symbol_id, span)`
|
||||
* `bind_node(node_id, symbol_id)`
|
||||
|
||||
### Testes
|
||||
|
||||
* Duplicate symbol
|
||||
* Undefined identifier
|
||||
* Visibility violations
|
||||
* Determinismo (mesma input → mesma ordem IDs, quando possível)
|
||||
|
||||
---
|
||||
|
||||
## PR-05 — TypeArena + TypeFacts (node→type, symbol→type)
|
||||
|
||||
**Branch:** `pr-05-type-arena-facts`
|
||||
|
||||
### Objetivo
|
||||
|
||||
Dar base para hover, completion e erros de tipo.
|
||||
|
||||
### Estruturas obrigatórias
|
||||
|
||||
```rust
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TypeId(pub u32);
|
||||
|
||||
pub enum TypeKind {
|
||||
Primitive { name: NameId },
|
||||
Struct { sym: SymbolId },
|
||||
Optional { inner: TypeId },
|
||||
Result { ok: TypeId, err: TypeId },
|
||||
Array { inner: TypeId, len: Option<u32> },
|
||||
// ... conforme v0
|
||||
}
|
||||
|
||||
pub struct TypeArena { pub types: Vec<TypeKind> }
|
||||
|
||||
pub struct TypeFacts {
|
||||
pub node_type: Vec<Option<TypeId>>, // index by NodeId
|
||||
pub symbol_type: Vec<Option<TypeId>>, // index by SymbolId
|
||||
}
|
||||
```
|
||||
|
||||
### APIs obrigatórias
|
||||
|
||||
* `intern_type(TypeKind) -> TypeId` (dedup opcional; documentar)
|
||||
* `set_node_type(NodeId, TypeId)`
|
||||
* `set_symbol_type(SymbolId, TypeId)`
|
||||
|
||||
### Testes
|
||||
|
||||
* Hover type display snapshots.
|
||||
|
||||
---
|
||||
|
||||
## PR-06 — Diagnostics canonizados (E_* / W_*)
|
||||
|
||||
**Branch:** `pr-06-diagnostics`
|
||||
|
||||
### Objetivo
|
||||
|
||||
Diagnósticos estáveis e serializáveis, alinhados ao Canonical Addenda (codes).
|
||||
|
||||
### Estruturas obrigatórias
|
||||
|
||||
```rust
|
||||
pub enum Severity { Error, Warning }
|
||||
|
||||
pub struct Diagnostic {
|
||||
pub severity: Severity,
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
pub span: Span,
|
||||
pub related: Vec<(String, Span)>,
|
||||
}
|
||||
```
|
||||
|
||||
### Regras
|
||||
|
||||
* Codes conforme Canonical Addenda (E_PARSE_*, E_RESOLVE_*, E_TYPE_*).
|
||||
|
||||
### Testes
|
||||
|
||||
* Golden diag JSON determinístico.
|
||||
|
||||
---
|
||||
|
||||
## PR-07 — Export: `symbols.json` v1 + `analysis.json` v0
|
||||
|
||||
**Branch:** `pr-07-analysis-export`
|
||||
|
||||
### Objetivo
|
||||
|
||||
Separar índice leve (`symbols.json`) e export completo (`analysis.json`).
|
||||
|
||||
### Símbolos (novo formato recomendado)
|
||||
|
||||
* **IMPORTANTE:** parar de gravar paths absolutos (usar URI relativo ao projeto quando possível).
|
||||
|
||||
`symbols.json` v1 (schema_version=1):
|
||||
|
||||
* `projects[]`:
|
||||
|
||||
* `project` (string)
|
||||
* `project_dir` (string) (pode ficar absoluto por enquanto)
|
||||
* `symbols[]`:
|
||||
|
||||
* `symbol_id` (u32) **OU** string estável atual (mantém compat)
|
||||
* `name` (string)
|
||||
* `kind` (string)
|
||||
* `exported` (bool)
|
||||
* `module_path` (string)
|
||||
* `decl_span`: `{ file_uri, start:{line,col}, end:{line,col} }`
|
||||
|
||||
`analysis.json` (schema_version=0):
|
||||
|
||||
* `file_table`: [{file_id:u32, uri:string}]
|
||||
* `symbols`: [{symbol_id:u32, name_id:u32, kind, exported, module_id, decl_span}]
|
||||
* `refs`: [{symbol_id:u32, spans:[Span]}]
|
||||
* `types`: [{type_id:u32, kind:...}]
|
||||
* `facts`: { node_type: [...], symbol_type: [...] }
|
||||
* `diagnostics`: [Diagnostic]
|
||||
|
||||
### Testes
|
||||
|
||||
* Snapshot de ambos JSONs.
|
||||
|
||||
---
|
||||
|
||||
## PR-08 — LSP MVP (diagnostics + symbols + goto definition)
|
||||
|
||||
**Branch:** `pr-08-lsp-mvp`
|
||||
|
||||
### Objetivo
|
||||
|
||||
LSP funcional mínimo usando `AnalysisDb` (em memória), recompilando quando arquivo muda.
|
||||
|
||||
### Features
|
||||
|
||||
* `initialize`
|
||||
* `didOpen`/`didChange`/`didClose`
|
||||
* `publishDiagnostics`
|
||||
* `documentSymbol` / `workspaceSymbol`
|
||||
* `definition`
|
||||
|
||||
### Regras
|
||||
|
||||
* `didChange`: por enquanto full-text (simplifica)
|
||||
* Rebuild: coarse (projeto inteiro) inicialmente
|
||||
|
||||
### Testes
|
||||
|
||||
* Test harness: abrir doc e pedir definition.
|
||||
|
||||
---
|
||||
|
||||
## PR-09 — LSP: references + rename
|
||||
|
||||
**Branch:** `pr-09-lsp-refs-rename`
|
||||
|
||||
### Objetivo
|
||||
|
||||
Baseado em RefIndex e NodeToSymbol.
|
||||
|
||||
### Features
|
||||
|
||||
* `references`
|
||||
* `prepareRename`
|
||||
* `rename`
|
||||
|
||||
### Regras de segurança
|
||||
|
||||
* Não renomear se símbolo não-resolvido.
|
||||
* Não renomear se span em comentário/string.
|
||||
|
||||
---
|
||||
|
||||
## PR-10 — LSP: hover + signatureHelp
|
||||
|
||||
**Branch:** `pr-10-lsp-hover-sighelp`
|
||||
|
||||
### Features
|
||||
|
||||
* `hover`: type + docstring (docstring opcional por enquanto)
|
||||
* `signatureHelp`: para call nodes (se parser já marca)
|
||||
|
||||
---
|
||||
|
||||
## PR-11 — LSP: completion
|
||||
|
||||
**Branch:** `pr-11-lsp-completion`
|
||||
|
||||
### Buckets
|
||||
|
||||
* locals em scope (se você tiver scope facts)
|
||||
* symbols do módulo
|
||||
* exports importados
|
||||
* members via type facts
|
||||
|
||||
---
|
||||
|
||||
## PR-12 — LSP: semantic tokens + highlights
|
||||
|
||||
**Branch:** `pr-12-lsp-semantic`
|
||||
|
||||
---
|
||||
|
||||
## PR-13 — LSP: formatting + code actions (opcional)
|
||||
|
||||
**Branch:** `pr-13-lsp-actions`
|
||||
|
||||
---
|
||||
|
||||
## PR-14 — Incremental analysis + cancelation
|
||||
|
||||
**Branch:** `pr-14-incremental`
|
||||
|
||||
---
|
||||
|
||||
## PR-15 — Debug map (pc→span) + integração com traps
|
||||
|
||||
**Branch:** `pr-15-debug-map`
|
||||
|
||||
---
|
||||
|
||||
# Ordem recomendada (para minimizar refator churn)
|
||||
|
||||
1. PR-00, PR-01
|
||||
2. PR-02
|
||||
3. PR-03
|
||||
4. PR-04
|
||||
5. PR-06
|
||||
6. PR-05
|
||||
7. PR-07
|
||||
8. PR-08 → PR-12
|
||||
9. PR-14
|
||||
10. PR-15
|
||||
Loading…
x
Reference in New Issue
Block a user