You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
9.8 KiB
282 lines
9.8 KiB
using System; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Diagnostics; |
|
using UnityEditor; |
|
using UnityEngine; |
|
using Unity.CodeEditor; |
|
|
|
namespace VSCodeEditor |
|
{ |
|
[InitializeOnLoad] |
|
public class VSCodeScriptEditor : IExternalCodeEditor |
|
{ |
|
const string vscode_argument = "vscode_arguments"; |
|
const string vscode_extension = "vscode_userExtensions"; |
|
static readonly GUIContent k_ResetArguments = EditorGUIUtility.TrTextContent("Reset argument"); |
|
string m_Arguments; |
|
|
|
IDiscovery m_Discoverability; |
|
IGenerator m_ProjectGeneration; |
|
|
|
static readonly string[] k_SupportedFileNames = { "code.exe", "visualstudiocode.app", "visualstudiocode-insiders.app", "vscode.app", "code.app", "code.cmd", "code-insiders.cmd", "code", "com.visualstudio.code" }; |
|
|
|
static bool IsOSX => Application.platform == RuntimePlatform.OSXEditor; |
|
|
|
static string DefaultApp => EditorPrefs.GetString("kScriptsDefaultApp"); |
|
|
|
static string DefaultArgument { get; } = "\"$(ProjectPath)\" -g \"$(File)\":$(Line):$(Column)"; |
|
|
|
string Arguments |
|
{ |
|
get => m_Arguments ?? (m_Arguments = EditorPrefs.GetString(vscode_argument, DefaultArgument)); |
|
set |
|
{ |
|
m_Arguments = value; |
|
EditorPrefs.SetString(vscode_argument, value); |
|
} |
|
} |
|
|
|
static string[] defaultExtensions |
|
{ |
|
get |
|
{ |
|
var customExtensions = new[] { "json", "asmdef", "log" }; |
|
return EditorSettings.projectGenerationBuiltinExtensions |
|
.Concat(EditorSettings.projectGenerationUserExtensions) |
|
.Concat(customExtensions) |
|
.Distinct().ToArray(); |
|
} |
|
} |
|
|
|
static string[] HandledExtensions |
|
{ |
|
get |
|
{ |
|
return HandledExtensionsString |
|
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) |
|
.Select(s => s.TrimStart('.', '*')) |
|
.ToArray(); |
|
} |
|
} |
|
|
|
static string HandledExtensionsString |
|
{ |
|
get => EditorPrefs.GetString(vscode_extension, string.Join(";", defaultExtensions)); |
|
set => EditorPrefs.SetString(vscode_extension, value); |
|
} |
|
|
|
public bool TryGetInstallationForPath(string editorPath, out CodeEditor.Installation installation) |
|
{ |
|
var lowerCasePath = editorPath.ToLower(); |
|
var filename = Path.GetFileName(lowerCasePath).Replace(" ", ""); |
|
var installations = Installations; |
|
if (!k_SupportedFileNames.Contains(filename)) |
|
{ |
|
installation = default; |
|
return false; |
|
} |
|
|
|
if (!installations.Any()) |
|
{ |
|
installation = new CodeEditor.Installation |
|
{ |
|
Name = "Visual Studio Code", |
|
Path = editorPath |
|
}; |
|
} |
|
else |
|
{ |
|
try |
|
{ |
|
installation = installations.First(inst => inst.Path == editorPath); |
|
} |
|
catch (InvalidOperationException) |
|
{ |
|
installation = new CodeEditor.Installation |
|
{ |
|
Name = "Visual Studio Code", |
|
Path = editorPath |
|
}; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
public void OnGUI() |
|
{ |
|
Arguments = EditorGUILayout.TextField("External Script Editor Args", Arguments); |
|
if (GUILayout.Button(k_ResetArguments, GUILayout.Width(120))) |
|
{ |
|
Arguments = DefaultArgument; |
|
} |
|
|
|
EditorGUILayout.LabelField("Generate .csproj files for:"); |
|
EditorGUI.indentLevel++; |
|
SettingsButton(ProjectGenerationFlag.Embedded, "Embedded packages", ""); |
|
SettingsButton(ProjectGenerationFlag.Local, "Local packages", ""); |
|
SettingsButton(ProjectGenerationFlag.Registry, "Registry packages", ""); |
|
SettingsButton(ProjectGenerationFlag.Git, "Git packages", ""); |
|
SettingsButton(ProjectGenerationFlag.BuiltIn, "Built-in packages", ""); |
|
#if UNITY_2019_3_OR_NEWER |
|
SettingsButton(ProjectGenerationFlag.LocalTarBall, "Local tarball", ""); |
|
#endif |
|
SettingsButton(ProjectGenerationFlag.Unknown, "Packages from unknown sources", ""); |
|
RegenerateProjectFiles(); |
|
EditorGUI.indentLevel--; |
|
|
|
HandledExtensionsString = EditorGUILayout.TextField(new GUIContent("Extensions handled: "), HandledExtensionsString); |
|
} |
|
|
|
void RegenerateProjectFiles() |
|
{ |
|
var rect = EditorGUI.IndentedRect(EditorGUILayout.GetControlRect(new GUILayoutOption[] { })); |
|
rect.width = 252; |
|
if (GUI.Button(rect, "Regenerate project files")) |
|
{ |
|
m_ProjectGeneration.Sync(); |
|
} |
|
} |
|
|
|
void SettingsButton(ProjectGenerationFlag preference, string guiMessage, string toolTip) |
|
{ |
|
var prevValue = m_ProjectGeneration.AssemblyNameProvider.ProjectGenerationFlag.HasFlag(preference); |
|
var newValue = EditorGUILayout.Toggle(new GUIContent(guiMessage, toolTip), prevValue); |
|
if (newValue != prevValue) |
|
{ |
|
m_ProjectGeneration.AssemblyNameProvider.ToggleProjectGeneration(preference); |
|
} |
|
} |
|
|
|
public void CreateIfDoesntExist() |
|
{ |
|
if (!m_ProjectGeneration.SolutionExists()) |
|
{ |
|
m_ProjectGeneration.Sync(); |
|
} |
|
} |
|
|
|
public void SyncIfNeeded(string[] addedFiles, string[] deletedFiles, string[] movedFiles, string[] movedFromFiles, string[] importedFiles) |
|
{ |
|
(m_ProjectGeneration.AssemblyNameProvider as IPackageInfoCache)?.ResetPackageInfoCache(); |
|
m_ProjectGeneration.SyncIfNeeded(addedFiles.Union(deletedFiles).Union(movedFiles).Union(movedFromFiles).ToList(), importedFiles); |
|
} |
|
|
|
public void SyncAll() |
|
{ |
|
(m_ProjectGeneration.AssemblyNameProvider as IPackageInfoCache)?.ResetPackageInfoCache(); |
|
AssetDatabase.Refresh(); |
|
m_ProjectGeneration.Sync(); |
|
} |
|
|
|
public bool OpenProject(string path, int line, int column) |
|
{ |
|
if (path != "" && (!SupportsExtension(path) || !File.Exists(path))) // Assets - Open C# Project passes empty path here |
|
{ |
|
return false; |
|
} |
|
|
|
if (line == -1) |
|
line = 1; |
|
if (column == -1) |
|
column = 0; |
|
|
|
string arguments; |
|
if (Arguments != DefaultArgument) |
|
{ |
|
arguments = m_ProjectGeneration.ProjectDirectory != path |
|
? CodeEditor.ParseArgument(Arguments, path, line, column) |
|
: m_ProjectGeneration.ProjectDirectory; |
|
} |
|
else |
|
{ |
|
arguments = $@"""{m_ProjectGeneration.ProjectDirectory}"""; |
|
if (m_ProjectGeneration.ProjectDirectory != path && path.Length != 0) |
|
{ |
|
arguments += $@" -g ""{path}"":{line}:{column}"; |
|
} |
|
} |
|
|
|
if (IsOSX) |
|
{ |
|
return OpenOSX(arguments); |
|
} |
|
|
|
var app = DefaultApp; |
|
var process = new Process |
|
{ |
|
StartInfo = new ProcessStartInfo |
|
{ |
|
FileName = app, |
|
Arguments = arguments, |
|
WindowStyle = app.EndsWith(".cmd", StringComparison.OrdinalIgnoreCase) ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal, |
|
CreateNoWindow = true, |
|
UseShellExecute = true, |
|
} |
|
}; |
|
|
|
process.Start(); |
|
return true; |
|
} |
|
|
|
static bool OpenOSX(string arguments) |
|
{ |
|
var process = new Process |
|
{ |
|
StartInfo = new ProcessStartInfo |
|
{ |
|
FileName = "open", |
|
Arguments = $"-n \"{DefaultApp}\" --args {arguments}", |
|
UseShellExecute = true, |
|
} |
|
}; |
|
|
|
process.Start(); |
|
return true; |
|
} |
|
|
|
static bool SupportsExtension(string path) |
|
{ |
|
var extension = Path.GetExtension(path); |
|
if (string.IsNullOrEmpty(extension)) |
|
return false; |
|
return HandledExtensions.Contains(extension.TrimStart('.')); |
|
} |
|
|
|
public CodeEditor.Installation[] Installations => m_Discoverability.PathCallback(); |
|
|
|
public VSCodeScriptEditor(IDiscovery discovery, IGenerator projectGeneration) |
|
{ |
|
m_Discoverability = discovery; |
|
m_ProjectGeneration = projectGeneration; |
|
} |
|
|
|
static VSCodeScriptEditor() |
|
{ |
|
var editor = new VSCodeScriptEditor(new VSCodeDiscovery(), new ProjectGeneration(Directory.GetParent(Application.dataPath).FullName)); |
|
CodeEditor.Register(editor); |
|
|
|
if (IsVSCodeInstallation(CodeEditor.CurrentEditorInstallation)) |
|
{ |
|
editor.CreateIfDoesntExist(); |
|
} |
|
} |
|
|
|
static bool IsVSCodeInstallation(string path) |
|
{ |
|
if (string.IsNullOrEmpty(path)) |
|
{ |
|
return false; |
|
} |
|
|
|
var lowerCasePath = path.ToLower(); |
|
var filename = Path |
|
.GetFileName(lowerCasePath.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar)) |
|
.Replace(" ", ""); |
|
return k_SupportedFileNames.Contains(filename); |
|
} |
|
|
|
public void Initialize(string editorInstallationPath) { } |
|
} |
|
}
|
|
|