├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── greetings.yml │ └── publish.yml ├── .gitignore ├── .prettierrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── ST.g4 ├── example ├── test.iecst ├── test.md └── text.xml ├── extension-icon.png ├── icon.drawio.png ├── images ├── demo.gif ├── icon.drawio.png └── md-highlight.png ├── language-configuration.json ├── package-lock.json ├── package.json ├── snippets └── st.snippets.json ├── src ├── extension.ts ├── formatter.ts └── symbolprovider.ts ├── syntaxes ├── md.codeblock.json ├── st.tmLanguage.json └── xml.codeblock.json ├── text ├── IECST.xtext ├── test.iecst ├── test.md ├── test.st └── text.xml └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.yml] 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/naming-convention": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | }, 19 | "ignorePatterns": [ 20 | "out", 21 | "dist", 22 | "**/*.d.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.{cmd,[cC][mM][dD]} text eol=crlf 3 | *.{bat,[bB][aA][tT]} text eol=crlf -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: serhioromano 2 | custom: https://www.paypal.com/donate/?hosted_button_id=UTU4EMPLLLX54 -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | issues: 8 | types: 9 | - opened 10 | 11 | jobs: 12 | greeting: 13 | name: Greet new participant 14 | runs-on: ubuntu-latest 15 | permissions: 16 | issues: write 17 | pull-requests: write 18 | steps: 19 | - uses: actions/first-interaction@v1 20 | with: 21 | repo-token: ${{ secrets.GITHUB_TOKEN }} 22 | issue-message: "## Welcome! \n\nThank you for finding time to write an issue and make this extension better. I`ll appreciate if you find some time to [rate this extension here](https://marketplace.visualstudio.com/items?itemName=Serhioromano.vscode-st&ssr=false#review-details). \n\n I`ll get back to your ASAP." 23 | pr-message: "## Welcome! \n\nThank you very match for your contribution. I very appreciate it. Please find some time to [rate this extension here](https://marketplace.visualstudio.com/items?itemName=Serhioromano.vscode-st&ssr=false#review-details). \n\n I`ll get to this PR ASAP." 24 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - 1.* 7 | jobs: 8 | publish: 9 | name: Publish VS Code Extension 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout Tag 14 | uses: actions/checkout@v4 15 | with: 16 | ref: ${{ github.event.push.ref }} 17 | 18 | - name: Use Node.js 20 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: '20' 22 | cache: 'npm' 23 | - name: Install NPM Packages 24 | run: | 25 | npm -v 26 | npm ci 27 | npm install -g vsce ovsx semver 28 | - name: Package 29 | run: vsce package 30 | - name: Publish 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | run: | 34 | file=$(realpath $(ls -t *.vsix | head -1)) 35 | vsce publish -i $file -p ${{ secrets.VSCE_TOKEN }} 36 | npx ovsx publish $file -p ${{ secrets.OVSX_TOKEN }} 37 | gh release create '${{ github.ref_name }}' -n "See [Changelog](https://github.com/Serhioromano/vscode-st/blob/master/CHANGELOG.md) for details." $file 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.vsix 3 | out 4 | src/test 5 | npm-debug.log -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "printWidth": 110, 4 | "tabWidth": 4, 5 | "useTabs": false, 6 | "semi": true, 7 | "bracketSpacing": false, 8 | "parser":"typescript", 9 | "endOfLine": "lf", 10 | "singleQuote": false 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "hediet.vscode-drawio", 6 | "Serhioromano.vscode-st" 7 | ] 8 | 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--profile=ExtensionDebug", 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "${defaultBuildTask}" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "args": [ 26 | "--extensionDevelopmentPath=${workspaceFolder}", 27 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 28 | ], 29 | "outFiles": [ 30 | "${workspaceFolder}/out/**/*.js" 31 | ], 32 | "preLaunchTask": "${defaultBuildTask}" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.language": "ru,en", 3 | "workbench.colorTheme": "Default Dark+", 4 | "spellright.language": [ 5 | "en" 6 | ], 7 | "spellright.documentTypes": [ 8 | "markdown", 9 | "latex", 10 | "plaintext" 11 | ], 12 | "i18n-ally.localesPaths": [], 13 | "gitflow.replaceSymbol": "-", 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .github/** 3 | .vscode-test/** 4 | .gitignore 5 | out/test/** 6 | out/**/*.map 7 | src/** 8 | tsconfig.json 9 | tslint.json 10 | st_demo.st 11 | test.iecst 12 | test.xml 13 | test.md 14 | images/icon.psd 15 | images/demo.gif 16 | images/md-highlight.png 17 | index.js -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "st" extension will be documented in this file. 4 | 5 | ## [1.12.30] - 2025-04-28 6 | 7 | - add SEL RTAC (real time automation controller) exports its ST files as xml with the ST code in Implementaiton or Interface tags. This PR simply adds those two tags. 8 | 9 | ## [1.12.29] - 2024-09-29 10 | 11 | - add GX Works Devices syntax support 12 | - add GX Works instructions syntax highlights 13 | 14 | ## [1.12.25] - 2022-10-18 15 | 16 | - add `PROPERTY` with SET and GET and snippet 17 | - add `METHOD` snippet 18 | - add - `CLASS ... END_CLASS` keywords and snippet 19 | 20 | ## [1.12.14] - 2022-10-18 21 | 22 | - fix - highlight of names with `_TO_` in it 23 | - fix - comment in comment highlight 24 | - fix - capitalization of s,h,m in `T#***` 25 | 26 | ## [1.12.12] - 2022-08-07 27 | 28 | - fix - Extra spaces was added after tab or in front of the comment. 29 | - add - `VAR_INST` and `VAR_STAT` support 30 | - enhance - highlight Siemens SCL 31 | - add - `(END_?)CONST`, `(END_?)ORGANIZATION_BLOCK`, `(END_?)DATA_BLOCK`, `(END_?)LABEL`, `BEGIN`, `COUNTER`, `VOID`, `NIL` and `GOTO` keywords 32 | - add - support for `PQ` and `PI` in constants like `%PQW10.0`. 33 | 34 | ## [1.11.2] - 2022-01-20 35 | 36 | - fix - highlight for typed variables with - (`INT#-100`) 37 | - fix - highlight for typed variables with numeric constants (`INT#16#10AE`) 38 | 39 | ## [1.11.1] - 2022-01-07 40 | 41 | - enhance - performance improve by using bundler 42 | - enhance - Format add spaces refactored performance and logic improvement. 43 | 44 | ## [1.10.8] - 2021-11-21 45 | 46 | - fix - Format `<>` and `**` correctly. 47 | - fix - Document format in strings fixed. Now formatting is not done in strings and comments. 48 | 49 | ## [1.10.5] - 2021-08-07 50 | 51 | - improve - Formatting add spaces new conditions. 52 | 53 | ## [1.10.4] - 2021-05-17 54 | 55 | - improve - Formatting. Add spaces in comments and other places like before `:=` or after. 56 | - add - file extension `*.TcDUT`, `*.TcPOU`. 57 | 58 | ## [1.10.1] - 2021-02-04 59 | 60 | - add- syntax highlights for comment attributes like `(* @NESTEDCOMMENTS := 'Yes' *)` generated by CoDeSys 2.3 in `.EXP` files. 61 | - add - Pragma attribute and message highlights like `{attribute 'call_after_global_init_slot' := 'slot'}` or `{info 'TODO: should get another name'}` supported in CoDeSys. 62 | 63 | ## [1.10.0] - 2021-02-03 64 | 65 | - fix - lot of minor syntax highlight mistakes. 66 | - improve - syntax highlight was fully revised and improved a lot. 67 | 68 | ## [1.9.2] - 2021-02-02 69 | 70 | - enhance - Outline now understand `CONSTANT` variables. Thanks to @msftrncs. 71 | - fix - `VAR` word break keyword parse issue. Thanks to @msftrncs. 72 | - fix - Some issues outline tree parse. Thanks to @msftrncs. 73 | 74 | ## [1.9.1] - 2021-02-01 75 | 76 | - add - Formatting capitalization for all `VAR_*` keywords and `__NEW` and `__DELETE` 77 | - fix - one closed comment formatting as function #29 78 | 79 | ## [1.9.0] - 2021-02-01 80 | 81 | - enhance - better snippets titles 82 | - enhance - better symbol navigation in outline and breadcrumbs. Thanks to @msftrncs. 83 | - add - Formatting capitalize time keys like `t#10s` to `T#10S`, `ANY_*` types 84 | 85 | ## [1.8.3] - 2020-10-29 86 | 87 | - add - support for UNION syntax 88 | - add - Highlight ST inside some XML files key. 89 | 90 | ## [1.8.2] - 2020-10-13 91 | 92 | - change - enter `st ` to see list of all snippets. 93 | - add - Highlight ST in Markdown files when use `iecst` key. 94 | 95 | ![Example](https://raw.githubusercontent.com/Serhioromano/vscode-st/master/images/md-highlight.png) 96 | 97 | - add - new icon :)) 98 | 99 | ## [1.8.1] - 2020-09-11 100 | 101 | - add - new formatting architecture that utilize VS Code formatting API. 102 | 103 | ## [1.7.3] - 2019-09-14 104 | 105 | - add - syntax highlights keyword `__TRY`, `__CATCH`, `__FINALY`, `__NEW`, `__DELETE`, `S=`, `R=` 106 | - add - snippets fo `__TRY`, `__CATCH`, `__FINALY`, `__NEW`, `__DELETE` 107 | 108 | Thanks to @nikvoronin 109 | 110 | ## [1.7.1] - 2019-08-20 111 | 112 | - fix - syntax highlights keyword inside another word 113 | - fix - build document structure with block indented 114 | - add - outline for Structures and Enumerations 115 | - enhance - outline for other elements 116 | - enhance - right click commands now appear only in ST files 117 | 118 | ## [1.6.0] - 2019-23-07 119 | 120 | - add - outline document structure 121 | - add - Go to symbol in file support 122 | - add - breadcrumbs 123 | 124 | ## [1.5.0] - 2019-15-03 125 | 126 | - improve - function highlights. Now we Highlight all function either user defined or built in. 127 | - improve - variable declaration or `STRUCT` declaration section has got a lot of improvements. We highlight custom types now, and whole section looks much cleaner because scopes were changed. 128 | - improve - If you call for a method of a function block through dot `my_fb.method()` method is highlighted. We also highlight function block properties or parameters. 129 | - add - TODO or TODO: in comments attract an attention 130 | - add - Highlights for action modifiers `myAction(DS, T#3s);` where DS will be highlighted. 131 | - add - Highlights for names of parameters of function block. 132 | 133 | ## [1.4.0] - 2019-13-03 134 | 135 | I've learned few things about syntax highlights. Big refactoring. I added some missing keywords, changed some coloring scopes and improved coloring in general. 136 | 137 | ## [1.3.1] - 2018-31-12 138 | 139 | - add - TYPE creation snippets (enumerations ans structure) and enhance it's syntax highlights 140 | - enhance - Select type out of suggestion list when inset variable in declaration area from snippet 141 | - enhance - Utilise [October 2018](https://code.visualstudio.com/updates/v1_28#_project-level-snippets) feature for multiple prefixes in snippets and add multiple prefixes to some snippets. 142 | - fix - syntax highlights for function_block 143 | 144 | ## [1.3.0] - 2018-19-12 145 | 146 | More stable version 147 | 148 | - fix - Auto close comments `/*`. `(*`. 149 | - enhance - revision of highlights much better now. 150 | 151 | ## [1.2.3] - 2018-13-05 152 | 153 | - fix - Insert `END_VAR` after `VAR` is inserted. 154 | - fix - highlights for `WORD` datatype. 155 | - fix - other minor changes 156 | 157 | ## [1.2.3] - 2018-13-05 158 | 159 | - fix - Notification "This file not a structured text language!" #6 160 | 161 | ## [1.2.0] - 2018-13-05 162 | 163 | Big step forward. Now it is not only language configuration it is JS code that can enhance codding experience. 164 | 165 | - add - Command pallet `Ctrl+Shift+P` command `ST: Format` 166 | - add - same command context menu 167 | - add - user parameter `st.autoFormat` 168 | 169 | All 3 make the same. Capitalize constants like `TRUE`, `FALSE`, `EXIT` and others. Right now only few. This is only proof of concept. Later we will add more beautifications. 170 | 171 | ## [1.1.0] - 2018-13-05 172 | 173 | - add - a lot of different snippets 174 | - enhance - syntax highlight like function names, program and function definitions, etc.. 175 | 176 | ## [1.0.9] - 2018-13-05 177 | 178 | - fix - extensions metadata tags 179 | - add - new functions to support logi.CAD 3 reference variables. 180 | - enhance - auto indentations inside IF, PROGRAM, VAR, ... 181 | 182 | ## [1.0.8] - 2018-10-05 183 | 184 | - fix - close `[` bracket 185 | - add - region folding 186 | - improve - keywords case 187 | 188 | ## [1.0.7] - 2018-08-05 189 | 190 | - improve - readme file 191 | - improve - syntax highlights scopes 192 | 193 | ## [1.0.5] - 2018-08-05 194 | 195 | - add- few new snippets. 196 | - fix - some keywords highlight issue 197 | 198 | ## [1.0.4] - 2018-04-05 199 | 200 | - add- new file extensions to support logi.CAD 3 projects. 201 | 202 | ## [1.0.3] - 2018-04-05 203 | 204 | ## Added 205 | 206 | - add - Syntax highlights 207 | - add - Some snippets 208 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Sergey Romanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Structured Text (IEC 61131-3) 2 | 3 | Most complete Structured Text language support. Features includes: 4 | 5 | - Syntax highlights in \*.st, \*.iecst files 6 | - Syntax highlights in \*.md (Markdown) files in `iecst` code block 7 | - Syntax highlights in \*.xml files in `` and `` code block 8 | - Snippets (enter `st ` to see list of all snippets) 9 | - Outline view 10 | - Breadcrumbs code navigation 11 | - Go to Symbol in File 12 | - Formatting (*beta) 13 | - Commands 14 | 15 | ## Syntax Highlights 16 | 17 | Includes syntax highlight based on IEC 61131-3 draft, including namespaces, SFC elements (STEP, ACTION, TRANSITION), constant variables (2#000_1010, 16#1E, INT#23) and more. 18 | 19 | ![Structured Text syntax highlights example](https://raw.githubusercontent.com/Serhioromano/vscode-st/master/images/demo.gif) 20 | 21 | Also highlight in Markdown files 22 | 23 | ![Example](https://raw.githubusercontent.com/Serhioromano/vscode-st/master/images/md-highlight.png) 24 | 25 | ## Snippets 26 | 27 | Fast growing snippets library. All cycles, conditions and declarations. 28 | 29 | ## Formatter (work in progress) 30 | 31 | Utilize VS Code Formatter API. Use general formatting command short keys. It capitalize all known keywords like `TRUE`, `FALSE`, `IF`, `BOOL`, ... It also add spaces. 32 | 33 | ## Note 34 | 35 | - The same Author contributed ST support for: 36 | - [Microsoft Monaco Editor](https://github.com/microsoft/monaco-editor) (base editor for VS Code) 37 | - [highlight.js](https://github.com/highlightjs/highlightjs-structured-text) 38 | - [prism.js](https://github.com/PrismJS/prism) 39 | - This extension project is a part of my 320 pages "Learn Structured Text IEC 61131-3" book which I published in [Russian 2020](https://www.youtube.com/watch?v=c0bjUFzSHxs). (translation is on the way) 40 | 41 | ## Roadmap 42 | 43 | - LSP (Language Server Protocol) 44 | - Suggestions 45 | - Lints 46 | 47 | ## Release Notes 48 | 49 | ### 1.12.30 50 | 51 | - add SEL RTAC (real time automation controller) exports its ST files as xml with the ST code in Implementaiton or Interface tags. This PR simply adds those two tags. 52 | - add GX Works Devices syntax support 53 | - add GX Works instructions syntax highlights 54 | 55 | ### 1.12.25 56 | 57 | - add spaces formatting 58 | - add `PROPERTY` with `SET` and `GET` and snippet 59 | - add `METHOD` snippet 60 | - add - `CLASS ... END_CLASS` keywords and snippet 61 | - fix - Extra spaces was added after tab or in front of the comment. 62 | - enhance - highlight Siemens SCL 63 | - add - `(END_?)CONST`, `(END_?)ORGANIZATION_BLOCK`, `(END_?)DATA_BLOCK`, `(END_?)LABEL`, `BEGIN`, `COUNTER`, `VOID`, `NIL` and `GOTO` keywords 64 | - add - support for `PQ` and `PI` in constants like `%PQW10.0`. 65 | 66 | ### 1.11.14 67 | 68 | - fix - highlight of names with `_TO_` in it 69 | - fix - comment in comment highlight 70 | - fix - capitalization of s,h,m in `T#***` 71 | 72 | ### 1.11.2 73 | 74 | - fix - highlight for typed variables with - (`INT#-100`) 75 | - fix - highlight for typed variables with numeric constants (`INT#16#10AE`) 76 | 77 | ### 1.11.1 78 | 79 | - enhance - performance improve by using bundler 80 | - enhance - Format add spaces refactored performance and logic improvement. 81 | 82 | ### 1.10 83 | 84 | - fix - Format `<>` and `**` correctly. 85 | - fix - Document format in strings fixed. Now formatting is not done in strings and comments. 86 | - improve - Formatting add spaces new conditions. 87 | - improve - Formatting. Add spaces in comments and other places like before `:=` or after. 88 | - add - file extension `*.TcDUT`, `*.TcPOU`. 89 | - add- syntax highlights for comment attributes like `(* @NESTEDCOMMENTS := 'Yes' *)` generated by CoDeSys 2.3 in `.EXP` files. 90 | - add - Pragma attribute and message highlights like `{attribute 'call_after_global_init_slot' := 'slot'}` or `{info 'TODO: should get another name'}` supported in CoDeSys. 91 | - fix - lot of minor syntax highlight mistakes. 92 | - improve - syntax highlight was fully revised and improved a lot. 93 | 94 | ### 1.9 95 | 96 | - enhance - Outline now understand `CONSTANT` variables. Thanks to @msftrncs. 97 | - fix - `VAR` word break keyword parse issue. Thanks to @msftrncs. 98 | - fix - Some issues outline tree parse. Thanks to @msftrncs. 99 | - add - Formatting capitalization for all `VAR_*` keywords and `__NEW` and `__DELETE` 100 | - fix - one closed comment formatting as function #29 101 | - enhance - better snippets titles 102 | - enhance - better symbol navigation in outline and breadcrumbs. Thanks to @msftrncs. 103 | - add - Formatting capitalize time keys like `t#10s` to `T#10S`, `ANY_*` types 104 | 105 | ### 1.8 106 | 107 | - enhance - better snippets titles 108 | - add - support for UNION syntax 109 | - add - Highlight ST inside some XML files key. 110 | - change - enter `st ` to see list of all snippets. 111 | - add - Highlight ST in Markdown files when use `iecst` key. 112 | - add - new icon :)) 113 | - add - new formatting architecture that utilize VS Code formatting API. 114 | 115 | ### 1.7 116 | 117 | - add - syntax highlights keyword `__TRY`, `__CATCH`, `__FINALY`, `__NEW`, `__DELETE`, `S=`, `R=` 118 | - add - snippets fo `__TRY`, `__CATCH`, `__FINALY`, `__NEW`, `__DELETE` 119 | 120 | Thanks to @nikvoronin 121 | 122 | - fix - syntax highlights keyword inside another word 123 | - fix - build document structure with block indented 124 | - add - outline for Structures and Enumerations 125 | - enhance - outline for other elements 126 | - enhance - right click commands now appear only in ST files 127 | 128 | ### 1.6 129 | 130 | - add - outline document structure 131 | - add - Go to symbol in file support 132 | - add - breadcrumbs 133 | 134 | ### 1.5.0 135 | 136 | - improve - function highlights. Now we Highlight all function either user defined or built in. 137 | - improve - variable declaration or `STRUCT` declaration section has got a lot of improvements. We highlight custom types now, and whole section looks much cleaner because scopes were changed. 138 | - improve - If you call for a method of a function block through dot `my_fb.method()` method is highlighted. We also highlight function block properties or parameters. 139 | - add - TODO or TODO: in comments attract an attention 140 | - add - Highlights for action modifiers `myAction(DS, T#3s);` where DS will be highlighted. 141 | - add - Highlights for names of parameters of function block. 142 | - 143 | ### 1.4.0 144 | 145 | I've learned few things about syntax highlights. Big refactoring. I added some missing keywords, changed some coloring scopes and improved coloring in general. 146 | 147 | ### 1.3.0 148 | 149 | - add - TYPE creation snippets (enumerations ans structure) and enhance it's syntax highlights 150 | - enhance - Select type out of suggestion list when inset variable in declaration area from snippet 151 | - enhance - Utilise [October 2018](https://code.visualstudio.com/updates/v1_28#_project-level-snippets) feature for multiple prefixes in snippets and add multiple prefixes to some snippets. 152 | - fix - syntax highlights for function_block 153 | - fix - Auto close comments `/*`. `(*`. 154 | - enhance - revision of highlights much better now. 155 | 156 | ### 1.2.0 157 | 158 | - fix - Insert `END_VAR` after `VAR` is inserted. 159 | - fix - highlights for `WORD` datatype. 160 | - fix - other minor changes 161 | - fix - Notification "This file not a structured text language!" #6 162 | - add - Command pallet `Ctrl+Shift+P` command `ST: Format` 163 | - add - same command context menu 164 | - add - user parameter `st.autoFormat` 165 | 166 | All 3 make the same. Capitalize constants like `TRUE`, `FALSE`, `EXIT` and others. Right now only few. This is only proof of concept. Later we will add more beautifications. 167 | 168 | ### 1.1.0 169 | 170 | - add - a lot of different snippets 171 | - enhance - syntax highlight like function names, program and function definitions, etc.. 172 | 173 | ### 1.0.0 174 | 175 | - fix - extensions metadata tags 176 | - add - new functions to support logi.CAD 3 reference variables. 177 | - enhance - auto indentations inside IF, PROGRAM, VAR, ... 178 | - fix - close `[` bracket 179 | - add - region folding 180 | - improve - keywords case 181 | - improve - readme file 182 | - improve - syntax highlights scopes 183 | - add- few new snippets. 184 | - fix - some keywords highlight issue 185 | - add- new file extensions to support logi.CAD 3 projects. 186 | -------------------------------------------------------------------------------- /ST.g4: -------------------------------------------------------------------------------- 1 | // https://neuroning.com/post/implementing-code-completion-for-vscode-with-antlr/ 2 | // https://github.com/antlr/antlr4/blob/master/doc/parser-rules.md 3 | // https://github.com/mike-lischke/antlr4-c3 4 | 5 | grammar ST; 6 | 7 | compilePOU: 8 | namespaceDeclaration 9 | | programDeclaration 10 | | functionDeclaration 11 | | functionBlockDeclaration 12 | | globalVar 13 | | typeDeclaration; 14 | 15 | namespaceDeclaration: 16 | 'NAMESPACE' nsName (compilePOU)* 'END_NAMESPACE'; 17 | programDeclaration: 18 | 'PROGRAM' programName varDeclarationList statementList 'END_PROGRAM'; 19 | typeDeclaration: 'TYPE' typeName ':' typeUnit 'END_TYPE'; 20 | functionBlockDeclaration: 21 | 'FUNCTION_BLOCK' functionBlockName varDeclarationList statementList 'END_FUNCTION_BLOCK'; 22 | functionDeclaration: 23 | 'FUNCTION' functionName ':' dataType varDeclarationList statementList 'END_FUNCTION'; 24 | globalVar: 'VAR_GLOBAL' (VARKEY)* (varDeclaration)* 'END_VAR'; 25 | 26 | typeUnit: typeKindAlias | typeKindEnum | typeKindStruct; 27 | 28 | typeKindEnum: 29 | '(' enumNameUnit (',' enumNameUnit)* ')' (':=' enumValue)? ';'; 30 | enumNameUnit: enumName (':=' enumVal)?; 31 | enumVal: INTEGER | enumName; 32 | typeKindAlias: dataType (':=' dataTypeValue)? ';'; 33 | typeKindStruct: 'STRUCT' (varDeclaration)* 'END_STRUCT'; 34 | 35 | varDeclarationList: (varDeclarationSet)*; 36 | varDeclarationSet: 37 | VAROPEN (VARKEY)* (varDeclaration)* 'END_VAR'; 38 | 39 | varDeclaration: 40 | varName (',' varName)* ':' dataType (':=' dataTypeValue)? ';' 41 | | varName 'AT' LA ':' dataType ( 42 | ':=' dataTypeValue 43 | )? ';'; 44 | 45 | statementList: (statement)*; 46 | statement: 47 | statementAssign 48 | | statementIf 49 | | statementWhile 50 | | statementFor 51 | | statementRepeat 52 | | statementCase; 53 | 54 | statementAssign: 55 | varNameAssign (':=' varNameAssign)* ':=' expression; 56 | varNameAssign: varName | varName ('.' ID)*; 57 | 58 | statementIf: 59 | 'IF' expression 'THEN' statementList 'END_IF' 60 | | 'IF' expression 'THEN' statementList ( 61 | 'ELSEIF' expression 'THEN' statementList 62 | )* ('ELSE' statementList)? 'END_IF'; 63 | 64 | statementWhile: 65 | 'WHILE' expression 'DO' statementListBreak 'END_WHILE'; 66 | statementRepeat: 67 | 'REPEAT' statementListBreak 'UNTIL' expression 'END_REPEAT'; 68 | statementFor: 69 | 'FOR' varName ':=' INTEGER 'TO' INTEGER ('BY' INTEGER)? 'DO' statementListBreak 'END_FOR'; 70 | statementCase: 71 | 'CASE' varName 'OF' caseItemList ('ELSE' statementList)? 'END_CASE'; 72 | caseItemList: (caseItem)*; 73 | caseItem: INTEGER ':' statementList; 74 | statementListBreak: statementList | LOOP ';'; 75 | 76 | expression: 77 | '(' expression OPERATOR expression ')' 78 | | expression OPERATOR expression 79 | | NUMERIC 80 | | functionName '(' functionParams ')'; 81 | 82 | functionParams: functionParam (',' functionParam)*; 83 | functionParam: ID | ID ':=' expression | ID '=>' varName; 84 | 85 | programName: ID; 86 | typeName: ID; 87 | functionName: ID; 88 | functionBlockName: ID; 89 | nsName: ID; 90 | enumName: ID; 91 | varName: ID; 92 | enumValue: ID | INTEGER; 93 | dataType: typeName | functionBlockName | programName | DT; 94 | dataTypeValue: STRING | NUMERIC; 95 | 96 | /* 97 | * Lexer Rules 98 | */ 99 | LA: 100 | '%' ('I' | 'Q' | 'M') ('X' | 'W' | 'D' | 'L') INTEGER ( 101 | '.' INTEGER 102 | )*; 103 | ID: ('_' | ('a' ..'z') | ('A' ..'Z')) ( 104 | '_' 105 | | ('a' ..'z') 106 | | ('A' ..'Z') 107 | | ('0' ..'9') 108 | )*; 109 | 110 | DT: 111 | ANY_BIT 112 | | ANY_REAL 113 | | ANY_INT 114 | | ANY_STRING 115 | | ANY_TIME 116 | | ARRAY; 117 | 118 | ANY_INT: 119 | 'INT' 120 | | 'UINT' 121 | | 'SINT' 122 | | 'USINT' 123 | | 'DINT' 124 | | 'UDINT' 125 | | 'LINT' 126 | | 'ULINT'; 127 | 128 | ANY_BIT: 'BYTE' | 'WORD' | 'DWORD' | 'LWORD'; 129 | ANY_STRING: 'STRING' | 'WSTRING'; 130 | ANY_REAL: 'REAL' | 'LREAL'; 131 | ANY_TIME: 'DT' | 'TOD' | 'TIME' | 'DATE'; 132 | 133 | VAROPEN: 134 | 'VAR' 135 | | 'VAR_INPUT' 136 | | 'VAR_OUTPUT' 137 | | 'VAR_IN_OUT' 138 | | 'VAR_TMP'; 139 | ARRAY: 'ARRAY[' INTEGER '..' INTEGER '] OF '; 140 | NUMERIC: INTEGER | FLOAT; 141 | VARKEY: 'CONSTANT' | 'RETAIN' | 'PERSISTANT'; 142 | LOOP: 'EXIT' | 'CONTINUE'; 143 | WHITESPACE: (' ' | '\t')+ -> skip; 144 | NEWLINE: ('\r'? '\n' | '\r')+ -> skip; 145 | OPERATOR: '+' | '-' | '*' | '/' | '>' | '<' | '=' | '>=' | '<='; 146 | INTEGER: ('0' ..'9')+; 147 | STRING_S: '\'' ('\'\'' | ~ ('\''))* '\''; 148 | STRING_D: '"' ('""' | ~ ('"'))* '"'; 149 | STRING: STRING_D | STRING_S; 150 | FLOAT: ('0' .. '9')+ ( 151 | ('.' ('0' .. '9')+ (EXPONENT)?)? 152 | | EXPONENT 153 | ); 154 | fragment EXPONENT: ('e') ('+' | '-')? ('0' .. '9')+; 155 | 156 | /* Comments */ 157 | SingleLineComment: '//' ~('\r' | '\n')* -> Skip; 158 | 159 | MultiLineComment: '/*' .* '*/' -> Skip; 160 | -------------------------------------------------------------------------------- /example/test.iecst: -------------------------------------------------------------------------------- 1 | VAR_GLOBAL 2 | TEST_ARR: ARRAY[0..2] OF WORD; 3 | END_VAR 4 | 5 | FUNCTION_BLOCK FB_TEST 6 | VAR 7 | iNum: WORD; 8 | 9 | END_VAR 10 | TEST_ARR[iNum] := 10; 11 | END_FUNCTION_BLOCK 12 | 13 | PROGRAM PLC_PRG 14 | VAR 15 | iIndex : WORD; 16 | fbTest: FB_TEST; 17 | END_VAR 18 | fbTest(iNum := 0); // Работает 19 | fbTest(iNum := iIndex); // Не Работает 20 | TEST_ARR[1] := 10; // Работает 21 | TEST_ARR[iIndex] := 10; // Не Работает 22 | D100 := 1; 23 | TEST_ARR[D100] := 10; // Не Работает 24 | 25 | END_PROGRAM 26 | 27 | CONST 28 | MY_TON_D := 2; 29 | MY_TO_D := 2; 30 | LIMIT := 7; 31 | END_CONST 32 | 33 | ORGANIZATION_BLOCK CYCLE 34 | (* ********************************************************** 35 | CYCLE - название OB1, он вызывается циклически системой S7. 36 | Часть 1 : Вызов функционального блока и передача входных величин 37 | Часть 2 : Чтение выходных величин и вывод с переключением выходов 38 | ********************************************************** *) 39 | VAR_TEMP 40 | systemdata : ARRAY[0..20] OF BYTE; // Область для OB1 41 | END_VAR 42 | BEGIN 43 | (* Part 1 : ************************************************** *) 44 | ACQUIRE.ACQUIRE_DATA( 45 | measval_in := WORD_TO_INT(input), 46 | newval := "Input 0.0", // Входной ключ как идентификатор сигнала 47 | resort := %PQW10.23, 48 | funct_sel := Function_switch, 49 | newsel := Coding_switch, 50 | selection := Coding 51 | ); 52 | 53 | (* Part 2 : ************************************************* *) 54 | IF Output_switch THEN // Изменение выхода 55 | Output := ACQUIRE_DATA.result_out; // Квадратный корень или квадрат 56 | ELSE 57 | Output := ACQUIRE_DATA.measval_out; // Измеряемая величина 58 | END_IF; 59 | END_ORGANIZATION_BLOCK 60 | 61 | TYPE Direction ( 62 | DIR_CW, (* по часовой *) 63 | DIR_CCW (* против часовой *) 64 | ) 65 | END_TYPE 66 | 67 | VAR 68 | xA : BOOL; (* Фаза а *) 69 | xB : BOOL; (* Фаза б *) 70 | enDir : Direction; (* Направление движения *) 71 | udiCount : DINT; (* Счетчик *) 72 | fbRTA, fbRTB : R_TRIG; 73 | fbFTA, fbFTB : F_TRIG; 74 | END_VAR 75 | 76 | fbRTA(CLK := xA); 77 | fbFTA(CLK := xA); 78 | fbRTB(CLK := xB); 79 | fbFTB(CLK := xB); 80 | 81 | IF (fbRTA.Q AND NOT xB) OR 82 | (fbFTA.Q AND xB) OR 83 | (fbRTB.Q AND xA) OR 84 | (fbFTB.Q AND NOT xA ) 85 | THEN 86 | enDir := Direction.DIR_CW; 87 | udiCount := udiCount + 1; 88 | ELSE 89 | enDir := Direction.DIR_CCW; 90 | udiCount := udiCount - 1; 91 | END_IF; 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | PROGRAM PLC_PRG 112 | VAR 113 | xStart : BOOL := 'dfsfdfd'; // something 114 | fbTON1 : TON; (* \. @WE := 'dfdf' *) 115 | rValue : REAL; /* Конечное значение */ 116 | END_VAR 117 | 118 | fbTON1(IN := xStart, PT => tDuration); 119 | IF xStart <> TRUE THEN 120 | rValue := easeLinear(fbTON1.ET, rFrom, rTo, tDuration); 121 | END_IF; 122 | (a = b)ANDc > 10; 123 | c := (b / 3) * 12**; 124 | a := CONCAT(a, ':= **'); // a := b 125 | END_PROGRAM 126 | -------------------------------------------------------------------------------- /example/test.md: -------------------------------------------------------------------------------- 1 | # This is test in MD 2 | dddd ddd 3 | Here is the code, should be highlighted. 4 | 5 | ```iecst 6 | VAR 7 | xStart: BOOL; (* This is comment *) 8 | END_VAR 9 | ``` 10 | 11 | Here is the code. -------------------------------------------------------------------------------- /example/text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /extension-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Serhioromano/vscode-st/9343764020ce93f22d42ceee47bd5be69c2aeeb7/extension-icon.png -------------------------------------------------------------------------------- /icon.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Serhioromano/vscode-st/9343764020ce93f22d42ceee47bd5be69c2aeeb7/icon.drawio.png -------------------------------------------------------------------------------- /images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Serhioromano/vscode-st/9343764020ce93f22d42ceee47bd5be69c2aeeb7/images/demo.gif -------------------------------------------------------------------------------- /images/icon.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Serhioromano/vscode-st/9343764020ce93f22d42ceee47bd5be69c2aeeb7/images/icon.drawio.png -------------------------------------------------------------------------------- /images/md-highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Serhioromano/vscode-st/9343764020ce93f22d42ceee47bd5be69c2aeeb7/images/md-highlight.png -------------------------------------------------------------------------------- /language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "//", 4 | "blockComment": ["(*", "*)"] 5 | }, 6 | "brackets": [ 7 | ["{", "}"], 8 | ["[", "]"], 9 | ["VAR", "END_VAR"], 10 | ["VAR_INPUT", "END_VAR"], 11 | ["VAR_OUTPUT", "END_VAR"], 12 | ["VAR_IN_OUT", "END_VAR"], 13 | ["VAR_TEMP", "END_VAR"], 14 | ["VAR_GLOBAL", "END_VAR"], 15 | ["VAR_ACCESS", "END_VAR"], 16 | ["VAR_EXTERNAL", "END_VAR"], 17 | ["TYPE", "END_TYPE"], 18 | ["STRUCT", "END_STRUCT"], 19 | ["PROGRAM", "END_PROGRAM"], 20 | ["CLASS", "END_CLASS"], 21 | ["FUNCTION", "END_FUNCTION"], 22 | ["FUNCTION_BLOCK", "END_FUNCTION_BLOCK"], 23 | ["ACTION", "END_ACTION"], 24 | ["STEP", "END_STEP"], 25 | ["INITIAL_STEP", "END_STEP"], 26 | ["TRANSACTION", "END_TRANSACTION"], 27 | ["CONFIGURATION", "END_CONFIGURATION"], 28 | ["TCP", "END_TCP"], 29 | ["RECOURCE", "END_RECOURCE"], 30 | ["CHANNEL", "END_CHANNEL"], 31 | ["LIBRARY", "END_LIBRARY"], 32 | ["FOLDER", "END_FOLDER"], 33 | ["BINARIES", "END_BINARIES"], 34 | ["INCLUDES", "END_INCLUDES"], 35 | ["SOURCES", "END_SOURCE"], 36 | ["IF", "THEN"], 37 | ["THEN", "END_IF"], 38 | ["THEN", "ELSE"], 39 | ["THEN", "ELSIF"], 40 | ["ELSIF", "THEN"], 41 | ["ELSE", "END_IF"], 42 | ["CASE", "OF"], 43 | ["OF", "END_CASE"], 44 | ["DO", "END_FOR"], 45 | ["FOR", "END_FOR"], 46 | ["DO", "END_WHILE"], 47 | ["WHILE", "DO"], 48 | ["REPEAT", "END_REPEAT"], 49 | ["(", ")"], 50 | ["__TRY", "__CATCH"], 51 | ["__CATCH", "__FINALLY"], 52 | ["__FINALLY", "__ENDTRY"] 53 | ], 54 | "autoClosingPairs": [ 55 | { "open": "{", "close": "}" }, 56 | { "open": "[", "close": "]" }, 57 | { "open": "(", "close": ")" }, 58 | { "open": "\"", "close": "\"", "notIn": ["string"] }, 59 | { "open": "'", "close": "'", "notIn": ["string", "comment"] }, 60 | { "open": "(*", "close": " *", "notIn": ["string"] }, 61 | { "open": "/*", "close": " */", "notIn": ["string"] }, 62 | { "open": "VAR_INPUT", "close": "END_VAR", "notIn": ["string"] }, 63 | { "open": "VAR_OUTPUT", "close": "END_VAR", "notIn": ["string"] }, 64 | { "open": "VAR_IN_OUT", "close": "END_VAR", "notIn": ["string"] }, 65 | { "open": "VAR_TEMP", "close": "END_VAR", "notIn": ["string"] }, 66 | { "open": "VAR_GLOBAL", "close": "END_VAR", "notIn": ["string"] }, 67 | { "open": "VAR_ACCESS", "close": "END_VAR", "notIn": ["string"] }, 68 | { "open": "VAR_EXTERNAL", "close": "END_VAR", "notIn": ["string"] }, 69 | { "open": "TYPE", "close": "END_TYPE", "notIn": ["string"] }, 70 | { "open": "STRUCT", "close": "END_STRUCT", "notIn": ["string"] }, 71 | { "open": "PROGRAM", "close": "END_PROGRAM", "notIn": ["string"] }, 72 | { "open": "CLASS", "close": "END_CLASS", "notIn": ["string"] }, 73 | { "open": "FUNCTION", "close": "END_FUNCTION", "notIn": ["string"] }, 74 | { "open": "FUNCTION_BLOCK", "close": "END_FUNCTION_BLOCK", "notIn": ["string"] }, 75 | { "open": "ACTION", "close": "END_ACTION", "notIn": ["string"] }, 76 | { "open": "STEP", "close": "END_STEP", "notIn": ["string"] }, 77 | { "open": "INITIAL_STEP", "close": "END_STEP", "notIn": ["string"] }, 78 | { "open": "TRANSACTION", "close": "END_TRANSACTION", "notIn": ["string"] }, 79 | { "open": "CONFIGURATION", "close": "END_CONFIGURATION", "notIn": ["string"] }, 80 | { "open": "TCP", "close": "END_TCP", "notIn": ["string"] }, 81 | { "open": "RECOURCE", "close": "END_RECOURCE", "notIn": ["string"] }, 82 | { "open": "CHANNEL", "close": "END_CHANNEL", "notIn": ["string"] }, 83 | { "open": "LIBRARY", "close": "END_LIBRARY", "notIn": ["string"] }, 84 | { "open": "FOLDER", "close": "END_FOLDER", "notIn": ["string"] }, 85 | { "open": "BINARIES", "close": "END_BINARIES", "notIn": ["string"] }, 86 | { "open": "INCLUDES", "close": "END_INCLUDES", "notIn": ["string"] }, 87 | { "open": "SOURCES", "close": "END_SOURCE", "notIn": ["string"] } 88 | ], 89 | "autoCloseBefore": " \n\t", 90 | "surroundingPairs": [ 91 | ["{", "}"], 92 | ["[", "]"], 93 | ["(", ")"], 94 | ["(*", "*)"], 95 | ["\"", "\""], 96 | ["'", "'"] 97 | ], 98 | "folding": { 99 | "offSide": true, 100 | "markers": { 101 | "start": "^\\s*//#region", 102 | "end": "^\\s*//#endregion" 103 | } 104 | }, 105 | "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)" 106 | // "indentationRules": { 107 | // "increaseIndentPattern": "/^.*(THEN|DO|OF|VAR)$/", 108 | // "decreaseIndentPattern": "/^(END_IF;)$/" 109 | // // "increaseIndentPattern": "({(?!.+}).*|\\(|\\[|(IF|ELSE|FOR|WHILE|CASE|THEN|ELSIF|DO|BY|OF|REPEAT|).*)\\s*(/[\\(*].*)?$", 110 | // // "decreaseIndentPattern": "^((.*\\*\\/)?|(.*\\*\\)?)\\s*((\\})|(\\)+[;,])|(\\][;,])|\\b(else)|\\b((END_(IF|FOR|WHILE|CASE|REPEAT));))" 111 | // } 112 | 113 | //"enhancedBrackets":[ 114 | // { "openTrigger": "\\s", "open": "VAR", "closeComplete": "END_VAR", "matchCase": true }, 115 | // { openTrigger: 'e', open: /case$/i, closeComplete: 'end', matchCase: true }, 116 | // { openTrigger: 'n', open: /when$/i, closeComplete: 'then', matchCase: true } 117 | // ] 118 | // noindentBrackets: '()', 119 | } 120 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-st", 3 | "displayName": "Structured Text language Support", 4 | "description": "Supports for syntax highlights, snippets for IEC 61131-3 Structured Text", 5 | "version": "1.12.36", 6 | "publisher": "Serhioromano", 7 | "sponsor": { 8 | "url": "https://www.paypal.com/donate/?hosted_button_id=UTU4EMPLLLX54" 9 | }, 10 | "scripts": { 11 | "vscode:prepublish": "npm run esbuild-base -- --minify", 12 | "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node", 13 | "esbuild": "npm run esbuild-base -- --sourcemap", 14 | "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch", 15 | "publish": "vsce package && gh release create $(node -pe \"require('./package.json')['version']\") --generate-notes \"./vscode-gitflow-$(node -pe \"require('./package.json')['version']\").vsix\" && vsce publish", 16 | "_vscode:prepublish": "npm run compile", 17 | "compile": "tsc -p ./", 18 | "watch": "tsc -watch -p ./", 19 | "pretest": "npm run compile && npm run lint", 20 | "lint": "eslint src --ext ts", 21 | "vars": "env | grep npm_package_", 22 | "test": "node ./out/test/runTest.js" 23 | }, 24 | "main": "./out/extension.js", 25 | "_main": "./out/main.js", 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/Serhioromano/vscode-st" 29 | }, 30 | "keywords": [ 31 | "ST", 32 | "Structured Text", 33 | "IEC 61131-3", 34 | "PLC", 35 | "TwinCAT3", 36 | "CoDeSys", 37 | "GX Works", 38 | "logi.CAD 3" 39 | ], 40 | "author": "Sergey Romanov", 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/Serhioromano/vscode-st/issues", 44 | "email": "serg4172@mail.ru" 45 | }, 46 | "homepage": "https://github.com/Serhioromano/vscode-st#readme", 47 | "engines": { 48 | "vscode": "^1.36.0" 49 | }, 50 | "activationEvents": [ 51 | "onLanguage:st", 52 | "onCommand:extension.st.autoformat" 53 | ], 54 | "categories": [ 55 | "Programming Languages", 56 | "Snippets", 57 | "Formatters" 58 | ], 59 | "icon": "icon.drawio.png", 60 | "devDependencies": { 61 | "@types/glob": "^7.2.0", 62 | "@types/mocha": "^9.0.0", 63 | "@types/node": "14.x", 64 | "@types/vscode": "^1.36.0", 65 | "@typescript-eslint/eslint-plugin": "^5.9.1", 66 | "@typescript-eslint/parser": "^5.9.1", 67 | "@vscode/test-electron": "^2.0.3", 68 | "chevrotain": "^10.4.2", 69 | "esbuild": "^0.25.0", 70 | "eslint": "^8.6.0", 71 | "glob": "^7.2.0", 72 | "langium-cli": "^0.5.0", 73 | "mocha": "^11.1.0", 74 | "typescript": "^4.6.3", 75 | "vsce": "^1.97.0" 76 | }, 77 | "contributes": { 78 | "configuration": { 79 | "type": "object", 80 | "title": "Structured Text configuration", 81 | "properties": { 82 | "st.format.enable": { 83 | "type": "boolean", 84 | "default": true, 85 | "description": "Automatically convert constants like TRUE, FALSE, NULL, or types like INT, BOOL, ...and other keywords to upper case." 86 | } 87 | } 88 | }, 89 | "languages": [ 90 | { 91 | "id": "st", 92 | "aliases": [ 93 | "Structured Text", 94 | "ST" 95 | ], 96 | "firstLine": "^([\\(\\/\\*]?.*)?(PROGRAM ).*", 97 | "extensions": [ 98 | ".st", 99 | ".scl", 100 | ".exp", 101 | ".vpl", 102 | ".iecplc", 103 | ".iecst", 104 | ".TcDUT", 105 | ".TcPOU", 106 | ".iecstl" 107 | ], 108 | "configuration": "./language-configuration.json" 109 | } 110 | ], 111 | "grammars": [ 112 | { 113 | "scopeName": "source.st", 114 | "language": "st", 115 | "path": "./syntaxes/st.tmLanguage.json" 116 | }, 117 | { 118 | "scopeName": "markdown.iecst.codeblock", 119 | "path": "./syntaxes/md.codeblock.json", 120 | "injectTo": [ 121 | "text.html.markdown" 122 | ], 123 | "embeddedLanguages": { 124 | "meta.embedded.block.iecstmd": "st" 125 | } 126 | }, 127 | { 128 | "scopeName": "xml.iecst.codeblock", 129 | "path": "./syntaxes/xml.codeblock.json", 130 | "injectTo": [ 131 | "text.xml" 132 | ], 133 | "embeddedLanguages": { 134 | "meta.embedded.block.iecstxml": "st" 135 | } 136 | } 137 | ], 138 | "snippets": [ 139 | { 140 | "language": "st", 141 | "path": "./snippets/st.snippets.json" 142 | } 143 | ] 144 | }, 145 | "dependencies": { 146 | "langium": "^0.5.0", 147 | "vscode-languageclient": "^8.0.2", 148 | "vscode-languageserver": "^8.0.2" 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /snippets/st.snippets.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROGRAM …": { 3 | "prefix": ["st program"], 4 | "body": [ 5 | "PROGRAM ${1:PLC_PRG}", 6 | "\t$0", 7 | "END_PROGRAM", 8 | "" 9 | ], 10 | "description": "Define program" 11 | }, 12 | "IF END_IF...": { 13 | "prefix": "st if", 14 | "body": [ 15 | "IF ${1:Condition} THEN", 16 | "\t$0", 17 | "END_IF;", 18 | "" 19 | ], 20 | "description": "Define IF condition" 21 | }, 22 | "IF ELSE END_IF...": { 23 | "prefix": "st if else", 24 | "body": [ 25 | "IF ${1:Condition} THEN", 26 | "\t$0", 27 | "ELSE", 28 | "\t$0", 29 | "END_IF;", 30 | "" 31 | ], 32 | "description": "Define IF condition with ELSE" 33 | }, 34 | "IF ELSIF ELSE END_IF...": { 35 | "prefix": "st if elsif else", 36 | "body": [ 37 | "IF ${1:Condition} THEN", 38 | "\t$0", 39 | "ELSIF ${2:Condition2} THEN", 40 | "\t", 41 | "ELSE", 42 | "\t", 43 | "END_IF;", 44 | "" 45 | ], 46 | "description": "Define IF condition with ELSIF and ELSE" 47 | }, 48 | "ELSIF…": { 49 | "prefix": "st elsif ", 50 | "body": [ 51 | "ELSIF (${1:Condition1}) THEN", 52 | "\t$0" 53 | ], 54 | "description": "Define ELSIF condition" 55 | }, 56 | "WHILE …": { 57 | "prefix": "st while ", 58 | "body": [ 59 | "WHILE ${1:Condition} DO", 60 | "\t$0", 61 | "END_WHILE;", 62 | "" 63 | ], 64 | "description": "Define WHILE cycle" 65 | }, 66 | "REPEAT …": { 67 | "prefix": "st repeat ", 68 | "body": [ 69 | "REPEAT", 70 | "\t$0", 71 | "UNTIL ${1:Condition}", 72 | "END_REPEAT;", 73 | "" 74 | ], 75 | "description": "Define REPEAT cycle" 76 | }, 77 | "CASE …": { 78 | "prefix": "st case ", 79 | "body": [ 80 | "CASE ${1:ANY_NUM} OF", 81 | "\t0: $0", 82 | "\t1:", 83 | "\t2:", 84 | "END_CASE;", 85 | "" 86 | ], 87 | "description": "Define CASE condition" 88 | }, 89 | "CASE … ELSE": { 90 | "prefix": "st case else", 91 | "body": [ 92 | "CASE ${1:ANY_NUM} OF", 93 | "\t0: $0", 94 | "\t1:", 95 | "ELSE", 96 | "\t", 97 | "END_CASE;", 98 | "" 99 | ], 100 | "description": "Define CASE condition with ELSE" 101 | }, 102 | "ACTION …": { 103 | "prefix": "st action ", 104 | "body": [ 105 | "ACTION ${1:Name}:", 106 | "\t$0", 107 | "END_ACTION;", 108 | "" 109 | ], 110 | "description": "Define ACTION" 111 | }, 112 | "STEP …": { 113 | "prefix": "st step ", 114 | "body": [ 115 | "STEP ${1:Name}:", 116 | "\t$0", 117 | "END_STEP;", 118 | "" 119 | ], 120 | "description": "Define STEP" 121 | }, 122 | "INITIAL_STEP …": { 123 | "prefix": "st initial_step ", 124 | "body": [ 125 | "INITIAL_STEP ${1:Name}:", 126 | "\t$0", 127 | "END_STEP;", 128 | "" 129 | ], 130 | "description": "Define Initial STEP" 131 | }, 132 | "TRANSITION …": { 133 | "prefix": "st transaction", 134 | "body": [ 135 | "TRANSITION ${1:Name} FROM ${2:Step1} TO ${3:Step2}", 136 | "\t$0", 137 | "END_TRANSITION;", 138 | "" 139 | ], 140 | "description": "Define ACTION" 141 | }, 142 | "FOR …": { 143 | "prefix": "st for ", 144 | "body": [ 145 | "FOR ${1:Var} TO ${2:Num} DO", 146 | "\t$0", 147 | "END_FOR;", 148 | "" 149 | ], 150 | "description": "Define FOR cycle" 151 | }, 152 | "FOR BY…": { 153 | "prefix": "st for by", 154 | "body": [ 155 | "FOR ${1:Var} TO ${2:Num} BY ${3:1} DO", 156 | "\t$0", 157 | "END_FOR;", 158 | "" 159 | ], 160 | "description": "Define FOR cycle with BY" 161 | }, 162 | "NAMESPACE ... END_NAMESPACE": { 163 | "prefix": "st namespace ", 164 | "body": [ 165 | "NAMESPACE ${1:Name}", 166 | "\t$0", 167 | "END_NAMESPACE" 168 | ], 169 | "description": "Define NAMESPACE" 170 | }, 171 | "CLASS ... END_CLASS": { 172 | "prefix": "st class ", 173 | "body": [ 174 | "CLASS ${1:Name}", 175 | "\t$0", 176 | "END_CLASS" 177 | ], 178 | "description": "Define CLASS" 179 | }, 180 | "PROPERTY ... END_PROPERTY": { 181 | "prefix": "st property ", 182 | "body": [ 183 | "PROPERTY ${1:Name} : ${2:Type} ", 184 | "\tGET", 185 | "\t\t$0", 186 | "\tEND_GET", 187 | "\tSET", 188 | "\t\t$0", 189 | "\tEND_SET", 190 | "END_PROPERTY" 191 | ], 192 | "description": "Define Property" 193 | }, 194 | "FUNCTION ... END_FUNCTION": { 195 | "prefix": "st function ", 196 | "body": [ 197 | "FUNCTION ${1:Name} : ${2:Type}", 198 | "\t$0", 199 | "END_FUNCTION" 200 | ], 201 | "description": "Define FUNCTION" 202 | }, 203 | "FUNCTION_BLOCK ... END_FUNCTION_BLOCK": { 204 | "prefix": "st function_block", 205 | "body": [ 206 | "FUNCTION_BLOCK ${1:Name}", 207 | "\t$0", 208 | "END_FUNCTION_BLOCK" 209 | ], 210 | "description": "Define FUNCTION_BLOCK" 211 | }, 212 | "METHOD ... END_METHOD": { 213 | "prefix": "st Method", 214 | "body": [ 215 | "(* ${1:Description} *)", 216 | "METHOD ${2:Name} : ${3:ReturnType}", 217 | "\t$0", 218 | "END_METHOD" 219 | ], 220 | "description": "Define METHOD" 221 | }, 222 | "IMPLEMENTATION ... END_IMPLEMENTATION": { 223 | "prefix": "st implement", 224 | "body": [ 225 | "IMPLEMENTATION", 226 | "\t$0", 227 | "END_IMPLEMENTATION" 228 | ], 229 | "description": "Define IMPLEMENTATION" 230 | }, 231 | "Variable declaration": { 232 | "prefix": ["st var declaration", "st declare"], 233 | "body": [ 234 | "${1:Variable}: ${2|BOOL,BYTE,WORD,DWORD,STRING,WSTRING,REAL,LREAL,ARARY,POINTER TO,REF_TO,INT,SINT,DINT,LINT,UINT,USINT,UDINT,ULINT,TIME,TOD,DATE,DT,ANY, ANY_NUM,ANY_INT,ANY_REAL,TON,TOF,TP,RS,SR,R_TRIG,F_TRIG,CTU,CTD,CTUD|}; (* ${3:Comment} *)$0" 235 | ], 236 | "description": "Define new variable" 237 | }, 238 | "VAR ... END_VAR": { 239 | "prefix": "st var", 240 | "body": [ 241 | "VAR", 242 | "\t${1:Variable}: ${2|BOOL,BYTE,WORD,DWORD,STRING,WSTRING,REAL,LREAL,ARARY,POINTER TO,REF_TO,INT,SINT,DINT,LINT,UINT,USINT,UDINT,ULINT,TIME,TOD,DATE,DT,ANY, ANY_NUM,ANY_INT,ANY_REAL,TON,TOF,TP,RS,SR,R_TRIG,F_TRIG,CTU,CTD,CTUD|}; (* ${3:Comment} *)$0", 243 | "END_VAR" 244 | ], 245 | "description": "Define VAR block" 246 | }, 247 | "VAR_GLOBAL ... END_VAR": { 248 | "prefix": ["st var_global", "global"], 249 | "body": [ 250 | "VAR_GLOBAL", 251 | "\t${1:Variable}: ${2|BOOL,BYTE,WORD,DWORD,STRING,WSTRING,REAL,LREAL,ARARY,POINTER TO,REF_TO,INT,SINT,DINT,LINT,UINT,USINT,UDINT,ULINT,TIME,TOD,DATE,DT,ANY, ANY_NUM,ANY_INT,ANY_REAL,TON,TOF,TP,RS,SR,R_TRIG,F_TRIG,CTU,CTD,CTUD|}; (* ${3:Comment} *)$0", 252 | "END_VAR" 253 | ], 254 | "description": "Define VAR_GLOBAL" 255 | }, 256 | "VAR_IN_OUT ... END_VAR": { 257 | "prefix": ["st var_in_out"], 258 | "body": [ 259 | "VAR_IN_OUT", 260 | "\t${1:Variable}: ${2|BOOL,BYTE,WORD,DWORD,STRING,WSTRING,REAL,LREAL,ARARY,POINTER TO,REF_TO,INT,SINT,DINT,LINT,UINT,USINT,UDINT,ULINT,TIME,TOD,DATE,DT,ANY, ANY_NUM,ANY_INT,ANY_REAL,TON,TOF,TP,RS,SR,R_TRIG,F_TRIG,CTU,CTD,CTUD|}; (* ${3:Comment} *)$0", 261 | "END_VAR" 262 | ], 263 | "description": "Define VAR_IN_OUT" 264 | }, 265 | "VAR_INPUT ... END_VAR": { 266 | "prefix": ["st var_input"], 267 | "body": [ 268 | "VAR_INPUT", 269 | "\t${1:Variable}: ${2|BOOL,BYTE,WORD,DWORD,STRING,WSTRING,REAL,LREAL,ARARY,POINTER TO,REF_TO,INT,SINT,DINT,LINT,UINT,USINT,UDINT,ULINT,TIME,TOD,DATE,DT,ANY, ANY_NUM,ANY_INT,ANY_REAL,TON,TOF,TP,RS,SR,R_TRIG,F_TRIG,CTU,CTD,CTUD|}; (* ${3:Comment} *)$0", 270 | "END_VAR" 271 | ], 272 | "description": "Define VAR_INPUT" 273 | }, 274 | "VAR_OUTPUT ... END_VAR": { 275 | "prefix": ["st var_output"], 276 | "body": [ 277 | "VAR_OUTPUT", 278 | "\t${1:Variable}: ${2|BOOL,BYTE,WORD,DWORD,STRING,WSTRING,REAL,LREAL,ARARY,POINTER TO,REF_TO,INT,SINT,DINT,LINT,UINT,USINT,UDINT,ULINT,TIME,TOD,DATE,DT,ANY, ANY_NUM,ANY_INT,ANY_REAL,TON,TOF,TP,RS,SR,R_TRIG,F_TRIG,CTU,CTD,CTUD|}; (* ${3:Comment} *)$0", 279 | "END_VAR" 280 | ], 281 | "description": "Define VAR_OUTPUT" 282 | }, 283 | "TYPE ... END_TYPE": { 284 | "prefix": ["st type enum"], 285 | "body": [ 286 | "TYPE ${1:EnumName} (", 287 | "\t${2:Variable1}, ${3:Variable2}$0", 288 | ")", 289 | "END_TYPE" 290 | ], 291 | "description": "Define Enumeration" 292 | }, 293 | "TYPE ... STRUCT ...END_STRUCT END_TYPE": { 294 | "prefix": ["st type struct"], 295 | "body": [ 296 | "TYPE ${1:StructName} : STRUCT", 297 | "\t\t${2:Variable} : ${3|BOOL,BYTE,WORD,DWORD,STRING,WSTRING,REAL,LREAL,ARARY,POINTER TO,REF_TO,INT,SINT,DINT,LINT,UINT,USINT,UDINT,ULINT,TIME,TOD,DATE,DT,ANY, ANY_NUM,ANY_INT,ANY_REAL,TON,TOF,TP,RS,SR,R_TRIG,F_TRIG,CTU,CTD,CTUD|}; (* ${4:Comment} *)", 298 | "\t\t${5:Variable} : ${6|BOOL,BYTE,WORD,DWORD,STRING,WSTRING,REAL,LREAL,ARARY,POINTER TO,REF_TO,INT,SINT,DINT,LINT,UINT,USINT,UDINT,ULINT,TIME,TOD,DATE,DT,ANY, ANY_NUM,ANY_INT,ANY_REAL,TON,TOF,TP,RS,SR,R_TRIG,F_TRIG,CTU,CTD,CTUD|}; (* ${7:Comment} *)$0", 299 | "\tEND_STRUCT", 300 | "END_TYPE" 301 | ], 302 | "description": "Define STRUCT" 303 | }, 304 | "TYPE ... UNION ...END_UNION END_TYPE": { 305 | "prefix": ["st type union"], 306 | "body": [ 307 | "TYPE ${1:UnionName} : UNION", 308 | "\t\t${2:Variable} : ${3|BOOL,BYTE,WORD,DWORD,STRING,WSTRING,REAL,LREAL,ARARY,POINTER TO,REF_TO,INT,SINT,DINT,LINT,UINT,USINT,UDINT,ULINT,TIME,TOD,DATE,DT,ANY, ANY_NUM,ANY_INT,ANY_REAL,TON,TOF,TP,RS,SR,R_TRIG,F_TRIG,CTU,CTD,CTUD|}; (* ${4:Comment} *)", 309 | "\t\t${5:Variable} : ${6|BOOL,BYTE,WORD,DWORD,STRING,WSTRING,REAL,LREAL,ARARY,POINTER TO,REF_TO,INT,SINT,DINT,LINT,UINT,USINT,UDINT,ULINT,TIME,TOD,DATE,DT,ANY, ANY_NUM,ANY_INT,ANY_REAL,TON,TOF,TP,RS,SR,R_TRIG,F_TRIG,CTU,CTD,CTUD|}; (* ${7:Comment} *)$0", 310 | "\tEND_UNION", 311 | "END_TYPE" 312 | ], 313 | "description": "Define UNION" 314 | }, 315 | "comment": { 316 | "prefix": "st comment", 317 | "body": "$BLOCK_COMMENT_START $0 $BLOCK_COMMENT_END", 318 | "description": "Define comment" 319 | }, 320 | "block comment": { 321 | "prefix": "st comment block", 322 | "body": [ 323 | "$BLOCK_COMMENT_START ************************************* $BLOCK_COMMENT_END", 324 | "$BLOCK_COMMENT_START $0 $BLOCK_COMMENT_END", 325 | "$BLOCK_COMMENT_START ************************************* $BLOCK_COMMENT_END" 326 | ], 327 | "description": "Define comment block" 328 | }, 329 | "TRY ... CATCH ... FINALLY ... ENDTRY": { 330 | "prefix": "st try", 331 | "body": [ 332 | "__TRY", 333 | "\t$0", 334 | "__CATCH (${1:Variable})", 335 | "\t", 336 | "__FINALLY", 337 | "__ENDTRY" 338 | ], 339 | "description": "Define TRY-CATCH-FINALLY exception handling block. Variable is an UDINT or __SYSTEM.ExceptionCode type. These operators are extended from the IEC 61131-3 standard." 340 | }, 341 | "Dynamic allocation scalar": { 342 | "prefix": "st new", 343 | "body": [ 344 | "__NEW(${1:Scalar Type}, ${2:Number of elements});" 345 | ], 346 | "description": "The __NEW operator allocates memory for arrays of standard data types. The operator returns a suitable typed pointer to the object. These operators are extended from the IEC 61131-3 standard." 347 | }, 348 | "Dynamic allocation function": { 349 | "prefix": ["st new fb"], 350 | "body": [ 351 | "__NEW(${1:Function Block Or Structure Type});" 352 | ], 353 | "description": "The __NEW operator allocates memory for function block instances or structure. The operator returns a suitable typed pointer to the object. These operators are extended from the IEC 61131-3 standard." 354 | }, 355 | "Dynamic memory free": { 356 | "prefix": "st delete", 357 | "body": [ 358 | "__DELETE(${1:POINTER_TO Variable});" 359 | ], 360 | "description": "The operator releases the memory of instances, which the operator __NEW generated dynamically. These operators are extended from the IEC 61131-3 standard." 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as vscode from 'vscode'; 4 | 5 | import { STDocumentSymbolProvider } from './symbolprovider'; 6 | import { STFormatterProvider } from './formatter'; 7 | 8 | export function activate(context: vscode.ExtensionContext) { 9 | 10 | vscode.languages.registerDocumentFormattingEditProvider('st', 11 | new STFormatterProvider()); 12 | 13 | context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider( 14 | { language: "st" }, new STDocumentSymbolProvider() 15 | )); 16 | } 17 | 18 | export function deactivate() { 19 | } 20 | -------------------------------------------------------------------------------- /src/formatter.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import * as vscode from 'vscode'; 3 | 4 | export class STFormatterProvider implements vscode.DocumentFormattingEditProvider { 5 | private functions: Array = []; 6 | private keywords: Array = []; 7 | private types: Array = []; 8 | private ends: Array = []; 9 | private skipString: Array = []; 10 | 11 | provideDocumentFormattingEdits(document: vscode.TextDocument) { 12 | if (vscode.window.visibleTextEditors.every((e) => e.document.fileName !== document.fileName)) { 13 | return []; 14 | } 15 | 16 | let out = []; 17 | this.functions = [ 18 | 'abs', 'sin', 'mod', 'abs', 'acos', 'asin', 'atan', 'cos', 'exp', 19 | 'expt', 'ln', 'log', 'sin', 'sqrt', 'tan', 'sel', 'mux', 'shl', 20 | 'shr', 'rol', 'ror', 'add', 'div', 'mul', 'sub', 'limit', 'max', 21 | 'min', 'adr', 'adrinst', 'size', 'sizeof', 'bit_adr', 'bit_trunc', 22 | 'rs', 'sr', 'ton', 'tp', 'tof', 'trunc', 'ctd', 'ctu', 'ctud', 'r_trig', 23 | 'f_trig', 'move', 'concat', 'delete', 'find', 'insert', 'left', 'len', 24 | 'replace', 'right', 'rtc', 'mid', 'sema', 'round', 'floor', 'ceil', 25 | 'unpack', 'ref', '__new', '__delete', 26 | 27 | // GXW2 inscructions 28 | 'addp', 'dadd', 'daddp', 'subp', 'dsub', 'dsubp', 'mulp', 'dmul', 'dmulp', 'divp', 29 | 'ddiv', 'ddivp', 'inc', 'incp', 'dinc', 'dincp', 'dec', 'decp', 'ddec', 'ddecp', 30 | 'wand', 'wandp', 'dand', 'dandp', 'wor', 'worp', 'dor', 'dorp', 'wxor', 'wxorp', 31 | 'dxor', 'dxorp', 'neg', 'negp', 'dneg', 'dnegp', 'decmp', 'decmpp', 'dezcp', 32 | 'dezcpp', 'demov', 'demovp', 'destr', 'destrp', 'deval', 'devalp', 'debcd', 'debcdp', 33 | 'debin', 'debinp', 'deadd', 'deaddp', 'desub', 'desubp', 'demul', 'demulp', 'dediv', 34 | 'dedivp', 'dexp', 'dexpp', 'dloge', 'dlogep', 'dlog10', 'dlog10p', 'desqr', 'desqrp', 35 | 'deneg', 'denegp', 'int', 'intp', 'dint', 'dintp', 'dsin', 'dsinp', 'dcos', 'dcosp', 36 | 'dtan', 'dtanp', 'dasin', 'dasinp', 'dacos', 'dacosp', 'datan', 'datanp', 'drad', 37 | 'dradp', 'ddeg', 'ddegp', 'limitation', 'limit', 'limitp', 'dlimit', 'dlimitp', 'band', 38 | 'bandp', 'dband', 'dbandp', 'zone', 'zonep', 'dzone', 'dzonep', 'scl', 'sclp', 'dscl', 39 | 'dsclp', 'dabin', 'dabinp', 'ddabin', 'ddabinp', 'binda', 'bin', 'bindap', 'dbinda', 40 | 'dbindap', 'scl2', 'scl2p', 'dscl2', 'dscl2p', 'dszr', 'dvit', 'ddvit', 'dtbl', 'dabs', 41 | 'zrn', 'dzrn', 'plsv', 'dplsv', 'drvi', 'ddrvi', 'drva', 'ddrva', 'wsum', 'wsump', 42 | 'dwsum', 'dwsump', 'wtob', 'wtobp', 'btow', 'btowp', 'uni', 'unip', 'dis', 'disp', 43 | 'swap', 'swapp', 'dswap', 'dswapp', 'sort2', 'dsort2', 'ror', 'rorp', 'dror', 'drorp', 44 | 'rol', 'rolp', 'drol', 'drolp', 'rcr', 'rcrp', 'drcr', 'drcrp', 'rcl', 'rclp', 'drcl', 45 | 'drclp', 'sftr', 'sftrp', 'sftl', 'sftlp', 'wsfr', 'wsfrp', 'wsfl', 'wsflp', 'sfwr', 46 | 'sfwrp', 'sfrd', 'sfrdp', 'refp', 'reff', 'reffp', 'mtr', 'dhscs', 'dhscs_i', 'dhscr', 47 | 'dhsz', 'spd', 'dspd', 'plsy', 'dplsy', 'pwm', 'plsr', 'dplsr', 'ist', 'ser', 'serp', 48 | 'dser', 'dserp', 'absd', 'dabsd', 'incd', 'ttmr', 'stmr', 'alt', 'altp', 'ramp', 49 | 'rotc', 'sort', 'cmp', 'cmpp', 'dcmp', 'dcmpp', 'zcp', 'zcpp', 'dzcp', 'dzcpp', 50 | 'mov', 'movp', 'dmov', 'dmovp', 'smov', 'smovp', 'cml', 'cmlp', 'dcml', 'dcmlp', 51 | 'bmov', 'bmovp', 'fmov', 'fmovp', 'dfmov', 'dfmovp', 'xch', 'xchp', 'dxch', 'dxchp', 52 | 'bcd', 'bcdp', 'dbcd', 'dbcdp', 'bin', 'binp', 'dbin', 'dbinp', 53 | 54 | '[A-Za-z_]*(_TO_)[A-Za-z_]*', '(?:TO_|FROM_|TRUNC_)[A-Za-z_]*' 55 | ]; 56 | 57 | this.keywords = [ 58 | 'true', 'false', 'exit', 'continue', 'return', 'constant', 'retain', 59 | 'public', 'private', 'protected', 'abstract', 'persistent', 'internal', 60 | 'final', 'of', 'else', 'elsif', 'then', '__try', '__catch', '__finally', 61 | '__endtry', 'do', 'to', 'by', 'task', 'with', 'using', 'uses', 'from', 62 | 'until', 'or', 'or_else', 'and', 'and_then', 'not', 'xor', 'nor', 'ge', 63 | 'le', 'eq', 'ne', 'gt', 'lt', 'extends', 'implements', 'this', 'super', 64 | '(?:T|DT|TOD|D)#(?=[0-9])', 65 | // '(?:T|DT|TOD|D)#[0-9\\:\\-\\_yYmMdDhHsS]+', 66 | 'var_(?:input|output|in_out|temp|global|access|external)' 67 | ]; 68 | 69 | this.types = [ 70 | 'AT', 'BOOL', 'BYTE', '(?:D|L)?WORD', 'U?(?:S|D|L)?INT', 'L?REAL', 71 | 'TIME(?:_OF_DAY)?', 'TOD', 'DT', 'DATE(?:_AND_TIME)?', 'W?STRING', 72 | 'ARRAY', 'ANY', 'ANY_(?:NUM|INT|REAL)']; 73 | 74 | this.ends = [ 75 | 'var', 'program', 'if', 'case', 'while', 'for', 'repeat', 'function', 76 | 'function_block', 'struct', 'configuration', 'tcp', 'resource', 77 | 'channel', 'library', 'folder', 'binaries', 'includes', 'sources', 78 | 'action', 'step', 'initial_step', 'transition', 'type', 'namespace', 79 | 'implementation', 'interface', 'property', 'get', 'set', 'method', 'union', 'class' 80 | ]; 81 | 82 | // Do not format this strings 83 | this.skipString = [ 84 | `["']{1}[^\"\'\\\\]*(?:\\\\[\\s\\S][^"'\\\\]*)*["']{1}`, // All strings in quotes 85 | '\\(\\*[\\s\\S]*?\\*\\)', // All comments in braces 86 | '\\/\\*[\\s\\S]*?\\*\\/', // All comments in slashes 87 | '\\/\\/[^\\n]*\\n' // All single line comments 88 | ]; 89 | 90 | let text = document.getText(); 91 | 92 | text = this.spaces(text); 93 | text = this.capitalize(text); 94 | 95 | out.push( 96 | new vscode.TextEdit(new vscode.Range( 97 | new vscode.Position(0, 0), 98 | document.lineAt(document.lineCount - 1).range.end), text 99 | ) 100 | ); 101 | 102 | return out; 103 | } 104 | 105 | spaces(text: string): string { 106 | // Delete all double spaces 107 | while(text.match(/(? { 115 | return match.replace(/\s+/, ''); 116 | }); 117 | 118 | // Add space after keywords 119 | // IF( to IF ( 120 | regEx = new RegExp(`\\b(IF|WHILE|CASE)\\(`, "ig"); 121 | text = text.replace(regEx, (match, key) => { 122 | return key !== undefined ? key + ' (' : match; 123 | }); 124 | 125 | // Add before after keywords 126 | // )THEN to ) THEN 127 | regEx = new RegExp(`\\)(THEN|DO|OF)\\b`, "ig"); 128 | text = text.replace(regEx, (match, key) => { 129 | return key !== undefined ? ') ' + key : match; 130 | }); 131 | 132 | let addSpace = { 133 | csb: ['\\*\\)', '\\*\\/', '(?<=.)\\/\\/', '(?<=.)\\(\\*', '(?<=.)\\/\\*'], 134 | csa: ['\\(\\*', '\\/\\*', '\\/\\/'], 135 | ss: [':=', '<>', '>=', '<=', '=>', '\\+', '\\-', '\\/'], 136 | sb: ['(?|:)=', ':', '(?'], 137 | sa: ['=(?!>| )', ':(?!=)', '\\*(?!\\*|;)', ',', '<(?!=|>)', '>(?!=)'] 138 | }; 139 | 140 | regEx = new RegExp(`(? " " + sign); 142 | regEx = new RegExp(`(${addSpace.csa.join('|')})(?! |\\t)`, "ig"); 143 | text = text.replace(regEx, (match, sign) => sign + " "); 144 | 145 | regEx = new RegExp(`${this.skipString.join('|')}|(? sign !== undefined ? " " + sign : match); 147 | regEx = new RegExp(`${this.skipString.join('|')}|(${addSpace.ss.join('|')}|${addSpace.sa.join('|')})(?! |\\t)`, "ig"); 148 | text = text.replace(regEx, (match, sign) => sign !== undefined ? sign + " " : match); 149 | 150 | // Delete all spaces at the end of the lines 151 | text = text.split("\n").map(el => el.trimEnd()).join("\n"); 152 | 153 | // regEx = new RegExp(`[\s]+:(?!=)`, "ig"); 154 | text = text.replace(/[\s]+:(?!=)/g, ":"); 155 | // regEx = new RegExp(`;[\s]+\n$`, "ig"); 156 | text = text.replace(/;[\s]+\n$/g, ";\n"); 157 | 158 | 159 | 160 | return text; 161 | } 162 | 163 | capitalize(text: string): string { 164 | let regEx = new RegExp(`(? { 166 | return group !== undefined ? match.toUpperCase() : match; 167 | }); 168 | 169 | text = text.replace(/(?<=T|DT|TOD|D)#[0-9\\:\\-\\_yYmMdDhHsS]+/ig, (match, group) => { 170 | return group !== undefined ? match.toLowerCase() : match; 171 | }); 172 | 173 | return text; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/symbolprovider.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import * as vscode from 'vscode'; 3 | 4 | interface TPouBlockDesc { PouBlock: string, SymType: vscode.SymbolKind, Desc: string }; 5 | interface TVarBlockDesc { varKeyword: string, desc: string }; 6 | 7 | export class STDocumentSymbolProvider implements vscode.DocumentSymbolProvider { 8 | /* 9 | While identifying POU blocks, it is important to not allow the search in certain text, 10 | Quoted strings : (["'])(?:(?!\1)(?:\$\1|[\s\S]))*(?:\1|$) 11 | Commented lines : \/\/.*(?=\r?\n|$) 12 | Comment Blocks : \(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$) 13 | 14 | The search must consume wholly the parts above, so 'as few as possible but as many as needed' quantifier (*?) must be used along with: 15 | other text : [\s\S] 16 | 17 | And all patterns must be allowed to match the end of the document in place of the proper ending mark, but the whole pattern cannot match at 18 | the end of the document alone (probably a JavaScript / Chromium / V8 bug, empty match repeats indefinitely): 19 | base regex: /(?!$)(?:\/\/.*(?=\r?\n|$)|(["'])(?:(?!\1)(?:\$\1|[\s\S]))*(?:\1|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[\s\S])*?/ 20 | 21 | While searching for identifiers, we do not ignore quoted strings, but quotes strings or other punctuation will cause the search for 22 | an identifier to end. 23 | Pattern for identifiers: (case insensitive): \b[A-Z_](?:[A-Z0-9]|(? { 61 | const doc = document.getText(); 62 | return new Promise((resolve, reject) => { 63 | resolve(recursePouSymbols(doc, 0)); 64 | 65 | }); 66 | 67 | function recursePouSymbols(text: string, offset: number): vscode.DocumentSymbol[] { 68 | // break down the boundaries of potentially nested POU's and gather identifiers to return as symbols. 69 | let symbols: vscode.DocumentSymbol[] = []; 70 | // regex determines boundaries for POU's and which POU's are nested in other POU's, not all POU's can be nested. 71 | const rgx_pou = /(?!$)((?:\/\/.*(?=\r?\n|$)|(["'])(?:(?!\2)(?:\$\2|[\s\S]))*(?:\2|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[\s\S])*?)(?:$|\b((PROGRAM|FUNCTION(?:_BLOCK)?|INTERFACE|ACTION|METHOD|TYPE|VAR(?=\b|_(?:INPUT|OUTPUT|IN_OUT|INST|TEMP|STAT|GLOBAL|ACCESS|EXTERNAL|CONFIG)))(?:\b|(?<=VAR)_(?:INPUT|OUTPUT|IN_OUT|INST|TEMP|STAT|GLOBAL|ACCESS|EXTERNAL|CONFIG)))\b((? { 92 | symbols.push(item); 93 | }); 94 | } 95 | } 96 | 97 | else if (pou_type.substr(0, 3) === "VAR") { 98 | // determine attributes of the VAR block, and then iterate over all items, though it is unusual for sub-elements (STRUCT / UNION or enums) to be nested. 99 | const rgx_var_attr = /((?:(?:\b(?:ABSTRACT|CONSTANT|RETAIN|PERSISTENT|PUBLIC|PRIVATE|PROTECTED|INTERNAL|FINAL)\b)?(?:\/\/.*(?=\r?\n|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s]*?(?:\*\/|$)|[\s])*?)*)(?:$|(?=\S))/iy 100 | /* Captures break-down 101 | [1] POU attributes prior to first sub-item 102 | */ 103 | let var_attr = pous[5].match(rgx_var_attr); 104 | // determine if the CONSTANT attribute was present 105 | let var_attr_constant = var_attr && var_attr[1].match(/(?:\/\/.*(?=\r?\n|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[\s\S])*?(?:$|\b(CONSTANT)\b)/iy) || null; 106 | let isConstantVar = var_attr_constant && var_attr_constant[1] !== undefined || false; 107 | const varSymbol = STDocumentSymbolProvider.varBlocksList.find(varDes => varDes.varKeyword === pou_type) 108 | || { varKeyword: pou_type, desc: "" } as TVarBlockDesc; 109 | let symbol = new vscode.DocumentSymbol( 110 | pou_type, varSymbol.desc, vscode.SymbolKind.File, pou_full_range, pou_reveal_range); 111 | const var_attr_offset = var_attr && var_attr[0] !== undefined ? var_attr[0].length : 0; 112 | symbol.children = listVariables(pous[5].substr(var_attr_offset), pou_content_offset + var_attr_offset, false, isConstantVar); 113 | symbols.push(symbol); 114 | } 115 | 116 | else { 117 | // for all other POU types, find the block's name, and then recurse for possible nested symbols and POU's. 118 | const rgx_pou_name = /((?:(?:\b(?:ABSTRACT|CONSTANT|RETAIN|PERSISTENT|PUBLIC|PRIVATE|PROTECTED|INTERNAL|FINAL)\b)?(?:\/\/.*(?=\r?\n|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[\s])*?)*)(?:$|\b(?=(?:(?:END_)?(?:ACTION|METHOD|STRUCT|UNION|(?<=END_)VAR|(? blockDes.PouBlock === pou_type) 125 | || { PouBlock: pou_type, SymType: vscode.SymbolKind.Null, Desc: "" } as TPouBlockDesc; 126 | let symbol = new vscode.DocumentSymbol( 127 | checkUnnamedItem(pou_name && pou_name[2] || undefined), pouBlockSymbol.Desc, 128 | pouBlockSymbol.SymType, 129 | pou_full_range, pou_reveal_range 130 | ); 131 | const pou_name_offset = pou_name && pou_name[0] !== undefined ? pou_name[0].length : 0; 132 | symbol.children = recursePouSymbols(pous[5].substr(pou_name_offset), pou_content_offset + pou_name_offset); 133 | symbols.push(symbol); 134 | } 135 | } 136 | 137 | } 138 | return symbols; 139 | 140 | function listVariables(text: string, offset: number, isType: Boolean, isConstantVar?: Boolean): vscode.DocumentSymbol[] { 141 | // break down variable / type / structure / enum lists, they are literally all the same 142 | let items: vscode.DocumentSymbol[] = []; 143 | let rgx_struct = /(?!\s*$)((?:\/\/.*(?=\r?\n|$)|(["'])(?:(?!\2)(?:\$\2|[\s\S]))*(?:\2|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[\s;])*?(?!\/\/|\/\*|\(\*|[\s;]))(?:$|((?:\/\/.*(?=\r?\n|$)|(["'])(?:(?!\4)(?:\$\4|[\s\S]))*(?:\4|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[^:;]|:=)*?)(?:$|;|(:(?!=)(?:\/\/.*(?=\r?\n|$)|(["'])(?:(?!\6)(?:\$\6|[\s\S]))*(?:\6|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[\s])*?)(?:$|\b(STRUCT|UNION)\b((?:\/\/.*(?=\r?\n|$)|(["'])(?:(?!\9)(?:\$\9|[\s\S]))*(?:\9|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[\s\S])*?)\bEND_\7\b|(?:\((?!\*)((?:\/\/.*(?=\r?\n|$)|(["'])(?:(?!\11)(?:\$\11|[\s\S]))*(?:\11|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[\s\S])*?)(?:$|\)(?:\/\/.*(?=\r?\n|$)|(["'])(?:(?!\12)(?:\$\12|[\s\S]))*(?:\12|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[\s\S])*?))?(?:\b([A-Z_](?:[A-Z0-9]|(? { 164 | const enums_offset = content_offset + varIdent.startOffset; 165 | const enums_pos = document.positionAt(enums_offset); 166 | itemChildren.push(new vscode.DocumentSymbol( 167 | checkUnnamedItem(varIdent.varIdent), 'Enumerator', 168 | vscode.SymbolKind.EnumMember, 169 | new vscode.Range(enums_pos, document.positionAt(content_offset + varIdent.endOffset)), 170 | new vscode.Range(enums_pos, document.positionAt(enums_offset + (varIdent.varIdent !== undefined ? varIdent.varIdent.length : 0))) 171 | )); 172 | }); 173 | } 174 | 175 | else if (ms[8] !== undefined) { 176 | // need to recurse through elements in a STRUCT or UNION 177 | const content_offset = struct_offset + ms[3].length + ms[5].length + ms[7].length; 178 | itemChildren = listVariables(ms[8], content_offset, false); 179 | } 180 | 181 | // create a separate item for each variable/type identifier defined here 182 | separateVarIdents(ms[3]).forEach(varIdent => { 183 | const varIdent_offset = struct_offset + varIdent.startOffset; 184 | const struct_pos = document.positionAt(varIdent_offset); 185 | let item = new vscode.DocumentSymbol( 186 | (ms![13] !== undefined) ? (checkUnnamedItem(varIdent.varIdent) + " (" + ms![13] + ")") : checkUnnamedItem(varIdent.varIdent), 187 | isType ? (isEnum ? 'Enumeration' : ((ms![7] === undefined || ms![7].toUpperCase() === 'STRUCT') ? 'Structure' : 'Union')) : ((isConstantVar !== undefined ? isConstantVar : false) ? 'Constant' : 'Variable'), 188 | isType ? (isEnum ? vscode.SymbolKind.Enum : vscode.SymbolKind.Struct) : ((isConstantVar !== undefined ? isConstantVar : false) ? vscode.SymbolKind.Constant : vscode.SymbolKind.Variable), 189 | new vscode.Range(struct_pos, document.positionAt(offset + ms!.index + ms![0].length)), 190 | new vscode.Range(struct_pos, document.positionAt(varIdent_offset + (varIdent.varIdent !== undefined ? varIdent.varIdent.length : 0))) 191 | ); 192 | item.children = itemChildren; 193 | items.push(item); 194 | }); 195 | } 196 | } 197 | return items; 198 | 199 | // a helper object for returning identifiers after separating them 200 | type TVarIdentReturn = { varIdent: string | undefined, startOffset: number, endOffset: number }; 201 | 202 | function separateVarIdents(text: string): TVarIdentReturn[] { 203 | // separate a comma seperated list of variable / type / struct / union / enum identifiers 204 | // forcibly ignore certain common keywords that might appear nearby, so we don't scan too deeply 205 | let rgx_varIdent = /(?!$)((?:\/\/.*(?=\r?\n|$)|\(\*[\s\S]*?(?:\*\)|$)|\/\*[\s\S]*?(?:\*\/|$)|[\s\S])*?)(?:$|,|(?:\b(?=(?:EXTENDS|IMPLEMENTS|PROPERTY|USES|IF|CASE|WHILE|REPEAT|DO|FOR|RETURN|EXIT|CONTINUE|AT|ANY|BIT|BOOL|BYTE|[DL]?WORD|U?[SDL]?INT|L?REAL|L?TIME(?:_OF_DAY)?|L?TOD|L?DT|L?DATE(?:_AND_TIME)?|W?STRING|W?CHAR|ARRAY)\b)|\b([A-Z_](?:[A-Z0-9]|(?` if no name was found 227 | return name !== undefined ? name : ""; 228 | } 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /syntaxes/md.codeblock.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileTypes": [], 3 | "injectionSelector": "L:text.html.markdown", 4 | "patterns": [ 5 | { 6 | "include": "#iecst-code-block" 7 | } 8 | ], 9 | "repository": { 10 | "iecst-code-block": { 11 | "begin": "(^|\\G)(\\s*)(\\`{3,}|~{3,})\\s*(?i:(iecst)(\\s+[^`~]*)?$)", 12 | "name": "markup.fenced_code.block.markdown", 13 | "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", 14 | "beginCaptures": { 15 | "3": { 16 | "name": "punctuation.definition.markdown" 17 | }, 18 | "5": { 19 | "name": "fenced_code.block.language" 20 | }, 21 | "6": { 22 | "name": "fenced_code.block.language.attributes" 23 | } 24 | }, 25 | "endCaptures": { 26 | "3": { 27 | "name": "punctuation.definition.markdown" 28 | } 29 | }, 30 | "patterns": [ 31 | { 32 | "begin": "(^|\\G)(\\s*)(.*)", 33 | "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", 34 | "contentName": "meta.embedded.block.iecstmd", 35 | "patterns": [ 36 | { 37 | "include": "source.st" 38 | } 39 | ] 40 | } 41 | ] 42 | } 43 | }, 44 | "scopeName": "markdown.iecst.codeblock" 45 | } -------------------------------------------------------------------------------- /syntaxes/st.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "Structured Text", 4 | "patterns": [ 5 | { 6 | "include": "#system" 7 | }, 8 | { 9 | "include": "#comments" 10 | }, 11 | { 12 | "include": "#strings" 13 | }, 14 | { 15 | "include": "#operators" 16 | }, 17 | { 18 | "include": "#functions" 19 | }, 20 | { 21 | "include": "#meta" 22 | }, 23 | { 24 | "include": "#numbers" 25 | }, 26 | { 27 | "include": "#variables" 28 | }, 29 | { 30 | "include": "#keywords" 31 | } 32 | ], 33 | "repository": { 34 | "system": { 35 | "patterns": [ 36 | { 37 | "match": "\\n", 38 | "name": "meta.ending-space" 39 | }, 40 | { 41 | "begin": "^(?=\\t)", 42 | "end": "(?=[^\\t])", 43 | "name": "meta.leading-space", 44 | "patterns": [ 45 | { 46 | "match": "(\\t)(\\t)?", 47 | "captures": { 48 | "1": { 49 | "name": "meta.odd-tab.tabs" 50 | }, 51 | "2": { 52 | "name": "meta.even-tab.tabs" 53 | } 54 | } 55 | } 56 | ] 57 | }, 58 | { 59 | "begin": "^(?= )", 60 | "end": "(?=[^ ])", 61 | "name": "meta.leading-space", 62 | "patterns": [ 63 | { 64 | "match": "( )( )?", 65 | "captures": { 66 | "1": { 67 | "name": "meta.odd-tab.spaces" 68 | }, 69 | "2": { 70 | "name": "meta.even-tab.spaces" 71 | } 72 | } 73 | } 74 | ] 75 | }, 76 | { 77 | "name": "storage.type.class.st", 78 | "match": "\\s*\\b(PROGRAM|FUNCTION_BLOCK|FUNCTION|TYPE)\\b\\s*(PRIVATE|PUBLIC|PROTECTED)?\\s*\\b([a-zA-Z_0-9]*)\\b", 79 | "captures": { 80 | "2": { 81 | "name": "storage.modifier.st" 82 | }, 83 | "3": { 84 | "name": "entity.name.function.st" 85 | } 86 | } 87 | }, 88 | { 89 | "name": "storage.type.class.st", 90 | "match": "\\s*\\b(EXTENDS|IMPLEMENTS)\\b\\s*\\b([a-zA-Z_0-9]*)\\b", 91 | "captures": { 92 | "2": { 93 | "name": "entity.name.function.st" 94 | } 95 | } 96 | } 97 | ] 98 | }, 99 | "numbers": { 100 | "patterns": [ 101 | { 102 | "name": "constant.numeric.integer.st", 103 | "match": "\\b(2#[0-9\\_]+)\\b" 104 | }, 105 | { 106 | "name": "constant.numeric.hex.st", 107 | "match": "\\b(16#[0-9A-Fa-f\\_]+)\\b" 108 | }, 109 | { 110 | "name": "constant.numeric.integer.st", 111 | "match": "\\b(8#[0-9\\_]+)\\b" 112 | }, 113 | { 114 | "name": "constant.numeric.float.st", 115 | "match": "\\b\\d*\\.\\d+([eE][\\-+]?\\d+)?\\b" 116 | }, 117 | { 118 | "name": "constant.numeric.integer.st", 119 | "match": "\\b(\\d)+\\b" 120 | }, 121 | { 122 | "name": "constant.numeric.integer.st", 123 | "match": "\\b(?:H)[0-9ABCDEF]+\\b" 124 | }, 125 | { 126 | "name": "constant.numeric.integer.st", 127 | "match": "\\b(?:K)[0-9]+\\b" 128 | }, 129 | { 130 | "name": "constant.numeric.integer.st", 131 | "match": "\\b(BYTE|(?:D|L)?WORD|U?(?:S|D|L)?INT)#(2|8)#[0-9\\_]+\\b" 132 | }, 133 | { 134 | "name": "constant.numeric.integer.st", 135 | "match": "\\b(BYTE|(?:D|L)?WORD|U?(?:S|D|L)?INT)#16#[a-fA-F0-9\\_]+\\b" 136 | }, 137 | { 138 | "name": "constant.numeric.integer.st", 139 | "match": "\\b(BYTE|(?:D|L)?WORD|U?(?:S|D|L)?INT)#-?[0-9\\_]+\\b" 140 | }, 141 | { 142 | "name": "constant.numeric.float.st", 143 | "match": "\\b(L?REAL)#-?\\d*\\.\\d+([eE][\\-+]?\\d+)?\\b" 144 | } 145 | ] 146 | }, 147 | "controls": { 148 | "patterns": [ 149 | { 150 | "name": "keyword.control.conditional.st", 151 | "match": "\\b(?:END_)?(?:IF|CASE|OF|ELSE|ELSIF|THEN|__TRY|__CATCH|__FINALLY|__ENDTRY)\\b" 152 | }, 153 | { 154 | "name": "keyword.control.loop.st", 155 | "match": "\\b(?:END_)?(?:WHILE|FOR|REPEAT|DO|TO|BY)\\b" 156 | }, 157 | { 158 | "name": "keyword.control.flow.st", 159 | "match": "\\b(?:RETURN|EXIT|CONTINUE|GOTO|JMP|BEGIN|COUNTER|GOTO)\\b" 160 | }, 161 | { 162 | "name": "keyword.control.st", 163 | "match": "\\b(END_)?(TASK|WITH|USING|USES|FROM|UNTIL)\\b" 164 | } 165 | ] 166 | }, 167 | "operators": { 168 | "patterns": [ 169 | { 170 | "name": "keyword.operator.range.st", 171 | "match": "(?|S=|R=)" 176 | }, 177 | { 178 | "name": "keyword.operator.content.st", 179 | "match": "\\^|MOVE" 180 | }, 181 | { 182 | "name": "keyword.operator.logical.st", 183 | "match": "\\b(OR(_ELSE)?|AND(_THEN)?|NOT|XOR|NOR)\\b|&" 184 | }, 185 | { 186 | "name": "keyword.operator.comparison.st", 187 | "match": "(?:<>|<|=|>|<=|>=)" 188 | }, 189 | { 190 | "name": "keyword.operator.comparison.st", 191 | "match": "\\b(?:GE|LE|EQ|NE|GT|LT)\\b" 192 | }, 193 | { 194 | "name": "keyword.operator.arithmetic.st", 195 | "match": "(?:\\+|\\-|\\/|\\*|MOD)" 196 | }, 197 | { 198 | "name": "keyword.operator.arithmetic.st", 199 | "match": "\\b(?:ABS|SIN|ABS|ACOS|ASIN|ATAN|COS|EXP|EXPT|LN|LOG|SIN|SQRT|TAN|SEL|MUX|SHL|SHR|ROL|ROR|ADD|DIV|MUL|SUB|MAX|MIN|ADR(INST)?|SIZE(OF)?|BIT_?ADR|TRUNC(_INT)?)(?=\\()" 200 | }, 201 | { 202 | "name": "keyword.operator.arithmetic.st", 203 | "comment": "GXW2 operators", 204 | "match": "\\b(?:ADDP|DADD|DADDP|SUBP|DSUB|DSUBP|MULP|DMUL|DMULP|DIVP|DDIV|DDIVP|INC|INCP|DINC|DINCP|DEC|DECP|DDEC|DDECP|WAND|WANDP|DAND|DANDP|WOR|WORP|DOR|DORP|WXOR|WXORP|DXOR|DXORP|NEG|NEGP|DNEG|DNEGP|DECMP|DECMPP|DEZCP|DEZCPP|DEMOV|DEMOVP|DESTR|DESTRP|DEVAL|DEVALP|DEBCD|DEBCDP|DEBIN|DEBINP|DEADD|DEADDP|DESUB|DESUBP|DEMUL|DEMULP|DEDIV|DEDIVP|DEXP|DEXPP|DLOGE|DLOGEP|DLOG10|DLOG10P|DESQR|DESQRP|DENEG|DENEGP|INT|INTP|DINT|DINTP|DSIN|DSINP|DCOS|DCOSP|DTAN|DTANP|DASIN|DASINP|DACOS|DACOSP|DATAN|DATANP|DRAD|DRADP|DDEG|DDEGP)(?=\\()" 205 | }, 206 | { 207 | "name": "keyword.operator.arithmetic.st", 208 | "comment": "GXW2 functions", 209 | "match": "\\b(?:LIMITATION|LIMIT|LIMITP|DLIMIT|DLIMITP|BAND|BANDP|DBAND|DBANDP|ZONE|ZONEP|DZONE|DZONEP|SCL|SCLP|DSCL|DSCLP|DABIN|DABINP|DDABIN|DDABINP|BINDA|BIN|BINDAP|DBINDA|DBINDAP|SCL2|SCL2P|DSCL2|DSCL2P|DSZR|DVIT|DDVIT|DTBL|DABS|ZRN|DZRN|PLSV|DPLSV|DRVI|DDRVI|DRVA|DDRVA|WSUM|WSUMP|DWSUM|DWSUMP|WTOB|WTOBP|BTOW|BTOWP|UNI|UNIP|DIS|DISP|SWAP|SWAPP|DSWAP|DSWAPP|SORT2|DSORT2|ROR|RORP|DROR|DRORP|ROL|ROLP|DROL|DROLP|RCR|RCRP|DRCR|DRCRP|RCL|RCLP|DRCL|DRCLP|SFTR|SFTRP|SFTL|SFTLP|WSFR|WSFRP|WSFL|WSFLP|SFWR|SFWRP|SFRD|SFRDP|REFP|REFF|REFFP|MTR|DHSCS|DHSCS_I|DHSCR|DHSZ|SPD|DSPD|PLSY|DPLSY|PWM|PLSR|DPLSR|IST|SER|SERP|DSER|DSERP|ABSD|DABSD|INCD|TTMR|STMR|ALT|ALTP|RAMP|ROTC|SORT|CMP|CMPP|DCMP|DCMPP|ZCP|ZCPP|DZCP|DZCPP|MOV|MOVP|DMOV|DMOVP|SMOV|SMOVP|CML|CMLP|DCML|DCMLP|BMOV|BMOVP|FMOV|FMOVP|DFMOV|DFMOVP|XCH|XCHP|DXCH|DXCHP|BCD|BCDP|DBCD|DBCDP|BIN|BINP|DBIN|DBINP)(?=\\()" 210 | }, 211 | { 212 | "name": "keyword.operator.arithmetic.st", 213 | "match": "\\b(SEL_|MUX_)[A-Za-z]*\\b" 214 | }, 215 | { 216 | "name": "keyword.operator.new.st", 217 | "match": "\\b__(NEW|DELETE)(?=\\()\\b" 218 | } 219 | ] 220 | }, 221 | "functions": { 222 | "patterns": [ 223 | { 224 | "name": "support.function.st", 225 | "match": "\\b[A-Za-z_]*(_TO_|TO_|FROM_|TRUNC_)[A-Za-z_]+(?=\\()" 226 | }, 227 | { 228 | "name": "support.function.st", 229 | "match": "\\b(?:TRUNC|MOVE|CONCAT|LIMIT|DELETE|FIND|INSERT|LEFT|LEN|REPLACE|RIGHT|RTC|MID|SEMA|ROUND|FLOOR|CEIL|UNPACK|REF)(?=\\()" 230 | }, 231 | { 232 | "begin": "([a-zA-Z_0-9]+)(\\()", 233 | "beginCaptures": { 234 | "1": { 235 | "name": "entity.name.function.st" 236 | }, 237 | "2": { 238 | "name": "punctuation.parenthesis.begin.st" 239 | } 240 | }, 241 | "end": "\\)", 242 | "endCaptures": { 243 | "0": { 244 | "name": "punctuation.parenthesis.end.st" 245 | } 246 | }, 247 | "patterns": [ 248 | { 249 | "include": "$self" 250 | }, 251 | { 252 | "match": "\\b(N|R0|S0|L|D|P|SD|DS|SL|T)\\b", 253 | "name": "variable.parameter.st", 254 | "comment": "Action parameters" 255 | }, 256 | { 257 | "match": "\\b([a-zA-Z0-9_]+) *:?=>? *", 258 | "captures": { 259 | "1": { 260 | "name": "variable.parameter.st" 261 | } 262 | } 263 | } 264 | ] 265 | }, 266 | { 267 | "name": "punctuation.accessor.st", 268 | "match": "(?)\\s*", 12 | "end": "\\s*(<\\/)(\\2)(>)$", 13 | "beginCaptures": { 14 | "1": { 15 | "name": "punctuation.definition.tag.xml" 16 | }, 17 | "3": { 18 | "name": "punctuation.definition.tag.xml" 19 | }, 20 | "2": { 21 | "name": "entity.name.tag.localname.xml" 22 | } 23 | }, 24 | "endCaptures": { 25 | "1": { 26 | "name": "punctuation.definition.tag.xml" 27 | }, 28 | "3": { 29 | "name": "punctuation.definition.tag.xml" 30 | }, 31 | "2": { 32 | "name": "entity.name.tag.localname.xml" 33 | } 34 | }, 35 | "patterns": [ 36 | { 37 | "begin": "\\s*()", 39 | "beginCaptures": { 40 | "1": { 41 | "name": "string.unquoted.cdata.xml" 42 | } 43 | }, 44 | "endCaptures": { 45 | "1": { 46 | "name": "string.unquoted.cdata.xml" 47 | } 48 | }, 49 | "contentName": "meta.embedded.block.iecst", 50 | "patterns": [ 51 | { 52 | "include": "source.st" 53 | } 54 | ] 55 | } 56 | ] 57 | } 58 | }, 59 | "scopeName": "xml.iecst.codeblock" 60 | } 61 | -------------------------------------------------------------------------------- /text/IECST.xtext: -------------------------------------------------------------------------------- 1 | grammar org.xtext.example.iecst.IECST with org.eclipse.xtext.common.Terminals 2 | 3 | generate iECST "http://www.xtext.org/example/iecst/IECST" 4 | 5 | StModel: 6 | (elements+=AbstractElement)*; 7 | 8 | 9 | AbstractElement: 10 | Namespace | Program | Function | FunctionBlock | Type; 11 | 12 | Namespace: 13 | 'NAMESPACE' name=ID 14 | (elements+=AbstractElement)* 15 | 'END_NAMESPACE' 16 | ; 17 | 18 | Type: 19 | 'TYPE' name=ID '('? 20 | data=ID 21 | ')'? 'END_TYPE' 22 | ; 23 | 24 | Program: 25 | 'PROGRAM' name=ID 26 | (defenitions+=Defenition)* 27 | 'END_PROGRAM' 28 | ; 29 | 30 | Function: 31 | 'FUNCTION' name=ID ':' datatype=ID 32 | (defenitions+=Defenition)* 33 | 34 | 'END_FUNCTION' 35 | ; 36 | 37 | FunctionBlock: 38 | 'FUNCTION_BLOK' name=ID 39 | (defenitions+=Defenition)* 40 | 41 | 'END_FUNCTION_BLOCK' 42 | ; 43 | 44 | Defenition: 45 | {Defenition} ('VAR' | 'VAR_INPUT' | 'VAR_OUTPUT' | 'VAR_IN_OUT' | 'VAR_TEMP' | 'VAR_EXTERNAL' | 'VAR_GLOBAL') ('CONSTANT' | 'RETAIN')* 46 | (declarations+=Declaration)* 47 | 'END_VAR' 48 | ; 49 | 50 | Declaration: 51 | variable=ID ('AT')? ':' datatype=ID (':=' value+=Value)? ';' 52 | ; 53 | 54 | Value: 55 | 'test' 56 | ; -------------------------------------------------------------------------------- /text/test.iecst: -------------------------------------------------------------------------------- 1 | a := "dfd"; 2 | -------------------------------------------------------------------------------- /text/test.md: -------------------------------------------------------------------------------- 1 | # This is test in MD 2 | dddd ddd 3 | Here is the code, should be highlighted. 4 | 5 | ```iecst 6 | VAR 7 | xStart: BOOL; (* This is comment *) 8 | END_VAR 9 | ``` 10 | 11 | Here is the code. -------------------------------------------------------------------------------- /text/test.st: -------------------------------------------------------------------------------- 1 | 2 | 3 | { attribute 'something' } 4 | PROGRAM PLC_PRG 5 | VAR 6 | xStart: BOOL := 'dfsfdfd'; // something 7 | fbTON1: TON; (* Таймер время исполнения *) 8 | rValue: REAL; /* Конечное значение */ 9 | END_VAR 10 | 11 | fbTON1(IN := xStart, PT => tDuration); 12 | 13 | a := () 14 | IF xStart <> FALSE THEN 15 | rValue := easeLinear(fbTON1.ET, rFrom, rTo, tDuration); 16 | END_IF; 17 | (a = b) AND c > 10; 18 | c := (b / 3) * 12**; 19 | a := CONCAT(a, ':= **'); // a := b 20 | (* Description *) 21 | METHOD GetThat: BOOL 22 | VAR_INPUT 23 | xStart: BOOL; (* Comment *) 24 | END_VAR 25 | END_METHOD 26 | END_PROGRAM 27 | 28 | NAMESPACE SOmething 29 | 30 | END_NAMESPACE 31 | -------------------------------------------------------------------------------- /text/text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2020", 5 | "outDir": "out", 6 | "lib": [ 7 | "ES2020" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | /* Strict Type-Checking Option */ 12 | "experimentalDecorators": true, 13 | "esModuleInterop": true, 14 | "strict": true 15 | /* Additional Checks */ 16 | // "noUnusedLocals": true /* Report errors on unused locals. */ 17 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 18 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 19 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 20 | }, 21 | "exclude": [ 22 | "node_modules", 23 | ".vscode-test" 24 | ] 25 | } 26 | --------------------------------------------------------------------------------