Compare commits

..

98 Commits

Author SHA1 Message Date
Geequlim
2ba776dc40 Merge pull request #94 from anoadragon453/patch-1
Fix small grammar issue
2019-03-13 20:40:28 +08:00
Andrew Morgan
abaa5d32d0 Fix some grammar issue
Lint configuration, not configurations.
2019-03-12 15:24:17 +00:00
Geequlim
06817de78e Merge pull request #84 from lleaff/feat-reuse-term
Close existing terminal when executing "run" commands
2018-12-03 13:06:03 +08:00
lleaff
6aab0be1d4 "run" commands close existing launched instances 2018-10-10 02:03:04 +02:00
Geequlim
307e29a7ec Merge pull request #79 from AlienGamesLLC/master
single line if-else statement
2018-09-08 12:04:47 +08:00
Geequlim
58e8626cac Merge pull request #77 from fractile81/deps-updates
Dependencies all updated to latest versions.
2018-09-08 11:55:57 +08:00
Geequlim
3ac359af86 Merge branch 'master' into deps-updates 2018-09-08 11:55:40 +08:00
Akira Kido
c20909fdb3 fixed regex to reflect my issue 2018-09-04 17:30:40 +09:00
Geequlim
ec9fe7fdc1 Merge pull request #80 from fractile81/update-readme
Further README refinements.  Updates to strings for consistency.
2018-09-04 09:20:58 +08:00
Craig A. Hancock
c84d7f9c12 Further README refinements. Updates to strings in package.json and tool_manager.ts to match. 2018-09-03 10:26:22 -06:00
Akira Kido
492fe5663c single if-else statement 2018-09-03 18:58:47 +09:00
Geequlim
8f5da41a41 Merge pull request #78 from fractile81/update-readme
README updated for consistency.
2018-09-03 00:12:39 +08:00
Craig A. Hancock
e4d86f35be README updated for consistency. 2018-09-02 07:37:37 -06:00
Craig A. Hancock
eed2d4f516 Dependencies all updated to latest versions. Only affects development packages. 2018-09-02 07:07:31 -06:00
geequlim
63b10b1d72 Sync docs with godot 3.0.4
Release 0.3.7
2018-06-29 14:03:42 +08:00
geequlim
fdc07e4743 fix syntax checing with inline condition expression fix #58 2018-06-29 13:19:14 +08:00
geequlim
80ce466d53 fix #54 2018-06-29 13:09:49 +08:00
geequlim
7e3e95086b Enable/Disable unused symbol checking 2018-06-29 13:04:25 +08:00
geequlim
7c8696abc1 Fix bracket checing in conditions fix #59 2018-06-29 12:53:32 +08:00
geequlim
8ddc7dd310 Indentifier checking for keywords bug fix
fix #65
2018-06-29 12:47:11 +08:00
geequlim
e76c06a31a Don't compains with assert checking fix #66 2018-06-29 12:35:10 +08:00
geequlim
4c0f864cf8 Add lint configuration to enable or disable some feature for syntax check 2018-06-29 12:20:09 +08:00
Geequlim
462a7bdbd7 Merge pull request #56 from RivenCode/master
Added handling of paths with spaces in them
2018-03-18 19:48:33 +08:00
RivenCode
e2dbf8146f Added handling of paths with spaces in them.
For the editor path, we use PowerShell syntax, when required.
2018-03-18 21:14:14 +11:00
geequlim
7b21267d07 Update to 0.3.6 2018-02-01 23:06:28 +08:00
geequlim
ef3a70f417 Set default project file to project.godot 2018-02-01 22:52:22 +08:00
geequlim
fc027ea9ad Update to 0.3.5 to works fine with Godot 3.0 2018-01-30 22:47:57 +08:00
geequlim
4c47fff9af Add option to disable syntax checking for GDScript 2018-01-01 15:22:29 +08:00
geequlim
d7ff45edac Add more text resource type support with syntax highlight. 2017-12-25 21:28:43 +08:00
Geequlim
38b0898649 Fix syntax checking for inline if else statement 2017-12-22 15:23:10 +08:00
geequlim
a3b062e242 Release 0.3.4 2017-10-28 11:53:27 +08:00
Geequlim
071e59364f Show window progress when parse workspace symbols 2017-10-16 15:33:25 +08:00
Geequlim
a47980981b Show relative script path in code completion and hover documentation 2017-10-16 15:00:47 +08:00
Geequlim
edcb8b96d1 Fix builtin documentation parsing for godot 2.1 fix #44 2017-10-16 14:17:43 +08:00
geequlim
1248d8e25a release 0.3.3 2017-10-15 15:23:57 +08:00
geequlim
ae0830638b Update documentations with latest 2017-10-15 15:11:39 +08:00
geequlim
bab6fed5cf Fix run editor with space conained path close #42 2017-10-15 14:58:33 +08:00
geequlim
27d7501672 Fix syntax checking error with for close #43 and #39 2017-10-15 14:51:42 +08:00
geequlim
4dff47d97b Fix errors with run current scene 2017-10-15 14:41:45 +08:00
Geequlim
7a8f00422b Update readme 2017-10-13 13:31:11 +08:00
geequlim
447af0183f Improved documentation page. 2017-10-09 23:06:24 +08:00
geequlim
1cb8a5724d Fix hover highlight with latest VSCode version 2017-10-09 21:45:55 +08:00
geequlim
0a9a0117f0 release 0.3.2 2017-09-23 02:07:58 +08:00
geequlim
d8b9dee510 improve hover documentations for code blocks 2017-09-23 02:05:07 +08:00
geequlim
0f31c66c5a MarkedString to MarkdonwString 2017-09-23 01:52:28 +08:00
geequlim
638a5362e6 Fix error complain about match statement 2017-09-23 01:31:07 +08:00
geequlim
4f0550ce86 release 0.3.1 2017-09-22 23:57:26 +08:00
geequlim
bc96a9462b Add constants back with completion 2017-09-09 23:00:57 +08:00
geequlim
749148841d Fix errors with run script
Fix build error on linux
2017-09-09 22:43:32 +08:00
Geequlim
2384004ef9 Implements base class and native class of script parsing 2017-09-06 21:30:28 +08:00
Geequlim
927297ad03 Show symbols in code completion in current document 2017-09-06 21:29:54 +08:00
Geequlim
5b1bee0c68 Merge branch 'master' of github.com:GodotExplorer/godot-tools 2017-08-31 19:17:43 +08:00
Alvaro
7f5b244113 Update package.json (#38)
fix strng to string
2017-08-29 20:01:44 -05:00
Geequlim
670f46014b release 0.3.0 2017-08-29 22:12:34 +08:00
Geequlim
203e32c3ed release 0.3.0 2017-08-29 22:10:03 +08:00
Geequlim
b7dbbcc589 Use ${workspaceRoot} in the settings
Cleanup code for debugger
2017-08-29 22:02:16 +08:00
Konstantin Zaitcev
ec7e1013aa Add project root configuration settings (#37)
* Add project root configuration settings

* Add run scene support for custom godot project directory
2017-08-29 08:43:27 -05:00
Geequlim
4b3282cf03 Support for latest 3.0 run command 2017-08-29 21:38:29 +08:00
Geequlim
58203708ba Add constants of Node and Control to global comletion item list 2017-08-14 21:05:11 +08:00
Geequlim
57b6f1ad01 Fix indentations
Add more snippet
2017-08-14 20:47:59 +08:00
Geequlim
921977fbdb Merge branch 'master' of github.com:GodotExplorer/godot-tools 2017-08-14 16:41:14 +08:00
Geequlim
0618034ace Fix errors with gdscript language support 2017-08-14 16:41:10 +08:00
Geequlim
2fb1e333b1 Merge pull request #35 from somkun/master
Update readme with spelling and FAQ
2017-08-12 12:40:55 +08:00
Andrew Nichols
07740b5c97 Update readme with spelling and FAQ 2017-08-11 20:33:26 -07:00
Geequlim
b08c61ac8d Add indentation rules support for gdscript 2017-08-10 13:51:49 +08:00
geequlim
f36169d685 Make $ to complete node path only 2017-08-06 22:54:15 +08:00
geequlim
0be4aef2bd release 0.2.9 2017-08-06 22:52:47 +08:00
geequlim
32b3449bf2 Add configuration to control is complete with node pathes 2017-08-06 22:43:27 +08:00
geequlim
a45a5e27fa Fix errors with builtin function default values 2017-08-06 17:33:36 +08:00
geequlim
dda701026c enhanced code completion 2017-08-06 16:57:08 +08:00
geequlim
90457a0df6 enhanced syntax highlight 2017-08-06 00:36:44 +08:00
geequlim
b2c5d69692 new icon 2017-08-05 15:03:12 +08:00
geequlim
43e31f04cb Add more 3.0 support
Release 0.2.8
2017-08-04 09:48:45 +08:00
geequlim
4a1e59697a Merge branches 'master' and 'master' of github.com:GodotExplorer/godot-tools 2017-08-03 10:05:16 +08:00
geequlim
911f5c8d1c Add 3.0 support and allow disable parse scenes 2017-08-03 10:04:48 +08:00
Geequlim
4918a938b7 Updat ReadMe 2017-06-15 18:47:24 +08:00
Geequlim
eac1bbd042 update readme 2017-06-15 18:45:02 +08:00
geequlim
d97c586327 release 0.2.7 2017-05-24 21:43:38 +08:00
geequlim
3c547cc595 remove keybindings 2017-05-24 21:43:11 +08:00
geequlim
25afaedfb3 parse named enunmerations 2017-05-23 22:43:43 +08:00
geequlim
39c642ae82 Ignore trailing spaces with indent block checking
Fix #24
2017-05-23 20:44:50 +08:00
arrkiin
55b16c3824 Ignore keywords in comments (#23) 2017-05-10 09:01:12 +08:00
geequlim
86ccb8318f release 0.2.6 2017-05-07 18:21:15 +08:00
geequlim
2834c2b19c Update documentation data to godot 2.1.3 2017-05-07 18:20:45 +08:00
geequlim
71e431a5ec add a script to convert xml documentation into json 2017-05-07 18:20:21 +08:00
geequlim
65cb8c2deb add key bindings for open godot editor and update workspace symbols 2017-05-07 13:33:35 +08:00
geequlim
cf5faf2152 Run script extends from SceneTree is supported 2017-05-07 13:19:40 +08:00
geequlim
0f7d4902fd 1. Add enum and match keywords highlight support
2. Shorthand if else support fix #17
3. Fix compains with used variables in array and dictionary fix #18
4. Fix error syntax check with keywords in strings fix #12
5. Add syntax check for end of expression
2017-05-06 23:25:39 +08:00
arrkiin
f40f091421 Fix sourceMap functionality 2017-05-06 20:22:09 +08:00
arrkiin
e21de33ffc Multiline statements 2017-05-02 11:51:42 +02:00
geequlim
2d8df590b4 release 0.2.5 2017-03-22 03:05:00 +08:00
geequlim
590f9e51e7 Run game within terminal
Remove Debugger
2017-03-22 02:59:21 +08:00
geequlim
519add7029 Move workspace smbols state to status bar 2017-03-22 02:35:01 +08:00
geequlim
236adcab1c improved variable parsing
improved unused variable checking fix #10
2017-03-13 09:48:29 +08:00
geequlim
77dcdf5a5b update 0.2.4 2017-03-09 20:43:26 +08:00
geequlim
82f4765bcc add method index list in documentation previewer 2017-03-09 20:34:19 +08:00
geequlim
91c6acb1fd fix unused variable checking error when be same part of another one 2017-03-09 19:30:06 +08:00
geequlim
c657fc16de add asignment and comparation expression checking 2017-03-09 17:33:01 +08:00
31 changed files with 177772 additions and 66867 deletions

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ test
*.vsix
configurations/tmp.txt
configurations/test.py
.vscode-test

2
.vscode/launch.json vendored
View File

@@ -10,7 +10,7 @@
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": ["${workspaceRoot}/out/src"],
"outFiles": ["${workspaceRoot}/out/src/**/*.js"],
"preLaunchTask": "npm"
},
{

View File

@@ -1,5 +1,119 @@
# Change Log
### 0.3.7
* Add `lint` configuration to control the behaviors of syntax checking
* Fix error with run godot editor when the editor contains spaces
* Disable semicolons and brackets checks as default can be enabled with project settings
* Fix bugs in syntax valiadating
* Sync documentations with godot 3.0.4
```json
{
"lint": {
"semicolon": true,
"conditionBrackets": true
}
}
```
### 0.3.6
* Fix project configuartion file path
### 0.3.5
* Add option to disable syntax checking for GDScript
* Improved inline if else statement syntax checking
* More resource type supported for syntax highglight
* Bump default godot version to 3.0
* Sync the documentations from godot 3.0
### 0.3.4
* Fix bug with builtin symbols parsing for godot 2.1
* Improved hover documentation
* Show window progress when parsing workspace symbols
### 0.3.3
* Fix some syntax checking errors.
* Fix problems with hover documentation with latest VSCode.
* Improved builtin class documentation page.
* Update the documentation data with latest godot version.
### 0.3.2
* Fix syntax checking error with match statement.
* Improved documentation for builtin code blocks.
* Start using MarkdonwString to keep links valid.
### 0.3.1
* Update documentations with latest godot.
* Fix errors with run script and run project.
* Improve code completion with opening script file and constants.
* Some improvements for documentations.
### 0.3.0
* Add project root configuration settings as `GodotTools.godotProjectRoot` thanks Konstantin Zaitcev
* Add auto indent support for gdscript language
* More friendly with godot 3.0 alpha
* Updated script snippets
* Fix highglight error with gdscript language
* Limited code completions
### 0.2.9
* Add configuration `GodotTools.completeNodePath` to switch is complete node pathes
* Enhanced syntax highlight with GDScript
* Enhanced code completion with GDScript
### 0.2.8
* Add godot 3.0 project support with configuration `GodotTools.parseTextScene` >= 3
* Add configuration `GodotTools.parseTextScene` to allow disable node path parsing
* Remove `GodotTools.editorServerPort` configuration
### 0.2.7
* Fix some error with syntax checking
* Add symbol support for enumerations
* Remove key bindings for `F5`~`F8` as it might be confict with other functionalities of VSCode
* You can bind the key bindings back by add following configurations
```json
{
"command": "godot.runWorkspace",
"key": "F5"
},
{
"command": "godot.runCurrentScene",
"key": "F6"
},
{
"command": "godot.openWithEditor",
"key": "F7"
},
{
"command": "godot.updateWorkspaceSymbols",
"key": "F8"
}
```
For more references please ready [keybindings](https://code.visualstudio.com/docs/getstarted/keybindings)
### 0.2.6
* Add shorthand if else expression support
* Add `enum` and `match` expression support
* Fix bugs with syntax checking
* Updated documentation data with godot 2.1.3
* Add syntax checking for end of expression
* The pulugin is compiled with latest VSCode thanks @arrkiin
* Add key bindings for open workspace with godot editor with `F7` and update workspace symbols with `F8`
### 0.2.5
* Run games within VSCode terminals
* Add key bindings for `F5 to run the workspace` and `F6 to run the edting scene`
* Fix a lot of bugs with unused vaiable cheching
* Move workspace symbols state notice to status bar
### 0.2.4
* Add code cheching for asignments and comparations
* Impoved builtin documentation preview page
* Fix bugs with unused vaiable cheching
### 0.2.3
* Fix known errors with code syntax checking
* Add configuration `ignoreIndentedVars` to allow ignore indented variables in scripts

View File

@@ -2,78 +2,63 @@ A complete set of tools to code games with the [Godot game engine](http://www.go
## Features
The plug-in comes with a wealth of features to make your programming experience as comfortable as possible
The extension comes with a wealth of features to make your Godot programming experience as comfortable as possible:
- Syntax highlighting for the GDscript language
- Syntax highlighting for the tscn and tres scene formats
- Function definitions and documentation on hover
- Rich auto completion
- Syntax highlighting for the GDscript (`.gd`) language
- Syntax highlighting for the `.tscn` and `.tres` scene formats
- Function definitions and documentation display on hover (see image below)
- Rich auto-completion
- Static code validation
- Open projects and scenes in Godot from VScode
- Ctrl click on a variable or method call to jump to its definition
- Run/debug the godot game with VSCode with F5(coming soon)
- Open projects and scenes in Godot from VS Code
- Ctrl-click on a variable or method call to jump to its definition
- Full documentation of the Godot engine's API supported
![Showing the documentation on hover feature](https://raw.githubusercontent.com/GodotExplorer/godot-tools/master/img/documentation-on-hover.png "Method definition and docs on hover")
![Showing the documentation on hover feature](img/godot-tools.jpg)
## Available commands
## Available Commands
The plug-ins adds a few entries to the command palette
The extension adds a few entries to the VS Code Command Palette under "GodotTools":
- Update Workspace Symbols
- Run workspace as godot project
- Open workspace with godot editor
- Update workspace symbols
- Run workspace as Godot project
- Open workspace with Godot editor
- Run current scene
## Settings
You can use the following settings to setup the Godot Tools:
### Godot
- GodotTools.editorServerPort: The http server port used by the EditorServer Godot module (_see Extra Functionality below_)
- GodotTools.maxNumberOfProblems: Sets the limit for the issues reported by the static code validator
- GodotTools.editorPath: An absolute path pointing at the Godot Editor executable file. Required to run the project and test scenes from VScode
- GodotTools.workspaceDocumentWithMarkdown: Control the documentations of workspace symbols should be rendered as plain text or html from markdown
- GodotTools.ignoreIndentedVars: Parse variables defined after indent of not
If you like this extension, you can set VS Code as your default script editor for Godot by following these steps:
1. Open editor settings
2. Select `Text Editor / External`
3. Make sure the `Use External Editor` box is checked
4. Fill `Exec Path` with the path to your VS Code executable
5. Fill `Exec Flags` with `{project} --goto {file}:{line}:{col}`
## Extra functionality
### VS Code
If you want to get an even better experience with this plug-in, you can extend its functionality using the following modules and VScode extensions:
You can use the following settings to configure Godot Tools:
- **GodotTools.godotVersion** - The Godot version of your project.
- **GodotTools.editorPath** - The absolute path to the Godot executable. Required to run the project and test scenes directly from VS Code.
- **GodotTools.workspaceDocumentWithMarkdown** - Control how the documentation of workspace symbols should be rendered: as plain text or as HTML from Markdown.
- **GodotTools.ignoreIndentedVars** - Only parse variables defined on lines without an indentation.
- **GodotTools.parseTextScene** - Parse a file as a Godot scene when the file name ends with `.tscn`.
- **GodotTools.completeNodePath** - Show node paths within a workspace as part of code completion.
- **GodotTools.godotProjectRoot** - Your Godot project's directory, which contains `project.godot` or `engine.cfg`.
### Godot modules
## Issues and Contributions
These are modules for the goats editor itself, programmed in C++. In order to use them, you have to create a [custom build](http://docs.godotengine.org/en/stable/reference/compiling_for_windows.html) of the engine. Only do that if you know what you're doing.
The [Godot Tools](https://github.com/GodotExplorer/godot-tools) extension and [engine modules](https://github.com/GodotExplorer/editor-server) are both hosted on GitHub. Feel free to open issues there and create pull requests anytime.
- [EditorServer](https://github.com/GodotExplorer/editor-server/tree/master/editor_server): Using HTTP requests, this module gets extra information from Godot to improve autocompletion.
- [VSCode](https://github.com/GodotExplorer/editor-server/tree/master/vscode_tools): The VS code module generates a setting file that Visual Studio code can use to generate Tasks automatically.
See the [full changelog](https://github.com/GodotExplorer/godot-tools/blob/master/CHANGELOG.md) for the latest changes.
### VScode extensions
## FAQ
### Why isn't Intellisense showing up for me?
- [TOML language](https://marketplace.visualstudio.com/items?itemName=be5invis.toml): Godot uses this minimal language to store settings. For example in your project config file. If you want to get syntax highlighting for these files, you will have to install the TOML language extension.
Make sure you save your `.gd` file, then run "GodotTools: Update Workspace Symbols" from the Command Palette.
## Issues and contributions
The [Godot Tools](https://github.com/GodotExplorer/godot-tools) and the go to [engine modules](https://github.com/GodotExplorer/editor-server) are all hosted on GitHub. Feel free to open issues there and create pull requests anytime.
## Release Notes
### 0.2.3
* Fix known errors with code syntax checking
* Add configuration `GodotTools.ignoreIndentedVars` to allow ignore indented variables in scripts
* Enhanced hover tip documentation rendering with code examples
* Add launch configurations to launch game with F5(expiremental)
* The plugin is open source under MIT license
### 0.2.2
* Better Syntax validating for code blocks
* More waring for non-python liked expression
### 0.2.1
* Support markdown render in hover tips for documentations in workspace symbols
* Add configuration `GodotTools.workspaceDocumentWithMarkdown` to control workspace documentation rendering
[Read more from the full change log](https://github.com/GodotExplorer/godot-tools/blob/master/CHANGELOG.md)
## TODOS:
* Print game output log into VSCode console while game launched from the plugin
* Convert official BBCode documentation into Markdown and render it to HTML with documentation previewer pages
* Add mermaid support with documentation
## TODO:
* Convert official BBCode documentation into Markdown and render it into HTML with documentation previewer pages
* Add mermaid support with documentation
* Undefined variable checking

View File

@@ -20,7 +20,7 @@
"name": "comment.line.number-sign.gdscript"
},
{
"match": "\\b(?i:elif|else|for|if|while|break|continue|pass|and|in|is|not|or|return|onready|setget|breakpoint)\\b",
"match": "\\b(?i:elif|else|for|if|while|break|continue|pass|and|in|is|not|or|return|onready|setget|enum|match|breakpoint)\\b",
"name": "keyword.control.gdscript"
},
{

File diff suppressed because one or more lines are too long

View File

@@ -21,5 +21,9 @@
["(", ")"],
["[", "]"],
["{", "}"]
]
}
],
"indentationRules": {
"increaseIndentPattern": "^\\s*((class|func|else|elif|for|if|match|while|enum)|(.*\\sdo\\b))\\b[^\\{;]*$",
"decreaseIndentPattern": "^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(else|elif)\\b)"
}
}

View File

@@ -4,7 +4,7 @@
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, ${id} and ${id:label} and ${1:label} for variables. Variables with the same id are connected.
// Example:
"Subclass": {
"Inner class": {
"prefix": "class",
"body": [
"class $1 extends ${2:Reference}",
@@ -57,7 +57,23 @@
"func _input_event(event):",
"\t${1:pass}"
]
},
},
"_draw method of Node": {
"prefix": "draw",
"body": [
"func _draw():",
"\t${1:pass}"
]
},
"_gui_input method of Node": {
"prefix": "guii",
"body": [
"func _gui_input(event):",
"\t${1:pass}"
]
},
"for loop": {
"prefix": "for",
@@ -145,7 +161,7 @@
"define onready variables": {
"prefix": "onready",
"body": [
"onready var ${1:name}${2: = default}${3: setget }"
"onready var ${1:name} = get_node($2)"
]
},
@@ -156,6 +172,13 @@
]
},
"Is instance of a class or script": {
"prefix": "is",
"body": [
"${1:instance} is ${2:class}"
]
},
"element in array": {
"prefix": "in",
"body": [
@@ -192,5 +215,11 @@
"body": [
"set_process_input(true)"
]
},
"pass statement": {
"prefix": "pass",
"body": [
"pass"
]
}
}
}

2
doc/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
classes/*.xml
classes-*.xml

83763
doc/classes-2.1.json Normal file

File diff suppressed because it is too large Load Diff

90257
doc/classes-3.0.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

66
doc/xmldoc2json-2.1.py Executable file
View File

@@ -0,0 +1,66 @@
#!/usr/bin/python
import sys
import xml.etree.ElementTree as ET
import json
def parseClass(data):
dictCls = dict(data.attrib)
dictCls['brief_description'] = data.find("brief_description").text.strip()
dictCls['description'] = data.find("description").text.strip()
dictCls['methods'] = []
for m in data.find("methods"):
dictCls['methods'].append(parseMethod(m))
dictCls['signals'] = []
for s in (data.find("signals") if data.find("signals") is not None else []):
dictCls['signals'].append(parseMethod(s))
dictCls['constants'] = []
for c in (data.find("constants") if data.find("constants") is not None else []):
dictCls['constants'].append(parseConstant(c))
dictCls['properties'] = []
for m in (data.find("members") if data.find("members") is not None else []):
dictCls['properties'].append(parseProperty(m))
dictCls['theme_properties'] = []
for thi in (data.find("theme_items") if data.find("theme_items") is not None else []):
dictCls['theme_properties'].append(parseProperty(thi))
return dictCls
def parseMethod(data):
dictMethod = dict(data.attrib)
dictMethod['description'] = data.find("description").text.strip()
dictMethod['return_type'] = data.find("return").attrib["type"] if data.find("return") is not None else ""
if "qualifiers" not in dictMethod: dictMethod["qualifiers"] = ""
dictMethod["arguments"] = []
for arg in data.iter('argument'):
dictMethod["arguments"].append(parseArgument(arg))
return dictMethod
def parseArgument(data):
dictArg = dict(data.attrib)
if "dictArg" in dictArg: dictArg.pop("index")
dictArg["default_value"] = dictArg["default"] if "default" in dictArg else ""
if "default" in dictArg: dictArg.pop("default")
return dictArg
def parseConstant(data):
dictConst = dict(data.attrib)
dictConst["description"] = data.text.strip()
return dictConst
def parseProperty(data):
dictProp = dict(data.attrib)
dictProp["description"] = data.text.strip()
return dictProp
def main():
if len(sys.argv) >=2 :
tree = ET.parse(open(sys.argv[1], 'r'))
classes = {}
for cls in tree.getroot():
dictCls = parseClass(cls)
classes[dictCls['name']] = dictCls
jsonContent = json.dumps({"classes": classes, "version": "2.1.4"}, ensure_ascii=False, indent=2)
print(jsonContent)
if __name__ == '__main__':
main()

80
doc/xmldoc2json-3.0.py Executable file
View File

@@ -0,0 +1,80 @@
#!/usr/bin/python
import sys
import xml.etree.ElementTree as ET
import json
import os
def glob_path(path, pattern):
import os, fnmatch
result = []
for root, _, files in os.walk(path):
for filename in files:
if fnmatch.fnmatch(filename, pattern):
result.append(os.path.join(root, filename))
return result
def parseClass(data):
dictCls = dict(data.attrib)
dictCls['brief_description'] = data.find("brief_description").text.strip()
dictCls['description'] = data.find("description").text.strip()
dictCls['methods'] = []
for m in data.find("methods"):
dictCls['methods'].append(parseMethod(m))
dictCls['signals'] = []
for s in (data.find("signals") if data.find("signals") is not None else []):
dictCls['signals'].append(parseMethod(s))
dictCls['constants'] = []
for c in (data.find("constants") if data.find("constants") is not None else []):
dictCls['constants'].append(parseConstant(c))
dictCls['properties'] = []
for m in (data.find("members") if data.find("members") is not None else []):
dictCls['properties'].append(parseProperty(m))
dictCls['theme_properties'] = []
for thi in (data.find("theme_items") if data.find("theme_items") is not None else []):
dictCls['theme_properties'].append(parseProperty(thi))
return dictCls
def parseMethod(data):
dictMethod = dict(data.attrib)
dictMethod['description'] = data.find("description").text.strip()
dictMethod['return_type'] = data.find("return").attrib["type"] if data.find("return") is not None else ""
if "qualifiers" not in dictMethod: dictMethod["qualifiers"] = ""
dictMethod["arguments"] = []
for arg in data.iter('argument'):
dictMethod["arguments"].append(parseArgument(arg))
return dictMethod
def parseArgument(data):
dictArg = dict(data.attrib)
if "dictArg" in dictArg: dictArg.pop("index")
dictArg["default_value"] = dictArg["default"] if "default" in dictArg else ""
if "default" in dictArg: dictArg.pop("default")
return dictArg
def parseConstant(data):
dictConst = dict(data.attrib)
dictConst["description"] = data.text.strip()
return dictConst
def parseProperty(data):
dictProp = dict(data.attrib)
dictProp["description"] = data.text.strip()
return dictProp
def main():
if len(sys.argv) >=2 :
if os.path.isdir(sys.argv[1]):
classes = {}
for f in glob_path(sys.argv[1], "**.xml"):
if f.find("/classes/") == -1 and f.find("/doc_classes/") == -1:
continue
tree = ET.parse(open(f, 'r'))
cls = tree.getroot()
dictCls = parseClass(cls)
classes[dictCls['name']] = dictCls
jsonContent = json.dumps({"classes": classes, "version": "3.0.4"}, ensure_ascii=False, indent=2)
print(jsonContent)
if __name__ == '__main__':
main()

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

BIN
img/godot-tools.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

2236
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,35 +2,36 @@
"name": "godot-tools",
"displayName": "Godot Tools",
"icon": "icon.png",
"description": "\"Tools for game development with godot game engine\"",
"version": "0.2.3",
"description": "Tools for game development with godot game engine",
"version": "0.3.7",
"publisher": "geequlim",
"repository": "https://github.com/GodotExplorer/godot-tools",
"license": "MIT",
"engines": {
"vscode": "^1.5.0"
"vscode": "^1.1.21"
},
"categories": [
"Other"
],
"activationEvents": [
"workspaceContains:engine.cfg",
"workspaceContains:project.godot",
"onLanguage:gdscript"
],
"main": "./out/src/extension",
"contributes": {
"commands":[
"commands": [
{
"command": "godot.updateWorkspaceSymbols",
"title": "GodotTools: Update Workspace Symbols"
"title": "GodotTools: Update workspace symbols"
},
{
"command": "godot.runWorkspace",
"title": "GodotTools: Run workspace as godot project"
"title": "GodotTools: Run workspace as Godot project"
},
{
"command": "godot.openWithEditor",
"title": "GodotTools: Open workspace with godot editor"
"title": "GodotTools: Open workspace with Godot editor"
},
{
"command": "godot.runCurrentScene",
@@ -39,13 +40,8 @@
],
"configuration": {
"type": "object",
"title": "Godot tools configuration",
"title": "Godot Tools configuration",
"properties": {
"GodotTools.editorServerPort": {
"type": "number",
"default": 6996,
"description": "The server port of your EditorServer"
},
"GodotTools.maxNumberOfProblems": {
"type": "number",
"default": 100,
@@ -54,17 +50,51 @@
"GodotTools.editorPath": {
"type": "string",
"default": "",
"description": "The absolute path of your godot editor"
"description": "The absolute path to the Godot executable"
},
"GodotTools.workspaceDocumentWithMarkdown": {
"type": "boolean",
"default": false,
"description": "Render workspace documentations as markdown content"
"description": "Render workspace documentations as Markdown content"
},
"GodotTools.ignoreIndentedVars": {
"type": "boolean",
"default": false,
"description": "Only parse variables without indents in GDScript"
},
"GodotTools.godotVersion": {
"type": "number",
"default": 3.0,
"description": "The Godot version of your project"
},
"GodotTools.parseTextScene": {
"type": "boolean",
"default": true,
"description": "Parse a file as a Godot scene when the file name ends with tscn"
},
"GodotTools.completeNodePath": {
"type": "boolean",
"default": false,
"description": "Show node paths within a workspace as part of code completion"
},
"GodotTools.godotProjectRoot": {
"type": "string",
"default": "${workspaceRoot}",
"description": "Your Godot project's directory"
},
"GodotTools.enableSyntaxChecking": {
"type": "boolean",
"default": true,
"description": "Turn on/off syntax checking for GDScript"
},
"GodotTools.lint": {
"type": "object",
"default": {
"semicolon": false,
"conditionBrackets": false,
"unusedSymbols": true
},
"description": "Lint configuration"
}
}
},
@@ -72,13 +102,24 @@
{
"id": "gdscript",
"aliases": [
"Godot Engine Script",
"GDScript",
"gdscript"
],
"extensions": [
".gd"
],
"configuration": "./configurations/gdscript-configuration.json"
},
{
"id": "properties",
"extensions": [
"cfg",
"tres",
"tscn",
"godot",
"gdns",
"gdnlib"
]
}
],
"grammars": [
@@ -93,82 +134,24 @@
"language": "gdscript",
"path": "./configurations/snippets.json"
}
],
"breakpoints": [
{
"language": "gdscript"
}
],
"debuggers": [
{
"type": "godot",
"label": "Godot Game",
"program": "./out/src/debug/godotDebugger.js",
"runtime": "node",
"configurationSnippets": [
{
"label": "Godot Game: Launch",
"description": "A new configuration for launching a godot game",
"body": {
"type": "godot",
"request": "launch",
"name": "Godot Game",
"godot": "${1:The abusolut path of your godot binary}",
"projectDir": "^\"\\${workspaceRoot}\"",
"params": [],
"runWithEditor": false
}
}
],
"configurationAttributes": {
"launch": {
"required": [ "godot", "runWithEditor", "projectDir" ],
"properties": {
"godot": {
"type": "string",
"description": "The dirctory of your godot project",
"default": ""
},
"runWithEditor": {
"type": "boolean",
"description": "Launch the game with godot editor.",
"default": false
},
"projectDir": {
"type": "string",
"description": "The dirctory of your godot project",
"default": "${workspaceRoot}"
},
"params": {
"type": "array",
"description": "Addtional params passed to godot",
"default": []
}
}
}
},
"initialConfigurations": "godot.provideInitialDebugConfigurations"
}
]
]
},
"scripts": {
"vscode:prepublish": "tsc -p ./",
"compile": "tsc -watch -p ./",
"compile": "node ./node_modules/typescript/bin/tsc -p ./",
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "node ./node_modules/vscode/bin/test"
},
"devDependencies": {
"typescript": "^2.0.3",
"vscode": "^1.0.0",
"mocha": "^2.3.3",
"@types/node": "^6.0.40",
"@types/mocha": "^2.2.32"
"@types/mocha": "^5.2.5",
"@types/node": "^10.9.4",
"mocha": "^5.2.0",
"typescript": "^3.0.3",
"vscode": "^1.1.21"
},
"dependencies": {
"glob": "^7.1.1",
"node-cmd": "1.2.0",
"vscode-debugprotocol": "^1.17.0",
"vscode-debugadapter": "^1.17.0"
"vscode-debugadapter": "^1.17.0"
}
}

View File

@@ -9,10 +9,21 @@ interface NodeInfo {
instance: string
};
interface CompletionSymbols {
classes : CompletionItem[],
functions : CompletionItem[],
signals : CompletionItem[],
constants : CompletionItem[],
properties : CompletionItem[],
nodes : CompletionItem[],
builtinConstants: CompletionItem[]
};
class Config {
private symbols;
private classes;
public bintinSybmolInfoList: CompletionItem[];
private workspaceSymbols; // filePath: GDScript in symbolparser.ts
private builtinCompletions : CompletionSymbols;
private builtinClassDoc;
public parser: GDScriptSymbolParser;
// scriptpath : scenepath
public scriptSceneMap: Object;
@@ -22,8 +33,16 @@ class Config {
public builtinSymbolInfoMap: Object;
constructor() {
this.symbols = {};
this.bintinSybmolInfoList = [];
this.builtinCompletions = {
classes : [],
functions : [],
signals : [],
constants : [],
properties : [],
nodes : [],
builtinConstants: []
};
this.workspaceSymbols = {};
this.builtinSymbolInfoMap = {};
this.nodeInfoMap = {};
this.scriptSceneMap = {};
@@ -40,19 +59,19 @@ class Config {
}
setSymbols(path, s) {
this.symbols[this.normalizePath(path)] = s;
this.workspaceSymbols[this.normalizePath(path)] = s;
}
getSymbols(path) {
return this.symbols[this.normalizePath(path)];
return this.workspaceSymbols[this.normalizePath(path)];
}
setAllSymbols(s) {
this.symbols = s;
this.workspaceSymbols = s;
}
getAllSymbols() {
return this.symbols;
return this.workspaceSymbols;
}
normalizePath(path) {
@@ -75,7 +94,7 @@ class Config {
const content = fs.readFileSync(docfile, "utf-8");
const docdata = JSON.parse(content);
if(docdata.classes) {
this.classes = docdata.classes;
this.builtinClassDoc = docdata.classes;
done = true;
}
}
@@ -83,18 +102,16 @@ class Config {
console.error(error);
}
if(done) {
for (let key of Object.keys(this.classes)) {
const classdoc = this.classes[key];
const bintinSybmolInfoList = this.bintinSybmolInfoList;
for (let key of Object.keys(this.builtinClassDoc)) {
const classdoc = this.builtinClassDoc[key];
const builtinSymbolInfoMap = this.builtinSymbolInfoMap;
// class
// ---------------------- class -----------------
const item: CompletionItem = new CompletionItem(classdoc.name, CompletionItemKind.Class);
item.detail = 'Native Class';
item.documentation = classdoc.brief_description + " \n\n" +classdoc.description;
bintinSybmolInfoList.push(item);
item.documentation = classdoc.brief_description + " \n" +classdoc.description;
this.builtinCompletions.classes.push(item);
builtinSymbolInfoMap[classdoc.name] = {completionItem: item, rowDoc: classdoc};
// methods
const methods = classdoc.methods
// ----------------------- functions -----------------------
const parsMethod = (m, kind: CompletionItemKind, insertAction=(name)=>name)=>{
const mi = new CompletionItem(m.name, kind);
mi.insertText = insertAction(m.name) + (m.arguments.length==0?"()":"");
@@ -103,38 +120,55 @@ class Config {
mi.detail = m.return_type;
let argstr = "";
m.arguments.map(arg=>{
argstr += `${arg.type} ${arg.name}${arg.default_value.length>0?'='+arg.default_value.length:''}${m.arguments.indexOf(arg)==m.arguments.length-1?'':', '}`;
argstr += `${arg.type} ${arg.name}${arg.default_value.length>0?'='+arg.default_value:''}${m.arguments.indexOf(arg)==m.arguments.length-1?'':', '}`;
});
// mi.label=`${m.name}(${argstr}) ${m.qualifiers}`;
let mdoc = `${m.return_type} ${classdoc.name}.${m.name}(${argstr}) ${m.qualifiers}`;
let methodName = `${classdoc.name}.${m.name}`;
if (classdoc.name == m.name) methodName = m.name;
let mdoc = `${m.return_type} ${methodName}(${argstr}) ${m.qualifiers}`;
mdoc += " \n\n";
mdoc += m.description;
mi.documentation = mdoc;
bintinSybmolInfoList.push(mi);
if(CompletionItemKind.Interface == kind)
this.builtinCompletions.signals.push(mi);
else
this.builtinCompletions.functions.push(mi);
builtinSymbolInfoMap[`${classdoc.name}.${m.name}`] = {completionItem: mi, rowDoc: m};
};
// methods
const methods = classdoc.methods
methods.map(m=>parsMethod(m, CompletionItemKind.Method));
// signals
const signals = classdoc.signals;
signals.map(s=>parsMethod(s, CompletionItemKind.Interface));
// constants
// ------------------------------ constants ---------------------
const constants = classdoc.constants;
constants.map(c=>{
const ci = new CompletionItem(c.name, CompletionItemKind.Enum);
ci.detail = c.value;
ci.documentation = `${classdoc.name}.${c.name} = ${c.value}`;
bintinSybmolInfoList.push(ci);
if(key[0] == "@" || key == "Node" || key == "Control")
this.builtinCompletions.builtinConstants.push(ci);
else
this.builtinCompletions.constants.push(ci);
builtinSymbolInfoMap[`${classdoc.name}.${c.name}`] = {completionItem: ci, rowDoc: c};
});
// properties
const properties = classdoc.properties;
const parseProp = (p)=>{
});
// ----------------------- properties -----------------------
const parseProp = (p) => {
const pi = new CompletionItem(p.name, CompletionItemKind.Property);
pi.detail = `${p.type} of ${classdoc.name}`;
pi.documentation = p.description;
bintinSybmolInfoList.push(pi);
builtinSymbolInfoMap[`${classdoc.name}.${p.name}`] = {completionItem: pi, rowDoc: p};
this
.builtinCompletions
.properties
.push(pi);
builtinSymbolInfoMap[`${classdoc.name}.${p.name}`] = {
completionItem: pi,
rowDoc: p
};
};
// properties
const properties = classdoc.properties;
properties.map(p=>parseProp(p));
// theme_properties
const theme_properties = classdoc.theme_properties;
@@ -144,10 +178,25 @@ class Config {
return done;
};
getWorkspaceCompletionItems(): CompletionItem[] {
let items: CompletionItem[] = [];
for (let path of Object.keys(this.symbols)) {
const script = this.symbols[path];
getWorkspaceCompletionItems(script_files = []) : CompletionSymbols {
const symbols = {
classes: [],
functions: [],
signals: [],
constants: [],
properties: [],
nodes: [],
builtinConstants: []
};
if (script_files.length == 0)
script_files = Object.keys(this.workspaceSymbols);
for (let path of script_files) {
const script = this.workspaceSymbols[path];
if (workspace) {
const root = this.normalizePath(workspace.rootPath) + "/";
if (path.startsWith(root))
path = path.replace(root, "");
}
const addScriptItems = (items, kind: CompletionItemKind, kindName:string = "Symbol", insertText = (n)=>n)=>{
const _items: CompletionItem[] = [];
for (let name of Object.keys(items)) {
@@ -159,41 +208,47 @@ class Config {
item.detail = cvalue;
item.insertText = insertText(name) + (signature=="()"?"()":"");
item.documentation = (script.documents && script.documents[name])?script.documents[name]+"\r\n":"";
item.documentation += `${kindName} defined in ${workspace.asRelativePath(path)}`;
item.documentation += `${kindName} defined in ${path}`;
_items.push(item);
}
return _items;
}
items = [...items, ...addScriptItems(script.classes, CompletionItemKind.Class, "Class")];
items = [...items, ...addScriptItems(script.functions, CompletionItemKind.Method, "Method")];
items = [...items, ...addScriptItems(script.variables, CompletionItemKind.Variable, "Variable")];
items = [...items, ...addScriptItems(script.signals, CompletionItemKind.Interface, "Signal")];
items = [...items, ...addScriptItems(script.constants, CompletionItemKind.Enum, "Constant")];
symbols.classes = [ ...(symbols.classes), ...(addScriptItems(script.classes, CompletionItemKind.Class, "Class"))]
symbols.functions = [ ...(symbols.functions), ...(addScriptItems(script.functions, CompletionItemKind.Method, "Method"))]
symbols.signals = [ ...(symbols.signals), ...(addScriptItems(script.signals, CompletionItemKind.Interface, "Signal"))]
symbols.properties = [ ...(symbols.properties), ...(addScriptItems(script.variables, CompletionItemKind.Variable, "Variable"))]
symbols.constants = [ ...(symbols.constants), ...(addScriptItems(script.constants, CompletionItemKind.Enum, "Constant"))]
if(script.enumerations)
symbols.constants = [...(symbols.constants), ...(addScriptItems(script.enumerations, CompletionItemKind.Enum, "Enumeration"))];
}
if(workspace.getConfiguration("GodotTools").get("completeNodePath", false)) {
const addSceneNodes = ()=>{
const _items: CompletionItem[] = [];
for (let scnenepath of Object.keys(this.nodeInfoMap)) {
const nodes: NodeInfo[] = this.nodeInfoMap[scnenepath];
nodes.map((n=>{
const item = new CompletionItem(n.name, CompletionItemKind.Reference);
item.detail = n.type;
item.documentation = `${n.parent}/${n.name} in ${scnenepath}`;
_items.push(item);
const fullitem = new CompletionItem(`${n.parent}/${n.name}`, CompletionItemKind.Reference);
fullitem.detail = n.type;
fullitem.filterText = n.name;
fullitem.sortText = n.name;
fullitem.documentation = `${n.parent}/${n.name} in ${scnenepath}`;
_items.push(fullitem);
}));
}
return _items;
};
symbols.nodes = [...(symbols.nodes), ...(addSceneNodes())];
}
const addSceneNodes = ()=>{
const _items: CompletionItem[] = [];
for (let scnenepath of Object.keys(this.nodeInfoMap)) {
const nodes: NodeInfo[] = this.nodeInfoMap[scnenepath];
nodes.map((n=>{
const item = new CompletionItem(n.name, CompletionItemKind.Reference);
item.detail = n.type;
item.documentation = `${n.parent}/${n.name} in ${scnenepath}`;
_items.push(item);
const fullitem = new CompletionItem(`${n.parent}/${n.name}`, CompletionItemKind.Reference);
fullitem.detail = n.type;
fullitem.filterText = n.name;
fullitem.sortText = n.name;
fullitem.documentation = `${n.parent}/${n.name} in ${scnenepath}`;
_items.push(fullitem);
}));
}
return _items;
};
items = [...items, ...addSceneNodes()];
return items;
return symbols;
}
loadScene(scenePath: string) {
@@ -255,13 +310,17 @@ class Config {
}
getClass(name: string) {
return this.classes[name];
return this.builtinClassDoc[name];
}
getBuiltinCompletions() {
return this.builtinCompletions;
}
getBuiltinClassNameList() {
let namelist = null;
if(this.classes)
namelist = Object.keys(this.classes);
if (this.builtinClassDoc)
namelist = Object.keys(this.builtinClassDoc);
if(!namelist)
namelist = [];
return namelist;

View File

@@ -1,162 +1,162 @@
import {
DebugSession,
InitializedEvent, TerminatedEvent, StoppedEvent, BreakpointEvent, OutputEvent, Event,
Thread, StackFrame, Scope, Source, Handles, Breakpoint
} from 'vscode-debugadapter';
import {DebugProtocol} from 'vscode-debugprotocol';
import * as fs from 'fs';
import * as path from 'path';
const cmd = require('node-cmd');
// import {
// DebugSession,
// InitializedEvent, TerminatedEvent, StoppedEvent, BreakpointEvent, OutputEvent, Event,
// Thread, StackFrame, Scope, Source, Handles, Breakpoint
// } from 'vscode-debugadapter';
// import {DebugProtocol} from 'vscode-debugprotocol';
// import * as fs from 'fs';
// import * as path from 'path';
// const cmd = require('node-cmd');
export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
godot: string;
projectDir: string;
runWithEditor: boolean;
params: string[];
}
// export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments {
// godot: string;
// projectDir: string;
// runWithEditor: boolean;
// params: string[];
// }
class GodotDebugSession extends DebugSession {
// class GodotDebugSession extends DebugSession {
// we don't support multiple threads, so we can use a hardcoded ID for the default thread
private static THREAD_ID = 1;
// // we don't support multiple threads, so we can use a hardcoded ID for the default thread
// private static THREAD_ID = 1;
/**
* Creates a new debug adapter that is used for one debug session.
* We configure the default implementation of a debug adapter here.
*/
public constructor() {
super();
}
// /**
// * Creates a new debug adapter that is used for one debug session.
// * We configure the default implementation of a debug adapter here.
// */
// public constructor() {
// super();
// }
/**
* The 'initialize' request is the first request called by the frontend
* to interrogate the features the debug adapter provides.
*/
protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void {
// /**
// * The 'initialize' request is the first request called by the frontend
// * to interrogate the features the debug adapter provides.
// */
// protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void {
// since this debug adapter can accept configuration requests like 'setBreakpoint' at any time,
// we request them early by sending an 'initializeRequest' to the frontend.
// The frontend will end the configuration sequence by calling 'configurationDone' request.
this.sendEvent(new InitializedEvent());
// // since this debug adapter can accept configuration requests like 'setBreakpoint' at any time,
// // we request them early by sending an 'initializeRequest' to the frontend.
// // The frontend will end the configuration sequence by calling 'configurationDone' request.
// this.sendEvent(new InitializedEvent());
// This debug adapter implements the configurationDoneRequest.
response.body.supportsConfigurationDoneRequest = true;
// // This debug adapter implements the configurationDoneRequest.
// response.body.supportsConfigurationDoneRequest = true;
// make VS Code to use 'evaluate' when hovering over source
response.body.supportsEvaluateForHovers = true;
// // make VS Code to use 'evaluate' when hovering over source
// response.body.supportsEvaluateForHovers = true;
// make VS Code to show a 'step back' button
response.body.supportsStepBack = true;
// // make VS Code to show a 'step back' button
// response.body.supportsStepBack = true;
this.log("initializeRequest");
this.log_err("initializeRequest");
this.log_console("initializeRequest");
this.sendResponse(response);
}
// this.log("initializeRequest");
// this.log_err("initializeRequest");
// this.log_console("initializeRequest");
// this.sendResponse(response);
// }
protected launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): void {
for(let key of Object.keys(args))
this.log(`${key} : ${args[key]}`);
let workspaceValid = false
if(args.godot && fs.existsSync(args.godot) && fs.statSync(args.godot).isFile() ) {
if(args.projectDir && fs.existsSync(args.projectDir) && fs.statSync(args.projectDir).isDirectory() ) {
let cfg = path.join(args.projectDir, "engine.cfg");
if( fs.existsSync(cfg) && fs.statSync(cfg).isFile())
workspaceValid = true;
}
}
if(workspaceValid) {
let params = `-path ${args.projectDir} `;
if(args.runWithEditor)
params += "-e";
if(args.params) {
for(let p of args.params)
params += " " + p;
}
let cmdcontent = `${args.godot} ${params}`;
this.log(cmdcontent)
// TODO: print outputs in terminal console
cmd.run(cmdcontent);
this.sendEvent(new TerminatedEvent());
}
else {
this.log_err("Invalidate path of projectDir or godot:");
this.log_err(JSON.stringify(args, null, '\t'));
this.sendEvent(new TerminatedEvent());
}
}
// protected launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): void {
// for(let key of Object.keys(args))
// this.log(`${key} : ${args[key]}`);
// let workspaceValid = false
// if(args.godot && fs.existsSync(args.godot) && fs.statSync(args.godot).isFile() ) {
// if(args.projectDir && fs.existsSync(args.projectDir) && fs.statSync(args.projectDir).isDirectory() ) {
// let cfg = path.join(args.projectDir, "engine.cfg");
// if( fs.existsSync(cfg) && fs.statSync(cfg).isFile())
// workspaceValid = true;
// }
// }
// if(workspaceValid) {
// let params = `-path ${args.projectDir} `;
// if(args.runWithEditor)
// params += "-e";
// if(args.params) {
// for(let p of args.params)
// params += " " + p;
// }
// let cmdcontent = `${args.godot} ${params}`;
// this.log(cmdcontent)
// // TODO: print outputs in terminal console
// cmd.run(cmdcontent);
// this.sendEvent(new TerminatedEvent());
// }
// else {
// this.log_err("Invalidate path of projectDir or godot:");
// this.log_err(JSON.stringify(args, null, '\t'));
// this.sendEvent(new TerminatedEvent());
// }
// }
protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void {
// protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): void {
this.sendResponse(response);
}
// this.sendResponse(response);
// }
protected threadsRequest(response: DebugProtocol.ThreadsResponse): void {
// protected threadsRequest(response: DebugProtocol.ThreadsResponse): void {
// return the default thread
response.body = {
threads: [
new Thread(GodotDebugSession.THREAD_ID, "thread 1")
]
};
this.sendResponse(response);
}
// // return the default thread
// response.body = {
// threads: [
// new Thread(GodotDebugSession.THREAD_ID, "thread 1")
// ]
// };
// this.sendResponse(response);
// }
/**
* Returns a fake 'stacktrace' where every 'stackframe' is a word from the current line.
*/
protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): void {
this.sendResponse(response);
}
// /**
// * Returns a fake 'stacktrace' where every 'stackframe' is a word from the current line.
// */
// protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): void {
// this.sendResponse(response);
// }
protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void {
this.sendResponse(response);
}
// protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void {
// this.sendResponse(response);
// }
protected variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): void {
this.sendResponse(response);
}
// protected variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): void {
// this.sendResponse(response);
// }
protected continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): void {
this.sendEvent(new TerminatedEvent());
}
// protected continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): void {
// this.sendEvent(new TerminatedEvent());
// }
protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, args: DebugProtocol.ReverseContinueArguments) : void {
this.sendResponse(response);
}
// protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, args: DebugProtocol.ReverseContinueArguments) : void {
// this.sendResponse(response);
// }
protected nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): void {
this.sendResponse(response);
}
// protected nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): void {
// this.sendResponse(response);
// }
protected stepBackRequest(response: DebugProtocol.StepBackResponse, args: DebugProtocol.StepBackArguments): void {
this.sendResponse(response);
}
// protected stepBackRequest(response: DebugProtocol.StepBackResponse, args: DebugProtocol.StepBackArguments): void {
// this.sendResponse(response);
// }
protected evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments): void {
this.sendResponse(response);
}
// protected evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments): void {
// this.sendResponse(response);
// }
/**
* Fire StoppedEvent if line is not empty.
*/
private fireStepEvent(response: DebugProtocol.Response, ln: number): boolean {
return false;
}
// /**
// * Fire StoppedEvent if line is not empty.
// */
// private fireStepEvent(response: DebugProtocol.Response, ln: number): boolean {
// return false;
// }
private log(msg: string) {
const e = new OutputEvent(msg, "stdout");
this.sendEvent(e);
}
private log_err(msg: string) {
const e = new OutputEvent(msg, "stderr");
this.sendEvent(e);
}
private log_console(msg: string) {
const e = new OutputEvent(msg, "console");
this.sendEvent(e);
}
}
// private log(msg: string) {
// const e = new OutputEvent(msg, "stdout");
// this.sendEvent(e);
// }
// private log_err(msg: string) {
// const e = new OutputEvent(msg, "stderr");
// this.sendEvent(e);
// }
// private log_console(msg: string) {
// const e = new OutputEvent(msg, "console");
// this.sendEvent(e);
// }
// }
DebugSession.run(GodotDebugSession);
// DebugSession.run(GodotDebugSession);

View File

@@ -34,14 +34,67 @@ class GDScriptCompletionItemProvider implements CompletionItemProvider {
constructor() {
}
private get_previous_flag(document : TextDocument, position : Position): string {
const line = document.lineAt(position).text;
let res = "";
for (let index = position.character; index >= 0; index--) {
res = line[index];
if (['.', '$', '"', "'"].indexOf(res) != -1 )
break;
}
return res;
}
provideCompletionItems(document : TextDocument, position : Position, token : CancellationToken) : CompletionItem[] | Thenable < CompletionItem[] > | CompletionList | Thenable < CompletionList > {
return new Promise((resolve, reject) => {
let items:CompletionItem[] = config.getWorkspaceCompletionItems();
items = [...items, ...config.bintinSybmolInfoList];
resolve(items);
});
const lastFlag = this.get_previous_flag(document, position);
const builtins = config.getBuiltinCompletions();
let items:CompletionItem[] = [...(builtins.builtinConstants)];
if(!lastFlag || lastFlag.trim().length == 0) {
const workspaces = config.getWorkspaceCompletionItems([config.normalizePath(document.fileName)]);
items = [
...items,
...(workspaces.functions),
...(workspaces.classes),
...(workspaces.constants),
...(workspaces.properties),
...(builtins.functions),
...(builtins.classes),
...(builtins.constants),
]
}
else {
const workspaces = config.getWorkspaceCompletionItems();
if(lastFlag.trim() == ".") {
items = [
...items,
...(workspaces.functions),
...(workspaces.constants),
...(workspaces.properties),
...(workspaces.classes),
...(builtins.functions),
...(builtins.constants),
...(builtins.properties)
]
}
else if(lastFlag.trim() == "'" || lastFlag.trim() == '"') {
items = [
...items,
...(workspaces.signals),
...(workspaces.functions),
...(workspaces.properties),
...(builtins.signals),
...(builtins.functions),
...(builtins.properties),
...(workspaces.nodes),
]
}
else if(lastFlag.trim() == "$") {
items = [ ...(workspaces.nodes) ]
}
}
return items;
}
resolveCompletionItem(item : CompletionItem, token : CancellationToken) : CompletionItem | Thenable < CompletionItem > {

View File

@@ -15,16 +15,19 @@ import config from '../config';
import {isStr, getSelectedContent, getStrContent} from './utils';
class GDScriptDefinitionProivder implements DefinitionProvider {
constructor() {
private _rootFolder : string = "";
constructor(rootFolder: string) {
this._rootFolder = rootFolder;
}
provideDefinition(document: TextDocument, position: Position, token: CancellationToken): Definition | Thenable < Definition > {
const getDefinitions = (content: string):Location[]| Location => {
if(content.startsWith("res://")) {
content = content.replace("res://", "");
if(workspace && workspace.rootPath)
content = path.join(workspace.rootPath, content)
if(workspace && workspace.rootPath) {
content = path.join(this._rootFolder, content);
}
return new Location(Uri.file(content), new Range(0,0,0,0));
}
else if(fs.existsSync(content) && fs.statSync(content).isFile()) {
@@ -54,6 +57,8 @@ class GDScriptDefinitionProivder implements DefinitionProvider {
scriptitems = [...scriptitems, ...checkDifinition(script.functions)];
scriptitems = [...scriptitems, ...checkDifinition(script.signals)];
scriptitems = [...scriptitems, ...checkDifinition(script.classes)];
if(script.enumerations)
scriptitems = [...scriptitems, ...checkDifinition(script.enumerations)];
locations = [...locations, ...scriptitems];
}
// check from builtin

View File

@@ -1,134 +1,194 @@
import requestGodot from "../request";
import * as vscode from 'vscode';
import {DiagnosticCollection, DiagnosticSeverity} from 'vscode';
import config from '../config';
interface GDParseError {
message: string,
column: number,
row: number
}
interface GDScript {
members: {
constants: {},
functions: {},
variables: {},
signals: {}
},
base: string,
errors: GDParseError[],
valid: boolean,
is_tool: boolean,
native: string
}
interface ParseRequest {
text: string,
path: string
}
class GDScriptDiagnosticSeverity {
private _subscription: DiagnosticCollection;
constructor() {
this._subscription = vscode.languages.createDiagnosticCollection("gdscript")
}
dispose() {
this._subscription.dispose()
}
async validateScript(doc: vscode.TextDocument, script: any) {
if(doc.languageId == 'gdscript') {
if(script) {
let diagnostics = [
...(this.validateExpression(doc)),
...(this.validateUnusedSymbols(doc, script)),
];
this._subscription.set(doc.uri, diagnostics);
return true;
}
}
return false;
}
private validateUnusedSymbols(doc: vscode.TextDocument,script) {
let diagnostics = [];
const text = doc.getText();
const check = (name:string, range: vscode.Range) => {
var matchs = text.match(new RegExp(`[^0-9A-Za-z_]\\s*${name}[^0-9A-Za-z_]\\s*`, 'g'));
let count = matchs?matchs.length:0;
var incomment = text.match(new RegExp(`#[^0-9A-z_]*${name}[^0-9A-z_]`, 'g'));
count -= incomment?incomment.length:0;
if(count <= 1)
diagnostics.push(new vscode.Diagnostic(range, `${name} is never used.`, DiagnosticSeverity.Warning));
};
// Unused variables
for (let key of Object.keys(script.variables))
check(key, script.variables[key]);
for (let key of Object.keys(script.constants))
check(key, script.constants[key]);
return diagnostics;
}
private validateExpression(doc: vscode.TextDocument) {
let diagnostics = [];
const text = doc.getText();
const lines = text.split(/\r?\n/);
lines.map((line:string, i: number) =>{
let matchstart = /[^\s]+.*/.exec(line);
let curLineStartAt = 0;
if(matchstart)
curLineStartAt = matchstart.index;
// ignore comments
if(line.match(/^\s*#.*/) || line.match(/^#.*/)) return
// normalize line content
line = "\t" + line + "\t";
if(line.match(/[^#].*?\;/) && !line.match(/[#].*?\;/)) {
const semicolonIndex = line.indexOf(';');
diagnostics.push(new vscode.Diagnostic(new vscode.Range(i, semicolonIndex, i, semicolonIndex+1), "Statement contains a semicolon.", DiagnosticSeverity.Warning));
}
if(line.match(/[^\w](if|elif|else|for|while|func|class)[^\w].*?/) && !line.match(/#.*?[^\w](if|elif|else|for|while|func|class)[^\w].*?/)) {
var range = new vscode.Range(i, curLineStartAt, i, line.length);
if(!line.match(/(if|elif|else|for|while|func|class).*?\:/))
diagnostics.push(new vscode.Diagnostic(range, "':' expected at end of the line.", DiagnosticSeverity.Error));
else if(line.match(/(if|elif|while|func|class)\s*\:/))
diagnostics.push(new vscode.Diagnostic(range, "Indentifier expected before ':'", DiagnosticSeverity.Error));
else if(line.match(/[^\w]for[^\w]/) && !line.match(/\s+for\s\w+\s+in\s+[\w+]|\{.*?\}|\[.*?\]/))
diagnostics.push(new vscode.Diagnostic(range, "Invalid for expression", DiagnosticSeverity.Error));
else if(line.match(/(if|elif|while)\s*\(.*\)/))
diagnostics.push(new vscode.Diagnostic(range, "Extra brackets in condition expression.", DiagnosticSeverity.Warning));
if(line.match(/([^\w]if|elif|else|for|while|func|class[^\w]).*\:[ \t]+[^#\s]+/))
return
else if( i < lines.length-1) {
let next = i+1;
let nextline = lines[next];
// changes nextline until finds a line containg text or comes to the last line
while ((!nextline || nextline.match(/\s+$/)) && next < lines.length-1) {
++next;
nextline = lines[next];
}
let nextLineStartAt = -1;
let match = /[^\s]+.*/.exec(nextline);
if(match)
nextLineStartAt = match.index;
if(nextLineStartAt <= curLineStartAt)
diagnostics.push(new vscode.Diagnostic(range, "Expected indented block after expression", DiagnosticSeverity.Error));
}
else
diagnostics.push(new vscode.Diagnostic(range, "Expected indented block after expression", DiagnosticSeverity.Error));
}
});
return diagnostics;
}
}
export default GDScriptDiagnosticSeverity;
import requestGodot from "../request";
import * as vscode from 'vscode';
import {DiagnosticCollection, DiagnosticSeverity} from 'vscode';
import config from '../config';
interface GDParseError {
message : string,
column : number,
row : number
}
interface GDScript {
members : {
constants: {},
functions: {},
variables: {},
signals: {}
},
base : string,
errors : GDParseError[],
valid : boolean,
is_tool : boolean,
native : string
}
interface ParseRequest {
text : string,
path : string
}
class GDScriptDiagnosticSeverity {
private _subscription : DiagnosticCollection;
constructor() {
this._subscription = vscode.languages.createDiagnosticCollection("gdscript")
}
dispose() {
this._subscription.dispose()
}
async validateScript(doc : vscode.TextDocument, script : any) {
if (doc.languageId == 'gdscript') {
if (script) {
let diagnostics = [ ...(this.validateExpression(doc)), ...(this.validateUnusedSymbols(doc, script)) ];
this._subscription.set(doc.uri, diagnostics);
return true;
}
}
return false;
}
private validateUnusedSymbols(doc : vscode.TextDocument, script) {
let diagnostics = [];
let cfg : any = vscode.workspace.getConfiguration("GodotTools").get("lint");
if (!cfg.unusedSymbols)
return diagnostics
const text = doc.getText();
const check = (name : string, range : vscode.Range) => {
var matchs = text.match(new RegExp(`([^\\w]|\\[|\\{)\\s*${name}\\s*([^\\w]|\\[|\\{)`, 'g'));
let count = matchs ? matchs.length : 0;
var incomment = text.match(new RegExp(`#.*?([^\\w]|\\[|\\{)\\s*${name}\\s*([^\\w]|\\[|\\{)`, 'g'));
count -= incomment ? incomment.length : 0;
if (count <= 1)
diagnostics.push(new vscode.Diagnostic(range, `${name} is never used.`, DiagnosticSeverity.Warning));
};
// Unused variables
for (let key of Object.keys(script.variables))
check(key, script.variables[key]);
for (let key of Object.keys(script.constants))
check(key, script.constants[key]);
return diagnostics;
}
private validateExpression(doc : vscode.TextDocument) {
let cfg : any = vscode.workspace.getConfiguration("GodotTools").get("lint");
let diagnostics = [];
let expectEndOfLine = false;
const text = doc.getText();
const lines = text.split(/\r?\n/);
lines.map((line : string, i : number) => {
let matchstart = /[^\s]+.*/.exec(line);
let curLineStartAt = 0;
if (matchstart)
curLineStartAt = matchstart.index;
// ignore comments
if (line.match(/^\s*#.*/) || line.match(/^#.*/))
return
// normalize line content
line = "\t" + line + "\t";
var range = new vscode.Range(i, curLineStartAt, i, line.length);
if (cfg.semicolon && line.match(/[^#].*?\;/) && !line.match(/[#].*?\;/)) {
const semicolonIndex = line.indexOf(';');
diagnostics.push(new vscode.Diagnostic(new vscode.Range(i, semicolonIndex, i, semicolonIndex + 1), "Statement contains a semicolon.", DiagnosticSeverity.Warning));
}
if (line.match(/[^#].*?/) && expectEndOfLine) {
if (!line.match(/.*?(\\|\:)/)) {
diagnostics.push(new vscode.Diagnostic(range, "': or \\' expected at end of the line.", DiagnosticSeverity.Error));
expectEndOfLine = false;
}
if (line.match(/.*?\:/))
expectEndOfLine = false;
}
const colonKeywords = /\b(if|elif|else|for|while|func|class|match)\b/;
let keywords = line.match(colonKeywords)
if (keywords) {
if(line.match(new RegExp(`".*?\\s${keywords[1]}\\s.*?"`)) || line.match(new RegExp(`'.*?\\s${keywords[1]}\\s.*?\'`)))
return
if(line.match(new RegExp(`.*?#.*?\\s${keywords[1]}\\s.*?`)))
return
if(line.match(/.*?\sif\s+(\!|\[|\{|\w|").*?\s+else\s+[^\s]+/))
return
if (line.match(/.*?\\/))
expectEndOfLine = true;
else if (line.match(/.*?\:[\s+]+[^#\s]+/))
return
else if (!line.match(/.*?(\\|\:)/))
diagnostics.push(new vscode.Diagnostic(range, "': or \\' expected at end of the line.", DiagnosticSeverity.Error));
else if (line.match(/\s(if|elif|while|func|class|match)\s*\:/))
diagnostics.push(new vscode.Diagnostic(range, "Indentifier expected before ':'", DiagnosticSeverity.Error));
else if (line.match(/[^\w]for[^\w]/) && !line.match(/\s+for\s\w+\s+in\s+|[\w+]|\{.*?\}|\[.*?\]|\(.*?\)/)){
if(!(line.match(/".*?for.*?"/) || line.match(/'.*?for.*?'/)))
diagnostics.push(new vscode.Diagnostic(range, "Invalid for expression", DiagnosticSeverity.Error));
}
else if (cfg.conditionBrackets && line.match(/\s(if|elif|while|match)\s*\(.*\)\s*:\s*$/))
diagnostics.push(new vscode.Diagnostic(range, "Extra brackets in condition expression.", DiagnosticSeverity.Warning));
const blockIndetCheck = function() {
const err = new vscode.Diagnostic(range, "Expected indented block after expression", DiagnosticSeverity.Error);
if (i < lines.length - 1) {
let next = i + 1;
let nextline = lines[next];
// changes nextline until finds a line containg text or comes to the last line
while (((!nextline || !nextline.trim().length) || nextline.match(/^\s*#/)) && next < lines.length - 1) {
++next;
nextline = lines[next];
}
let nextLineStartAt = -1;
let match = /[^\s]+.*/.exec(nextline);
if (match)
nextLineStartAt = match.index;
if (nextLineStartAt <= curLineStartAt)
diagnostics.push(err);
}
else if(line.match(/\:\s*$/))
diagnostics.push(err);
};
if(!expectEndOfLine)
blockIndetCheck();
}
// Do not check : for end of statement as it breaks match statment
let endOfStateMentWithComma = false;
if(endOfStateMentWithComma && !line.match(colonKeywords) && line.match(/\:\s*$/)) {
let showErr = true;
if( i >= 1 ) {
let previous = i - 1;
let previousline = lines[previous];
while(previousline.match(/\\\s*$/) && previous>=1) {
--previous;
const ppreviousline = lines[previous];
if(ppreviousline.match(/\\\s*$/))
previousline = ppreviousline;
}
const keywords = previousline.match(colonKeywords);
if(keywords && !(previousline.match(new RegExp(`".*?\\s${keywords[1]}\\s.*?"`)) || previousline.match(new RegExp(`'.*?\\s${keywords[1]}\\s.*?'`)) ))
showErr = false
}
if(showErr)
diagnostics.push(new vscode.Diagnostic(range, "Expected end of statement after expression", DiagnosticSeverity.Error));
}
if (line.match(/(if|elif|while|return)\s+\w+\s*=\s*\w+/))
diagnostics.push(new vscode.Diagnostic(range, "Assignment in condition or return expressions", DiagnosticSeverity.Warning));
else if (line.indexOf("==") > 0 && !line.match(/\:\s*/)) {
const endAt = line.indexOf("==");
const precontent = line.substring(0, endAt);
if (!precontent.match(/\s(if|elif|while|return)\s/) && !precontent.match(/=[^=]/) && !precontent.match(/assert\s*\(/) && !expectEndOfLine) {
diagnostics.push(new vscode.Diagnostic(range, "Unhandled comparation expression contains", DiagnosticSeverity.Warning));
}
}
let match = /var\s+(\w+)\s*=\s*(\w+)/.exec(line);
if (match && match.length > 2 && match[1].length > 0 && match[1] == match[2]) {
diagnostics.push(new vscode.Diagnostic(range, "Self Assignment may cause error.", DiagnosticSeverity.Warning));
}
});
return diagnostics;
}
}
export default GDScriptDiagnosticSeverity;

View File

@@ -17,9 +17,7 @@ function genLink(title:string, uri:string, span=true):string {
function getProp(rawDoc:any, propname: string, action=(s :string)=>s): string {
let prop = rawDoc[propname];
if(prop && prop.length > 0)
prop = action(prop);
return prop;
return action(prop);
}
class GDScriptDocumentContentProvider implements TextDocumentContentProvider{
@@ -67,9 +65,55 @@ class GDScriptDocumentContentProvider implements TextDocumentContentProvider{
});
}
format_documentation(text: string): string {
let doc = text.replace(/\[code\]/g, "<code>").replace(/\[\/code\]/g, "</code>");
doc = doc.replace(/\[codeblock\]/g, '<pre><code class="gdscript">').replace(/\[\/codeblock]/g, "</code></pre>");
doc = doc.replace(/\[i\]/g, "<i>").replace(/\[\/i\]/g, "</i>");
doc = doc.replace(/\[b\]/g, "<b>").replace(/\[\/b\]/g, "</b>");
doc = doc.replace(/\[u\]/g, "<u>").replace(/\[\/u\]/g, "</u>");
doc = doc.replace(/\n\t\t\t\t/g, "\n\t");
return doc;
};
genMethodDoc(mDoc:any):string {
let ret_type = getProp(mDoc, "return_type", (type:string):string =>{
return `${genLink(type,type)} `;
if(type.length > 0)
return `${genLink(type,type)} `;
else
return "void";
});
let args = "";
for(let arg of mDoc.arguments){
if(mDoc.arguments.indexOf(arg)!=0)
args += ", ";
args += `${genLink(arg.type, arg.type)} ${arg.name}`
if(arg.default_value && arg.default_value.length > 0)
args += `=${arg.default_value}`;
}
var docContent = mDoc.description;
if (!docContent) {
docContent = `There is currently no description for this method. Please help us by <span><a href="http://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html">contributing one</a></span>!`;
}
let doc = `
<li>
<h4 id="${mDoc.name}">${ret_type} ${mDoc.name} (${args}) <i>${mDoc.qualifiers}</i></h4>
<p>${docContent}</p>
</li>
`;
return doc;
}
genPropHeader(mDoc:any, classname:string): string {
let type = getProp(mDoc, "type", (type:string):string => `${genLink(type,type)} `);
return `<li>${type} ${genLink(mDoc.name, classname+"."+mDoc.name)}</li>`;
}
genMethodHeader(mDoc:any, classname:string):string {
let ret_type = getProp(mDoc, "return_type", (type:string):string =>{
if(type.length > 0)
return `${genLink(type,type)} `;
else
return "void";
});
let args = "";
for(let arg of mDoc.arguments){
@@ -81,18 +125,30 @@ class GDScriptDocumentContentProvider implements TextDocumentContentProvider{
}
let doc = `
<li>
<h4>${ret_type} ${mDoc.name} (${args}) <i>${mDoc.qualifiers}</i></h4>
<p>${mDoc.description}</p>
${ret_type} ${genLink(mDoc.name, classname+"."+mDoc.name)} (${args}) <i>${mDoc.qualifiers}</i>
</li>
`;
`;
return doc;
}
genPropDoc(pDoc:any): string {
let setter = pDoc.setter;
if(setter) setter = `<li>Setter: ${setter}(value)</li>`; else setter = "";
let getter = pDoc.getter;
if(getter) getter = `<li>Getter: ${getter}()</li>`; else getter = "";
let descContent = pDoc.description;
if(!descContent) {
descContent = `There is currently no description for this property. Please help us by <span><a href="http://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html">contributing one</a></span>!`;
}
let doc = `
<li>
<h4>${genLink(pDoc.type,pDoc.type)} ${pDoc.name}</h4>
<p>${pDoc.description}</p>
<h4>${genLink(pDoc.type,pDoc.type)} ${pDoc.name}</h4>
<ul>
${setter}
${getter}
</ul>
<p>${descContent}</p>
</li>
`;
return doc;
@@ -171,6 +227,7 @@ class GDScriptDocumentContentProvider implements TextDocumentContentProvider{
return null;
const classname = rawDoc.name;
let inherits = getProp(rawDoc, "inherits", (inherits:string)=>{
if (!inherits) return "";
return "<h4>Inherits: " + genLink(inherits, inherits, true) +"</h4>";
});
@@ -189,18 +246,38 @@ class GDScriptDocumentContentProvider implements TextDocumentContentProvider{
subclasses = "<h3>Inherited by</h3> " + "<ul><li>" + subclasses + "</li></ul>";
let briefDescript = getProp(rawDoc, "brief_description", (dec:string)=>{
return "<h3>Brief Description</h3>" + "<ul><li>" + dec + "</li></ul>";
return dec;
});
let descript = getProp(rawDoc, "description", (dec:string)=>{
return "<h3>Description</h3>" + "<ul><li>" + dec + "</li></ul>";
if(dec)
return "<h3>Description</h3>" + "<ul><li>" + dec + "</li></ul>";
else
return "";
});
const setter_getters = {};
let propHeaders = ""
for(let p of rawDoc.properties) {
propHeaders += this.genPropHeader(p, classname);
if(p.setter)
setter_getters[p.setter] = true;
if(p.getter)
setter_getters[p.getter] = true;
}
if(propHeaders.length >0)
propHeaders = `<h3>Member List</h3><ul>${propHeaders}</ul/>`;
let methodHeaders = ""
let methods = "";
for(let m of rawDoc.methods) {
if(setter_getters[m.name]) continue;
methodHeaders += this.genMethodHeader(m, classname);
methods += this.genMethodDoc(m);
}
if(methodHeaders.length >0)
methodHeaders = `<h3>Public Methods</h3><ul>${methodHeaders}</ul/>`;
if(methods.length >0 )
methods = `<h3>Methods</h3><ul>${methods}</ul/>`;
methods = `<h3>Public Methods</h3><ul>${methods}</ul/>`;
let signals = "";
for(let s of rawDoc.signals) {
@@ -229,17 +306,19 @@ class GDScriptDocumentContentProvider implements TextDocumentContentProvider{
let doc = `
${linkStyle}
<h1>Native Class ${classname}</h1>
<h4>${briefDescript}</h4>
<p>${category}</p>
<p>${inherits}</p>
<p>${subclasses}</p>
<p>${briefDescript}</p>
<p>${descript}</p>
<p>${methods}</p>
<p>${propHeaders}</p>
<p>${methodHeaders}</p>
<p>${signals}</p>
<p>${constants}</p>
<p>${props}</p>
<p>${methods}</p>
`;
return doc;
return this.format_documentation(doc);
}
}

View File

@@ -4,7 +4,7 @@ import {
Position,
CancellationToken,
Hover,
MarkedString,
MarkdownString,
workspace,
Uri,
CompletionItem,
@@ -32,44 +32,57 @@ class GDScriptHoverProvider implements HoverProvider {
if (isStr(hoverText))
hoverText = getStrContent(hoverText);
const workspaceSymbols = config.getAllSymbols();
let tips: MarkedString[] = [];
let tips: MarkdownString[] = [];
const withMarkdwon = workspace.getConfiguration("GodotTools").get("workspaceDocumentWithMarkdown", false);
const makeMarkdown = (content): MarkdownString => {
let md = new MarkdownString(content);
md.isTrusted = true;
return md;
}
// check from workspace
const genWorkspaceTips = ()=> {
for (let path of Object.keys(workspaceSymbols)) {
const script = workspaceSymbols[path];
let scriptips: MarkedString[] = [];
const getHoverText = (items, type, path): MarkedString[] => {
const _items: MarkedString[] = [];
for (let filepath of Object.keys(workspaceSymbols)) {
const script = workspaceSymbols[filepath];
let scriptips: MarkdownString[] = [];
const getHoverText = (items, type, gdpath): MarkdownString[] => {
const _items: MarkdownString[] = [];
for (let name of Object.keys(items)) {
if (name == hoverText) {
let dfile = path;
if (workspace && workspace.asRelativePath(dfile))
dfile = workspace.asRelativePath(dfile);
let dfile = gdpath;
if (workspace) {
const root = config.normalizePath(workspace.rootPath) + "/";
if (gdpath.startsWith(root))
dfile = gdpath.replace(root, "");
}
let signature = "";
if(type == "func"|| type == "signal" && script.signatures[name])
signature = script.signatures[name];
if(type == "const" && script.constvalues[name])
signature = ` = ${script.constvalues[name]}`;
_items.push({language:'gdscript', value:`${type} ${name}${signature}`});
let doc = script.documents[name];
let doc ='```gdscript\n' + `${type} ${name}${signature}` + '\n```\n';
let rowDoc = script.documents[name];
if(!withMarkdwon)
doc = "```plaintext\r\n"+doc+"\r\n```";
rowDoc += "```plaintext\r\n"+rowDoc+"\r\n```";
doc += rowDoc;
doc = doc?doc+"\r\n\r\n":"";
if(path != "autoload")
doc += `*Defined in [${dfile}](${Uri.file(path).toString()})*`;
_items.push(doc)
if(gdpath != "autoload")
doc += `*Defined in [${dfile}](${Uri.file(gdpath).toString()})*`;
_items.push(makeMarkdown(doc));
break;
}
}
return _items;
}
scriptips = [...scriptips, ...getHoverText(script.variables, 'var', path)];
scriptips = [...scriptips, ...getHoverText(script.constants, 'const', path)];
scriptips = [...scriptips, ...getHoverText(script.functions, 'func', path)];
scriptips = [...scriptips, ...getHoverText(script.signals, 'signal', path)];
scriptips = [...scriptips, ...getHoverText(script.classes, 'class', path)];
scriptips = [...scriptips, ...getHoverText(script.variables, 'var', filepath)];
scriptips = [...scriptips, ...getHoverText(script.constants, 'const', filepath)];
scriptips = [...scriptips, ...getHoverText(script.functions, 'func', filepath)];
scriptips = [...scriptips, ...getHoverText(script.signals, 'signal', filepath)];
scriptips = [...scriptips, ...getHoverText(script.classes, 'class', filepath)];
if(script.enumerations)
scriptips = [...scriptips, ...getHoverText(script.enumerations, 'const', filepath)];
tips = [...tips, ...scriptips];
}
};
@@ -93,8 +106,8 @@ class GDScriptHoverProvider implements HoverProvider {
instance = ` which is an instance of *[${node.instance}](${Uri.file(instancepath).toString()})*`;
}
tips = [...tips,
`${genLink(node.type, node.type)} ${fullpath}`,
`${node.type} defined in *[${scnenepath}](${Uri.file(filepath).toString()})*${instance}`
makeMarkdown(`${genLink(node.type, node.type)} ${fullpath}`),
makeMarkdown(`${node.type} defined in *[${scnenepath}](${Uri.file(filepath).toString()})*${instance}`)
];
break;
}
@@ -102,11 +115,21 @@ class GDScriptHoverProvider implements HoverProvider {
}
};
const format_documentation = (text, cls="") => {
let doc = text.replace(/\[code\]/g, "`").replace(/\[\/code\]/g, "`");
doc = doc.replace(/\[codeblock\]/g, "\n```gdscript\n").replace(/\[\/codeblock]/g, "\n```");
doc = doc.replace(/\[i\]/g, "*").replace(/\[\/i\]/g, "*");
doc = doc.replace(/\[b\]/g, "**").replace(/\[\/b\]/g, "**");
doc = doc.replace(/\[u\]/g, "__").replace(/\[\/u\]/g, "__");
doc = doc.replace(/\n\t\t\t\t/g, "\n");
return doc;
};
// check from builtin
const genBuiltinTips = ()=> {
const item2MarkdStrings = (name: string,item: CompletionItem, rowDoc: any):MarkedString[] => {
const item2MarkdStrings = (name: string,item: CompletionItem, rowDoc: any):MarkdownString => {
let value = "";
let doc = item.documentation;
let doc = format_documentation(item.documentation);
// get class name
let classname = name;
let matchs = name.match(/[@A-z][A-z0-9]*\./);
@@ -118,8 +141,7 @@ class GDScriptHoverProvider implements HoverProvider {
const genMethodMarkDown = ():string =>{
let content = `${genLink(rowDoc.return_type, rowDoc.return_type)} `;
let matchs = name.match(/[@A-z][A-z0-9]*\./);
content += `${genLink(classname, classname)}.`;
if (rowDoc.name != classname) content += `${genLink(classname, classname)}.`;
let args = "";
for(let arg of rowDoc.arguments){
if(rowDoc.arguments.indexOf(arg)!=0)
@@ -128,34 +150,34 @@ class GDScriptHoverProvider implements HoverProvider {
if(arg.default_value && arg.default_value.length > 0)
args += `=${arg.default_value}`;
}
content += `${genLink(rowDoc.name, classname+'.'+rowDoc.name)}(${args}) ${rowDoc.qualifiers}`;
content += `${genLink(rowDoc.name, classname+'.' + rowDoc.name)}(${args}) ${rowDoc.qualifiers}`;
return content;
};
switch (item.kind) {
case CompletionItemKind.Class:
return [`Native Class ${genLink(classname, classname)}`, doc];
return makeMarkdown(`Native Class ${genLink(classname, classname)}\n${doc}`);
case CompletionItemKind.Method:
doc = item.documentation.substring(item.documentation.indexOf("\n")+1, item.documentation.length);
return [genMethodMarkDown(), doc];
doc = doc.substring(doc.indexOf("\n")+1, doc.length);
return makeMarkdown(`${genMethodMarkDown()}\n${doc}`);
case CompletionItemKind.Interface:
doc = item.documentation.substring(item.documentation.indexOf("\n")+1, item.documentation.length);
return ['signal ' + genMethodMarkDown(), doc];
doc = doc.substring(doc.indexOf("\n")+1, doc.length);
return makeMarkdown(`signal ${genMethodMarkDown()}\n${doc}`);
case CompletionItemKind.Variable:
case CompletionItemKind.Property:
return [`${rowDoc.type} ${genLink(classname, classname)}.${genLink(rowDoc.name, classname+"."+rowDoc.name)}`, doc];
return makeMarkdown(`${rowDoc.type} ${genLink(classname, classname)}.${genLink(rowDoc.name, classname + "." + rowDoc.name)}\n${doc}`);
case CompletionItemKind.Enum:
return [`const ${genLink(classname, classname)}.${genLink(rowDoc.name, classname+"."+rowDoc.name)} = ${rowDoc.value}`, doc];
return makeMarkdown(`const ${genLink(classname, classname)}.${genLink(rowDoc.name, classname + "." + rowDoc.name)} = ${rowDoc.value}\n${doc}`);
default:
break;
}
return [name, doc];
return makeMarkdown(`${name} ${doc}`);
};
for (let name of Object.keys(config.builtinSymbolInfoMap)) {
const pattern = `[A-z@_]+[A-z0-9_]*\\.${hoverText}\\b`;
if(name == hoverText || name.match(new RegExp(pattern))) {
const item: {completionItem: CompletionItem, rowDoc: any} = config.builtinSymbolInfoMap[name];
tips = [...tips, ...(item2MarkdStrings(name, item.completionItem, item.rowDoc))];
tips = [...tips, item2MarkdStrings(name, item.completionItem, item.rowDoc)];
}
}
};

View File

@@ -13,7 +13,8 @@ interface GDScript {
// symbol: marked string
documents: {},
// name : value
constvalues: {}
constvalues: {},
enumerations: {}
}
class GDScriptSymbolParser {
@@ -27,14 +28,26 @@ class GDScriptSymbolParser {
variables: {},
signals: {},
classes: {},
base: "Object",
native: "Object",
base: "",
native: "",
signatures: {},
documents: {},
constvalues: {}
constvalues: {},
enumerations: {}
}
const text = content;
const lines = text.split(/\r?\n/);
// Base class and native class
for (let line of lines) {
let match;
if (match = line.match(/extends\s+(\w+)/)) {
script.native = match[1];
break;
} else if (match = line.match(/extends\s+('|")(.*)('|")/)) {
script.base = match[2];
}
}
const getMatches = (regex:RegExp, index=1) => {
var matches = [];
@@ -138,7 +151,7 @@ class GDScriptSymbolParser {
}
let varreg = /var\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*/;
let varreg2 = "var\\s+$X$\\s*";
let varreg2 = "var\\s+$X$([^\\w]|$)";
let vargroup = 1;
if(ignoreIndentedVars) {
varreg = /^((export.*?var)|var)\s+([_A-Za-z]+[_A-Za-z0-9]*)\s?/;
@@ -172,14 +185,39 @@ class GDScriptSymbolParser {
script.constvalues[key] = match[2];
}
let classnames = getMatches(/class\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*extends\s+/, 1);
const classes = findLineRanges(classnames, "class\\s+$X$\\s*extends\\s+");
let classnames = getMatches(/class\s+([_A-Za-z]+[_A-Za-z0-9]*)(\s|\:)/, 1);
const classes = findLineRanges(classnames, "class\\s+$X$(\\s|\\:)");
for (let key of Object.keys(classes)) {
const r:Range = determRange(key, classes)
script.classes[key] = determRange(key, classes);
script.classes[key] = r;
script.documents[key] = parseDocument(r);
}
// console.log(script);
let enumnames = getMatches(/enum\s+([_A-Za-z]+[_A-Za-z0-9]*)\s+\{/, 1);
const enums = findLineRanges(enumnames, "enum\\s+$X$\\s+\{");
for (let key of Object.keys(enums)) {
const r:Range = determRange(key, enums)
script.constants[key] = r;
script.documents[key] = parseDocument(r);
let curindex = r.start.line
while (curindex < lines.length) {
const line = lines[curindex];
let matchs = line.match(/([_A-Za-z]+[_A-Za-z0-9]*)/g);
if(matchs && matchs.length >= 1 ){
for (var i = 0; i < matchs.length; i++)
if(line.indexOf(matchs[i]) > line.indexOf("{"))
script.enumerations[matchs[i]] = new Range(curindex, 0, curindex, line.length);
}
if(line.indexOf("}") == -1)
curindex += 1;
else
break;
}
}
// TODO: enumerations without name
// const unnamedEnums = text.match(/enum\s+\{.*\}/gm)
return script;
}
@@ -195,4 +233,4 @@ class GDScriptSymbolParser {
}
export default GDScriptSymbolParser;
export default GDScriptSymbolParser;

View File

@@ -48,6 +48,12 @@ class GDScriptSymbolProvider implements DocumentSymbolProvider {
for (let key of Object.keys(classes))
symbols.push(new SymbolInformation(key, SymbolKind.Class, classes[key]));
if(script.enumerations) {
const enumerations = script.enumerations;
for (let key of Object.keys(enumerations))
symbols.push(new SymbolInformation(key, SymbolKind.Enum, enumerations[key]));
}
return symbols;
}

View File

@@ -30,6 +30,8 @@ class GDScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider
queryMembers(query, scrip.variables, vscode.SymbolKind.Variable, path);
queryMembers(query, scrip.constants, vscode.SymbolKind.Constant, path);
queryMembers(query, scrip.classes, vscode.SymbolKind.Class, path);
if(scrip.enumerations)
queryMembers(query, scrip.enumerations, vscode.SymbolKind.Enum, path);
}
return symbols;
}

View File

@@ -11,7 +11,6 @@ var glob = require("glob")
import config from './config';
import * as path from 'path';
import * as fs from 'fs';
const cmd = require('node-cmd');
class ToolManager {
private workspaceDir: string = "";
@@ -19,19 +18,30 @@ class ToolManager {
private workspacesymbolprovider: GDScriptWorkspaceSymbolProvider = null;
private _disposable: vscode.Disposable;
private _context: vscode.ExtensionContext;
private _projectFile : string = "project.godot";
private _rootDir : string = "";
private _biuitinDocFile : string = "doc/classes-3.0.json";
constructor(context: vscode.ExtensionContext) {
this._context = context;
this.workspaceDir = vscode.workspace.rootPath;
if(vscode.workspace && this.workspaceDir) {
vscode.workspace.registerTextDocumentContentProvider('godotdoc', new GDScriptDocumentContentProvider());
this.workspaceDir = this.workspaceDir.replace(/\\/g, "/");
this.loadWorkspaceSymbols();
}
if(0) { // TODO: EditorServer validate
this.validate();
let completionDollar = false;
if (vscode.workspace.getConfiguration("GodotTools").get("godotVersion", 3.0) < 3) {
this._projectFile = "engine.cfg";
this._biuitinDocFile = "doc/classes-2.1.json";
completionDollar = true;
}
this.loadClasses();
if (vscode.workspace && this.workspaceDir) {
vscode.workspace.registerTextDocumentContentProvider('godotdoc', new GDScriptDocumentContentProvider());
this.workspaceDir = this.workspaceDir.replace(/\\/g, "/");
this._rootDir = vscode.workspace.getConfiguration("GodotTools").get("godotProjectRoot", this.workspaceDir);
this._rootDir = this._rootDir.replace("${workspaceRoot}", this.workspaceDir);
this.loadWorkspaceSymbols();
}
// documentation symbol provider
this.symbolprovider = new GDScriptSymbolProvider();
vscode.languages.registerDocumentSymbolProvider('gdscript', this.symbolprovider);
@@ -39,66 +49,82 @@ class ToolManager {
this.workspacesymbolprovider = new GDScriptWorkspaceSymbolProvider();
vscode.languages.registerWorkspaceSymbolProvider(this.workspacesymbolprovider);
// definition provider
vscode.languages.registerDefinitionProvider('gdscript', new GDScriptDefinitionProivder());
vscode.languages.registerDefinitionProvider('gdscript', new GDScriptDefinitionProivder(this._rootDir));
// hover provider
vscode.languages.registerHoverProvider('gdscript', new GDScriptHoverProvider());
// code completion provider
vscode.languages.registerCompletionItemProvider('gdscript', new GDScriptCompletionItemProvider(), '.', '"', "'");
if (completionDollar)
vscode.languages.registerCompletionItemProvider('gdscript', new GDScriptCompletionItemProvider(), '.', '"', "'", "$");
else
vscode.languages.registerCompletionItemProvider('gdscript', new GDScriptCompletionItemProvider(), '.', '"', "'");
// signature help provider
vscode.languages.registerSignatureHelpProvider('gdscript', new GDScriptSignatureHelpProvider(), '(', ',');
// Commands
this._disposable = vscode.Disposable.from(
vscode.commands.registerCommand('godot.updateWorkspaceSymbols', this.loadWorkspaceSymbols.bind(this)),
vscode.commands.registerCommand('godot.runWorkspace', ()=>{this.openWorkspaceWithEditor()}),
vscode.commands.registerCommand('godot.openWithEditor', ()=>{this.openWorkspaceWithEditor("-e")}),
vscode.commands.registerCommand('godot.runCurrentScene', this.runCurrentScenr.bind(this)),
vscode.commands.registerCommand('godot.provideInitialDebugConfigurations', this.getDefaultDebugConfig.bind(this))
vscode.commands.registerCommand('godot.runWorkspace', () => { this.openWorkspaceWithEditor() }),
vscode.commands.registerCommand('godot.openWithEditor', () => { this.openWorkspaceWithEditor("-e") }),
vscode.commands.registerCommand('godot.runCurrentScene', this.runCurrentScene.bind(this)),
);
}
validate() {
const self = this;
godotRequest({action: "editor", command: "projectdir"}).then((res: any)=>{
godotRequest({
action: "editor",
command: "projectdir"
}).then((res: any) => {
let path = res.path;
if(path && path.length> 0 && path.endsWith("/"))
path = path.substring(0, path.length-1)
if( path.toLowerCase() == self.workspaceDir.toLowerCase())
vscode.window.showInformationMessage("Connected to godot editor server");
if (path && path.length > 0 && path.endsWith("/"))
path = path.substring(0, path.length - 1)
if (path.toLowerCase() == self.workspaceDir.toLowerCase())
vscode.window.showInformationMessage("Connected to the Godot editor server");
else {
vscode.window.showWarningMessage("The opened project is not same with godot editor");
vscode.window.showWarningMessage("The opened project is not the same within the Godot editor");
}
}).catch(e=>{
vscode.window.showErrorMessage("Failed connect to godot editor server");
}).catch(e => {
vscode.window.showErrorMessage("Failed connecting to the Godot editor server");
});
}
loadAllSymbols(): Promise<any> {
loadAllSymbols(): Promise < any > {
const self = this;
return new Promise((resolve, reject) => {
glob( self.workspaceDir +"/**/*.gd", (err, files)=>{
if(!err) {
glob(self.workspaceDir + "/**/*.gd", (err, files) => {
if (!err) {
const symbols = {};
for(let i=0; i< files.length; i++)
for (let i = 0; i < files.length; i++)
symbols[config.normalizePath(files[i])] = config.loadSymbolsFromFile(files[i]);
// load autoloads from engin.cfg
const engincfg = path.join(self.workspaceDir, "engine.cfg");
if(fs.existsSync(engincfg) && fs.statSync(engincfg).isFile()) {
const engincfg = path.join(self.workspaceDir, this._projectFile);
if (fs.existsSync(engincfg) && fs.statSync(engincfg).isFile()) {
try {
const script = { constants: {}, functions: {}, variables: {}, signals: {}, classes: {}, base: "Object", native: "Object", constpathes: {}, documents: {}, constvalues: {}};
const script = {
constants: {},
functions: {},
variables: {},
signals: {},
classes: {},
base: "Object",
native: "Object",
constpathes: {},
documents: {},
constvalues: {}
};
let content: string = fs.readFileSync(engincfg, 'utf-8');
if(content && content.indexOf("[autoload]") != -1) {
content = content.substring(content.indexOf("[autoload]")+"[autoload]".length, content.length);
if (content && content.indexOf("[autoload]") != -1) {
content = content.substring(content.indexOf("[autoload]") + "[autoload]".length, content.length);
content = content.substring(0, content.indexOf("["));
const lines = content.split(/\r?\n/);
lines.map((l)=>{
if(l.indexOf("=") != 0) {
lines.map((l) => {
if (l.indexOf("=") != 0) {
const name = l.substring(0, l.indexOf("="));
let gdpath = l.substring(l.indexOf("res://")+"res://".length, l.indexOf(".gd")+".gd".length);
gdpath = path.join( self.workspaceDir, gdpath);
let gdpath = l.substring(l.indexOf("res://") + "res://".length, l.indexOf(".gd") + ".gd".length);
gdpath = path.join(this._rootDir, gdpath);
let showgdpath = vscode.workspace.asRelativePath(gdpath);
let doc = "Auto loaded instance of " + `[${showgdpath}](${vscode.Uri.file(gdpath).toString()})`;
let doc = "Autoloaded instance of " + `[${showgdpath}](${vscode.Uri.file(gdpath).toString()})`;
doc = doc.replace(/"/g, " ");
script.constants[name] = new vscode.Range(0, 0, 0, 0);
@@ -114,106 +140,144 @@ class ToolManager {
}
}
resolve(symbols);
}
else
} else
reject(err);
});
});
}
private loadAllNodesInWorkspace() {
glob( this.workspaceDir +"/**/*.tscn", (err, files)=>{
if(!err) {
glob(this.workspaceDir + "/**/*.tscn", (err, files) => {
if (!err) {
const symbols = {};
for(let i=0; i< files.length; i++)
for (let i = 0; i < files.length; i++)
config.loadScene(files[i]);
}
});
}
private loadWorkspaceSymbols() {
this.loadAllNodesInWorkspace();
this.loadAllSymbols().then(symbols=>{
vscode.window.showInformationMessage("Update GDScript symbols done");
config.setAllSymbols(symbols);
}).catch(e=>{
vscode.window.showWarningMessage("Update GDScript symbols failed");
let handle = this.showProgress("Loading symbols");
if (vscode.workspace.getConfiguration("GodotTools").get("parseTextScene", false)) {
this.loadAllNodesInWorkspace();
}
this.loadAllSymbols().then(symbols => {
handle();
vscode.window.setStatusBarMessage("$(check) Workspace symbols", 5000);
config.setAllSymbols(symbols);
}).catch(e => {
handle();
vscode.window.setStatusBarMessage("$(x) Workspace symbols", 5000);
});
}
private openWorkspaceWithEditor(params="") {
private openWorkspaceWithEditor(params = "") {
let workspaceValid = false
if(this.workspaceDir) {
let cfg = path.join(this.workspaceDir, "engine.cfg");
if( fs.existsSync(cfg) && fs.statSync(cfg).isFile())
if (this.workspaceDir) {
let cfg = path.join(this._rootDir, this._projectFile);
console.log(cfg);
if (fs.existsSync(cfg) && fs.statSync(cfg).isFile())
workspaceValid = true;
}
if(workspaceValid)
this.runEditor(`-path ${this.workspaceDir} ${params}`);
if (workspaceValid) {
let pathFlag = "-path";
if (vscode.workspace.getConfiguration("GodotTools").get("godotVersion", 2.1) >= 3)
pathFlag = "--path";
this.runEditor(`${pathFlag} "${this._rootDir}" ${params}`);
}
else
vscode.window.showErrorMessage("Current workspace is not a godot project");
vscode.window.showErrorMessage("Current workspace is not a Godot project");
}
private runEditor(params="") {
const editorPath = vscode.workspace.getConfiguration("GodotTools").get("editorPath", "")
if(!fs.existsSync(editorPath) || !fs.statSync(editorPath).isFile()) {
private runEditor(params = "") {
let editorPath = vscode.workspace.getConfiguration("GodotTools").get("editorPath", "")
editorPath = editorPath.replace("${workspaceRoot}", this.workspaceDir);
console.log(editorPath);
if (!fs.existsSync(editorPath) || !fs.statSync(editorPath).isFile()) {
vscode.window.showErrorMessage("Invalid editor path to run the project");
}
else {
cmd.run(`${editorPath} ${params}`);
}
}
private runCurrentScenr() {
let scenePath = null
if(vscode.window.activeTextEditor)
scenePath = vscode.workspace.asRelativePath(vscode.window.activeTextEditor.document.uri);
if(scenePath.endsWith(".gd"))
scenePath = config.scriptSceneMap[config.normalizePath(scenePath)];
if(scenePath && (scenePath.endsWith(".tscn") || scenePath.endsWith(".scn"))) {
scenePath = ` res://${scenePath} `;
this.openWorkspaceWithEditor(scenePath);
}
else
vscode.window.showErrorMessage("Current document is not a scene file");
}
private getDefaultDebugConfig() {
const editorPath = vscode.workspace.getConfiguration("GodotTools").get("editorPath", "")
if(this.workspaceDir) {
const config = {
version: '0.2.3',
configurations: [{
type: 'godot',
request: 'launch',
name: path.basename(this.workspaceDir),
godot: editorPath,
projectDir: "${workspaceRoot}",
params: [],
runWithEditor: false
}]
} else {
let existingTerminal = vscode.window.terminals.find(t => t._name === "GodotTools")
if (existingTerminal) {
existingTerminal.dispose()
}
return JSON.stringify(config, null, '\t');
let terminal = vscode.window.createTerminal("GodotTools");
editorPath = this.escapeCmd(editorPath);
let cmmand = `${editorPath} ${params}`;
terminal.sendText(cmmand, true);
terminal.show();
}
else {
vscode.window.showErrorMessage("Cannot create launch without godot project workspace");
return ""
}
private runCurrentScene() {
const absFilePath = vscode.window.activeTextEditor.document.uri.fsPath;
let scenePath = null
if (vscode.window.activeTextEditor) {
scenePath = path.relative(this._rootDir, absFilePath);
scenePath = scenePath.replace(/\\/g, "/");
}
// Run scripts directly which is inhired from SceneTree or MainLoop
if (scenePath.endsWith(".gd")) {
const scriptPath = scenePath;
scenePath = config.scriptSceneMap[config.normalizePath(scenePath)];
if (!scenePath) {
const script = config.loadSymbolsFromFile(absFilePath);
if (script) {
if(script.native == "SceneTree" || script.native == "MainLoop") {
this.runEditor(`-s "${absFilePath}"`);
return;
}
}
}
}
if (scenePath) {
if (scenePath.endsWith(".gd"))
scenePath = ` -s "res://${scenePath}" `;
else
scenePath = ` "res://${scenePath}" `;
this.openWorkspaceWithEditor(scenePath);
} else
vscode.window.showErrorMessage("Current document is not a scene file or MainLoop");
}
loadClasses() {
let done :boolean = false;
if(this.workspaceDir)
let done: boolean = false;
if (this.workspaceDir)
done = config.loadClasses(path.join(this.workspaceDir, ".vscode", "classes.json"));
if(!done)
done = config.loadClasses(path.join(this._context.extensionPath, "doc", "classes.json"));
if(!done)
vscode.window.showErrorMessage("Load GDScript documentations failed");
if (!done)
done = config.loadClasses(path.join(this._context.extensionPath, this._biuitinDocFile));
if (!done)
vscode.window.showErrorMessage("Loading GDScript documentation failed");
}
dispose() {
this._disposable.dispose();
}
private showProgress(message: string) {
let r_resolve;
vscode.window.withProgress({ location: vscode.ProgressLocation.Window}, p => {
return new Promise((resolve, reject) => {
p.report({message});
r_resolve = resolve;
});
});
return r_resolve;
}
private escapeCmd(cmd: string) {
// Double quote command (should work in at least cmd.exe and bash)
let cmdEsc = `"${cmd}"`;
// Fetch Windows shell type
let shell = vscode.workspace.getConfiguration("terminal.integrated.shell").get("windows", "");
// For powershell we prepend an & to prevent the command being treated as a string
if (shell.endsWith("powershell.exe") && process.platform === "win32") {
cmdEsc = `&${cmdEsc}`;
}
return cmdEsc
}
};
export default ToolManager;

View File

@@ -1,3 +1,4 @@
import * as vscode from 'vscode';
import {Disposable, window} from 'vscode';
import GDScriptDiagnosticSeverity from './gdscript/diagnostic';
import GDScriptCompleter from './gdscript/completion';
@@ -40,7 +41,9 @@ class WindowWatcher {
if(window.activeTextEditor != undefined) {
const doc = window.activeTextEditor.document;
const script = config.loadSymbolsFromFile(doc.fileName);
this._diagnosticSeverity.validateScript(doc, script).then(()=>{});
if (vscode.workspace.getConfiguration("GodotTools").get("enableSyntaxChecking", true)) {
this._diagnosticSeverity.validateScript(doc, script).then(()=>{});
}
this._lastText = {path: doc.fileName, version: doc.version};
}
}
@@ -55,7 +58,9 @@ class WindowWatcher {
// Check content changed
if(this._lastText.path != curText.path || this._lastText.version != curText.version) {
const script = config.loadSymbolsFromFile(doc.fileName);
this._diagnosticSeverity.validateScript(doc, script).then(()=>{});
if (vscode.workspace.getConfiguration("GodotTools").get("enableSyntaxChecking", true)) {
this._diagnosticSeverity.validateScript(doc, script).then(()=>{});
}
this._lastText = curText;
}
}