diff --git a/.gitignore b/.gitignore index b30a37a..3f5159d 100644 --- a/.gitignore +++ b/.gitignore @@ -197,3 +197,8 @@ spin2/scripts/mode.flg f1.txt filt-*.log spin2/TEST_LANG_SERVER/spin2/USB_Packet_Driver.spin2 +spin2/TEST_LANG_SERVER/spin2/chk_code_comment_demo.readme.txt +/spin2/TEST_LANG_SERVER/FmAda +/spin2/TEST_LANG_SERVER/FmJose +/spin2/TEST_LANG_SERVER/FlexSpin +EditingSPIN2.md diff --git a/DOCs/Spin2Ext-Settings1of3.png b/DOCs/Spin2Ext-Settings1of3.png new file mode 100644 index 0000000..e0d43ca Binary files /dev/null and b/DOCs/Spin2Ext-Settings1of3.png differ diff --git a/DOCs/Spin2Ext-Settings2of3.png b/DOCs/Spin2Ext-Settings2of3.png new file mode 100644 index 0000000..cd11eb6 Binary files /dev/null and b/DOCs/Spin2Ext-Settings2of3.png differ diff --git a/DOCs/Spin2Ext-Settings3of3.png b/DOCs/Spin2Ext-Settings3of3.png new file mode 100644 index 0000000..477b8fa Binary files /dev/null and b/DOCs/Spin2Ext-Settings3of3.png differ diff --git a/EXTENSIONS.md b/EXTENSIONS.md index dcaff75..294a31c 100644 --- a/EXTENSIONS.md +++ b/EXTENSIONS.md @@ -2,6 +2,7 @@ ![Project Maintenance][maintenance-shield] + [![License][license-shield]](LICENSE) Scine i've been using VSCode and now especially after having extended VScode to support our new favortie language Spin2 (Spin for the P2) I have found a number of VScode Extensions useful when writing Spin2 and Pasm2 code. @@ -42,8 +43,8 @@ Lastly, you could PM [me in the Paralax forums](https://forums.parallax.com/prof ## License -Licensed under the MIT License.
-
+Licensed under the MIT License. + Follow these links for more information: ### [Copyright](copyright) | [License](LICENSE) @@ -52,4 +53,5 @@ Follow these links for more information: [maintenance-shield]: https://img.shields.io/badge/maintainer-stephen%40ironsheep%2ebiz-blue.svg?style=for-the-badge -[license-shield]: https://camo.githubusercontent.com/bc04f96d911ea5f6e3b00e44fc0731ea74c8e1e9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f69616e74726963682f746578742d646976696465722d726f772e7376673f7374796c653d666f722d7468652d6261646765 +[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg + diff --git a/InsertMode.md b/InsertMode.md index 84daac0..d5fb52b 100644 --- a/InsertMode.md +++ b/InsertMode.md @@ -2,6 +2,7 @@ ![Project Maintenance][maintenance-shield] + [![License][license-shield]](LICENSE) @@ -253,5 +254,6 @@ Follow these links for more information: [maintenance-shield]: https://img.shields.io/badge/maintainer-stephen%40ironsheep%2ebiz-blue.svg?style=for-the-badge -[license-shield]: https://camo.githubusercontent.com/bc04f96d911ea5f6e3b00e44fc0731ea74c8e1e9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f69616e74726963682f746578742d646976696465722d726f772e7376673f7374796c653d666f722d7468652d6261646765 +[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg + diff --git a/PT-Color-setup.md b/PT-Color-setup.md index 63e716e..6e02b0a 100644 --- a/PT-Color-setup.md +++ b/PT-Color-setup.md @@ -92,4 +92,5 @@ Follow these links for more information: [marketplace-rating]: https://vsmarketplacebadge.apphb.com/rating-short/ironsheepproductionsllc.spin2.svg -[license-shield]: https://camo.githubusercontent.com/bc04f96d911ea5f6e3b00e44fc0731ea74c8e1e9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f69616e74726963682f746578742d646976696465722d726f772e7376673f7374796c653d666f722d7468652d6261646765 +[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg + diff --git a/README.md b/README.md index bd986ca..6d4cc8e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ The **P1 Forum Thread** containing discussion of [this VSCode support](https://f - **P1: Syntax and Semantic Highlighting** for both Spin and Pasm - **P1: Show Hovers Feature** Hovers show information about the symbol/object that's below the mouse cursor. In our case this is for both user written code and for Spin built-ins. - **P1: Signature Help Feature** As you are typing a method name show signature help for both user written methods and for Spin built-in methods. -- **Object Public interface documentation generation** via keystroke [Ctrl+Alt+d], doc opens on right side of editor +- **Object Public interface Documentation generation** via keystroke [Ctrl+Alt+d] - Ctrl+Alt+( d )ocument.
- Document opens on right side of editor +- **Object Hierarchy Report generation** via keystroke [Ctrl+Alt+r] - Ctrl+Alt+( r )eport.
- Report opens on right side of editor - **Doc-Comment Generation** for PUB and PRI methods via keystroke [Ctrl+Alt+c] - Ctrl+Alt+( c )omment.
- Comment is inserted immediately below the PUB or PRI line. - Editor **Screen Coloring** support per section à la Parallax **Propeller Tool** - **Custom tabbing** Tab-stop support per section à la Parallax **Propeller Tool** @@ -44,13 +45,13 @@ The **P1 Forum Thread** containing discussion of [this VSCode support](https://f ### Up next We are working on the next updates: -- Code folding support - Improve Hover support (more doc details such as pasm code help) These are not yet definate but I'm: - Looking into adding a setting to our extension allowing one to change a "PNut Enable Debug" setting which would be used when building with on windows with PNut - Looking into developing a Task Provider (to be built into our extension) which would recognize the tools installed and the OS and then provide only the tasks appropriate for the OS with the tools installed. +- Looking into customizable Spin code formatter with features like format on save. ### Future directions @@ -118,8 +119,8 @@ George (GitHub [DrMerfy](https://github.com/DrMerfy)) for the latest [VSCode-Ove ## License -Licensed under the MIT License.
-
+Licensed under the MIT License. + Follow these links for more information: ### [Copyright](copyright) | [License](LICENSE) diff --git a/Spin2-Settings.md b/Spin2-Settings.md index adb2304..713e238 100644 --- a/Spin2-Settings.md +++ b/Spin2-Settings.md @@ -9,29 +9,29 @@ In general To open the Settings editor, navigate to **[Code]** > **Settings** > The Spin2 extension settings are in 3 sections. If when you get to settings and type in "**SpinExt**" as a filter and you'll see the 3 sections of our new Spin2 Extension settings: -![Settings 1 of 3](./Spin2/DOCs/images/Spin2Ext-Settings1of3.png) +![Settings 1 of 3](./DOCs/Spin2Ext-Settings1of3.png) **FIGURE 1**: Our three sections, with the first section selected -- **Color Editor Background** - check this to turn on Propeller Tool coloring (you'll also need to adjust the color theme to make this readable!) -- **Editor Background Alpha** - if you wnat to something a bit darker then you can adjust this. -- **Highlight FlexSpin directives** - This adds support for #if, #else, etc. FlexSpin directives +- **Highlight FlexSpin directives** - This enables support for #if, #else, etc. FlexSpin directives - **Max Number of Reported Issues** - This allows you to limit how many messages are shown per file - **Trace Server**- boared? want to see how the client interacts with the server then select a value here other then off. (this can slow things down) +- **Color Editor Background** - check this to turn on Propeller Tool coloring (you'll also need to adjust the color theme to make this readable!) +- **Editor Background Alpha** - if you wnat to something a bit darker then you can adjust this. Click on the 2nd section to see: -![Settings 2 of 3](./Spin2/DOCs/images/Spin2Ext-Settings2of3.png) +![Settings 2 of 3](./DOCs/Spin2Ext-Settings2of3.png) **FIGURE 2**: with he 2nd section selected +- Elastic Tabstops **Enable** - check this to turn ON the Elastic Tabstops feature - **Iron Sheep** - click on link to adjust the tab columns (default is what the Author uses) - **Propeller Tool**- click on link to adjust the tab columns (default is Propeller Tool defaults) - **User1** - click on link to adjust the tab columns (this one is meant for you to customize if you wnat your own settings) - Elastic Tabstops **Choice** - select the set of tabstops you wish to use -- Elastic Tabstops **Enable** - check this to turn ON the Elastic Tabstops feature Click on the 3rd section to see: -![Settings 3 of 3](./Spin2/DOCs/images/Spin2Ext-Settings3of3.png) +![Settings 3 of 3](./DOCs/Spin2Ext-Settings3of3.png) **FIGURE 3**: with the 3rd section selected Insert Mode Adjustments: @@ -48,9 +48,8 @@ Insert Mode Adjustments: ## License -Copyright © 2023 Iron Sheep Productions, LLC.
-Licensed under the MIT License.
-
+Licensed under the MIT License. + Follow these links for more information: ### [Copyright](copyright) | [License](LICENSE) @@ -63,7 +62,7 @@ Follow these links for more information: [marketplace-rating]: https://vsmarketplacebadge.apphb.com/rating-short/ironsheepproductionsllc.spin2.svg -[license-shield]: https://camo.githubusercontent.com/bc04f96d911ea5f6e3b00e44fc0731ea74c8e1e9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f69616e74726963682f746578742d646976696465722d726f772e7376673f7374796c653d666f722d7468652d6261646765 +[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg [Release-shield]: https://img.shields.io/github/release/ironsheep/P2-vscode-extensions/all.svg diff --git a/TAB-SPECs.md b/TAB-SPECs.md index e05ad1a..afc1fb6 100644 --- a/TAB-SPECs.md +++ b/TAB-SPECs.md @@ -386,4 +386,5 @@ Follow these links for more information: [marketplace-rating]: https://vsmarketplacebadge.apphb.com/rating-short/ironsheepproductionsllc.spin2.svg -[license-shield]: https://camo.githubusercontent.com/bc04f96d911ea5f6e3b00e44fc0731ea74c8e1e9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f69616e74726963682f746578742d646976696465722d726f772e7376673f7374796c653d666f722d7468652d6261646765 +[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg + diff --git a/TAB-VisualEx.md b/TAB-VisualEx.md index 805c2cd..526af88 100644 --- a/TAB-VisualEx.md +++ b/TAB-VisualEx.md @@ -39,4 +39,5 @@ Follow these links for more information: [marketplace-rating]: https://vsmarketplacebadge.apphb.com/rating-short/ironsheepproductionsllc.spin2.svg -[license-shield]: https://camo.githubusercontent.com/bc04f96d911ea5f6e3b00e44fc0731ea74c8e1e9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f69616e74726963682f746578742d646976696465722d726f772e7376673f7374796c653d666f722d7468652d6261646765 +[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg + diff --git a/TASKS-User-RPi.md b/TASKS-User-RPi.md index e2293fc..fb6375b 100644 --- a/TASKS-User-RPi.md +++ b/TASKS-User-RPi.md @@ -2,6 +2,7 @@ ![Project Maintenance][maintenance-shield] + [![License][license-shield]](LICENSE) **NOTE**: This page describes creating tasks **common to all of your projects/workspaces**. If, instead, you wish to have your P2 compile and download tasks unique to each your projects then go to the [Project Tasks](TASKS.md) page. @@ -649,4 +650,5 @@ Follow these links for more information: [maintenance-shield]: https://img.shields.io/badge/maintainer-stephen%40ironsheep%2ebiz-blue.svg?style=for-the-badge -[license-shield]: https://camo.githubusercontent.com/bc04f96d911ea5f6e3b00e44fc0731ea74c8e1e9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f69616e74726963682f746578742d646976696465722d726f772e7376673f7374796c653d666f722d7468652d6261646765 +[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg + diff --git a/TASKS-User-macOS.md b/TASKS-User-macOS.md index f8e99e0..df92938 100644 --- a/TASKS-User-macOS.md +++ b/TASKS-User-macOS.md @@ -2,6 +2,7 @@ ![Project Maintenance][maintenance-shield] + [![License][license-shield]](LICENSE) **NOTE**: This page describes creating tasks **common to all of your projects/workspaces**. If, instead, you wish to have your P2 compile and download tasks unique to each your projects then go to the [Project Tasks](TASKS.md) page. @@ -573,8 +574,8 @@ And our **DownloadP2** task can reference the binary file using `${config:topLev ## License -Licensed under the MIT License.
-
+Licensed under the MIT License. + Follow these links for more information: ### [Copyright](copyright) | [License](LICENSE) @@ -583,4 +584,5 @@ Follow these links for more information: [maintenance-shield]: https://img.shields.io/badge/maintainer-stephen%40ironsheep%2ebiz-blue.svg?style=for-the-badge -[license-shield]: https://camo.githubusercontent.com/bc04f96d911ea5f6e3b00e44fc0731ea74c8e1e9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f69616e74726963682f746578742d646976696465722d726f772e7376673f7374796c653d666f722d7468652d6261646765 +[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg + diff --git a/TASKS-User-win.md b/TASKS-User-win.md index 2aecf72..560565d 100644 --- a/TASKS-User-win.md +++ b/TASKS-User-win.md @@ -1,6 +1,7 @@ # VSCode - User-wide defined Tasks (Windows) ![Project Maintenance][maintenance-shield] + [![License][license-shield]](LICENSE) **NOTE**: This page describes creating tasks **common to all of your projects/workspaces**. If, instead, you wish to have your P2 compile and download tasks unique to each your projects then go to the [Project Tasks](TASKS.md) page. @@ -696,4 +697,6 @@ Follow these links for more information: ### [Copyright](copyright) | [License](LICENSE) [maintenance-shield]: https://img.shields.io/badge/maintainer-stephen%40ironsheep%2ebiz-blue.svg?style=for-the-badge -[license-shield]: https://camo.githubusercontent.com/bc04f96d911ea5f6e3b00e44fc0731ea74c8e1e9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f69616e74726963682f746578742d646976696465722d726f772e7376673f7374796c653d666f722d7468652d6261646765 + +[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg + diff --git a/spin2/.vscode/settings.json b/spin2/.vscode/settings.json index d273752..b9dc208 100644 --- a/spin2/.vscode/settings.json +++ b/spin2/.vscode/settings.json @@ -18,5 +18,9 @@ "**/node_modules": true, "**/out": true }, - "explorerExclude.backup": {} +"explorerExclude.backup": {}, +"cSpell.enableFiletypes": [ + "!json", + "!typescript" +] } diff --git a/spin2/CHANGELOG.md b/spin2/CHANGELOG.md index 02c92f1..7e33abe 100644 --- a/spin2/CHANGELOG.md +++ b/spin2/CHANGELOG.md @@ -18,11 +18,21 @@ Possible next additions: - Add new-file templates as Snippets - Add additional Snippets as the community identifies them -## [2.2.15] 2024-01-11 +## [2.2.15] 2024-04-09 + +Update P1 and P2 + +- Adjust object hierarchy to fully expand the tree by default +- [-] collapse and [+] expand icons now work in object hierarchy tree view +- Adds new Object Hierarchy report similar to that generated during propeller tool "Archive". Report is available via keystroke [Ctrl+Alt+r] - Ctrl+Alt+( r )eport from within spin/spin2 source file. Update P2 Only -- Add support for v44 new built-in method names +- Add highlighting of object[index] expressions where index itself is an expression +- BUGFIX don't report `@instance[index].method` reference as bad constant use when it really is a method +- preliminary flexspin support changes: + - update conditional compile suppot to grey out deslected code + - add support for #import of .spin2 code ## [2.2.14] 2024-01-11 @@ -59,7 +69,7 @@ Update P2 Only - Add semantic highlight color change for byte(), word(), and long() method overrides - Add recognition of byte(), word(), and long() method names to provide method vs. storage type hover text - Add recognition of {Spin2_v##} format Spin Language Requirement directive -- Emit any languge directive when used to generated interface documentation +- Emit any language directive when used to generated interface documentation - Add support for lstring() when {Spin2_v43} is specified - Add detection of/error generation for duplicate declarations within CON, VAR and DAT sections diff --git a/spin2/EarlyTesting.md b/spin2/EarlyTesting.md index bbe2ea4..6eb64ea 100644 --- a/spin2/EarlyTesting.md +++ b/spin2/EarlyTesting.md @@ -1,6 +1,7 @@ # VSCode support for the Parallax Propeller 1 & 2 Multicore MCU's ![Project Maintenance][maintenance-shield] + [![License][license-shield]](LICENSE) ## Early Testing of the new Language-Server based VSCode Extension @@ -60,7 +61,7 @@ It should now be installed - Ensure [Error Lens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens) extension is installed -![Error Lens](./DOCs/images/ErrorLens-Ext.png) +![Error Lens](./images/ErrorLens-Ext.png) **FIGURE 1**: The Error Lens Extension When you DON'T have error Lens installed (or it is disabled) Your errors (highlighted in the code as bright RED text) only show in bottom area in the [Problems] Tab: @@ -182,9 +183,8 @@ The Spin2 extension provides **5 themes**. 2 for light mode and 3 for dark mode. ## License -Copyright © 2023 Iron Sheep Productions, LLC.
-Licensed under the MIT License.
-
+Licensed under the MIT License. + Follow these links for more information: ### [Copyright](copyright) | [License](LICENSE) @@ -197,7 +197,7 @@ Follow these links for more information: [marketplace-rating]: https://vsmarketplacebadge.apphb.com/rating-short/ironsheepproductionsllc.spin2.svg -[license-shield]: https://camo.githubusercontent.com/bc04f96d911ea5f6e3b00e44fc0731ea74c8e1e9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f69616e74726963682f746578742d646976696465722d726f772e7376673f7374796c653d666f722d7468652d6261646765 +[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg [Release-shield]: https://img.shields.io/github/release/ironsheep/P2-vscode-extensions/all.svg diff --git a/spin2/TEST_LANG_SERVER/spin2/240406-fixes.spin2 b/spin2/TEST_LANG_SERVER/spin2/240406-fixes.spin2 new file mode 100644 index 0000000..c03a21f --- /dev/null +++ b/spin2/TEST_LANG_SERVER/spin2/240406-fixes.spin2 @@ -0,0 +1,89 @@ +' ------------------------------- +CON + +VAR cog 'status + av_base_pin_ + +PUB stop() + +'' Stop VGA driver cog + + if cog + cogstop(cog-1) + pinclear(av_base_pin_ addpins 4) + +' ------------------------------- +DAT + +Mario0 byte { +}$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,{ +}$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,{ +}$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + +MarioColors long { +}%00000000_00000000_00000000_00000000,{ $7fff 'any bit 31..24 set means transparent +}%11111111_11111000_11111000_11111000,{ $7fff +}%11111111_11111000_11111000_11111000,{ $7fff +}%11111111_11111000_11111000_11111000,{ $7fff +}%11111111_11111000_11011000_10011000,{ $7f73 +}%11111111_11010000_00000000_00100000,{ $6804 12->32 +}%11111111_00100000_00100000_00100000 ' $1084 13->33 'address=51 + +' ------------------------------- +CON +{ +To instantiate a display type: + --->>> inline comments shouldn't end block comment!! +DEBUG("`DISPLAY_TYPE name {configurations and values}") + +To feed a display {or more than one display}: + +DEBUG("`name {name name ...} value value value keyword value etc") + +} + +' ------------------------------- + +CON +offx = 100 +offy = 1000 +spacex = 16 +spacey = 16 +dims = 8 +dotsize = 8 +winx = 6 + dims * dotsize +winy = 29 + dims * dotsize + +pub go() | i, k + + repeat i from 0 to 15 + k := long[@pixels + $36][i] + long[@pixels + $36][i] := k >> 16 & $0000FF | k << 16 & $FF0000 | k & $00FF00 + + repeat i from 0 to 15 + debug(`bitmap b`(i) pos `(offx + spacex + i & %11 * (winx + spacex), offy + spacey + i >> 2 & %11 * (winy + spacey)) rate 1 title '`(i)' size `(dims,dims) dotsize `(dotsize) lut4 trace `(i)) + waitms(1000) + repeat i from 0 to 15 + debug(`b`(i) lutcolors `uhex_long_array_(@pixels+$36,16)) + k~ + repeat + i := byte[@pixels + $76][k >> 1 & $3F] >> (!k & 1 * 4) & $F + debug(`b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b15 `(i)) + 'debug(`b0 `(i)) + if k >= 63 and k < 64 * 3 and (k & $00 == $0) + debug(`b0 save `(offx, offy, cntx * (winx + spacex) + spacex, cnty * (winy + spacey) + spacey) 'TRACE`(k-63)') + k++ + 'waitms(25) + +DAT +pixels BYTE 0,0,0 ' file "bitmapdemo.bmp" +' ------------------------------- + +pub main() + 'Configure display + debug(`term message pos 200 500 size 12 4 textsize 40 rgbi8x) ' <-- WHY RED?? fix this + + '' FOLLOWING LINE CRASHES!!! + 'debug(`message 'Hello World!') 'Display "Hello World!" on computer screen + +' ------------------------------- diff --git a/spin2/TEST_LANG_SERVER/spin2/comment_cert.spin2 b/spin2/TEST_LANG_SERVER/spin2/comment_cert.spin2 index bc5a34f..0a24629 100644 --- a/spin2/TEST_LANG_SERVER/spin2/comment_cert.spin2 +++ b/spin2/TEST_LANG_SERVER/spin2/comment_cert.spin2 @@ -1,7 +1,22 @@ + +obj {{ +    +     ┌───── ┌─────┐ ┌───── +    │ ┌─── │ ┌─┐ │ │ ┌─── +    │ │ │ │ │ │ │ │ ┐ ┐ +    │ └─── │ └─┘ │ │ └─┘ │ +    └───── └─────┘ └─────┘ V1.2.1 + + The sound of the Super Nintendo in a cog! + +}} + con { fixed io pins } LIB_VERSION = 140 ' 1.4.0 + FILENAME_SIZE = 40 + SF_CS = 61 { O } ' flash chip select SF_SCLK = 60 { O } ' flash clock SF_MOSI = 59 { O } ' flash data in diff --git a/spin2/TEST_LANG_SERVER/spin2/neoyume_lower.spin2 b/spin2/TEST_LANG_SERVER/spin2/neoyume_lower.spin2 index 6084472..080566e 100644 --- a/spin2/TEST_LANG_SERVER/spin2/neoyume_lower.spin2 +++ b/spin2/TEST_LANG_SERVER/spin2/neoyume_lower.spin2 @@ -61,8 +61,11 @@ _lspc_animctr byte 0 orgh $60 ' control bit format is %ET_DCBA_RLDU (E=sElect,T=sTart) -io_pl1_padw word 0 ' < $60 (populated by USB code) -io_pl2_padw word 0 ' < $62 (populated by USB code) +io_pl1_padw long 0 ' < $60 (populated by USB code) +io_pl2_padw long 0 ' < $64 (populated by USB code) + + long 0 ' < $68 stupid hack to pass down pointer from USB code + long 0 ' < $6C orgh $80 ' Stuff set by upcode @@ -309,15 +312,27 @@ mk_rdimm mk_wrportbank mov mk_memtmp3,mk_effaddr zerox mk_memtmp3,#19 - cmp mk_memtmp3,##$FFFF0 wc - 'debug("Bankswitch write: ",uhex_long(mk_memvalue,mk_effaddr,mk_virtualpc)) + cmp mk_memtmp3,##$FFFF0 wcz setq #1 rdlong mk_memtmp0,#_exr_port_base + tjs mk_memtmp1,#.pvc + 'debug("Bankswitch write: ",uhex_long(mk_memvalue,mk_effaddr,mk_virtualpc)) shl mk_memvalue,#20 ' 1 MB steps and mk_memtmp1,mk_memvalue add mk_memtmp0,mk_memtmp1 if_ae mov mk_port_base,mk_memtmp0 ret wcz +.pvc + 'debug("PVC write ",uhex_long(mk_memtmp3,mk_memvalue)) + if_e setword mk_pvc_bankreg,mk_memvalue,#0 + cmp mk_memtmp3,##$FFFF2 wcz + if_e setword mk_pvc_bankreg,mk_memvalue,#1 + mov mk_memvalue,mk_pvc_bankreg + shr mk_memvalue,#8 + zerox mk_memvalue,mk_memtmp1 ' limit range + add mk_memtmp0,mk_memvalue + mov mk_port_base,mk_memtmp0 + ret wcz mk_longio_tmp long 0 @@ -548,7 +563,7 @@ mk_nibble_0 ' Immediate ops (decode headache!) mov pb,mk_opword if_z call #mk_setup_reg32 if_nz and mk_optmp1,#7 - if_nz call #mk_setup_operand8 + if_nz call #mk_setup_operand8_move call mk_readf 'debug("doing bitop with ",uhex_byte(mk_optmp1),uhex_long(mk_memvalue)) testbn mk_memvalue,mk_optmp1 wc @@ -570,6 +585,18 @@ mk_cmp push #mk_cmp_common jmp #mk_get_second_reg +mk_cmpa + testb mk_opword,#8 wc ' long sized? + if_nc callpb mk_opword,#mk_setup_operand16_move + if_c callpb mk_opword,#mk_setup_operand32_move + call mk_readf + if_nc signx mk_memvalue,#15 + + shr mk_opword,#9 + and mk_opword,#7 + skipf #%11_111_00 ' Skip swap and shift + alts mk_opword,#mk_a0 + mov mk_optmp1,0-0 mk_cmp_swapped xor mk_memvalue,mk_optmp1 xor mk_optmp1,mk_memvalue @@ -592,22 +619,6 @@ mk_cmp_common _ret_ bitc mk_sr,#MK_NEG_BIT -mk_cmpa - testb mk_opword,#8 wc ' long sized? - if_nc callpb mk_opword,#mk_setup_operand16 - if_c callpb mk_opword,#mk_setup_operand32 - call mk_readf - if_nc signx mk_memvalue,#15 - mov pb,mk_opword - shr pb,#9 - and pb,#7 - alts pb,#mk_a0 - mov mk_optmp1,0-0 - mov mk_shiftit,#0 - jmp #mk_cmp_common - - - mk_nibble_9 mk_nibble_D @@ -674,8 +685,8 @@ mk_addsuba setq mk_dreg_mask muxq .adda_op,mk_opword testb mk_opword,#8 wc ' long sized? - if_nc callpb mk_opword,#mk_setup_operand16 - if_c callpb mk_opword,#mk_setup_operand32 + if_nc callpb mk_opword,#mk_setup_operand16_move + if_c callpb mk_opword,#mk_setup_operand32_move call mk_readf 'debug("in mk_adda: ",uhex_long(mk_memvalue)) if_nc signx mk_memvalue,#15 @@ -776,7 +787,7 @@ mk_thelogicop and mk_memvalue,mk_optmp1 wz mk_getop_ram mov mk_memtmp0,mk_virtualpc add mk_virtualpc,#2 - setword mk_memtmp0,#1,#1 ' main_ram! + setword mk_memtmp0,#@main_ram>>16,#1 rdword mk_opword,mk_memtmp0 _ret_ movbyts mk_opword,#%%3201 @@ -798,6 +809,7 @@ mk_dreg_mask long %111_000_000_000 mk_vectors_mask long $FF_FF80 mk_bit31 long 1<<31 +mk_minus4 long -4 mk_cogvar_start @@ -813,6 +825,7 @@ mk_vectors_ptr res 1 mk_prog_base res 1 mk_port_base res 1 mk_bios_base res 1 +mk_pvc_bankreg res 1 mk_virtualpc res 1 ' PC in virtual space mk_effaddr res 1 ' calculated effective address @@ -939,7 +952,7 @@ mk_nibble_5_not_addsub testb mk_opword,#3 andz if_z jmp #mk_dbcc ' got SCC - callpb mk_opword,#mk_setup_operand8 + callpb mk_opword,#mk_setup_operand8_move call mk_readf ' <- yes, this is stupid but accurate getnib pb,mk_opword,#2 shr pb,#1 wc @@ -951,22 +964,22 @@ mk_nibble_5_not_addsub mk_romque_refill mov mk_romqueio_target,mk_romque_area_ptr - mov mk_romqueio_length,#MK_ROMQUE_SIZE/2 - mov mk_romque_left,#MK_ROMQUE_SIZE wcz ' clear C and Z + mov mk_romqueio_length,#MK_ROMQUE_SIZE/2 wcz ' clear C and Z mov mk_romqueio_addr,mk_virtualpc - zerox mk_romqueio_addr,mk_romque_zerox' Limit to 1 MB range + zerox mk_romqueio_addr,mk_romque_zerox ' Limit to 1 MB range add mk_romqueio_addr,mk_romque_base ' Handle not long-aligned access rczr mk_romqueio_addr wcz - if_z jmp #mk_address_error_debug ' TODO: maybe make AE function code read program in this case? + 'if_z jmp #mk_address_error_debug ' TODO: maybe make AE function code read program in this case? if_c sub mk_romqueio_target,#2 if_c add mk_romqueio_length,#1 setq #2 wrlong mk_romqueio_addr,#_progrq_addr cogatn mk_romio_atnval mov ptra,mk_romque_area_ptr - waitatn - jmp #mk_getop_rom + mov mk_romque_left,#MK_ROMQUE_SIZE + push #mk_getop_rom + _ret_ waitatn mk_compute_ea ' CAN AND WILL OVERWRITE FLAGS @@ -1016,7 +1029,7 @@ mk_nibble_E ' shifts .memshift mov pb,mk_opword - call #mk_setup_operand16 + call #mk_setup_operand16_move mov mk_shiftit,#15 mov mk_optmp1,#1 shr mk_opword,#8 ' get shift mode bits into place @@ -1098,21 +1111,6 @@ mk_do_roxl ret - - -mk_readrom_ea32 - mov mk_romio_length,#2 - skipf #%10 -mk_readrom_ea - mov pa,mk_effaddr - mov mk_romio_length,#1 - rczr pa wcz - mov mk_romio_target,mk_romio_area_ptr - if_c sub mk_romio_target,#2 - if_z sub mk_romio_target,#1 - - jmp #$ - mk_setup_nothing8 mk_setup_nothing16 mk_setup_nothing32 @@ -1302,14 +1300,6 @@ mk_nibble_4 ' AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA altd mk_optmp0,#mk_flowop_impl_tbl jmp 0-0 -.jmp_jsr - call #mk_compute_ea - mov mk_branchdisplace,mk_effaddr - sub mk_branchdisplace,mk_virtualpc - testb mk_opword,#6 wc ' if NC, got JSR - if_c jmp #mk_dobranch - jmp #mk_call - .swap and mk_opword,#15 @@ -1336,8 +1326,8 @@ mk_nibble_4 ' AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA test mk_opword,#%110_000 wz if_nz jmp #mk_hub_movem testb mk_opword,#6 wc - if_nc call #mk_setup_operand16 - if_c call #mk_setup_operand32 + if_nc call #mk_setup_operand16_move + if_c call #mk_setup_operand32_move call mk_readf if_nc signx mk_memvalue,#7 wcz if_c signx mk_memvalue,#15 wcz @@ -1378,6 +1368,22 @@ mk_nibble_4 ' AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA jmp mk_writef +.jmp_jsr + call #mk_compute_ea + mov mk_branchdisplace,mk_effaddr + sub mk_branchdisplace,mk_virtualpc + testb mk_opword,#6 wc ' if NC, got JSR + if_c jmp #mk_dobranch + ' fall through +mk_call + ' Push return address on stack + sub mk_a7,#4 + mov mk_effaddr,mk_a7 + call #mk_setup_ea32 + mov mk_memvalue,mk_virtualpc + call mk_writef + 'debug("SP after call: ",uhex_long(mk_a7),uhex_long(mk_virtualpc),shex_long(mk_branchdisplace)) + jmp #mk_dobranch @@ -1437,8 +1443,9 @@ mk_dbcc ' DBcc sub mk_optmp0,#1 wc altd mk_opword,#mk_d0 setword 0-0,mk_optmp0,#0 - if_nc jmp #mk_dobranch - ret + cmp mk_branchdisplace,mk_minus4 wz + if_z waitx #100 ' Slow down delay loops - fix Z80 race condition in pulstar/blazstar + _ret_ tjnf mk_optmp0,#mk_dobranch mk_get_second_reg mov pb,mk_opword @@ -1473,16 +1480,6 @@ mk_getopimm32 -mk_call - ' Push return address on stack - sub mk_a7,#4 - mov mk_effaddr,mk_a7 - call #mk_setup_ea32 - mov mk_memvalue,mk_virtualpc - call mk_writef - 'debug("SP after call: ",uhex_long(mk_a7),uhex_long(mk_virtualpc),shex_long(mk_branchdisplace)) - jmp #mk_dobranch - mk_nibble_6 ' branches ' compute destination mov mk_optmp1,mk_opword @@ -1519,11 +1516,10 @@ mk_dobranch 'jmp #$ .not_a_sussy_baka '} 'debug("branching ",shex(mk_branchdisplace)," to ",uhex_long(mk_virtualpc)) - testb mk_virtualpc,#0 wc + sar mk_branchdisplace,#1 wc if_c jmp #mk_address_error_debug ' TODO: maybe make AE function code read program in this case? tjz ptra,#.notfast ' if que not active, this isn't relevant ' see if fast branch within que is ok - sar mk_branchdisplace,#1 sub mk_romque_left,mk_branchdisplace cmp mk_romque_left,#MK_ROMQUE_SIZE+1 wc ' see below 'debug("would be ",sdec(mk_romque_left)) @@ -1689,8 +1685,7 @@ mk_ea_headache mov mk_eatmp1,mk_opword ' backup call mk_getopf 'debug("in mk_ea_headache ",uhex_word(mk_opword)) - testb pb,#0 wz - testb pb,#1 wc + rczr pb wcz if_01 jmp #.longabs if_11 jmp #.index signx mk_opword,#15 ' absolute short or PC displacement @@ -1853,7 +1848,6 @@ mk_address_error ' can basically only happen due to misalignment? mov mk_optmp1,mk_vectors_ptr add mk_optmp1,#$0C rdlong mk_branchdisplace,mk_optmp1 - sub mk_branchdisplace,mk_virtualpc mov mk_optmp1,mk_effaddr ' save for later ' set supervisor mode, unset trace mode mov mk_optmp2,mk_sr @@ -1894,6 +1888,8 @@ mk_address_error ' can basically only happen due to misalignment? testb mk_optmp2,#MK_SUPER_BIT wc bitc mk_memvalue,#2 ' we do know what mode we were in though call mk_writef + mov ptra,#0 ' force reset ROM que + mov mk_virtualpc, #0 ' force reset PC to avoid double fault jmp #mk_dobranch @@ -1954,8 +1950,8 @@ mk_hub_rte add mk_a7,#4 'debug("RTE from ",uhex_long_(mk_virtualpc)," to ",uhex_long(mk_memvalue)) mov mk_memvalue,mk_optmp2 ' only write SR after we're done popping - call #mk_wr_sr - jmp #mk_dobranch + push ##mk_dobranch + jmp #mk_wr_sr mk_hub_rtr mov mk_effaddr,mk_a7 @@ -2231,7 +2227,7 @@ mk_hub_movep ' MOVEP mk_hub_mul ' MULU/MULS mov pb,mk_opword - call #mk_setup_operand16 + call #mk_setup_operand16_move call mk_readf mov pb,mk_opword and pb,mk_dreg_mask @@ -2248,7 +2244,7 @@ mk_hub_mul ' MULU/MULS mk_hub_div ' DIVU/DIVS mov pb,mk_opword - call #mk_setup_operand16 + call #mk_setup_operand16_move call mk_readf ' get divider mov pb,mk_opword shr pb,#9 @@ -2256,26 +2252,24 @@ mk_hub_div ' DIVU/DIVS alts pb,#mk_d0 mov mk_optmp0,0-0 ' get divisor tjz mk_memvalue,#.by_zero - mov mk_optmp1,#0 ' bit 0 holds appropriate result sign testb mk_opword,#8 wz ' set z if signed if_z signx mk_memvalue,#15 if_z abs mk_memvalue wc - if_z_and_c bitnot mk_optmp1,#0 + if_z wrc mk_optmp1 ' bit 0 holds appropriate result sign if_z abs mk_optmp0 wc - if_z_and_c bitnot mk_optmp1,#0 qdiv mk_optmp0,mk_memvalue + if_z_and_c bitnot mk_optmp1,#0 bitl mk_sr,#MK_CARRY_BIT getqx mk_memvalue ' quotient getqy mk_optmp0 ' remainder ' Apply sign - testb mk_optmp1,#0 wc - negc mk_memvalue - negc mk_optmp0 + if_z testb mk_optmp1,#0 wc + if_z negc mk_memvalue + if_z negc mk_optmp0 testb mk_memvalue,#15 wc bitc mk_sr,#MK_NEG_BIT ' Check overflow mov mk_optmp1,mk_memvalue - testb mk_opword,#8 wz ' set z if signed if_z signx mk_memvalue,#15 if_nz zerox mk_memvalue,#15 cmp mk_optmp1,mk_memvalue wz @@ -2305,7 +2299,7 @@ mk_hub_div ' DIVU/DIVS mk_hub_srmove - call #mk_setup_operand16 + call #mk_setup_operand16_move testb mk_opword,#10 wc ' FROM (NC) or TO (C) SR if_nc push mk_writef if_nc jmp #mk_rd_sr @@ -2503,7 +2497,7 @@ mk_hub_sbcd mk_hub_nbcd 'debug ("in NBCD with ",uhex(mk_opword)) - call #mk_setup_operand8 ' opword still in PB! + call #mk_setup_operand8_move ' opword still in PB! call mk_readf ' See: https://github.com/flamewing/68k-bcd-verifier/blob/master/bcd-emul.cc#L104 ' compute dd @@ -2571,7 +2565,7 @@ mk_hub_addsubx testb mk_sr,#MK_EXT_BIT wc ' must be rcl for correct ripple carry rcl mk_memvalue,mk_shiftit - rcl mk_optmp1,mk_shiftit + shl mk_optmp1,mk_shiftit ' generate stupid overflow flag mov mk_optmp0,mk_optmp1 addsx mk_optmp0,mk_memvalue wc @@ -2669,7 +2663,7 @@ mk_hub_cmpm jmp mk_readf mk_hub_chk - call #mk_setup_operand16 ' pb already loaded + call #mk_setup_operand16_move ' pb already loaded call mk_readf call #mk_get_second_reg andn mk_sr,#MK_OVER_MASK|MK_CARRY_MASK ' Undocumented flags @@ -2711,61 +2705,6 @@ mk_init rdword mk_romio_atnval,#_ma_cogatn_w - { - ' Compute timing constant for H counter emulation - ' (note that "pixel" here means hcounter values, which are actually 2 pixels) - rdlong pa,#_clkf - qmul pa,##round(6.36e-5/211.0*4294967296.0) ' cy/s * s/px -> cy/px - getqy pa ' cycles per pixel - getqx pb ' fractional part - rolword pa,pb,#1 - qfrac #1,pa - getqx pa ' our timing reciporal - wrword pa,#vdp_cyc2hcnt_w - debug("H counter timing constant: ",udec_word_array(#vdp_cyc2hcnt_w,#1)) - ' Compute timing constant for 6-button state machine reset - rdlong pa,#_clkf - qdiv pa,##666 ' 1.5 ms - unfortunate reciporal - getqx pa - wrlong pa,#io_6btn_timeout - ' Reset I/O ports (so it seems like we're cold-booting) - wrbyte #0,#io_pl1_dir - wrbyte #0,#io_pl2_dir - wrbyte #0,#io_pl1_out - wrbyte #0,#io_pl2_out - } - - ' allocate all the locks like some sort of maniac - { - mov pa,#0 - rep @.lockmadness,#16 - locknew pb wc - if_nc bith pa,pb -.lockmadness - ' deallocate all the ones we don't want - andn pa,##STATIC_LOCKS - mov pb,#0 - rep @.lockmadness2,#16 - shr pa,#1 wc - if_c lockret pb - add pb,#1 -.lockmadness2} - -{ - rdbyte mk_romio_zerox,#rom_zerox - sub mk_romio_zerox,#2 ' we apply it to long-unit addresses - rdlong mk_sram_begin,#sram_map_start - rdlong mk_sram_stop,#sram_map_end - ' Reset ROM/SRAM mapping - rdbyte mk_memvalue,#sram_bankreg - call #sram_set_bank -} - - ' setup interrupts - 'setse1 #$10+HINT_LOCK - 'setse2 #$10+VINT_LOCK - 'drvl #38 addpins 1 ' DEBUG LEDs - rdlong mk_prog_base,#_exr_prog_base rdlong mk_port_base,#_exr_port_base rdlong mk_bios_base,#_exr_bios_base @@ -2904,6 +2843,8 @@ mk_check_core_integrity CON ' Memory arbiter constants +#define USE_PSRAM16 + #ifdef USE_PSRAM16 MA_CHAR_ASHIFT = 1 MA_CHAR_CYCLES = 4 @@ -2934,16 +2875,32 @@ MA_PAGESIZE = 0 addbits 7 MA_BANKSHIFT = 23 #endif +#ifdef USE_HYPER +MA_CHAR_ASHIFT = 0 ' special access code +MA_CHAR_CYCLES = 8 +MA_PCM_ASHIFT = 0 ' special access code +MA_PCM_CYCLES = 16 +MA_PROGCYSHL = 2 +MA_PAGESIZE = 0 addbits 15 ' Technically only bounds at 8MB? +MA_BANKSHIFT = 23 +#endif + +#ifdef USE_PSRAM_EITHER #ifdef USE_PSRAM_SLOW MA_CLKDIV = 3 MA_CYMUL = 1 +#define MA_SYSCLK3 #else MA_CLKDIV = 2 MA_CYMUL = 2 #endif +#elseifdef USE_HYPER +MA_CLKDIV = 2 +MA_CYMUL = 1 +#endif DAT ' Memory arbiter (PSRAM) -#ifdef USE_PSRAM_EITHER + org 0 ma_entry cogid pa @@ -2956,9 +2913,11 @@ ma_entry rdlong 0,##@ma_lutcode rdlong ma_char_base,#_exr_char_base - shr ma_char_base,#2 +#ifdef USE_PSRAM_EITHER + shr ma_char_base,#2 + fltl #PSRAM_CLK #ifdef USE_PSRAM_SLOW wrpin ##P_PULSE|P_OE|(PSRAM_SYNC_CLOCK ? P_SYNC_IO : P_ASYNC_IO), #PSRAM_CLK @@ -2973,6 +2932,21 @@ ma_entry drvl #PSRAM_SELECT addpins (PSRAM_BANKS-1) wrpin ##(PSRAM_SYNC_DATA ? P_SYNC_IO : P_ASYNC_IO),ma_psram_pinfield +#elseifdef USE_HYPER + shr ma_char_base,#3 + + fltl #HYPER_CLK + wrpin ##P_TRANSITION|P_OE|(PSRAM_SYNC_CLOCK ? P_SYNC_IO : P_ASYNC_IO), #HYPER_CLK + wxpin #2, #HYPER_CLK + + wrpin ##P_INVERT_OUTPUT,#HYPER_SELECT addpins (HYPER_BANKS-1) + drvl #HYPER_SELECT addpins (HYPER_BANKS-1) + + wrpin ##(HYPER_SYNC_DATA ? P_SYNC_IO : P_ASYNC_IO),#HYPER_BASE addpins 7 + + modc HYPER_RESET < 0 ? _clr : _set wc + if_c drvh #HYPER_RESET&511 +#endif setxfrq ma_nco_slow waitx #200 @@ -3010,12 +2984,16 @@ ma_lineloop testb ma_curline,#1 wc if_c add ptrb,##96*4*4*2 -.slotlp - rdlong ma_mtmp1,ptrb[2] wc + rdlong ma_mtmp1,ptrb[2] wc ' Read first address if_c jmp #ma_lineloop ' got sentinel - +#ifdef USE_PSRAM_EITHER shl ma_mtmp1,#MA_CHAR_ASHIFT ' sprite lines are 2 longs +#endif add ma_mtmp1,ma_char_base +.slotlp + +#ifdef USE_PSRAM_EITHER + #ifndef USE_PSRAM_NOBANKS mov ma_mtmp3,ma_mtmp1 shr ma_mtmp3,#MA_BANKSHIFT @@ -3047,15 +3025,46 @@ ma_lineloop fltl ma_psram_pinfield setq ma_nco_slow xcont ma_psram_readspr_cmd,#0 + rdlong ma_mtmp1,ptrb[2+4] wc ' Read address for next slot ( C = got sentinel ) + add ptrb,#4*4 + shl ma_mtmp1,#MA_CHAR_ASHIFT ' sprite lines are 2 longs + add ma_mtmp1,ma_char_base waitxfi drvl #PSRAM_SELECT addpins (PSRAM_BANKS-1) #ifdef USE_PSRAM_SLOW fltl #PSRAM_CLK #endif .irqshield - add ptrb,#4*4 - djnz ma_slotleft,#.slotlp - 'debug("canary alive. Lorem ipsum dolor sit amet. Take it easy!") +#elseifdef USE_HYPER + add ma_mtmp1,ma_char_base + shr ma_mtmp1,#1 wc + wrc ma_mtmp2 + shl ma_mtmp2,#10 + setbyte ma_mtmp1,#%101_00000,#3 ' read linear burst + movbyts ma_mtmp1, #%%0123 + rep @.irqshield,#1 + drvh #HYPER_SELECT + drvl #HYPER_BASE addpins 7 + drvl #HYPER_CLK ' Init clock pin with correct (?) alignment + xinit ma_hyper_addr_cmd1,ma_mtmp1 + wypin #(6+HYPER_WAIT+MA_CHAR_CYCLES)*MA_CYMUL,#HYPER_CLK ' setup clock periods + xcont ma_hyper_addr_cmd2,ma_mtmp2 + setq ma_nco_fast + xcont #HYPER_WAIT*MA_CLKDIV+HYPER_DELAY,#0 + wrfast ma_bit31,ptrb + waitxmt + fltl #HYPER_BASE addpins 7 + setq ma_nco_slow + xcont ma_hyper_readspr_cmd,#0 + rdlong ma_mtmp1,ptrb[2+4] wc ' Read address for next slot ( C = got sentinel ) + add ptrb,#4*4 + add ma_mtmp1,ma_char_base + waitxfi + drvl #HYPER_SELECT addpins (HYPER_BANKS-1) + fltl #HYPER_CLK +.irqshield +#endif + if_nc djnz ma_slotleft,#.slotlp ' fall through if sentinel got jmp #ma_lineloop @@ -3064,6 +3073,7 @@ ma_do_adpcm 'drvh #38 +#ifdef USE_PSRAM_EITHER shl ma_mtmp1,#MA_PCM_ASHIFT ' ADPCM cache lines are 4 longs #ifndef USE_PSRAM_NOBANKS mov ma_mtmp3,ma_mtmp1 @@ -3108,6 +3118,35 @@ ma_do_adpcm fltl #PSRAM_CLK #endif .irqshield +#elseifdef USE_HYPER + ' 16 byte ADPCM blocks line up with HyperRAM half-pages + setbyte ma_mtmp1,#%101_00000,#3 ' read linear burst + movbyts ma_mtmp1, #%%0123 + rep @.irqshield,#1 + drvh #HYPER_SELECT + drvl #HYPER_BASE addpins 7 + drvl #HYPER_CLK ' Init clock pin with correct (?) alignment + xinit ma_hyper_addr_cmd1,ma_mtmp1 + wypin #(6+HYPER_WAIT+MA_PCM_CYCLES)*MA_CYMUL,#HYPER_CLK ' setup clock periods + xcont ma_hyper_addr_cmd2,#0 ' no intra-page address needed + setq ma_nco_fast + xcont #HYPER_WAIT*MA_CLKDIV+HYPER_DELAY,#0 + waitxmt + fltl #HYPER_BASE addpins 7 + mov ma_mtmp3,ma_adpcm_pollptr + sub ma_mtmp3,#@adpcm_pollbox + shl ma_mtmp3,#3 ' 4*8 -> 32 bytes per channel + testb ma_mtmp1,#(MA_PCM_ASHIFT^24) wc ' odd blocks go in odd buffers (bit 0 of original poll value) + if_c add ma_mtmp3,#16 + add ma_mtmp3,ma_adpcm_bufferbase + wrfast ma_bit31,ma_mtmp3 + setq ma_nco_slow + xcont ma_hyper_readpcm_cmd,#0 + waitxfi + drvl #HYPER_SELECT addpins (HYPER_BANKS-1) + fltl #HYPER_CLK +.irqshield +#endif _ret_ wrlong #0,ma_adpcm_pollptr @@ -3133,6 +3172,7 @@ ma_68krequest cogatn ma_mk_cogatn_val reti2 +#ifdef USE_PSRAM_EITHER ma_psram_read68k mov ma_itmp0,#(8+PSRAM_WAIT)*MA_CYMUL shl ma_prog_length,#MA_PROGCYSHL + (encod MA_CYMUL) @@ -3182,15 +3222,44 @@ ma_psram_read68k #else _ret_ drvl #PSRAM_SELECT addpins (PSRAM_BANKS-1) #endif - +#elseifdef USE_HYPER +ma_psram_read68k + mov ma_itmp0,#(6+HYPER_WAIT)*MA_CYMUL + shl ma_prog_length,#MA_PROGCYSHL + (encod MA_CYMUL) + add ma_itmp0,ma_prog_length + ' Prepare command + rczr ma_prog_addr wcz + rczl ma_itmp3 + and ma_itmp3,#%11 + shl ma_itmp3,#9 + setbyte ma_prog_addr,#%101_00000,#3 ' read linear burst + movbyts ma_prog_addr, #%%0123 + drvh #HYPER_SELECT + drvl #HYPER_BASE addpins 7 + drvl #HYPER_CLK ' Init clock pin with correct (?) alignment + xinit ma_hyper_addr_cmd1,ma_prog_addr + wypin ma_itmp0,#HYPER_CLK ' setup clock periods + xcont ma_hyper_addr_cmd2,ma_itmp3 + setq ma_nco_fast + xcont #HYPER_WAIT*MA_CLKDIV+HYPER_DELAY,#0 + setword ma_hyper_read68k_cmd,ma_prog_length,#0 + waitxmt + fltl #HYPER_BASE addpins 7 + setq ma_nco_slow + xcont ma_hyper_read68k_cmd,#0 + waitxfi + drvl #HYPER_SELECT addpins (HYPER_BANKS-1) + fltl #HYPER_CLK + ret wcz +#endif ma_bit31 'alias ma_nco_fast long $8000_0000 -#ifdef USE_PSRAM_SLOW -ma_nco_slow long $2AAAAAAB +#ifdef MA_SYSCLK3 +ma_nco_slow long $2AAA_AAAB #else ma_nco_slow long $4000_0000 #endif @@ -3219,6 +3288,14 @@ ma_psram_readspr_cmd long (PSRAM_BASE<<17)|X_WRITE_ON| X_4P_4DAC1_WFBYTE + MA_CH ma_psram_readpcm_cmd long (PSRAM_BASE<<17)|X_WRITE_ON| X_4P_4DAC1_WFBYTE + MA_PCM_CYCLES #endif +#ifdef USE_HYPER +ma_hyper_addr_cmd1 long (HYPER_BASE<<17)|X_PINS_ON | X_IMM_4X8_1DAC8 + 4 +ma_hyper_addr_cmd2 long (HYPER_BASE<<17)|X_PINS_ON | X_IMM_4X8_1DAC8 + 2 +ma_hyper_read68k_cmd long (HYPER_BASE<<17)|X_WRITE_ON| X_8P_1DAC8_WFBYTE +ma_hyper_readspr_cmd long (HYPER_BASE<<17)|X_WRITE_ON| X_8P_1DAC8_WFBYTE + MA_CHAR_CYCLES +ma_hyper_readpcm_cmd long (HYPER_BASE<<17)|X_WRITE_ON| X_8P_1DAC8_WFBYTE + MA_PCM_CYCLES +#endif + ma_adpcm_pollptr long @adpcm_pollbox ma_adpcm_bufferbase long @adpcm_buffers @@ -3285,8 +3362,6 @@ ma_lutcode fit 1024 -#endif - DAT ' I/O glue orgh @@ -3302,7 +3377,7 @@ iog_z80port_wr8 iog_status_a_rd16 rdbyte mk_memvalue,#z80_reply_b - rolbyte mk_memvalue,#$1F,#0 ' Does a real AES _have_ a STATUS_A, anyways? + rolbyte mk_memvalue,#$07,#0 ' Coin 3/4 being always low is a weird way of detecting AES hardware used by unibios. ret wcz iog_status_b_rd16 @@ -3552,6 +3627,7 @@ lspc_entry if_be wrbyte lspc_tmp1,ptra++ incmod lspc_tmp1,#255 wc if_nc jmp #.inner + incmod lspc_vshrink,#255 wc if_nc jmp #.outer } @@ -3599,7 +3675,7 @@ lspc_linelp if_11 incmod lspc_autoframe,#7 if_11 wrbyte lspc_autoframe,#_lspc_animctr - cmp lspc_chkline,#224 wz + cmp lspc_chkline,#224+1 wz ' +1 to avoid glitches on last line if_z locktry #VINT_LOCK wc if_z lockrel #VINT_LOCK 'if_11 drvnot #38 @@ -5198,24 +5274,6 @@ zk_lutbase long zk_immmath + (%11_00_00_0000_0011_0_10_00_0<<10) ' $FE: CP imm8 long zk_rst ' $FF: RST 38h - ' PSRAM helper table. Must be at $300 - long $0000 - long $1111 - long $2222 - long $3333 - long $4444 - long $5555 - long $6666 - long $7777 - long $8888 - long $9999 - long $AAAA - long $BBBB - long $CCCC - long $DDDD - long $EEEE - long $FFFF - zk_rolla mov zk_tmp8,zk_accu zk_shiftop @@ -5604,7 +5662,7 @@ zk_doreset CON ' OPNBCog constants - VOLUME_BALANCE = 0.8 ' Balance between FM/ADPCM and SSG. 1.0 is all OPN2, 0.0 is all SSG + VOLUME_BALANCE = 0.8 ' Balance between FM/ADPCM and SSG. 1.0 is all FM/ADPCM, 0.0 is all SSG OVERDRIVE = 1.0 @@ -5649,7 +5707,7 @@ CON ' OPNBCog constants OP_SIZE = 12 ' Size of an operator CH_OFFSET = 24 ' Offset from global registers to channels == Size of the global registers CH_SIZE = OP_OFFSET + 4*OP_SIZE ' Total size of a channel - OPN_SIZE = 4*CH_SIZE + CH_OFFSET ' Total OPN2 register count + OPN_SIZE = 4*CH_SIZE + CH_OFFSET ' Total OPNB register count SSG_SIZE = 16 @@ -5765,7 +5823,7 @@ EG_RATE_63 = round(8.0*EG_FACTOR) DAT ' OPNBCog init code orgh '=========================================================== -' Assembly OPN2 emulator +' Assembly OPNB emulator '=========================================================== opn_init 'debug (udec(leftp,rightp),uhex_long(sampleRate)) @@ -6161,9 +6219,8 @@ opn_muteChannels long 0'%11_11 opn_leftp long 0 opn_rightp long 0 - 'long 0[10] ' The elder gods ask for this to be here. opn_sampleRate long 0 -opn_lpf_scas long $1000 +opn_lpf_scas long $1800 ' lower values lead to more muffled sound. $4000 is unfiltered ' Constants ' --------- opn_dac_center long $7F80 @@ -6197,7 +6254,7 @@ adpa_accl long 0 adpa_accr long 0 -' OPN2 internal state +' OPNB internal state ' ------------------- op_phase long 0[4*4] eg_state_b byte EG_RELEASE[4*4] @@ -6509,7 +6566,7 @@ opn_arg2 res 1 opn_arg3 res 1 opn_arg4 res 1 -' OPN2 global/channel regs +' OPNB global/channel regs ' ------------------------ gl_unused_alev_lfo_bfreq res 1 gl_ch3_kcodes_mode res 1 @@ -6522,18 +6579,18 @@ ch_freq_algo_pan res 1 gl_keyon res 1 ch_am_enable res 1 -' OPN2 current operator regs +' OPNB current operator regs ' -------------------------- op_mul_tl res 1 -' OPN2 current EG operator regs +' OPNB current EG operator regs ' ----------------------------- opn_tempValue3 res 1 ' here so it can get overwritten when the EG reads operator data eg_ar_dr_sr_rr res 1 eg_rs_sl_ssgeg res 1 -' OPN2 temporary +' OPNB temporary ' -------------- op_accu res 1 ch_out res 1 @@ -6717,7 +6774,7 @@ do_output add opn_arg1,opn_outl add opn_arg2,opn_outr - ' apply low-pass filter and mix PSG + ' apply low-pass filter scas opn_arg1,opn_lpf_scas add opn_filter_left,0-0 diff --git a/spin2/client/src/extension.ts b/spin2/client/src/extension.ts index 359781e..4120766 100644 --- a/spin2/client/src/extension.ts +++ b/spin2/client/src/extension.ts @@ -4,7 +4,7 @@ * ------------------------------------------------------------------------------------------ */ 'use strict'; // src/extensions.ts - +/* eslint-disable no-console */ // allow console writes from this file import * as path from 'path'; import * as vscode from 'vscode'; @@ -23,19 +23,19 @@ import { isSpinOrPasmDocument } from './spin.vscode.utils'; let client: LanguageClient; -const extensionDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit -let extensionOutputChannel: vscode.OutputChannel | undefined = undefined; +const isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit +let debugOutputChannel: vscode.OutputChannel | undefined = undefined; const objTreeProvider: ObjectTreeProvider = new ObjectTreeProvider(); const tabFormatter: Formatter = new Formatter(); -const docGenerator: DocGenerator = new DocGenerator(); +const docGenerator: DocGenerator = new DocGenerator(objTreeProvider); const codeBlockColorizer: RegionColorizer = new RegionColorizer(); const logExtensionMessage = (message: string): void => { // simple utility to write to TABBING output window. - if (extensionDebugLogEnabled && extensionOutputChannel != undefined) { + if (isDebugLogEnabled && debugOutputChannel !== undefined) { //Write to output window. - extensionOutputChannel.appendLine(message); + debugOutputChannel.appendLine(message); } }; @@ -91,7 +91,7 @@ function registerCommands(context: vscode.ExtensionContext): void { try { // and test it! docGenerator.generateDocument(); - docGenerator.showDocument(); + docGenerator.showDocument('.txt'); } catch (error) { await vscode.window.showErrorMessage('Document Generation Problem'); console.error(error); @@ -99,6 +99,29 @@ function registerCommands(context: vscode.ExtensionContext): void { }) ); + // ---------------------------------------------------------------------------- + // Hook GENERATE Object Hierarchy Document + // + const generateHierarchyFileCommand: string = 'spinExtension.generate.hierarchy.file'; + + context.subscriptions.push( + vscode.commands.registerCommand(generateHierarchyFileCommand, async () => { + docGenerator.logMessage('* generateHierarchyFileCommand'); + try { + // and test it! + docGenerator.generateHierarchyDocument(); + docGenerator.showDocument('.readme.txt'); + } catch (error) { + await vscode.window.showErrorMessage(`Hierarchy Generation Problem\n${error.stack}`); + this.logMessage(`Exception: Hierarchy Generation Problem\n${error.stack}`); + console.error(error); + } + }) + ); + + // ---------------------------------------------------------------------------- + // Hook ... + // const statusBarItem: vscode.StatusBarItem = createStatusBarItem(); handleActiveTextEditorChanged(); // now show or hide based upon current/active window @@ -171,7 +194,7 @@ function registerCommands(context: vscode.ExtensionContext): void { // Set Up our TAB Formatting // // post information to out-side world via our CONTEXT - vscode.commands.executeCommand('setContext', 'spinExtension.tabStops.enabled', tabFormatter.isEnbled()); + vscode.commands.executeCommand('setContext', 'runtime.spinExtension.elasticTabstops.enabled', tabFormatter.isEnabled()); // Hook TAB Formatting const insertTabStopsCommentCommand = 'spinExtension.elasticTabstops.generate.tabStops.comment'; @@ -248,15 +271,20 @@ function registerCommands(context: vscode.ExtensionContext): void { // eslint-disable-next-line @typescript-eslint/no-unused-vars const objDepTreeView: vscode.TreeView = vscode.window.createTreeView('spinExtension.objectDependencies', { canSelectMany: false, - showCollapseAll: true, + showCollapseAll: false, // now false so we don't have disfunctioning [-] button treeDataProvider: objTreeProvider }); //objDepTreeView.onDidChangeSelection(objTreeProvider.onElementClick); const objectTreeViewRefreshCommand = 'spinExtension.objectDependencies.refreshEntry'; + const objectTreeViewExpandAllCommand = 'spinExtension.objectDependencies.expandAll'; + const objectTreeViewCollapseAllCommand = 'spinExtension.objectDependencies.collapseAll'; const objectTreeViewActivateFileCommand = 'spinExtension.objectDependencies.activateFile'; + // post information to out-side world via our CONTEXT - we default to showing top-level only (so, collapsed is true) - vscode.commands.registerCommand(objectTreeViewRefreshCommand, () => objTreeProvider.refresh()); + vscode.commands.registerCommand(objectTreeViewRefreshCommand, async () => objTreeProvider.refresh()); + vscode.commands.registerCommand(objectTreeViewExpandAllCommand, async () => objTreeProvider.expandAll()); + vscode.commands.registerCommand(objectTreeViewCollapseAllCommand, async () => objTreeProvider.collapseAll()); vscode.commands.registerCommand(objectTreeViewActivateFileCommand, async (arg1) => objTreeProvider.onElementClick(arg1)); } @@ -293,10 +321,10 @@ function initializeProviders(): void { } export function activate(context: vscode.ExtensionContext) { - if (extensionDebugLogEnabled) { - if (extensionOutputChannel === undefined) { + if (isDebugLogEnabled) { + if (debugOutputChannel === undefined) { //Create output channel - extensionOutputChannel = vscode.window.createOutputChannel('Spin/Spin2 Extension DEBUG'); + debugOutputChannel = vscode.window.createOutputChannel('Spin/Spin2 Extension DEBUG'); logExtensionMessage('Spin/Spin2 Extension log started.'); } else { logExtensionMessage('\n\n------------------ NEW FILE ----------------\n\n'); @@ -402,7 +430,7 @@ function handleActiveTextEditorChanged(textEditor?: vscode.TextEditor) { //logExtensionMessage(`* SHOW SB-ITEM mode=[${modeName(mode)}]`); } // post information to out-side world via our CONTEXT - vscode.commands.executeCommand('setContext', 'spinExtension.insert.mode', modeName(mode)); + vscode.commands.executeCommand('setContext', 'runtime.spinExtension.insert.mode', modeName(mode)); // if in overtype mode, set the cursor to secondary style; otherwise, reset to default let cursorStyle; @@ -523,12 +551,12 @@ function getShowInStatusBar(): boolean { function typeCommand(args: { text: string }) { const editor = vscode.window.activeTextEditor; let editMode: eEditMode = eEditMode.INSERT; - if (editor == undefined) { + if (editor === undefined) { //logExtensionMessage("* VSCode type (early)"); vscode.commands.executeCommand('default:type', args); return; } - if (extensionDebugLogEnabled) { + if (isDebugLogEnabled) { const firstChar: number = args.text.charCodeAt(0); if (args.text.length == 1 && firstChar < 0x20) { logExtensionMessage('* type [0x' + firstChar.toString(16) + '](' + args.text.length + ')'); @@ -536,13 +564,13 @@ function typeCommand(args: { text: string }) { logExtensionMessage('* type [' + args.text + '](' + args.text.length + ')'); } } - if (editor != undefined) { + if (editor !== undefined) { editMode = getMode(editor); } - if (editor != undefined && tabFormatter.isEnbled() && editMode == eEditMode.OVERTYPE) { + if (editor !== undefined && tabFormatter.isEnabled() && editMode == eEditMode.OVERTYPE) { logExtensionMessage('* OVERTYPE type'); overtypeBeforeType(editor, args.text, false); - } else if (editor != undefined && tabFormatter.isEnbled() && editMode == eEditMode.ALIGN) { + } else if (editor !== undefined && tabFormatter.isEnabled() && editMode == eEditMode.ALIGN) { tabFormatter.alignBeforeType(editor, args.text, false); } else { //logExtensionMessage("* VSCode type"); @@ -553,14 +581,14 @@ function typeCommand(args: { text: string }) { function deleteLeftCommand() { const editor = vscode.window.activeTextEditor; logExtensionMessage('* deleteLeft'); - let bAlignEdit: boolean = editor != undefined && tabFormatter.isEnbled(); - if (editor != undefined) { + let bAlignEdit: boolean = editor !== undefined && tabFormatter.isEnabled(); + if (editor !== undefined) { const editMode = getMode(editor); if (editMode != eEditMode.ALIGN) { bAlignEdit = false; } } - if (bAlignEdit && editor != undefined) { + if (bAlignEdit && editor !== undefined) { tabFormatter.alignDelete(editor, false); return null; } else { @@ -572,7 +600,7 @@ function deleteLeftCommand() { function deleteRightCommand() { const editor = vscode.window.activeTextEditor; logExtensionMessage('* deleteRight'); - if (tabFormatter.isEnbled() && editor && getMode(editor) == eEditMode.ALIGN) { + if (tabFormatter.isEnabled() && editor && getMode(editor) == eEditMode.ALIGN) { tabFormatter.alignDelete(editor, true); return null; } else { @@ -583,14 +611,14 @@ function deleteRightCommand() { function pasteCommand(args: { text: string; pasteOnNewLine: boolean }) { const editor = vscode.window.activeTextEditor; - if (editor != undefined) { + if (editor !== undefined) { logExtensionMessage('* paste'); if (getMode(editor) == eEditMode.OVERTYPE && editModeConfiguration.overtypePaste) { // TODO: Make paste work with align logExtensionMessage('* OVERTYPE paste'); overtypeBeforePaste(editor, args.text, args.pasteOnNewLine); return vscode.commands.executeCommand('default:paste', args); - } else if (tabFormatter.isEnbled() && getMode(editor) == eEditMode.ALIGN && !args.pasteOnNewLine) { + } else if (tabFormatter.isEnabled() && getMode(editor) == eEditMode.ALIGN && !args.pasteOnNewLine) { tabFormatter.alignBeforeType(editor, args.text, true); return null; } else { diff --git a/spin2/client/src/providers/spin.block.tracker.ts b/spin2/client/src/providers/spin.block.tracker.ts index b5dbeb7..0796ed5 100644 --- a/spin2/client/src/providers/spin.block.tracker.ts +++ b/spin2/client/src/providers/spin.block.tracker.ts @@ -25,8 +25,8 @@ export interface IBlockSpan { // CLASS DocumentFindings export class LocatedBlockFindings { // tracking of Spin Code Blocks - private trackerDebugLogEnabled: boolean = false; - private trackerOutputChannel: vscode.OutputChannel | undefined = undefined; + private isDebugLogEnabled: boolean = false; + private debugOutputChannel: vscode.OutputChannel | undefined = undefined; private instanceId: string = `BT:${new Date().getTime()}`; @@ -37,8 +37,8 @@ export class LocatedBlockFindings { private findingsLogEnabled: boolean = false; constructor(callerOutputChannel: vscode.OutputChannel, isCallerDebugEnabled: boolean) { - this.trackerDebugLogEnabled = isCallerDebugEnabled; - this.trackerOutputChannel = callerOutputChannel; + this.isDebugLogEnabled = isCallerDebugEnabled; + this.debugOutputChannel = callerOutputChannel; } public clear() { // clear spin-code-block tracking @@ -57,9 +57,9 @@ export class LocatedBlockFindings { // PRIVATE (Utility) Methods // private _logMessage(message: string): void { - if (this.trackerDebugLogEnabled && this.trackerOutputChannel != undefined) { + if (this.isDebugLogEnabled && this.debugOutputChannel !== undefined) { //Write to output window. - this.trackerOutputChannel.appendLine(message); + this.debugOutputChannel.appendLine(message); } } diff --git a/spin2/client/src/providers/spin.color.regions.ts b/spin2/client/src/providers/spin.color.regions.ts index 73f3574..1d5b69d 100644 --- a/spin2/client/src/providers/spin.color.regions.ts +++ b/spin2/client/src/providers/spin.color.regions.ts @@ -24,9 +24,9 @@ interface DecoratorInstanceHash { } export class RegionColorizer { - private coloringDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit - private trackerDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit - private coloringOutputChannel: vscode.OutputChannel | undefined = undefined; + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isTrackerDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private debugOutputChannel: vscode.OutputChannel | undefined = undefined; private namedColors: { [Identifier: string]: string } = { // key: "rgba hex value" @@ -100,10 +100,10 @@ export class RegionColorizer { private spinCodeUtils: SpinCodeUtils = new SpinCodeUtils(); constructor() { - if (this.coloringDebugLogEnabled) { - if (this.coloringOutputChannel === undefined) { + if (this.isDebugLogEnabled) { + if (this.debugOutputChannel === undefined) { //Create output channel - this.coloringOutputChannel = vscode.window.createOutputChannel('Spin/Spin2 BGColor DEBUG'); + this.debugOutputChannel = vscode.window.createOutputChannel('Spin/Spin2 BGColor DEBUG'); this.logMessage('Spin/Spin2 BGColor log started.'); } else { this.logMessage('\n\n------------------ NEW FILE ----------------\n\n'); @@ -125,7 +125,7 @@ export class RegionColorizer { blockFindings = this.findingsByFilespec.get(fileSpec); this.logMessage(` -- REUSE blockSpanInformation.id=[${blockFindings.id}] for [${path.basename(fileSpec)}]`); } else { - blockFindings = new LocatedBlockFindings(this.coloringOutputChannel, this.trackerDebugLogEnabled); + blockFindings = new LocatedBlockFindings(this.debugOutputChannel, this.isTrackerDebugLogEnabled); this.findingsByFilespec.set(fileSpec, blockFindings); this.logMessage(` -- NEW blockSpanInformation.id=[${blockFindings.id}] for [${path.basename(fileSpec)}]`); } @@ -531,7 +531,7 @@ export class RegionColorizer { // add range to new or existing decoration decorationsByColor[color].regions.push(decorationRange); - if (decorationsByColor[color].decorator == undefined) { + if (decorationsByColor[color].decorator === undefined) { decorationsByColor[color].decorator = colorDecorator; } } @@ -666,9 +666,9 @@ export class RegionColorizer { * @returns nothing */ public logMessage(message: string): void { - if (this.coloringDebugLogEnabled && this.coloringOutputChannel != undefined) { + if (this.isDebugLogEnabled && this.debugOutputChannel !== undefined) { //Write to output window. - this.coloringOutputChannel.appendLine(message); + this.debugOutputChannel.appendLine(message); } } diff --git a/spin2/client/src/providers/spin.document.generate.ts b/spin2/client/src/providers/spin.document.generate.ts index d89506e..4614cc6 100644 --- a/spin2/client/src/providers/spin.document.generate.ts +++ b/spin2/client/src/providers/spin.document.generate.ts @@ -9,17 +9,23 @@ import * as path from 'path'; import { isSpin1Document, isSpin2File, isSpin1File } from '../spin.vscode.utils'; import { SpinCodeUtils, eParseState } from '../spin.code.utils'; +import { ObjectTreeProvider, SpinDependency } from '../spin.object.dependencies'; export class DocGenerator { - private generatorDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit - private docGenOutputChannel: vscode.OutputChannel | undefined = undefined; + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private debugOutputChannel: vscode.OutputChannel | undefined = undefined; private spinCodeUtils: SpinCodeUtils = new SpinCodeUtils(); + private objTreeProvider: ObjectTreeProvider; + private endOfLineStr: string = '\r\n'; + private hierarchyFilenameTotal: number = 0; + private hierarchyFilenameCount: number = 0; - constructor() { - if (this.generatorDebugLogEnabled) { - if (this.docGenOutputChannel === undefined) { + constructor(objectTreeProvider: ObjectTreeProvider) { + this.objTreeProvider = objectTreeProvider; + if (this.isDebugLogEnabled) { + if (this.debugOutputChannel === undefined) { //Create output channel - this.docGenOutputChannel = vscode.window.createOutputChannel('Spin/Spin2 DocGen DEBUG'); + this.debugOutputChannel = vscode.window.createOutputChannel('Spin/Spin2 DocGen DEBUG'); this.logMessage('Spin/Spin2 DocGen log started.'); } else { this.logMessage('\n\n------------------ NEW FILE ----------------\n\n'); @@ -27,24 +33,14 @@ export class DocGenerator { } } - /** - * write message to formatting log (when log enabled) - * - * @param the message to be written - * @returns nothing - */ - public logMessage(message: string): void { - if (this.generatorDebugLogEnabled && this.docGenOutputChannel != undefined) { - //Write to output window. - this.docGenOutputChannel.appendLine(message); - } - } - + // ---------------------------------------------------------------------------- + // Hook GENERATE PUB/PRI doc comment + // public insertDocComment(document: vscode.TextDocument, selections: readonly vscode.Selection[]): vscode.ProviderResult { return selections .map((selection) => { const results: vscode.ProviderResult = []; - const endOfLineStr: string = document.eol == EndOfLine.CRLF ? '\r\n' : '\n'; + this.endOfLineStr = document.eol == EndOfLine.CRLF ? '\r\n' : '\n'; const isSpin1Doc: boolean = isSpin1Document(document); this.logMessage( `* iDc selection(isSingle-[${selection.isSingleLine}] isSpin1Doc-(${isSpin1Doc}) isEmpty-[${selection.isEmpty}] s,e-[${selection.start.line}:${selection.start.character} - ${selection.end.line}:${selection.end.character}] activ-[${selection.active.character}] anchor-[${selection.anchor.character}])` @@ -64,7 +60,7 @@ export class DocGenerator { // insert the lines, if any if (linesToInsert.length > 0) { for (const line of linesToInsert) { - results.push(vscode.TextEdit.insert(cursorPos, `${line}` + endOfLineStr)); + results.push(vscode.TextEdit.insert(cursorPos, `${line}` + this.endOfLineStr)); } } return results; @@ -204,10 +200,13 @@ export class DocGenerator { return desiredDocComment; } + // ---------------------------------------------------------------------------- + // Hook GENERATE Object Public Interface Document + // public generateDocument(): void { const textEditor = vscode.window.activeTextEditor; if (textEditor) { - const endOfLineStr: string = textEditor.document.eol == EndOfLine.CRLF ? '\r\n' : '\n'; + this.endOfLineStr = textEditor.document.eol == EndOfLine.CRLF ? '\r\n' : '\n'; let bHuntingForVersion: boolean = true; // initially we re hunting for a {Spin2_v##} spec in file-top comments const currentlyOpenTabfilePath = textEditor.document.uri.fsPath; @@ -266,12 +265,12 @@ export class DocGenerator { currState = priorState; // if last line has additional text write it! if (trimmedLine.length > 2 && shouldEmitTopDocComments) { - fs.appendFileSync(outFile, trimmedLine + endOfLineStr); + fs.appendFileSync(outFile, trimmedLine + this.endOfLineStr); } } else { // if last line has additional text write it! if (shouldEmitTopDocComments) { - fs.appendFileSync(outFile, trimmedLine + endOfLineStr); + fs.appendFileSync(outFile, trimmedLine + this.endOfLineStr); } } continue; @@ -307,7 +306,7 @@ export class DocGenerator { currState = eParseState.inMultiLineDocComment; // if first line has additional text write it! if (trimmedLine.length > 2 && shouldEmitTopDocComments) { - fs.appendFileSync(outFile, trimmedLine + endOfLineStr); + fs.appendFileSync(outFile, trimmedLine + this.endOfLineStr); } } continue; @@ -327,7 +326,7 @@ export class DocGenerator { // process single-line doc comment if (trimmedLine.length > 2 && shouldEmitTopDocComments) { // emit comment without leading '' - fs.appendFileSync(outFile, trimmedLine.substring(2) + endOfLineStr); + fs.appendFileSync(outFile, trimmedLine.substring(2) + this.endOfLineStr); } continue; } @@ -342,19 +341,19 @@ export class DocGenerator { pubsFound++; if (shouldEmitTopDocComments) { this.logMessage(`+ (DBG) generateDocument() EMIT object header`); - fs.appendFileSync(outFile, '' + endOfLineStr); // blank line + fs.appendFileSync(outFile, '' + this.endOfLineStr); // blank line const introText: string = 'Object "' + objectName + '" Interface:'; - fs.appendFileSync(outFile, introText + endOfLineStr); + fs.appendFileSync(outFile, introText + this.endOfLineStr); if (requiredLanguageVersion > 0) { const lanVersionText: string = ` (Requires Spin2 Language v${requiredLanguageVersion})`; - fs.appendFileSync(outFile, lanVersionText + endOfLineStr); + fs.appendFileSync(outFile, lanVersionText + this.endOfLineStr); } - fs.appendFileSync(outFile, '' + endOfLineStr); // blank line + fs.appendFileSync(outFile, '' + this.endOfLineStr); // blank line } shouldEmitTopDocComments = false; // no more of these! // emit new PUB prototype (w/o any trailing comment) const trimmedNonCommentLine = this.getNonCommentLineRemainder(0, trimmedLine); - fs.appendFileSync(outFile, trimmedNonCommentLine + endOfLineStr); + fs.appendFileSync(outFile, trimmedNonCommentLine + this.endOfLineStr); } } // @@ -376,12 +375,12 @@ export class DocGenerator { currState = priorState; // if last line has additional text write it! if (trimmedLine.length > 2 && (emitTrailingDocComment || emitPubDocComment)) { - fs.appendFileSync(outFile, line.text.substring(2).trimEnd() + endOfLineStr); + fs.appendFileSync(outFile, line.text.substring(2).trimEnd() + this.endOfLineStr); } } else { // if last line has additional text write it! if (emitTrailingDocComment || emitPubDocComment) { - fs.appendFileSync(outFile, line.text.trimEnd() + endOfLineStr); + fs.appendFileSync(outFile, line.text.trimEnd() + this.endOfLineStr); } } continue; @@ -415,7 +414,7 @@ export class DocGenerator { currState = eParseState.inMultiLineDocComment; // if first line has additional text write it! if (trimmedLine.length > 2 && (emitTrailingDocComment || emitPubDocComment)) { - fs.appendFileSync(outFile, line.text.trimEnd() + endOfLineStr); + fs.appendFileSync(outFile, line.text.trimEnd() + this.endOfLineStr); } } continue; @@ -435,7 +434,7 @@ export class DocGenerator { // process single-line doc comment if (trimmedLine.length > 2 && (emitTrailingDocComment || emitPubDocComment)) { // emit comment without leading '' - fs.appendFileSync(outFile, trimmedLine.substring(2) + endOfLineStr); + fs.appendFileSync(outFile, trimmedLine.substring(2) + this.endOfLineStr); } } else if (sectionStatus.isSectionStart && currState == eParseState.inPri) { emitPubDocComment = false; @@ -446,12 +445,12 @@ export class DocGenerator { const trailingDocComment: string | undefined = this.getTrailingDocComment(trimmedLine); const trimmedNonCommentLine = this.getNonCommentLineRemainder(0, trimmedLine); const header: string = '_'.repeat(trimmedNonCommentLine.length); - fs.appendFileSync(outFile, '' + endOfLineStr); // blank line - fs.appendFileSync(outFile, header + endOfLineStr); // underscore header line - fs.appendFileSync(outFile, trimmedNonCommentLine + endOfLineStr); - fs.appendFileSync(outFile, '' + endOfLineStr); // blank line + fs.appendFileSync(outFile, '' + this.endOfLineStr); // blank line + fs.appendFileSync(outFile, header + this.endOfLineStr); // underscore header line + fs.appendFileSync(outFile, trimmedNonCommentLine + this.endOfLineStr); + fs.appendFileSync(outFile, '' + this.endOfLineStr); // blank line if (trailingDocComment) { - fs.appendFileSync(outFile, trailingDocComment + endOfLineStr); // underscore header line + fs.appendFileSync(outFile, trailingDocComment + this.endOfLineStr); // underscore header line } if (pubsSoFar >= pubsFound) { emitTrailingDocComment = true; @@ -459,7 +458,7 @@ export class DocGenerator { } } else if (sectionStatus.isSectionStart && currState != eParseState.inPub && emitTrailingDocComment) { // emit blank line just before we do final doc comment at end of file - fs.appendFileSync(outFile, '' + endOfLineStr); // blank line + fs.appendFileSync(outFile, '' + this.endOfLineStr); // blank line } } fs.closeSync(outFile); @@ -495,7 +494,7 @@ export class DocGenerator { return docComment; } - async showDocument() { + async showDocument(reportFileType: string) { const textEditor = vscode.window.activeTextEditor; if (textEditor) { const currentlyOpenTabfilePath = textEditor.document.uri.fsPath; @@ -515,7 +514,7 @@ export class DocGenerator { } } if (isSpinFile) { - const docFilename: string = currentlyOpenTabfileName.replace(fileType, '.txt'); + const docFilename: string = currentlyOpenTabfileName.replace(fileType, reportFileType); //this.logMessage(`+ (DBG) generateDocument() outFn-(${docFilename})`); const outFSpec = path.join(currentlyOpenTabfolderName, docFilename); //this.logMessage(`+ (DBG) generateDocument() outFSpec-(${outFSpec})`); @@ -527,4 +526,266 @@ export class DocGenerator { } } } + + // ---------------------------------------------------------------------------- + // Hook GENERATE Object Hierarchy Document + // + public generateHierarchyDocument(): void { + const textEditor = vscode.window.activeTextEditor; + if (textEditor) { + this.endOfLineStr = textEditor.document.eol == EndOfLine.CRLF ? '\r\n' : '\n'; + const bHuntingForVersion: boolean = true; // initially we re hunting for a {Spin2_v##} spec in file-top comments + + const currentlyOpenTabfilePath = textEditor.document.uri.fsPath; + const currentlyOpenTabfolderName = path.dirname(currentlyOpenTabfilePath); + const currentlyOpenTabfileName = path.basename(currentlyOpenTabfilePath); + this.logMessage(`+ (DBG) generateHierarchyDocument() fsPath-(${currentlyOpenTabfilePath})`); + this.logMessage(`+ (DBG) generateHierarchyDocument() folder-(${currentlyOpenTabfolderName})`); + this.logMessage(`+ (DBG) generateHierarchyDocument() filename-(${currentlyOpenTabfileName})`); + let isSpinFile: boolean = isSpin2File(currentlyOpenTabfileName); + let isSpin1: boolean = false; + let fileType: string = '.spin2'; + if (!isSpinFile) { + isSpinFile = isSpin1File(currentlyOpenTabfileName); + if (isSpinFile) { + isSpin1 = true; + fileType = '.spin'; + } + } + if (isSpinFile) { + const objectName: string = currentlyOpenTabfileName.replace(fileType, ''); + const docFilename: string = currentlyOpenTabfileName.replace(fileType, '.readme.txt'); + this.logMessage(`+ (DBG) generateHierarchyDocument() outFn-(${docFilename})`); + const outFSpec = path.join(currentlyOpenTabfolderName, docFilename); + this.logMessage(`+ (DBG) generateHierarchyDocument() outFSpec-(${outFSpec})`); + + const outFile = fs.openSync(outFSpec, 'w'); + + const rptHoriz: string = '─'; + + // add generation here + + // write report title + const rptTitle: string = 'Parallax Propeller Chip Object Hierarchy'; + fs.appendFileSync(outFile, `${rptHoriz.repeat(rptTitle.length)}${this.endOfLineStr}`); // horizontal line + fs.appendFileSync(outFile, `${rptTitle}${this.endOfLineStr}`); // blank line + fs.appendFileSync(outFile, `${rptHoriz.repeat(rptTitle.length)}${this.endOfLineStr}`); // horizontal line + fs.appendFileSync(outFile, `${this.endOfLineStr}`); // blank line + fs.appendFileSync(outFile, ` Project : "${objectName}"${this.endOfLineStr}${this.endOfLineStr}`); + fs.appendFileSync(outFile, `Reported : ${this.reportDateString()}${this.endOfLineStr}${this.endOfLineStr}`); + const versionStr: string = this.extensionVersionString(); + fs.appendFileSync(outFile, ` Tool : VSCode Spin2 Extension ${versionStr} ${this.endOfLineStr}${this.endOfLineStr}`); + fs.appendFileSync(outFile, `${this.endOfLineStr}`); // blank line + + // Get object tree from ObjectTreeProvider (may have to add supporting code therein) + // then report on object tree obtained + const [topFilename, depMap] = this.objTreeProvider.getObjectHierarchy(); + this.hierarchyFilenameTotal = this.countFiles(topFilename, depMap); + this.hierarchyFilenameCount = 0; + if (topFilename.length == 0 || depMap.size == 0) { + fs.appendFileSync(outFile, `NO Dependencies found!${this.endOfLineStr}`); // blank line + } else { + this.logMessage(`+ (DBG) generateHierarchyDocument() topFilename=[${topFilename}], deps=(${depMap.size})`); + const depth: number = 0; + const topChildren = depMap.get(topFilename); + const lastParent: boolean = this.isOnlyParent(topFilename, depMap); + const lastChild = false; + this.reportDeps(depth, [], topFilename, depMap, outFile, lastParent, lastChild); + } + fs.appendFileSync(outFile, `${this.endOfLineStr}`); // blank line + fs.appendFileSync(outFile, `${this.endOfLineStr}`); // blank line + fs.appendFileSync(outFile, `${rptHoriz.repeat(rptTitle.length)}${this.endOfLineStr}`); // horizontal line + fs.appendFileSync(outFile, `Parallax Inc.${this.endOfLineStr}`); + fs.appendFileSync(outFile, `www.parallax.com${this.endOfLineStr}`); + fs.appendFileSync(outFile, `support@parallax.com${this.endOfLineStr}${this.endOfLineStr}`); + fs.appendFileSync(outFile, `VSCode Spin2 Extension by:${this.endOfLineStr}`); + fs.appendFileSync(outFile, ` Iron Sheep Productions, LLC${this.endOfLineStr}`); + fs.closeSync(outFile); + } else { + this.logMessage(`+ (DBG) generateHierarchyDocument() NOT a spin file! can't generate doc.`); + } + } else { + this.logMessage(`+ (DBG) generateHierarchyDocument() NO active editor.`); + } + } + + private countFiles(topFilename: string, deps: Map): number { + let desiredFileCount: number = topFilename && topFilename.length > 0 ? 1 : 0; + const topDep: SpinDependency | undefined = deps.get(topFilename); + if (topDep !== undefined) { + desiredFileCount += topDep.children.length; + let currChildren: string[] = topDep.children.map((child) => child.name); + while (currChildren.length > 0) { + const childrenThisPass: string[] = currChildren; + currChildren = []; + for (let index = 0; index < childrenThisPass.length; index++) { + const childName = childrenThisPass[index]; + const childDep: SpinDependency | undefined = deps.get(childName); + if (childDep.hasChildren) { + desiredFileCount += childDep.children.length; + const grandChildrenName: string[] = childDep.children.map((grandChild) => grandChild.name); + currChildren.push(...grandChildrenName); + } + } + } + } + this.logMessage(`* countFiles() desiredFileCount=(${desiredFileCount})`); + return desiredFileCount; + } + + private isOnlyParent(topFilename: string, depMap: Map): boolean { + let onlyParentStatus: boolean = true; + if (depMap.has(topFilename)) { + const fileWithchildren: SpinDependency = depMap.get(topFilename); + if (fileWithchildren !== undefined) { + this.logMessage(`* isOnlyParent() childrenCt=[${fileWithchildren.children.length}]`); + for (let index = 0; index < fileWithchildren.children.length; index++) { + const child = fileWithchildren.children[index]; + const grandChildWithChildren = depMap.get(child.name); + if (grandChildWithChildren !== undefined && grandChildWithChildren.children.length > 0) { + this.logMessage(`* isOnlyParent() [${child.name}] grandChildren=[${grandChildWithChildren.children.length}]`); + onlyParentStatus = false; + break; + } + } + } + } + this.logMessage(`* isOnlyParent()=(${onlyParentStatus})`); + return onlyParentStatus; + } + + private reportDeps( + depth: number, + nestList: boolean[], + filename: string, + depMap: Map, + outFile: number, + isLastParent: boolean, + isLastChild: boolean + ) { + this.logMessage(`+ rD() d=(${depth}), nd=[${nestList}], ilp=(${isLastParent}), ilc=(${isLastChild}), fn=[${filename}]`); + const baseIndent: number = 12; + const rptHoriz: string = '─'; + const rptVert: string = '│'; + const rptTeeRight: string = '├'; + const rptElbow: string = '└'; + const childWithChildren: SpinDependency | undefined = depMap.get(filename); + const haveChildren: boolean = childWithChildren !== undefined && childWithChildren.children.length > 0; + const NoEndBlank: boolean = true; + const AllowEndBlank: boolean = false; + // file line prefix + const fileEndChar: string = isLastChild ? rptElbow : rptTeeRight; + const prefixFillTee: string = this.fillWithVerts(nestList, fileEndChar, NoEndBlank); + let linePrefixFile: string = ' '.repeat(baseIndent - 2); + if (depth == 0) { + linePrefixFile = `${linePrefixFile} `; + } else { + linePrefixFile = `${linePrefixFile}${prefixFillTee}${rptHoriz}${rptHoriz}`; + } + // blank line prefix + if (nestList.length > 1) { + nestList[depth - 1] = isLastChild ? false : true; + } + const vertNestList: boolean[] = haveChildren ? [...nestList, true] : nestList; // create copy and add true if children + const specialEndBlanking: boolean = isLastChild && !haveChildren ? false : NoEndBlank; + const prefixFillVert: string = this.fillWithVerts(vertNestList, rptVert, specialEndBlanking); + let linePrefixSpacer: string = ' '.repeat(baseIndent - 2); + if (depth == 0) { + linePrefixSpacer = ' '.repeat(baseIndent - 2) + this.fillWithVerts([true], rptVert, NoEndBlank); + } else { + linePrefixSpacer = `${linePrefixSpacer}${prefixFillVert}`; + } + // write one or both lines + fs.appendFileSync(outFile, `${linePrefixFile}${filename}${this.endOfLineStr}`); // filename line + this.hierarchyFilenameCount++; + // show last line of not last line to be drawn in chart + // last line is when we are both at last parent and last child + const showLastBlankLine: boolean = this.hierarchyFilenameCount < this.hierarchyFilenameTotal; + this.logMessage( + `+ rD() showLastBlankLine=(${showLastBlankLine}), count=(${this.hierarchyFilenameCount}), total=(${this.hierarchyFilenameTotal})` + ); + if (showLastBlankLine) { + fs.appendFileSync(outFile, `${linePrefixSpacer}${this.endOfLineStr}`); // blank line + } + // process children of this object + if (haveChildren) { + for (let index = 0; index < childWithChildren.children.length; index++) { + const childDep = childWithChildren.children[index]; + const isLastChild: boolean = index == childWithChildren.children.length - 1; + + const nextIsLastParent = depth == 0 && isLastChild ? true : isLastParent; + nestList.push(nextIsLastParent ? false : true); + this.reportDeps(depth + 1, nestList, childDep.name, depMap, outFile, nextIsLastParent, isLastChild); + } + } + if (nestList.length > 0) { + nestList.pop(); + } + } + + private fillWithVerts(nestList: boolean[], lastVert: string, noEndBlank: boolean): string { + const rptVert: string = '│'; + const isBlankLineGen: boolean = lastVert == rptVert; + let prefixFill: string = ''; + if (nestList.length > 0) { + //this.logMessage(`+ fwV() nestList=[${nestList}], lastVert=(${lastVert})`); + for (let index = 0; index < nestList.length; index++) { + const isLastChild = index == nestList.length - 1; + let showSymbol = nestList[index]; + if (isBlankLineGen) { + if (isLastChild && !noEndBlank && nestList.length > 2) { + showSymbol = false; + } + } else { + showSymbol = isLastChild ? true : showSymbol; + } + + const vertSym: string = isLastChild ? lastVert : rptVert; + const fillSegment = showSymbol ? ` ${vertSym}` : ` `; + prefixFill = `${prefixFill}${fillSegment}`; + } + } + this.logMessage(`+ fwV()=[${prefixFill}], nestList=[${nestList}], lastVert=(${lastVert}), noEndBlank=(${noEndBlank})`); + return prefixFill; + } + + private extensionVersionString(): string { + // return the version string of this extension + const extension = vscode.extensions.getExtension('IronSheepProductionsLLC.spin2'); + let version: string = extension?.packageJSON.version; + if (version === undefined) { + version = '?.?.?'; + } + return `v${version}`; // the version of the extension + } + + private reportDateString(): string { + const date = new Date(); + const options: Intl.DateTimeFormatOptions = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: true + }; + + const formattedDate = new Intl.DateTimeFormat('en-US', options).format(date); + return formattedDate; // Prints: "Saturday, January 13, 2024 at 6:50:29 PM" + } + + /** + * write message to formatting log (when log enabled) + * + * @param the message to be written + * @returns nothing + */ + public logMessage(message: string): void { + if (this.isDebugLogEnabled && this.debugOutputChannel !== undefined) { + //Write to output window. + this.debugOutputChannel.appendLine(message); + } + } } diff --git a/spin2/client/src/providers/spin.editMode.configuration.ts b/spin2/client/src/providers/spin.editMode.configuration.ts index 90c05ae..56cfc99 100644 --- a/spin2/client/src/providers/spin.editMode.configuration.ts +++ b/spin2/client/src/providers/spin.editMode.configuration.ts @@ -35,10 +35,10 @@ const loadEditModeConfiguration = () => { //const editorConfiguration = vscode.workspace.getConfiguration('editor'); return { - overtypePaste: insertModeConfiguration.get('overtypePaste'), + overtypePaste: insertModeConfiguration.get('overtypePaste') ? true : false, perEditor: insertModeConfiguration.get('perEditor') ? true : false, - enableAlign: insertModeConfiguration.get('enableAlign'), + enableAlign: insertModeConfiguration.get('enableAlign') ? true : false, labelInsertMode: insertModeConfiguration.get('labelInsertMode'), labelOvertypeMode: insertModeConfiguration.get('labelOvertypeMode'), diff --git a/spin2/client/src/providers/spin.tabFormatter.configuration.ts b/spin2/client/src/providers/spin.tabFormatter.configuration.ts index ef999bf..62709d9 100644 --- a/spin2/client/src/providers/spin.tabFormatter.configuration.ts +++ b/spin2/client/src/providers/spin.tabFormatter.configuration.ts @@ -35,13 +35,13 @@ const loadTabConfiguration = () => { //const tabSize = tabFormatterConfiguration.get('editor.tabSize'); //const useTabStops = tabFormatterConfiguration.get("editor.useTabStops"); - //const enable = tabFormatterConfiguration.get('enable'); + //const enable = tabFormatterConfiguration.get('enable') ? true: false; //const timeout = tabFormatterConfiguration.get("timeout"); //const maxLineCount = tabFormatterConfiguration.get("maxLineCount"); //const maxLineLength = tabFormatterConfiguration.get("maxLineLength"); return { - enable: tabFormatterConfiguration.get('enable'), + enable: tabFormatterConfiguration.get('enable') ? true : false, tabSet: tabFormatterConfiguration.get('choice')!, blocks: tabFormatterConfiguration.get(tabsUserSelection)!, tabSize: tabFormatterConfiguration.get('editor.tabSize') diff --git a/spin2/client/src/providers/spin.tabFormatter.ts b/spin2/client/src/providers/spin.tabFormatter.ts index 49cbbaf..4fa55ea 100644 --- a/spin2/client/src/providers/spin.tabFormatter.ts +++ b/spin2/client/src/providers/spin.tabFormatter.ts @@ -65,8 +65,8 @@ export class Formatter { readonly endIdentifierREgEx1 = /^(?\s*(end|endasm))\s+/i; readonly endIdentifierREgEx2 = /^(?\s*(end|endasm))$/i; - private tabbingDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit - private tabbingOutputChannel: vscode.OutputChannel | undefined = undefined; + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private debugOutputChannel: vscode.OutputChannel | undefined = undefined; private finalTabSelection: vscode.Selection; private bFinalTabSelectionValid: boolean = false; @@ -76,10 +76,10 @@ export class Formatter { //export const configuration = loadConfiguration(); constructor() { - if (this.tabbingDebugLogEnabled) { - if (this.tabbingOutputChannel === undefined) { + if (this.isDebugLogEnabled) { + if (this.debugOutputChannel === undefined) { //Create output channel - this.tabbingOutputChannel = vscode.window.createOutputChannel('Spin/Spin2 Format DEBUG'); + this.debugOutputChannel = vscode.window.createOutputChannel('Spin/Spin2 Format DEBUG'); this.logMessage('Spin/Spin2 Format log started.'); } else { this.logMessage('\n\n------------------ NEW FILE ----------------\n\n'); @@ -112,9 +112,9 @@ export class Formatter { * @returns nothing */ public logMessage(message: string): void { - if (this.tabbingDebugLogEnabled && this.tabbingOutputChannel != undefined) { + if (this.isDebugLogEnabled && this.debugOutputChannel !== undefined) { //Write to output window. - this.tabbingOutputChannel.appendLine(message); + this.debugOutputChannel.appendLine(message); } } @@ -122,7 +122,7 @@ export class Formatter { * Return T/F where T means the formatter should be enabled * @returns T/F */ - isEnbled(): boolean { + isEnabled(): boolean { const bEnableStatus: boolean = this.enable ? true : false; return bEnableStatus; } diff --git a/spin2/client/src/spin.clientBehavior.configuration.ts b/spin2/client/src/spin.clientBehavior.configuration.ts index 78ad5c5..c2005e3 100644 --- a/spin2/client/src/spin.clientBehavior.configuration.ts +++ b/spin2/client/src/spin.clientBehavior.configuration.ts @@ -7,7 +7,7 @@ const loadEditorConfiguration = () => { const editorConfiguration = vscode.workspace.getConfiguration('spinExtension.ClientBehavior'); return { - colorBackground: editorConfiguration.get('colorEditorBackground'), + colorBackground: editorConfiguration.get('colorEditorBackground') ? true : false, backgroundApha: editorConfiguration.get('editorBackgroundAlpha') }; }; diff --git a/spin2/client/src/spin.code.utils.ts b/spin2/client/src/spin.code.utils.ts index 4b2581b..432bbd5 100644 --- a/spin2/client/src/spin.code.utils.ts +++ b/spin2/client/src/spin.code.utils.ts @@ -18,20 +18,20 @@ export enum eParseState { } export class SpinCodeUtils { - private debugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit - private outputChannel: vscode.OutputChannel | undefined = undefined; + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private debugOutputChannel: vscode.OutputChannel | undefined = undefined; public constructor() {} public enableLogging(channel: vscode.OutputChannel, doEnable: boolean = true): void { - this.debugLogEnabled = doEnable; - this.outputChannel = channel; + this.isDebugLogEnabled = doEnable; + this.debugOutputChannel = channel; } private _logMessage(message: string): void { - if (this.debugLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. - this.outputChannel.appendLine(message); + this.debugOutputChannel.appendLine(message); } } diff --git a/spin2/client/src/spin.object.dependencies.ts b/spin2/client/src/spin.object.dependencies.ts index b813d35..f054a38 100644 --- a/spin2/client/src/spin.object.dependencies.ts +++ b/spin2/client/src/spin.object.dependencies.ts @@ -13,191 +13,766 @@ import * as fs from 'fs'; import { SpinCodeUtils, eParseState } from './spin.code.utils'; import { isSpinFile } from './spin.vscode.utils'; +const CALLED_INTERNALLY: boolean = true; // refresh() called by code, not button press +const ELEM_COLLAPSED: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.Collapsed; +const ELEM_EXPANDED: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.Expanded; +const ELEM_NONE: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None; + +enum eTreeState { + TS_Unknown, + TS_ExpandTop, + TS_ExpandAll +} + export class ObjectTreeProvider implements vscode.TreeDataProvider { private rootPath: string | undefined = vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0 ? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined; private bFixHierToTopLevel: boolean = false; private topLevelFSpec: string = ''; private topLevelFName: string = ''; - - private objTreeDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit - private objTreeOutputChannel: vscode.OutputChannel | undefined = undefined; - - private isDocument: boolean = false; + private fixedTopLevelFSpec: string = ''; + private treeState: eTreeState = eTreeState.TS_ExpandAll; // eTreeState.TS_ExpandTop; // tracks current state of treeView + private viewEnabledState: boolean = false; + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private debugOutputChannel: vscode.OutputChannel | undefined = undefined; + private isEmptying: boolean = false; private spinCodeUtils: SpinCodeUtils = new SpinCodeUtils(); + private latestHierarchy: Map = new Map(); // children by filename + private existingIDs: string[] = []; // https://code.visualstudio.com/api/extension-guides/tree-view#view-container - private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter< - Dependency | undefined | null | void - >(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; constructor() { - if (this.objTreeDebugLogEnabled) { - if (this.objTreeOutputChannel === undefined) { + if (this.isDebugLogEnabled) { + if (this.debugOutputChannel === undefined) { //Create output channel - this.objTreeOutputChannel = vscode.window.createOutputChannel('Spin/Spin2 ObjTree DEBUG'); - this.logMessage('Spin/Spin2 ObjTree log started.'); + this.debugOutputChannel = vscode.window.createOutputChannel('Spin/Spin2 ObjTree DEBUG'); + this._logMessage('Spin/Spin2 ObjTree log started.'); } else { - this.logMessage('\n\n------------------ NEW FILE ----------------\n\n'); + this._logMessage('\n\n------------------ NEW FILE ----------------\n\n'); } } + // add subscriptions - vscode.window.onDidChangeActiveTextEditor(() => this.onActiveEditorChanged()); + vscode.window.onDidChangeActiveTextEditor(() => this.activeEditorChanged()); //vscode.workspace.onDidChangeTextDocument(e => this.onDocumentChanged(e)); + + // publish current tree state + this._publishTreeState(); + //this._publishViewEnableState(true); + if (!this.rootPath) { - this.logMessage('+ (DBG) ObjDep: no root path!'); - } else { - let topFileBaseName: string | undefined = undefined; - const fullConfiguration = vscode.workspace.getConfiguration(); - //this.logMessage(`+ (DBG) fullConfiguration=${JSON.stringify(fullConfiguration)}`); - this.logMessage(`+ (DBG) fullConfiguration=${fullConfiguration}`); - if (fullConfiguration.has('topLevel')) { - topFileBaseName = vscode.workspace.getConfiguration().get('topLevel'); // this worked! - } else { - this.logMessage(`+ (DBG) topLevel key NOT present!`); - } - this.logMessage(`+ (DBG) ObjDep: topFileBaseName=[${topFileBaseName}]`); - if (topFileBaseName) { - this.bFixHierToTopLevel = true; - const fileBasename = this._filenameWithSpinFileType(topFileBaseName); - this.topLevelFSpec = path.join(this.rootPath, fileBasename); - if (!this._fileExists(this.topLevelFSpec)) { - this.logMessage(`+ (DBG) ObjDep: FILE NOT FOUND! [${this.topLevelFSpec}]!`); - this.bFixHierToTopLevel = false; - } - } + this._logMessage('+ (DBG) ObjDep: no root path!'); + } + + // load topFile variable if present in config + // sets: bFixHierToTopLevel, fixedTopLevelFSpec + this._loadConfigWithTopFileInfo(); + if (this.bFixHierToTopLevel) { + this.topLevelFSpec = this.fixedTopLevelFSpec; } if (!this.bFixHierToTopLevel) { - this.logMessage(`+ (DBG) ObjDep: failed to ID top file for workspace`); + this._logMessage(`+ (DBG) ObjDep: failed to ID top file for workspace`); this.topLevelFSpec = this._getActiveSpinFile(); - this.logMessage(`+ (DBG) ObjDep: NO TOP/curr, using activeFile=[${this.topLevelFSpec}]`); + this._logMessage(`+ (DBG) ObjDep: NO TOP/curr, using activeFile=[${this.topLevelFSpec}]`); if (this.topLevelFSpec.length == 0) { // ERROR failed to ID open file (or file not open) } } else { - this.logMessage(`+ (DBG) ObjDep: topLevelFSpec=[${this.topLevelFSpec}]`); + this._logMessage(`+ (DBG) ObjDep: topLevelFSpec=[${this.topLevelFSpec}]`); } this.rootPath = path.dirname(this.topLevelFSpec); this.topLevelFName = path.basename(this.topLevelFSpec); // cause our initial status to be exposed - this.onActiveEditorChanged(); + + if (this.topLevelFSpec.length > 0) { + this._logMessage(`+ (DBG) ObjDep: constructor() done, firing editorChanged...`); + this.activeEditorChanged(CALLED_INTERNALLY); + this._preloadFullSpinDependencies(); + } else { + this._logMessage(`+ (DBG) ObjDep: constructor() done, NO TOP/curr, NO activeFile`); + } } - /** - * write message to formatting log (when log enabled) - * - * @param the message to be written - * @returns nothing - */ - logMessage(message: string): void { - if (this.objTreeDebugLogEnabled && this.objTreeOutputChannel != undefined) { - //Write to output window. - this.objTreeOutputChannel.appendLine(message); + private _loadConfigWithTopFileInfo(): boolean { + let tmpTopFileBaseName: string | undefined = undefined; + let topFileChanged: boolean = false; + const fullConfiguration = vscode.workspace.getConfiguration(); + //this._logMessage(`+ (DBG) fullConfiguration=${JSON.stringify(fullConfiguration)}`); + //this._logMessage(`+ (DBG) fullConfiguration=${fullConfiguration}`); + const runtimeConfiguration = vscode.workspace.getConfiguration('runtime'); + this._logMessage(`+ (DBG) runtimeConfiguration=${JSON.stringify(runtimeConfiguration)}`); + if (fullConfiguration.has('topLevel')) { + tmpTopFileBaseName = vscode.workspace.getConfiguration().get('topLevel'); // this worked! + } else { + this._logMessage(`+ (DBG) ObjDep: topLevel key NOT present!`); + } + this._logMessage(`+ (DBG) ObjDep: fixedTopFileBaseName=[${tmpTopFileBaseName}]`); + if (tmpTopFileBaseName) { + const fileBasename = this._filenameWithSpinFileType(tmpTopFileBaseName); + const tmpTopLevelFSpec = path.join(this.rootPath, fileBasename); + if (!this._fileExists(tmpTopLevelFSpec)) { + this._logMessage(`+ (DBG) ObjDep: FILE NOT FOUND! [${tmpTopLevelFSpec}]!`); + this.bFixHierToTopLevel = false; + } else { + topFileChanged = this.fixedTopLevelFSpec != tmpTopLevelFSpec || this.bFixHierToTopLevel == false; + this.bFixHierToTopLevel = true; + this.fixedTopLevelFSpec = tmpTopLevelFSpec; + } } + return topFileChanged; } - getTreeItem(element: Dependency): vscode.TreeItem { - this.logMessage(`+ (DBG) ObjDep: getTreeItem(${element.label})`); - return element; + public getObjectHierarchy(): [string, Map] { + // used by report generator + this._logMessage(`+==+ (DBG) ObjDep: getObjectHierarchy()`); + return [this.topLevelFName, this.latestHierarchy]; } - getChildren(element?: Dependency): Thenable { - if (!this.rootPath) { - vscode.window.showInformationMessage('No dependency in empty workspace'); - const noDep = new Dependency('No object references in empty workspace', '', vscode.TreeItemCollapsibleState.None); - noDep.removeIcon(); // this is message, don't show icon - return Promise.resolve([noDep]); + public onElementClick(element: Dependency | undefined): void { + const clickArg: string = element !== undefined ? element?.label.toString() : ''; + this._logMessage(`+==+ (DBG) ObjDep: onElementClick() element=[${clickArg}]`); + if (!element?.isFileMissing && this.rootPath && element) { + const fileFSpec: string = path.join(this.rootPath, element.label.toString()); + this._showDocument(fileFSpec); + } + } + + public refresh(internalUse: boolean = false): void { + const invokeType: string = internalUse ? 'INTERNAL' : 'CLICK'; + this._logMessage(`+${invokeType}+ (DBG) ObjDep: refresh()`); + this._onDidChangeTreeData.fire(undefined); + } + + public async expandAll(): Promise { + this._logMessage('+CLICK+ (DBG) ObjDep: expandAll()'); + this.isEmptying = true; + this.refresh(CALLED_INTERNALLY); + this.treeState = eTreeState.TS_ExpandAll; + this._publishTreeState(); + this.refresh(CALLED_INTERNALLY); + } + + public collapseAll(): void { + this._logMessage('+CLICK+ (DBG) ObjDep: collapseAll()'); + this.isEmptying = true; + this.refresh(CALLED_INTERNALLY); + this.treeState = eTreeState.TS_ExpandTop; + this._publishTreeState(); + this.refresh(CALLED_INTERNALLY); + } + + public getTreeItem(element: Dependency): vscode.TreeItem { + /** + * Get {@link TreeItem} representation of the `element` + * + * @param element The element for which {@link TreeItem} representation is asked for. + * @returns TreeItem representation of the element. + */ + this._logMessage(`+==+ (DBG) ObjDep: getTreeItem(${element.label}[${element.id}]) [${this._collapseStateString(element.collapsibleState)}]`); + // set our collapse expand value here? + const desiredElement: Dependency = element; + return desiredElement; + } + + /* + public async getParentOLD2(element: Promise | Dependency): Promise { + const resolvedElement = element instanceof Promise ? await element : element; + let topArg: string = resolvedElement !== undefined ? resolvedElement.label.toString() : ''; + if (topArg === undefined) { + topArg = '{element.label=undefined!}'; + } + this._dumpElement('getParent()', resolvedElement); + const parentRslt: string = + resolvedElement !== undefined && resolvedElement.parentDep !== undefined ? resolvedElement.parentDep.label.toString() : ''; + // given child, locate and return the parent + this._logMessage(`+==+ (DBG) ObjDep: getParent(${topArg}) -> [${parentRslt}]`); + let desiredParent: Dependency | undefined = undefined; + if (resolvedElement !== undefined) { + desiredParent = resolvedElement.parentDep; + } + return desiredParent; + } + + public getParentOLD3(element: Dependency): Dependency | undefined { + let topArg: string = element !== undefined ? element.label.toString() : ''; + if (topArg === undefined) { + topArg = '{element.label=undefined!}'; + } + this._dumpElement('getParent()', element); + const parentRslt: string = element !== undefined && element.parentDep !== undefined ? element.parentDep.label.toString() : ''; + // given child, locate and return the parent + this._logMessage(`+==+ (DBG) ObjDep: getParent(${topArg}) -> [${parentRslt}]`); + let desiredParent: Dependency | undefined = undefined; + if (element !== undefined) { + desiredParent = element.parentDep; + } + return desiredParent; + } + + public getParentOLD(element: Dependency): vscode.ProviderResult { + ** + * Optional method to return the parent of `element`. + * Return `null` or `undefined` if `element` is a child of root. + * + * **NOTE:** This method should be implemented in order to access {@link TreeView.reveal reveal} API. + * + * @param element The element for which the parent has to be returned. + * @return Parent of `element`. + * + if (element instanceof Promise) { + this._logMessage(`+==+ (DBG) ObjDep: getParent() called with Promise!!`); + element.then((value) => { + element = value; // Unwrap the promise + }); + } + let topArg: string = element !== undefined ? element.label.toString() : ''; + if (topArg === undefined) { + topArg = '{element.label=undefined!}'; + } + this._dumpElement('getParent()', element); + const parentRslt: string = element !== undefined && element.parentDep !== undefined ? element.parentDep.label.toString() : ''; + // given child, locate and return the parent + this._logMessage(`+==+ (DBG) ObjDep: getParent(${topArg}) -> [${parentRslt}]`); + let desiredParent: Dependency | undefined = undefined; + if (element !== undefined) { + desiredParent = element.parentDep; + } + return Promise.resolve(desiredParent); + } +*/ + + public getChildren(element?: Dependency): vscode.ProviderResult { + /** + * Get the children of `element` or root if no element is passed. + * + * @param element The element from which the provider gets children. Can be `undefined`. + * @return Children of `element` or root if no element is passed. + */ + const topId: string = element !== undefined ? `[${element?.id}]` : ''; + const topArg: string = element !== undefined ? `${element?.label.toString()}${topId}` : ''; + this._logMessage(`+==+ (DBG) ObjDep: getChildrn(${topArg}) isEmptying=(${this.isEmptying})`); + if (element !== undefined) { + this._dumpElement('getChildrn()', element); } const subDeps: Dependency[] = []; - if (element) { - this.logMessage(`+ (DBG) ObjDep: getChildren() element=[${element?.label}]`); + if (this.isEmptying) { + // CASE we are letting the OBJ TREE remove all content... + // so we return nothing but clear flag telling us to do this + // NOT NEEDED vscode.window.showInformationMessage('Rebuilding object hierarchy tree'); + const rebuildingDep = new Dependency('...', '', ELEM_NONE, -1, 'xyzzy'); + rebuildingDep.removeIcon(); // this is message, don't show icon + subDeps.push(rebuildingDep); + this.isEmptying = false; // we've done this once... + } else if (!this.rootPath) { + // CASE we dont know where folder containing .spin2 files is yet! + vscode.window.showInformationMessage('No object references in empty workspace'); + const noDep = new Dependency('No object references in empty workspace', '', ELEM_NONE, -1, 'x'); + noDep.removeIcon(); // this is message, don't show icon + subDeps.push(noDep); + } else if (element !== undefined) { + // CASE we have CHILD: get grandchildren + //this._logMessage(`+ (DBG) ObjDep: getChildrn() element=[${element?.label}]`); // get for underlying file - const fileBasename = this._filenameWithSpinFileType(element.label); - const fileSpec = path.join(this.rootPath, fileBasename); - if (!this._fileExists(fileSpec)) { - this.logMessage(`+ (DBG) ObjDep: getChildren() element=[${fileBasename}] has (???) deps - MISSING FILE`); - const topState: vscode.TreeItemCollapsibleState = this._stateForDependCount(0); - const subDep = new Dependency(element.label, element.descriptionString, topState); + const childFileBasename = this._filenameWithSpinFileType(element.label.toString()); + //const childFileSpec = path.join(this.rootPath, childFileBasename); + const childFileStructure: SpinDependency = this._getDepsFromHierarchy(element.label.toString()); + if (childFileStructure.isFileMissing) { + // CASE: file is missing for child + this._logMessage(`+ (DBG) ObjDep: getChildrn() element=[${childFileBasename}] has (???) deps - MISSING FILE`); + const subDep = new Dependency(element.label, element.descriptionString, ELEM_NONE, -1, `${childFileStructure.id}z`); subDep.setFileMissing(); subDeps.push(subDep); } else { - const spinDeps = this._getDepsFromSpinFile(fileSpec); - this.logMessage(`+ (DBG) ObjDep: getChildren() element=[${fileBasename}] has (${spinDeps.length}) deps`); - spinDeps.forEach((depency) => { - const depFileSpec = path.join(this.rootPath!, depency.baseName); - // huh, is this if really needed? - if (this._fileExists(depFileSpec)) { - const subSpinDeps = this._getDepsFromSpinFile(depFileSpec); - const topState: vscode.TreeItemCollapsibleState = this._stateForDependCount(subSpinDeps.length); - const subDep = new Dependency(depency.baseName, depency.knownAs, topState); + // CASE: have child, return deps for this child + this._logMessage(`+ (DBG) ObjDep: getChildrn() element=[${childFileBasename}] has (${childFileStructure.childCount}) deps`); + const haveOrigChild: boolean = this._isFirstDefnOfFile(childFileStructure.name, element.id); + if (haveOrigChild) { + // CASE: ORIGINAL CHILD use discovered state + for (let index = 0; index < childFileStructure.childCount; index++) { + // turn each child into real information with temp overrides + const childsChild: SpinDependency = childFileStructure.children[index]; + const childsChildState: vscode.TreeItemCollapsibleState = this._elementCollapseState( + childsChild.depth, + !childsChild.isFileMissing, + this._actualChildCount(childsChild.name) + ); + const subDep = new Dependency(childsChild.name, childsChild.knownAs, childsChildState, childsChild.depth, childsChild.id); + if (childsChild.isFileMissing) { + subDep.setFileMissing(); + } subDeps.push(subDep); - } else { - const topState: vscode.TreeItemCollapsibleState = this._stateForDependCount(0); - const subDep = new Dependency(depency.baseName, depency.knownAs, topState); - subDep.setFileMissing(); + } + } else { + // CASE: NOT-ORIGINAL CHILD use computed state (replacement IDs, depth, but live children count) + const childsChildFileStructure: SpinDependency = this._getTempDepsFromHierarchy(element.id, childFileStructure); + for (let index = 0; index < childsChildFileStructure.childCount; index++) { + // turn each child into real information with temp overrides + const childsChild: SpinDependency = childsChildFileStructure.children[index]; + const childsChildState: vscode.TreeItemCollapsibleState = this._elementCollapseState( + childsChild.depth, + !childsChild.isFileMissing, + childsChild.childCount + ); + const subDep = new Dependency(childsChild.name, childsChild.knownAs, childsChildState, childsChild.depth, childsChild.id); + if (childsChild.isFileMissing) { + subDep.setFileMissing(); + } subDeps.push(subDep); } - }); + } } } else { - this.logMessage(`+ (DBG) ObjDep: getChildren() topLevel`); + this._logMessage(`+ (DBG) ObjDep: getChildrn() [topLevel]`); // get for project top level file - let spinDeps = []; - if (this.isDocument) { - const textEditor = vscode.window.activeTextEditor; - if (textEditor) { - spinDeps = this._getDepsFromDocument(textEditor.document); - } - } else { - spinDeps = this._getDepsFromSpinFile(this.topLevelFSpec); - } - this.logMessage(`+ (DBG) ObjDep: getChildren() topLevel has (${spinDeps.length}) deps`); - let topState: vscode.TreeItemCollapsibleState = this._stateForDependCount(spinDeps.length); - if (spinDeps.length > 0) { - topState = vscode.TreeItemCollapsibleState.Expanded; // always leave top-level expanded? - } - if (spinDeps.length > 0) { - const topDep = new Dependency(this.topLevelFName, '(top-file)', topState); + const topFileStructure: SpinDependency = this._getDepsFromHierarchy(); + this._logMessage(`+ (DBG) ObjDep: getChildrn() topLevel has (${topFileStructure.childCount}) deps`); + if (topFileStructure.childCount > 0) { + const topState: vscode.TreeItemCollapsibleState = this._elementCollapseState(0, true, topFileStructure.childCount); + const topDep = new Dependency(this.topLevelFName, '(top-file)', topState, topFileStructure.depth, topFileStructure.id); subDeps.push(topDep); } else { //vscode.window.showInformationMessage("Workspace has no package.json"); - const emptyMessage: string = 'No object references found in `' + `${this.topLevelFName}` + '`'; - const emptyDep = new Dependency(emptyMessage, '', vscode.TreeItemCollapsibleState.None); + const emptyMessage: string = `No object references found in ${this.topLevelFName}`; + const emptyDep = new Dependency(emptyMessage, '', ELEM_NONE, -1, 'y'); emptyDep.removeIcon(); // this is message, don't show icon subDeps.push(emptyDep); } } + this._dumpSubDeps(' --> ', subDeps); return Promise.resolve(subDeps); } - onElementClick(element: Dependency | undefined): void { - this.logMessage(`+ (DBG) ObjDep: onElementClick() element=[${element?.label}]`); - if (!element?.isFileMissing() && this.rootPath && element) { - const fileFSpec: string = path.join(this.rootPath, element.label); - this._showDocument(fileFSpec); + private _isFirstDefnOfFile(childName: string, childId: string): boolean { + const childFileStructure: SpinDependency = this._getDepsFromHierarchy(childName); + const desiredMatchState: boolean = childFileStructure.id === childId; + this._logMessage(`* _isFirstDefnOfFile([${childName}], [${childId}]) [${childFileStructure.id}] -> (${desiredMatchState})`); + return desiredMatchState; + } + + private _getTempDepsFromHierarchy(newTopId: string, actualDep: SpinDependency): SpinDependency { + // Create a modified copy without affecting original DATA!! + this._logMessage(`* TMP redo name=[${actualDep.name}], id=[${actualDep.id}], childCt=(${actualDep.childCount})`); + this._dumpSpinDeps(' ', [actualDep]); + const desiredFileStructure: SpinDependency = new SpinDependency('', undefined, '', this._getDepsFromHierarchy(actualDep.name)); + // the the structure... then re-ID, and force childCount of the children based on current parent + desiredFileStructure.replaceId(newTopId); + if (desiredFileStructure.hasChildren) { + for (let index = 0; index < desiredFileStructure.childCount; index++) { + const desiredChildDep = desiredFileStructure.children[index]; + const newId: string = `${newTopId}${index}`; + const newChildCount: number = this._actualChildCount(desiredChildDep.name); + this._logMessage( + ` TMP redo child[${desiredChildDep.name}] id:[${desiredChildDep.id}] -> [${newId}], count=(${desiredChildDep.childCount}) -> (${newChildCount})` + ); + desiredChildDep.replaceId(newId); + desiredChildDep.forceChildCount(newChildCount); + } + } + return desiredFileStructure; + } + + private _actualChildCount(filename: string): number { + const desiredFileStructure: SpinDependency = this._getDepsFromHierarchy(filename); + return desiredFileStructure.childCount; + } + + private activeEditorChanged(internalUse: boolean = false): void { + // if editor is currently a SPIN file and file is in hierarchy the refresh deps + // if editor is not a SPIN file but topFile changed then refresh deps + const invokeType: string = internalUse ? ' INTERNAL ' : ' TAB-change '; + this._logMessage(`+==+ (DBG) ObjDep: activeEditorChanged(${invokeType})`); + const initialViewEnabledState: boolean = this.viewEnabledState; + const topChanged: boolean = this._loadConfigWithTopFileInfo(); // ensure we have latest in case it changed + const haveActiveEditor: boolean = vscode.window.activeTextEditor ? true : false; + let editedFileNameChanged: boolean = false; + let haveSpinFile: boolean = false; + if (haveActiveEditor) { + // determine if edited file changed + const fileFSpec: string = this._getActiveSpinFile(); + const fileName: string = path.basename(fileFSpec); + haveSpinFile = isSpinFile(fileFSpec) ? true : false; // matches .spin and .spin2 + editedFileNameChanged = haveSpinFile && fileFSpec != this.topLevelFSpec ? true : false; + this._logMessage(`+ (DBG) ObjDep: aeChg() editFName=[${fileName}], nmChg=(${editedFileNameChanged}), haveSpinFile=(${haveSpinFile})`); + if (editedFileNameChanged && !this.bFixHierToTopLevel) { + this.topLevelFSpec = fileFSpec; + this.topLevelFName = fileName; + this.rootPath = path.dirname(this.topLevelFSpec); + } + } + this._logMessage(`+ (DBG) ObjDep: aeChg() topFName=[${this.topLevelFName}], topChg=(${topChanged}), editedNameChg=(${editedFileNameChanged})`); + const newViewEnabledState: boolean = haveSpinFile && (this.bFixHierToTopLevel || haveActiveEditor) ? true : false; + const stateChanged: boolean = initialViewEnabledState != newViewEnabledState; + this.viewEnabledState = newViewEnabledState; + + if (!this.bFixHierToTopLevel && !haveActiveEditor) { + this._logMessage(`+ (DBG) ObjDep: aeChg() [NO-top, NO-editor]`); + // CASE [00] NO top file, NO active editor + // nothing to do, DONE + } else if (!this.bFixHierToTopLevel && haveActiveEditor) { + this._logMessage(`+ (DBG) ObjDep: aeChg() [NO-top, YES-editor]`); + // CASE [01] NO top file, YES active editor + let reloadHierarchy: boolean = false; + if (haveSpinFile) { + if (editedFileNameChanged) { + // reload full tree hierarchy + reloadHierarchy = true; + } else { + // just a content change + // if files' children changed then update file in curr tree + if (this.latestHierarchy.has(this.topLevelFName)) { + this._logMessage(`+ (DBG) ObjDep: aeChg() file is in tree!`); + const currDep = this.latestHierarchy.get(this.topLevelFName); + const needsReload: boolean = this._updateDependencies(currDep); + if (needsReload) { + // child changes were significant (dep removed or added) so... + // reload full tree hierarchy + reloadHierarchy = true; + } + } + } + } + if (reloadHierarchy) { + // child changes were significant (dep removed or added) so let's just rebuild the tree + this._preloadFullSpinDependencies(); + } + // if edited file changed, rebuild tree + } else if (this.bFixHierToTopLevel && !haveActiveEditor) { + this._logMessage(`+ (DBG) ObjDep: aeChg() [YES-top, NO-editor]`); + // CASE [10] YES topFile, NO active editor + // if top file changed, rebuild tree + if (topChanged) { + this._preloadFullSpinDependencies(); + } + } else if (this.bFixHierToTopLevel && haveActiveEditor) { + this._logMessage(`+ (DBG) ObjDep: aeChg() [YES-top, YES-editor]`); + // CASE [11] YES top file, YES active editor + // if top file changed -OR- if edited file changed, rebuild tree + if (topChanged) { + this._preloadFullSpinDependencies(); + } else if (haveSpinFile && this.latestHierarchy.has(this.topLevelFName)) { + this._logMessage(`+ (DBG) ObjDep: aeChg() file is in tree!`); + // else if only file changed the reload deps for file + // if file change was drammatic rebuild the file + const currDep = this.latestHierarchy.get(this.topLevelFName); + const needsReload: boolean = this._updateDependencies(currDep); + if (needsReload) { + // child changes were significant (dep removed or added) so... + // reload full tree hierarchy + this._preloadFullSpinDependencies(); + } + } + } + if (stateChanged) { + // only publish on change + this._publishViewEnableState(this.viewEnabledState); + if (this.viewEnabledState == true) { + // if enabled after change, refresh view + if (this.latestHierarchy.size == 0) { + this._preloadFullSpinDependencies(); + } + this.refresh(CALLED_INTERNALLY); + } else { + // is our view is not enabled, remove all hierarchy state + this.clearHierarchy(); + } + } + } + + private _publishTreeState() { + const currentState: boolean = this.treeState == eTreeState.TS_ExpandTop ? true : false; + this._logMessage(`* ObjDep: treeState .objectDeps.showingTopOnly=(${currentState})`); + vscode.commands.executeCommand('setContext', 'runtime.spinExtension.objectDeps.showingTopOnly', currentState); + } + + private _publishViewEnableState(desiredEnableState: boolean) { + this._logMessage(`* ObjDep: treeView .objectDeps.enabled=(${desiredEnableState})`); + // record new published state + // and publish new state + vscode.commands.executeCommand('setContext', 'runtime.spinExtension.objectDeps.enabled', desiredEnableState); + } + + private _collapseStateString(collapseMode: vscode.TreeItemCollapsibleState): string { + let desiredString: string = ''; + switch (collapseMode) { + case ELEM_COLLAPSED: + desiredString = 'CS_COLLAPSED'; + break; + + case ELEM_EXPANDED: + desiredString = 'CS_EXPANDED'; + break; + + case ELEM_NONE: + desiredString = 'CS_NONE'; + break; + + default: + desiredString = '?unk?'; + break; } + return desiredString; } - // getParent(element: Dependency): Thenable { + ////////////////////////////////////////////////////////////////////////////////// + // build internal full project object hierarchy // - //} + private clearHierarchy() { + // remove all present knowledge of spin object hierarchy + this.latestHierarchy.clear(); + this.existingIDs = []; + } + + private _preloadFullSpinDependencies() { + // prescan all files starting from current file in active editor if any, else scan topFile if given + // will do NOTHING if these conditions are not met + this._logMessage(`* *** _preloadFullSpinDeps()`); + if (this.viewEnabledState == true) { + // + // GET top level, schedule children for next pass + // + let nextDeps: SpinDependency[] = []; + // ensure empty tree at start, clear ID cache + this.clearHierarchy(); + // start reload from root + const rootDep: SpinDependency = this._getDependencies(); + if (rootDep !== undefined) { + this._addDependency(rootDep); + for (let index = 0; index < rootDep.childCount; index++) { + const childDep = rootDep.children[index]; + nextDeps.push(childDep); + } + } else { + this._logMessage(`ERROR: _preloadFullSpinDeps() _getDeps({root}) returned NOTHING!`); + } + // + // GET next level (existing children), schedule grand-children for next pass + // keep repeating until all children are processed + // + let nextDepsCopy: SpinDependency[] = []; + do { + nextDepsCopy = nextDeps; + nextDeps = []; + this._logMessage(`* *** _preloadFullSpinDeps() loading ${nextDepsCopy.length} children`); + for (let index = 0; index < nextDepsCopy.length; index++) { + const child = nextDepsCopy[index]; + const thisFileExplored: boolean = this.latestHierarchy.has(child.name) ? true : false; + if (thisFileExplored) { + child.setExplored(); + } else { + const childDep: SpinDependency = this._getDependencies(child); + if (childDep !== undefined) { + this._addDependency(childDep); + for (let index = 0; index < childDep.childCount; index++) { + const grandChildDep = childDep.children[index]; + nextDeps.push(grandChildDep); + } + } else { + this._logMessage(`ERROR: _preloadFullSpinDeps() _getDeps(${child.name}) returned NOTHING!`); + } + } + } + } while (nextDeps.length > 0); + } else { + this._logMessage(`ERROR: _preloadFullSpinDeps() viewEnabledState == FALSE returned NOTHING!`); + } + + this._logMessage(`* *** _preloadFullSpinDeps() ended with ${this.latestHierarchy.size} files`); + this._dumpTree(); + } + + private _addDependency(fileWithChildren: SpinDependency) { + const filename: string = fileWithChildren.name; + if (fileWithChildren.isExplored == true) { + if (this.latestHierarchy.has(filename) == false) { + this._logMessage(`**** _addDepends(${fileWithChildren.name})[${fileWithChildren.id}], ${fileWithChildren.childCount} file(s)`); + this.latestHierarchy.set(filename, fileWithChildren); + } + } + } + + private _updateDependencies(childSpinDep?: SpinDependency): boolean { + const topArg: string = childSpinDep !== undefined ? childSpinDep?.name : ''; + let needTreeRebuiltStatus: boolean = false; + if (childSpinDep) { + //this._dumpSpinDeps('', [childSpinDep]); + const newSpinDep: SpinDependency = this._getDependencies(childSpinDep, false); + const needUpdate: boolean = childSpinDep.childCount != newSpinDep.childCount; + if (!needUpdate) { + // get list of orig child names + const originalChildNames: string[] = childSpinDep.children.map((child) => child.name); + // get list of orig child names + const latestChildNames: string[] = newSpinDep.children.map((child) => child.name); + + const childrenMatch: boolean = originalChildNames.sort().join(',') === latestChildNames.sort().join(','); + // if we have names added or removed then we need to update the list + if (!childrenMatch) { + this._logMessage(` _updateDeps() MISMATCH orig=[${originalChildNames}](${originalChildNames.length})`); + this._logMessage(` latest=[${latestChildNames}](${latestChildNames.length})`); + // this could cause files to be removed from our list, instead let's just ask for the tree to be rebuilt + needTreeRebuiltStatus = true; + } + } + } + this._logMessage(`**** _updateDeps(${topArg}) -> needRebuild=(${needTreeRebuiltStatus})`); + return needTreeRebuiltStatus; + } - refresh(): void { - this.logMessage('+ (DBG) ObjDep: refresh()'); - this._onDidChangeTreeData.fire(); + private _getDependencies(childSpinDep: SpinDependency = undefined, checkIDs: boolean = true): SpinDependency | undefined { + /** + * Get the top-file w/children or specified-file w/children + * return this object and its dependencies + */ + //const topArg: string = childSpinDep !== undefined ? childSpinDep?.name : ''; + //this._logMessage(`++++ (DBG) ObjDep: getDeps(${topArg})`); + let objectAndDependency: SpinDependency = undefined; + if (!this.rootPath) { + this._logMessage(`!!!! ERROR: ObjDep: Unable to locate objects without rootPath`); + return objectAndDependency; + } + if (childSpinDep !== undefined) { + // CASE we have CHILD: get grandchildren + childSpinDep.setExplored(); + //this._logMessage(`+ (DBG) ObjDep: getDeps() childSpinDep=[${childSpinDep?.name}] IS EXPLORED`); + // get for underlying file + this._logMessage(` -- _getDeps(${childSpinDep.name}) childID=[${childSpinDep.id}]`); + const childFileBasename = this._filenameWithSpinFileType(childSpinDep.name); + const childFileSpec = path.join(this.rootPath, childFileBasename); + if (!this._fileExists(childFileSpec)) { + // CASE: file is missing for child + //this._logMessage(`+ (DBG) ObjDep: getDeps() element=[${childFileBasename}] has (???) deps - MISSING FILE`); + childSpinDep.setFileMissing(); + } else { + // CASE: get child deps + const grandChildDeps: SpinObject[] = this._getDepsFromSpinFile(childFileSpec); + //this._logMessage(`+ (DBG) ObjDep: getDeps() element=[${childFileBasename}] has (${grandChildDeps.length}) deps`); + let grandChildNbr: number = 0; + for (let index = 0; index < grandChildDeps.length; index++) { + const childsChild = grandChildDeps[index]; + const grandChildFileSpec = path.join(this.rootPath!, childsChild.fileName); + const childDep = new SpinDependency(grandChildFileSpec, childsChild, `${childSpinDep.id}${grandChildNbr}`); + if (checkIDs) { + this._checkId(childDep); + } + childSpinDep.addChild(childDep); + grandChildNbr++; + } + } + objectAndDependency = childSpinDep; + } else { + //this._logMessage(`+ (DBG) ObjDep: getDeps() [topLevel]`); + // get for project top level file + this._logMessage(` -- _getDeps() topLevelFName=[${this.topLevelFName}]`); + const childDeps: SpinObject[] = this._getDepsFromSpinFile(this.topLevelFSpec); + const topSpin = new SpinObject(this.topLevelFName, '(top-file)'); + const topSpinDep = new SpinDependency(this.topLevelFSpec, topSpin, '0'); + if (checkIDs) { + this._checkId(topSpinDep); + } + topSpinDep.setExplored(); + //this._logMessage(`+ (DBG) ObjDep: getDeps() topLevel has (${childDeps.length}) children - IS EXPLORED`); + for (let index = 0; index < childDeps.length; index++) { + const childSpinDep = childDeps[index]; + const newChildDep = new SpinDependency(this.topLevelFSpec, childSpinDep, `${topSpinDep.id}${index}`); + if (checkIDs) { + this._checkId(newChildDep); + } + topSpinDep.addChild(newChildDep); + } + objectAndDependency = topSpinDep; + } + // pre-mark our results + // if we have already explored any of these filenames then don't allow 2nd explore + for (let index = 0; index < objectAndDependency.childCount; index++) { + const childDep = objectAndDependency.children[index]; + const filename: string = childDep.name; + if (this.latestHierarchy.has(filename)) { + childDep.setExplored(); + } + } + + //this._dumpSpinDeps('', [objectAndDependency]); + return objectAndDependency; } + private _checkId(newDep: SpinDependency) { + if (this.existingIDs.includes(newDep.id)) { + this._logMessage(`ERROR: DUPE id[${newDep.id}] exists in ${newDep.toString()}`); + } else { + this.existingIDs.push(newDep.id); + //this._logMessage(`* USED id[${newDep.id}] in ${newDep.toString()}`); + } + } + + private _dumpTree() { + let keyNumber: number = 1; + this.latestHierarchy.forEach((value, key) => { + this._logMessage(`+ dumpTree() #${keyNumber} key=[${key}]`); + this._dumpSpinDeps('', [value], keyNumber); + //console.log(`Key: ${key}, Value: ${value}`); + keyNumber++; + }); + } + + private _dumpSpinDeps(prefixStr: string, deps: SpinDependency[], keyNbr: number = -1) { + const itemNbr: string = keyNbr == -1 ? '#1' : ' '; + for (let index = 0; index < deps.length; index++) { + const spinDep = deps[index]; + this._logMessage( + `${prefixStr}+ dumpDeps() ${itemNbr} id=[${spinDep.id}], name=[${spinDep.name}], known=[${spinDep.knownAs}], nbrChildren=(${spinDep.childCount}), depth=(${spinDep.depth}), explored=(${spinDep.isExplored})` + ); + for (let index = 0; index < spinDep.childCount; index++) { + const childDep = spinDep.children[index]; + this._logMessage( + `${prefixStr}+ dumpDeps() --- child #${index} id=[${childDep.id}], name=[${childDep.name}], known=[${childDep.knownAs}], depth=(${childDep.depth}), explored=(${childDep.isExplored})` + ); + } + } + } + + private _dumpSubDeps(prefixStr: string, subDeps: Dependency[]) { + for (let index = 0; index < subDeps.length; index++) { + const dependency = subDeps[index]; + this._logMessage(`${prefixStr}* _dumpSubDeps() #${index} [${dependency.toString()}]`); + } + } + + private _getDepsFromHierarchy(filename?: string): SpinDependency | undefined { + //const desiredName: string = filename ? filename : '[topLevel]'; + //this._logMessage(`+ (DBG) ObjDep: _getDepsFromHier(${desiredName})`); + let desiredDeps: SpinDependency | undefined = undefined; + const topName: string = filename ? filename : this.topLevelFName; + if (this.latestHierarchy.has(topName)) { + desiredDeps = this.latestHierarchy.get(topName); + } + //this._dumpSpinDeps('', [desiredDeps]); + return desiredDeps; + } private async _showDocument(fileFSpec: string) { - this.logMessage(`+ (DBG) ObjDep: _showDocument() [${fileFSpec}]`); + this._logMessage(`+ (DBG) ObjDep: _showDocument() [${fileFSpec}]`); const textDocument = await vscode.workspace.openTextDocument(fileFSpec); await vscode.window.showTextDocument(textDocument, { preview: false }); } + private _elementCollapseState(depth: number, fileExists: boolean, nbrChildren: number): vscode.TreeItemCollapsibleState { + let desiredState: vscode.TreeItemCollapsibleState = ELEM_NONE; + if (fileExists == true && nbrChildren > 0) { + if (depth == 0) { + desiredState = ELEM_EXPANDED; // root is always expanded + } else { + // non-root is always expanded or collapsed depending upon tree state + desiredState = this.treeState == eTreeState.TS_ExpandTop ? ELEM_COLLAPSED : ELEM_EXPANDED; + } + } + return desiredState; + } + private _filenameWithSpinFileType(filename: string): string { const bHasFileType: boolean = isSpinFile(filename) ? true : false; // matches .spin and .spin2! (not .spin3, etc.) let desiredName: string = filename; @@ -210,42 +785,17 @@ export class ObjectTreeProvider implements vscode.TreeDataProvider { return desiredName; } - private onActiveEditorChanged(): void { - // if we are not fixes - if (vscode.window.activeTextEditor) { - if (!this.bFixHierToTopLevel) { - const fileFSpec: string = this._getActiveSpinFile(); - const enabled: boolean = isSpinFile(fileFSpec) ? true : false; // matches .spin and .spin2 - vscode.commands.executeCommand('setContext', 'spinExtension.objectDeps.enabled', enabled); - if (enabled) { - // set new file top - this.topLevelFSpec = fileFSpec; - this.topLevelFName = path.basename(this.topLevelFSpec); - this.rootPath = path.dirname(this.topLevelFSpec); - this.logMessage(`+ (DBG) ObjDep: onActiveEditorChanged() topLevelFSpec=[${this.topLevelFSpec}]`); - this.refresh(); - } - } else { - // we have topLevel for this workspace, stay enabled - vscode.commands.executeCommand('setContext', 'spinExtension.objectDeps.enabled', true); - } - } else { - vscode.commands.executeCommand('setContext', 'spinExtension.objectDeps.enabled', false); - } - } - private _getActiveSpinFile(): string { const textEditor = vscode.window.activeTextEditor; let foundFSpec: string = ''; if (textEditor) { if (textEditor.document.uri.scheme === 'file') { - this.isDocument = true; // we're loading initial deps from current tab, not file! const currentlyOpenTabFSpec = textEditor.document.uri.fsPath; //var currentlyOpenTabfolderName = path.dirname(currentlyOpenTabFSpec); const currentlyOpenTabfileName = path.basename(currentlyOpenTabFSpec); - //this.logMessage(`+ (DBG) ObjDep: fsPath-(${currentlyOpenTabFSpec})`); - //this.logMessage(`+ (DBG) ObjDep: folder-(${currentlyOpenTabfolderName})`); - this.logMessage(`+ (DBG) ObjDep: filename-(${currentlyOpenTabfileName})`); + //this._logMessage(`+ (DBG) ObjDep: fsPath-(${currentlyOpenTabFSpec})`); + //this._logMessage(`+ (DBG) ObjDep: folder-(${currentlyOpenTabfolderName})`); + this._logMessage(`+ (DBG) ObjDep: filename-(${currentlyOpenTabfileName})`); if (isSpinFile(currentlyOpenTabfileName)) { // matches .spin and .spin2 foundFSpec = currentlyOpenTabFSpec; @@ -270,13 +820,13 @@ export class ObjectTreeProvider implements vscode.TreeDataProvider { */ private _getDepsFromDocument(activeEditDocument: vscode.TextDocument): SpinObject[] { - this.logMessage(`+ (DBG) ObjDep: _getDepsFromDocument()`); + this._logMessage(`+ (DBG) ObjDep: _getDepsFromDocument(${path.basename(activeEditDocument.fileName)})`); let currState: eParseState = eParseState.inCon; // compiler defaults to CON at start! let priorState: eParseState = currState; const deps = []; for (let i = 0; i < activeEditDocument.lineCount; i++) { const line = activeEditDocument.lineAt(i); - const trimmedLine: string = line.text.replace(/\s+$/, ''); + const trimmedLine: string = line.text !== undefined ? line.text.replace(/\s+$/, '') : ''; if (trimmedLine.length == 0) { continue; // skip blank lines } @@ -332,33 +882,24 @@ export class ObjectTreeProvider implements vscode.TreeDataProvider { priorState = currState; currState = sectionStatus.inProgressStatus; } - //this.logMessage(`+ (DBG) ObjDep: _getDepsFromSpinFile() eval trimmedLine=[${trimmedLine}]`); + //this._logMessage(`+ (DBG) ObjDep: _getDepsFromDocument() eval trimmedLine=[${trimmedLine}]`); if (currState == eParseState.inObj && nonCommentLineRemainder.includes(':')) { const spinObj = this._spinDepFromObjectLine(nonCommentLineRemainder); if (spinObj) { - this.logMessage(`+ (DBG) ObjDep: _getDepsFromSpinFile() basename=[${spinObj.baseName}] known as (${spinObj.knownAs})`); + //this._logMessage(`+ (DBG) ObjDep: _getDepsFromDocument() basename=[${spinObj.baseName}] known as (${spinObj.knownAs})`); deps.push(spinObj); } else { - this.logMessage(`+ (DBG) ObjDep: _getDepsFromDocument() BAD parse of OBJ line [${trimmedLine}]`); + this._logMessage(`+ (DBG) ObjDep: _getDepsFromDocument() BAD parse of OBJ line [${trimmedLine}]`); } } } - this.logMessage(`+ (DBG) ObjDep: -- returns ${deps.length} dep(s)`); + this._logMessage(`+ (DBG) ObjDep: -- returns ${deps.length} dep(s)`); return deps; } - private _stateForDependCount(nbrDeps: number): vscode.TreeItemCollapsibleState { - // determine initial state of tree entry - let interpState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None; - if (nbrDeps > 0) { - interpState = vscode.TreeItemCollapsibleState.Collapsed; - } - return interpState; - } - private _getDepsFromSpinFile(fileSpec: string): SpinObject[] { const deps = []; - this.logMessage(`+ (DBG) ObjDep: _getDepsFromSpinFile(${fileSpec})`); + //this._logMessage(`+ (DBG) ObjDep: _getDepsFmSpinFile(${fileSpec})`); if (this._fileExists(fileSpec)) { const spinFileContent = this._loadFileAsString(fileSpec); // handles utf8/utf-16 let lines = spinFileContent.split('\r\n'); @@ -366,13 +907,13 @@ export class ObjectTreeProvider implements vscode.TreeDataProvider { // file not CRLF is LF only! lines = spinFileContent.split('\n'); } - this.logMessage(`+ (DBG) ObjDep: file has (${lines.length}) lines`); + this._logMessage(`+ (DBG) ObjDep: getDeps ${path.basename(fileSpec)} has (${lines.length}) lines`); let currState: eParseState = eParseState.inCon; // compiler defaults to CON at start! let priorState: eParseState = currState; for (let i = 0; i < lines.length; i++) { const text = lines[i]; - const trimmedLine = text.replace(/\s+$/, ''); + const trimmedLine = text !== undefined ? text.replace(/\s+$/, '') : ''; if (trimmedLine.length == 0) { continue; // skip blank lines } @@ -427,21 +968,21 @@ export class ObjectTreeProvider implements vscode.TreeDataProvider { priorState = currState; currState = sectionStatus.inProgressStatus; } - //this.logMessage(`+ (DBG) ObjDep: _getDepsFromSpinFile() eval trimmedLine=[${trimmedLine}]`); + //this._logMessage(`+ (DBG) ObjDep: _getDepsFmSpinFile() eval trimmedLine=[${trimmedLine}]`); if (currState == eParseState.inObj && nonCommentLineRemainder.includes(':')) { const spinObj = this._spinDepFromObjectLine(nonCommentLineRemainder); if (spinObj) { - this.logMessage(`+ (DBG) ObjDep: _getDepsFromSpinFile() basename=[${spinObj.baseName}] known as (${spinObj.knownAs})`); + //this._logMessage(`+ (DBG) ObjDep: _getDepsFmSpinFile() basename=[${spinObj.baseName}] known as (${spinObj.knownAs})`); deps.push(spinObj); } else { - this.logMessage(`+ (DBG) ObjDep: _getDepsFromDocument() BAD parse of OBJ line [${trimmedLine}]`); + this._logMessage(`+ (DBG) ObjDep: _getDepsFmSpinFile() BAD parse of OBJ line [${trimmedLine}]`); } } } } else { - this.logMessage(`+ (DBG) ObjDep: NOT FOUND! file=(${fileSpec})`); + this._logMessage(`+ (DBG) ObjDep: NOT FOUND! file=(${fileSpec})`); } - this.logMessage(`+ (DBG) ObjDep: -- returns ${deps.length} dep(s)`); + this._logMessage(`+ (DBG) ObjDep: -- returns ${deps.length} dep(s)`); return deps; } @@ -450,7 +991,7 @@ export class ObjectTreeProvider implements vscode.TreeDataProvider { const conOverrideLocn: number = objLine.indexOf('|'); const usefullObjLine: string = conOverrideLocn != -1 ? objLine.substring(0, conOverrideLocn) : objLine; const lineParts = usefullObjLine.split(/[ \t"]/).filter(Boolean); - this.logMessage(`+ (DBG) ObjDep: _spinDepFromObjectLine() lineParts=[${lineParts}](${lineParts.length}) line=[${objLine}]`); + //this._logMessage(`+ (DBG) ObjDep: _spinDepFromObjectLine() lineParts=[${lineParts}](${lineParts.length}) line=[${objLine}]`); let objName: string = ''; let filename: string = ''; if (lineParts.length >= 2) { @@ -476,17 +1017,17 @@ export class ObjectTreeProvider implements vscode.TreeDataProvider { private _loadFileAsString(fspec: string): string { let fileContent: string = ''; if (fs.existsSync(fspec)) { - this.logMessage(`* loadFileAsString() attempt load of [${fspec}]`); + this._logMessage(`* loadFileAsString() attempt load of [${fspec}]`); try { fileContent = fs.readFileSync(fspec, 'utf-8'); if (fileContent.includes('\x00')) { fileContent = fs.readFileSync(fspec, 'utf16le'); } } catch (err) { - this.logMessage(`* loadFileAsString() EXCEPTION: err=[${err}]`); + this._logMessage(`* loadFileAsString() EXCEPTION: err=[${err}]`); } } else { - this.logMessage(`* loadFileAsString() fspec=[${fspec}] NOT FOUND!`); + this._logMessage(`* loadFileAsString() fspec=[${fspec}] NOT FOUND!`); } return fileContent; } @@ -499,53 +1040,237 @@ export class ObjectTreeProvider implements vscode.TreeDataProvider { } return existsStatus; } + /** + * write message to formatting log (when log enabled) + * + * @param the message to be written + * @returns nothing + */ + private _logMessage(message: string): void { + if (this.isDebugLogEnabled && this.debugOutputChannel !== undefined) { + //Write to output window. + this.debugOutputChannel.appendLine(message); + } + } + + private _spinDepFromDep(dep: Dependency): SpinDependency { + //this._logMessage(`* _spinDepFromDep() label=[${dep.label}]`); + this._logMessage(`* _spinDepFromDep() filename=[${dep.filename}]`); + const spinObj = new SpinObject(dep.filename, '??WHY??'); + const newRawDep: SpinDependency = new SpinDependency(dep.filename, spinObj, '???'); + return newRawDep; + } + + private async _dumpElement(callerID: string, element?: Dependency) { + let dumpElement = element; + if (element !== undefined) { + if (element instanceof Promise) { + this._logMessage(`+ (DBG) ObjDep: _dumpElement() called with Promise!!`); + dumpElement = await element; // Unwrap the promise + } + this._logMessage( + `* ${callerID} DUMP LBL=[${dumpElement.label}], OBJ=[${dumpElement.objName}], [${this._collapseStateString( + dumpElement.collapsibleState + )}], depth=(${dumpElement.depth})` + ); + } else { + this._logMessage(`ERROR: _dumpElement(undefined) NOT supported!`); + } + } } // class ProviderResult: Dependency | undefined | null | Thenable class SpinObject { - public readonly baseName: string = ''; + public readonly fileName: string = ''; public readonly knownAs: string = ''; - constructor( - public readonly fileBaseName: string, - public objName: string - ) { - this.baseName = fileBaseName; - this.knownAs = objName; + constructor(fileBaseName: string, objInstanceName: string) { + this.fileName = fileBaseName; + this.knownAs = objInstanceName; + } +} + +export class SpinDependency { + private _id: string = ''; + private _basename: string = ''; + private _fileSpec: string = ''; + private _fileName: string = ''; + private _overrideChildCount: number = -1; + private _spinFile: SpinObject; + private _children: SpinDependency[] = []; + private _isFileMissing: boolean = false; + private _isExplored: boolean = false; + + constructor(fileSpec: string, spinFile: SpinObject, id: string, copy?: SpinDependency) { + if (copy !== undefined) { + // do COPY construction instead of normal + this._id = copy.id; + this._spinFile = copy.spinFile(); + this._fileSpec = this._spinFile.fileName; + this._fileName = path.basename(this._fileSpec); + this._basename = this._fileName.replace('.spin2', ''); + // and deep copy the children !! + if (copy.hasChildren) { + for (let index = 0; index < copy.childCount; index++) { + const copyChild = copy.children[index]; + this.children.push(new SpinDependency('', undefined, '', copyChild)); + } + } + } else { + // do NORMAL construction + this._id = id; + this._spinFile = spinFile; + this._fileSpec = spinFile.fileName; + this._fileName = path.basename(this._fileSpec); + this._basename = this._fileName.replace('.spin2', ''); + } + } + + public addChild(child: SpinDependency) { + this._children.push(child); + } + + get id(): string { + return this._id; + } + + private spinFile(): SpinObject { + return this._spinFile; + } + + public replaceId(newId: string) { + this._id = newId; + } + + get knownAs(): string { + return this._spinFile.knownAs; + } + + get depth(): number { + return this._id.length - 1; + } + + get childCount(): number { + let desiredCount = this._children.length; + if (this._overrideChildCount != -1) { + desiredCount = this._overrideChildCount; + } + return desiredCount; + } + + get hasChildren(): boolean { + return this.childCount > 0 ? true : false; + } + + public forceChildCount(newCount: number) { + this._overrideChildCount = newCount; + } + + get children(): SpinDependency[] { + return this._children; + } + + get name(): string { + return this._fileSpec; + } + + get isFileMissing(): boolean { + // return file-missing state + return this._isFileMissing; + } + + public setFileMissing() { + // record file-missing state + this._isFileMissing = true; + } + + get isExplored(): boolean { + return this._isExplored; + } + + public setExplored() { + this._isExplored = true; + } + + /* + public replaceChildren(newChildren: SpinDependency[]) { + this._children = newChildren; + } + */ + + public toString(callerId: string = undefined): string { + let descriptionString: string = ''; + const callerIdStr: string = callerId !== undefined ? `${callerId} ` : ''; + descriptionString = `${callerIdStr} ${this.id} ${this._fileName} [${this.knownAs}] ${this.childCount} chidren`; + return descriptionString; } } export class Dependency extends vscode.TreeItem { + // TreeItem notes: id:string, label, resourceUri, tooltip, iconPath, description, contextValue, command, collapsibleState, accessibilityInformation + // Treeitem constructor(label, ?collapsibleState) OR constructor(resourceUri, ?collapsibleState) + // NOTE: in TreeView 'label' is bold white, 'description' is light-grey text + // //private icon: vscode.ThemeIcon = new vscode.ThemeIcon("file-code", "#FF8000"); //private icon: vscode.ThemeIcon = new vscode.ThemeIcon("symbol-field"); // hrmf... blue //private icon: vscode.ThemeIcon = new vscode.ThemeIcon("symbol-enum"); // nice, orange! + //private icon: vscode.ThemeIcon = new vscode.ThemeIcon("symbol-color"); // hrmf, grey + //private icon: vscode.ThemeIcon = new vscode.ThemeIcon("symbol-enum-member"); // hrmf... blue + //private icon: vscode.ThemeIcon = new vscode.ThemeIcon("symbol-method"); // hrmf, purple! + //private icon: vscode.ThemeIcon = new vscode.ThemeIcon("symbol-variable"); // hrmf, blue! + //private icon: vscode.ThemeIcon = new vscode.ThemeIcon("symbol-constant"); // hrmf, grey + //private icon: vscode.ThemeIcon = new vscode.ThemeIcon("symbol-array"); // hrmf, grey //private icon: vscode.ThemeIcon = new vscode.ThemeIcon("symbol-structure"); // hrmf, no color (white) private icon: vscode.ThemeIcon = new vscode.ThemeIcon('symbol-class'); // nice, orange! - private basename: string = ''; - public readonly descriptionString: string = ''; + private _basename: string = ''; + private _filename: string = ''; + private _depth: number = 0; + private _objName: string; private fileMissing: boolean = false; + private _parent: Dependency | undefined = undefined; + public readonly descriptionString: string = ''; // map our fields to underlying TreeItem constructor( - public readonly label: string, - private objName: string, - public readonly collapsibleState: vscode.TreeItemCollapsibleState + label: string | vscode.TreeItemLabel, + objName: string, + collapsibleState: vscode.TreeItemCollapsibleState, + depth: number, + userId: string, + parent: Dependency | undefined = undefined ) { // element label is filebasename // element description is object name in containing file // element tooltip is filename (object name) + // element depth is nesting level where 0 means top super(label, collapsibleState); - this.description = this.objName; - this.descriptionString = this.objName; - this.basename = label.replace('.spin2', ''); - this.basename = this.basename.replace('.spin', ''); + this._objName = objName; + this.description = objName; //LIVE + //this.description = `[${userId}] ${objName}`; // TESTING + this._parent = parent; + this._depth = depth; + this.descriptionString = objName; + this._filename = label.toString(); // save 'given' name + this._basename = label.toString(); // save 'given' name + this.id = userId; + if (label !== undefined) { + this._basename = label.toString().replace('.spin2', ''); + this._basename = this._basename.replace('.spin', ''); + } if (objName.includes('top-file')) { this.tooltip = `This is the project top-most file`; } else { - this.tooltip = `An instance of ${this.basename} known as ${this.objName}`; + this.tooltip = `An instance of ${this._basename} known as ${this._objName}`; } //this.iconPath = { light: new vscode.ThemeIcon("file-code").id, dark: new vscode.ThemeIcon("file-code").id }; //this.resourceUri = new vscode.ThemeIcon('file-code'). //this.icon = new vscode.ThemeIcon("file-code"); // nope!! + /* + if (this.collapsibleState == ELEM_NONE) { + this.icon = new vscode.ThemeIcon('symbol-enum'); + } else { + this.icon = new vscode.ThemeIcon('symbol-class'); + } + */ this.iconPath = this.icon; this.contextValue = 'dependency'; this.command = { @@ -556,21 +1281,44 @@ export class Dependency extends vscode.TreeItem { }; } - isFileMissing(): boolean { + get filename(): string { + return this._filename; + } + + get objName(): string { + return this._objName; + } + + get depth(): number { + return this._depth; + } + + get parentDep(): Dependency | undefined { + return this._parent; + } + + get isFileMissing(): boolean { + // record file-missing state return this.fileMissing; } - setFileMissing() { + public setFileMissing() { this.fileMissing = true; const origText = this.description; - if (origText) { + if (origText !== undefined) { this.description = `${origText} - MISSING FILE`; } else { this.description = `- MISSING FILE`; } } + public toString(callerId: string = undefined): string { + let descriptionString: string = ''; + const callerIdStr: string = callerId !== undefined ? `${callerId} ` : ''; + descriptionString = `${callerIdStr} ${this.id} ${this.filename} ${this.descriptionString}`; + return descriptionString; + } - removeIcon() { + public removeIcon() { // take off icon if we are showing dep as error/warning message this.iconPath = undefined; } diff --git a/spin2/client/src/test/helper.ts b/spin2/client/src/test/helper.ts index 556cda7..3b30a81 100644 --- a/spin2/client/src/test/helper.ts +++ b/spin2/client/src/test/helper.ts @@ -66,7 +66,7 @@ export function isHash(possHash: any): boolean { export function showObject(responseObject: any, level: number = 0, idString: string = undefined): string { const answerStrings: string[] = []; const padding: string = ' '.repeat(level * 2); - const bIsTopeLevel: boolean = idString == undefined; + const bIsTopeLevel: boolean = idString === undefined; const typeString: string = objectTypeString(responseObject); if (bIsTopeLevel) { answerStrings.push(``); diff --git a/spin2/images/commandSearch.png b/spin2/images/commandSearch.png new file mode 100644 index 0000000..f093c46 Binary files /dev/null and b/spin2/images/commandSearch.png differ diff --git a/spin2/images/newDocGeneration.png b/spin2/images/newDocGeneration.png new file mode 100644 index 0000000..4adc60b Binary files /dev/null and b/spin2/images/newDocGeneration.png differ diff --git a/spin2/images/vscode-dark-bgColoring.png b/spin2/images/vscode-dark-bgColoring.png new file mode 100644 index 0000000..30e9a86 Binary files /dev/null and b/spin2/images/vscode-dark-bgColoring.png differ diff --git a/spin2/images/vscode-light-bgColoring.png b/spin2/images/vscode-light-bgColoring.png new file mode 100644 index 0000000..7fa08d9 Binary files /dev/null and b/spin2/images/vscode-light-bgColoring.png differ diff --git a/spin2/package.json b/spin2/package.json index aa5333d..ea849ba 100644 --- a/spin2/package.json +++ b/spin2/package.json @@ -5,7 +5,7 @@ "icon": "images/Propeller.ico", "author": "IronSheep", "license": "MIT", - "version": "2.2.14", + "version": "2.2.15", "repository": { "type": "git", "url": "https://github.com/ironsheep/P2-vscode-langserv-extension" @@ -38,6 +38,10 @@ "main": "./client/out/extension", "contributes": { "commands": [ + { + "command": "spinExtension.generate.hierarchy.file", + "title": "Spin2: Generate hierarchy documentation file from spin source" + }, { "command": "spinExtension.generate.doc.comment", "title": "Spin2: Generate and insert documentation comment" @@ -79,6 +83,16 @@ "title": "Spin2: Refresh object dependencies view", "icon": "$(refresh)" }, + { + "command": "spinExtension.objectDependencies.expandAll", + "title": "Spin2: Expand All", + "icon": "$(expand-all)" + }, + { + "command": "spinExtension.objectDependencies.collapseAll", + "title": "Spin2: Collapse All", + "icon": "$(collapse-all)" + }, { "command": "spinExtension.objectDependencies.activateFile", "title": "Spin2: Open source code for this object", @@ -478,6 +492,11 @@ } ], "keybindings": [ + { + "key": "Ctrl+Alt+r", + "command": "spinExtension.generate.hierarchy.file", + "when": "editorTextFocus && !editorReadonly && !suggestWidgetVisible && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2)" + }, { "key": "Ctrl+Alt+d", "command": "spinExtension.generate.documentation.file", @@ -491,39 +510,39 @@ { "key": "tab", "command": "spinExtension.elasticTabstops.indentTabStop", - "when": "editorTextFocus && !editorReadonly && !suggestWidgetVisible && !editorHoverFocused && !editorHoverVisible && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && !suggestWidgetVisible && !editorHoverFocused && !editorHoverVisible && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "key": "Shift+tab", "command": "spinExtension.elasticTabstops.outdentTabStop", - "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "key": "Ctrl+Alt+tab", "command": "spinExtension.elasticTabstops.generate.tabStops.comment", - "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.rotate", "key": "insert", "mac": "F13", - "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.deleteLeft", "key": "backspace", - "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.deleteRight", "key": "delete", - "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.toggle", "key": "Ctrl+Shift+i", "mac": "Cmd+Shift+i", - "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" } ], "menus": { @@ -532,6 +551,16 @@ "command": "spinExtension.objectDependencies.refreshEntry", "when": "view == spinExtension.objectDependencies", "group": "navigation" + }, + { + "command": "spinExtension.objectDependencies.expandAll", + "when": "view == spinExtension.objectDependencies && runtime.spinExtension.objectDeps.showingTopOnly == true", + "group": "navigation" + }, + { + "command": "spinExtension.objectDependencies.collapseAll", + "when": "view == spinExtension.objectDependencies && runtime.spinExtension.objectDeps.showingTopOnly == false", + "group": "navigation" } ] }, @@ -657,6 +686,10 @@ { "id": "illegalUse", "description": "Identifies keywords where they shouldn't be!" + }, + { + "id": "disabled", + "description": "Identifies code disabled by preprocessor" } ], "semanticTokenScopes": [ @@ -678,7 +711,7 @@ "name": "Object Dependencies", "icon": "$(type-hierarchy)", "contextualTitle": "Propeller Spin2 Development", - "when": "spinExtension.objectDeps.enabled" + "when": "runtime.spinExtension.objectDeps.enabled" } ] }, diff --git a/spin2/scripts/LIVE-package.json b/spin2/scripts/LIVE-package.json index aa5333d..ea849ba 100644 --- a/spin2/scripts/LIVE-package.json +++ b/spin2/scripts/LIVE-package.json @@ -5,7 +5,7 @@ "icon": "images/Propeller.ico", "author": "IronSheep", "license": "MIT", - "version": "2.2.14", + "version": "2.2.15", "repository": { "type": "git", "url": "https://github.com/ironsheep/P2-vscode-langserv-extension" @@ -38,6 +38,10 @@ "main": "./client/out/extension", "contributes": { "commands": [ + { + "command": "spinExtension.generate.hierarchy.file", + "title": "Spin2: Generate hierarchy documentation file from spin source" + }, { "command": "spinExtension.generate.doc.comment", "title": "Spin2: Generate and insert documentation comment" @@ -79,6 +83,16 @@ "title": "Spin2: Refresh object dependencies view", "icon": "$(refresh)" }, + { + "command": "spinExtension.objectDependencies.expandAll", + "title": "Spin2: Expand All", + "icon": "$(expand-all)" + }, + { + "command": "spinExtension.objectDependencies.collapseAll", + "title": "Spin2: Collapse All", + "icon": "$(collapse-all)" + }, { "command": "spinExtension.objectDependencies.activateFile", "title": "Spin2: Open source code for this object", @@ -478,6 +492,11 @@ } ], "keybindings": [ + { + "key": "Ctrl+Alt+r", + "command": "spinExtension.generate.hierarchy.file", + "when": "editorTextFocus && !editorReadonly && !suggestWidgetVisible && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2)" + }, { "key": "Ctrl+Alt+d", "command": "spinExtension.generate.documentation.file", @@ -491,39 +510,39 @@ { "key": "tab", "command": "spinExtension.elasticTabstops.indentTabStop", - "when": "editorTextFocus && !editorReadonly && !suggestWidgetVisible && !editorHoverFocused && !editorHoverVisible && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && !suggestWidgetVisible && !editorHoverFocused && !editorHoverVisible && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "key": "Shift+tab", "command": "spinExtension.elasticTabstops.outdentTabStop", - "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "key": "Ctrl+Alt+tab", "command": "spinExtension.elasticTabstops.generate.tabStops.comment", - "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.rotate", "key": "insert", "mac": "F13", - "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.deleteLeft", "key": "backspace", - "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.deleteRight", "key": "delete", - "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.toggle", "key": "Ctrl+Shift+i", "mac": "Cmd+Shift+i", - "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" } ], "menus": { @@ -532,6 +551,16 @@ "command": "spinExtension.objectDependencies.refreshEntry", "when": "view == spinExtension.objectDependencies", "group": "navigation" + }, + { + "command": "spinExtension.objectDependencies.expandAll", + "when": "view == spinExtension.objectDependencies && runtime.spinExtension.objectDeps.showingTopOnly == true", + "group": "navigation" + }, + { + "command": "spinExtension.objectDependencies.collapseAll", + "when": "view == spinExtension.objectDependencies && runtime.spinExtension.objectDeps.showingTopOnly == false", + "group": "navigation" } ] }, @@ -657,6 +686,10 @@ { "id": "illegalUse", "description": "Identifies keywords where they shouldn't be!" + }, + { + "id": "disabled", + "description": "Identifies code disabled by preprocessor" } ], "semanticTokenScopes": [ @@ -678,7 +711,7 @@ "name": "Object Dependencies", "icon": "$(type-hierarchy)", "contextualTitle": "Propeller Spin2 Development", - "when": "spinExtension.objectDeps.enabled" + "when": "runtime.spinExtension.objectDeps.enabled" } ] }, diff --git a/spin2/scripts/TEST-package.json b/spin2/scripts/TEST-package.json index 06aa003..6770988 100644 --- a/spin2/scripts/TEST-package.json +++ b/spin2/scripts/TEST-package.json @@ -4,7 +4,7 @@ "description": "P1 and P2 Spin/Pasm Syntax/Semantic Highlighting w/Code Outline, Object Outline and Custom tabbing support", "author": "IronSheep", "license": "MIT", - "version": "2.2.14", + "version": "2.2.15", "repository": { "type": "git", "url": "https://github.com/ironsheep/P2-vscode-langserv-extension" @@ -32,6 +32,10 @@ "main": "./client/out/extension", "contributes": { "commands": [ + { + "command": "spinExtension.generate.hierarchy.file", + "title": "Spin2: Generate hierarchy documentation file from spin source" + }, { "command": "spinExtension.generate.doc.comment", "title": "Spin2: Generate and insert documentation comment" @@ -73,6 +77,16 @@ "title": "Spin2: Refresh object dependencies view", "icon": "$(refresh)" }, + { + "command": "spinExtension.objectDependencies.expandAll", + "title": "Spin2: Expand All", + "icon": "$(expand-all)" + }, + { + "command": "spinExtension.objectDependencies.collapseAll", + "title": "Spin2: Collapse All", + "icon": "$(collapse-all)" + }, { "command": "spinExtension.objectDependencies.activateFile", "title": "Spin2: Open source code for this object", @@ -93,11 +107,7 @@ "spinExtension.trace.server": { "scope": "window", "type": "string", - "enum": [ - "off", - "messages", - "verbose" - ], + "enum": ["off", "messages", "verbose"], "default": "off", "description": "Traces the communication between VSCode client and the spin2 language server." }, @@ -131,11 +141,7 @@ "spinExtension.elasticTabstops.choice": { "type": "string", "default": "PropellerTool", - "enum": [ - "PropellerTool", - "IronSheep", - "User1" - ], + "enum": ["PropellerTool", "IronSheep", "User1"], "description": "Select the set of Tabstops you wish to use." }, "spinExtension.elasticTabstops.blocks.PropellerTool": { @@ -149,78 +155,22 @@ }, "default": { "con": { - "tabStops": [ - 2, - 8, - 16, - 18, - 32, - 56, - 78, - 80 - ] + "tabStops": [2, 8, 16, 18, 32, 56, 78, 80] }, "var": { - "tabStops": [ - 2, - 8, - 22, - 32, - 56, - 80 - ] + "tabStops": [2, 8, 22, 32, 56, 80] }, "obj": { - "tabStops": [ - 2, - 8, - 16, - 18, - 32, - 56, - 80 - ] + "tabStops": [2, 8, 16, 18, 32, 56, 80] }, "pub": { - "tabStops": [ - 2, - 4, - 6, - 8, - 10, - 12, - 14, - 16, - 32, - 56, - 80 - ] + "tabStops": [2, 4, 6, 8, 10, 12, 14, 16, 32, 56, 80] }, "pri": { - "tabStops": [ - 2, - 4, - 6, - 8, - 10, - 12, - 14, - 16, - 32, - 56, - 80 - ] + "tabStops": [2, 4, 6, 8, 10, 12, 14, 16, 32, 56, 80] }, "dat": { - "tabStops": [ - 8, - 14, - 24, - 32, - 48, - 56, - 80 - ] + "tabStops": [8, 14, 24, 32, 48, 56, 80] } } }, @@ -235,87 +185,22 @@ }, "default": { "con": { - "tabStops": [ - 4, - 8, - 16, - 20, - 32, - 44, - 56, - 68, - 80 - ] + "tabStops": [4, 8, 16, 20, 32, 44, 56, 68, 80] }, "var": { - "tabStops": [ - 4, - 12, - 24, - 28, - 32, - 44, - 56, - 68, - 80 - ] + "tabStops": [4, 12, 24, 28, 32, 44, 56, 68, 80] }, "obj": { - "tabStops": [ - 4, - 8, - 16, - 20, - 32, - 44, - 56, - 68, - 80 - ] + "tabStops": [4, 8, 16, 20, 32, 44, 56, 68, 80] }, "pub": { - "tabStops": [ - 4, - 8, - 12, - 16, - 20, - 24, - 28, - 32, - 56, - 80 - ] + "tabStops": [4, 8, 12, 16, 20, 24, 28, 32, 56, 80] }, "pri": { - "tabStops": [ - 4, - 8, - 12, - 16, - 20, - 24, - 28, - 32, - 56, - 80 - ] + "tabStops": [4, 8, 12, 16, 20, 24, 28, 32, 56, 80] }, "dat": { - "tabStops": [ - 4, - 16, - 20, - 24, - 28, - 48, - 52, - 56, - 60, - 64, - 68, - 80 - ] + "tabStops": [4, 16, 20, 24, 28, 48, 52, 56, 60, 64, 68, 80] } } }, @@ -330,80 +215,22 @@ }, "default": { "con": { - "tabStops": [ - 2, - 8, - 16, - 18, - 32, - 56, - 76, - 77, - 78, - 80 - ] + "tabStops": [2, 8, 16, 18, 32, 56, 76, 77, 78, 80] }, "var": { - "tabStops": [ - 2, - 8, - 22, - 32, - 56, - 80 - ] + "tabStops": [2, 8, 22, 32, 56, 80] }, "obj": { - "tabStops": [ - 2, - 8, - 16, - 18, - 32, - 56, - 80 - ] + "tabStops": [2, 8, 16, 18, 32, 56, 80] }, "pub": { - "tabStops": [ - 2, - 4, - 6, - 8, - 10, - 12, - 14, - 16, - 32, - 56, - 80 - ] + "tabStops": [2, 4, 6, 8, 10, 12, 14, 16, 32, 56, 80] }, "pri": { - "tabStops": [ - 2, - 4, - 6, - 8, - 10, - 12, - 14, - 16, - 32, - 56, - 80 - ] + "tabStops": [2, 4, 6, 8, 10, 12, 14, 16, 32, 56, 80] }, "dat": { - "tabStops": [ - 8, - 14, - 24, - 32, - 48, - 56, - 80 - ] + "tabStops": [8, 14, 24, 32, 48, 56, 80] } } } @@ -472,6 +299,11 @@ } ], "keybindings": [ + { + "key": "Ctrl+Alt+r", + "command": "spinExtension.generate.hierarchy.file", + "when": "editorTextFocus && !editorReadonly && !suggestWidgetVisible && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2)" + }, { "key": "Ctrl+Alt+d", "command": "spinExtension.generate.documentation.file", @@ -485,39 +317,39 @@ { "key": "tab", "command": "spinExtension.elasticTabstops.indentTabStop", - "when": "editorTextFocus && !editorReadonly && !suggestWidgetVisible && !editorHoverFocused && !editorHoverVisible && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && !suggestWidgetVisible && !editorHoverFocused && !editorHoverVisible && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "key": "Shift+tab", "command": "spinExtension.elasticTabstops.outdentTabStop", - "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "key": "Ctrl+Alt+tab", "command": "spinExtension.elasticTabstops.generate.tabStops.comment", - "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && !editorTabMovesFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.rotate", "key": "insert", "mac": "F13", - "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.deleteLeft", "key": "backspace", - "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.deleteRight", "key": "delete", - "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorTextFocus && !editorReadonly && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" }, { "command": "spinExtension.insertMode.toggle", "key": "Ctrl+Shift+i", "mac": "Cmd+Shift+i", - "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && config.spinExtension.elasticTabstops.enable == true" + "when": "editorFocus && (editorLangId == spin || editorLangId == spin2) && runtime.spinExtension.elasticTabstops.enable == true" } ], "menus": { @@ -526,6 +358,16 @@ "command": "spinExtension.objectDependencies.refreshEntry", "when": "view == spinExtension.objectDependencies", "group": "navigation" + }, + { + "command": "spinExtension.objectDependencies.expandAll", + "when": "view == spinExtension.objectDependencies && runtime.spinExtension.objectDeps.showingTopOnly == true", + "group": "navigation" + }, + { + "command": "spinExtension.objectDependencies.collapseAll", + "when": "view == spinExtension.objectDependencies && runtime.spinExtension.objectDeps.showingTopOnly == false", + "group": "navigation" } ] }, @@ -571,26 +413,14 @@ "languages": [ { "id": "spin2", - "aliases": [ - "spin2", - "Spin2", - "SPIN2" - ], - "extensions": [ - ".spin2" - ], + "aliases": ["spin2", "Spin2", "SPIN2"], + "extensions": [".spin2"], "configuration": "./spin2.language-configuration.json" }, { "id": "spin", - "aliases": [ - "spin", - "Spin", - "SPIN" - ], - "extensions": [ - ".spin" - ], + "aliases": ["spin", "Spin", "SPIN"], + "extensions": [".spin"], "configuration": "./spin1.language-configuration.json" } ], @@ -651,17 +481,17 @@ { "id": "illegalUse", "description": "Identifies keywords where they shouldn't be!" + }, + { + "id": "disabled", + "description": "Identifies code disabled by preprocessor" } ], "semanticTokenScopes": [ { "scopes": { - "returnValue": [ - "meta.returntype.spin" - ], - "storageType": [ - "storage.type.pasm.spin2" - ] + "returnValue": ["meta.returntype.spin"], + "storageType": ["storage.type.pasm.spin2"] } } ], @@ -672,7 +502,7 @@ "name": "Object Dependencies", "icon": "$(type-hierarchy)", "contextualTitle": "Propeller Spin2 Development", - "when": "spinExtension.objectDeps.enabled" + "when": "runtime.spinExtension.objectDeps.enabled" } ] }, @@ -698,8 +528,8 @@ "@types/mocha": "^9.1.0", "@types/node": "^16.18.34", "@typescript-eslint/eslint-plugin": "^6.17.0", - "@typescript-eslint/parser": "^6.17.0", - "eslint": "^8.56.0", + "@typescript-eslint/parser": "^6.17.0", + "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-config-standard-with-typescript": "^43.0.0", "eslint-plugin-import": "^2.29.1", diff --git a/spin2/scripts/bashrc b/spin2/scripts/bashrc new file mode 100644 index 0000000..fdde72b --- /dev/null +++ b/spin2/scripts/bashrc @@ -0,0 +1,20 @@ +# ~/.bashrc: executed by bash(1) for non-login shells. + +# Note: PS1 and umask are already set in /etc/profile. You should not +# need this unless you want different defaults for root. +# PS1='${debian_chroot:+($debian_chroot)}\h:\w\$ ' +# umask 022 +set -o vi + +# You may uncomment the following lines if you want `ls' to be colorized: +export LS_OPTIONS='--color=auto' +eval "$(dircolors)" +alias ls='ls $LS_OPTIONS' +alias ll='ls $LS_OPTIONS -l' +alias lsf='ls $LS_OPTIONS -F' +# alias l='ls $LS_OPTIONS -lA' +# +# Some more alias to avoid making mistakes: +# alias rm='rm -i' +# alias cp='cp -i' +# alias mv='mv -i' diff --git a/spin2/server/src/DocumentProcessor.ts b/spin2/server/src/DocumentProcessor.ts index f5d46d9..6d9487a 100644 --- a/spin2/server/src/DocumentProcessor.ts +++ b/spin2/server/src/DocumentProcessor.ts @@ -197,6 +197,7 @@ export default class DocumentProcessor { async process(document: TextDocument, isInclude: boolean = false, skipIncludeScan: boolean = false): Promise { this.ctx.logger.log(`TRC: DP.process(${document.uri}`); const docFSpec: string = fileSpecFromURI(document.uri); + const fileName: string = path.basename(docFSpec); this.ctx.logger.log(`TRC: DP.process() isInclude=${isInclude}, skipIncludeScan=${skipIncludeScan}, docFSpec=[${docFSpec}]`); // we keep a single DocumentFindings for each document.uri -> docFSpec @@ -229,7 +230,7 @@ export default class DocumentProcessor { // do first pass parse to fill in DocumentFindings with list of object references (includes) const languageId: string = isSpin1File(docFSpec) ? 'Spin1' : 'Spin2'; this.ctx.logger.log(`TRC: Object Scan of ${languageId} Document: ${docFSpec}`); - this.spin2ObjectReferenceParser.locatReferencedObjects(document, currDocumentFindings); // for objects included + this.spin2ObjectReferenceParser.locateReferencedObjects(document, currDocumentFindings); // for objects included } const includedFiles: string[] = skipIncludeScan == false ? currDocumentFindings.includeFilenames() : []; this.ctx.logger.log(`TRC: [${currDocumentFindings.instanceName()}] includedFiles=[${includedFiles}]`); @@ -274,11 +275,12 @@ export default class DocumentProcessor { // this.ctx.logger.log(`TRC: -- STEP incorporate included docs into maps ...`); const objectReferences: Map = - skipIncludeScan == false ? currDocumentFindings.includeObjectNamesByFilename() : new Map(); + skipIncludeScan == false ? currDocumentFindings.includedObjectNamesByFilename() : new Map(); + const includes: string[] = currDocumentFindings.includeNamesForFilename(fileName); this.ctx.logger.log( `TRC: [${currDocumentFindings.instanceName()}] nameHashKeys=[${Array.from(objectReferences.keys())}], nameHashValues=[${Array.from( objectReferences.values() - )}]` + )}], includedFiles=[${includes}]` ); const objectNames: string[] = Array.from(objectReferences.keys()); const objectFileNames: string[] = Array.from(objectReferences.values()); @@ -291,6 +293,8 @@ export default class DocumentProcessor { this.ctx.logger.log( `TRC: [${currDocumentFindings.instanceName()}] clear() previous findings but NOT include info so can load included documents` ); + // + // connnect our child objects so document will hightlight child references for (let index = 0; index < objectNames.length; index++) { const objectName = objectNames[index]; const objectSpinFilename = objectFileNames[index]; @@ -323,6 +327,40 @@ export default class DocumentProcessor { this.ctx.logger.log(`TRC: NO include filename matches found!`); } } + // + // load symbols from included files so document will hightlight these symbols as well + for (let index = 0; index < includes.length; index++) { + const includeFilename = includes[index]; + const includeSpinFilename = includeFilename; + let matchFilename: string = includeFilename.toLowerCase(); + if (!matchFilename?.toLowerCase().includes('.spin')) { + matchFilename = `${matchFilename}.`.toLowerCase(); + } + this.ctx.logger.log(`TRC: MATCHING includeFilename=[${includeFilename}], matchFilename=[${matchFilename}]`); + let bFound: boolean = false; + for (let index = 0; index < currDocumentInProcess.referencedFileSpecsCount; index++) { + const fSpec: string = currDocumentInProcess.referencedFileSpec(index); + if (fSpec.toLowerCase().includes(includeFilename)) { + bFound = true; + // located, now add parse results for include to this document map of includes + const processedInclude = this.ctx.docsByFSpec.get(fSpec); + if (processedInclude) { + this.ctx.logger.log( + `TRC: merging symbols from [${includeFilename}]: [${processedInclude.parseResult.instanceName()}]: into=[${currDocumentFindings.instanceName()}]` + ); + currDocumentFindings.enableLogging(this.ctx, true); + currDocumentFindings.loadIncludeSymbols(includeFilename, processedInclude.parseResult); + currDocumentFindings.enableLogging(this.ctx, false); + //this.ctx.logger.log(`TRC: Added include findings for ${objectName}: "${objectSpinFilename}"`); + } else { + this.ctx.logger.log(`TRC: NO findings found for ${includeFilename}: "${includeSpinFilename}": uri=[${fSpec}]`); + } + } + } + if (!bFound) { + this.ctx.logger.log(`TRC: NO include filename matches found!`); + } + } } else { this.ctx.logger.log(`TRC: DP.process() [${currDocumentFindings.instanceName()}] No included files, just do final parse`); } diff --git a/spin2/server/src/parser/spin.objectReferenceParser.ts b/spin2/server/src/parser/spin.objectReferenceParser.ts index c48a8d4..6c81359 100644 --- a/spin2/server/src/parser/spin.objectReferenceParser.ts +++ b/spin2/server/src/parser/spin.objectReferenceParser.ts @@ -2,7 +2,7 @@ // server/src/parser/spin.objectReferenceParser.ts import { TextDocument } from 'vscode-languageserver-textdocument'; -import { Context } from '../context'; +import { Context, ServerBehaviorConfiguration } from '../context'; //import { semanticConfiguration, reloadSemanticConfiguration } from "./spin2.extension.configuration"; import { DocumentFindings, RememberedToken } from './spin.semantic.findings'; @@ -10,6 +10,7 @@ import { Spin2ParseUtils } from './spin2.utils'; import { isSpin1File } from './lang.utils'; import { eParseState } from './spin.common'; import { ExtensionUtils } from '../parser/spin.extension.utils'; +import path = require('path'); // ---------------------------------------------------------------------------- // Semantic Highlighting Provider @@ -31,7 +32,7 @@ export class Spin2ObjectReferenceParser { private bLogStarted: boolean = false; // adjust following true/false to show specific parsing debug - private spin2ObjectLocatorLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private showCON: boolean = true; private showOBJ: boolean = true; private showPAsmCode: boolean = true; @@ -40,10 +41,13 @@ export class Spin2ObjectReferenceParser { private semanticFindings: DocumentFindings = new DocumentFindings(); // this gets replaced private currentFilespec: string = ''; private isSpin1Document: boolean = false; + private includingDocumentFilename: string = ''; + private configuration: ServerBehaviorConfiguration; public constructor(protected readonly ctx: Context) { - this.extensionUtils = new ExtensionUtils(ctx, this.spin2ObjectLocatorLogEnabled); - if (this.spin2ObjectLocatorLogEnabled) { + this.extensionUtils = new ExtensionUtils(ctx, this.isDebugLogEnabled); + this.configuration = this.ctx.parserConfig; // ensure we have latest + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; //Create output channel @@ -53,27 +57,33 @@ export class Spin2ObjectReferenceParser { } } - //this.semanticFindings = new DocumentFindings(this.spin2ObjectLocatorLogEnabled, this.spin1log); + //this.semanticFindings = new DocumentFindings(this.isDebugLogEnabled, this.spin1log); } public docFindings(): DocumentFindings { return this.semanticFindings; } - public locatReferencedObjects(document: TextDocument, findings: DocumentFindings): void { + public locateReferencedObjects(document: TextDocument, findings: DocumentFindings): void { this.semanticFindings = findings; - if (this.spin2ObjectLocatorLogEnabled) { + if (this.isDebugLogEnabled) { this.semanticFindings.enableLogging(this.ctx); } this.isSpin1Document = isSpin1File(document.uri); this.currentFilespec = document.uri; - this._logMessage(`* locatReferencedObjects(${this.currentFilespec})`); - + this.includingDocumentFilename = path.basename(this.currentFilespec); + this._logMessage(`* locateReferencedObjects(${this.currentFilespec})`); this._parseText(document.getText()); } private _parseText(text: string): IParsedToken[] { // parse our entire file + // if user has enabled flexspin then we hunt for #includes, too! + this._logMessage(`++ SORP maxNumberOfReportedIssues=(${this.ctx.parserConfig.maxNumberOfReportedIssues})`); + this._logMessage(`++ SORP highlightFlexspinDirectives=(${this.ctx.parserConfig.highlightFlexspinDirectives})`); + if (this.ctx.parserConfig.maxNumberOfReportedIssues == -1) { + this._logMessage('++ SORP WARNING: client configurataion NOT yet available...'); + } const lines = text.split(/\r\n|\r|\n/); let currState: eParseState = eParseState.inCon; // compiler defaults to CON at start let priorState: eParseState = currState; @@ -177,6 +187,19 @@ export class Spin2ObjectReferenceParser { } else if (trimmedLine.startsWith("'")) { // process single line non-doc comment continue; + } else if (trimmedLine.startsWith('#include') && this.ctx.parserConfig.highlightFlexspinDirectives == true) { + // user has enabled FLexspin so let's handle #includes, too + // Ex: #include "config.spin2" + const lineParts: string[] = trimmedLine.split('"').filter(Boolean); + this._logMessage(`- scan Ln#${i + 1} #include lineParts=[${lineParts}](${lineParts.length})`); + // lineParts should now be ["#include ", "config.spin2"] + if (lineParts.length >= 2) { + const filename: string = lineParts[1]; + this._logMessage(` -- ADD file [${filename}] included by [${this.includingDocumentFilename}]`); + this.semanticFindings.recordIncludeByWhom(this.includingDocumentFilename, filename); + } else { + this._logMessage(`ERROR: bad parse of #include Ln#${i + 1} [${trimmedLine}]`); + } } else if (sectionStatus.isSectionStart) { // mark end of method, if we were in a method currState = sectionStatus.inProgressStatus; @@ -234,11 +257,12 @@ export class Spin2ObjectReferenceParser { // HAVE DIGIT_NO_VALUE = -2 ' digit value when NOT [0-9] // -or- _clkmode = xtal1 + pll16x // - if (line.substr(startingOffset).length > 1) { + const isPreprocessorStatement: boolean = this.parseUtils.lineStartsWithFlexspinPreprocessorDirective(line); + if (isPreprocessorStatement == false && line.substr(startingOffset).length > 1) { //skip Past Whitespace let currentOffset: number = this.parseUtils.skipWhite(line, startingOffset); const nonCommentConstantLine = this.parseUtils.getNonCommentLineRemainder(currentOffset, line); - this._logCON(` - Ln#${lineNbr} GetCONDecl nonCommentConstantLine=[${nonCommentConstantLine}]`); + this._logCON(` - Ln#${lineNbr} SORP GetCONDecl nonCommentConstantLine=[${nonCommentConstantLine}]`); const haveEnumDeclaration: boolean = nonCommentConstantLine.startsWith('#'); const containsMultiAssignments: boolean = nonCommentConstantLine.indexOf(',') != -1; @@ -321,7 +345,7 @@ export class Spin2ObjectReferenceParser { // get line parts - we only care about first one const overrideParts: string[] = remainingNonCommentLineStr.split('|').filter(Boolean); const lineParts: string[] = overrideParts[0].split(':').filter(Boolean); - this._logOBJ(' -- GLBL GetOBJDecl lineParts=[' + lineParts + ']'); + this._logOBJ(' -- SORP GLBL GetOBJDecl lineParts=[' + lineParts + ']'); let instanceNamePart = lineParts[0].trim(); // if we have instance array declaration, then remove it if (instanceNamePart.includes('[')) { @@ -363,7 +387,7 @@ export class Spin2ObjectReferenceParser { } private _logMessage(message: string): void { - if (this.spin2ObjectLocatorLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. this.ctx.logger.log(message); } diff --git a/spin2/server/src/parser/spin.semantic.findings.ts b/spin2/server/src/parser/spin.semantic.findings.ts index 3ee43eb..2df2374 100644 --- a/spin2/server/src/parser/spin.semantic.findings.ts +++ b/spin2/server/src/parser/spin.semantic.findings.ts @@ -6,7 +6,6 @@ import { displayEnumByTypeName } from './spin2.utils'; import { eDebugDisplayType } from './spin.common'; import { Context } from '../context'; import { Position } from 'vscode-languageserver-textdocument'; -//import { PortMessageReader } from 'vscode-languageserver/node'; // ============================================================================ // this file contains objects we use in tracking symbol use and declaration @@ -38,6 +37,15 @@ export enum eDefinitionType { NonLabel } +export enum ePreprocessState { + PPS_Unknown, + PPS_IFDEF, + PPS_IFNDEF, + PPS_ELSE, + PPS_ELSEIFDEF, + PPS_ENDIF +} + // search comment type: non-doc only, doc-only, or mixed enum eCommentFilter { Unknown = 0, @@ -144,6 +152,7 @@ export interface IFoldSpan { // CLASS DocumentFindings export class DocumentFindings { private globalTokens; + private includeGlobalTokens; private methodLocalTokens; private instanceId: string = `ID:${new Date().getTime()}`; private declarationInfoByGlobalTokenName; @@ -160,6 +169,17 @@ export class DocumentFindings { private declarationGlobalLabelListCache: number[] = []; private declarationLocalLabelLineCache = new Map(); // line numbers by localLabelName + // tracking preprocessor code enable state + private disabledLines: Range[] = []; + private preProcSymbols: string[] = []; + private includePreProcSymbols: string[] = []; + private inPreProcIfStatement: boolean = false; + // [isLineEnabled] tracks current state + // when this changes from false to true we record a range of disabled lines into [disabledLines] + private preProcNestDepth: number = 0; // > 0 if we are in #if statement + private isLineEnabled: boolean[] = [true]; // we start enabled + private startingDisabledLineNbr: number[] = [-1]; + // tracking of Spin Code Blocks private priorBlockType: eBLockType = eBLockType.Unknown; private priorBlockStartLineIdx: number = -1; @@ -172,7 +192,7 @@ export class DocumentFindings { private pasmCodeSpans: IPasmCodeSpan[] = []; private pasmIsInline: boolean = false; - private findingsLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private bLogStarted: boolean = false; // tracking object outline @@ -180,8 +200,11 @@ export class DocumentFindings { private semanticTokens: IParsedToken[] = []; - // tracking includes + // tracking includes (both OBJ (object use) and #include use) private objectFilenameByInstanceName = new Map(); + // here we track which file #include's what file(s) + private includedFilenameByIncluderFilename = new Map(); + private ctx: Context | undefined; private docUri: string = '--uri-not-set--'; @@ -189,7 +212,7 @@ export class DocumentFindings { if (documentUri) { this.docUri = documentUri; } - if (this.findingsLogEnabled) { + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; //Create output channel @@ -201,6 +224,7 @@ export class DocumentFindings { this._logMessage("* Global, Local, MethodScoped Token repo's ready"); this.globalTokens = new TokenSet('gloTOK'); + this.includeGlobalTokens = new TokenSet('gloTOKIncl'); this.methodLocalTokens = new NameScopedTokenSet('methLocTOK'); this.declarationInfoByGlobalTokenName = new Map(); this.declarationInfoByLocalTokenName = new Map(); @@ -259,20 +283,24 @@ export class DocumentFindings { this.diagnosticMessages = []; this.outlineSymbols = []; this.semanticTokens = []; + this.preProcSymbols = []; if (clearIncludesToo) { this.objectParseResultByObjectName.clear(); this._logMessage(` -- FND-clear REMOVED object includes [${this.instanceName()}]`); + this.includeGlobalTokens.clear(); + this.includePreProcSymbols = []; } } public enableLogging(ctx: Context, doEnable: boolean = true): void { - this.findingsLogEnabled = doEnable; + this.isDebugLogEnabled = doEnable; this.ctx = ctx; this.globalTokens.enableLogging(ctx, doEnable); + this.includeGlobalTokens.enableLogging(ctx, doEnable); this.methodLocalTokens.enableLogging(ctx, doEnable); this.methodLocalPasmTokens.enableLogging(ctx, doEnable); // since we are already constructed, repeat this.... - if (this.findingsLogEnabled && this.bLogStarted == false) { + if (this.isDebugLogEnabled && this.bLogStarted == false) { this.bLogStarted = true; //Create output channel this._logMessage('Spin2 SemanticFindings log started.'); @@ -288,6 +316,231 @@ export class DocumentFindings { this.declarationGlobalLabelListCache = this.declarationGlobalLabelListCache.sort((n1, n2) => n1 - n2); } + // ------------------------------------------------------------------------------------- + // TRACK Proprocessor declarations and conditional code ranges + // + + public preProcDisabledRanges(): Range[] { + return this.disabledLines; + } + + public preProcIsLineDisabled(lineNbr: number): boolean { + let isDisabledStatus: boolean = false; + for (const disabledRange of this.disabledLines) { + if (lineNbr >= disabledRange.start.line && lineNbr <= disabledRange.end.line) { + isDisabledStatus = true; + break; + } + } + this._logMessage( + `* [PreProc] isLineDisabled(Ln#${lineNbr}) inPreProcStmnt=(${this.inPreProcIfStatement}), nestDepth=(${ + this.preProcNestDepth + }), isLineEnabled=(${this.isLineEnabled[this.preProcNestDepth]})` + ); + if (!isDisabledStatus) { + isDisabledStatus = this.isLineEnabled[this.preProcNestDepth] == false; + } + this._logMessage(`* [PreProc] preProcIsLineDisabled() -> (${isDisabledStatus})`); + return isDisabledStatus; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public preProcRecordConditionalSymbol(symbolName: string, line: string, lineNbr: number) { + // record a new preprossor symbol for #ifdef check + this._logMessage( + `* [PreProc] ADD SYM? nestDepth=(${this.preProcNestDepth}), isPreProcIf=(${this.inPreProcIfStatement}), isLineEnabled=(${ + this.isLineEnabled[this.preProcNestDepth] + }), at ln#${lineNbr}` + ); + if (!this.inPreProcIfStatement || (this.inPreProcIfStatement = true && this.isLineEnabled[this.preProcNestDepth] == true)) { + this.definePreProcSymbol(symbolName); + } + } + + public preProcRecordConditionChange(directive: ePreprocessState, symbolName: string = '', line: string, lineNbr: number) { + // handle preprocessor state transition + this._logMessage(`* [PreProc] rcdCondChg() [${ePreprocessState[directive]}], symbol=[${symbolName}], ln#${lineNbr}`); + const startInPreProc: boolean = this.inPreProcIfStatement; + switch (directive) { + case ePreprocessState.PPS_IFDEF: + { + // if symbol is defined, enable lines after this one, else disable lines after this one + // ensure we are nesting if already in ifdef + const priorDepth: number = this.preProcNestDepth; + this.preProcNestDepth = this.inPreProcIfStatement ? this.preProcNestDepth + 1 : 0; + this.inPreProcIfStatement = true; + // at new depth?, initialize it! + if (priorDepth != this.preProcNestDepth) { + this.isLineEnabled[this.preProcNestDepth] = true; + this.startingDisabledLineNbr[this.preProcNestDepth] = -1; + } + this._logMessage( + `* [PreProc] IFDEF nestDepth=(${this.preProcNestDepth}), isPreProcIf=(${this.inPreProcIfStatement}), from ln#${lineNbr}` + ); + // now handle ifdef + if (this.isPreProcSymbolDefined(symbolName)) { + this.preProcEnableLinesFrom(line, lineNbr); + } else { + this.preProcDisableLinesFrom(line, lineNbr + 1); + } + } + break; + case ePreprocessState.PPS_IFNDEF: + { + // ensure we are nesting if already in ifdef + const priorDepth: number = this.preProcNestDepth; + this.preProcNestDepth = this.inPreProcIfStatement ? this.preProcNestDepth + 1 : 0; + this.inPreProcIfStatement = true; + if (priorDepth != this.preProcNestDepth) { + this.isLineEnabled[this.preProcNestDepth] = true; + this.startingDisabledLineNbr[this.preProcNestDepth] = -1; + } + this._logMessage( + `* [PreProc] IFNDEF nestDepth=(${this.preProcNestDepth}), isPreProcIf=(${this.inPreProcIfStatement}), from ln#${lineNbr}` + ); + // now handle ifndef + if (!this.isPreProcSymbolDefined(symbolName)) { + this.preProcEnableLinesFrom(line, lineNbr); + } else { + this.preProcDisableLinesFrom(line, lineNbr + 1); + } + } + break; + case ePreprocessState.PPS_ENDIF: + this.preProcEnableLinesFrom(line, lineNbr); + this.preProcNestDepth = this.preProcNestDepth > 0 ? this.preProcNestDepth - 1 : 0; + this.inPreProcIfStatement = this.preProcNestDepth > 0 ? true : false; + this.isLineEnabled[this.preProcNestDepth] = true; // TODO: is this a BUG???!!! + this._logMessage( + `* [PreProc] ENDIF nestDepth=(${this.preProcNestDepth}), inPreProcStmnt=(${this.inPreProcIfStatement}), from ln#${lineNbr}` + ); + break; + case ePreprocessState.PPS_ELSE: + // invert current enable state + this.preProcInvertEnableLinesFrom(line, lineNbr); + break; + case ePreprocessState.PPS_ELSEIFDEF: + // end prior + // start new ifdef + if (this.isPreProcSymbolDefined(symbolName)) { + // is defined, set new enable state + this.preProcEnableLinesFrom(line, lineNbr); + } else { + // NOT defined so just invert state + this.preProcInvertEnableLinesFrom(line, lineNbr); + } + break; + + default: + break; + } + if (startInPreProc != this.inPreProcIfStatement) { + this._logMessage(`* [PreProc] inPreProcIfStatement:(${startInPreProc}) -> (${this.inPreProcIfStatement})`); + } + } + + private preProcEnableLinesFrom(line: string, lineNbr: number) { + // BEGIN a set of enabled lines + this._logMessage(`* [PreProc] ENABLE from ln#${lineNbr}`); + if (this.isLineEnabled[this.preProcNestDepth] == false) { + // record prior disabled range of lines + const startPosn: Position = { line: this.startingDisabledLineNbr[this.preProcNestDepth], character: 0 }; + const endPosn: Position = { line: lineNbr - 1, character: 0 }; + const newDisableEntry: Range = { start: startPosn, end: endPosn }; + this.disabledLines.push(newDisableEntry); + const itemNbr: number = this.disabledLines.length; + this._logMessage( + `* [PreProc] NEW Disable #${itemNbr} Range([${startPosn.line},${startPosn.character}], [${endPosn.line}, ${endPosn.character}])(${ + endPosn.line - startPosn.line + 1 + })` + ); + } + this.isLineEnabled[this.preProcNestDepth] = true; + } + + private preProcDisableLinesFrom(line: string, lineNbr: number) { + this._logMessage(`* [PreProc] DISABLE from ln#${lineNbr}`); + // BEGIN a set of disabled lines + if (this.isLineEnabled[this.preProcNestDepth] == false) { + // error!!! LOG THIS! + this._logMessage( + `ERROR: [PreProc] want to disable but ALREADY IS! line=[${line}](${lineNbr}) disabled at Ln#${ + this.startingDisabledLineNbr[this.preProcNestDepth] + }` + ); + } else { + this.isLineEnabled[this.preProcNestDepth] = false; + this.startingDisabledLineNbr[this.preProcNestDepth] = lineNbr; + } + } + + private preProcInvertEnableLinesFrom(line: string, lineNbr: number) { + if (this.isLineEnabled[this.preProcNestDepth]) { + // enabled, disable + this.preProcDisableLinesFrom(line, lineNbr + 1); + } else { + // disabled, enable + this.preProcEnableLinesFrom(line, lineNbr); + } + } + + private definePreProcSymbol(symbolName: string) { + const symbolKey: string = symbolName.toUpperCase(); + const currSymbolCount: number = this.preProcSymbols.length; + if (symbolKey.length > 0) { + if (!this.preProcSymbols.includes(symbolKey)) { + this.preProcSymbols.push(symbolKey); + this._logMessage(`* [PreProc] ADD symbol=[${symbolName}](${symbolName.length})`); + } + } + if (currSymbolCount == this.preProcSymbols.length) { + this._logMessage(`ERROR: [PreProc] FAILED to define new symbol=[${symbolName}](${symbolName.length})`); + } + } + + public isPreProcSymbolDefined(symbolName: string): boolean { + let foundSymbolStatus: boolean = false; + const symbolKey: string = symbolName.toUpperCase(); + if (symbolKey.length > 0) { + if (this.preProcSymbols.includes(symbolKey)) { + foundSymbolStatus = true; + } else if (this.includePreProcSymbols.includes(symbolKey)) { + foundSymbolStatus = true; + } + } + return foundSymbolStatus; + } + + private defineIncludePreProcSymbol(symbolName: string) { + const symbolKey: string = symbolName.toUpperCase(); + const currSymbolCount: number = this.includePreProcSymbols.length; + if (symbolKey.length > 0) { + if (!this.includePreProcSymbols.includes(symbolKey)) { + this.includePreProcSymbols.push(symbolKey); + this._logMessage(`* [PreProc] ADD symbol=[${symbolName}](${symbolName.length})`); + } + } + if (currSymbolCount == this.includePreProcSymbols.length) { + this._logMessage(`ERROR: [PreProc] FAILED to define new symbol=[${symbolName}](${symbolName.length})`); + } + } + + public isIncludePreProcSymbolDefined(symbolName: string): boolean { + let foundSymbolStatus: boolean = false; + const symbolKey: string = symbolName.toUpperCase(); + if (symbolKey.length > 0) { + if (this.includePreProcSymbols.includes(symbolKey)) { + foundSymbolStatus = true; + } + } + return foundSymbolStatus; + } + + private allPreprocessorSymbols(): string[] { + // used by include file handler + return this.preProcSymbols; + } + // ------------------------------------------------------------------------------------- // TRACK Diagnistic Messages found during parse of file // @@ -346,34 +599,38 @@ export class DocumentFindings { public pushDiagnosticMessage(lineIdx: number, startChar: number, endChar: number, severity: eSeverity, message: string): void { // record a new diagnostic message - let severityStr: string = '??severity??'; - if (this.findingsLogEnabled) { - switch (severity) { - case eSeverity.Error: { - severityStr = 'ERROR'; - break; - } - case eSeverity.Warning: { - severityStr = 'WARNING'; - break; - } - case eSeverity.Hint: { - severityStr = 'HINT'; - break; - } - case eSeverity.Information: { - severityStr = 'INFORMATION'; - break; + // NEW if is for disabled line, ignore it! + const lineNbr: number = lineIdx + 1; + if (!this.preProcIsLineDisabled(lineNbr)) { + let severityStr: string = '??severity??'; + if (this.isDebugLogEnabled) { + switch (severity) { + case eSeverity.Error: { + severityStr = 'ERROR'; + break; + } + case eSeverity.Warning: { + severityStr = 'WARNING'; + break; + } + case eSeverity.Hint: { + severityStr = 'HINT'; + break; + } + case eSeverity.Information: { + severityStr = 'INFORMATION'; + break; + } } } - } - if (startChar == -1 || endChar == -1) { - this._logMessage(`ERROR(BAD) DIAGNOSIS SKIPPED - ${severityStr}(${lineIdx + 1})[${startChar} - ${endChar}]: [${message}]`); - } else { - const location: Range = Range.create(lineIdx, startChar, lineIdx, endChar); - const diagnosis: DiagnosticReport = new DiagnosticReport(message, severity, location); - this.diagnosticMessages.push(diagnosis); - this._logMessage(`Add DIAGNOSIS - ${severityStr}(${lineIdx + 1})[${startChar}-${endChar}]: [${message}]`); + if (startChar == -1 || endChar == -1) { + this._logMessage(`ERROR(BAD) DIAGNOSIS SKIPPED - ${severityStr}(${lineIdx + 1})[${startChar} - ${endChar}]: [${message}]`); + } else { + const location: Range = Range.create(lineIdx, startChar, lineIdx, endChar); + const diagnosis: DiagnosticReport = new DiagnosticReport(message, severity, location); + this.diagnosticMessages.push(diagnosis); + this._logMessage(`Add DIAGNOSIS - ${severityStr}(${lineIdx + 1})[${startChar}-${endChar}]: [${message}]`); + } } } @@ -479,6 +736,10 @@ export class DocumentFindings { return sortedArray; } + public clearSemanticTokens() { + this.semanticTokens = []; + } + public pushSemanticToken(newToken: IParsedToken) { // record a new Semantic token found in this document if (!this.semanticTokenExists(newToken)) { @@ -498,6 +759,31 @@ export class DocumentFindings { return dupeTokenStatus; } + // ------------------------------------------------------------------------------------- + // TRACK #include's + // + public loadIncludeSymbols(includeFilename: string, includeSymbols: DocumentFindings) { + // merge global symbols from include into our findings... + //this._logMessage(`* merging symbols from [${includeSymbols.instanceName()}] into [${this.instanceName()}]`); + const globalTokenList = Array.from(includeSymbols.globalTokenSet()); + for (const [tokenName, token] of globalTokenList) { + this._logMessage(` -- Including new global [${tokenName}] from [${includeFilename}]`); + const declInfo = includeSymbols.globalTokenDeclarationInfo(tokenName); + let declarationComment: string | undefined = undefined; + if (declInfo !== undefined) { + declarationComment = declInfo.comment; + } + this.setIncludeGlobalToken(tokenName, token, declarationComment); + } + // merge info about which symbols are from #define statements + const defines: string[] = includeSymbols.allPreprocessorSymbols(); + for (let index = 0; index < defines.length; index++) { + const newDefinedSymbol = defines[index]; + this._logMessage(` -- Including new DEFINE [${newDefinedSymbol}] from [${includeFilename}]`); + this.defineIncludePreProcSymbol(newDefinedSymbol); + } + } + // ------------------------------------------------------------------------------------- // TRACK namespaces // @@ -655,7 +941,7 @@ export class DocumentFindings { if (symbolsFound) { if (this.ctx) { - symbolsFound.enableLogging(this.ctx, this.findingsLogEnabled); + symbolsFound.enableLogging(this.ctx, this.isDebugLogEnabled); } symbolsFound.appendLocationsOfToken(tokenName, locationsSoFar, nameSpace, postion); } @@ -686,7 +972,7 @@ export class DocumentFindings { // TRACK ranges of CON/PUB/PRI/VAR/DAT/OBJ blocks within file // public recordBlockStart(eCurrBlockType: eBLockType, currLineIdx: number) { - this._logMessage(` -- FND-RCD-BLOCK iblockType=[${eCurrBlockType}], span=[${currLineIdx} - ???]`); + this._logMessage(` -- FND-RCD-BLOCK iblockType=[${eBLockType[eCurrBlockType]}], span=[${currLineIdx} - ???]`); if (currLineIdx == 0 && this.priorBlockType != eBLockType.Unknown) { // we are getting a replacement for the default CON start section, use it! this.priorBlockType = eCurrBlockType; // override the default with possibly NEW block type @@ -801,7 +1087,7 @@ export class DocumentFindings { } // ------------------------------------------------------------------------------------- - // TRACK objects + // TRACK objects and #includes // public recordObjectImport(name: string, filename: string): void { // record use of object namespace @@ -814,21 +1100,60 @@ export class DocumentFindings { } } + public recordIncludeByWhom(fileIncluding: string, filename: string): void { + // record use of object namespace + const includerNameKey: string = fileIncluding.toLowerCase(); + if (!this.includedFilenameByIncluderFilename.has(includerNameKey)) { + this.includedFilenameByIncluderFilename.set(includerNameKey, [filename]); + this._logMessage(` -- ADD-INCLUDE by=[${fileIncluding}] of filename=[${filename}]`); + } else { + const filesIncluded: string[] | undefined = this.includedFilenameByIncluderFilename.get(includerNameKey); + if (filesIncluded) { + if (filesIncluded.includes(filename) == false) { + filesIncluded.push(filename); + this.includedFilenameByIncluderFilename.set(includerNameKey, filesIncluded); + } else { + this._logMessage(` -- DUPE-INCLUDE SKIPPED by=[${fileIncluding}] of filename=[${filename}]`); + } + } else { + this._logMessage(`ERROR:[INTERNAL] recordIncludeByWhom() failed to get curr list of includes for ${fileIncluding}`); + } + } + } + public includeFilenames(): string[] { // return the list of filenames of included objects const filenames: string[] = []; // eslint-disable-next-line @typescript-eslint/no-unused-vars - for (const [name, filename] of this.objectFilenameByInstanceName) { + for (const [instanceName, filename] of this.objectFilenameByInstanceName) { filenames.push(filename); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + for (const [includer, includedFilenames] of this.includedFilenameByIncluderFilename) { + filenames.push(...includedFilenames); + } + return filenames; } - public includeObjectNamesByFilename(): Map { + public includedObjectNamesByFilename(): Map { // return the full object set: instance names with assoc. file names return this.objectFilenameByInstanceName; } + public includeNamesForFilename(includerName: string): string[] { + // return the set: files include by includer + let includedFiles: string[] = []; + const includerNameKey: string = includerName.toLowerCase(); + for (const [includer, includedFilenames] of this.includedFilenameByIncluderFilename) { + if (includer == includerNameKey) { + includedFiles = includedFilenames; + break; // have our answer abort search + } + } + return includedFiles; + } + public isNameSpace(possibleNamespace: string): boolean { // return T/F where T means we have this name in our list const objectNameKey: string = possibleNamespace.toLowerCase(); @@ -1077,6 +1402,12 @@ export class DocumentFindings { return findings; } + private globalTokenDeclarationInfo(tokenName: string): RememberedTokenDeclarationInfo | undefined { + const desiredTokenKey = tokenName.toLowerCase(); + const declInfo: RememberedTokenDeclarationInfo | undefined = this.declarationInfoByGlobalTokenName.get(desiredTokenKey); + return declInfo; + } + private _locateNonBlankLineAfter(lineIdx: number): number { let desiredLineIdx: number = lineIdx; if (this.blockComments.length > 0) { @@ -1243,11 +1574,18 @@ export class DocumentFindings { } public isGlobalToken(tokenName: string): boolean { - const foundStatus: boolean = this.globalTokens.hasToken(tokenName); + let foundStatus: boolean = this.globalTokens.hasToken(tokenName); + if (foundStatus == false) { + foundStatus = this.includeGlobalTokens.hasToken(tokenName); + } this._logMessage(` -- IS-gloTOK [${tokenName}] says ${foundStatus}`); return foundStatus; } + private globalTokenSet(): [string, RememberedToken][] { + return this.globalTokens.entries(); + } + private _getBestLocalLabelPostionForPosition(position: Position, labelTokenName: string): Position { // locate range in which we can find a local token def'n // (this is global name above and below currLine) @@ -1341,25 +1679,45 @@ export class DocumentFindings { this._logMessage(` `); } - public setGlobalToken(tokenName: string, token: RememberedToken, declarationComment: string | undefined, reference?: string | undefined): void { + private setIncludeGlobalToken(tokenName: string, token: RememberedToken, declarationComment: string | undefined): void { // FIXME: TODO: UNDONE - this needs to allow multiple .tokenName's or :tokenName's and keep line numbers for each. // this allows go-to to get to nearest earlier than right-mouse line - const isLocalLabel: boolean = (tokenName.startsWith('.') || tokenName.startsWith(':')) && token.type === 'label'; if (!this.isGlobalToken(tokenName)) { this._logMessage( - ' -- NEW-gloTOK ' + - this._rememberdTokenString(tokenName, token) + - `, ln#${token.lineIndex + 1}, cmt=[${declarationComment}], ref=[${reference}]` + ' -- NEW-IncGloTOK ' + this._rememberdTokenString(tokenName, token) + `, ln#${token.lineIndex + 1}, cmt=[${declarationComment}]` ); - this.globalTokens.setToken(tokenName, token); + this.includeGlobalTokens.setToken(tokenName, token); // and remember declataion line# for this token - const newDescription: RememberedTokenDeclarationInfo = new RememberedTokenDeclarationInfo(token.lineIndex, declarationComment, reference); + const newDescription: RememberedTokenDeclarationInfo = new RememberedTokenDeclarationInfo(token.lineIndex, declarationComment, undefined); const desiredTokenKey: string = tokenName.toLowerCase(); this.declarationInfoByGlobalTokenName.set(desiredTokenKey, newDescription); } - // NEW record line numbers for local labels - if (isLocalLabel) { - this.trackLocalTokenLineNbr(tokenName, token.lineIndex); + } + + public setGlobalToken(tokenName: string, token: RememberedToken, declarationComment: string | undefined, reference?: string | undefined): void { + // FIXME: TODO: UNDONE - this needs to allow multiple .tokenName's or :tokenName's and keep line numbers for each. + const tokenLineNbr: number = token.lineIndex + 1; + if (!this.preProcIsLineDisabled(tokenLineNbr)) { + // this allows go-to to get to nearest earlier than right-mouse line + const isLocalLabel: boolean = (tokenName.startsWith('.') || tokenName.startsWith(':')) && token.type === 'label'; + if (!this.isGlobalToken(tokenName)) { + this._logMessage( + ' -- NEW-gloTOK ' + + this._rememberdTokenString(tokenName, token) + + `, ln#${token.lineIndex + 1}, cmt=[${declarationComment}], ref=[${reference}]` + ); + this.globalTokens.setToken(tokenName, token); + // and remember declataion line# for this token + const newDescription: RememberedTokenDeclarationInfo = new RememberedTokenDeclarationInfo(token.lineIndex, declarationComment, reference); + const desiredTokenKey: string = tokenName.toLowerCase(); + this.declarationInfoByGlobalTokenName.set(desiredTokenKey, newDescription); + } + // NEW record line numbers for local labels + if (isLocalLabel) { + this.trackLocalTokenLineNbr(tokenName, token.lineIndex); + } + } else { + this._logMessage(`* SKIP token setGLobal for disabled ln#(${tokenLineNbr}) token=[${this._rememberdTokenString(tokenName, token)}]`); } } @@ -1381,7 +1739,10 @@ export class DocumentFindings { public getGlobalToken(tokenName: string): RememberedToken | undefined { let desiredToken: RememberedToken | undefined = this.globalTokens.getToken(tokenName); - if (desiredToken != undefined) { + if (desiredToken === undefined) { + desiredToken = this.includeGlobalTokens.getToken(tokenName); + } + if (desiredToken !== undefined) { // let's never return a declaration modifier! (somehow declaration creeps in to our list!??) //let modifiersNoDecl: string[] = this._modifiersWithout(desiredToken.modifiers, "declaration"); const modifiersNoDecl: string[] = desiredToken.modifiersWithout('declaration'); @@ -1437,7 +1798,7 @@ export class DocumentFindings { const methodName: string | undefined = this._getMethodNameForLine(lineNbr); if (methodName) { desiredToken = this.methodLocalTokens.getTokenForMethod(methodName, tokenName); - if (desiredToken != undefined) { + if (desiredToken !== undefined) { this._logMessage(` -- FND-locTOK ln#${lineNbr} method=[${methodName}], ` + this._rememberdTokenString(tokenName, desiredToken)); } else { this._logMessage(` -- FAILED to FND-locTOK ln#${lineNbr} method=[${methodName}], ` + tokenName); @@ -1575,7 +1936,7 @@ export class DocumentFindings { // PRIVATE (Utility) Methods // private _logMessage(message: string): void { - if (this.findingsLogEnabled) { + if (this.isDebugLogEnabled) { // Write to output window. if (this.ctx) { this.ctx.logger.log(message); @@ -1585,7 +1946,7 @@ export class DocumentFindings { private _rememberdTokenString(tokenName: string, aToken: RememberedToken | undefined): string { let desiredInterp: string = ' -- token=[len:' + tokenName.length + ' [' + tokenName + '](undefined)'; - if (aToken != undefined) { + if (aToken !== undefined) { desiredInterp = ' -- token=[len:' + tokenName.length + ' [' + tokenName + '](' + aToken.type + '[' + aToken.modifiers + '])]'; } return desiredInterp; @@ -1635,6 +1996,7 @@ export class DocumentFindings { } public getDebugDisplayEnumForUserName(possibleUserName: string): eDebugDisplayType { + this._logMessage(`* getDebugDisplayEnumForUserName(${possibleUserName})`); const nameKey: string = possibleUserName.toLowerCase(); let desiredEnumValue: eDebugDisplayType = eDebugDisplayType.Unknown; if (this.isKnownDebugDisplay(possibleUserName)) { @@ -1647,6 +2009,7 @@ export class DocumentFindings { } public getDebugDisplayInfoForUserName(possibleUserName: string): IDebugDisplayInfo { + this._logMessage(`* getDebugDisplayInfoForUserName(${possibleUserName})`); const nameKey: string = possibleUserName.toLowerCase(); let possibleInfo: IDebugDisplayInfo = { displayTypeString: '', @@ -1676,6 +2039,7 @@ export class DocumentFindings { } public isKnownDebugDisplay(possibleUserName: string): boolean { + this._logMessage(`* isKnownDebugDisplay(${possibleUserName})`); const nameKey: string = possibleUserName.toLowerCase(); const foundStatus: boolean = this.displayInfoByDebugDisplayName.has(nameKey); this._logMessage(' DDsply _isKnownDebugDisplay(' + possibleUserName + ') = ' + foundStatus); @@ -1704,7 +2068,7 @@ export class DocumentFindings { export class TokenSet { public constructor(idString: string) { //this.bLogEnabled = isLogging; - //this.outputChannel = logHandle; + //this.debugOutputChannel = logHandle; this.id = idString; this._logMessage(`* ${this.id} ready`); } @@ -1732,7 +2096,7 @@ export class TokenSet { yield* this.rememberedTokenByName; } - public entries() { + public entries(): [string, RememberedToken][] { return Array.from(this.rememberedTokenByName.entries()); } @@ -1748,7 +2112,7 @@ export class TokenSet { public rememberdTokenString(tokenName: string, aToken: RememberedToken | undefined): string { let desiredInterp: string = ` -- token=[len:${tokenName.length} [${tokenName}](undefined)`; - if (aToken != undefined) { + if (aToken !== undefined) { desiredInterp = ` -- token=[len:${tokenName.length} [${tokenName}](${aToken.type}[${aToken.modifiers}])]`; } return desiredInterp; @@ -1782,7 +2146,7 @@ export class TokenSet { public getToken(tokenName: string): RememberedToken | undefined { const desiredTokenKey: string = tokenName.toLowerCase(); let desiredToken: RememberedToken | undefined = this.rememberedTokenByName.get(desiredTokenKey); - if (desiredToken != undefined) { + if (desiredToken !== undefined) { // let's never return a declaration modifier! (somehow "declaration" creeps in to our list!??) //let modifiersNoDecl: string[] = this._modifiersWithout(desiredToken.modifiers, "declaration"); const modifiersNoDecl: string[] = desiredToken.modifiersWithout('declaration'); @@ -1805,7 +2169,7 @@ export class NameScopedTokenSet { public constructor(idString: string) { //this.bLogEnabled = isLogging; - //this.outputChannel = logHandle; + //this.debugOutputChannel = logHandle; this.id = idString; this._logMessage(`* ${this.id} ready`); } @@ -1969,7 +2333,7 @@ export class NameScopedTokenSet { private _rememberdTokenString(tokenName: string, aToken: RememberedToken | undefined): string { let desiredInterp: string = ' -- LP token=[len:' + tokenName.length + ' [' + tokenName + '](undefined)'; - if (aToken != undefined) { + if (aToken !== undefined) { desiredInterp = ' -- LP token=[len:' + tokenName.length + ' [' + tokenName + '](' + aToken.type + '[' + aToken.modifiers + '])]'; } return desiredInterp; @@ -1999,7 +2363,7 @@ export class RememberedToken { this._type = type; this._lineIdx = lineIdx; this._charIdx = charIdx; - if (modifiers != undefined) { + if (modifiers !== undefined) { this._modifiers = modifiers; } } @@ -2079,7 +2443,7 @@ export class RememberedTokenDeclarationInfo { this._declcomment = declarationComment.trim(); } } - if (typeof reference !== 'undefined' && reference != undefined) { + if (typeof reference !== 'undefined' && reference !== undefined) { this._reference = reference; } } diff --git a/spin2/server/src/parser/spin1.documentSemanticParser.ts b/spin2/server/src/parser/spin1.documentSemanticParser.ts index 4aabb74..d71ebbf 100644 --- a/spin2/server/src/parser/spin1.documentSemanticParser.ts +++ b/spin2/server/src/parser/spin1.documentSemanticParser.ts @@ -34,7 +34,7 @@ export class Spin1DocumentSemanticParser { private bLogStarted: boolean = false; // adjust following true/false to show specific parsing debug - private spin1DebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private showSpinCode: boolean = true; private showPreProc: boolean = true; private showCON: boolean = true; @@ -61,10 +61,10 @@ export class Spin1DocumentSemanticParser { private directory: string = ''; public constructor(protected readonly ctx: Context) { - this.extensionUtils = new ExtensionUtils(ctx, this.spin1DebugLogEnabled); + this.extensionUtils = new ExtensionUtils(ctx, this.isDebugLogEnabled); this.configuration = ctx.parserConfig; this.editorConfiguration = ctx.editorConfig; - if (this.spin1DebugLogEnabled) { + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; //Create output channel @@ -74,7 +74,7 @@ export class Spin1DocumentSemanticParser { } } - //this.semanticFindings = new DocumentFindings(this.spin1DebugLogEnabled, this.spin1log); + //this.semanticFindings = new DocumentFindings(this.isDebugLogEnabled, this.spin1log); } public docFindings(): DocumentFindings { @@ -84,7 +84,7 @@ export class Spin1DocumentSemanticParser { public reportDocumentSemanticTokens(document: TextDocument, findings: DocumentFindings, dirSpec: string): void { this.semanticFindings = findings; this.directory = dirSpec; - if (this.spin1DebugLogEnabled) { + if (this.isDebugLogEnabled) { this.semanticFindings.enableLogging(this.ctx); this.parseUtils.enableLogging(this.ctx); this.spinControlFlowTracker.enableLogging(this.ctx); @@ -98,6 +98,7 @@ export class Spin1DocumentSemanticParser { // retrieve tokens to highlight, post to DocumentFindings const allTokens = this._parseText(document.getText()); + this.semanticFindings.clearSemanticTokens(); allTokens.forEach((token) => { // prevent crash in server and emit debug so we can find problem this.semanticFindings.pushSemanticToken(token); @@ -225,6 +226,12 @@ export class Spin1DocumentSemanticParser { } currState = priorState; this._logMessage(`* Ln#${lineNbr} foundMuli end-} exit MultiLineComment`); + // if NO more code on line after close then skip line + const tempLine: string = lineWOutInlineComments.substring(closingOffset + 1).trim(); + if (tempLine.length == 0) { + this._logMessage(`* SKIP MultiLineComment Ln#${i + 1} lineWOutInlineComments=[${lineWOutInlineComments}]`); + continue; + } } else { // add line to the comment recording currBlockComment?.appendLine(line); @@ -539,6 +546,12 @@ export class Spin1DocumentSemanticParser { // have close, comment ended currState = priorState; this._logMessage(`* Ln#${lineNbr} foundMuli end-}} exit MultiLineDocComment`); + // if NO more code on line after close then skip line + const tempLine: string = lineWOutInlineComments.substring(closingOffset + 1).trim(); + if (tempLine.length == 0) { + this._logMessage(`* SKIP MultiLineComment Ln#${i + 1} trimmedNonCommentLine=[${trimmedNonCommentLine}]`); + continue; + } } else { continue; // only SKIP if we don't have closing marker } @@ -553,9 +566,16 @@ export class Spin1DocumentSemanticParser { this._logMessage(` FOUND '}' Ln#${lineNbr} trimmedLine=[${trimmedLine}]`); currState = priorState; this._logMessage(`* Ln#${lineNbr} foundMuli end-} exit MultiLineComment`); + // if NO more code on line after close then skip line + const tempLine: string = lineWOutInlineComments.substring(closingOffset + 1).trim(); + if (tempLine.length == 0) { + this._logMessage(`* SKIP MultiLineComment Ln#${i + 1} lineWOutInlineComments=[${lineWOutInlineComments}]`); + continue; + } } else { continue; // only SKIP if we don't have closing marker } + // DO NOTHING Let Syntax highlighting do this } else if (lineParts.length > 0 && this.parseUtils.isFlexspinPreprocessorDirective(lineParts[0])) { const partialTokenSet: IParsedToken[] = this._reportFlexspinPreProcessorLine(i, 0, line); partialTokenSet.forEach((newToken) => { @@ -829,7 +849,7 @@ export class Spin1DocumentSemanticParser { const symbolName: string | undefined = lineParts.length > 1 ? lineParts[1] : undefined; if (this.parseUtils.isFlexspinPreprocessorDirective(directive)) { // check a valid preprocessor line for a declaration - if (symbolName != undefined && directive.toLowerCase() == '#define') { + if (symbolName !== undefined && directive.toLowerCase() == '#define') { this._logPreProc(' -- new PreProc Symbol=[' + symbolName + ']'); const nameOffset = line.indexOf(symbolName, currentOffset); // FIXME: UNDONE, do we have to dial this in? this.semanticFindings.recordDeclarationLine(line, lineNbr); @@ -1133,7 +1153,7 @@ export class Spin1DocumentSemanticParser { const searchFilename: string = `"${filenameNoQuotes}`; const hasPathSep: boolean = filenameNoQuotes.includes('/'); const nameOffset: number = line.indexOf(searchFilename, startingOffset); - const logCtx: Context | undefined = this.spin1DebugLogEnabled ? this.ctx : undefined; + const logCtx: Context | undefined = this.isDebugLogEnabled ? this.ctx : undefined; if (hasPathSep) { this.semanticFindings.pushDiagnosticMessage( lineIdx, @@ -1161,7 +1181,7 @@ export class Spin1DocumentSemanticParser { const hasPathSep: boolean = filenameNoQuotes.includes('/'); const fileWithExt = `${filenameNoQuotes}.spin`; const nameOffset: number = line.indexOf(filenameNoQuotes, startingOffset); - const logCtx: Context | undefined = this.spin1DebugLogEnabled ? this.ctx : undefined; + const logCtx: Context | undefined = this.isDebugLogEnabled ? this.ctx : undefined; const checkFilename: string = hasSuffix ? filenameNoQuotes : fileWithExt; if (hasPathSep) { this.semanticFindings.pushDiagnosticMessage( @@ -3485,7 +3505,7 @@ export class Spin1DocumentSemanticParser { } private _logMessage(message: string): void { - if (this.spin1DebugLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. this.ctx.logger.log(message); } @@ -3625,7 +3645,7 @@ export class Spin1DocumentSemanticParser { private _checkTokenSet(tokenSet: IParsedToken[]): void { this._logMessage('\n---- Checking ' + tokenSet.length + ' tokens. ----'); tokenSet.forEach((parsedToken) => { - if (parsedToken.length == undefined || parsedToken.startCharacter == undefined) { + if (parsedToken.length === undefined || parsedToken.startCharacter === undefined) { this._logMessage('- BAD Token=[' + parsedToken + ']'); } }); diff --git a/spin2/server/src/parser/spin1.documentSymbolParser.ts b/spin2/server/src/parser/spin1.documentSymbolParser.ts index 50640de..8b90a3a 100644 --- a/spin2/server/src/parser/spin1.documentSymbolParser.ts +++ b/spin2/server/src/parser/spin1.documentSymbolParser.ts @@ -16,7 +16,7 @@ import { ExtensionUtils } from '../parser/spin.extension.utils'; // the DocumentFindings object assiciated with this file // export class Spin1DocumentSymbolParser { - private spin1OutlineLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private bLogStarted: boolean = false; private extensionUtils: ExtensionUtils; @@ -25,8 +25,8 @@ export class Spin1DocumentSymbolParser { private symbolsFound: DocumentFindings | undefined = undefined; public constructor(protected readonly ctx: Context) { - this.extensionUtils = new ExtensionUtils(ctx, this.spin1OutlineLogEnabled); - if (this.spin1OutlineLogEnabled) { + this.extensionUtils = new ExtensionUtils(ctx, this.isDebugLogEnabled); + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; //Create output channel @@ -42,7 +42,7 @@ export class Spin1DocumentSymbolParser { let priorState: eParseState = currState; let prePasmState: eParseState = currState; this.symbolsFound = findings; - if (this.spin1OutlineLogEnabled) { + if (this.isDebugLogEnabled) { this.symbolsFound.enableLogging(this.ctx); } @@ -168,7 +168,17 @@ export class Spin1DocumentSymbolParser { if (linePrefix == 'CON' || linePrefix == 'DAT' || linePrefix == 'VAR' || linePrefix == 'OBJ') { // start CON/VAR/OBJ/DAT const sectionComment = lineHasComment ? line.substr(commentOffset, commentLength) : ''; - const blockSymbol: OutLineSymbol = new OutLineSymbol(linePrefix + ' ' + sectionComment, '', lsp.SymbolKind.Field, lineRange); + let blockSymbolKind: lsp.SymbolKind = lsp.SymbolKind.Variable; + if (linePrefix == 'CON') { + blockSymbolKind = lsp.SymbolKind.Method; + } else if (linePrefix == 'DAT') { + blockSymbolKind = lsp.SymbolKind.EnumMember; + } else if (linePrefix == 'OBJ') { + blockSymbolKind = lsp.SymbolKind.Class; + } else if (linePrefix == 'VAR') { + blockSymbolKind = lsp.SymbolKind.Variable; + } + const blockSymbol: OutLineSymbol = new OutLineSymbol(linePrefix + ' ' + sectionComment, '', blockSymbolKind, lineRange); this.setContainerSymbol(blockSymbol); // HANDLE label declaration on DAT line! if (linePrefix == 'DAT') { @@ -188,7 +198,8 @@ export class Spin1DocumentSymbolParser { posssibleLabel = undefined; // Nope! } if (posssibleLabel) { - const labelSymbol: OutLineSymbol = new OutLineSymbol(lineParts[1], '', lsp.SymbolKind.Constant, lineRange); + //const labelSymbol: OutLineSymbol = new OutLineSymbol(lineParts[1], '', lsp.SymbolKind.Constant, lineRange); + const labelSymbol: OutLineSymbol = new OutLineSymbol(lineParts[1], '', lsp.SymbolKind.String, lineRange); if (this.containerDocSymbol) { this.containerDocSymbol.addChild(labelSymbol); } @@ -219,7 +230,8 @@ export class Spin1DocumentSymbolParser { } // NOTE this changed to METHOD when we added global labels which are to be Functions! - const methodSymbol: OutLineSymbol = new OutLineSymbol(linePrefix + ' ' + methodName, '', lsp.SymbolKind.Method, lineRange); + const methodSymbolKind: lsp.SymbolKind = linePrefix == 'PUB' ? lsp.SymbolKind.Method : lsp.SymbolKind.Field; + const methodSymbol: OutLineSymbol = new OutLineSymbol(linePrefix + ' ' + methodName, '', methodSymbolKind, lineRange); this.setContainerSymbol(methodSymbol); } } else { @@ -253,7 +265,8 @@ export class Spin1DocumentSymbolParser { if (global_label) { // was Variable: sorta OK (image good, color bad) // was Constant: sorta OK (image good, color bad) SAME - const labelSymbol: OutLineSymbol = new OutLineSymbol(global_label, '', lsp.SymbolKind.Constant, lineRange); + //const labelSymbol: OutLineSymbol = new OutLineSymbol(global_label, '', lsp.SymbolKind.Constant, lineRange); + const labelSymbol: OutLineSymbol = new OutLineSymbol(global_label, '', lsp.SymbolKind.String, lineRange); if (this.containerDocSymbol) { this.containerDocSymbol.addChild(labelSymbol); } else { @@ -280,7 +293,7 @@ export class Spin1DocumentSymbolParser { } private _logMessage(message: string): void { - if (this.spin1OutlineLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. this.ctx.logger.log(message); } diff --git a/spin2/server/src/parser/spin2.documentSemanticParser.ts b/spin2/server/src/parser/spin2.documentSemanticParser.ts index 3edf95f..b6405f7 100644 --- a/spin2/server/src/parser/spin2.documentSemanticParser.ts +++ b/spin2/server/src/parser/spin2.documentSemanticParser.ts @@ -1,7 +1,7 @@ 'use strict'; // src/spin2.documentSemanticParser.ts -import { TextDocument } from 'vscode-languageserver-textdocument'; +import { Range, TextDocument } from 'vscode-languageserver-textdocument'; import { Position } from 'vscode-languageserver-types'; import { Context, ServerBehaviorConfiguration, EditorConfiguration } from '../context'; @@ -13,7 +13,8 @@ import { eBLockType, IParsedToken, eSeverity, - eDefinitionType + eDefinitionType, + ePreprocessState } from './spin.semantic.findings'; import { Spin2ParseUtils } from './spin2.utils'; import { isSpin1File } from './lang.utils'; @@ -30,9 +31,7 @@ import { isMethodCallEmptyParens } from './spin.common'; import { fileInDirExists } from '../files'; -//import { PublishDiagnosticsNotification } from "vscode-languageserver"; import { ExtensionUtils } from '../parser/spin.extension.utils'; -//import { channel } from "diagnostics_channel"; // ---------------------------------------------------------------------------- // Semantic Highlighting Provider @@ -65,7 +64,7 @@ export class Spin2DocumentSemanticParser { private bLogStarted: boolean = false; // adjust following true/false to show specific parsing debug - private spin2DebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private showSpinCode: boolean = true; private showPreProc: boolean = true; private showCON: boolean = true; @@ -95,10 +94,10 @@ export class Spin2DocumentSemanticParser { private bHuntingForVersion: boolean = true; // initially we re hunting for a {Spin2_v##} spec in file-top comments public constructor(protected readonly ctx: Context) { - this.extensionUtils = new ExtensionUtils(ctx, this.spin2DebugLogEnabled); + this.extensionUtils = new ExtensionUtils(ctx, this.isDebugLogEnabled); this.configuration = ctx.parserConfig; this.editorConfiguration = ctx.editorConfig; - if (this.spin2DebugLogEnabled) { + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; //Create output channel @@ -117,7 +116,7 @@ export class Spin2DocumentSemanticParser { this.semanticFindings = findings; this.directory = dirSpec; const startingLangVersion: number = this.parseUtils.selectedSpinVersion(); - if (this.spin2DebugLogEnabled) { + if (this.isDebugLogEnabled) { this.semanticFindings.enableLogging(this.ctx); this.parseUtils.enableLogging(this.ctx); this.spinControlFlowTracker.enableLogging(this.ctx); @@ -130,14 +129,15 @@ export class Spin2DocumentSemanticParser { this._logMessage(`* ------ into findings=[${findings.instanceName()}]`); // retrieve tokens to highlight, post to DocumentFindings - const allTokens = this._parseText(document.getText()); + let allTokens = this._parseText(document.getText()); const endingLangVersion: number = this.parseUtils.selectedSpinVersion(); if (startingLangVersion != endingLangVersion) { this._logMessage(`* Spin2 LANG VERSION HUNT [${startingLangVersion} -> ${endingLangVersion}]`); } else { this._logMessage(`* Spin2 LANG VERSION HUNT [${startingLangVersion}]`); } - this._checkTokenSet(allTokens); + allTokens = this._checkTokenSet(allTokens, document.getText()); + this.semanticFindings.clearSemanticTokens(); allTokens.forEach((token) => { this.semanticFindings.pushSemanticToken(token); }); @@ -173,10 +173,11 @@ export class Spin2DocumentSemanticParser { // ============================================================================== // prepass to find declarations: PRI/PUB method, OBJ names, and VAR/DAT names // - if (this.spin2DebugLogEnabled) { + if (this.isDebugLogEnabled) { continuedLineSet.enableLogging(this.ctx); } - // -------------------- PRE-PARSE just locating symbol names, spin folding info -------------------- + + // -------- PRE-PARSE just locating symbol names, spin folding info ----------- // also track and record block comments (both braces and tic's!) // let's also track prior single line and trailing comment on same line this._logMessage(`---> Pre SCAN -- `); @@ -191,6 +192,7 @@ export class Spin2DocumentSemanticParser { const NONDOC_COMMENT = false; const BLOCK_COMMENT = true; //const LINE_COMMENT = false; + let blocksFoundCount: number = 0; for (let i = 0; i < lines.length; i++) { const lineNbr: number = i + 1; @@ -221,7 +223,8 @@ export class Spin2DocumentSemanticParser { this.rightEdgeComment = tempComment.length > 0 ? tempComment : undefined; const sectionStatus = this.extensionUtils.isSectionStartLine(line); if (sectionStatus.isSectionStart) { - trimmedNonCommentLine = trimmedNonCommentLine.substring(3); + blocksFoundCount += 1; + trimmedNonCommentLine = trimmedNonCommentLine.substring(3).trimStart(); } const singleLineParts: string[] = trimmedNonCommentLine.split(/[ \t]/).filter(Boolean); // NOTE: comment mid-line set a pending state so next line uses the new state @@ -256,8 +259,8 @@ export class Spin2DocumentSemanticParser { // now start our processing if (currState == eParseState.inMultiLineDocComment) { - // in multi-line doc-comment, hunt for end '}}' to exit - const closingOffset = lineWOutInlineComments.indexOf('}}'); + // in multi-line doc-comment, hunt for end '} }' to exit + const closingOffset = trimmedNonCommentLine.indexOf('}}'); let commentLen: number = line.length; if (closingOffset != -1) { commentLen = closingOffset + 2; @@ -277,6 +280,12 @@ export class Spin2DocumentSemanticParser { // Mark comment line this._recordToken(tokenSet, line, this._generateComentToken(i, 0, commentLen, BLOCK_COMMENT, DOC_COMMENT, line)); } + // if NO more code on line after close then skip line + const tempLine: string = lineWOutInlineComments.substring(closingOffset + 1).trim(); + if (tempLine.length == 0) { + this._logMessage(`* SKIP MultiLineDocComment Ln#${i + 1} nonCommentLine=[${nonCommentLine}]`); + continue; + } } else { // add line to the comment recording currBlockComment?.appendLine(line); @@ -288,8 +297,8 @@ export class Spin2DocumentSemanticParser { } else if (currState == eParseState.inMultiLineComment) { // in multi-line non-doc-comment, hunt for end '}' to exit // ALLOW {...} on same line without closing! - const closingOffset: number = lineWOutInlineComments.indexOf('}'); - if (closingOffset != -1) { + const [closingMultiline, closingOffset] = this._haveUnmatchCloseOnLine(line, '}'); + if (closingMultiline) { // have close, comment ended // end the comment recording currBlockComment?.appendLastLine(i, line); @@ -301,21 +310,21 @@ export class Spin2DocumentSemanticParser { } currState = priorState; this._logMessage(`* Ln#${lineNbr} foundMuli end-} exit MultiLineComment`); + // if NO more code on line after close then skip line + const tempLine: string = lineWOutInlineComments.substring(closingOffset + 1).trim(); + if (tempLine.length == 0) { + this._logMessage(`* SKIP MultiLineComment Ln#${i + 1} nonCommentLine=[${nonCommentLine}]`); + continue; + } } else { // add line to the comment recording currBlockComment?.appendLine(line); continue; // nothing more to do with this line, skip to next } - // fall THRU let rest of line be processed } else if (trimmedLine.length == 0) { // a blank line clears pending single line comments this.priorSingleLineComment = undefined; continue; - } else if (singleLineParts.length > 0 && this.parseUtils.isFlexspinPreprocessorDirective(singleLineParts[0])) { - this._getFlexspinPreProcessor_Declaration(0, lineNbr, line); - // a FlexspinPreprocessorDirective line clears pending single line comments - this.priorSingleLineComment = undefined; - continue; } else if (trimmedLine.startsWith("''")) { if (bBuildingSingleLineDocCmtBlock) { // process single line doc comment which follows one of same @@ -369,9 +378,7 @@ export class Spin2DocumentSemanticParser { // start NEW comment currBlockComment = new RememberedComment(eCommentType.multiLineDocComment, i, line); // DO NOTHING Let Syntax highlighting do this - // Mark comment line - //this._recordToken(tokenSet, line, this._generateComentToken(i, 0, line.length, BLOCK_COMMENT, DOC_COMMENT, line)); - continue; // only SKIP if we don't have closing marker + continue; // only SKIP if we DON't have closing marker and comment is only thing on line } } else if (trimmedNonCommentLine.startsWith('{')) { // TODO: the second if clause confuses me... why did I do this? @@ -395,24 +402,22 @@ export class Spin2DocumentSemanticParser { continue; // only SKIP if we don't have closing marker } } else if (trimmedNonCommentLine.includes('{{')) { - // process multi-line doc comment + // process multi-line doc comment which doesn't start at beginning of line const openingOffset = trimmedNonCommentLine.indexOf('{{'); const closingOffset = trimmedNonCommentLine.indexOf('}}', openingOffset + 2); + // if we only have open... if (closingOffset == -1) { // is open of multiline comment without CLOSE priorState = currState; pendingState = eParseState.inMultiLineDocComment; - this._logMessage(`* Ln#${lineNbr} foundMuli mid-{{ starting MultiLineDocComment`); + this._logMessage(`* Ln#${lineNbr} priorState=[${eParseState[priorState]}] pendingState=[${eParseState[pendingState]}]`); + this._logMessage(`* Ln#${lineNbr} PRE foundMuli mid-{{ starting MultiLineDocComment`); // start NEW comment - currBlockComment = new RememberedComment(eCommentType.multiLineDocComment, i, line); - // DO NOTHING Let Syntax highlighting do this + const lineOffset: number = line.indexOf('{{'); + currBlockComment = new RememberedComment(eCommentType.multiLineDocComment, i, line.substring(lineOffset)); // Mark comment line - this._recordToken( - tokenSet, - line, - this._generateComentToken(i, openingOffset, line.length - openingOffset, BLOCK_COMMENT, DOC_COMMENT, line) - ); - //continue; // DON'T SKIP, process rest of line + this._recordToken(tokenSet, line, this._generateComentToken(i, lineOffset, line.length - openingOffset, BLOCK_COMMENT, DOC_COMMENT, line)); + // DO NOTHING Let Syntax highlighting do this } } else if (trimmedNonCommentLine.includes('{') && !trimmedNonCommentLine.includes('{{')) { /// FIXME: TODO: this needs to be searching in non-string-containing line @@ -424,15 +429,20 @@ export class Spin2DocumentSemanticParser { // is open of multiline comment (with NO closing) priorState = currState; pendingState = eParseState.inMultiLineComment; + this._logMessage(`* Ln#${lineNbr} priorState=[${eParseState[priorState]}] pendingState=[${eParseState[pendingState]}]`); this._logMessage(`* Ln#${lineNbr} foundMuli mid-{ starting MultiLineComment`); // start NEW comment currBlockComment = new RememberedComment(eCommentType.multiLineComment, i, line); - // Mark comment line - //this._recordToken(tokenSet, line, this._generateComentToken(i, openingOffset, line.length - openingOffset, BLOCK_COMMENT, NONDOC_COMMENT, line)); // DO NOTHING Let Syntax highlighting do this //continue; // DON'T SKIP, process rest of line } + } else if (singleLineParts.length > 0 && this.parseUtils.isFlexspinPreprocessorDirective(singleLineParts[0])) { + this._getFlexspinPreProcessor_Declaration(0, lineNbr, line); + // a FlexspinPreprocessorDirective line clears pending single line comments + this.priorSingleLineComment = undefined; + continue; // only SKIP if we have FlexSpin directive } + // handle wrap-up before we do continued-line gathering if (sectionStatus.isSectionStart) { // mark end of method, if we were in a method @@ -441,7 +451,7 @@ export class Spin2DocumentSemanticParser { if (currState == eParseState.inDatPAsm) { this.semanticFindings.recordPasmEnd(i - 1); currState = prePAsmState; - this._logState('- scan Ln#' + lineNbr + ' POP currState=[' + currState + ']'); + this._logState(`- scan Ln#${lineNbr} POP currState=[${eParseState[currState]}]`); } if (currState == eParseState.inPub || currState == eParseState.inPri) { @@ -455,7 +465,8 @@ export class Spin2DocumentSemanticParser { } } - if (this.bHuntingForVersion) { + if (this.bHuntingForVersion && lineNbr > 3 && blocksFoundCount > 1) { + // we are in a new block (sectionStart) if 2nd or later block then we stop our search this.bHuntingForVersion = false; // done, we passed the file-top comments. we can no longer search const newLangVersion: number = this.parseUtils.selectedSpinVersion(); this._logMessage(` -- stopping HUNT w/lang=(${newLangVersion}), Ln#${lineNbr}=[${trimmedLine}]`); @@ -479,12 +490,12 @@ export class Spin2DocumentSemanticParser { newBlockType = eBLockType.isPri; } this.semanticFindings.recordBlockStart(newBlockType, i); // start new one which ends prior - this._logState('- scan Ln#' + lineNbr + ' currState=[' + currState + ']'); + this._logState(`- scan Ln#${lineNbr} currState=[${eParseState[currState]}]`); } - // ---------------------------------------------- - // gather our multi-line set if line is continued - // ---------------------------------------------- + // ------------------------------------------------------------------- + // ----- gather our multi-line set if line is continued + // ------------------------------------------------------------------- // const isContinued: boolean = trimmedNonCommentLine.length > 0 ? trimmedNonCommentLine.endsWith('...') : false; let continuedSectionStatus = { @@ -519,7 +530,7 @@ export class Spin2DocumentSemanticParser { } } else if (currState == eParseState.inCon) { // process a constant line - if (trimmedNonCommentLine.length > 3) { + if (trimmedNonCommentLine.length > 0) { this._getCON_Declaration(3, lineNbr, line); } } else if (currState == eParseState.inDat) { @@ -550,7 +561,7 @@ export class Spin2DocumentSemanticParser { } } else if (currState == eParseState.inVar) { // process a instance-variable line - if (trimmedNonCommentLine.length > 3) { + if (trimmedNonCommentLine.length > 0) { this._getVAR_Declaration(3, lineNbr, line); } } @@ -617,7 +628,7 @@ export class Spin2DocumentSemanticParser { // record start of PASM code inline this.semanticFindings.recordPasmEnd(i); currState = prePAsmState; - this._logState('- scan Ln#' + lineNbr + ' POP currState=[' + currState + ']'); + this._logState(`- scan Ln#${lineNbr} POP currState=[${eParseState[currState]}]`); // and ignore rest of this line } else { this._getSPIN_PAsmDeclaration(0, lineNbr, line); @@ -678,15 +689,18 @@ export class Spin2DocumentSemanticParser { } } } + // we processed statements in this line, now clear prior comment associated with this line this.priorSingleLineComment = undefined; // clear it out... continuedLineSet.clear(); // end of processing this multi-line set if (pendingState != eParseState.Unknown) { + this._logState(`- scan Ln#${lineNbr} DELAYED currState [${eParseState[currState]}] -> [${eParseState[pendingState]}]`); currState = pendingState; // only once... pendingState = eParseState.Unknown; } } + // mark end of code fold, if we had started one const spanSet: ICurrControlSpan[] = this.spinControlFlowTracker.finishControlFlow(lines.length - 1); // pass last line index! if (spanSet.length > 0) { @@ -699,7 +713,9 @@ export class Spin2DocumentSemanticParser { this.semanticFindings.finishFinalBlock(lines.length - 1); // mark end of final block in file this.semanticFindings.finalize(); - // -------------------- End of PRE-PARSE -------------------- + // ------------------------------------------------------------------- + // ---------------------- Actual SCAN ---------------------- + // this._logMessage('---> <---'); this._logMessage('---> Actual SCAN'); @@ -724,18 +740,24 @@ export class Spin2DocumentSemanticParser { let trimmedNonCommentLine: string = bHaveLineToProcess ? nonCommentLine.trimStart() : ''; const sectionStatus = this.extensionUtils.isSectionStartLine(line); if (sectionStatus.isSectionStart) { - trimmedNonCommentLine = trimmedNonCommentLine.substring(3); + trimmedNonCommentLine = trimmedNonCommentLine.substring(3).trimStart(); } const singleLineParts: string[] = trimmedNonCommentLine.split(/[ \t]/).filter(Boolean); if (currState == eParseState.inMultiLineDocComment) { - // in multi-line doc-comment, hunt for end '}}' to exit + // in multi-line doc-comment, hunt for end '} }' to exit // ALLOW {cmt}, {{cmt}} on same line without closing! const closingOffset = trimmedNonCommentLine.indexOf('}}'); if (closingOffset != -1) { // have close, comment ended currState = priorState; this._logMessage(`* Ln#${lineNbr} foundMuli end-}} exit MultiLineDocComment`); + // if NO more code on line after close then skip line + const tempLine: string = lineWOutInlineComments.substring(closingOffset + 1).trim(); + if (tempLine.length == 0) { + this._logMessage(`* SKIP MultiLineComment Ln#${i + 1} nonCommentLine=[${nonCommentLine}]`); + continue; + } // DO NOTHING Let Syntax highlighting do this } else { //this._logMessage(`* Ln#${lineNbr} SKIP in MultiLineDocComment`); @@ -744,16 +766,22 @@ export class Spin2DocumentSemanticParser { } else if (currState == eParseState.inMultiLineComment) { // in multi-line non-doc-comment, hunt for end '}' to exit // ALLOW {cmt}, {{cmt}} on same line without closing! - const closingOffset: number = lineWOutInlineComments.indexOf('}'); - if (closingOffset != -1) { + const [closingMultiline, closingOffset] = this._haveUnmatchCloseOnLine(line, '}'); + if (closingMultiline) { // have close, comment ended currState = priorState; this._logMessage(`* Ln#${lineNbr} foundMuli end-} exit MultiLineComment`); - // DO NOTHING Let Syntax highlighting do this + // if NO more code on line after close then skip line + const tempLine: string = lineWOutInlineComments.substring(closingOffset + 1).trim(); + if (tempLine.length == 0) { + this._logMessage(`* SKIP MultiLineComment Ln#${i + 1} nonCommentLine=[${nonCommentLine}]`); + continue; + } } else { //this._logMessage(`* Ln#${lineNbr} SKIP in MultiLineDocComment`); continue; // only SKIP if we don't have closing marker } + // DO NOTHING Let Syntax highlighting do this } else if (singleLineParts.length > 0 && this.parseUtils.isFlexspinPreprocessorDirective(singleLineParts[0])) { const partialTokenSet: IParsedToken[] = this._reportFlexspinPreProcessorLine(i, 0, line); this._reportNonDupeTokens(partialTokenSet, '=> PreProc: ', line, tokenSet); @@ -774,10 +802,10 @@ export class Spin2DocumentSemanticParser { // DON'T mark the section literal, Syntax highlighting does this well! } - // ---------------------------------------------- - // gather our multi-line set if line is continued - // OR in SPIN code - // ---------------------------------------------- + // ------------------------------------------------------------------- + // ----- gather our multi-line set if line is continued + // OR in SPIN code + // ------------------------------------------------------------------- // const isContinued: boolean = trimmedNonCommentLine.length > 0 ? trimmedNonCommentLine.endsWith('...') : false; let continuedSectionStatus = { @@ -845,7 +873,8 @@ export class Spin2DocumentSemanticParser { // is open of multiline comment priorState = currState; pendingState = eParseState.inMultiLineDocComment; - this._logMessage(`* Ln#${lineNbr} foundMuli mid-{{ starting MultiLineDocComment`); + this._logMessage(`* Ln#${lineNbr} priorState=[${eParseState[priorState]}] pendingState=[${eParseState[pendingState]}]`); + this._logMessage(`* Ln#${lineNbr} FINAL foundMuli mid-{{ starting MultiLineDocComment`); // DO NOTHING Let Syntax highlighting do this } // don't continue there might be some text to process before the {{ @@ -860,6 +889,7 @@ export class Spin2DocumentSemanticParser { // is open of multiline comment priorState = currState; pendingState = eParseState.inMultiLineComment; + this._logMessage(`* Ln#${lineNbr} priorState=[${eParseState[priorState]}] pendingState=[${eParseState[pendingState]}]`); this._logMessage(`* Ln#${lineNbr} foundMuli mid-{ starting MultiLineComment`); // DO NOTHING Let Syntax highlighting do this } @@ -1032,7 +1062,7 @@ export class Spin2DocumentSemanticParser { const lineParts: string[] = trimmedLine.split(/[ \t]/).filter(Boolean); if (lineParts.length > 0 && (lineParts[0].toUpperCase() == 'END' || lineParts[0].toUpperCase() == 'ENDASM')) { currState = prePAsmState; - this._logState('- scan Ln#' + lineNbr + ' POP currState=[' + currState + ']'); + this._logState(`- scan Ln#${lineNbr} POP currState=[${eParseState[currState]}]`); if (lineParts[0].toUpperCase() == 'ENDASM' && !this.configuration.highlightFlexspinDirectives) { // report this unsupported line (FlexSpin) const nameOffset: number = line.indexOf(lineParts[0]); @@ -1141,6 +1171,7 @@ export class Spin2DocumentSemanticParser { } continuedLineSet.clear(); // end of processing this multi-line set if (pendingState != eParseState.Unknown) { + this._logState(`- scan Ln#${lineNbr} DELAYED currState [${eParseState[currState]}] -> [${eParseState[pendingState]}]`); currState = pendingState; // only once... pendingState = eParseState.Unknown; @@ -1149,6 +1180,29 @@ export class Spin2DocumentSemanticParser { return tokenSet; } + private _haveUnmatchCloseOnLine(line: string, searchChar: string): [boolean, number] { + let unmatchedCloseStatus: boolean = false; + let matchOffset: number = 0; + const closeString: string = searchChar; + const openString: string = searchChar == '}' ? '{' : '{{'; + const matchLen: number = searchChar.length; + let nestLevel: number = 0; + if (line.length >= searchChar.length) { + for (let offset = 0; offset < line.length; offset++) { + const matchString = line.substring(offset, offset + matchLen); + if (matchString == openString) { + nestLevel++; + } else if (matchString == closeString) { + matchOffset = offset; + nestLevel--; + } + } + } + unmatchedCloseStatus = nestLevel == -1 ? true : false; + this._logMessage(` -- _haveUnmatchCloseOnLine() isClosed=(${unmatchedCloseStatus}), ofs=(${matchOffset}) line=[${line}](${line.length})`); + return [unmatchedCloseStatus, matchOffset]; + } + private _generateFakeCommentForSignature(startingOffset: number, lineNbr: number, line: string): RememberedComment { let desiredComment: RememberedComment = new RememberedComment(eCommentType.Unknown, -1, ''); const linePrefix: string = line.substring(0, 3).toLowerCase(); @@ -1306,18 +1360,38 @@ export class Spin2DocumentSemanticParser { // get line parts - we only care about first one const lineParts: string[] = nonCommentConstantLine.split(/[ \t=]/).filter(Boolean); this._logPreProc(' - Ln#' + lineNbr + ' GetPreProcDecl lineParts=[' + lineParts + ']'); - const directive: string = lineParts[0]; + const directive: string = lineParts[0].toLowerCase(); const symbolName: string | undefined = lineParts.length > 1 ? lineParts[1] : undefined; if (this.parseUtils.isFlexspinPreprocessorDirective(directive)) { // check a valid preprocessor line for a declaration - if (symbolName != undefined && directive.toLowerCase() == '#define') { + if (symbolName !== undefined && directive == '#define') { this._logPreProc(' -- new PreProc Symbol=[' + symbolName + ']'); + this.semanticFindings.preProcRecordConditionalSymbol(symbolName, line, lineNbr); this.semanticFindings.recordDeclarationLine(line, lineNbr); this.semanticFindings.setGlobalToken( symbolName, new RememberedToken('variable', lineNbr - 1, 0, ['readonly']), this._declarationComment() ); + } else { + // handle non-define directives + let directiveType: ePreprocessState = ePreprocessState.PPS_Unknown; + if (directive === '#ifdef') { + directiveType = ePreprocessState.PPS_IFDEF; + } else if (directive === '#ifndef') { + directiveType = ePreprocessState.PPS_IFNDEF; + } else if (directive === '#else') { + directiveType = ePreprocessState.PPS_ELSE; + } else if (directive === '#elseifdef') { + directiveType = ePreprocessState.PPS_ELSEIFDEF; + } else if (directive === '#endif') { + directiveType = ePreprocessState.PPS_ENDIF; + } + + const symbolToPass: string = symbolName === undefined ? '' : symbolName; + if (directiveType != ePreprocessState.PPS_Unknown) { + this.semanticFindings.preProcRecordConditionChange(directiveType, symbolToPass, line, lineNbr); + } } } } @@ -1438,14 +1512,17 @@ export class Spin2DocumentSemanticParser { // NEW: multi line enums with no punctuation, ends at blank line (uses this.conEnumInProgress) // this._logCON(` - Ln#${lineNbr} GetCONDecl startingOffset=(${startingOffset}), line=[${line}](${line.length})`); - if (line.substring(startingOffset).length > 1) { + const isPreprocessorStatement: boolean = this.parseUtils.lineStartsWithFlexspinPreprocessorDirective(line); + if (isPreprocessorStatement == false && line.substring(startingOffset).length > 1) { //skip Past Whitespace let currentOffset: number = this.parseUtils.skipWhite(line, startingOffset); const nonCommentConstantLine = this.parseUtils.getNonCommentLineRemainder(currentOffset, line); if (nonCommentConstantLine.length == 0) { this.conEnumInProgress = false; // if we have a blank line after removing comment then weve ended the enum set } else { - this._logCON(` -- GetCONDecl nonCommentConstantLine=[${nonCommentConstantLine}](${nonCommentConstantLine.length})`); + this._logCON( + ` -- GetCONDecl isPreProc=(${isPreprocessorStatement}), nonCommentConstantLine=[${nonCommentConstantLine}](${nonCommentConstantLine.length})` + ); const haveEnumDeclaration: boolean = this._isEnumDeclarationLine(lineNbr - 1, 0, nonCommentConstantLine); const isAssignment: boolean = nonCommentConstantLine.indexOf('=') != -1; if (!haveEnumDeclaration && isAssignment) { @@ -1475,25 +1552,33 @@ export class Spin2DocumentSemanticParser { this._logCON(' -- GetCONDecl assign lineParts=[' + lineParts + '](' + lineParts.length + ')'); const newName = lineParts[0]; if (newName.charAt(0).match(/[a-zA-Z_]/) && !this.parseUtils.isP1AsmVariable(newName)) { - this._logCON(' -- GLBL GetCONDecl newName=[' + newName + ']'); - // remember this object name so we can annotate a call to it + // if this line is NOT disabled, record new global (or error with DUPLICATE) + const lineIsDisabled: boolean = this.semanticFindings.preProcIsLineDisabled(lineNbr); + this._logCON(` -- GLBL GetCONDecl newName=[${newName}], lineIsDisabled=(${lineIsDisabled})`); const nameOffset = line.indexOf(newName, currentOffset); // FIXME: UNDONE, do we have to dial this in? - const referenceDetails: RememberedToken | undefined = this.semanticFindings.getGlobalToken(newName); - if (referenceDetails) { - this.semanticFindings.pushDiagnosticMessage( - lineNbr - 1, - nameOffset, - nameOffset + newName.length, - eSeverity.Error, - `P2 Spin Duplicate constant name [${newName}], already declared` - ); + if (!lineIsDisabled) { + // remember this object name so we can annotate a call to it + const referenceDetails: RememberedToken | undefined = this.semanticFindings.getGlobalToken(newName); + if (referenceDetails) { + this.semanticFindings.pushDiagnosticMessage( + lineNbr - 1, + nameOffset, + nameOffset + newName.length, + eSeverity.Error, + `P2 Spin Duplicate constant name [${newName}], already declared` + ); + } else { + this.semanticFindings.recordDeclarationLine(line, lineNbr); + this.semanticFindings.setGlobalToken( + newName, + new RememberedToken('variable', lineNbr - 1, nameOffset, ['readonly']), + this._declarationComment() + ); + } } else { - this.semanticFindings.recordDeclarationLine(line, lineNbr); - this.semanticFindings.setGlobalToken( - newName, - new RememberedToken('variable', lineNbr - 1, nameOffset, ['readonly']), - this._declarationComment() - ); + const token = new RememberedToken('variable', lineNbr - 1, nameOffset, ['readonly']); + this._declarationComment(); + this._logMessage(`* SKIP token setGLobal for disabled ln#(${lineNbr}) token=[${this._rememberdTokenString(newName, token)}]`); } } } @@ -1686,7 +1771,7 @@ export class Spin2DocumentSemanticParser { const nameOffset: number = line.indexOf(searchFilename, startingOffset); const hasPathSep: boolean = filenameNoQuotes.includes('/'); this._logMessage(` -- looking for DataFile [${this.directory}/${filenameNoQuotes}]`); - const logCtx: Context | undefined = this.spin2DebugLogEnabled ? this.ctx : undefined; + const logCtx: Context | undefined = this.isDebugLogEnabled ? this.ctx : undefined; if (hasPathSep) { this.semanticFindings.pushDiagnosticMessage( lineIdx, @@ -1714,7 +1799,7 @@ export class Spin2DocumentSemanticParser { const hasPathSep: boolean = filenameNoQuotes.includes('/'); const fileWithExt = `${filenameNoQuotes}.spin2`; const nameOffset: number = line.indexOf(filenameNoQuotes, startingOffset); - const logCtx: Context | undefined = this.spin2DebugLogEnabled ? this.ctx : undefined; + const logCtx: Context | undefined = this.isDebugLogEnabled ? this.ctx : undefined; const checkFilename: string = hasSuffix ? filenameNoQuotes : fileWithExt; if (hasPathSep) { this.semanticFindings.pushDiagnosticMessage( @@ -2040,7 +2125,7 @@ export class Spin2DocumentSemanticParser { if (nonCommentConstantLine.length > 0) { // get line parts - we only care about first one const lineParts: string[] = nonCommentConstantLine.split(/[ \t=]/).filter(Boolean); - this._logPreProc(' - Ln#' + lineNbr + ' reportPreProc lineParts=[' + lineParts + ']'); + this._logPreProc(` - Ln#${lineNbr} reportPreProc lineParts=[${lineParts}]`); const directive: string = lineParts[0]; const symbolName: string | undefined = lineParts.length > 1 ? lineParts[1] : undefined; @@ -2054,15 +2139,15 @@ export class Spin2DocumentSemanticParser { ptTokenType: 'keyword', ptTokenModifiers: ['control', 'directive'] }); - const hasSymbol: boolean = + const lineHasSymbol: boolean = directive.toLowerCase() == '#define' || directive.toLowerCase() == '#ifdef' || directive.toLowerCase() == '#ifndef' || directive.toLowerCase() == '#elseifdef' || directive.toLowerCase() == '#elseifndef'; - if (hasSymbol && symbolName != undefined) { + if (lineHasSymbol && symbolName !== undefined) { const nameOffset = line.indexOf(symbolName, currentOffset); - this._logPreProc(' -- GLBL symbolName=[' + symbolName + ']'); + this._logPreProc(` -- GLBL symbolName=[${symbolName}]`); let referenceDetails: RememberedToken | undefined = undefined; if (this.semanticFindings.isGlobalToken(symbolName)) { referenceDetails = this.semanticFindings.getGlobalToken(symbolName); @@ -2079,6 +2164,14 @@ export class Spin2DocumentSemanticParser { ptTokenType: referenceDetails.type, ptTokenModifiers: updatedModificationSet }); + } else if (this.semanticFindings.isPreProcSymbolDefined(symbolName)) { + this._recordToken(tokenSet, line, { + line: lineIdx, + startCharacter: nameOffset, + length: symbolName.length, + ptTokenType: 'variable', + ptTokenModifiers: ['readonly'] + }); } else if (this.parseUtils.isFlexspinReservedWord(symbolName)) { // record a constant reference this._recordToken(tokenSet, line, { @@ -2094,8 +2187,8 @@ export class Spin2DocumentSemanticParser { line: lineIdx, startCharacter: nameOffset, length: symbolName.length, - ptTokenType: 'comment', - ptTokenModifiers: ['line'] + ptTokenType: 'variable', //'comment', + ptTokenModifiers: ['disabled'] // ['line'] }); } } @@ -2127,8 +2220,12 @@ export class Spin2DocumentSemanticParser { const currentOffset: number = this.parseUtils.skipWhite(line, startingOffset); const nonCommentConstantLine = this.parseUtils.getNonCommentLineRemainder(currentOffset, line); let enumDeclStatus: boolean = nonCommentConstantLine.trim().startsWith('#'); + const isPreprocessorStatement: boolean = this.parseUtils.lineStartsWithFlexspinPreprocessorDirective(nonCommentConstantLine); + if (isPreprocessorStatement) { + enumDeclStatus = false; + } // if not yet sure... - if (enumDeclStatus == false) { + if (isPreprocessorStatement == false && enumDeclStatus == false) { // don't know what this line is, yet const containsMultiStatements: boolean = nonCommentConstantLine.indexOf(',') != -1; let statements: string[] = [nonCommentConstantLine]; @@ -2180,7 +2277,7 @@ export class Spin2DocumentSemanticParser { this._logCON(` -- assignments statements=[${statements}](${statements.length})`); for (let index = 0; index < statements.length; index++) { const conDeclarationLine: string = statements[index].trim(); - this._logCON(' -- conDeclarationLine=[' + conDeclarationLine + ']'); + this._logCON(' -- m conDeclarationLine=[' + conDeclarationLine + ']'); //currSingleLineOffset = line.indexOf(conDeclarationLine, currSingleLineOffset); const symbolPosition: Position = multiLineSet.locateSymbol(conDeclarationLine, currSingleLineOffset); // locate key indicators of line style @@ -2370,6 +2467,8 @@ export class Spin2DocumentSemanticParser { } else { if ( !this.parseUtils.isP2AsmReservedWord(namePart) && + !this.parseUtils.isBuiltInSmartPinReservedWord(namePart) && + !this.parseUtils.isBuiltinStreamerReservedWord(namePart) && !this.parseUtils.isUnaryOperator(namePart) && !this.parseUtils.isBinaryOperator(namePart) && !this.parseUtils.isSpinNumericSymbols(namePart) @@ -2681,6 +2780,8 @@ export class Spin2DocumentSemanticParser { } else { if ( !this.parseUtils.isP2AsmReservedWord(namePart) && + !this.parseUtils.isBuiltInSmartPinReservedWord(namePart) && + !this.parseUtils.isBuiltinStreamerReservedWord(namePart) && !this.parseUtils.isUnaryOperator(namePart) && !this.parseUtils.isBinaryOperator(namePart) && !this.parseUtils.isSpinNumericSymbols(namePart) @@ -3415,17 +3516,11 @@ export class Spin2DocumentSemanticParser { } currSingleLineOffset = startingOffset; // reset to beginnning of line this.currentMethodName = methodName; // notify of latest method name so we can track inLine PASM symbols - const spin2MethodName: string = methodName + '('; - const spin2MethodNameWithSpace: string = methodName + ' ('; - // FIXME: TODO: replaces name-concat with regEX search past whitespace for '(' - this._logSPIN('-reportPubPriSig: spin2MethodName=[' + spin2MethodName + '], startNameOffset=(' + startNameOffset + ')'); - let bHaveSpin2Method: boolean = false; - let symbolPosition: Position = multiLineSet.locateSymbol(spin2MethodName, currSingleLineOffset); - bHaveSpin2Method = symbolPosition.character != -1; - if (symbolPosition.character == -1) { - symbolPosition = multiLineSet.locateSymbol(spin2MethodNameWithSpace, currSingleLineOffset); - bHaveSpin2Method = symbolPosition.character != -1; - } + + const methodFollowString: string = remainingNonCommentLineStr.substring(startNameOffset + methodName.length); + this._logSPIN(` -- rptPubPriMulti() methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + const bHaveSpin2Method: boolean = isMethodCall(methodFollowString); + let symbolPosition: Position = multiLineSet.locateSymbol(methodName, currSingleLineOffset); const nameOffset = multiLineSet.offsetIntoLineForPosition(symbolPosition); if (bHaveSpin2Method) { const declModifiers: string[] = isPrivate ? ['declaration', 'static'] : ['declaration']; @@ -3838,16 +3933,14 @@ export class Spin2DocumentSemanticParser { } this.currentMethodName = methodName; // notify of latest method name so we can track inLine PASM symbols - const spin2MethodName: string = methodName + '('; - const spin2MethodNameWithSpace: string = methodName + ' ('; - // FIXME: TODO: replaces name-concat with regEX search past whitespace for '(' + + const methodFollowString: string = line.substring(startNameOffset + methodName.length); + this._logSPIN(` -- rptPubPriSig() methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + const bHaveSpin2Method: boolean = isMethodCall(methodFollowString); + //const myExpression: string = `${methodName}\s\(`; //const methodNameRegEx = new RegExp(myExpression, "i"); // case-insensative method name with arbitrary whitespace before open paren - const bHaveSpin2Method: boolean = line.includes(spin2MethodName) || line.includes(spin2MethodNameWithSpace); - const bHaveSpin2Method2: boolean = false; // methodNameRegEx.test(line); - this._logSPIN( - `-reportPubPriSig: spin2MethodName=[${spin2MethodName}], startNameOffset=(${startNameOffset}), bHaveSpin2Method=(${bHaveSpin2Method}), bHaveSpin2Method2=(${bHaveSpin2Method2})` - ); + this._logSPIN(`-reportPubPriSig: spin2MethodName=[${methodName}], startNameOffset=(${startNameOffset}), bHaveSpin2Method=(${bHaveSpin2Method})`); if (bHaveSpin2Method) { const declModifiers: string[] = isPrivate ? ['declaration', 'static'] : ['declaration']; this._recordToken(tokenSet, line, { @@ -4263,6 +4356,15 @@ export class Spin2DocumentSemanticParser { const lineInfo: IFilteredStrings = this._getNonWhiteSpinLineParts(filteredLine); varNameList = lineInfo.lineParts; } + // FIXME: TODO: needs code to process index suff here.. + if (possibleVariableName.includes('[')) { + // if variable contains index / index expression... + const leftEdge: number = possibleVariableName.indexOf('['); + const rightEdge: number = possibleVariableName.indexOf(']'); + if (leftEdge != -1 && rightEdge != -1 && leftEdge < rightEdge) { + // + } + } this._logSPIN(` -- LHS: varNameList=[${varNameList}]`); for (let index = 0; index < varNameList.length; index++) { const variableName: string = varNameList[index]; @@ -4401,12 +4503,12 @@ export class Spin2DocumentSemanticParser { } else if (this.semanticFindings.isGlobalToken(namePart)) { referenceDetails = this.semanticFindings.getGlobalToken(namePart); this._logSPIN(` -- FOUND global name=[${namePart}], referenceDetails=(${referenceDetails})`); - if (referenceDetails != undefined && referenceDetails?.type == 'method') { + if (referenceDetails !== undefined && referenceDetails?.type == 'method') { const addressOf = `@${namePart}`; // if it's not a legit method call, kill the reference //const searchSpace: string = multiLineSet.line.substring(nameOffset); const methodFollowString: string = multiLineSet.line.substring(nameOffset + namePart.length); - this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + this._logSPIN(` -- Multi-A methodFollowString=[${methodFollowString}](${methodFollowString.length})`); if (!isMethodCall(methodFollowString) && !searchString.includes(addressOf)) { this._logSPIN(` -- MISSING parens on method=[${namePart}]`); referenceDetails = undefined; @@ -4582,7 +4684,7 @@ export class Spin2DocumentSemanticParser { // if whitespace without parens we have white in statement singleElement = false; } - this._logSPIN(` -- SPIN assignmentRHSStr=[${assignmentRHSStr}], singleElement=(${singleElement})`); + this._logSPIN(` -- SPINMulti assignmentRHSStr=[${assignmentRHSStr}], singleElement=(${singleElement})`); // SPECIAL Ex: scroller[scrollerIndex].initialize() if (singleElement && this._isPossibleObjectReference(assignmentRHSStr) && assignmentRHSStr.includes('[')) { @@ -4678,10 +4780,10 @@ export class Spin2DocumentSemanticParser { if (!referenceDetails && this.semanticFindings.isGlobalToken(namePart)) { referenceDetails = this.semanticFindings.getGlobalToken(namePart); this._logSPIN(` -- FOUND Global name=[${namePart}], referenceDetails=(${referenceDetails})`); - if (referenceDetails != undefined && referenceDetails?.type == 'method') { + if (referenceDetails !== undefined && referenceDetails?.type == 'method') { const addressOf = `@${namePart}`; const methodFollowString: string = multiLineSet.line.substring(nameOffset + namePart.length); - this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + this._logSPIN(` -- Multi-B methodFollowString=[${methodFollowString}](${methodFollowString.length})`); if (!isMethodCall(methodFollowString) && !nonStringAssignmentRHSStr.includes(addressOf)) { this._logSPIN(` -- MISSING parens on method=[${namePart}]`); referenceDetails = undefined; @@ -4699,7 +4801,7 @@ export class Spin2DocumentSemanticParser { }); } else { const methodFollowString: string = multiLineSet.lineAt(symbolPosition.line).substring(nameOffset + namePart.length); - this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + this._logSPIN(` -- Multi-C methodFollowString=[${methodFollowString}](${methodFollowString.length})`); if (this.parseUtils.isMethodOverride(namePart, this.parseUtils.selectedSpinVersion()) && isMethodCall(methodFollowString)) { this._logSPIN(` -- override with method coloring name=[${namePart}](${namePart.length}), ofs=(${nameOffset})`); this._recordToken(tokenSet, multiLineSet.lineAt(symbolPosition.line), { @@ -4931,6 +5033,19 @@ export class Spin2DocumentSemanticParser { const lineInfo: IFilteredStrings = this._getNonWhiteSpinLineParts(filteredLine); varNameList = lineInfo.lineParts; } + /* + if (possibleVariableName.includes('[')) { + // if variable contains index / index expression... + const leftEdge: number = possibleVariableName.indexOf('['); + const rightEdge: number = possibleVariableName.indexOf(']'); + if (leftEdge != -1 && rightEdge != -1 && leftEdge < rightEdge) { + // send of index expression to be parsed and reported + const indexExpression: string = possibleVariableName.substring(leftEdge, rightEdge + 1); + const partialTokenSet: IParsedToken[] = this._reportSPIN_IndexExpression(indexExpression, lineIdx, startingOffset, line); + this._reportNonDupeTokens(partialTokenSet, '=> SPINindex: ', line, tokenSet); + } + } + */ this._logSPIN(' -- LHS: varNameList=[' + varNameList + ']'); for (let index = 0; index < varNameList.length; index++) { const variableName: string = varNameList[index]; @@ -4938,7 +5053,7 @@ export class Spin2DocumentSemanticParser { // NOTE this handles code: byte[pColor][2] := {value} // NOTE2 this handles code: result.byte[3] := {value} P2 OBEX: jm_apa102c.spin2 (139) // have complex target name, parse in loop - const variableNameParts: string[] = variableName.split(/[ \t[\]/*+\-()<>]/); + const variableNameParts: string[] = variableName.split(/[ \t[\]&/*+\-()<>]/); this._logSPIN(' -- LHS: [] variableNameParts=[' + variableNameParts + ']'); for (let index = 0; index < variableNameParts.length; index++) { let variableNamePart = variableNameParts[index].replace('@', ''); @@ -5052,11 +5167,11 @@ export class Spin2DocumentSemanticParser { } else if (this.semanticFindings.isGlobalToken(namePart)) { referenceDetails = this.semanticFindings.getGlobalToken(namePart); this._logSPIN(' -- FOUND global name=[' + namePart + ']'); - if (referenceDetails != undefined && referenceDetails?.type == 'method') { + if (referenceDetails !== undefined && referenceDetails?.type == 'method') { const addressOf = `@${namePart}`; // if it's not a legit method call, kill the reference const methodFollowString: string = line.substring(nameOffset + namePart.length); - this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + this._logSPIN(` -- A methodFollowString=[${methodFollowString}](${methodFollowString.length})`); if (!isMethodCall(methodFollowString) && !searchString.includes(addressOf)) { this._logSPIN(` -- MISSING parens on method=[${namePart}]`); referenceDetails = undefined; @@ -5076,6 +5191,8 @@ export class Spin2DocumentSemanticParser { //const searchKey: string = namePart.toLowerCase(); //const isMethodNoParen: boolean = searchKey == 'return' || searchKey == 'abort'; // have unknown name!? is storage type spec? + const methodFollowString: string = line.substring(nameOffset + namePart.length); + this._logSPIN(` -- A methodFollowString=[${methodFollowString}](${methodFollowString.length})`); if (this.parseUtils.isStorageType(namePart)) { this._logSPIN(' -- SPIN RHS storageType=[' + namePart + ']'); this._recordToken(tokenSet, line, { @@ -5087,11 +5204,10 @@ export class Spin2DocumentSemanticParser { }); } else if ( this.parseUtils.isSpinBuiltinMethod(namePart) && - !searchString.includes(namePart + '(') && + !isMethodCall(methodFollowString) && !this.parseUtils.isSpinNoparenMethod(namePart) ) { - // FIXME: TODO: replaces name-concat with regEX search past whitespace for '(' - this._logSPIN(' -- SPIN MISSING PARENS name=[' + namePart + ']'); + this._logSPIN(` -- SPIN MISSING PARENS name=[${namePart}]`); this._recordToken(tokenSet, line, { line: lineIdx, startCharacter: nameOffset, @@ -5214,31 +5330,39 @@ export class Spin2DocumentSemanticParser { currentOffset = 0; const preCleanAssignmentRHSStr = this.parseUtils.getNonInlineCommentLine(assignmentRHSStr).replace('..', ' '); const dotOffset: number = assignmentRHSStr.indexOf('.'); - const spaceOffset: number = assignmentRHSStr.indexOf(' '); - const tabOffset: number = assignmentRHSStr.indexOf('\t'); - const bracketOffset: number = assignmentRHSStr.indexOf('['); - const parenOffset: number = assignmentRHSStr.indexOf('('); + const spaceOffset: number = assignmentRHSStr.trimStart().indexOf(' '); + const tabOffset: number = assignmentRHSStr.trimStart().indexOf('\t'); + const ltBbracketOffset: number = assignmentRHSStr.indexOf('['); + const rtBbracketOffset: number = assignmentRHSStr.indexOf(']'); + const bracketOffset: number = rtBbracketOffset > ltBbracketOffset ? rtBbracketOffset : ltBbracketOffset; + const ltParenOffset: number = assignmentRHSStr.indexOf('('); + const rtParenOffset: number = assignmentRHSStr.indexOf(')'); + const haveParens: boolean = ltParenOffset != -1 || rtParenOffset != -1; const whiteOffset: number = spaceOffset != -1 ? spaceOffset : tabOffset; const hasWhite: boolean = whiteOffset != -1; // we have a single element if we have "." with "[" and "[" is before "." - let singleElement: boolean = dotOffset != -1 && bracketOffset != -1 && dotOffset > bracketOffset ? true : false; - if (singleElement && hasWhite && parenOffset != -1 && parenOffset > whiteOffset) { + const indexExpression: string = bracketOffset != -1 ? line.substring(ltBbracketOffset, rtBbracketOffset + 1) : ''; // include the [] + const indexHasWHiteSpace: boolean = indexExpression.indexOf(' ') != -1 || indexExpression.indexOf('\t') != -1; + const indexedObjectRef: boolean = dotOffset != -1 && bracketOffset != -1 && dotOffset > bracketOffset ? true : false; + let singleElement: boolean = dotOffset != -1 && ltBbracketOffset != -1 && dotOffset > ltBbracketOffset ? true : false; + if (singleElement && hasWhite && ltParenOffset != -1 && ltParenOffset > whiteOffset) { // if whitespace before paren we have white in statement vs in parameter list singleElement = false; } - if (singleElement && hasWhite) { + if (singleElement && hasWhite && !haveParens) { // if whitespace without parens we have white in statement singleElement = false; } - this._logSPIN(` -- SPIN assignmentRHSStr=[${assignmentRHSStr}], singleElement=(${singleElement})`); + this._logSPIN(` -- SPIN assignmentRHSStr=[${assignmentRHSStr}], singleElement=(${singleElement})`); // XYZZY path 2 - // SPECIAL Ex: scroller[scrollerIndex].initialize() - if (singleElement && this._isPossibleObjectReference(assignmentRHSStr) && assignmentRHSStr.includes('[')) { + // SPECIAL Ex: digits[(numberDigits - 1) - digitIdx].setValue(digitValue) + if (singleElement && this._isPossibleObjectReference(assignmentRHSStr) && indexedObjectRef && indexHasWHiteSpace) { const bHaveObjReference: boolean = this._reportObjectReference(assignmentRHSStr, lineIdx, currentOffset, line, tokenSet); if (bHaveObjReference) { return tokenSet; } } + // special code to handle case range strings: [e.g., SEG_TOP..SEG_BOTTOM:] //const isCaseValue: boolean = assignmentRHSStr.endsWith(':'); //if (isCaseValue && possNames[0].includes("..")) { @@ -5248,7 +5372,7 @@ export class Spin2DocumentSemanticParser { const possNames: string[] = lineInfo.lineParts; const nonStringAssignmentRHSStr: string = lineInfo.lineNoQuotes; this._logSPIN(` -- SPIN possNames=[${possNames}](${possNames.length})`); - const bIsDebugLine: boolean = haveDebugLine(nonStringAssignmentRHSStr); // nonStringAssignmentRHSStr.toLowerCase().indexOf("debug(") != -1 ? true : false; + const bIsDebugLine: boolean = haveDebugLine(nonStringAssignmentRHSStr); const assignmentStringOffset = currentOffset; this._logSPIN(` -- assignmentStringOffset=[${assignmentStringOffset}], bIsDebugLine=(${bIsDebugLine})`); let offsetInNonStringRHS = 0; @@ -5325,10 +5449,10 @@ export class Spin2DocumentSemanticParser { } else { this._logSPIN(` -- EXISTS Global name=[${namePart}], BUT referenceDetails=(${referenceDetails})`); } - if (referenceDetails != undefined && referenceDetails?.type == 'method') { + if (referenceDetails !== undefined && referenceDetails?.type == 'method') { const addressOf = `@${namePart}`; const methodFollowString: string = line.substring(nameOffset + namePart.length); - this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + this._logSPIN(` -- B methodFollowString=[${methodFollowString}](${methodFollowString.length})`); if (!isMethodCall(methodFollowString) && !nonStringAssignmentRHSStr.includes(addressOf)) { this._logSPIN(` -- MISSING parens on method=[${namePart}]`); referenceDetails = undefined; @@ -5346,7 +5470,7 @@ export class Spin2DocumentSemanticParser { }); } else { const methodFollowString: string = line.substring(nameOffset + namePart.length); - this._logSPIN(` -- methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + this._logSPIN(` -- C methodFollowString=[${methodFollowString}](${methodFollowString.length})`); if (this.parseUtils.isMethodOverride(namePart, this.parseUtils.selectedSpinVersion()) && isMethodCall(methodFollowString)) { this._logSPIN(` -- override with method coloring name=[${namePart}](${namePart.length}), ofs=(${nameOffset})`); this._recordToken(tokenSet, line, { @@ -5356,12 +5480,8 @@ export class Spin2DocumentSemanticParser { ptTokenType: 'function', ptTokenModifiers: ['support'] }); - } else if ( - this.parseUtils.isFloatConversion(namePart) && - (nonStringAssignmentRHSStr.indexOf(namePart + '(') == -1 || nonStringAssignmentRHSStr.indexOf(namePart + '()') != -1) - ) { - // FIXME: TODO: replaces name-concat with regEX search past whitespace for '(' (ABOVE LINEs) - this._logSPIN(' -- SPIN MISSING PARENS name=[' + namePart + ']'); + } else if (this.parseUtils.isFloatConversion(namePart) && !isMethodCall(methodFollowString)) { + this._logSPIN(` -- SPIN MISSING PARENS name=[${namePart}]`); this._recordToken(tokenSet, line, { line: lineIdx, startCharacter: nameOffset, @@ -5388,11 +5508,10 @@ export class Spin2DocumentSemanticParser { }); } else if ( this.parseUtils.isSpinBuiltinMethod(namePart) && - !nonStringAssignmentRHSStr.includes(namePart + '(') && + !isMethodCall(methodFollowString) && !this.parseUtils.isSpinNoparenMethod(namePart) ) { - // FIXME: TODO: replaces name-concat with regEX search past whitespace for '(' - this._logSPIN(' -- SPIN MISSING PARENS name=[' + namePart + ']'); + this._logSPIN(` -- SPIN MISSING PARENS name=[${namePart}]`); this._recordToken(tokenSet, line, { line: lineIdx, startCharacter: nameOffset, @@ -5497,6 +5616,67 @@ export class Spin2DocumentSemanticParser { return tokenSet; } + private _reportSPIN_IndexExpression(indexExpression: string, lineIdx: number, startingOffset: number, line: string): IParsedToken[] { + const lineNbr: number = lineIdx + 1; + const tokenSet: IParsedToken[] = []; + this._logSPIN(` -- Ln#${lineNbr} _rsIE() indexExpression=[${indexExpression}](${indexExpression.length})`); + const possNames: string[] = indexExpression.split(/[ \t[\]&+-/]/).filter(Boolean); + const expressionOffset: number = line.indexOf(indexExpression, startingOffset); + this._logSPIN(` -- _rsIE possNames=[${possNames}](${possNames.length})`); + if (indexExpression.length > 0) { + for (let index = 0; index < possNames.length; index++) { + const possSymbolName = possNames[index]; + const nameOffset: number = line.indexOf(possSymbolName, expressionOffset); + if (possSymbolName.charAt(0).match(/[a-zA-Z_]/)) { + let referenceDetails: RememberedToken | undefined = undefined; + if (this.semanticFindings.isLocalToken(possSymbolName)) { + referenceDetails = this.semanticFindings.getLocalTokenForLine(possSymbolName, lineNbr); + this._logSPIN(` -- FOUND local name=[${possSymbolName}], referenceDetails=(${referenceDetails})`); + } else if (this.semanticFindings.isGlobalToken(possSymbolName)) { + referenceDetails = this.semanticFindings.getGlobalToken(possSymbolName); + this._logSPIN(` -- FOUND global name=[${possSymbolName}, referenceDetails=[${referenceDetails}]]`); + } + if (referenceDetails != undefined) { + const modificationArray: string[] = referenceDetails.modifiersWith('modification'); + this._logSPIN(` -- SPIN variableName=[${possSymbolName}], ofs=(${nameOffset})`); + this._recordToken(tokenSet, line, { + line: lineIdx, + startCharacter: nameOffset, + length: possSymbolName.length, + ptTokenType: referenceDetails.type, + ptTokenModifiers: modificationArray + }); + } else { + if ( + !this.parseUtils.isSpinReservedWord(possSymbolName) && + !this.parseUtils.isBuiltinStreamerReservedWord(possSymbolName) && + !this.parseUtils.isDebugControlSymbol(possSymbolName) && + !this.parseUtils.isSpinBuiltinMethod(possSymbolName) + ) { + // we don't have name registered so just mark it + this._logSPIN(` -- SPIN MISSING varname=[${possSymbolName}], ofs=(${nameOffset})`); + this._recordToken(tokenSet, line, { + line: lineIdx, + startCharacter: nameOffset, + length: possSymbolName.length, + ptTokenType: 'variable', + ptTokenModifiers: ['modification', 'missingDeclaration'] + }); + this.semanticFindings.pushDiagnosticMessage( + lineIdx, + nameOffset, + nameOffset + possSymbolName.length, + eSeverity.Error, + `P2 Spin E2 missing declaration [${possSymbolName}]` + ); + } + } + } + } + } + return tokenSet; + } + private _reportSPIN_PAsmCode(lineIdx: number, startingOffset: number, line: string): IParsedToken[] { const tokenSet: IParsedToken[] = []; const lineNbr: number = lineIdx + 1; @@ -6132,21 +6312,21 @@ export class Spin2DocumentSemanticParser { } /* - private isNumeric(val: any): boolean { - // REF https://stackoverflow.com/questions/23437476/in-typescript-how-to-check-if-a-string-is-numeric - let desiredNumericStatus: boolean = false; - if (val.indexOf('%%') == 0) { - desiredNumericStatus = true; - } else if (val.indexOf('%') == 0) { - desiredNumericStatus = true; - } else if (val.indexOf('$') == 0) { - desiredNumericStatus = true; - } else { - desiredNumericStatus = !(val instanceof Array) && val - parseFloat(val) + 1 >= 0; - } - return desiredNumericStatus; - } - */ + private isNumeric(val: any): boolean { + // REF https://stackoverflow.com/questions/23437476/in-typescript-how-to-check-if-a-string-is-numeric + let desiredNumericStatus: boolean = false; + if (val.indexOf('%%') == 0) { + desiredNumericStatus = true; + } else if (val.indexOf('%') == 0) { + desiredNumericStatus = true; + } else if (val.indexOf('$') == 0) { + desiredNumericStatus = true; + } else { + desiredNumericStatus = !(val instanceof Array) && val - parseFloat(val) + 1 >= 0; + } + return desiredNumericStatus; + } + */ private _reportVAR_DeclarationLine(lineIdx: number, startingOffset: number, line: string): IParsedToken[] { const tokenSet: IParsedToken[] = []; @@ -6400,7 +6580,7 @@ export class Spin2DocumentSemanticParser { nameOffset = multiLineSet.offsetIntoLineForPosition(symbolPosition); const bIsParameterName: boolean = this.parseUtils.isNameWithTypeInstantiation(newParameter, eDisplayType); if (bIsParameterName) { - this._logDEBUG(` -- rptDbg newParam=[${newParameter}]`); + this._logDEBUG(` -- rptDbg mA newParam=[${newParameter}]`); this._recordToken(tokenSet, multiLineSet.lineAt(symbolPosition.line), { line: symbolPosition.line, startCharacter: symbolPosition.character, @@ -6452,6 +6632,7 @@ export class Spin2DocumentSemanticParser { !this.parseUtils.isBinaryOperator(newParameter) && !this.parseUtils.isFloatConversion(newParameter) && !this.parseUtils.isSpinBuiltinMethod(newParameter) && + !this.parseUtils.isDebugBitmapColorMode(newParameter) && !this.parseUtils.isBuiltinStreamerReservedWord(newParameter) ) { this._logDEBUG(' -- rptDbg 1 unkParam=[${newParameter}]'); @@ -6467,7 +6648,7 @@ export class Spin2DocumentSemanticParser { symbolPosition.character, symbolPosition.character + newParameter.length, eSeverity.Error, - `P2 Spin debug() A unknown name [${newParameter}]` + `P2 Spin debug() mA unknown name [${newParameter}]` ); } } @@ -6556,7 +6737,7 @@ export class Spin2DocumentSemanticParser { bIsParameterName = true; } if (bIsParameterName) { - this._logDEBUG(` -- rptDbg newParam=[${newParameter}]`); + this._logDEBUG(` -- rptDbg mB newParam=[${newParameter}]`); this._recordToken(tokenSet, multiLineSet.lineAt(symbolPosition.line), { line: symbolPosition.line, startCharacter: symbolPosition.character, @@ -6624,7 +6805,7 @@ export class Spin2DocumentSemanticParser { symbolPosition.character, symbolPosition.character + newParameter.length, eSeverity.Error, - `P2 Spin debug() B unknown name [${newParameter}]` + `P2 Spin debug() mB unknown name [${newParameter}]` ); } } @@ -6729,7 +6910,7 @@ export class Spin2DocumentSemanticParser { symbolPosition.character, symbolPosition.character + newParameter.length, eSeverity.Error, - `P2 Spin debug() C unknown name [${newParameter}]` + `P2 Spin debug() mC unknown name [${newParameter}]` ); } } @@ -6834,7 +7015,7 @@ export class Spin2DocumentSemanticParser { symbolOffset = line.indexOf(newParameter, symbolOffset); const bIsParameterName: boolean = this.parseUtils.isNameWithTypeInstantiation(newParameter, eDisplayType); if (bIsParameterName) { - this._logDEBUG(' -- rptDbg newParam=[' + newParameter + ']'); + this._logDEBUG(' -- rptDbg sA newParam=[' + newParameter + ']'); this._recordToken(tokenSet, line, { line: lineIdx, startCharacter: symbolOffset, @@ -6886,6 +7067,7 @@ export class Spin2DocumentSemanticParser { !this.parseUtils.isBinaryOperator(newParameter) && !this.parseUtils.isFloatConversion(newParameter) && !this.parseUtils.isSpinBuiltinMethod(newParameter) && + !this.parseUtils.isDebugBitmapColorMode(newParameter) && !this.parseUtils.isBuiltinStreamerReservedWord(newParameter) ) { this._logDEBUG(' -- rptDbg 1 unkParam=[' + newParameter + ']'); @@ -6901,7 +7083,7 @@ export class Spin2DocumentSemanticParser { symbolOffset, symbolOffset + newParameter.length, eSeverity.Error, - `P2 Spin debug() A unknown name [${newParameter}]` + `P2 Spin debug() sA unknown name [${newParameter}]` ); } } @@ -6941,7 +7123,7 @@ export class Spin2DocumentSemanticParser { // handle one or more names! do { // (0) register UserName use - this._logDEBUG(' -- rptDbg displayName=[' + displayName + ']'); + this._logDEBUG(` -- rptDbg displayName=[${displayName}]`); this._recordToken(tokenSet, line, { line: lineIdx, startCharacter: symbolOffset, @@ -6949,6 +7131,7 @@ export class Spin2DocumentSemanticParser { ptTokenType: 'displayName', ptTokenModifiers: ['reference'] }); + this._logDEBUG(` -- rptDbg lineParts=[${lineParts}](${lineParts.length})`); symbolOffset += displayName.length + 1; if (firstParamIdx < lineParts.length) { firstParamIdx++; @@ -6982,7 +7165,7 @@ export class Spin2DocumentSemanticParser { bIsParameterName = true; } if (bIsParameterName) { - this._logDEBUG(' -- rptDbg newParam=[' + newParameter + ']'); + this._logDEBUG(' -- rptDbg sB newParam=[' + newParameter + ']'); this._recordToken(tokenSet, line, { line: lineIdx, startCharacter: symbolOffset, @@ -7050,7 +7233,7 @@ export class Spin2DocumentSemanticParser { symbolOffset, symbolOffset + newParameter.length, eSeverity.Error, - `P2 Spin debug() B unknown name [${newParameter}]` + `P2 Spin debug() sB unknown name [${newParameter}]` ); } } @@ -7141,7 +7324,7 @@ export class Spin2DocumentSemanticParser { symbolOffset, symbolOffset + newParameter.length, eSeverity.Error, - `P2 Spin debug() C unknown name [${newParameter}]` + `P2 Spin debug() sC unknown name [${newParameter}]` ); } } @@ -7166,16 +7349,21 @@ export class Spin2DocumentSemanticParser { // but can NOT be ".name" // NOTE" '%' is special object constant override mechanism // NEW adjust dot check to symbol.symbol + // BUG missed an object REF of form: + // digits[(numberDigits - 1) - digitIdx].setValue(digitValue) const dottedSymbolRegex = /[a-zA-Z0-9_]\.[a-zA-Z_]/; // sym.sym - const dottedIndexedSymbolRegex = /[a-zA-Z0-9_]\]\.[a-zA-Z_]/; // sym#sym + const dottedIndexedSymbolRegex = /\]\.[a-zA-Z_]/; // indexExpre].sym const hashedSymbolRegex = /[a-zA-Z0-9_]#[a-zA-Z_]/; // sym#sym - const percentSymbolRegex = /[a-zA-Z0-9_]%[a-zA-Z_]/; // sym#sym + const percentSymbolRegex = /[a-zA-Z0-9_]%[a-zA-Z_]/; // sym%sym const hasSymbolDotSymbol: boolean = dottedSymbolRegex.test(possibleRef); const hasSymbolHashSymbol: boolean = hashedSymbolRegex.test(possibleRef); const hasPercentHashSymbol: boolean = percentSymbolRegex.test(possibleRef); const hasSymbolDOtIndexedSymbol: boolean = dottedIndexedSymbolRegex.test(possibleRef); const possibleRefLC: string = possibleRef.toLowerCase(); const isPartialVariableAccess: boolean = possibleRefLC.includes('.byte[') || possibleRefLC.includes('.word[') || possibleRefLC.includes('.long['); + this._logMessage( + ` -- isObjRef() hasSymbolDotSymbol=(${hasSymbolDotSymbol}), hasSymbolHashSymbol=(${hasSymbolHashSymbol}) hasPercentHashSymbol=(${hasPercentHashSymbol}) hasSymbolDOtIndexedSymbol=(${hasSymbolDOtIndexedSymbol})` + ); const refFoundStatus: boolean = !possibleRef.startsWith('.') && !isPartialVariableAccess && @@ -7189,78 +7377,55 @@ export class Spin2DocumentSemanticParser { // NEW handle objInstanceName[index].constant or objInstanceName[index].constant // NOTE: we allow old P1 style constant references to get here but are then FAILED // NOTE" '%' is special object constant override mechanism to allow this to happen + // NOTE BUG: not handled: + // digits[(numberDigits - 1) - digitIdx].setValue(digitValue) const lineLength: number = line ? line.length : -1; - this._logMessage(`- rptObjectReference() line(${lineIdx + 1}):[${dotReference}], ofs=(${startingOffset}), line=[${line}](${lineLength})`); + const matchOffset: number = line.indexOf(dotReference); + this._logMessage( + `- rptObjectReference() ln#${lineIdx + 1}: dotRef=[${dotReference}], ofs(s/m)=(${startingOffset}/${matchOffset}), line=[${line}](${lineLength})` + ); let bGeneratedReference: boolean = false; if (line && line != null && line.length > 0) { const lineNbr: number = lineIdx + 1; let possibleNameSet: string[] = []; const isP1ObjectConstantRef: boolean = dotReference.includes('#'); const isP2ObjectOverrideConstantRef: boolean = dotReference.includes('%'); - if ((dotReference.includes('.') || dotReference.includes('#') || dotReference.includes('%')) && !dotReference.includes('..')) { - this._logMessage(` -- rObjRef dotReference=[${dotReference}]`); - const symbolOffset: number = line.indexOf(dotReference, startingOffset); // walk this past each - possibleNameSet = dotReference.split(/[.#%]/).filter(Boolean); + if ((dotReference.includes('.') || isP1ObjectConstantRef || isP2ObjectOverrideConstantRef) && !dotReference.includes('..')) { + const symbolOffset: number = line.indexOf(dotReference.trimStart(), startingOffset); // walk this past each + possibleNameSet = dotReference.trimStart().split(/[.#%]/).filter(Boolean); let objInstanceName = possibleNameSet[0]; - const dotLHS: string = objInstanceName; this._logMessage(` -- rObjRef possibleNameSet=[${possibleNameSet}](${possibleNameSet.length})`); let nameParts: string[] = [objInstanceName]; - let indexNames: string | undefined = undefined; + const indexNames: string[] = []; + let objectRefContainsIndex: boolean = false; + // if we have arrayed object instances... + let indexesOffset: number = -1; if (objInstanceName.includes('[')) { - nameParts = objInstanceName.split(/[[\]]/).filter(Boolean); - objInstanceName = nameParts[0]; + // remove parens, brackets, and math ops + const leftBracketOffset: number = line.indexOf('[', symbolOffset); + indexesOffset = leftBracketOffset + 1; + nameParts = objInstanceName.split(/[ ()*+\-/[\]]/).filter(Boolean); + objInstanceName = nameParts[0]; // collect the object instance name + this._logMessage(` -- rObjRef-Idx nameParts=[${nameParts}]`); // FIXME: handle nameParts[1] is likely a local file variable if (nameParts.length > 1) { - indexNames = nameParts[1]; - } - if (indexNames && !indexNames.charAt(0).match(/[a-zA-Z_]/)) { - indexNames = undefined; - } - } - if (indexNames) { - // handle case: instance[index].reference[()] - "index" value - const currentOffset: number = startingOffset; - const namePart = indexNames; - const nameOffset = line.indexOf(namePart, startingOffset); - this._logMessage(` -- rObjRef-Idx searchString=[${namePart}]`); - this._logMessage(` -- rObjRef-Idx nameOffset=(${nameOffset}), currentOffset=(${currentOffset})`); - let referenceDetails: RememberedToken | undefined = undefined; - if (this.semanticFindings.isLocalToken(namePart)) { - referenceDetails = this.semanticFindings.getLocalTokenForLine(namePart, lineNbr); - this._logMessage(` -- FOUND local name=[${namePart}]`); - } else if (this.semanticFindings.isGlobalToken(namePart)) { - referenceDetails = this.semanticFindings.getGlobalToken(namePart); - this._logMessage(` -- FOUND global name=[${namePart}]`); + for (let index = 1; index < nameParts.length; index++) { + const indexName = nameParts[index]; + if (indexName && indexName.charAt(0).match(/[a-zA-Z_]/)) { + indexNames.push(indexName); + } + } } - if (referenceDetails != undefined) { - this._logMessage(` -- rObjRef-Idx name=[${namePart}](${namePart.length}), ofs(${nameOffset})`); - this._recordToken(tokenSet, line, { - line: lineIdx, - startCharacter: nameOffset, - length: namePart.length, - ptTokenType: referenceDetails.type, - ptTokenModifiers: referenceDetails.modifiers - }); - } else { - // have unknown name!? what is it? - this._logSPIN(` -- SPIN Unknown name=[${namePart}]`); - this._recordToken(tokenSet, line, { - line: lineIdx, - startCharacter: nameOffset, - length: namePart.length, - ptTokenType: 'variable', - ptTokenModifiers: ['illegalUse'] - }); - this.semanticFindings.pushDiagnosticMessage( - lineIdx, - nameOffset, - nameOffset + namePart.length, - eSeverity.Error, - `P2 Spin failed to parse index value [${namePart}]` - ); + objectRefContainsIndex = indexNames.length > 0 ? true : false; + + if (objectRefContainsIndex) { + // handle case: instance[index].reference[()] - "index" value + // now too handle case: digits[(numberDigits - 1) - digitIdx].setValue(digitValue) + this.reportsSymbolsForSet('index value', indexesOffset, indexNames, line, lineNbr, tokenSet, lineIdx); } } - // processed objectInstance name and [indexName], now do ref part + // processed objectInstance[index] (indexes now marked) + // now do objectInstance.constant or objectInstance.method() reference if (this.semanticFindings.isNameSpace(objInstanceName)) { let referenceDetails: RememberedToken | undefined = undefined; if (this.semanticFindings.isGlobalToken(objInstanceName)) { @@ -7280,35 +7445,63 @@ export class Spin2DocumentSemanticParser { }); } if (possibleNameSet.length > 1) { - // we have .constant namespace suffix + // we have .constant namespace suffix // XYZZY XYZZY OFFSETS NEED REWORK!!! // determine if this is method has '(' or is constant name - const refParts = possibleNameSet[1].split(/[()]/).filter(Boolean); + // we need to allow objInstance.CONSTANT and fail objectInstance[index].CONSTANT + const refParts: string[] = possibleNameSet[1].split(/[ ()*+,\-/[\]]/).filter(Boolean); + const parameters: string[] = []; + this._logMessage(` -- possibleNameSet[1]=[${possibleNameSet[1]}] split into refParts=[${refParts}](${refParts.length})`); const refPart = refParts[0]; - const referenceOffset = line.indexOf(refPart, symbolOffset + dotLHS.length + 1); - let isMethod: boolean = false; - if (line.substr(referenceOffset + refPart.length, 1) == '(') { - isMethod = true; + //const rhsOffset = line.indexOf(possibleNameSet[1], startingOffset); + const referenceOffset = line.indexOf(refPart, startingOffset); + //const addressOf = `@${refPart}`; + // if it "could" be a method + let isMethod = line.substring(matchOffset).includes('(') ? true : false; + if (isMethod) { + // ok, now let's be really sure! + const methodFollowString: string = line.substring(matchOffset + dotReference.length); + this._logSPIN(` -- ObjRef func Paren chk methodFollowString=[${methodFollowString}](${methodFollowString.length})`); + isMethod = isMethodCall(methodFollowString); } - - referenceDetails = undefined; + if (isMethod && refParts.length > 1) { + // assign all but the first value which is the method name + for (let index = 1; index < refParts.length; index++) { + const parameterName = refParts[index]; + if (parameterName && parameterName.charAt(0).match(/[a-zA-Z_]/)) { + parameters.push(parameterName); + } + } + } + referenceDetails = undefined; // preset to we didn't find a ref... const nameSpaceFindings: DocumentFindings | undefined = this.semanticFindings.getFindingsForNamespace(objInstanceName); if (!isP1ObjectConstantRef && nameSpaceFindings) { referenceDetails = nameSpaceFindings.getPublicToken(refPart); this._logMessage(` -- LookedUp Object-global token [${refPart}] got [${referenceDetails}]`); + // NOTE: in @instance[index].method the normal parenthesis are missing... + // if the lookup sees that it's a method let's override the failed hunt for (...) + if (referenceDetails?._type == 'method') { + isMethod = true; + this._logMessage(` -- rObjRef OVERRIDE isMethod (object lookup says it is!)`); + } } - if (referenceDetails) { + this._logMessage( + ` -- rObjRef isMethod=(${isMethod}), isP1ObjectConstRef=(${isP1ObjectConstantRef}), objectRefHasIndex=(${objectRefContainsIndex})` + ); + if (referenceDetails && (isMethod || (!isMethod && !objectRefContainsIndex))) { + // we need to allow objInstance.CONSTANT and fail objectInstance[index].CONSTANT const constantPart: string = possibleNameSet[1]; + const constantOffset: number = line.indexOf(constantPart, matchOffset + possibleNameSet[0].length); const tokenModifiers: string[] = isMethod ? [] : ['readonly']; - this._logMessage(' -- rObjRef rhs constant=[' + constantPart + '](' + (referenceOffset + 1) + ') (' + referenceDetails.type + ')'); + this._logMessage(` -- rObjRef rhs constant=[${constantPart}], ofs=(${referenceOffset + 1}) (${referenceDetails.type})`); this._recordToken(tokenSet, line, { line: lineIdx, - startCharacter: referenceOffset, + startCharacter: constantOffset, length: refPart.length, ptTokenType: referenceDetails.type, ptTokenModifiers: tokenModifiers }); } else { - this._logMessage(' -- rObjRef Error refPart=[' + refPart + '](' + (referenceOffset + 1) + ')'); + this._logMessage(` -- rObjRef Error refPart=[${refPart}](${referenceOffset + 1})`); this._recordToken(tokenSet, line, { line: lineIdx, startCharacter: referenceOffset, @@ -7316,7 +7509,17 @@ export class Spin2DocumentSemanticParser { ptTokenType: 'variable', ptTokenModifiers: ['illegalUse'] }); - if (!isP1ObjectConstantRef) { + if (!isMethod && objectRefContainsIndex) { + const refType: string = 'Constant'; + const adjustedName: string = refPart; + this.semanticFindings.pushDiagnosticMessage( + lineIdx, + referenceOffset, + referenceOffset + refPart.length, + eSeverity.Error, + `P2 Spin Object ${refType} form "${objInstanceName}[...].${adjustedName}" not allowed, (use "${objInstanceName}.${adjustedName}" instead)` + ); + } else if (!isP1ObjectConstantRef) { const refType: string = isMethod ? 'Method' : 'Constant'; const adjustedName: string = isMethod ? `${refPart}()` : refPart; this.semanticFindings.pushDiagnosticMessage( @@ -7324,7 +7527,7 @@ export class Spin2DocumentSemanticParser { referenceOffset, referenceOffset + refPart.length, eSeverity.Error, - `Object ${refType} [${adjustedName}] not found in [${objInstanceName}]` + `P2 Spin Object ${refType} [${adjustedName}] not found in [${objInstanceName}]` ); } else { // have old style P1 Constant ref @@ -7339,6 +7542,10 @@ export class Spin2DocumentSemanticParser { ); } } + // now handle any parameters + if (isMethod && parameters.length > 0) { + this.reportsSymbolsForSet('parameter value', referenceOffset + refPart.length, parameters, line, lineNbr, tokenSet, lineIdx); + } } } } else { @@ -7423,6 +7630,60 @@ export class Spin2DocumentSemanticParser { return bGeneratedReference; } + private reportsSymbolsForSet( + parseType: string, + startingOffset: number, + nameSet: string[], + line: string, + lineNbr: number, + tokenSet: IParsedToken[], + lineIdx: number + ) { + this._logMessage(` -- rObjRef-Set startingOffset=(${startingOffset}), nameSet=[${nameSet}]`); + const currentOffset: number = startingOffset; + for (let index = 0; index < nameSet.length; index++) { + const namePart = nameSet[index]; + const nameOffset = line.indexOf(namePart, startingOffset); + this._logMessage(` -- rObjRef-Set searchString=[${namePart}]`); + this._logMessage(` -- rObjRef-Set nameOffset=(${nameOffset}), currentOffset=(${currentOffset})`); + let referenceDetails: RememberedToken | undefined = undefined; + if (this.semanticFindings.isLocalToken(namePart)) { + referenceDetails = this.semanticFindings.getLocalTokenForLine(namePart, lineNbr); + this._logMessage(` -- FOUND local name=[${namePart}]`); + } else if (this.semanticFindings.isGlobalToken(namePart)) { + referenceDetails = this.semanticFindings.getGlobalToken(namePart); + this._logMessage(` -- FOUND global name=[${namePart}]`); + } + if (referenceDetails != undefined) { + this._logMessage(` -- rObjRef-Set name=[${namePart}](${namePart.length}), ofs(${nameOffset})`); + this._recordToken(tokenSet, line, { + line: lineIdx, + startCharacter: nameOffset, + length: namePart.length, + ptTokenType: referenceDetails.type, + ptTokenModifiers: referenceDetails.modifiers + }); + } else { + // have unknown name!? what is it? + this._logSPIN(` -- SPIN Unknown name=[${namePart}]`); + this._recordToken(tokenSet, line, { + line: lineIdx, + startCharacter: nameOffset, + length: namePart.length, + ptTokenType: 'variable', + ptTokenModifiers: ['illegalUse'] + }); + this.semanticFindings.pushDiagnosticMessage( + lineIdx, + nameOffset, + nameOffset + namePart.length, + eSeverity.Error, + `P2 Spin failed to parse ${parseType} [${namePart}]` + ); + } + } + } + private _reportDebugStringsMultiLine(startingOffset: number, multiLineSet: ContinuedLines): IParsedToken[] { const tokenSet: IParsedToken[] = []; this._logDEBUG(`- Ln#${multiLineSet.lineStartIdx + 1} rtpDbgStrMulti line=[${multiLineSet.line}], lns=(${multiLineSet.numberLines})`); @@ -7759,7 +8020,7 @@ export class Spin2DocumentSemanticParser { } private _logMessage(message: string): void { - if (this.spin2DebugLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. this.ctx.logger.log(message); } @@ -7844,7 +8105,7 @@ export class Spin2DocumentSemanticParser { bHaveDocComment: boolean, line: string ): IParsedToken | undefined { - //this._logMessage(" -- gNCL-RC commentOffset=(" + commentOffset + "), bHaveDocComment=[" + bHaveDocComment + "], line=[" + line + "]"); + this._logMessage(` -- gNCL-RC startIdx=(${startIdx}), bHaveDocComment=[${bHaveDocComment}], line=[${line}]`); let desiredToken: IParsedToken | undefined = undefined; if (line.length > 0) { //const commentDocModifiers: string[] = bHaveBlockComment ? ["block", "documentation"] : ["line", "documentation"]; // A NO @@ -7900,10 +8161,11 @@ export class Spin2DocumentSemanticParser { // split(/[ \t\-\:\,\+\[\]\@\(\)\!\*\=\<\>\&\|\?\\\~\#\^\/]/); // mods to allow returning of objInstanceName#constant form of names // SPECIAL form: - // dont' initially remove [...] square brackets or periods + // don't initially remove [...] square brackets or periods // in second pass, if item dosn't have period but does have [...] then split a [] and push parts // const nonEqualsLine: string = this.parseUtils.removeDoubleQuotedStrings(line); + // first split const lineParts: string[] | null = nonEqualsLine.match(/[^ \t\-:,+@()!*=<>&|?\\~^/]+/g); this._logMessage(` -- gnwsplna line=[${line}](${line.length})`); const partsLen: number = lineParts == null ? 0 : lineParts.length; @@ -7923,7 +8185,7 @@ export class Spin2DocumentSemanticParser { } else if (name.endsWith('#')) { tempName = name.slice(0, -1); // remove last char } - ///now... + // 2nd split if (!tempName.includes('.') && (tempName.includes('[') || tempName.includes(']'))) { const moreParts: string[] = tempName.split(/[[\]]/).filter(Boolean); for (let index = 0; index < moreParts.length; index++) { @@ -7994,16 +8256,94 @@ export class Spin2DocumentSemanticParser { return desiredInterp; } - private _checkTokenSet(tokenSet: IParsedToken[]): void { + private _checkTokenSetOLD(tokenSet: IParsedToken[]): void { this._logMessage('\n---- Checking ' + tokenSet.length + ' tokens. ----'); tokenSet.forEach((parsedToken) => { - if (parsedToken.length == undefined || parsedToken.startCharacter == undefined) { + if (parsedToken.length === undefined || parsedToken.startCharacter === undefined) { this._logMessage('- BAD Token=[' + parsedToken + ']'); } }); this._logMessage('---- Check DONE ----\n'); } + private _checkTokenSet(tokenSet: IParsedToken[], text: string): IParsedToken[] { + // Sort tokens by lineNumber and startCharacter + const lines = text.split(/\r\n|\r|\n/); + + // sort the tokens + tokenSet.sort((a, b) => { + if (a.line === b.line) { + return a.startCharacter - b.startCharacter; + } + return a.line - b.line; + }); + let removedTokens: number = 0; + const filteredTokenSet: IParsedToken[] = []; + this._logMessage(`\n---- Checking ${tokenSet.length} tokens. ----`); + tokenSet.forEach((parsedToken) => { + // if badly created token, don't submit it! + if (parsedToken.length === undefined || parsedToken.startCharacter === undefined) { + this._logMessage(`- BAD Token=[${parsedToken}]`); + } else { + // NEW remove tokens for disabled lines + if (!this.semanticFindings.preProcIsLineDisabled(parsedToken.line + 1)) { + filteredTokenSet.push(parsedToken); + } else { + if (removedTokens < 10) { + this._logMessage(`* RMV #${removedTokens + 1}, token=<${this.stringForToken(parsedToken)}>`); + } + removedTokens++; + } + } + }); + + // NEW add disabled-line tokens + let addedTokens: number = 0; + const disabledLineRanges: Range[] = this.semanticFindings.preProcDisabledRanges(); + for (const disabledRange of disabledLineRanges) { + for (let lineNbr = disabledRange.start.line; lineNbr <= disabledRange.end.line; lineNbr++) { + const line: string = lines[lineNbr - 1]; + if (line === undefined) { + this._logMessage(`ERROR: [CODE] BAD Range<(${disabledRange.start.line}, 0) - (${disabledRange.end.line}, 0)>`); + } else { + const newToken: IParsedToken = { + line: lineNbr - 1, + startCharacter: 0, + length: line.length, + ptTokenType: 'string', + ptTokenModifiers: ['disabled'] + }; + filteredTokenSet.push(newToken); + if (addedTokens < 10) { + this._logMessage(`* ADD #${addedTokens + 1}, token=<${this.stringForToken(newToken)}>`); + } + addedTokens++; + } + } + } + // reSort the tokens + filteredTokenSet.sort((a, b) => { + if (a.line === b.line) { + return a.startCharacter - b.startCharacter; + } + return a.line - b.line; + }); + let checkNbr: number = 1; + filteredTokenSet.forEach((parsedToken) => { + if (parsedToken.line >= 2854 && parsedToken.line <= 2861) { + this._logMessage(`* CHK #${checkNbr}, token=<${this.stringForToken(parsedToken)}>`); + checkNbr++; + } + }); + this._logMessage(`---- Check DONE ---- countNow=(${tokenSet.length}), removed=(${removedTokens}), added=(${addedTokens})\n`); + return filteredTokenSet; + } + + private stringForToken(token: IParsedToken): string { + const lenStr: string = token.length == Number.MAX_VALUE ? '{MAX}' : `${token.length - 1}`; + return `TOK: Ln#${token.line}(${token.startCharacter}-${lenStr}) [${token.ptTokenType}], [${token.ptTokenModifiers}]`; + } + private _directiveString(aDirective: ISpin2Directive): string { const desiredInterp: string = ' -- directive=[Ln#' + aDirective.lineNumber + ',typ:' + aDirective.displayType + '[' + aDirective.eDisplayType + '])]'; diff --git a/spin2/server/src/parser/spin2.documentSymbolParser.ts b/spin2/server/src/parser/spin2.documentSymbolParser.ts index ced50ad..149f634 100644 --- a/spin2/server/src/parser/spin2.documentSymbolParser.ts +++ b/spin2/server/src/parser/spin2.documentSymbolParser.ts @@ -15,7 +15,7 @@ import { eParseState } from './spin.common'; // the DocumentFindings object assiciated with this file // export class Spin2DocumentSymbolParser { - private spin2OutlineLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private bLogStarted: boolean = false; private parseUtils = new Spin2ParseUtils(); @@ -23,7 +23,7 @@ export class Spin2DocumentSymbolParser { private symbolsFound: DocumentFindings | undefined = undefined; public constructor(protected readonly ctx: Context) { - if (this.spin2OutlineLogEnabled) { + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; //Create output channel @@ -39,7 +39,7 @@ export class Spin2DocumentSymbolParser { let priorState: eParseState = currState; let prePasmState: eParseState = currState; this.symbolsFound = findings; - if (this.spin2OutlineLogEnabled) { + if (this.isDebugLogEnabled) { this.symbolsFound.enableLogging(this.ctx); } @@ -90,10 +90,9 @@ export class Spin2DocumentSymbolParser { continue; } else if (currState == eParseState.inMultiLineComment) { // in multi-line non-doc-comment, hunt for end '}' to exit - const openingOffset = nonCommentLine.indexOf('{'); - const closingOffset = nonCommentLine.indexOf('}', openingOffset + 1); - const haveInlineCmt: boolean = openingOffset != -1 && closingOffset != -1 && openingOffset < closingOffset; - if (!haveInlineCmt && closingOffset != -1) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [closingMultiline, closingOffset] = this._haveUnmatchCloseOnLine(line, '}'); + if (closingMultiline) { // have close, comment ended currState = priorState; } @@ -185,12 +184,23 @@ export class Spin2DocumentSymbolParser { methodName = lineParts[0].trim(); } // NOTE this changed to METHOD when we added global labels which are to be Functions! - const methodSymbol: OutLineSymbol = new OutLineSymbol(linePrefix + ' ' + methodName, methodScope, lsp.SymbolKind.Method, lineRange); + const methodSymbolKind: lsp.SymbolKind = linePrefix == 'PUB' ? lsp.SymbolKind.Method : lsp.SymbolKind.Field; + const methodSymbol: OutLineSymbol = new OutLineSymbol(linePrefix + ' ' + methodName, methodScope, methodSymbolKind, lineRange); this.setContainerSymbol(methodSymbol); } else { // start CON/VAR/OBJ/DAT const sectionComment = lineHasComment ? line.substr(commentOffset, commentLength) : ''; - const blockSymbol: OutLineSymbol = new OutLineSymbol(linePrefix + ' ' + sectionComment, '', lsp.SymbolKind.Field, lineRange); + let blockSymbolKind: lsp.SymbolKind = lsp.SymbolKind.Variable; + if (linePrefix == 'CON') { + blockSymbolKind = lsp.SymbolKind.Method; + } else if (linePrefix == 'DAT') { + blockSymbolKind = lsp.SymbolKind.EnumMember; + } else if (linePrefix == 'OBJ') { + blockSymbolKind = lsp.SymbolKind.Class; + } else if (linePrefix == 'VAR') { + blockSymbolKind = lsp.SymbolKind.Variable; + } + const blockSymbol: OutLineSymbol = new OutLineSymbol(linePrefix + ' ' + sectionComment, '', blockSymbolKind, lineRange); this.setContainerSymbol(blockSymbol); // HANDLE label declaration on DAT line! if (linePrefix == 'DAT') { @@ -210,7 +220,8 @@ export class Spin2DocumentSymbolParser { posssibleLabel = undefined; // Nope! } if (posssibleLabel) { - const labelSymbol: OutLineSymbol = new OutLineSymbol(lineParts[1], '', lsp.SymbolKind.Constant, lineRange); + //const labelSymbol: OutLineSymbol = new OutLineSymbol(lineParts[1], '', lsp.SymbolKind.Constant, lineRange); + const labelSymbol: OutLineSymbol = new OutLineSymbol(lineParts[1], '', lsp.SymbolKind.String, lineRange); if (this.containerDocSymbol) { this.containerDocSymbol.addChild(labelSymbol); } @@ -226,11 +237,11 @@ export class Spin2DocumentSymbolParser { if (currState == eParseState.inPAsmInline) { // process pasm (assembly) lines if (trimmedLine.length > 0) { - this._logMessage(' scan inPAsmInline Ln#' + (i + 1) + ' nonCommentLine=[' + nonCommentLine + ']'); + this._logMessage(` scan inPAsmInline Ln#${i + 1} nonCommentLine=[${nonCommentLine}]`); const lineParts: string[] = nonCommentLine.split(/[ \t]/).filter(Boolean); if (lineParts.length > 0 && lineParts[0].toUpperCase() == 'END') { currState = prePasmState; - this._logMessage(' scan END-InLine Ln#' + (i + 1) + ' POP currState=[' + currState + ']'); + this._logMessage(` scan END-InLine Ln#${i + 1} POP currState=[${eParseState[currState]}]`); // and ignore rest of this line continue; } @@ -253,7 +264,7 @@ export class Spin2DocumentSymbolParser { this._logMessage(' - pre-scan DAT line trimmedLine=[' + trimmedLine + '] now Dat PASM'); prePasmState = currState; currState = eParseState.inDatPAsm; - this._logMessage(' scan START DATPasm Ln#' + (i + 1) + ' PUSH currState=[' + prePasmState + ']'); + this._logMessage(` scan START DATPasm Ln#${i + 1} PUSH currState=[${eParseState[prePasmState]}]`); // and ignore rest of this line global_label = this._getOlnDAT_PasmDeclaration(0, line); // let's get possible label on this ORG statement } @@ -271,7 +282,7 @@ export class Spin2DocumentSymbolParser { this._logMessage(' - (' + (i + 1) + '): outline PUB/PRI line trimmedLine=[' + trimmedLine + ']'); prePasmState = currState; currState = eParseState.inPAsmInline; - this._logMessage(' scan START-InLine Ln#' + (i + 1) + ' PUSH currState=[' + prePasmState + ']'); + this._logMessage(` scan START-InLine Ln#${i + 1} PUSH currState=[${eParseState[prePasmState]}]`); // and ignore rest of this line continue; } @@ -280,7 +291,8 @@ export class Spin2DocumentSymbolParser { if (global_label) { // was Variable: sorta OK (image good, color bad) // was Constant: sorta OK (image good, color bad) SAME - const labelSymbol: OutLineSymbol = new OutLineSymbol(global_label, '', lsp.SymbolKind.Constant, lineRange); + //const labelSymbol: OutLineSymbol = new OutLineSymbol(global_label, '', lsp.SymbolKind.Constant, lineRange); + const labelSymbol: OutLineSymbol = new OutLineSymbol(global_label, '', lsp.SymbolKind.String, lineRange); // if we have a container add to container, else just record it if (this.containerDocSymbol) { this.containerDocSymbol.addChild(labelSymbol); @@ -297,6 +309,28 @@ export class Spin2DocumentSymbolParser { this.containerDocSymbol = undefined; } } + private _haveUnmatchCloseOnLine(line: string, searchChar: string): [boolean, number] { + let unmatchedCloseStatus: boolean = false; + let matchOffset: number = 0; + const closeString: string = searchChar; + const openString: string = searchChar == '}' ? '{' : '{{'; + const matchLen: number = searchChar.length; + let nestLevel: number = 0; + if (line.length >= searchChar.length) { + for (let offset = 0; offset < line.length; offset++) { + const matchString = line.substring(offset, offset + matchLen); + if (matchString == openString) { + nestLevel++; + } else if (matchString == closeString) { + matchOffset = offset; + nestLevel--; + } + } + } + unmatchedCloseStatus = nestLevel == -1 ? true : false; + this._logMessage(` -- _haveUnmatchCloseOnLine() isClosed=(${unmatchedCloseStatus}), ofs=(${matchOffset}) line=[${line}](${line.length})`); + return [unmatchedCloseStatus, matchOffset]; + } private setContainerSymbol(newSymbol: OutLineSymbol): void { // report symbol, possible container symbol, then start a new container @@ -308,7 +342,7 @@ export class Spin2DocumentSymbolParser { } private _logMessage(message: string): void { - if (this.spin2OutlineLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. this.ctx.logger.log(message); } diff --git a/spin2/server/src/parser/spin2.utils.ts b/spin2/server/src/parser/spin2.utils.ts index 7f3e777..7a773de 100644 --- a/spin2/server/src/parser/spin2.utils.ts +++ b/spin2/server/src/parser/spin2.utils.ts @@ -1093,6 +1093,15 @@ export class Spin2ParseUtils { event_xro: '(12) event Streamer NCO-rollover' }; + public isBuiltInSmartPinReservedWord(name: string): boolean { + const nameKey: string = name.toLowerCase(); + let reservedStatus: boolean = false; + if (!reservedStatus) { + reservedStatus = nameKey in this._tableSmartPinNames; + } + return reservedStatus; + } + public isBuiltinStreamerReservedWord(name: string): boolean { // streamer constants, smart-pin constants const builtinNamesOfNote: string[] = [ @@ -2460,6 +2469,17 @@ export class Spin2ParseUtils { return desiredDocText; } + public lineStartsWithFlexspinPreprocessorDirective(line: string): boolean { + let lineIsDirectiveStatus: boolean = false; + if (line && line.length > 0) { + const lineParts: string[] = line.split(/[ \t]/).filter(Boolean); + if (lineParts.length > 0) { + lineIsDirectiveStatus = this.isFlexspinPreprocessorDirective(lineParts[0]); + } + } + return lineIsDirectiveStatus; + } + public isFlexspinPreprocessorDirective(name: string): boolean { const flexspinDirectiveOfNote: string[] = [ '#define', @@ -3369,6 +3389,9 @@ export class Spin2ParseUtils { break; case eDebugDisplayType.ddtBitmap: nameStatus = this.isDebugBitmapDeclarationParam(newParameter); + if (!nameStatus) { + nameStatus = this.isDebugBitmapColorMode(newParameter); + } break; case eDebugDisplayType.ddtMidi: nameStatus = this.isDebugMidiDeclarationParam(newParameter); @@ -3626,6 +3649,9 @@ export class Spin2ParseUtils { case eDebugDisplayType.ddtPlot: bHasColorMode = true; break; + case eDebugDisplayType.ddtTerm: // ?? demo shipped has this here??? not in DOCs??? + bHasColorMode = true; + break; default: break; } diff --git a/spin2/server/src/providers/DefinitionProvider.ts b/spin2/server/src/providers/DefinitionProvider.ts index c8862c3..87637a7 100644 --- a/spin2/server/src/providers/DefinitionProvider.ts +++ b/spin2/server/src/providers/DefinitionProvider.ts @@ -51,13 +51,13 @@ export interface FindingsAtPostion { } export default class DefinitionProvider implements Provider { - private defnLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private bLogStarted: boolean = false; private extensionUtils: ExtensionUtils; constructor(protected readonly ctx: Context) { - this.extensionUtils = new ExtensionUtils(ctx, this.defnLogEnabled); - if (this.defnLogEnabled) { + this.extensionUtils = new ExtensionUtils(ctx, this.isDebugLogEnabled); + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; this._logMessage('Spin Hover log started.'); @@ -72,7 +72,7 @@ export default class DefinitionProvider implements Provider { * @returns nothing */ private _logMessage(message: string): void { - if (this.defnLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. this.ctx.logger.log(message); } @@ -90,7 +90,7 @@ export default class DefinitionProvider implements Provider { return []; // empty case } const symbolsFound: DocumentFindings = documentFindings; - symbolsFound.enableLogging(this.ctx, this.defnLogEnabled); + symbolsFound.enableLogging(this.ctx, this.isDebugLogEnabled); const symbolIdent: FindingsAtPostion | undefined = this.symbolAtLocation(processed.document, position, symbolsFound); if (!symbolIdent) { diff --git a/spin2/server/src/providers/DocumentSymbolProvider.ts b/spin2/server/src/providers/DocumentSymbolProvider.ts index 75953a4..fc3acf3 100644 --- a/spin2/server/src/providers/DocumentSymbolProvider.ts +++ b/spin2/server/src/providers/DocumentSymbolProvider.ts @@ -44,7 +44,7 @@ export default class DocumentSymbolProvider implements Provider { range: currChildSymbol.location(), selectionRange: currChildSymbol.location() }; - if (newLspSymbol.children == undefined) { + if (newLspSymbol.children === undefined) { newLspSymbol.children = []; } newLspSymbol.children.push(newChildLspSymbol); diff --git a/spin2/server/src/providers/FoldingRangeProvider.ts b/spin2/server/src/providers/FoldingRangeProvider.ts index 52bd792..0e62f43 100644 --- a/spin2/server/src/providers/FoldingRangeProvider.ts +++ b/spin2/server/src/providers/FoldingRangeProvider.ts @@ -6,13 +6,13 @@ import { fileSpecFromURI } from '../parser/lang.utils'; import { DocumentFindings, IFoldSpan, eFoldSpanType } from '../parser/spin.semantic.findings'; export default class FoldingRangeProvider implements Provider { - private foldingLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private bLogStarted: boolean = false; private extensionUtils: ExtensionUtils; constructor(protected readonly ctx: Context) { - this.extensionUtils = new ExtensionUtils(ctx, this.foldingLogEnabled); - if (this.foldingLogEnabled) { + this.extensionUtils = new ExtensionUtils(ctx, this.isDebugLogEnabled); + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; this._logMessage('Spin Folding log started.'); @@ -27,7 +27,7 @@ export default class FoldingRangeProvider implements Provider { * @returns nothing */ private _logMessage(message: string): void { - if (this.foldingLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. this.ctx.logger.log(message); } @@ -45,7 +45,7 @@ export default class FoldingRangeProvider implements Provider { return []; // empty case } const symbolsFound: DocumentFindings = documentFindings; - symbolsFound.enableLogging(this.ctx, this.foldingLogEnabled); + symbolsFound.enableLogging(this.ctx, this.isDebugLogEnabled); const foldSpans: IFoldSpan[] = symbolsFound.allFoldSpans(); diff --git a/spin2/server/src/providers/HoverProvider.ts b/spin2/server/src/providers/HoverProvider.ts index b7c19ea..780bbfb 100644 --- a/spin2/server/src/providers/HoverProvider.ts +++ b/spin2/server/src/providers/HoverProvider.ts @@ -19,7 +19,7 @@ import { eBuiltInType, isMethodCall } from '../parser/spin.common'; import { isSpin1File, fileSpecFromURI } from '../parser/lang.utils'; export default class HoverProvider implements Provider { - private hoverLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private bLogStarted: boolean = false; private symbolsFound: DocumentFindings = new DocumentFindings(); // this gets replaced @@ -28,8 +28,8 @@ export default class HoverProvider implements Provider { private spin1File: boolean = false; constructor(protected readonly ctx: Context) { - this.extensionUtils = new ExtensionUtils(ctx, this.hoverLogEnabled); - if (this.hoverLogEnabled) { + this.extensionUtils = new ExtensionUtils(ctx, this.isDebugLogEnabled); + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; this._logMessage('Spin Hover log started.'); @@ -50,7 +50,7 @@ export default class HoverProvider implements Provider { return null; } this.symbolsFound = documentFindings; - this.symbolsFound.enableLogging(this.ctx, this.hoverLogEnabled); + this.symbolsFound.enableLogging(this.ctx, this.isDebugLogEnabled); this.spin1File = isSpin1File(docFSpec); this.parseUtils = this.spin1File ? new Spin1ParseUtils() : new Spin2ParseUtils(); @@ -70,7 +70,7 @@ export default class HoverProvider implements Provider { * @returns nothing */ private _logMessage(message: string): void { - if (this.hoverLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. this.ctx.logger.log(message); } @@ -234,7 +234,7 @@ export default class HoverProvider implements Provider { if (tmpSymbolsSet) { isObjectReference = true; symbolsSet = tmpSymbolsSet; - symbolsSet.enableLogging(this.ctx, this.hoverLogEnabled); + symbolsSet.enableLogging(this.ctx, this.isDebugLogEnabled); } } } diff --git a/spin2/server/src/providers/SemanticTokensProvider.ts b/spin2/server/src/providers/SemanticTokensProvider.ts index df511ba..0e41e89 100644 --- a/spin2/server/src/providers/SemanticTokensProvider.ts +++ b/spin2/server/src/providers/SemanticTokensProvider.ts @@ -45,6 +45,7 @@ export default class SemanticTokensProvider implements Provider { private tokenModifiersLegend = [ 'declaration', + 'disabled', 'documentation', 'readonly', 'static', @@ -61,7 +62,7 @@ export default class SemanticTokensProvider implements Provider { 'illegalUse' ]; - private semanticLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private bLogStarted: boolean = false; //private namedRegs: lsp.CompletionItem[]; @@ -75,7 +76,7 @@ export default class SemanticTokensProvider implements Provider { */ this.tokenTypesLegend.forEach((tokenType, index) => this.tokenTypes.set(tokenType, index)); this.tokenModifiersLegend.forEach((tokenModifier, index) => this.tokenModifiers.set(tokenModifier, index)); - if (this.semanticLogEnabled) { + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; //Create output channel @@ -87,7 +88,7 @@ export default class SemanticTokensProvider implements Provider { } private _logMessage(message: string): void { - if (this.semanticLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. this.ctx.logger.log(message); } diff --git a/spin2/server/src/providers/SignatureHelpProvider.ts b/spin2/server/src/providers/SignatureHelpProvider.ts index e9fc002..c7a5034 100644 --- a/spin2/server/src/providers/SignatureHelpProvider.ts +++ b/spin2/server/src/providers/SignatureHelpProvider.ts @@ -18,7 +18,7 @@ import { IBuiltinDescription, eBuiltInType } from '../parser/spin.common'; import { isSpin1File, fileSpecFromURI } from '../parser/lang.utils'; export default class SignatureHelpProvider implements Provider { - private signatureLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit + private isDebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private bLogStarted: boolean = false; private symbolsFound: DocumentFindings = new DocumentFindings(); // this gets replaced @@ -27,8 +27,8 @@ export default class SignatureHelpProvider implements Provider { private spin1File: boolean = false; constructor(protected readonly ctx: Context) { - this.extensionUtils = new ExtensionUtils(ctx, this.signatureLogEnabled); - if (this.signatureLogEnabled) { + this.extensionUtils = new ExtensionUtils(ctx, this.isDebugLogEnabled); + if (this.isDebugLogEnabled) { if (this.bLogStarted == false) { this.bLogStarted = true; this._logMessage('Spin signatureHelp log started.'); @@ -49,7 +49,7 @@ export default class SignatureHelpProvider implements Provider { return null; } this.symbolsFound = documentFindings; - this.symbolsFound.enableLogging(this.ctx, this.signatureLogEnabled); + this.symbolsFound.enableLogging(this.ctx, this.isDebugLogEnabled); this.spin1File = isSpin1File(docFSpec); this.parseUtils = this.spin1File ? new Spin1ParseUtils() : new Spin2ParseUtils(); @@ -72,7 +72,7 @@ export default class SignatureHelpProvider implements Provider { * @returns nothing */ private _logMessage(message: string): void { - if (this.signatureLogEnabled) { + if (this.isDebugLogEnabled) { //Write to output window. this.ctx.logger.log(message); } @@ -244,7 +244,7 @@ export default class SignatureHelpProvider implements Provider { if (tmpSymbolsSet) { isObjectReference = true; symbolsSet = tmpSymbolsSet; - symbolsSet.enableLogging(this.ctx, this.signatureLogEnabled); + symbolsSet.enableLogging(this.ctx, this.isDebugLogEnabled); } } } diff --git a/spin2/server/src/providers/TextDocumentSyncProvider.ts b/spin2/server/src/providers/TextDocumentSyncProvider.ts index 10a3479..c273693 100644 --- a/spin2/server/src/providers/TextDocumentSyncProvider.ts +++ b/spin2/server/src/providers/TextDocumentSyncProvider.ts @@ -37,6 +37,7 @@ export default class TextDocumentSyncProvider implements Provider { // ensure we have the clint settings before proceeding if (this.ctx.parserConfig.maxNumberOfReportedIssues == -1) { + this.ctx.logger.log(`TRC: LOAD client configuration`); await this.getLatestClientConfig(); } diff --git a/spin2/syntaxes/spin2.tmLanguage.YAML-tmLanguage b/spin2/syntaxes/spin2.tmLanguage.YAML-tmLanguage index 0661e8a..4b44e8c 100644 --- a/spin2/syntaxes/spin2.tmLanguage.YAML-tmLanguage +++ b/spin2/syntaxes/spin2.tmLanguage.YAML-tmLanguage @@ -67,6 +67,9 @@ repository: - include: "#configuration" - include: "#configuration" - include: "#constants" + - include: "#event_constants" + - include: "#smartpins_constants" + - include: "#streamer_constants" - include: "#string" - include: "#number" - include: "#debug_constants" diff --git a/spin2/syntaxes/spin2.tmLanguage.json b/spin2/syntaxes/spin2.tmLanguage.json index a4d714a..f13af4e 100644 --- a/spin2/syntaxes/spin2.tmLanguage.json +++ b/spin2/syntaxes/spin2.tmLanguage.json @@ -128,6 +128,15 @@ { "include": "#constants" }, + { + "include": "#event_constants" + }, + { + "include": "#smartpins_constants" + }, + { + "include": "#streamer_constants" + }, { "include": "#string" }, diff --git a/spin2/themes/isp-spin-dark-bg-theme.json b/spin2/themes/isp-spin-dark-bg-theme.json index e7ef601..a375072 100644 --- a/spin2/themes/isp-spin-dark-bg-theme.json +++ b/spin2/themes/isp-spin-dark-bg-theme.json @@ -33,8 +33,8 @@ "editor.foreground": "#5c5c5c", "editor.inactiveSelectionBackground": "#888888e0", "editor.selectionBackground": "#BBBBBBe0", - "editor.selectionHighlightBackground": "#d59880", - "editorCursor.foreground": "#000000", + "editor.selectionHighlightBackground": "#80d59880", + "editorCursor.foreground": "#000000", "editorGroup.border": "#ffffff17", "editorGroupHeader.tabsBackground": "#181818", "editorGroupHeader.tabsBorder": "#ffffff15", @@ -140,7 +140,7 @@ "terminal.foreground": "#cccccc", "terminal.inactiveSelectionBackground": "#3A3D41", "terminal.tab.activeBorder": "#0078d4", - "terminalCursor.foreground": "#CCCCCC", + "terminalCursor.foreground": "#CCCCCC", "textBlockQuote.background": "#010409", "textBlockQuote.border": "#ffffff14", "textCodeBlock.background": "#6e768166", @@ -185,7 +185,12 @@ } }, { - "scope": ["constant.character.character-class.regexp", "constant.character.set.regexp", "constant.other.character-class.regexp", "constant.other.character-class.set.regexp"], + "scope": [ + "constant.character.character-class.regexp", + "constant.character.set.regexp", + "constant.other.character-class.regexp", + "constant.other.character-class.set.regexp" + ], "settings": { "foreground": "#d16969" } @@ -198,7 +203,13 @@ }, { "name": "Constant", - "scope": ["constant.language", "keyword.operator.binary.spin2", "keyword.operator.field.spin2", "keyword.operator.float.spin2", "keyword.operator.unary.spin2"], + "scope": [ + "constant.language", + "keyword.operator.binary.spin2", + "keyword.operator.field.spin2", + "keyword.operator.float.spin2", + "keyword.operator.unary.spin2" + ], "settings": { "foreground": "#8a358a" } @@ -227,7 +238,13 @@ }, { "name": "Variable and parameter name", - "scope": ["constant.other.placeholder", "entity.name.variable", "meta.definition.variable.name", "support.variable", "variable"], + "scope": [ + "constant.other.placeholder", + "entity.name.variable", + "meta.definition.variable.name", + "support.variable", + "variable" + ], "settings": { "foreground": "#9CDCFE" } @@ -325,7 +342,13 @@ }, { "name": "Control flow / Special keywords", - "scope": ["entity.name.operator", "keyword.operator.delete", "keyword.other.operator", "keyword.other.using", "source.cpp keyword.operator.new"], + "scope": [ + "entity.name.operator", + "keyword.operator.delete", + "keyword.other.operator", + "keyword.other.using", + "source.cpp keyword.operator.new" + ], "settings": { "foreground": "#a863a3" } @@ -365,7 +388,14 @@ }, { "name": "Types declaration and references, TS grammar specific", - "scope": ["entity.other.inherited-class", "meta.type.cast.expr", "meta.type.new.expr", "support.constant.dom", "support.constant.json", "support.constant.math"], + "scope": [ + "entity.other.inherited-class", + "meta.type.cast.expr", + "meta.type.new.expr", + "support.constant.dom", + "support.constant.json", + "support.constant.math" + ], "settings": { "foreground": "#4EC9B0" } @@ -443,7 +473,10 @@ } }, { - "scope": ["keyword.operator.minus.exponent", "keyword.operator.plus.exponent"], + "scope": [ + "keyword.operator.minus.exponent", + "keyword.operator.plus.exponent" + ], "settings": { "foreground": "#b5cea8" } @@ -579,7 +612,11 @@ } }, { - "scope": ["meta.embedded", "source.groovy.embedded", "string meta.image.inline.markdown"], + "scope": [ + "meta.embedded", + "source.groovy.embedded", + "string meta.image.inline.markdown" + ], "settings": { "foreground": "#D4D4D4" } @@ -664,19 +701,33 @@ }, { "name": "String interpolation", - "scope": ["punctuation.definition.template-expression.begin", "punctuation.definition.template-expression.end", "punctuation.section.embedded"], + "scope": [ + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", + "punctuation.section.embedded" + ], "settings": { "foreground": "#569cd6" } }, { - "scope": ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"], + "scope": [ + "punctuation.section.embedded.begin.php", + "punctuation.section.embedded.end.php" + ], "settings": { "foreground": "#569cd6" } }, { - "scope": ["source.coffee.embedded", "support.type.property-name", "support.type.vendored.property-name", "variable.css", "variable.other.less", "variable.scss"], + "scope": [ + "source.coffee.embedded", + "support.type.property-name", + "support.type.vendored.property-name", + "variable.css", + "variable.other.less", + "variable.scss" + ], "settings": { "foreground": "#9cdcfe" } @@ -689,14 +740,22 @@ }, { "name": "coloring of the Java import and package identifiers", - "scope": ["storage.modifier.import.java", "storage.modifier.package.java", "variable.language.wildcard.java"], + "scope": [ + "storage.modifier.import.java", + "storage.modifier.package.java", + "variable.language.wildcard.java" + ], "settings": { "foreground": "#d4d4d4" } }, { "name": "pasm storage modifiers", - "scope": ["storage.modifier.p2asm", "storage.modifier.pasm", "storage.modifier.spin2"], + "scope": [ + "storage.modifier.p2asm", + "storage.modifier.pasm", + "storage.modifier.spin2" + ], "settings": { "foreground": "#7172ac" } @@ -861,6 +920,16 @@ "*.static.illegalUse": { "foreground": "#ff0000", "fontStyle": "bold" + }, + "*.disabled": { + // disabled lines of code + // was: #FFA500 orange + // was: #FFD8A8 pastel orange + // was: #9F8769 pastel orange of 62.5% + // want grey at 62.5% brightness: #9f9f9f + // want grey at 50% brightness: #808080 + // still too light...: #787878 + "foreground": "#7c7c7c" } } } diff --git a/spin2/themes/isp-spin-dark-theme.json b/spin2/themes/isp-spin-dark-theme.json index d1ecebe..f68100b 100644 --- a/spin2/themes/isp-spin-dark-theme.json +++ b/spin2/themes/isp-spin-dark-theme.json @@ -29,7 +29,7 @@ "editor.background": "#1f1f1f", "editor.findMatchBackground": "#9e6a03", "editor.foreground": "#cccccc", - "editorCursor.foreground": "#AEB0AD", + "editorCursor.foreground": "#AEB0AD", "editorGroup.border": "#ffffff17", "editorGroupHeader.tabsBackground": "#181818", "editorGroupHeader.tabsBorder": "#ffffff15", @@ -116,8 +116,8 @@ "tab.unfocusedHoverBackground": "#6e76811a", "terminal.foreground": "#cccccc", "terminal.tab.activeBorder": "#0078d4", - "terminalCursor.foreground": "#CCCCCC", - "textBlockQuote.background": "#010409", + "terminalCursor.foreground": "#CCCCCC", + "textBlockQuote.background": "#010409", "textBlockQuote.border": "#ffffff14", "textCodeBlock.background": "#6e768166", "textLink.activeForeground": "#40A6FF", @@ -131,10 +131,10 @@ "welcomePage.tileBackground": "#ffffff0f", "welcomePage.progress.foreground": "#0078d4", "widget.border": "#ffffff15", - "editor.inactiveSelectionBackground": "#3A3D41", + "editor.inactiveSelectionBackground": "#803A3D41", "editorIndentGuide.background1": "#404040", "editorIndentGuide.activeBackground1": "#707070", - "editor.selectionHighlightBackground": "#ADD6FF26", + "editor.selectionHighlightBackground": "#80D6FF26", "list.dropBackground": "#383B3D", "menu.foreground": "#CCCCCC", "menu.separatorBackground": "#454545", @@ -199,14 +199,28 @@ }, { "name": "Types declaration and references, TS grammar specific", - "scope": ["meta.type.cast.expr", "meta.type.new.expr", "support.constant.math", "support.constant.dom", "support.constant.json", "entity.other.inherited-class"], + "scope": [ + "meta.type.cast.expr", + "meta.type.new.expr", + "support.constant.math", + "support.constant.dom", + "support.constant.json", + "entity.other.inherited-class" + ], "settings": { "foreground": "#4EC9B0" } }, { "name": "Control flow / Special keywords", - "scope": ["keyword.control", "source.cpp keyword.operator.new", "keyword.operator.delete", "keyword.other.using", "keyword.other.operator", "entity.name.operator"], + "scope": [ + "keyword.control", + "source.cpp keyword.operator.new", + "keyword.operator.delete", + "keyword.other.using", + "keyword.other.operator", + "entity.name.operator" + ], "settings": { "foreground": "#C586C0" } @@ -269,7 +283,12 @@ } }, { - "scope": ["constant.character.character-class.regexp", "constant.other.character-class.set.regexp", "constant.other.character-class.regexp", "constant.character.set.regexp"], + "scope": [ + "constant.character.character-class.regexp", + "constant.other.character-class.set.regexp", + "constant.other.character-class.regexp", + "constant.character.set.regexp" + ], "settings": { "foreground": "#d16969" } @@ -305,7 +324,11 @@ } }, { - "scope": ["meta.embedded", "source.groovy.embedded", "string meta.image.inline.markdown"], + "scope": [ + "meta.embedded", + "source.groovy.embedded", + "string meta.image.inline.markdown" + ], "settings": { "foreground": "#D4D4D4" } @@ -341,7 +364,12 @@ } }, { - "scope": ["constant.numeric", "variable.other.enummember", "keyword.operator.plus.exponent", "keyword.operator.minus.exponent"], + "scope": [ + "constant.numeric", + "variable.other.enummember", + "keyword.operator.plus.exponent", + "keyword.operator.minus.exponent" + ], "settings": { "foreground": "#b5cea8" } @@ -540,7 +568,11 @@ }, { "name": "String interpolation", - "scope": ["punctuation.definition.template-expression.begin", "punctuation.definition.template-expression.end", "punctuation.section.embedded"], + "scope": [ + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", + "punctuation.section.embedded" + ], "settings": { "foreground": "#569cd6" } @@ -553,7 +585,14 @@ } }, { - "scope": ["support.type.vendored.property-name", "support.type.property-name", "variable.css", "variable.scss", "variable.other.less", "source.coffee.embedded"], + "scope": [ + "support.type.vendored.property-name", + "support.type.property-name", + "variable.css", + "variable.scss", + "variable.other.less", + "source.coffee.embedded" + ], "settings": { "foreground": "#9cdcfe" } @@ -600,7 +639,10 @@ } }, { - "scope": ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"], + "scope": [ + "punctuation.section.embedded.begin.php", + "punctuation.section.embedded.end.php" + ], "settings": { "foreground": "#569cd6" } @@ -619,7 +661,11 @@ }, { "name": "coloring of the Java import and package identifiers", - "scope": ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"], + "scope": [ + "storage.modifier.import.java", + "variable.language.wildcard.java", + "storage.modifier.package.java" + ], "settings": { "foreground": "#d4d4d4" } @@ -918,6 +964,16 @@ // keywords/variables in bad places "foreground": "#ff0000", "fontStyle": "bold" + }, + "*.disabled": { + // disabled lines of code + // was: #FFA500 orange + // was: #FFD8A8 pastel orange + // was: #9F8769 pastel orange of 62.5% + // want grey at 62.5% brightness: #9f9f9f + // want grey at 50% brightness: #808080 + // still too light...: #787878 + "foreground": "#747474" } } } diff --git a/spin2/themes/isp-spin-light-bg-theme.json b/spin2/themes/isp-spin-light-bg-theme.json index d320084..c29b78f 100644 --- a/spin2/themes/isp-spin-light-bg-theme.json +++ b/spin2/themes/isp-spin-light-bg-theme.json @@ -12,8 +12,8 @@ "editor.foreground": "#000000", "editor.inactiveSelectionBackground": "#666666e0", "editor.selectionBackground": "#222222e0", - "editor.selectionHighlightBackground": "#d59880", - "editorCursor.foreground": "#000000", + "editor.selectionHighlightBackground": "#80d59880", + "editorCursor.foreground": "#000000", "editorIndentGuide.activeBackground1": "#939393", "editorIndentGuide.background1": "#D3D3D3", "editorSuggestWidget.background": "#F3F3F3", @@ -44,8 +44,8 @@ "tab.lastPinnedBorder": "#61616130", "terminal.foreground": "#cccccc", "terminal.inactiveSelectionBackground": "#E5EBF1", - "terminalCursor.foreground": "#333333", - "widget.border": "#d4d4d4" + "terminalCursor.foreground": "#333333", + "widget.border": "#d4d4d4" }, "tokenColors": [ { @@ -78,7 +78,13 @@ }, { "name": "Constant", - "scope": ["constant.language", "keyword.operator.binary.spin2", "keyword.operator.field.spin2", "keyword.operator.float.spin2", "keyword.operator.unary.spin2"], + "scope": [ + "constant.language", + "keyword.operator.binary.spin2", + "keyword.operator.field.spin2", + "keyword.operator.float.spin2", + "keyword.operator.unary.spin2" + ], "settings": { "foreground": "#8a358a" } @@ -230,7 +236,11 @@ } }, { - "scope": ["keyword.operator.minus.exponent", "keyword.operator.plus.exponent", "variable.other.enummember"], + "scope": [ + "keyword.operator.minus.exponent", + "keyword.operator.plus.exponent", + "variable.other.enummember" + ], "settings": { "foreground": "#098658" } @@ -345,7 +355,11 @@ } }, { - "scope": ["meta.embedded", "source.groovy.embedded", "string meta.image.inline.markdown"], + "scope": [ + "meta.embedded", + "source.groovy.embedded", + "string meta.image.inline.markdown" + ], "settings": { "foreground": "#000000ff" } @@ -396,7 +410,10 @@ } }, { - "scope": ["punctuation.definition.list.begin.markdown", "punctuation.definition.quote.begin.markdown"], + "scope": [ + "punctuation.definition.list.begin.markdown", + "punctuation.definition.quote.begin.markdown" + ], "settings": { "foreground": "#0451a5" } @@ -410,19 +427,33 @@ }, { "name": "String interpolation", - "scope": ["punctuation.definition.template-expression.begin", "punctuation.definition.template-expression.end", "punctuation.section.embedded"], + "scope": [ + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", + "punctuation.section.embedded" + ], "settings": { "foreground": "#0000ff" } }, { - "scope": ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"], + "scope": [ + "punctuation.section.embedded.begin.php", + "punctuation.section.embedded.end.php" + ], "settings": { "foreground": "#800000" } }, { - "scope": ["source.coffee.embedded", "support.type.property-name", "support.type.vendored.property-name", "variable.css", "variable.other.less", "variable.scss"], + "scope": [ + "source.coffee.embedded", + "support.type.property-name", + "support.type.vendored.property-name", + "variable.css", + "variable.other.less", + "variable.scss" + ], "settings": { "foreground": "#e50000" } @@ -435,14 +466,22 @@ }, { "name": "coloring of the Java import and package identifiers", - "scope": ["storage.modifier.import.java", "storage.modifier.package.java", "variable.language.wildcard.java"], + "scope": [ + "storage.modifier.import.java", + "storage.modifier.package.java", + "variable.language.wildcard.java" + ], "settings": { "foreground": "#000000" } }, { "name": "pasm storage modifiers", - "scope": ["storage.modifier.p2asm", "storage.modifier.pasm", "storage.modifier.spin2"], + "scope": [ + "storage.modifier.p2asm", + "storage.modifier.pasm", + "storage.modifier.spin2" + ], "settings": { "foreground": "#7172ac" } @@ -537,7 +576,10 @@ }, { "name": "Variable", - "scope": ["variable.language.cog-register-names", "variable.language.readonly"], + "scope": [ + "variable.language.cog-register-names", + "variable.language.readonly" + ], "settings": { "foreground": "#8a358a" } @@ -633,6 +675,16 @@ "*.static.illegalUse": { "foreground": "#ff0000", "fontStyle": "bold" + }, + "*.disabled": { + // disabled lines of code + // was: #FFA500 orange + // was: #FFD8A8 pastel orange + // was: #9F8769 pastel orange of 62.5% + // want grey at 62.5% brightness: #9f9f9f + // want grey at 50% brightness: #808080 + // still too light...: #787878 + "foreground": "#7c7c7c" } } } diff --git a/spin2/themes/isp-spin-light-theme.json b/spin2/themes/isp-spin-light-theme.json index 40215c0..8da366c 100644 --- a/spin2/themes/isp-spin-light-theme.json +++ b/spin2/themes/isp-spin-light-theme.json @@ -5,11 +5,12 @@ "checkbox.border": "#919191", "editor.background": "#FFFFFF", "editor.foreground": "#000000", - "editor.inactiveSelectionBackground": "#E5EBF1", + "editor.inactiveSelectionBackground": "#80E5EBF1", + "editor.selectionBackground": "#BBBBBBe0", "editorIndentGuide.background1": "#D3D3D3", "editorIndentGuide.activeBackground1": "#939393", - "editor.selectionHighlightBackground": "#ADD6FF80", - "editorCursor.foreground": "#000000", + "editor.selectionHighlightBackground": "#80D6FF80", + "editorCursor.foreground": "#000000", "editorSuggestWidget.background": "#F3F3F3", "activityBarBadge.background": "#007ACC", "sideBarTitle.foreground": "#6F6F6F", @@ -31,12 +32,16 @@ "list.activeSelectionIconForeground": "#FFF", "list.focusAndSelectionOutline": "#90C2F9", "terminal.inactiveSelectionBackground": "#E5EBF1", - "terminalCursor.foreground": "#333333", + "terminalCursor.foreground": "#333333", "widget.border": "#d4d4d4" }, "tokenColors": [ { - "scope": ["meta.embedded", "source.groovy.embedded", "string meta.image.inline.markdown"], + "scope": [ + "meta.embedded", + "source.groovy.embedded", + "string meta.image.inline.markdown" + ], "settings": { "foreground": "#000000ff" } @@ -72,7 +77,12 @@ } }, { - "scope": ["constant.numeric", "variable.other.enummember", "keyword.operator.plus.exponent", "keyword.operator.minus.exponent"], + "scope": [ + "constant.numeric", + "variable.other.enummember", + "keyword.operator.plus.exponent", + "keyword.operator.minus.exponent" + ], "settings": { "foreground": "#098658" } @@ -174,7 +184,10 @@ } }, { - "scope": ["punctuation.definition.quote.begin.markdown", "punctuation.definition.list.begin.markdown"], + "scope": [ + "punctuation.definition.quote.begin.markdown", + "punctuation.definition.list.begin.markdown" + ], "settings": { "foreground": "#0451a5" } @@ -270,7 +283,11 @@ }, { "name": "String interpolation", - "scope": ["punctuation.definition.template-expression.begin", "punctuation.definition.template-expression.end", "punctuation.section.embedded"], + "scope": [ + "punctuation.definition.template-expression.begin", + "punctuation.definition.template-expression.end", + "punctuation.section.embedded" + ], "settings": { "foreground": "#0000ff" } @@ -297,7 +314,14 @@ } }, { - "scope": ["support.type.vendored.property-name", "support.type.property-name", "variable.css", "variable.scss", "variable.other.less", "source.coffee.embedded"], + "scope": [ + "support.type.vendored.property-name", + "support.type.property-name", + "variable.css", + "variable.scss", + "variable.other.less", + "source.coffee.embedded" + ], "settings": { "foreground": "#e50000" } @@ -350,7 +374,10 @@ } }, { - "scope": ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"], + "scope": [ + "punctuation.section.embedded.begin.php", + "punctuation.section.embedded.end.php" + ], "settings": { "foreground": "#800000" } @@ -369,7 +396,11 @@ }, { "name": "coloring of the Java import and package identifiers", - "scope": ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"], + "scope": [ + "storage.modifier.import.java", + "variable.language.wildcard.java", + "storage.modifier.package.java" + ], "settings": { "foreground": "#000000" } @@ -653,6 +684,16 @@ // keywords/variables in bad places "foreground": "#ff0000", "fontStyle": "bold" + }, + "*.disabled": { + // disabled lines of code + // was: #FFA500 orange + // was: #FFD8A8 pastel orange + // was: #9F8769 pastel orange of 62.5% + // want grey at 62.5% brightness: #9f9f9f + // want grey at 50% brightness: #808080 + // still too light...: #787878 + "foreground": "#747474" } } }