public override void OnInspectorGUI()
        {
            serializedObject.Update();

            YarnProjectImporter yarnProjectImporter = serializedObject.targetObject as YarnProjectImporter;

            EditorGUILayout.Space();

            if (sourceScriptsProperty.arraySize == 0)
            {
                EditorGUILayout.HelpBox("This Yarn Project has no content. Add Yarn Scripts to it.", MessageType.Warning);
            }
            EditorGUILayout.PropertyField(sourceScriptsProperty, true);

            EditorGUILayout.Space();

            bool hasCompileError = compileErrorsProperty.arraySize > 0;

            if (hasCompileError)
            {
                foreach (SerializedProperty compileError in compileErrorsProperty)
                {
                    EditorGUILayout.HelpBox(compileError.stringValue, MessageType.Error);
                }
            }

            serializedDeclarationsList.DrawLayout();

            // The 'Convert Implicit Declarations' feature has been
            // temporarily removed in v2.0.0-beta5.

#if false
            // If any of the serialized declarations are implicit, add a
            // button that lets you generate explicit declarations for them
            var anyImplicitDeclarations = false;
            foreach (SerializedProperty declProp in serializedDeclarationsProperty)
            {
                anyImplicitDeclarations |= declProp.FindPropertyRelative("isImplicit").boolValue;
            }

            if (hasCompileError == false && anyImplicitDeclarations)
            {
                if (GUILayout.Button("Convert Implicit Declarations"))
                {
                    // add explicit variable declarations to the file
                    YarnProjectUtility.ConvertImplicitVariableDeclarationsToExplicit(yarnProjectImporter);

                    // Return here becuase this method call will cause the
                    // YarnProgram contents to change, which confuses the
                    // SerializedObject when we're in the middle of a GUI
                    // draw call. So, stop here, and let Unity re-draw the
                    // Inspector (which it will do on the next editor tick
                    // because the item we're inspecting got re-imported.)
                    return;
                }
            }
#endif

            EditorGUILayout.PropertyField(defaultLanguageProperty, new GUIContent("Base Language"));

            CurrentProjectDefaultLanguageProperty = defaultLanguageProperty;

            EditorGUILayout.PropertyField(languagesToSourceAssetsProperty, new GUIContent("Localisations"));

            CurrentProjectDefaultLanguageProperty = null;

            // Ask the project importer if it can generate a strings table.
            // This involves querying several assets, which means various
            // exceptions might get thrown, which we'll catch and log (if
            // we're in debug mode).
            bool canGenerateStringsTable;

            try
            {
                canGenerateStringsTable = yarnProjectImporter.CanGenerateStringsTable;
            }
            catch (System.Exception e)
            {
#if YARNSPINNER_DEBUG
                Debug.LogWarning($"Encountered in error when checking to see if Yarn Project Importer could generate a strings table: {e}", this);
#else
                // Ignore the 'variable e is unused' warning
                var _ = e;
#endif
                canGenerateStringsTable = false;
            }

            // The following controls only do something useful if all of
            // the lines in the project have tags, which means the project
            // can generate a string table.
            using (new EditorGUI.DisabledScope(canGenerateStringsTable == false))
            {
#if USE_ADDRESSABLES
                // If the addressable assets package is available, show a
                // checkbox for using it.
                var hasAnySourceAssetFolders = yarnProjectImporter.languagesToSourceAssets.Any(l => l.assetsFolder != null);
                if (hasAnySourceAssetFolders == false)
                {
                    // Disable this checkbox if there are no assets
                    // available.
                    using (new EditorGUI.DisabledScope(true)) {
                        EditorGUILayout.Toggle(useAddressableAssetsProperty.displayName, false);
                    }
                }
                else
                {
                    EditorGUILayout.PropertyField(useAddressableAssetsProperty);

                    // Show a warning if we've requested addressables but
                    // haven't set it up.
                    if (useAddressableAssetsProperty.boolValue && AddressableAssetSettingsDefaultObject.SettingsExists == false)
                    {
                        EditorGUILayout.HelpBox("Please set up Addressable Assets in this project.", MessageType.Warning);
                    }
                }

                // Add a button for updating asset addresses, if any asset
                // source folders exist
                if (useAddressableAssetsProperty.boolValue && AddressableAssetSettingsDefaultObject.SettingsExists)
                {
                    using (new EditorGUI.DisabledScope(hasAnySourceAssetFolders == false)) {
                        if (GUILayout.Button($"Update Asset Addresses"))
                        {
                            YarnProjectUtility.UpdateAssetAddresses(yarnProjectImporter);
                        }
                    }
                }
#endif
            }

            EditorGUILayout.Space();

            EditorGUILayout.LabelField("Commands and Functions", EditorStyles.boldLabel);

            var searchAllAssembliesLabel = new GUIContent("Search All Assemblies", "Search all assembly definitions for commands and functions, as well as code that's not in a folder with an assembly definition");
            EditorGUILayout.PropertyField(searchAllAssembliesProperty, searchAllAssembliesLabel);

            if (searchAllAssembliesProperty.boolValue == false)
            {
                EditorGUI.indentLevel += 1;
                EditorGUILayout.PropertyField(assembliesToSearchProperty);
                EditorGUI.indentLevel -= 1;
            }

            using (new EditorGUI.DisabledGroupScope(canGenerateStringsTable == false))
            {
                if (GUILayout.Button("Export Strings as CSV"))
                {
                    var currentPath      = AssetDatabase.GetAssetPath(serializedObject.targetObject);
                    var currentFileName  = Path.GetFileNameWithoutExtension(currentPath);
                    var currentDirectory = Path.GetDirectoryName(currentPath);

                    var destinationPath = EditorUtility.SaveFilePanel("Export Strings CSV", currentDirectory, $"{currentFileName}.csv", "csv");

                    if (string.IsNullOrEmpty(destinationPath) == false)
                    {
                        // Generate the file on disk
                        YarnProjectUtility.WriteStringsFile(destinationPath, yarnProjectImporter);

                        // destinationPath may have been inside our Assets
                        // directory, so refresh the asset database
                        AssetDatabase.Refresh();
                    }
                }
                if (yarnProjectImporter.languagesToSourceAssets.Count > 0)
                {
                    if (GUILayout.Button("Update Existing Strings Files"))
                    {
                        YarnProjectUtility.UpdateLocalizationCSVs(yarnProjectImporter);
                    }
                }
            }

            // Does this project's source scripts list contain any actual
            // assets? (It can have a count of >0 and still have no assets
            // when, for example, you've just clicked the + button but
            // haven't dragged an asset in yet.)
            var hasAnyTextAssets = yarnProjectImporter.sourceScripts.Where(s => s != null).Count() > 0;

            // Disable this button if 1. all lines already have tags or 2.
            // no actual source files exist
            using (new EditorGUI.DisabledScope(canGenerateStringsTable == true || hasAnyTextAssets == false))
            {
                if (GUILayout.Button("Add Line Tags to Scripts"))
                {
                    YarnProjectUtility.AddLineTagsToFilesInYarnProject(yarnProjectImporter);
                }
            }

            var hadChanges = serializedObject.ApplyModifiedProperties();

#if UNITY_2018
            // Unity 2018's ApplyRevertGUI is buggy, and doesn't
            // automatically detect changes to the importer's
            // serializedObject. This means that we'd need to track the
            // state of the importer, and don't have a way to present a
            // Revert button.
            //
            // Rather than offer a broken experience, on Unity 2018 we
            // immediately reimport the changes. This is slow (we're
            // serializing and writing the asset to disk on every property
            // change!) but ensures that the writes are done.
            if (hadChanges)
            {
                // Manually perform the same tasks as the 'Apply' button
                // would
                ApplyAndImport();
            }
#endif

#if UNITY_2019_1_OR_NEWER
            // On Unity 2019 and newer, we can use an ApplyRevertGUI that
            // works identically to the built-in importer inspectors.
            ApplyRevertGUI();
#endif
        }
        public override void OnInspectorGUI()
        {
            serializedObject.Update();
            EditorGUILayout.Space();

            // If there's a parse error in any of the selected objects,
            // show an error. If the selected objects have the same
            // destination program, and there's a compile error in it, show
            // that.
            if (parseErrorMessagesProperty.arraySize > 0)
            {
                if (serializedObject.isEditingMultipleObjects)
                {
                    EditorGUILayout.HelpBox("Some of the selected scripts have errors.", MessageType.Error);
                }
                else
                {
                    foreach (SerializedProperty errorProperty in parseErrorMessagesProperty)
                    {
                        EditorGUILayout.HelpBox(errorProperty.stringValue, MessageType.Error);
                    }
                }
            }
            else if (DestinationProjectError.Count() > 0)
            {
                var displayMessage = string.Join("\n", DestinationProjectError);
                EditorGUILayout.HelpBox(displayMessage, MessageType.Error);
            }

            if (destinationYarnProject == null)
            {
                EditorGUILayout.HelpBox("This script is not currently part of a Yarn Project, so it can't be compiled or loaded into a Dialogue Runner. Either click Create New Yarn Project, or add a Yarn project to the field below.", MessageType.Info);
                if (GUILayout.Button("Create New Yarn Project..."))
                {
                    YarnProjectUtility.CreateYarnProject(target as YarnImporter);

                    UpdateDestinationProject();
                }
            }

            using (var change = new EditorGUI.ChangeCheckScope())
            {
                var project = EditorGUILayout.ObjectField("Project", destinationYarnProject, typeof(YarnProject), false);

                if (change.changed)
                {
                    string programPath = null;
                    if (project != null)
                    {
                        programPath = AssetDatabase.GetAssetPath(project);
                    }
                    YarnProjectUtility.AssignScriptToProject((target as YarnImporter).assetPath, programPath);

                    UpdateDestinationProject();
                }
            }

            EditorGUILayout.Space();

            var hadChanges = serializedObject.ApplyModifiedProperties();

#if UNITY_2018
            // Unity 2018's ApplyRevertGUI is buggy, and doesn't automatically
            // detect changes to the importer's serializedObject. This means
            // that we'd need to track the state of the importer, and don't
            // have a way to present a Revert button.
            //
            // Rather than offer a broken experience, on Unity 2018 we
            // immediately reimport the changes. This is slow (we're
            // serializing and writing the asset to disk on every property
            // change!) but ensures that the writes are done.
            if (hadChanges)
            {
                // Manually perform the same tasks as the 'Apply' button would
                ApplyAndImport();
            }
#endif

#if UNITY_2019_1_OR_NEWER
            // On Unity 2019 and newer, we can use an ApplyRevertGUI that
            // works identically to the built-in importer inspectors.
            ApplyRevertGUI();
#endif
        }