VS Code extension for IEC 61131-3 Structured Text. LSP-based architecture with client-server split.
Consult docs/IEC61131_SPECIFICATION.md before any language-related change. It is the authoritative source for keywords, operators, data types, standard function blocks, and vendor compatibility.
src/extension.ts - Extension entry, commands
src/validator.ts - Syntax validation
src/client/lsp-client.ts - LSP client
src/server/server.ts - LSP server
src/server/ast-parser.ts - AST parser (multi-line accumulator)
src/server/workspace-indexer.ts - Workspace symbol indexing
src/server/providers/completion-provider.ts
src/server/providers/definition-provider.ts
src/server/providers/formatting-provider.ts
src/server/providers/member-access-provider.ts
src/server/providers/rename-provider.ts
src/shared/types.ts - Shared type definitions
syntaxes/structured-text.tmLanguage.json - TextMate grammar
iec61131-definitions/ - Standard FB definitions (runtime, must ship in .vsix)
samples/ - User-facing example .st files
manual-tests/ - Internal QA test fixtures by feature
STASTParser.parseSymbols() -> consumed by server.ts (local SymbolIndex) and workspace-indexer.ts (WorkspaceSymbolIndex) -> providers consume indexed symbols for completion, definition, member-access, rename.
npm run compile # rm -rf out && tsc (dev/test output)
npm run webpack-prod # rm -rf dist && webpack (production bundle)
npm run clean # rm -rf out distout/= tsc output for dev/tests, excluded from .vsixdist/= webpack bundle shipped in .vsix- Both dirs cleaned before builds to prevent stale artifacts
npm run test:unit # compile + mocha unit tests (~307 tests, <1s)
npm run test:e2e # compile + @vscode/test-electron (needs display)
npm test # both- Mocha with
suite()/test(), files named*.unit.test.ts - Test discovery via glob
**/*.unit.test.jsin compiledout/ - Pre-commit hook (husky) runs unit + e2e before every commit
- All changes must pass:
npm run test:unitandnpm run webpack-prod
- TypeScript, 4-space indent, explicit types (no
any) - Files: kebab-case. Functions: camelCase. Classes: PascalCase. Constants: UPPER_SNAKE_CASE
- Language ID:
structured-text. Extensions:.st,.iecst - Keywords are case-insensitive per IEC 61131-3
- Prefer built-in VS Code/Node APIs over external deps
- Async/await over raw promises
- Dispose all disposables via
context.subscriptions
Use GitHub issue templates in .github/ISSUE_TEMPLATE/. Blank issues disabled.
| Template | Use for | Auto-label |
|---|---|---|
bug.yml |
Bugs, regressions | bug |
feature.yml |
New features, enhancements | enhancement |
technical-debt.yml |
Cleanup, refactoring, dep updates | technical-debt |
idea.yml |
Far-future concepts not yet ready for planning | idea |
Add Priority: and Effort: labels manually per issue. No emoji/icons in titles. Concise text, no time estimates.
main ← stable, tagged releases only; never commit directly
release-X.Y.0 ← integration branch per milestone; PRs merge here
feature/issue-N-desc ← new functionality
fix/issue-N-desc ← bug fixes
chore/desc ← tooling, config, deps
docs/desc ← documentation only
- All branch types target the active
release-X.Y.0via PR — no direct pushes, no exceptions mainupdated only when releasing — mergerelease-X.Y.0→mainvia a release PR, then tag- Hotfixes branch off
main, PR back to bothmainand the active release branch - Create
release-X.Y.0at milestone start; delete after merge tomain - Active release branch is the one matching the current in-progress milestone
At milestone start — create release branch:
git checkout main && git pullgit checkout -b release-X.Y.0 && git push -u origin release-X.Y.0- Apply branch protection (require PR, no force push, no deletion):
gh api repos/ControlForge-Systems/controlforge-structured-text/branches/release-X.Y.0/protection \ --method PUT --input - <<'EOF' { "required_pull_request_reviews": {"required_approving_review_count": 0}, "required_status_checks": null, "enforce_admins": true, "restrictions": null, "allow_force_pushes": false, "allow_deletions": false } EOF
During milestone — tag pre-release when QA starts:
- All milestone issues closed and merged into
release-X.Y.0 - Tag
vX.Y.0-rc.1onrelease-X.Y.0, publish as GitHub pre-release — signals QA in progress, not for production
When QA complete — ship:
- Move
CHANGELOG.md[Unreleased]entries to[X.Y.0] - YYYY-MM-DD - Open PR
release-X.Y.0→main - Merge PR, tag
vX.Y.0onmain vsce package && vsce publish- Close the milestone
- Delete
release-X.Y.0branch
Template at .github/PULL_REQUEST_TEMPLATE.md. Two sections:
- Summary — 1-3 bullet points: what changed, why, closes which issues
- Testing — what ran and results (e.g.
npm run test:unit: 44 passing)
Title format: vX.Y.0 - Short Theme (e.g. v1.3.0 - Stability & Core LSP). Theme should describe the capability area, not "Phase N". Description lists concrete deliverables. Each issue gets a vX.Y.0 release label matching its milestone. Keep labels and milestones in sync.
Follow Keep a Changelog with Fixed/Added/Changed/Removed sections. No severity labels. Concise, no implementation details. Unreleased work goes under [Unreleased].