pr 01
This commit is contained in:
parent
97c6ccb6f2
commit
6b372b2613
102
crates/prometeu-analysis/src/file_db.rs
Normal file
102
crates/prometeu-analysis/src/file_db.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::ids::FileId;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct FileDB {
|
||||||
|
files: Vec<FileData>,
|
||||||
|
uri_to_id: HashMap<String, FileId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileData {
|
||||||
|
uri: String,
|
||||||
|
text: String,
|
||||||
|
line_index: LineIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LineIndex {
|
||||||
|
line_starts: Vec<u32>,
|
||||||
|
total_len: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileDB {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
files: Vec::new(),
|
||||||
|
uri_to_id: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upsert(&mut self, uri: &str, text: String) -> FileId {
|
||||||
|
if let Some(&id) = self.uri_to_id.get(uri) {
|
||||||
|
let line_index = LineIndex::new(&text);
|
||||||
|
self.files[id.0 as usize] = FileData {
|
||||||
|
uri: uri.to_string(),
|
||||||
|
text,
|
||||||
|
line_index,
|
||||||
|
};
|
||||||
|
id
|
||||||
|
} else {
|
||||||
|
let id = FileId(self.files.len() as u32);
|
||||||
|
let line_index = LineIndex::new(&text);
|
||||||
|
self.files.push(FileData {
|
||||||
|
uri: uri.to_string(),
|
||||||
|
text,
|
||||||
|
line_index,
|
||||||
|
});
|
||||||
|
self.uri_to_id.insert(uri.to_string(), id);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_id(&self, uri: &str) -> Option<FileId> {
|
||||||
|
self.uri_to_id.get(uri).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uri(&self, id: FileId) -> &str {
|
||||||
|
&self.files[id.0 as usize].uri
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text(&self, id: FileId) -> &str {
|
||||||
|
&self.files[id.0 as usize].text
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn line_index(&self, id: FileId) -> &LineIndex {
|
||||||
|
&self.files[id.0 as usize].line_index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineIndex {
|
||||||
|
pub fn new(text: &str) -> Self {
|
||||||
|
let mut line_starts = vec![0];
|
||||||
|
for (offset, c) in text.char_indices() {
|
||||||
|
if c == '\n' {
|
||||||
|
line_starts.push((offset + 1) as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
line_starts,
|
||||||
|
total_len: text.len() as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset_to_line_col(&self, offset: u32) -> (u32, u32) {
|
||||||
|
let line = match self.line_starts.binary_search(&offset) {
|
||||||
|
Ok(line) => line as u32,
|
||||||
|
Err(line) => (line - 1) as u32,
|
||||||
|
};
|
||||||
|
let col = offset - self.line_starts[line as usize];
|
||||||
|
(line, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn line_col_to_offset(&self, line: u32, col: u32) -> Option<u32> {
|
||||||
|
let start = *self.line_starts.get(line as usize)?;
|
||||||
|
let offset = start + col;
|
||||||
|
|
||||||
|
let next_start = self.line_starts.get(line as usize + 1).copied().unwrap_or(self.total_len);
|
||||||
|
if offset < next_start || (offset == next_start && offset == self.total_len) {
|
||||||
|
Some(offset)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
crates/prometeu-analysis/src/ids.rs
Normal file
2
crates/prometeu-analysis/src/ids.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct FileId(pub u32);
|
||||||
@ -1,29 +1,7 @@
|
|||||||
//! Tipos e utilitários de análise usados pelo servidor LSP e pelo pipeline.
|
pub mod ids;
|
||||||
//!
|
pub mod span;
|
||||||
//! Este crate expõe o `FileDB`, repositório de textos do projeto.
|
pub mod file_db;
|
||||||
//! Ele será expandido nos próximos PRs para suportar snapshots e índices.
|
|
||||||
//!
|
|
||||||
//! Exemplo básico:
|
|
||||||
//! ```
|
|
||||||
//! use prometeu_analysis::FileDB;
|
|
||||||
//! let db = FileDB::default();
|
|
||||||
//! assert_eq!(db.len_files(), 0);
|
|
||||||
//! ```
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// Banco de arquivos do projeto.
|
pub use ids::FileId;
|
||||||
///
|
pub use span::Span;
|
||||||
/// Responsável por armazenar textos, URIs/paths e permitir criação de snapshots.
|
pub use file_db::{FileDB, LineIndex};
|
||||||
/// Implementação mínima no PR-00; funcionalidades serão adicionadas nos PRs seguintes.
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct FileDB {
|
|
||||||
// TODO(PR-03+): armazenar textos, URIs/paths, índices de linha e snapshots
|
|
||||||
#[serde(skip)]
|
|
||||||
_placeholder: (),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileDB {
|
|
||||||
/// Retorna a quantidade de arquivos conhecidos.
|
|
||||||
/// Implementação temporária para manter a API estável.
|
|
||||||
pub fn len_files(&self) -> usize { 0 }
|
|
||||||
}
|
|
||||||
|
|||||||
8
crates/prometeu-analysis/src/span.rs
Normal file
8
crates/prometeu-analysis/src/span.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use crate::ids::FileId;
|
||||||
|
|
||||||
|
#[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
|
||||||
|
}
|
||||||
69
crates/prometeu-analysis/tests/file_db_line_index.rs
Normal file
69
crates/prometeu-analysis/tests/file_db_line_index.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
use prometeu_analysis::{FileDB, LineIndex};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_line_index_roundtrip() {
|
||||||
|
let text = "line 1\nline 2\nline 3";
|
||||||
|
let index = LineIndex::new(text);
|
||||||
|
|
||||||
|
// Roundtrip for each character
|
||||||
|
for (offset, _) in text.char_indices() {
|
||||||
|
let (line, col) = index.offset_to_line_col(offset as u32);
|
||||||
|
let recovered_offset = index.line_col_to_offset(line, col).expect("Should recover offset");
|
||||||
|
assert_eq!(offset as u32, recovered_offset, "Offset mismatch at line {}, col {}", line, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_line_index_boundaries() {
|
||||||
|
let text = "a\nbc\n";
|
||||||
|
let index = LineIndex::new(text);
|
||||||
|
|
||||||
|
// "a" -> (0, 0)
|
||||||
|
assert_eq!(index.offset_to_line_col(0), (0, 0));
|
||||||
|
assert_eq!(index.line_col_to_offset(0, 0), Some(0));
|
||||||
|
|
||||||
|
// "\n" -> (0, 1)
|
||||||
|
assert_eq!(index.offset_to_line_col(1), (0, 1));
|
||||||
|
assert_eq!(index.line_col_to_offset(0, 1), Some(1));
|
||||||
|
|
||||||
|
// "b" -> (1, 0)
|
||||||
|
assert_eq!(index.offset_to_line_col(2), (1, 0));
|
||||||
|
assert_eq!(index.line_col_to_offset(1, 0), Some(2));
|
||||||
|
|
||||||
|
// "c" -> (1, 1)
|
||||||
|
assert_eq!(index.offset_to_line_col(3), (1, 1));
|
||||||
|
assert_eq!(index.line_col_to_offset(1, 1), Some(3));
|
||||||
|
|
||||||
|
// "\n" (second) -> (1, 2)
|
||||||
|
assert_eq!(index.offset_to_line_col(4), (1, 2));
|
||||||
|
assert_eq!(index.line_col_to_offset(1, 2), Some(4));
|
||||||
|
|
||||||
|
// EOF (after last \n) -> (2, 0)
|
||||||
|
assert_eq!(index.offset_to_line_col(5), (2, 0));
|
||||||
|
assert_eq!(index.line_col_to_offset(2, 0), Some(5));
|
||||||
|
|
||||||
|
// Out of bounds
|
||||||
|
assert_eq!(index.line_col_to_offset(2, 1), None);
|
||||||
|
assert_eq!(index.line_col_to_offset(3, 0), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_file_db_upsert_and_access() {
|
||||||
|
let mut db = FileDB::new();
|
||||||
|
let uri = "file:///test.txt";
|
||||||
|
let text = "hello\nworld".to_string();
|
||||||
|
|
||||||
|
let id = db.upsert(uri, text.clone());
|
||||||
|
assert_eq!(db.file_id(uri), Some(id));
|
||||||
|
assert_eq!(db.uri(id), uri);
|
||||||
|
assert_eq!(db.text(id), &text);
|
||||||
|
|
||||||
|
let index = db.line_index(id);
|
||||||
|
assert_eq!(index.offset_to_line_col(6), (1, 0)); // 'w' is at offset 6
|
||||||
|
|
||||||
|
// Update existing file
|
||||||
|
let new_text = "new content".to_string();
|
||||||
|
let same_id = db.upsert(uri, new_text.clone());
|
||||||
|
assert_eq!(id, same_id);
|
||||||
|
assert_eq!(db.text(id), &new_text);
|
||||||
|
}
|
||||||
@ -101,346 +101,6 @@ Workspace (Archive.zip) tem crates:
|
|||||||
|
|
||||||
# PRs (detalhados)
|
# 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).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
# Série PR-00.X — Reestruturação de crates (Arena-Driven + LSP-ready)
|
|
||||||
|
|
||||||
> **Objetivo da série PR-00.X:** reorganizar o workspace em crates com fronteiras claras, **sem alterar comportamento**, preparando o terreno para o Prometeu Arena-Driven (compiler/analysis) e um LSP completo depois.
|
|
||||||
>
|
|
||||||
> **Novo layout alvo (travado):**
|
|
||||||
>
|
|
||||||
> * `prometeu-bytecode` (fica)
|
|
||||||
> * `prometeu-abi` (novo nome para o atual `prometeu-core` depurado) = **types + model + protocols** (sem execução)
|
|
||||||
> * `prometeu-vm` (novo) = execução da VM
|
|
||||||
> * `prometeu-kernel` (novo) = OS + FS + syscalls (implementa interface para a VM)
|
|
||||||
> * `prometeu-runtime-desktop` (fica) = host desktop
|
|
||||||
> * `prometeu-compiler` (fica)
|
|
||||||
> * `prometeu` (CLI) (fica)
|
|
||||||
>
|
|
||||||
> **Regra de ouro:** 1 PR = 1 fronteira. Nada de mover tudo de uma vez.
|
|
||||||
>
|
|
||||||
> **Regras para Junie (serie 00.X):**
|
|
||||||
>
|
|
||||||
> 1. **Não mudar lógica**; apenas mover, ajustar imports, e deixar testes verdes.
|
|
||||||
> 2. Se algo exigir mudança de API pública, criar `TODO(PR-00.Y)` e parar.
|
|
||||||
> 3. Sempre manter o workspace compilável a cada PR.
|
|
||||||
|
|
||||||
---
|
|
||||||
## PR-00.4 — Depurar `prometeu-core`: remover execução e virar apenas compat layer
|
|
||||||
|
|
||||||
**Branch:** `pr-00-4-deprecate-prometeu-core`
|
|
||||||
|
|
||||||
### Objetivo
|
|
||||||
|
|
||||||
Eliminar o conteúdo executável restante do `prometeu-core`, deixando-o como reexport/compat por um curto período.
|
|
||||||
|
|
||||||
### Passos
|
|
||||||
|
|
||||||
* `hardware` e `firmware/hub` ficam onde estiverem por enquanto (podem virar PR-00.5/00.6 se necessário)
|
|
||||||
* `prometeu-core` passa a reexportar:
|
|
||||||
|
|
||||||
* `prometeu-abi`
|
|
||||||
* `prometeu-vm`
|
|
||||||
* `prometeu-kernel`
|
|
||||||
|
|
||||||
### Critérios
|
|
||||||
|
|
||||||
* Zero imports internos `crate::virtual_machine` etc. fora da facade.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## PR-00.6 — Remover `prometeu-core` e migrar consumidores
|
|
||||||
|
|
||||||
**Branch:** `pr-00-6-remove-prometeu-core`
|
|
||||||
|
|
||||||
### Objetivo
|
|
||||||
|
|
||||||
Apagar `prometeu-core` após migração total.
|
|
||||||
|
|
||||||
### Critérios
|
|
||||||
|
|
||||||
* Nenhum crate depende de `prometeu-core`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## PR-01 — FileDB + LineIndex (base do LSP e spans)
|
## PR-01 — FileDB + LineIndex (base do LSP e spans)
|
||||||
|
|
||||||
**Branch:** `pr-01-filedb`
|
**Branch:** `pr-01-filedb`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user