Files
tia-lsp/AGENTS.md
T

4.8 KiB

AGENTS.md - tia-lsp

A standalone LSP server for Siemens TIA Portal languages. Currently supports SCL; planned support for STL/AWL, LAD, FBD. This repo is the server only (src/ + tree-sitter grammar). The companion Neovim plugin lives in tia-lsp.nvim.

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

# Unit Tests
lua test/run_tests.lua       # Run all server tests
lua test/test_formatter.lua  # Run formatter tests
lua test/test_plc_json.lua   # Run plc_json tests

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

Project Structure

tia-lsp/
├── src/                     # LSP server (uses dofile)
│   ├── main.lua            # Entry point, JSON-RPC protocol
│   ├── parser.lua          # SCL parser (will become parser_scl.lua + dispatch)
│   ├── diagnostics.lua     # Linter (source = "tia_lsp")
│   ├── formatter.lua       # Document formatter
│   ├── treesitter.lua      # Tree-sitter integration
│   ├── plc_json.lua        # External UDT/DB loading (language-agnostic)
│   ├── builtin_instructions.lua  # TIA Portal built-in types
│   └── json.lua            # JSON encoder/decoder
├── grammar.js               # Tree-sitter grammar (language name: 'scl')
└── test/                    # Server tests

Naming Convention

  • Project name (repo, LSP server, mason package): tia-lsp / tia_lsp — umbrella for all TIA languages.
  • Language name (filetype, tree-sitter parser, grammar): scl — stays as-is. Future languages (stl, lad, fbd) get their own names.
  • LSP client name: tia_lsp (one client handles all TIA languages; dispatches by filetype / languageId).
  • Diagnostic source: tia_lsp.

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

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

Code Formatting

  • 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:

~/Documenta/siemens/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
  • Workspace type scanning