Files
lazar 8255db7f64 chore: adjust for plugin-only scope after split
After splitting from the original scl_lsp repo, update for plugin-only scope:

- README.md: focused on Neovim plugin installation, configuration,
  commands, keybindings, and editor features
- AGENTS.md: plugin-only project structure and conventions
- test/run_tests.lua: run only plugin tests (udt, db, fb, integration)
- test/test_integration.lua: remove plc_json dependency (server module)
- queries/: move from queries/*.scm to queries/scl/*.scm (nvim-treesitter
  per-language convention)
- ftdetect/scl.vim: add filetype detection for .scl/.udt/.db files
2026-07-18 11:46:18 +02:00

5.0 KiB

AGENTS.md - tia-lsp.nvim

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

Build/Lint/Test Commands

# Lua Linting
luacheck lua/tia/             # Lint language modules
luacheck lua/tia_lsp/         # Lint plugin entry point

# Unit & Integration Tests
lua test/run_tests.lua       # Run all plugin 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_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

tia-lsp.nvim/
├── lua/tia/                 # Language modules (uses require)
│   ├── 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
├── lua/tia_lsp/             # Plugin entry point
│   ├── init.lua            # Main plugin setup (vim.lsp.start)
│   └── plugins/scl.lua     # LazyVim plugin spec
├── queries/scl/             # Tree-sitter queries (per-language)
└── ftdetect/scl.vim         # Filetype detection

Naming Convention

  • Project name (repo, Lua module): tia-lsp.nvim / tia_lsp — umbrella for all TIA languages.
  • Language name (filetype, tree-sitter parser): 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).
  • Commands: :SCLFormat, :SCLShowVariables, etc. — per-filetype prefix. Future: :STLFormat, etc.

Filetype Detection

ftdetect/scl.vim registers the following extensions as scl filetype:

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/tia/ modules: require("tia.module_name")
  • lua/tia_lsp/ entry: require("tia_lsp")
  • External dependencies: wrap in pcall() for safety

Code Formatting

  • lua/tia/ and lua/tia_lsp/: 2 spaces 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:

  • Plugin features (auto-prefix, workspace scanning, attribute toggle)
  • Workspace type scanning