-
Notifications
You must be signed in to change notification settings - Fork 33
Overview
This article attempts to explain the various usage of this library by cwtools-vscode
, the main consuming application. cwtools-vscode
uses the cwtools
library as a Language Server, and to implement https://microsoft.github.io/language-server-protocol/ . In order to do this a lot of the features of cwtools
are based around providing the features required by that spec (validation, completion, hover, etc). However, the library can be used for more general analysis, in a similar way to most compiler services.
cwtools-vscode
interacts with cwtools
through the CWTools.Games.IGame
interface. This interface provides methods like:
abstract ValidationErrors : unit -> CWError list
abstract LocalisationErrors : bool * bool -> CWError list
abstract Complete : pos -> string -> string -> CompletionResponse list
abstract GoToType : pos -> string -> string -> range option
To get hold of the interface, you use one of the game specific classes that implement it, such as CWTools.Games.Stellaris.STLGame
and CWTools.Games.Custom.CustomGame
. These factories all take the same setup settings CWTools.Games.GameSetupSettings
and configure an internal instance of CWTools.Games.GameObject
with the game specific customization. The GameObject
does all the heavy lifting, then returns various services that are used to fulfill the IGame
interface either directly or through helpers in CWTools.Games.LanguageFeatures
.
A lot of the various xxxGame
factories is probably a bit redundant, but they're all subtly different.
From GameObject
downwards everything should be agnostic of specific games. The primary purpose of this class is to create and recreate services as necessary, as well as perform the initial all file load. initialLoad()
takes all files in the working directory, plus any embedded files (usually from vanilla), then passes them to the resource manager to be parsed. It makes sure that they're correctly filtered to prevent validation of vanilla files. This is the Loading project...
stage of cwtools-vscode
and about half of the loadtime.
The services it contains:
FileManager
is responsible for initial discovery and classification of files. Roughly, it:
- Takes a series of workspace roots and normalises them
- Classifies them into one of a. Vanilla, b. Multiple mod directory (containing .mod files), c. Direct mod folder (most common)
- Applies
scriptFolders
to find all files in the correct subfolders for this game - Finds all files in those folders, classifies them by extension into one of a.
EntityResourceInput
(script file), b.FileResourceInput
(name only), c.FileWithContentResourceInput
(name + contents, e.g. csv and yml) - Normalises the path for all files into one relative to mod/game root.
ResourceManager
is responsible for parsing script files as well as handling load order. Roughly, it:
- Takes a series of files as
ResourceInput
s - Parses and "Process"es them if they're
EntityResourceInput
s - Unpacks them if they're
CachedResourceInput
(embedded/cached files) - Populates
fileMap
with all the loaded files - Populates
entityMap
with all the parsed script files, and sets up the lazyComputedData
- Marks all entities as
Overwritten
,Overwrote
orNo
depending on load order, which is used to setvalidate
to false for overwritten files, and filter overwritten files from functions such asallEntities
.
After initial load UpdateFile
is generally used on demand as the user edits files to update and reparse.
It exposes the IResourceAPI
which is used for other services to get hold of entities/files.
Not too tricky, this manager uses IResourceAPI
to get all localisation files and runs the given localisation parser to get all localisation for the required languages. It should correctly update its lists of localisation keys when individual loc files are updated, and also "processes" loc into forms more useful for validation and detailed analysis (as opposed to just getting the raw text).
ValidationManager
, unsurprisingly, manages all validation. It manages, caches, and updates the lists of errors as entities are changed.
Errors (or more accurately, Diagnostics) in cwtools
are represented by the CWTools.Games.CWError
class which is almost always held in a CWTools.Validation.ValidationResult
wrapper:
type ValidationResult =
| OK
| Invalid of Guid * CWError list
So, all validators return either OK
or a list of errors (and a unique Guid used for equality).
Errors are usually created using CWTools.Validation.ValidationCore.inv
which takes an ErrorCodes
function and a location (a Node, Leaf, etc from the script data structure). They are easily merged using the operator <&&>
which will simply concatenate the two lists. However, where possible, it's better to use the <&&&>
operator to append a single error at a time, which improves performance. When applying a validation function to a collection, you can use <&!&>
.
There are four main types of validation in cwtools
:
-
FileValidator
,StructureValidator
andLookupValidator
are all self-contained validators that are given all changed entities as well as some context and can do whatever validation they want on them. This can result in a fair amount of redundant processing if they're too lazy. These are mainly used for fairly specific, complication, validators. -
LocalisationValidator
as above, but returning specifically localisation errors -
Rules
based validation. The bulk of validation is provided by theRuleValidationService
, which derives rules from config files. - Various custom validation. There are also a number of specific more custom validators scattered around, such as "global" localisation validators.
This is the brain of cwtools
. So far everything is just simple parsing and tailored validation function. RulesManager
takes a number of crowd-sourced configuration files such as https://github.com/cwtools/cwtools-hoi4-config/blob/master/Config/events.cwt and uses them as a schema to understand, analyse, and validate the script files.
This is all in CWTools.Rules
namespace, with CWTools.Rules.RulesParser
loading the rules and defining types, enums, etc. The rules format is very similar to pdxscript itself in order to simplify usage for modders. The rules format is explained here: https://github.com/tboby/cwtools/wiki/.cwt-config-file-guidance
RulesManager
does a number of passes over the script files, finding all defined instances of types (such as event
, focus
, decision
), enums (structurally defined) and "values" (contextually defined). It uses/creates RulesValidationService
, InfoService
and CompletionService
to do this, and those are also the end-products which are used to power validation, completion and other IDE features as defined by the LSP.