Files
tia-lsp/AGENTS.md
T
lazar 4429280524 docs(AGENTS.md): Update AGENTS.md
The new AGENTS.md includes:
- Reference project for testing
- Build/test commands (LSP and tree-sitter)
- Condensed project structure
- Code style guidelines (module pattern, naming, imports, comments)
- Parser module API convention
- Cache management patterns
- Error handling patterns (return pattern, pcall, validation)
- Completion system documentation
- Commands table
2026-02-18 10:25:35 +01:00

5.1 KiB

AGENTS.md - SCL Language Server (Unified)

A unified Neovim/LazyVim plugin for Siemens SCL language support providing LSP, linting, formatting, syntax highlighting, and auto-completion.

Reference Project for Testing

For manual testing and SCL/UDT/DB file examples, use:

~/dev/siemens/projects/scl_lang_support_lazyvim_ref_project

Build/Lint/Test Commands

LSP Server

make start                    # Start LSP server
make test                     # Run LSP in test mode
lua src/main.lua --test       # Direct test mode execution

Tree-sitter Parser

tree-sitter generate          # Generate parser from grammar.js
tree-sitter test              # Run tree-sitter tests
tree-sitter parse <file.scl>  # Parse a single SCL file

Project Structure

scl_lsp/
├── src/                    # Standalone LSP server (uses dofile)
│   ├── main.lua           # Entry point, JSON-RPC protocol
│   ├── parser.lua         # Regex-based SCL parser
│   ├── diagnostics.lua    # Linter diagnostics
│   └── formatter.lua      # Document formatter
├── lua/scl/                # Neovim plugin modules (uses require)
│   ├── blink_cmp_source.lua   # blink.cmp completion source
│   ├── udt_parser.lua         # UDT (.udt) parser
│   ├── db_parser.lua          # Global DB (.db) parser
│   ├── fb_parser.lua          # Function Block parser
│   ├── variables.lua          # Local variable extraction
│   ├── workspace_types.lua    # Workspace scanning
│   └── multiline_params.lua   # FB parameter filling
└── queries/                # Tree-sitter queries

Code Style Guidelines

Module Pattern

All modules follow the standard Lua module pattern:

-- Module description comment at top
local M = {}

-- Private module-level cache
local cache = {}

function M.public_function()
end

local function private_function()
end

return M

Naming Conventions

  • Functions/variables: snake_case (e.g., get_udt_members, var_types)
  • Constants: UPPER_SNAKE_CASE (e.g., PROJECT_MARKERS)
  • Private functions: declare as local function before public functions
  • Boolean variables: prefix with is_, has_ (e.g., is_udt, has_members)

Imports

  • lua/scl/ modules: use require("scl.module_name")
  • src/ modules: use dofile(script_path .. "/module.lua")
  • External dependencies: wrap in pcall() for safety

Comments

  • Every file must have a header comment explaining its purpose
  • All functions should have a brief comment describing what they do
  • Complex logic requires inline comments explaining the "why"
  • Non-obvious regex patterns must have explanatory comments

Parser Module API Convention

All parser modules (udt_parser, db_parser, fb_parser) must implement:

  • parse_*_file(filepath) - Parse a file and cache result
  • parse_*_content(content, filename) - Parse string content
  • get_*(name) - Get single cached item by name
  • get_all_*_names() - Return list of all cached names
  • get_*_members(name) - Get members/fields for a type
  • is_*_type(name) - Check if name is a known type
  • clear_cache() - Clear all cached data
  • get_cache_count() - Return number of cached items

Cache Management

  • Use module-level local tables for caching: local cache = {}
  • Clear caches by iterating and setting to nil, not by reassigning:
function M.clear_cache()
  for k in pairs(cache) do
    cache[k] = nil
  end
end

Error Handling

Return Pattern

Functions that can fail return nil, "error message":

function M.parse_file(filepath)
  local file = io.open(filepath, "r")
  if not file then
    return nil, "Cannot open file: " .. filepath
  end
  -- ... parse logic
  return result
end

External Dependencies

Always wrap external requires in pcall():

local ok, module = pcall(require, "some_module")
if ok and module then
  module.do_something()
end

Validation

Check required parameters early and return sensible defaults:

function M.get_members(type_name)
  if not type_name then
    return {}
  end
  -- ... continue
end

Completion System

Trigger Characters

  • # - Local variables (after BEGIN)
  • . - Member access (UDT/DB members)
  • " - Global DB names
  • ( - FB/Function parameters
  • (space) - General completion

Active Portion Detection

When matching patterns in completion, use the "active portion" of the line (after the last operator) to correctly handle expressions like:

"DB1".member := "DB2".member

Find the last :=, =>, =, <>, >=, <=, >, <, AND, OR, NOT and only match patterns after it.

Commands

Command Description
:SCLShowVariables Show local variables in current file
:SCLShowWorkspaceTypes Show workspace UDTs, FBs, and Global DBs count
:SCLRescanWorkspaceTypes Rescan workspace for types and DBs
:SCLPrefixWord Manually prefix current word with #
:SCLMultilineParams Fill multiline parameters for FB/Function call
:LspSCLFormat Format current SCL file
:SCLGeneratePlcJson Generate plc.data.json from data_types/