-
Notifications
You must be signed in to change notification settings - Fork 309
RubberduckParserState
The RubberduckParserState
is the central piece of Rubberduck. It stores all the information we extract about the modules currently present in the VBE, including parse trees and declarations of modules, procedures and variables. Moreover, it exposes the current state Rubberduck is in as well as a method to request a run of the parsing process.
The RubberduckParserState
is accessed by various features, from inspections to refactorings and toolwindows. It is constructor-injected in singleton scope by Ninject. So any class that needs to work with information about the parsed modules simply needs to have a RubberduckParserState
constructor parameter.
The possible states of individual modules and Rubberduck as a whole are the different values of the ParserState
enum:
public enum ParserState
{
/// <summary>
/// Parse was requested but hasn't started yet.
/// </summary>
Pending,
/// <summary>
/// Project references are being loaded into parser state.
/// </summary>
LoadingReference,
/// <summary>
/// Code from modified modules is being parsed.
/// </summary>
Parsing,
/// <summary>
/// Parse tree is waiting to be walked for identifier resolution.
/// </summary>
Parsed,
/// <summary>
/// Resolving declarations.
/// </summary>
ResolvingDeclarations,
/// <summary>
/// Resolved declarations.
/// </summary>
ResolvedDeclarations,
/// <summary>
/// Resolving identifier references.
/// </summary>
ResolvingReferences,
/// <summary>
/// Parser state is in sync with the actual code in the VBE.
/// </summary>
Ready,
/// <summary>
/// Parsing could not be completed for one or more modules.
/// </summary>
Error,
/// <summary>
/// Parsing completed, but identifier references could not be resolved for one or more modules.
/// </summary>
ResolverError,
/// <summary>
/// This component doesn't need a state. Use for built-in declarations.
/// </summary>
None,
}
The global state of Rubberduck can both be set directly or be derived from the individual module states. These options are exposed via the methods SetStatusAndFireStateChanged
and EvaluateParserState
, respectively. At which points which state gets set via which method can be found in the page about the parsing process.
When evaluating the global state of Rubberduck, the result generally is the lowest module state found. The exception are the two error states; these are always prioritized over all other states.
The RubberduckParserState
raises events to indicate a change in the state of Rubberduck; code that requires a specific state to work should register the StateChanged
and/or ModuleStateChanged
event, and determine what to do depending on the current state.
Features that require resolved identifier usages (inspections, refactorings, etc.) should wait for a global Ready
state; features that merely enable navigation and/or comments (e.g. code explorer), don't need to wait for the resolver to complete and can wait for a global ResolvedDeclarations
state instead.
When the parser is in a Parsed
state, all modules have successfully parsed, and all parse trees have been successfully walked and all declarations have been identified.
Use the AllDeclarations
property to return all Declaration
objects.
/// <summary>
/// Gets a copy of the collected declarations, including the built-in ones.
/// </summary>
public IEnumerable<Declaration> AllDeclarations { get { return _declarations.Keys.ToList(); } }
Rubberduck creates a vast amount of Declaration
objects to represent built-in modules and members of the VBA standard library, as well as some from host application object models, such as the Excel object model. A lot of features don't need to account for these built-in declarations.
Use the AllUserDeclarations
property to retun all Declaration
objects that only involve user code.
/// <summary>
/// Gets a copy of the collected declarations, excluding the built-in ones.
/// </summary>
public IEnumerable<Declaration> AllUserDeclarations { get { return _declarations.Keys.Where(e => !e.IsBuiltIn).ToList(); } }
Shortly put: any identifier that can be referred to in VBA code. In other words, it's a project, it's a module, a class, a property getter, a function, a variable, a constant, an event, its parameters, line labels, ..literally everything.
Declarations have a
References
collection that, once identifier resolution is completed, contains allIdentifierReference
objects that point to it.
By inspecting the declarations and their references, we can programmatically determine whether a variable is used or not, what scope they're declared in and how visible they are, and where and how they're used.
As soon as we programmatically modify a code module, all declarations and usages point to the wrong locations and need to be refreshed. While we only need to re-parse the modified module, we cannot be sure that identifier references were added or removed, that belong to declarations that aren't in the module we modified - for example if we have a DoSomething
method in Class1
and we modified Module1
to add a call to Class1.DoSomething
, the declaration for DoSomething
needs its references updated. Hence, even when we parse a single module, the resolver needs to re-process every single parse tree.
Code that has a RubberduckParserState
dependency can trigger a global re-parse by calling the OnParseRequested
method on the parser state:
_state.OnParseRequested();
To trigger the re-parsing of a specific component, pass the modified VBComponent
as a parameter:
_state.OnParseRequested(modifiedModule);
The parser processes everything asynchronously, so the IDE/UI should not be affected by the intense processing going on in the background.
Requesting a re-parse will change the parser state, and all features that have code to execute when parser state changes, will run that code. For this reason, the code that runs on the UI thread upon a change in parser state should be kept to a minimum, to avoid blocking the UI. Consider implementing extensive work on a background thread.
rubberduckvba.com
© 2014-2025 Rubberduck project contributors
- Contributing
- Build process
- Version bump
- Architecture Overview
- IoC Container
- Parser State
- The Parsing Process
- How to view parse tree
- UI Design Guidelines
- Strategies for managing COM object lifetime and release
- COM Registration
- Internal Codebase Analysis
- Projects & Workflow
- Adding other Host Applications
- Inspections XML-Doc
-
VBE Events