Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editor: test missing event handlers as a post-compilation step #2556

Conversation

ivan-mogilko
Copy link
Contributor

@ivan-mogilko ivan-mogilko commented Oct 25, 2024

Resolves #1363

This PR's additions may be separated in 3 parts:

  1. Improved function searching when pressing on "..." button on events pane. I found that the existing search was not fully reliable, as it used direct string search of a sequence function functionName(, and thus could break by simply an extra space. I changed that to using regex pattern, which finds both function and void, and handles whitespace. This keeps working even if you split function header on multiple lines, like:
void
cEgo_Lookat
(Character* c, CursorMode mode)
{

In addition, I tried to make it skip found matches if they are inside comments.
I also considered testing preprocessor conditions too, but on second thought decided to leave that be, as that would become tough to do without a full script's preprocessing, which seemed like an overkill for "..." button in events (I'd have to cache results and run preprocessing in background thread, or something...).

  1. If a function's name is inserted into event property, but that function is not actually exist in script, then pressing "..." will create that function anyway.

  2. Test missing event functions as a script compilation post-step. For this editor keeps a dictionary of preprocessed script texts at compilation stage, so that components could searching for regex patterns there and find matching functions.

This makes search much more reliable, as previously existing search for exact string could fail on any tiny difference or extra whitespace.
Here's a problem: InsideStringOrComment relies on scintilla styling. But styling is not necessarily present at the time if we just loaded a document when zoom-in to a function.
So we force the styling in case it was not done here yet.
Style only the portion of a document from the last styled position to the position of the found match (this speeds things up a little).
@ivan-mogilko ivan-mogilko added what: editor related to the game editor context: game building related to compiling the game from input assets labels Oct 25, 2024
@ivan-mogilko
Copy link
Contributor Author

Hmm, maybe I should switch to searching preprocessed scripts variant. There's another reason to do this: if we like to have this scan as a distinct operation, which may be run on its own without compiling scripts.

@ericoporto
Copy link
Member

ericoporto commented Oct 25, 2024

It could preprocess, and then search on it using a regex pattern, since ags script doesn't support nested functions it should be possible to find the functions, possibly with some checks to allow both void and function (maybe int too?) event handlers.

The regex pattern can check for either void`` or function, any number of whitespace characters, and then functionName, any number of whitespace characters and the ( character.

Erh, maybe something like "^\s*(?:void|function)\s+" + functionhandler + "\s*\(" ?

@ivan-mogilko
Copy link
Contributor Author

ivan-mogilko commented Oct 25, 2024

The regex pattern can check for either void`` or function, any number of whitespace characters, and then functionName, any number of whitespace characters and the ( character.

@ericoporto this is literally what I did in the first commit of this pr.

I made this work for "zoom into file" function, but same method may be used for a global scan.

@ivan-mogilko
Copy link
Contributor Author

Made it so that, if function's name was in the event property, but function itself did not exist, then pressing "..." button would create that function.

@ivan-mogilko
Copy link
Contributor Author

ivan-mogilko commented Oct 27, 2024

No, this is not right, I was not thinking straight apparently.

The function match should not be done over a script text, because that means regex search done over full script text for each event function name. Even if script is preprocessed, that will be very slow.

The function match must be done over prepared list of functions (or specifically Set, or ordered list for more optimization).
What this list is created by is a separate question.
If we run this check after compilation, then we might as well use the result of a compilation, because the parsing job was already done by compiler.
If we run this check unrelated to compilation, then we will have to utilize script parser somehow. Either ask compiler to do a "dry run" over a script, when it only gathers symbols but does not create a bytecode. Or even just write a primitive sort of parser that searches for all entries matching standard event function regex patterns.

But in the end this must be a set of function names, which in turn we use to match event properties.

@ericoporto
Copy link
Member

ericoporto commented Oct 27, 2024

About the function list, it's there already in the script autocomplete cache and used to fill the combobox at top of the script editor. Perhaps the script autocomplete cache should be kept somewhere when the script is unloaded so it can still be looked up?

There is a discussion about an idea of a dictionary to store the autocomplete stuff in #1454 (it's in a different context, but perhaps there is something that would work for both cases). Alternatively, I guess looking only at the opened scripts would already be somewhat fine? As probably if you just wrote something you didn't close the script before hitting play to test what you just did.

About actually testing this, what I noticed is that in the case of a room, if I have an invalid room event handler and click the play button for testing, it appears that it isn't checked - I guess because the room isn't rebuilt? Not sure. But if I click rebuild all scripts then the handler errors actually appear in the output panel. Edit: actually, it also happens with button onclick event handlers too, in any script. Edit2: I think for any handlers really, just a rebuild seems to trigger showing the warning messages but not clicking Play button.

@ivan-mogilko
Copy link
Contributor Author

ivan-mogilko commented Oct 28, 2024

About the function list, it's there already in the script autocomplete cache and used to fill the combobox at top of the script editor. Perhaps the script autocomplete cache should be kept somewhere when the script is unloaded so it can still be looked up?

That's a good idea on its own, and I also wonder if it could have been a design mistake to attach AutoCompleteData directly to a Script object. It could be a dictionary of autocomplete caches, where script name is the key, stored separately and provided through a different interface. Also it could be called not AutoCompleteData, but in a more generic way, like ParsedScriptData, for example, since it's not only useful for autocomplete.

Furthermore, it appears to me, that instead of having own script parser duplicated exclusively for autocomplete, this data could be generated by a script compiler's parser, since it's probably most advanced parser available here already. If there were a way to run compiler in "parsing only" mode, which returns a list of symbols. I don't know if it would be feasible to do with the old compiler, but new compiler seems to have components separated well.

IMO it makes sense to consider reworking autocomplete in this vein, the interface kept more or less same (also for backwards compatibility), but implementation use script compiler under the hood.

On the practical approach, even if the AutoCompleteData is not ready, it may be forced to generate.
The question here is rather this: if this missing handlers test is run right after the script compilation, then re-parsing the scripts just for that seems like a double work. That's why I think that it may be more optimal to get available parsing results from compilation at this point.

If this test is run separately, as a action from a menu, then of course it might force auto complete to rebuild and get its results.

EDIT: Hmm, actually, looking around the Editor's code, it seems like autocomplete is regenerated on game launch already. So maybe it is available even if script was never opened for edit during this session. I'm going to check that out...

About actually testing this, what I noticed is that in the case of a room, if I have an invalid room event handler and click the play button for testing, it appears that it isn't checked - I guess because the room isn't rebuilt?

More importantly than being rebuilt -- the rooms must be loaded into memory in order to know about the event handlers.
I doubt that's a good idea in case of a test run, as users expect as fast launch as possible, and AGS already cuts many corners in order to reduce number of things processed there.

Edit2: I think for any handlers really, just a rebuild seems to trigger showing the warning messages but not clicking Play button.

Yes, because I added its run to post-compilation step only so far.
#1363 said "check at compile time", so that's what I did...

UPD: I am going to edit engine warnings for missing handlers too, that might help noticing this at runtime as well.

@ericoporto
Copy link
Member

About the parser, I am not completely sure, but I think that the one for autocomplete data can pickup if you haven't finished writing some code instead of giving error and also understands that you are local to a function - so it shows local stuff, from picking up where the cursor is.

The one in the compiler errors when the code is unfinished, and I don't know if it has any sort of interface to be able to pickup local symbols too - which are necessary for autocomplete.

@ivan-mogilko
Copy link
Contributor Author

About the parser, I am not completely sure, but I think that the one for autocomplete data can pickup if you haven't finished writing some code instead of giving error and also understands that you are local to a function - so it shows local stuff, from picking up where the cursor is.

The one in the compiler errors when the code is unfinished, and I don't know if it has any sort of interface to be able to pickup local symbols too - which are necessary for autocomplete.

I suppose that might be a matter of making parser have its own interface exposed, and work in certain way, like, ignore unfinished parts, ignore all function bodies except one and gather local symbols from a function found at the given line, and so forth.

Given that logic of parsing the code and gathering symbols seems to be the same, I think it is worth investigating this approach, and see how feasible it is to share same tool for multiple purposes.

That's not something I want to try right away, but something to consider as an option in the future.

@ivan-mogilko ivan-mogilko force-pushed the 362--testmissinginteractions branch 2 times, most recently from fb12375 to 7150670 Compare October 28, 2024 15:47
* Added TestGameScriptIntegrity event to the Editor.
* As a post-compilation step, Editor updates AutoComplete cache for all script modules (as necessary) and fires TestGameScriptIntegrity event.
* Certain Components subscribe to TestGameScriptIntegrity, and run event handler checks for their respective types of game objects, using prepared AutoComplete data.
Do this as a post-room-compilation step.
@ivan-mogilko ivan-mogilko force-pushed the 362--testmissinginteractions branch 3 times, most recently from 73a049e to 3f46a5b Compare October 29, 2024 10:52
Add Enabled field to the InteractionEvents's members. Record call result at runtime and disable event handlers that were not found in script.
Issue a warning for the first time when this happened.

NOTE: Room events will likely be reported again when returning to the same room, as the state of event handler is not saved when room gets unloaded.
@ivan-mogilko
Copy link
Contributor Author

I changed it to use Autocomplete, and also put this process into a function called RunGameScriptTests, so it may be run from another place if anyone would like to do so later.

@ivan-mogilko ivan-mogilko merged commit 874b459 into adventuregamestudio:master Nov 7, 2024
21 checks passed
@ivan-mogilko ivan-mogilko deleted the 362--testmissinginteractions branch November 7, 2024 18:52
@ivan-mogilko
Copy link
Contributor Author

Okay, I merged this, as this works overall.
Any missing issues may be addressed separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
context: game building related to compiling the game from input assets what: editor related to the game editor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Editor: Check event name mismatch at compile time instead of runtime
2 participants