using System; using System.Collections.Generic; using System.IO; using System.Linq; using JetBrains.Annotations; using NugetForUnity.Helper; using NugetForUnity.Models; using UnityEditor; using UnityEngine; namespace NugetForUnity.Ui { /// /// Represents a custom editor inside the Unity editor that allows easy editing of a .nuspec file. /// public class NuspecEditor : EditorWindow { /// /// The API key used to verify an acceptable package being pushed to the server. /// private string apiKey = string.Empty; /// /// True if the dependencies list is expanded in the GUI. False if it is collapsed. /// private bool dependenciesExpanded = true; /// /// The full file-path to the .nuspec file that is being edited. /// [CanBeNull] private string filepath; /// /// The NuspecFile that was loaded from the .nuspec file. /// [CanBeNull] private NuspecFile nuspec; /// /// Creates a nuspec file at the given path and opens its editor. /// /// Absolute path to the folder within project where nuspec should be created. public static void CreateNuspecFile(string filePath) { if (!string.IsNullOrEmpty(Path.GetExtension(filePath))) { // if it was a file that was selected, use containing directory filePath = Path.GetDirectoryName(filePath); } Debug.Assert(filePath != null, "filepath != null"); var packageName = Path.GetFileName(filePath); filePath = Path.Combine(filePath, $"{packageName}.nuspec"); Debug.LogFormat("Creating: {0}", filePath); var file = NuspecFile.CreateDefault(packageName); file.Save(filePath); AssetDatabase.Refresh(); // select the newly created .nuspec file var relativeNuspecFilePath = filePath.Substring(UnityPathHelper.AbsoluteProjectPath.Length + 1); Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(relativeNuspecFilePath); // automatically display the editor with the newly created .nuspec file DisplayNuspecEditor(); } /// /// Creates a new MyPackage.nuspec file. /// [MenuItem("Assets/NuGet/Create Nuspec File", false, 2000)] protected static void CreateNuspecFile() { var filePath = Application.dataPath; if (Selection.activeObject != null && Selection.activeObject != Selection.activeGameObject) { var selectedFile = AssetDatabase.GetAssetPath(Selection.activeObject); filePath = Path.Combine(UnityPathHelper.AbsoluteProjectPath, selectedFile); } CreateNuspecFile(filePath); } /// /// Opens the .nuspec file editor. /// [MenuItem("Assets/NuGet/Open Nuspec Editor", false, 2000)] protected static void DisplayNuspecEditor() { var nuspecEditor = GetWindow(); nuspecEditor.Reload(); } /// /// Validates the opening of the .nuspec file editor. /// /// True if the editor can be opened, false otherwise. [MenuItem("Assets/NuGet/Open Nuspec Editor", true, 2000)] protected static bool DisplayNuspecEditorValidation() { var isNuspec = false; var defaultAsset = Selection.activeObject as DefaultAsset; if (defaultAsset != null) { var filePath = AssetDatabase.GetAssetPath(defaultAsset); filePath = Path.Combine(UnityPathHelper.AbsoluteProjectPath, filePath); isNuspec = string.Equals(Path.GetExtension(filePath), ".nuspec", StringComparison.OrdinalIgnoreCase); } return isNuspec; } /// /// Use the Unity GUI to draw the controls. /// protected void OnGUI() { if (nuspec == null) { Reload(); } if (nuspec == null) { titleContent = new GUIContent("[NO NUSPEC]"); EditorGUILayout.LabelField("There is no .nuspec file selected."); } else { if (filepath == null) { titleContent = new GUIContent("[NO PATH]"); EditorGUILayout.LabelField("There is no directory selected."); return; } EditorGUIUtility.labelWidth = 100; nuspec.Id = EditorGUILayout.TextField(new GUIContent("ID", "The id of the package."), nuspec.Id); nuspec.Title = EditorGUILayout.TextField(new GUIContent("Title", "The name of the package."), nuspec.Title); nuspec.Version = EditorGUILayout.TextField(new GUIContent("Version", "The semantic version of the package."), nuspec.Version); nuspec.Authors = EditorGUILayout.TextField(new GUIContent("Authors", "The authors of the package."), nuspec.Authors); nuspec.Owners = EditorGUILayout.TextField(new GUIContent("Owners", "The owners of the package."), nuspec.Owners); nuspec.LicenseUrl = EditorGUILayout.TextField( new GUIContent("License URL", "The URL for the license of the package."), nuspec.LicenseUrl); nuspec.ProjectUrl = EditorGUILayout.TextField(new GUIContent("Project URL", "The URL of the package project."), nuspec.ProjectUrl); nuspec.IconUrl = EditorGUILayout.TextField(new GUIContent("Icon URL", "The URL for the icon of the package."), nuspec.IconUrl); nuspec.RequireLicenseAcceptance = EditorGUILayout.Toggle( new GUIContent("Require License Acceptance", "Does the package license need to be accepted before use?"), nuspec.RequireLicenseAcceptance); nuspec.Description = EditorGUILayout.TextField(new GUIContent("Description", "The description of the package."), nuspec.Description); nuspec.Summary = EditorGUILayout.TextField(new GUIContent("Summary", "The brief description of the package."), nuspec.Summary); nuspec.ReleaseNotes = EditorGUILayout.TextField( new GUIContent("Release Notes", "The release notes for this specific version of the package."), nuspec.ReleaseNotes); nuspec.Copyright = EditorGUILayout.TextField(new GUIContent("Copyright", "The copyright details for the package."), nuspec.Copyright); nuspec.Tags = EditorGUILayout.TextField( new GUIContent( "Tags", "The space-delimited list of tags and keywords that describe the package and aid discoverability of packages through search and filtering."), nuspec.Tags); dependenciesExpanded = EditorGUILayout.Foldout( dependenciesExpanded, new GUIContent("Dependencies", "The list of NuGet packages that this packages depends on.")); if (dependenciesExpanded) { EditorGUILayout.BeginHorizontal(); { GUILayout.Space(50); // automatically fill in the dependencies based upon the "root" packages currently installed in the project if (GUILayout.Button( new GUIContent( "Automatically Fill Dependencies", "Populates the list of dependencies with the \"root\" NuGet packages currently installed in the project."))) { var installedPackages = InstalledPackagesManager.InstalledPackages.ToList(); // default all packages to being roots var roots = new List(installedPackages); // remove a package as a root if another package is dependent on it foreach (var package in installedPackages) { var frameworkDependencies = package.CurrentFrameworkDependencies; foreach (var dependency in frameworkDependencies) { roots.RemoveAll(p => p.Id == dependency.Id); } } // remove all existing dependencies from the .nuspec nuspec.Dependencies.Clear(); nuspec.Dependencies.Add(new NugetFrameworkGroup()); nuspec.Dependencies[0].Dependencies = roots; } } EditorGUILayout.EndHorizontal(); // display the dependencies NugetPackageIdentifier toDelete = null; var nuspecFrameworkGroup = TargetFrameworkResolver.GetBestDependencyFrameworkGroupForCurrentSettings(nuspec, null); foreach (var dependency in nuspecFrameworkGroup.Dependencies.Cast()) { EditorGUILayout.BeginHorizontal(); GUILayout.Space(75); var prevLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 50; dependency.Id = EditorGUILayout.TextField(new GUIContent("ID", "The ID of the dependency package."), dependency.Id); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Space(75); dependency.Version = EditorGUILayout.TextField( new GUIContent("Version", "The version number of the dependency package. (specify ranges with =><)"), dependency.Version); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); { GUILayout.Space(75); if (GUILayout.Button("Remove " + dependency.Id)) { toDelete = dependency; } } EditorGUILayout.EndHorizontal(); EditorGUILayout.Separator(); EditorGUIUtility.labelWidth = prevLabelWidth; } if (toDelete != null) { nuspecFrameworkGroup.Dependencies.Remove(toDelete); } EditorGUILayout.BeginHorizontal(); { GUILayout.Space(50); if (GUILayout.Button("Add Dependency")) { nuspecFrameworkGroup.Dependencies.Add(new NugetPackageIdentifier()); } } EditorGUILayout.EndHorizontal(); } EditorGUILayout.Separator(); if (GUILayout.Button($"Save {Path.GetFileName(filepath)}")) { nuspec.Save(filepath); } EditorGUILayout.Separator(); if (GUILayout.Button($"Pack {Path.GetFileNameWithoutExtension(filepath)}.nupkg")) { NugetCliHelper.Pack(filepath); } EditorGUILayout.Separator(); apiKey = EditorGUILayout.TextField(new GUIContent("API Key", "The API key to use when pushing the package to the server"), apiKey); if (GUILayout.Button("Push to Server")) { NugetCliHelper.Push(nuspec, filepath, apiKey); } } } /// /// Reload the currently selected asset as a .nuspec file. /// private void Reload() { var defaultAsset = Selection.activeObject as DefaultAsset; if (defaultAsset == null) { return; } var assetFilepath = AssetDatabase.GetAssetPath(defaultAsset); assetFilepath = Path.Combine(UnityPathHelper.AbsoluteProjectPath, assetFilepath); var isNuspec = Path.GetExtension(assetFilepath) == ".nuspec"; var alreadyLoaded = nuspec != null && filepath == assetFilepath; if (!isNuspec || alreadyLoaded) { return; } filepath = assetFilepath; nuspec = NuspecFile.Load(filepath); titleContent = new GUIContent(Path.GetFileNameWithoutExtension(filepath)); // force a repaint Repaint(); } /// /// Called when enabling the window. /// private void OnFocus() { Reload(); } /// /// Reloads the .nuspec file when the selection changes. /// private void OnSelectionChange() { Reload(); } } }