"fc --symbols, fc --lsp e a extensão do VS Code"
O índice de símbolos em JSON, o servidor de linguagem (definição, referências, hover, renomear) e o que a extensão do VS Code usa.
O que é e por que existe#
Para um editor oferecer "ir para a definição", "achar referências" e "renomear", alguém precisa entender o programa. Em vez de reimplementar o parser dentro de cada editor, o próprio fc expõe o que entende, em duas camadas:
| Camada | Comando | O que é |
|---|---|---|
| Dados | fc --symbols | escreve em JSON as declarações, as referências e os diagnósticos do fonte, sem compilar |
| Protocolo | fc --lsp | um servidor de linguagem (LSP 3.17, JSON-RPC por stdio) construído em cima do --symbols |
| Editor | extensão do VS Code | realce, trechos, leitura de erros e o cliente do --lsp (e do depurador --dap) |
fc --symbols#
fc --symbols prog.fxp [--symbols-out=arq.json] [--symbols-at=arq:linha:col]Faz só o parse (que aqui já resolve nomes e tipos), sem codegen nem ligador. Sem --symbols-out, o JSON vai para a saída padrão. A saída é 0 sempre que o JSON foi escrito, mesmo com erros no fonte: o parser é tolerante (sincroniza e segue), e o JSON traz "complete": false. Uma .fxm como entrada é lida com um programa-sonda virtual. A RTL é lida do fonte, para que TObject.Create etc. tenham arquivo e linha (origin: rtl).
O formato, resumido:
{ "symbols": {
"version": 1, "tool": "fc --symbols", "positionEncoding": "utf-8", "lineBase": 1, "columnBase": 1,
"complete": true,
"files": [ { "id": 0, "path": "...", "origin": "project|rtl", "module": "...", "readable": true } ],
"decls": [ { "id": 6, "name": "ScaledArea", "kind": "method", "file": 0, "line": 28, "col": 14,
"container": 4, "containerName": "TCircle", "visibility": "public",
"signature": "function TCircle.ScaledArea(AScale: Float64): Float64", "type": "Float64" } ],
"refs": [ { "file": 0, "line": 57, "col": 13, "endLine": 57, "endCol": 23, "decl": 6 } ],
"diagnostics": [ { "severity": "error|warning|hint", "file": 0, "line": 16, "col": 16, "message": "..." } ] } }kind pode ser module, type, class, record, interface, enum, enumMember, procedure, function, method, constructor, destructor, field, property, const, var ou param. Linha e coluna são base 1; a coluna é em bytes UTF-8 (a mesma das mensagens), com colU16 onde difere (o LSP padrão conta UTF-16). --symbols-at=arq:linha:col devolve só o símbolo naquela posição (uso ou declaração), com a declaração completa.
Com isso um adaptador faz: diagnostics → avisos do editor; definition = achar em refs o item cujo intervalo contém a posição e devolver a decl; references = todos os refs com a mesma decl; rename = declaração mais referências (recusar se origin for rtl); hover = decl.signature.
fc --lsp#
fc --lsp fala LSP 3.17 por stdio e oferece: diagnósticos enquanto se digita, ir para a definição, referências, hover (assinatura, tipo e o comentário ///), símbolos do documento e do workspace, completar (membros depois do . e identificadores do escopo) e renomear (com prepareRename; símbolos da RTL são recusados). O texto não salvo do editor é o que vale: o servidor o passa ao compilador por um arquivo de sobreposição (--symbols-overlay).
Cada diagnóstico leva code, source: "fc" e um codeDescription.href para o erros.md (códigos de diagnóstico).
A extensão do VS Code#
Além do realce e dos trechos (descritos em Extensão do VS Code), as versões recentes da extensão iniciam o fc --lsp sozinhas e registram o depurador fenner (fc --dap).
- Na pasta da extensão:
npm install(traz ovscode-languageclient), ou abra a pasta no VS Code e aperte F5 para testar. - Aponte o
fc: configuraçãomycompiler.lsp.path(ou a variávelMYCOMPILER_FC, ou ofcnoPATH). Ofc.configao lado dofc.exe(ouMYCOMPILER_RTL) precisa achar a RTL. - Abra um
.fxpou.fxm. A saída do servidor aparece em Output > MyCompiler LSP.
Outras configurações: mycompiler.lsp.enabled, mycompiler.lsp.debounceMs (espera sem digitar antes de reanalisar; padrão 700), mycompiler.lsp.check, workspaceIndex, rtlSnapshot e renameUnsafe (renomear mesmo com usos não resolvidos; padrão: recusa).
O que esperar (e os limites)#
- Cada análise é um
fc --symbolsde ~7 a 10 s (lê a RTL do fonte). O servidor nunca trava o editor: roda uma análise por vez, em segundo plano, e cancela as velhas. Não há análise residente (o parser tem estado global). - Os diagnósticos aparecem depois da primeira análise e são só os do
--symbols: erros de geração de código e de ligação não aparecem (omycompiler.lsp.checkroda umfc -Sem segundo plano). - O completar de membros só conhece a RTL que o projeto já cita, e acha o tipo do receptor pelo nome (não entende
X[i].,as,inherited,with). - Não geram referência:
Free, os intrínsecos (Ord,Inc,WriteLn…), os tipos embutidos (Int32,UString) e identificadores dentro de{…}de uma cadeia interpolada. A heurística de posição pode perder uma referência, nunca apontar para o alvo errado; por isso o rename pode sair incompleto. - Só Windows. O cliente do VS Code foi validado por um cliente de teste do protocolo; o próprio README admite que não foi executado num VS Code real.
Veja também#
- fc --doc, Linha de comando.
- Manual do compilador: seções "fc --symbols" e "Servidor de linguagem";
lsp-indice.md.