DynamoCore Modularization

## The Big Idea
The architecture of Dynamo was previously a tangled mess of dependencies. This PR refactors `DynamoCore` into a modular, layered architecture, where the boundaries of the various components of the application are clearly defined.

Or, in a nutshell, we are no longer accessing `DynamoModel` from remote parts of the application. This means that:
  1. In order to test parts of the application, we don't need to create an entire instance of the application.
  2. We can safely assume that different parts of the application aren't affecting other remote parts of the application unless there is a direct dependency between the two components.

## What's been changed?
* XML parsing
  - The logic for loading whole Workspaces, Nodes, Connectors, and Notes are now all located in a single place: `NodeGraph`
  - Loading .dyn and .dyf files now leverage this new API, previously we had duplicate (and unsynchronized) code for loading both.
* Node instantiation
  - `NodeFactory` has been overhauled; previously we were doing weird string parsing to determine how to create new nodes. Nothing was consistent, the API was cumbersome to use, and there was a ton of duplicate code.
  - All node instantiation is now processed through `NodeFactory`. Different types of nodes register `INodeLoader`  instances with the `NodeFactory`. For special kinds of nodes (Custom Nodes and Zero Touch Nodes), there are special `INodeLoaders` that handle the necessary extra logic for creating instances. This means that `NodeFactory` no longer needs to know about the `Function` and `DSFunction` classes.
  - All external implementations of Nodes which extend `NodeModel` are now instantiated using a pre-compiled `Expression`, as opposed to relying on an `Activator`. This means that instantiating large numbers of new nodes should be significantly faster (copy/paste, undo/redo, loading files).
* Custom Node Infrastructure
  - `CustomNodeManager` has become the primary interface for dealing with Custom Nodes.
    + It keeps track of `CustomNodeWorkspaces`, instead of just `CustomNodeDefinitions`.
  - `CustomNodeWorkspace` now emits events when the `CustomNodeDefinition` and `CustomNodeInfo` has changed.
    + This makes sense because the state of a `CustomNodeWorkspace` defines what the `CustomNodeDefinition` and `CustomNodeInfo` are.
    + These events are watched by `CustomNodeManager`, which then forwards them to `DynamoModel`, which then propagates them to the `EngineController`, Search Library, and Custom Node Instances.
  - Custom Node (`Function`) Instances are now created from `CustomNodeManager`, in order to ensure that they are kept in sync with their `CustomNodeDefinition`.
* Evaluation
  - Since `HomeWorkspaceModels` are the only kind of Workspaces that can be evaluated, `EngineController` has been moved to `HomeWorkspaceModel`
    + ~~Consequentially, all logic originally on `DynamoModel` associated with `EngineController` has now been moved to `HomeWorkspaceModel`~~ Unfortunately, `CodeBlockNodeModels` have a dependency on `EngineController` for Auto-complete. Further work can be done to decouple Auto-complete from `EngineController`, which would allow for total removal of `EngineController` from `DynamoModel`.
    + This sets the stage for eventually having multiple `HomeWorkspaceModels` active at once.
* Search
  - Search has been re-implemented to be as general as possible.
  - Search has been refactored to support the new method of instantiating Nodes.
  - The actual search method has been separated from the categorization infrastructure.
  - Categorization of Search Elements has been greatly generalized and simplified.

### Reviewers:
* [ ] @lukechurch 
* [ ] @ikeough 
* [ ] @Benglin 
* [ ] @pboyer 
The diff here got really messed up, so I will explain most of the changes.

`CustomNodeManager` keeps track of Custom Node Workspaces. These workspaces exist in Dynamo as one of two forms: Uninitialized, or Initialized.

Uninitialized Custom Node Workspaces have had their .dyf file parsed by Dynamo to get basic information about the Custom Node. This is stored as a `CustomNodeInfo` in the `NodeInfos` dictionary and is emitted through the `InfoUpdated` event so that other parts of the application can learn about the custom node, namely Search.

Uninitialized Custom Node workspaces are detected when a file is loaded in Dynamo (the file's directory is scanned for custom nodes, which are then added the the `CustomNodeManager` uninitialized). I believe they are also added by the package manager when a new package is downloaded, @pboyer can comment.

Initialized Custom Node Workspaces have had their entire workspace loaded from the .dyf and are stored in the `CustomNodeManager` as `CustomNodeWorkspaceModel` instances, inside of the `loadedWorkspaceModels` dictionary. Upon initialization, the `CustomNodeDefinition` for the workspace is generated, stored in the `loadedCustomNodes` dictionary, and emitted through the `DefinitionUpdated` event so that other parts of the application can be updated, namely `EngineController`.

An Uninitialized Custom Node becomes Initialized when a user attempts to view a Custom Node Workspace (right-click > Edit in search) or if the user attempts to place a Custom Node instance.

Creating a Custom Node instance is handled in `CustomNodeManager` as well. First, the Workspace is initialized if it hasn't been, and then the `CustomNodeInfo` and `CustomNodeDefinition` are accessed so that the instance can be created (the info is needed to set things like the name and description, the definition is needed for Input and Output ports and the internal function name to be used in the AST). The `CustomNodeWorkspaceModel` is also accessed so that when new `CustomNodeInfo` and `CustomNodeDefinition` instances are produced by the workspace, the instance can be kept in sync.

In the event the Custom Node Workspace does not exist for an instance being created (such as we're loading a file but the .dyf for the Custom Node instance in the file cannot be found), we create the Custom Node instance with the information that was serialized, and we setup event listeners so that if the workspace is loaded at a later time, the instance can be brought in sync.

Finally, a Custom Node can be completely removed from the `CustomNodeManager`, which makes it neither Initialized or Uninitialized. This is done when packages are uninstalled from Dynamo.