Files
tia-lsp.nvim/AGENTS.md
T
lazar 15b0e5360a docs: encourage adding comments; fix nested IF/ELSIF indentation
- Update AGENTS.md to encourage comments instead of forbidding them
- Add docstrings to all public functions in src/ modules
- Add module headers and function comments to lua/scl/ modules
- Fix formatter to properly indent multi-line IF/ELSIF conditions
- Fix FB call detection to not match IF statements with parentheses
2026-02-23 17:40:44 +01:00

5.3 KiB

AGENTS.md - SCL Language Server

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

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 execution

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

# Lua Linting
luacheck src/                 # Lint LSP server code
luacheck lua/scl/             # Lint plugin modules

# Unit & Integration Tests
lua test/run_tests.lua       # Run all tests
lua test/test_udt.lua        # Run UDT parser tests
lua test/test_db.lua         # Run DB parser tests
lua test/test_fb.lua         # Run FB parser tests
lua test/test_formatter.lua  # Run formatter tests
lua test/test_plc_json.lua   # Run plc_json tests
lua test/test_integration.lua # Run integration tests

# Reference Project
# Tests use files from: ~/dev/siemens/projects/scl_lang_support_lazyvim_ref_project/
# Override with: SCL_REF_PROJECT=/path/to/ref lua tests/run_tests.lua

Project Structure

scl_lsp/
├── src/                     # LSP server (uses dofile)
│   ├── main.lua            # Entry point, JSON-RPC protocol
│   ├── parser.lua          # SCL parser
│   ├── diagnostics.lua     # Linter
│   ├── formatter.lua       # Document formatter
│   ├── treesitter.lua      # Tree-sitter integration
│   ├── plc_json.lua        # External UDT/DB loading
│   ├── builtin_instructions.lua  # TIA Portal built-in types
│   └── json.lua            # JSON encoder/decoder
├── lua/scl/                 # Neovim plugin (uses require)
│   ├── init.lua            # Main plugin setup
│   ├── udt_parser.lua      # UDT parser
│   ├── db_parser.lua       # Global DB parser
│   ├── fb_parser.lua       # Function Block parser
│   ├── variables.lua       # Variable extraction
│   ├── workspace_types.lua # Workspace scanning
│   ├── multiline_params.lua
│   ├── auto_prefix.lua
│   └── attr_toggle.lua
├── queries/                 # Tree-sitter queries
└── grammar.js               # Tree-sitter grammar

Filetype Detection

Required in Neovim config (ftdetect/scl.vim):

au BufRead,BufNewFile *.scl set filetype=scl
au BufRead,BufNewFile *.udt set filetype=scl
au BufRead,BufNewFile *.db set filetype=scl

Code Style Guidelines

Module Pattern

local M = {}

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)
  • Constants: UPPER_SNAKE_CASE (e.g., PROJECT_MARKERS)
  • Private functions: declare as local function before public functions
  • Booleans: prefix with is_, has_ (e.g., is_udt, has_members)

Imports

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

Code Formatting

  • lua/scl/: 2 spaces indentation
  • src/: tab-based indentation
  • Max line length: 120 characters
  • No trailing whitespace

Comments

Add comments to explain non-obvious code sections, complex logic, and public API functions. Keep comments concise and relevant.

Parser Module API

All parser modules must implement:

  • parse_*_file(filepath) - Parse and cache
  • parse_*_content(content, filename) - Parse string
  • get_*(name) - Get cached item
  • get_all_*_names() - List all cached names
  • get_*_members(name) - Get members/fields
  • is_*_type(name) - Check if known type
  • clear_cache() - Clear cached data
  • get_cache_count() - Return cache size

Cache Management

local cache = {}

function M.clear_cache()
  for k in pairs(cache) do
    cache[k] = nil
  end
end

Error Handling

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

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

Lua Reserved Keywords

Cannot use reserved keywords as table keys directly:

-- WRONG
local range = { start = pos1, end = pos2 }

-- CORRECT
local range = { start = pos1, ["end"] = pos2 }

Critical for LSP range objects with end field.

Type System

The LSP validates data types and provides warnings for unknown types:

  • Elementary: BOOL, BYTE, WORD, DWORD, INT, DINT, REAL, TIME, STRING, etc.
  • Timer/counter: IEC_TIMER, TON_TIME, TOF_TIME, CTU, CTD, CTUD
  • Built-in FBs: TON, TOF, TP, R_TRIG, F_TRIG, etc.
  • Built-in functions: ADD, SUB, MUL, DIV, SIN, COS, SQRT, etc.
  • Workspace types: UDTs from .udt files, DBs from .db files

Testing

Use the reference project for testing:

~/dev/siemens/projects/scl_lang_support_lazyvim_ref_project/

Contains .scl, .db, .udt, and .xml files for comprehensive testing of:

  • LSP features (hover, completion, go-to-definition)
  • Formatter (:SCLFormat)
  • Attribute block toggle
  • Workspace type scanning