Skip to content
This repository was archived by the owner on Sep 20, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b571690
fix: `native`/`forward` com assinatura multilinha e doc após `#pragma`
NullSablex Sep 1, 2026
56480f0
docs: changelog das correções de parsing
NullSablex Sep 1, 2026
2d87963
perf: evita alocar por linha ao procurar `#pragma deprecated`
NullSablex Sep 1, 2026
79d8eff
feat: PP0019 valida `#pragma`, com quick fix
NullSablex Sep 1, 2026
d69efe1
feat: quick fix para PP0002/0003/0004/0010/0011/0012/0017
NullSablex Sep 1, 2026
4afc4ff
feat: ordena o autocomplete pela proximidade do cursor e limita a lista
NullSablex Sep 1, 2026
976ca04
fix: remove `.into_iter()` redundante (clippy useless_conversion)
NullSablex Sep 1, 2026
8a427ce
fix: doc comment vazando entre símbolos e hover desalinhado
NullSablex Sep 1, 2026
814e7b6
fix: régua de separação deixa de ser lida como doc comment
NullSablex Sep 1, 2026
df69d9b
style: encurta as mensagens de aviso e os títulos das correções
NullSablex Sep 1, 2026
f3c3468
fix: cabeçalho de seção com texto também não é doc comment
NullSablex Sep 1, 2026
ccf009c
fix: doc comment passa a ser só o bloco imediatamente acima
NullSablex Sep 1, 2026
556f7fe
docs: badge de estrelas no README
NullSablex Sep 1, 2026
77a45e7
feat: estilo de nomenclatura aceita regex do usuário
NullSablex Sep 1, 2026
15c4274
test: padrão do usuário na avaliação de nomes
NullSablex Sep 1, 2026
5b1c965
test: padrão catastrófico não degrada a análise
NullSablex Sep 1, 2026
15918a9
docs: padrão próprio no changelog e no guia do assistente de nomes
NullSablex Sep 1, 2026
bca3d87
docs: README diz o que o motor entrega e corrige o que estava errado
NullSablex Sep 1, 2026
4641e78
docs: 13 diagnósticos com correção automática, não 11
NullSablex Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ caso encontre por favor relate para ajudar a manter a consistência dos dados.
marcação continua virando a descrição, como antes
- **Tags de documentação no autocomplete** — o trigger `@`, dentro de um
comentário, passa a oferecer `@param`, `@return` e `@remarks` com snippets
- **Padrão próprio no estilo de nomenclatura** — um item da lista de estilos
aceitos escrito entre barras (`/^g_[a-z][a-zA-Z0-9]*$/`) passa a ser lido como
expressão regular, para convenções que os cinco estilos embutidos não descrevem
— prefixo de global, notação húngara. Convive com eles pela regra que já valia:
o nome é aceito se casar com **qualquer** critério da categoria.

O padrão é âncorado como `^(?:…)$` — descreve o nome inteiro, e o agrupamento
impede que uma alternância no topo ancore só os extremos. O `_` inicial **não**
é removido antes da comparação, ao contrário dos estilos embutidos: quem
escreve o padrão decide se o aceita. Um padrão inválido é ignorado sem derrubar
a análise nem os demais critérios da categoria.

Não gera sugestão de renomeação: de um regex arbitrário dá para saber se o nome
passa, não como reescrevê-lo. Por isso o critério é um tipo próprio (`Rule`) em
vez de uma variante de `Case`, que alimenta o gerador de nomes. A crate `regex`
tem tempo de execução linear garantido, então um padrão custoso não degrada a
análise
- **Diagnósticos e hovers traduzidos para Espanhol, Romeno e Russo** — as tabelas
de mensagens `messages/langs/{es,ro,ru}.rs` existiam como esqueleto (texto ainda
em inglês, copiado de `en.rs`) e agora estão de fato traduzidas: as 75 mensagens
Expand Down Expand Up @@ -80,7 +97,69 @@ caso encontre por favor relate para ajudar a manter a consistência dos dados.
por ecossistema (cargo, GitHub Actions, pip) em vez de abrir um PR por
dependência, reduzindo o ruído de manutenção

- **Autocomplete ordenado pela proximidade do cursor** — a lista vinha ordenada
de um jeito que punha as variáveis locais e os parâmetros **abaixo** de
milhares de nativas dos includes; o que está mais perto de quem escreve
aparecia por último. A ordem passa a ser: locais e parâmetros, símbolos do
próprio arquivo, símbolos dos includes, palavras-chave e, por fim, os
marcados com `#pragma deprecated` — que continuam aparecendo (às vezes é
mesmo o que se quer), mas nunca à frente de uma alternativa viva. Dentro de
cada grupo a ordem é alfabética sem diferenciar maiúsculas
- **Listas grandes de completion não travam mais a digitação** — um projeto com
muitos includes mandava todos os símbolos ao editor a cada tecla. A resposta
passa a ser cortada em 1000 itens e marcada como `isIncomplete`, fazendo o
editor pedir de novo conforme o prefixo cresce. O corte vem depois da
ordenação, então o que se perde são os itens mais distantes do cursor
- **Correções rápidas para mais nove diagnósticos** — passam a ter *quick fix*:
remover o corpo `{ }` ilegal de um `native`/`forward` (`PP0002`/`PP0003`);
dar corpo vazio ou converter em `forward` quando falta o corpo (`PP0004`);
trocar por um símbolo de nome parecido quando a função chamada não existe
(`PP0010`, o caso comum de erro de digitação); remover o `#define` e o
`#include` não utilizados (`PP0011`/`PP0012`); e reindentar a linha
(`PP0017`), usando o estilo de formatação configurado no projeto, não uma
indentação fixa. Somados aos que já existiam, catorze dos dezenove
diagnósticos agora oferecem correção
- **`PP0019` — `#pragma` desconhecido ou malformado** — o compilador rejeita uma
diretiva que não conhece (erro 207), mas só na compilação; agora o aviso
aparece enquanto se escreve, com *quick fix*. Cobre o nome errado
(`#pragma deprected` → sugere `deprecated`, comparando com a lista do
compilador) e a mensagem de `deprecated` escrita entre aspas — a diretiva toma
o resto da linha como texto livre, então as aspas entrariam na mensagem em vez
de delimitá-la. Aspas no meio do texto continuam sendo texto legítimo

### Corrigido
- **Comentário de outra parte do arquivo aparecendo no hover** — a varredura do
doc comment subia o arquivo acumulando linhas, e acabava trazendo réguas,
cabeçalhos de seção e o texto de outras funções para o hover do símbolo
abaixo. Passa a valer a mesma regra do Javadoc e do PHPDoc: **só o bloco
imediatamente acima da declaração** — um `/* … */` colado nela, ou uma
sequência contígua de linhas `//`. Uma linha em branco, ou qualquer código
entre os dois, separa. Entre o comentário e a declaração continua podendo
haver o `#pragma deprecated` que marca o símbolo
- **Doc comment de um símbolo aparecendo no hover de outro** — quando o
comentário era um bloco de uma linha só (`/** … */`), a varredura empurrava
essa linha e ia procurar o `/*` de abertura **a partir da linha anterior**,
atravessando o código acima até casar com o `/**` de outro comentário. O
hover de um `#define`, por exemplo, mostrava a documentação da função
anterior com o código do meio junto. Um `*/` sem abertura também deixa de
virar doc, em vez de arrastar o arquivo até o topo
- **Aviso de depreciado desalinhando o hover** — era escrito como *blockquote*
(`>`), o que fazia o editor recuar o bloco e ler a linha `---` seguinte como
continuação dele, desalinhando a documentação inteira. Passa a ser texto
normal — e agora traz junto a mensagem do `#pragma deprecated`, que antes só
aparecia no aviso de uso
- **`native`/`forward` com assinatura quebrada em várias linhas eram ignorados** —
ao fechar o `)`, o parser só criava o símbolo se a linha trouxesse `{`; sem
isso, ficava esperando um corpo que nunca chega, já que essas duas formas
declaram sem corpo e terminam em `;`. O símbolo se perdia: sem hover, sem
autocomplete e sem signature help. Atinge em cheio os includes do open.mp,
onde assinaturas longas em várias linhas são comuns — um
`ApplyActorAnimation`, com nove parâmetros em nove linhas, era invisível para
a engine
- **Comentário de documentação perdido quando `#pragma deprecated` ficava entre
ele e a declaração** — a varredura do doc caminha para cima e parava na
primeira linha que não fosse comentário, e a diretiva cortava o caminho. Passa
a pular a diretiva
- **CodeQL: análise ausente em PRs de docs e dependências** — o repositório usava
o *default setup* do CodeQL, que só analisa um pull request quando ele toca
arquivos das linguagens configuradas. Um PR que mexia apenas em documentação ou
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ src/
types.rs ← ParsedFile, Symbol, SymbolKind, IncludeDirective, Param
mod.rs ← re-exports públicos
analyzer/
codes.rs ← constantes PP0001–PP0013
codes.rs ← constantes PP0001–PP0019
diagnostic.rs ← PawnDiagnostic, Severity, construtores
includes.rs ← PP0001, PP0013 — resolve #include / #tryinclude
semantic.rs ← PP0002, PP0003, PP0004 — erros estruturais
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ O binário de debug é detectado automaticamente pela extensão PawnPro se estiv
```
src/
parser/ ← lexer, parser de símbolos e tipos
analyzer/ ← diagnósticos PP0001–PP0017
analyzer/ ← diagnósticos PP0001–PP0019
intellisense/ ← completions, hover, signature, codelens, references, semantic tokens, formatter
workspace.rs ← orquestra análise de cada arquivo
server.rs ← handlers LSP
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,36 @@
[![Clippy](https://img.shields.io/github/actions/workflow/status/NullSablex/PawnPro-Engine/ci.yml?style=flat-square&label=Clippy&logo=rust)](https://github.com/NullSablex/PawnPro-Engine/actions/workflows/ci.yml)
[![Security Audit](https://img.shields.io/github/actions/workflow/status/NullSablex/PawnPro-Engine/ci.yml?style=flat-square&label=Security%20Audit&logo=rust)](https://github.com/NullSablex/PawnPro-Engine/actions/workflows/ci.yml)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/NullSablex/PawnPro-Engine/badge?style=flat-square)](https://scorecard.dev/viewer/?uri=github.com/NullSablex/PawnPro-Engine)
[![Stars](https://img.shields.io/github/stars/NullSablex/PawnPro-Engine?style=flat-square&logo=github&label=stars)](https://github.com/NullSablex/PawnPro-Engine/stargazers)
[![License](https://img.shields.io/badge/licença-Source--Available-blue?style=flat-square)](LICENSE.md)

![Windows x64](https://img.shields.io/badge/Windows-x64-0078D4?style=flat-square&logo=windows11&logoColor=white)
![Linux x64 · arm64](https://img.shields.io/badge/Linux-x64%20·%20arm64-FCC624?style=flat-square&logo=linux&logoColor=black)
![macOS x64 · arm64](https://img.shields.io/badge/macOS-x64%20·%20arm64-000000?style=flat-square&logo=apple&logoColor=white)
</div>

Motor IntelliSense para a linguagem **Pawn** — servidor LSP em Rust integrado à extensão [PawnPro](https://github.com/NullSablex/PawnPro) para Visual Studio Code.
Motor de análise para a linguagem **Pawn** (SA-MP / open.mp) — um servidor LSP escrito em Rust, que dá à extensão [PawnPro](https://github.com/NullSablex/PawnPro) o IntelliSense e os diagnósticos.

## O que é

`pawnpro-engine` é o núcleo de análise do PawnPro. Roda como processo separado e se comunica com o editor via **Language Server Protocol (LSP)** sobre stdin/stdout — o mesmo protocolo usado por `rust-analyzer` e `clangd`.
`pawnpro-engine` é o núcleo de análise do PawnPro. Roda como **processo separado** e conversa com o editor pelo **Language Server Protocol** sobre stdin/stdout — o mesmo protocolo do `rust-analyzer` e do `clangd`.

A extensão PawnPro inicia o motor automaticamente ao detectar o binário. Se o binário não estiver presente, a extensão recua para o modo TypeScript como fallback transparente.
Estar fora do processo do editor é o que permite analisar um gamemode inteiro, com todos os seus includes transitivos, sem travar a digitação. Não é realce por expressão regular: o código é tokenizado e analisado de verdade, e é daí que vêm a resolução de símbolos entre arquivos, a contagem de referências e os diagnósticos com posição exata.

A extensão inicia o motor automaticamente quando encontra o binário; ele acompanha a instalação e não exige Rust nem nada instalado à parte.

## Capacidades

- **Diagnósticos** — 17 códigos `PP####` cobrindo erros de estrutura, código morto, símbolos não declarados, depreciação e indentação (ver [docs/diagnostics.md](docs/diagnostics.md)).
- **Diagnósticos** — 19 códigos `PP####` cobrindo erros de estrutura, código morto, símbolos não declarados, depreciação, indentação, nomenclatura e `#pragma` malformado; 13 deles com correção automática (ver [docs/diagnostics.md](docs/diagnostics.md)).
- **Completions** — símbolos de todos os includes transitivos com snippets de parâmetros; itens depreciados marcados.
- **Hover** — assinatura e comentário de documentação formatado (Javadoc `@param` e XMLdoc `<summary>`, o formato do `omp-stdlib`); em `#include` mostra o caminho resolvido.
- **Signature Help** — parâmetro ativo destacado ao digitar `(` e `,`.
- **CodeLens** — contagem de referências para todas as funções; clicável.
- **References** — `textDocument/references` (Shift+F12).
- **Semantic Tokens** — coloração semântica com suporte a chamadas multiline.
- **Formatação** — documento inteiro e seleção de intervalo.
- **Assistente de nomenclatura** — convenções de caixa por categoria, com padrão próprio por expressão regular para o que os estilos prontos não descrevem.
- **Code actions** — correção automática para 13 dos diagnósticos.
- **Invalidação granular** — ao salvar um include, o motor republica automaticamente os diagnósticos de todos os arquivos abertos que dependem dele, transitivamente.

Para detalhes do protocolo e das opções de configuração, consulte [docs/lsp.md](docs/lsp.md).
Expand Down
38 changes: 38 additions & 0 deletions docs/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,44 @@ O motor emite diagnósticos identificados por códigos `PP####`.
| `PP0016` | Aviso¹ | Função sem keyword declarada mas nunca chamada |
| `PP0017` | Aviso | Indentação inconsistente dentro de um bloco |
| `PP0018` | Hint | Nome de identificador pobre (assistente de nomes; desligado por padrão) |
| `PP0019` | Aviso | `#pragma` desconhecido ou malformado |

### PP0019 — `#pragma` desconhecido ou malformado

O compilador rejeita uma diretiva que não conheça (erro 207), mas só na compilação. Este diagnóstico antecipa o erro e oferece a correção.

Cobre dois casos:

- **Nome não reconhecido** — comparado com a lista do compilador (`sc2.c`); quando há uma diretiva próxima, é sugerida: `#pragma deprected` → `#pragma deprecated`.
- **Mensagem de `deprecated` entre aspas** — a diretiva toma **o resto da linha como texto livre**, sem aspas:

```pawn
#pragma deprecated Use BanPlayerFor // correto
#pragma deprecated "Use BanPlayerFor" // as aspas entram na mensagem
```

Aspas no meio do texto são literais legítimas e não são sinalizadas.

Nos dois casos há *quick fix*.

## Correções rápidas (quick fixes)

Onde a correção é determinística, o diagnóstico vem com uma ação aplicável por clique (`Ctrl+.` / lâmpada):

| Código | Ação |
|--------|------|
| `PP0002`, `PP0003` | Remover o corpo `{ … }` que o compilador não aceita em `native`/`forward` |
| `PP0004` | Adicionar corpo vazio, ou converter `public`/`stock` em `forward` |
| `PP0005`, `PP0006`, `PP0009`, `PP0016` | Remover a declaração não utilizada |
| `PP0010` | Trocar por um símbolo conhecido de nome parecido (erro de digitação) |
| `PP0011`, `PP0012` | Remover o `#define` / `#include` não utilizado |
| `PP0017` | Reindentar a linha, usando o estilo de formatação configurado |
| `PP0018` | Renomear para o estilo configurado |
| `PP0019` | Corrigir o nome da diretiva, ou remover as aspas da mensagem |

Os demais não têm correção automática: `PP0001` e `PP0013` dependem de onde o arquivo está; `PP0007` e `PP0008` dependem do que usar no lugar (que só quem escreveu sabe); `PP0014` e `PP0015` são informativos — remover uma `native` ou um `forward` de uma include quebraria quem a consome.

As remoções por "não utilizado" não são oferecidas em arquivos `.inc`, onde o símbolo costuma existir justamente para quem inclui o arquivo. A remoção de corpo ilegal (`PP0002`/`PP0003`), por ser erro de sintaxe, vale em qualquer arquivo.

> ¹ Marcados com `DiagnosticTag::UNNECESSARY` — o editor exibe o símbolo desbotado além do sublinhado diagnóstico.

Expand Down
14 changes: 13 additions & 1 deletion docs/lsp.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,19 @@ O motor registra três caracteres de disparo:
| `#` | Completions de diretivas (`#include`, `#define`, `#if`, `#ifdef`, etc.) com snippets |
| `@` | Tags de documentação (`@param`, `@return`, `@remarks`) — apenas dentro de comentários |

Completions normais (sem trigger) listam símbolos de todos os includes transitivos com snippets de parâmetros. Itens marcados com `#pragma deprecated` aparecem com tag de depreciação.
Completions normais (sem trigger) listam símbolos de todos os includes transitivos com snippets de parâmetros.

A lista é ordenada **pela proximidade do cursor**, não pelo alfabeto:

1. parâmetros e variáveis locais da função sob o cursor;
2. símbolos declarados no próprio arquivo;
3. símbolos vindos dos includes;
4. palavras-chave da linguagem;
5. qualquer símbolo marcado com `#pragma deprecated` — aparece com a tag de depreciação e vai para o fim, seja qual for sua origem.

Dentro de um grupo, a ordem é alfabética sem diferenciar maiúsculas.

Um projeto com muitos includes chega a milhares de símbolos. A resposta é cortada em **1000 itens** e marcada como `isIncomplete`, o que faz o editor pedir a lista de novo conforme o prefixo cresce — o mecanismo do LSP para listas grandes. Como o corte vem depois da ordenação, o que se perde são os itens mais distantes do cursor.

## Comentários de documentação

Expand Down
36 changes: 27 additions & 9 deletions docs/naming-assistant.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,14 @@ Em `.pawnpro/config.json`, seção `naming` (genérica, sem domínio):
"naming": {
"enabled": true,
"style": {
"functions": "camelCase", // camelCase | snake_case | PascalCase | off
"globals": "camelCase",
"locals": "camelCase",
"constants": "UPPER_CASE",
"enums": "PascalCase"
// Lista por categoria: o nome passa se casar com QUALQUER item.
// Lista vazia desliga a checagem daquela categoria.
"functions": ["camelCase"],
"globals": ["camelCase", "/^g_[a-z][a-zA-Z0-9]*$/"],
"locals": ["camelCase"],
"constants": ["UPPER_CASE"],
"macros": ["UPPER_CASE"],
"parameters": ["camelCase"]
},
"minLength": 2,
"allowShortInLoops": ["i", "j", "k"],
Expand Down Expand Up @@ -140,7 +143,22 @@ src/naming/
- Extração de locais: `src/naming/locals.rs` varre os tokens (o `StmtTree` não
guarda o identificador do `VarDecl`).
- **Estilo de caixa** (`src/naming/style.rs`): por categoria, em
`analysis.naming.style` (`functions`/`globals`/`locals`/`constants`/
`parameters`), cada um `camelCase`/`snake_case`/`PascalCase`/`UPPER_CASE`/`Capitalized_Snake`/`off`.
Padrão `off` em todas — só checa o que o usuário pedir. Ordem das regras:
placeholder → comprimento → estilo (a mais específica vence).
`analysis.naming.style` (`functions`/`globals`/`locals`/`constants`/`macros`/
`parameters`), cada uma uma **lista** de critérios — o nome passa se casar com
qualquer item; lista vazia desliga a checagem. Ordem das regras: placeholder →
comprimento → estilo (a mais específica vence).
- **`Rule`** é o critério aceito: `Builtin(Case)` para os cinco estilos
embutidos, ou `Custom { source, re }` para um padrão do usuário escrito entre
barras (`/^g_[a-z][a-zA-Z0-9]*$/`). É um tipo à parte de `Case` porque `Case` é
`Copy` e alimenta o `suggest`, que **gera** nomes — de um regex arbitrário não
se deriva um nome, então `Rule::builtin()` devolve `None` para eles e o quick
fix não oferece sugestão inválida.
- O padrão é compilado âncorado (`^(?:…)$`): descreve o nome inteiro, e o
agrupamento impede que uma alternância no topo ancore só os extremos.
- O `_` inicial **não** é removido antes da comparação, ao contrário dos
embutidos: quem escreve o padrão decide se o aceita.
- Um padrão inválido devolve `None` em `Rule::from_config` — a configuração é
do usuário e não pode derrubar a análise; a categoria só perde aquele
critério.
- `NameIssueKind::WrongStyle` carrega `Vec<String>` (e não `&'static str`)
porque o rótulo de um regex só existe em runtime.
1 change: 1 addition & 0 deletions src/analyzer/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ pub const PP0015: &str = "PP0015"; // Forward declarado mas nunca chamado
pub const PP0016: &str = "PP0016"; // Função plain (sem keyword) declarada mas nunca chamada
pub const PP0017: &str = "PP0017"; // Indentação inconsistente dentro de um bloco
pub const PP0018: &str = "PP0018"; // Nome de identificador pobre (assistente de nomes)
pub const PP0019: &str = "PP0019"; // #pragma desconhecido ou malformado
1 change: 1 addition & 0 deletions src/analyzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod hints;
pub mod includes;
pub mod indentation;
pub mod naming;
pub mod pragmas;
pub mod semantic;
pub mod undefined;
pub mod unused;
Expand Down
Loading