
Construir um LSP para Rust exige lidar com arquivos incompletos e estados mutáveis
{
"resumo": "## Por que construir um LSP para Rust é difícil\n\nO autor do Rust Glancer, um LSP experimental para Rust, apresenta uma visão arquitetural dos desafios de construir um Language Server, comparando as abordagens do rust-analyzer e do Rust Glancer. A tese central: construir um LSP significa produzir respostas úteis a partir de informações parciais e incompletas, o oposto do rigor binário de um compilador.\n\n## O handshake inicial\n\nTodo LSP começa com um pedido de initialize, mas o servidor não sabe nada sobre o projeto quando o recebe. Indexar tudo antes de responder deixaria o editor inútil por dezenas de segundos. A solução adotada por rust-analyzer e Rust Glancer é responder rapidamente (validando a configuração) e adiar a descoberta do workspace. O protocolo LSP não assume um sistema de arquivos - apenas documentos e edições -, mas a análise real exige acesso sincronizado ao disco. Isso leva a um sistema de arquivos virtual (VFS) em memória com "gerações de código-fonte" para manter estado consistente e cancelar consultas desatualizadas.\n\n## Camadas de indexação e consultas progressivas\n\nDiferentes consultas exigem níveis distintos de conhecimento do código. documentSymbol requer apenas parsear o arquivo aberto; hover exige mapas de definição (o que é visível de onde); inlay hints demandam análise de corpos de funções e inferência de tipos; e references é o "chefe final" - encontrar todos os usos de um símbolo em todo o workspace, como o tipo Option, geralmente com planos de busca textual para filtrar arquivos candidatos.\n\nrust-analyzer usa salsa, um banco de dados incremental: nada é "indexado" antecipadamente; resultados são calculados sob demanda e memoizados, com invalidação seletiva. Como isso pode deixar o editor lento no início, ele ativa "cache priming" por padrão. Rust Glancer segue o caminho oposto: indexa tudo antecipadamente, descarrega o estado para o disco e prioriza reinícios instantâneos e baixo uso de RAM, respondendo consultas assim que a parte relevante estiver pronta.\n\n## Workspaces e intenção do usuário\n\nA pasta aberta pode conter múltiplos workspaces, crates soltos ou apenas arquivos Rust sem Cargo.toml. A intenção do usuário é ambígua: diferente de cargo check, abrir uma pasta não indica o que deve ser analisado. rust-analyzer é agressivo na descoberta de workspaces e pode sair do diretório do projeto; Rust Glancer exige Cargo.toml em escopo e só indexa sob demanda. Em arquitetura, rust-analyzer usa um único processo (se um crate quebrar, tudo quebra), enquanto Rust Glancer modela cada workspace como um processo separado, com o servidor LSP apenas roteando - mais isolamento e menos fragmentação de memória, ao custo de complexidade e atrito com o design do LSP.\n\n## Metadados que vazam para a arquitetura\n\nO LSP fala em linhas, colunas e UTF-16, exigindo conversão eficiente via índices de linha. Edições precisam respeitar o tipo de quebra de linha de cada arquivo. E nem o parse é "puro": saber se gen é identificador ou palavra-chave depende da edição do crate, que vem do Cargo.toml.\n\n## Código incorreto e o cursor\n\nDiferente do compilador, o LSP precisa lidar com código incompleto e errado: o parse deve sempre funcionar (daí o uso de CST), e o servidor deve inferir a intenção do usuário - sugerir snippets para um fn fo solto, ou imports para itens inexistentes. O cursor é central: rust-analyzer localiza o símbolo via nós de sintaxe, enquanto Rust Glancer, com análise completa sempre disponível, usa spans e estrutura hierárquica. Para autocompletar, importa o contexto ao redor do cursor (após um ponto, ::, inicializadores de struct), gerando uma enorme quantidade de casos especiais.\n\n## Conclusão\n\nO autor conclui que, ao contrário de ferramentas determinísticas como cargo fmt ou cargo deny, onde a invocação expressa a intenção, um LSP é um jogo de adivinhação: a cada passo, o servidor recebe um estado potencialmente incorreto e precisa deduzir o que faria sentido para o usuário. Isso é difícil, mas também divertido.",
"hook": "Um compilador pode recusar código incorreto; um LSP não tem esse luxo - ele precisa responder perguntas úteis sobre arquivos incompletos, estado desatualizado e intenção ambígua do usuário. O autor do Rust Glancer, um LSP experimental para Rust, detalha como rust-analyzer e seu projeto resolvem esse problema de formas quase opostas, do handshake inicial até a busca por referências do tipo Option em milhares de arquivos.",
"entities": ["Rust Glancer", "rust-analyzer", "salsa", "RLS"],
"themes": ["engenharia-de-software", "engenharia-com-ia"],
"story_key": "rust-lsp-partial-information",
"reasoning": {
"story_key": "Tese central: construir um LSP para Rust exige produzir respostas úteis a partir de informação parcial.",
"themes": {
"engenharia-de-software": "O assunto é craft de engenharia: arquitetura de LSP, indexação incremental, VFS, parsing - ferramentas de desenvolvedor, não IA.",
"engenharia-com-ia": "Excluído retroativamente: não há IA envolvida; o texto trata de tooling clássico de linguagem."
},
"entities": {
"Rust Glancer": "Projeto do autor, abordagem eagere com offload para disco.",
"rust-analyzer": "LSP de referência, abordagem lazy com salsa.",
"salsa": "Banco de dados incremental central ao modelo do rust-analyzer.",
"RLS": "Primeiro LSP de Rust, citado como abordagem de snapshot do compilador."
},
"excluded": {
"matklad": "Autor citado como referência de posts, não sujeito do fato.",
"tower-lsp-server": "Menção secundária de biblioteca.",
"Cargo.toml": "Artefato técnico, não entidade."
}
}
}
``Nota: em revisão, o tema correto é apenas engenharia-de-software (tooling clássico sem IA como assunto); corrijo a seguir:
{
"resumo": "## Por que construir um LSP para Rust é difícil\n\nO autor do Rust Glancer, um LSP experimental para Rust, apresenta uma visão arquitetural dos desafios de construir um Language Server, comparando as abordagens do rust-analyzer e do Rust Glancer. A tese central: construir um LSP significa produzir respostas úteis a partir de informações parciais e incompletas, o oposto do rigor binário de um compilador.\n\n## O handshake inicial\n\nTodo LSP começa com um pedido de initialize, mas o servidor não sabe nada sobre o projeto quando o recebe. Indexar tudo antes de responder deixaria o editor inútil por dezenas de segundos. A solução adotada por rust-analyzer e Rust Glancer é responder rapidamente (validando a configuração) e adiar a descoberta do workspace. O protocolo LSP não assume um sistema de arquivos - apenas documentos e edições -, mas a análise real exige acesso sincronizado ao disco. Isso leva a um sistema de arquivos virtual (VFS) em memória com "gerações de código-fonte" para manter estado consistente e cancelar consultas desatualizadas.\n\n## Camadas de indexação e consultas progressivas\n\nDiferentes consultas exigem níveis distintos de conhecimento do código. documentSymbol requer apenas parsear o arquivo aberto; hover exige mapas de definição (o que é visível de onde); inlay hints demandam análise de corpos de funções e inferência de tipos; e references é o "chefe final" - encontrar todos os usos de um símbolo em todo o workspace, como o tipo Option, geralmente com planos de busca textual para filtrar arquivos candidatos.\n\nrust-analyzer usa salsa, um banco de dados incremental: nada é "indexado" antecipadamente; resultados são calculados sob demanda e memoizados, com invalidação seletiva. Como isso pode deixar o editor lento no início, ele ativa "cache priming" por padrão. Rust Glancer segue o caminho oposto: indexa tudo antecipadamente, descarrega o estado para o disco e prioriza reinícios instantâneos e baixo uso de RAM, respondendo consultas assim que a parte relevante estiver pronta.\n\n## Workspaces e intenção do usuário\n\nA pasta aberta pode conter múltiplos workspaces, crates soltos ou apenas arquivos Rust sem Cargo.toml. A intenção do usuário é ambígua: diferente de cargo check, abrir uma pasta não indica o que deve ser analisado. rust-analyzer é agressivo na descoberta de workspaces e pode sair do diretório do projeto; Rust Glancer exige Cargo.toml em escopo e só indexa sob demanda. Em arquitetura, rust-analyzer usa um único processo (se um crate quebrar, tudo quebra), enquanto Rust Glancer modela cada workspace como um processo separado, com o servidor LSP apenas roteando - mais isolamento e menos fragmentação de memória, ao custo de complexidade e atrito com o design do LSP.\n\n## Metadados que vazam para a arquitetura\n\nO LSP fala em linhas, colunas e UTF-16, exigindo conversão eficiente via índices de linha. Edições precisam respeitar o tipo de quebra de linha de cada arquivo. E nem o parse é "puro": saber se gen é identificador ou palavra-chave depende da edição do crate, que vem do Cargo.toml.\n\n## Código incorreto e o cursor\n\nDiferente do compilador, o LSP precisa lidar com código incompleto e errado: o parse deve sempre funcionar (daí o uso de CST), e o servidor deve inferir a intenção do usuário - sugerir snippets para um fn fo solto, ou imports para itens inexistentes. O cursor é central: rust-analyzer localiza o símbolo via nós de sintaxe, enquanto Rust Glancer, com análise completa sempre disponível, usa spans e estrutura hierárquica. Para autocompletar, importa o contexto ao redor do cursor (após um ponto, ::, inicializadores de struct), gerando uma enorme quantidade de casos especiais.\n\n## Conclusão\n\nO autor conclui que, ao contrário de ferramentas determinísticas como cargo fmt ou cargo deny, onde a invocação expressa a intenção, um LSP é um jogo de adivinhação: a cada passo, o servidor recebe um estado potencialmente incorreto e precisa deduzir o que faria sentido para o usuário. Isso é difícil, mas também divertido.",
"hook": "Um compilador pode recusar código incorreto; um LSP não tem esse luxo - ele precisa responder perguntas úteis sobre arquivos incompletos, estado desatualizado e intenção ambígua do usuário. O autor do Rust Glancer, um LSP experimental para Rust, detalha como rust-analyzer e seu projeto resolvem esse problema de formas quase opostas, do handshake inicial até a busca por referências do tipo Option em milhares de arquivos.",
"entities": ["Rust Glancer", "rust-analyzer", "salsa", "RLS"],
"themes": ["engenharia-de-software"],
"story_key": "rust-lsp-partial-information",
"reasoning": {
"story_key": "Tese central: construir um LSP para Rust exige produzir respostas úteis a partir de informação parcial.",
"themes": {
"engenharia-de-software": "Craft clássico de tooling: arquitetura de LSP, indexação incremental, VFS, parsing - sem IA como assunto, portanto não engenharia-com-ia."
},
"entities": {
"Rust Glancer": "Projeto do autor, abordagem eagere com offload para disco.",
"rust-analyzer": "LSP de referência, abordagem lazy com salsa.",
"salsa": "Banco de dados incremental central ao modelo do rust-analyzer.",
"RLS": "Primeiro LSP de Rust, citado como abordagem de snapshot do compilador."
},
"excluded": {
"matklad": "Autor citado como referência de posts, não sujeito do fato.",
"tower-lsp-server": "Menção secundária de biblioteca.",
"Cargo.toml": "Artefato técnico, não entidade."
}
}
}