Without this, DATA_BLOCK wasn't recognized as a declaration block,
so the stack was never populated for DB files. END_DATA_BLOCK fell
through to regular-line handling and got a tab prefix (from body
get_indent()), while DATA_BLOCK itself was flush-left.
Added DATA_BLOCK to both BLOCKS (ender = END_DATA_BLOCK) and
DECLARATIONS so it gets the same treatment as FUNCTION_BLOCK:
stack cleared, no_indent=true, and END_DATA_BLOCK handled by the
declaration ender path (flush-left output).
- Strip UTF-8 BOM so FUNCTION_BLOCK is correctly detected
- Add no_indent=true on DECLARATIONS blocks so they don't inflate body indent
- Fix is_case_label to match quoted strings (was using \s instead of %s)
- Push CASE_CONTENT block with label so content inside case labels gets
proper nesting (one extra level beyond the label)
- All 38 formatter tests pass
- LLT_EquipmentManager.scl now matches TIA Portal export exactly
When the user has typed something like #instTestTimer., has_hash_prefix
was true and the # trigger branch ran instead of the . branch, so dot
completion (IN/PT/Q/ET) never appeared. Changed the condition from
'if trigger == "#" or has_hash_prefix' to 'if trigger == "#"' so
the . branch always wins when trigger is ..
When nvim-cmp sends completion before didChange commits the ., the
cursor col is at the word end, but the old code always scanned from
col-1, cutting off the variable's last character (instTestTime vs
instTestTimer). Now checks if the character at col is . or empty;
if so, scans from col-1; otherwise scans from col.
- SCL006 now fires for both unassigned inputs (IN := <-,) and outputs (Q =>)
- Formatter removes both IN := and Q => when no value follows
- When ALL params are unassigned, the call collapses to name()
- Formatter now strips unassigned output params (e.g. Q =>, ET =>)
from FB/FC calls during formatting.
- Diagnostics now produce SCL006 warning for unassigned output params.
- Also converted src/ symlinks for diagnostics.lua and formatter.lua
in mason package so all three files stay in sync.
16#FFFF (hex), 2#1010 (binary), 8#777 (octal) are TIA Portal
base-specific number literals, not hash-prefixed variables.
Check if the character before # is a digit to distinguish them.
SCL005 false positive triggered on:
timers{InstructionName := 'IEC_TIMER'; LibVersion := '1.0'}
These are valid TIA Portal attributes for IEC timer variables.
When LazyVim auto-close inserts () after (, nvim-cmp sees ) as the
last character and may suppress the popup. By returning variables,
builtins, and data types alongside the function params, the response
is rich enough that the next keystroke (e.g. typing I after auto-close)
triggers a fresh TextChanged completion that re-detects the call
context via get_call_context in the else branch.
Nine sites used name:upper():find('^' .. prefix:upper(), 1, true).
With plain=true, '^' is treated as a literal character, so the prefix
was never found and no variables/params ever matched the typed text.
Replaced all with substring comparison:
name:upper():sub(1, #prefix) == prefix:upper()
This fixes:
- Variable completion without # prefix (instTestTimer never matched)
- Function parameter filtering by typed prefix (IN never matched IN)
- # trigger variable filtering
- When user types without # (e.g. instTestTimer), the label was still
#instTestTimer, so client-side filtering rejected the match.
- Now label = name (bare) in general/var-section context, insertText
still adds the # prefix for correct SCL insertion.
- # trigger branch sets filterText = "#" .. name to match the #-prefixed
word the client sends after typing #.
Variable labels use # prefix (#instTestTimer) but users type without #
(instTestTimer). Without filterText, the client filters using the label
and #instTestTimer doesn't match instTestTimer, so the variable never
appears in autocomplete. Setting filterText to the bare variable name
fixes this.
Also removed aggressive fallback in else branch that treated any word
typed as function call context even without a ( in the line.
The word_prefix calculation was scanning backwards from col-1, so after
instTestTimer( it would pick up 'instTestTimer' and cause the client to
filter out all parameters (none start with the function name). Fixed to
scan from col so delimiters like ( stop the scan, giving word_prefix=''.
Also added fallback in else branch when ( is not yet in the document line
(timing issue where completion arrives before didChange is processed).
VCI-exported .scl files use all-caps attribute names (EXTERNALACCESSIBLE,
EXTERNALVISIBLE, EXTERNALWRITABLE) which were not in the allowed list,
triggering spurious SCL005 warnings.
diagnostics.lua:
- Add VAR DB_SPECIFIC to section detection
- Make all VAR/END_VAR detection case-insensitive (matches grammar)
- Replace 20-line hardcoded type check list with builtin.is_known_data_type()
and builtin.is_known_type_or_instruction() — now uses the expanded
DATA_TYPES table from Phase 4 instead of inline string comparisons
- Add attribute validation: warn on unknown per-variable attributes
(SCL005). Valid attributes: ExternalWritable, ExternalVisible,
ExternalAccessible, S7_SetPoint, S7_Optimized_Access
- Allow optional semicolon after END_VAR
parser.lua:
- Add VAR DB_SPECIFIC to section detection and var_type classification
- Make all block detection case-insensitive (FUNCTION_BLOCK, FUNCTION,
ORGANIZATION_BLOCK, END_FUNCTION_BLOCK, etc.)
- Make TYPE/END_TYPE, STRUCT/END_STRUCT, VAR/END_VAR case-insensitive
- Fix VAR RETAIN vs VAR NON_RETAIN classification (NON_RETAIN was
matching the RETAIN branch first)
- Allow optional semicolon after END_VAR and END_STRUCT
formatter.lua:
- Add VAR DB_SPECIFIC and VAR_DB_SPECIFIC to BLOCKS and VAR_KEYWORDS
- Fix get_keyword() to handle multi-word VAR keywords (VAR NON_RETAIN,
VAR DB_SPECIFIC, VAR CONSTANT, VAR RETAIN) — normalizes spaces to
underscores so the keyword lookup succeeds
- Fix is_new_statement detection for space-form VAR keywords
All 38 formatter tests pass. Parser and diagnostics load without errors.
The semantic tokens delta calculation had a bug where previous delta lines
were used instead of absolute lines, causing cumulative errors that led
to 'Invalid line: out of range' in Neovim's semantic token renderer.
Since semantic tokens were already disabled (full=false, range=false) but
Neovim still attempted to process them, remove the capability entirely
so Neovim doesn't set up the semantic token decoration provider at all.
After splitting from the original scl_lsp repo, update documentation
and test infrastructure to reflect the server-only scope of this repo.
- README.md: focused on LSP server features, tree-sitter grammar,
running the server, and Mason installation reference
- AGENTS.md: server-only project structure and conventions
- test/run_tests.lua: run only server tests (formatter, plc_json)
Rename the project from scl_lsp to tia_lsp to future-proof for additional
TIA Portal languages (STL/AWL, LAD, FBD). The 'scl' language name is kept
as-is for the SCL filetype and tree-sitter parser; future languages get
their own names (stl, lad, fbd).
Project-wide changes:
- lua/scl_lsp/ → lua/tia_lsp/ (plugin entry point)
- lua/scl/ → lua/tia/ (language modules, shared across TIA langs)
- require('scl.*') → require('tia.*')
- require('scl_lsp') → require('tia_lsp')
- LSP client name: 'scl_lsp' → 'tia_lsp'
- Diagnostic source: 'scl_lsp' → 'tia_lsp' (src/diagnostics.lua)
- package.json name: 'scl-language-server' → 'tia-lsp'
lua/tia_lsp/init.lua:
- Add vim.fn.exepath('tia-lsp') detection so the plugin prefers the
mason-installed executable and falls back to { 'lua', server_path }
for manual installs.
- Add opts.ts_parser_url to configure the tree-sitter parser source.
- Remove hardcoded ~/dev/scl_lsp paths in favor of ~/dev/tia-lsp and
the new ts_parser_url option.
Cleanup:
- Delete lua/tia/init.lua (dead code, unreferenced duplicate of tia_lsp).
- Delete scl_lsp.sh (replaced by the mason-installed bin/tia-lsp wrapper).
- Update README.md and AGENTS.md to reflect the two-repo split
(tia-lsp server + tia-lsp.nvim plugin) and document Mason installation.
Unchanged (intentional):
- grammar.js, src/grammar.json, src/parser.c: tree-sitter language name
remains 'scl' (it's the language, not the project).
- Filetype patterns ('scl', 'udt', 'db') in autocmds.
- :SCL* command names (per-filetype prefix; future :STL* etc.).
Tests pass with the same pre-existing failures as before the rename
(formatter VAR_INPUT test, udt_parser line 199 const-variable bug,
plc_json reference-project-missing). No new failures introduced.
The formatter was incorrectly treating multi-line attribute blocks as
assignments, causing AUTHOR/VERSION lines to get weird indentation.
Lines like:
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
Were being processed incorrectly because the attribute block line
contains := but no semicolon, causing the formatter to treat the
next line as a continuation.
Fix by tracking in_attr_block state and skipping assignment detection
for lines inside or exiting attribute blocks.
- SCLFormat: Remove clear_buffer_state() call that was preventing
expand after formatting (attr_toggle state now preserved)
- Diagnostics: Fix false 'Undefined variable' for IEC literals
like T#2s, D#1d, S5T#1s (TIME, DATE, S5TIME types)
- Formatter: Fix block comments (* ... *) being misidentified as
FB calls in VAR_IN_OUT/VAR_INPUT/VAR_OUTPUT sections
- Formatter: Fix array-based FB calls like instTimers[idx].TON()
not being properly formatted with aligned parameters
- 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
- Add in_condition state to track when inside IF/ELSIF condition (before THEN)
- Use condition_indent for continuation lines in conditions
- Fixes multiline IF/ELSIF conditions being incorrectly indented
- Add comprehensive formatter test suite with 38 tests
- Test multiline assignments, FB calls, control structures
- Test IF, FOR, WHILE, CASE, REPEAT statements
- Test regions, structs, var sections
- Update AGENTS.md with formatter test command
- Fix formatter to properly detect when multiline assignment ends
- Add check for line ending with := and ; before checking continuation
- Fixes issue where subsequent lines were incorrectly indented
- Fix formatter to detect continuation lines that don't start with
operators (e.g., #variable AND ... continuation lines)
- All continuation lines now align vertically after :=
- Format multi-line assignments with proper alignment
- Format FB calls in multiline style with parameter alignment
- Auto-expand collapsed attribute blocks before save
- Rename command from LspSCLFormat to SCLFormat
- Condense AGENTS.md and add testing section with reference project
- Skip // line comments when finding type separator colon
- Only match : that is NOT followed by = (avoid := assignment)
- Remove line comments from type_part before extraction
- Fixes false 'Unknown data type' warnings in VAR sections
- Add pattern matching for quoted type names like "MyUDT"
- Handle quoted types in ARRAY ... OF declarations
- Skip empty base_type to avoid spurious warnings
- Fix UDT TYPE parsing: don't clear pending_type_name on VERSION/TITLE lines
- Fix UDT field type extraction: handle attribute blocks before colon
- Add comment capture for UDT type definitions
- Add better error messages when member resolution fails
- Handle quoted type names in field declarations
- Fix chain detection to properly extract #var and member path
- Check all type sources (doc.types, external_types, builtins) for resolution
- Return helpful error messages when type resolution fails
- Prevent fallthrough to type check when member access is detected
- Show which type couldn't be found in resolution chain
- Add member access chain resolution in hover handler
- Parse #var.member.submember notation and resolve type chain
- Show member comments in hover tooltip
- Extract comments from UDT and DB file fields
- Combine all type sources (doc.types, external_types, builtin)
- Add type_definition rule for UDT files (TYPE/STRUCT/END_STRUCT/END_TYPE)
- Add data_block rule for DB files (DATA_BLOCK/BEGIN/END_DATA_BLOCK)
- Add NON_RETAIN and RETAIN modifiers support in data blocks
- Keywords now highlighted correctly in .udt and .db files