Merge pull request #87137 from /dotnet/nullable-godot-tools
This commit is contained in:
commit
ccd9cefa8e
|
@ -7,11 +7,11 @@ namespace GodotTools.BuildLogger
|
|||
{
|
||||
public class GodotBuildLogger : ILogger
|
||||
{
|
||||
public string Parameters { get; set; }
|
||||
public string? Parameters { get; set; }
|
||||
public LoggerVerbosity Verbosity { get; set; }
|
||||
|
||||
private StreamWriter _logStreamWriter;
|
||||
private StreamWriter _issuesStreamWriter;
|
||||
private StreamWriter _logStreamWriter = StreamWriter.Null;
|
||||
private StreamWriter _issuesStreamWriter = StreamWriter.Null;
|
||||
private int _indent;
|
||||
|
||||
public void Initialize(IEventSource eventSource)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<ProjectGuid>{6CE9A984-37B1-4F8A-8FE9-609F05F071B3}</ProjectGuid>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" ExcludeAssets="runtime" />
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
<ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace GodotTools.Core
|
|||
{
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
|
||||
void ProcessExited(object sender, EventArgs e)
|
||||
void ProcessExited(object? sender, EventArgs e)
|
||||
{
|
||||
tcs.TrySetResult(true);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace GodotTools.Core
|
|||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
private static readonly string _driveRoot = Path.GetPathRoot(Environment.CurrentDirectory);
|
||||
private static readonly string _driveRoot = Path.GetPathRoot(Environment.CurrentDirectory)!;
|
||||
|
||||
public static string RelativeToPath(this string path, string dir)
|
||||
{
|
||||
|
@ -26,9 +26,6 @@ namespace GodotTools.Core
|
|||
|
||||
public static string NormalizePath(this string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return path;
|
||||
|
||||
bool rooted = path.IsAbsolutePath();
|
||||
|
||||
path = path.Replace('\\', '/');
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />
|
||||
|
|
|
@ -64,14 +64,14 @@ namespace GodotTools.IdeMessaging.CLI
|
|||
|
||||
while (!fwdClient.IsDisposed)
|
||||
{
|
||||
string firstLine = await inputReader.ReadLineAsync();
|
||||
string? firstLine = await inputReader.ReadLineAsync();
|
||||
|
||||
if (firstLine == null || firstLine == "QUIT")
|
||||
goto ExitMainLoop;
|
||||
|
||||
string messageId = firstLine;
|
||||
|
||||
string messageArgcLine = await inputReader.ReadLineAsync();
|
||||
string? messageArgcLine = await inputReader.ReadLineAsync();
|
||||
|
||||
if (messageArgcLine == null)
|
||||
{
|
||||
|
@ -89,7 +89,7 @@ namespace GodotTools.IdeMessaging.CLI
|
|||
|
||||
for (int i = 0; i < messageArgc; i++)
|
||||
{
|
||||
string bodyLine = await inputReader.ReadLineAsync();
|
||||
string? bodyLine = await inputReader.ReadLineAsync();
|
||||
|
||||
if (bodyLine == null)
|
||||
{
|
||||
|
@ -126,29 +126,29 @@ namespace GodotTools.IdeMessaging.CLI
|
|||
}
|
||||
}
|
||||
|
||||
private static async Task<Response> SendRequest(Client client, string id, MessageContent content)
|
||||
private static async Task<Response?> SendRequest(Client client, string id, MessageContent content)
|
||||
{
|
||||
var handlers = new Dictionary<string, Func<Task<Response>>>
|
||||
var handlers = new Dictionary<string, Func<Task<Response?>>>
|
||||
{
|
||||
[PlayRequest.Id] = async () =>
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<PlayRequest>(content.Body);
|
||||
return await client.SendRequest<PlayResponse>(request);
|
||||
return await client.SendRequest<PlayResponse>(request!);
|
||||
},
|
||||
[DebugPlayRequest.Id] = async () =>
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<DebugPlayRequest>(content.Body);
|
||||
return await client.SendRequest<DebugPlayResponse>(request);
|
||||
return await client.SendRequest<DebugPlayResponse>(request!);
|
||||
},
|
||||
[ReloadScriptsRequest.Id] = async () =>
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<ReloadScriptsRequest>(content.Body);
|
||||
return await client.SendRequest<ReloadScriptsResponse>(request);
|
||||
return await client.SendRequest<ReloadScriptsResponse>(request!);
|
||||
},
|
||||
[CodeCompletionRequest.Id] = async () =>
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<CodeCompletionRequest>(content.Body);
|
||||
return await client.SendRequest<CodeCompletionResponse>(request);
|
||||
return await client.SendRequest<CodeCompletionResponse>(request!);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
@ -27,7 +28,7 @@ namespace GodotTools.IdeMessaging
|
|||
|
||||
private readonly IMessageHandler messageHandler;
|
||||
|
||||
private Peer peer;
|
||||
private Peer? peer;
|
||||
private readonly SemaphoreSlim connectionSem = new SemaphoreSlim(1);
|
||||
|
||||
private readonly Queue<NotifyAwaiter<bool>> clientConnectedAwaiters = new Queue<NotifyAwaiter<bool>>();
|
||||
|
@ -53,6 +54,7 @@ namespace GodotTools.IdeMessaging
|
|||
public bool IsDisposed { get; private set; }
|
||||
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
[MemberNotNullWhen(true, "peer")]
|
||||
public bool IsConnected => peer != null && !peer.IsDisposed && peer.IsTcpClientConnected;
|
||||
|
||||
// ReSharper disable once EventNeverSubscribedTo.Global
|
||||
|
@ -111,7 +113,7 @@ namespace GodotTools.IdeMessaging
|
|||
if (disposing)
|
||||
{
|
||||
peer?.Dispose();
|
||||
fsWatcher?.Dispose();
|
||||
fsWatcher.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,12 +217,12 @@ namespace GodotTools.IdeMessaging
|
|||
using (var fileStream = new FileStream(MetaFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
using (var reader = new StreamReader(fileStream))
|
||||
{
|
||||
string portStr = reader.ReadLine();
|
||||
string? portStr = reader.ReadLine();
|
||||
|
||||
if (portStr == null)
|
||||
return null;
|
||||
|
||||
string editorExecutablePath = reader.ReadLine();
|
||||
string? editorExecutablePath = reader.ReadLine();
|
||||
|
||||
if (editorExecutablePath == null)
|
||||
return null;
|
||||
|
@ -333,7 +335,7 @@ namespace GodotTools.IdeMessaging
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<TResponse> SendRequest<TResponse>(Request request)
|
||||
public async Task<TResponse?> SendRequest<TResponse>(Request request)
|
||||
where TResponse : Response, new()
|
||||
{
|
||||
if (!IsConnected)
|
||||
|
@ -346,7 +348,7 @@ namespace GodotTools.IdeMessaging
|
|||
return await peer.SendRequest<TResponse>(request.Id, body);
|
||||
}
|
||||
|
||||
public async Task<TResponse> SendRequest<TResponse>(string id, string body)
|
||||
public async Task<TResponse?> SendRequest<TResponse>(string id, string body)
|
||||
where TResponse : Response, new()
|
||||
{
|
||||
if (!IsConnected)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GodotTools.IdeMessaging
|
||||
|
@ -9,7 +10,7 @@ namespace GodotTools.IdeMessaging
|
|||
|
||||
public string GetHandshakeLine(string identity) => $"{ClientHandshakeBase},{identity}";
|
||||
|
||||
public bool IsValidPeerHandshake(string handshake, out string identity, ILogger logger)
|
||||
public bool IsValidPeerHandshake(string handshake, [NotNullWhen(true)] out string? identity, ILogger logger)
|
||||
{
|
||||
identity = null;
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace GodotTools.IdeMessaging
|
|||
[OpenFileRequest.Id] = async (peer, content) =>
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<OpenFileRequest>(content.Body);
|
||||
return await HandleOpenFile(request);
|
||||
return await HandleOpenFile(request!);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Diagnostics.CodeAnalysis
|
||||
{
|
||||
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
|
||||
internal sealed class AllowNullAttribute : Attribute
|
||||
{ }
|
||||
|
||||
/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
|
||||
internal sealed class DisallowNullAttribute : Attribute
|
||||
{ }
|
||||
|
||||
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
|
||||
internal sealed class MaybeNullAttribute : Attribute
|
||||
{ }
|
||||
|
||||
/// <summary>Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns.</summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
|
||||
internal sealed class NotNullAttribute : Attribute
|
||||
{ }
|
||||
|
||||
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
|
||||
internal sealed class MaybeNullWhenAttribute : Attribute
|
||||
{
|
||||
/// <summary>Initializes the attribute with the specified return value condition.</summary>
|
||||
/// <param name="returnValue">
|
||||
/// The return value condition. If the method returns this value, the associated parameter may be null.
|
||||
/// </param>
|
||||
public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
|
||||
|
||||
/// <summary>Gets the return value condition.</summary>
|
||||
public bool ReturnValue { get; }
|
||||
}
|
||||
|
||||
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
|
||||
internal sealed class NotNullWhenAttribute : Attribute
|
||||
{
|
||||
/// <summary>Initializes the attribute with the specified return value condition.</summary>
|
||||
/// <param name="returnValue">
|
||||
/// The return value condition. If the method returns this value, the associated parameter will not be null.
|
||||
/// </param>
|
||||
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
|
||||
|
||||
/// <summary>Gets the return value condition.</summary>
|
||||
public bool ReturnValue { get; }
|
||||
}
|
||||
|
||||
/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
|
||||
internal sealed class NotNullIfNotNullAttribute : Attribute
|
||||
{
|
||||
/// <summary>Initializes the attribute with the associated parameter name.</summary>
|
||||
/// <param name="parameterName">
|
||||
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
|
||||
/// </param>
|
||||
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
|
||||
|
||||
/// <summary>Gets the associated parameter name.</summary>
|
||||
public string ParameterName { get; }
|
||||
}
|
||||
|
||||
/// <summary>Applied to a method that will never return under any circumstance.</summary>
|
||||
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
|
||||
internal sealed class DoesNotReturnAttribute : Attribute
|
||||
{ }
|
||||
|
||||
/// <summary>Specifies that the method will not return if the associated Boolean parameter is passed the specified value.</summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
|
||||
internal sealed class DoesNotReturnIfAttribute : Attribute
|
||||
{
|
||||
/// <summary>Initializes the attribute with the specified parameter value.</summary>
|
||||
/// <param name="parameterValue">
|
||||
/// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to
|
||||
/// the associated parameter matches this value.
|
||||
/// </param>
|
||||
public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
|
||||
|
||||
/// <summary>Gets the condition parameter value.</summary>
|
||||
public bool ParameterValue { get; }
|
||||
}
|
||||
|
||||
/// <summary>Specifies that the method or property will ensure that the listed field and property members have not-null values.</summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
|
||||
internal sealed class MemberNotNullAttribute : Attribute
|
||||
{
|
||||
/// <summary>Initializes the attribute with a field or property member.</summary>
|
||||
/// <param name="member">
|
||||
/// The field or property member that is promised to be not-null.
|
||||
/// </param>
|
||||
public MemberNotNullAttribute(string member) => Members = new[] { member };
|
||||
|
||||
/// <summary>Initializes the attribute with the list of field and property members.</summary>
|
||||
/// <param name="members">
|
||||
/// The list of field and property members that are promised to be not-null.
|
||||
/// </param>
|
||||
public MemberNotNullAttribute(params string[] members) => Members = members;
|
||||
|
||||
/// <summary>Gets field or property member names.</summary>
|
||||
public string[] Members { get; }
|
||||
}
|
||||
|
||||
/// <summary>Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition.</summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
|
||||
internal sealed class MemberNotNullWhenAttribute : Attribute
|
||||
{
|
||||
/// <summary>Initializes the attribute with the specified return value condition and a field or property member.</summary>
|
||||
/// <param name="returnValue">
|
||||
/// The return value condition. If the method returns this value, the associated parameter will not be null.
|
||||
/// </param>
|
||||
/// <param name="member">
|
||||
/// The field or property member that is promised to be not-null.
|
||||
/// </param>
|
||||
public MemberNotNullWhenAttribute(bool returnValue, string member)
|
||||
{
|
||||
ReturnValue = returnValue;
|
||||
Members = new[] { member };
|
||||
}
|
||||
|
||||
/// <summary>Initializes the attribute with the specified return value condition and list of field and property members.</summary>
|
||||
/// <param name="returnValue">
|
||||
/// The return value condition. If the method returns this value, the associated parameter will not be null.
|
||||
/// </param>
|
||||
/// <param name="members">
|
||||
/// The list of field and property members that are promised to be not-null.
|
||||
/// </param>
|
||||
public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
|
||||
{
|
||||
ReturnValue = returnValue;
|
||||
Members = members;
|
||||
}
|
||||
|
||||
/// <summary>Gets the return value condition.</summary>
|
||||
public bool ReturnValue { get; }
|
||||
|
||||
/// <summary>Gets field or property member names.</summary>
|
||||
public string[] Members { get; }
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace GodotTools.IdeMessaging
|
||||
{
|
||||
public readonly struct GodotIdeMetadata
|
||||
|
@ -23,7 +25,7 @@ namespace GodotTools.IdeMessaging
|
|||
return !(a == b);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is GodotIdeMetadata metadata && metadata == this;
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
<PropertyGroup>
|
||||
<ProjectGuid>{92600954-25F0-4291-8E11-1FEE9FC4BE20}</ProjectGuid>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<LangVersion>9</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<PackageId>GodotTools.IdeMessaging</PackageId>
|
||||
<Version>1.1.1</Version>
|
||||
<Version>1.1.2</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<Authors>Godot Engine contributors</Authors>
|
||||
<Company />
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace GodotTools.IdeMessaging
|
||||
{
|
||||
public interface IHandshake
|
||||
{
|
||||
string GetHandshakeLine(string identity);
|
||||
bool IsValidPeerHandshake(string handshake, out string identity, ILogger logger);
|
||||
bool IsValidPeerHandshake(string handshake, [NotNullWhen(true)] out string? identity, ILogger logger);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace GodotTools.IdeMessaging
|
|||
private class DecodedMessage
|
||||
{
|
||||
public MessageKind? Kind;
|
||||
public string Id;
|
||||
public string? Id;
|
||||
public MessageStatus? Status;
|
||||
public readonly StringBuilder Body = new StringBuilder();
|
||||
public uint? PendingBodyLines;
|
||||
|
@ -41,7 +41,7 @@ namespace GodotTools.IdeMessaging
|
|||
|
||||
private readonly DecodedMessage decodingMessage = new DecodedMessage();
|
||||
|
||||
public State Decode(string messageLine, out Message decodedMessage)
|
||||
public State Decode(string messageLine, out Message? decodedMessage)
|
||||
{
|
||||
decodedMessage = null;
|
||||
|
||||
|
|
|
@ -46,11 +46,11 @@ namespace GodotTools.IdeMessaging
|
|||
|
||||
private readonly SemaphoreSlim writeSem = new SemaphoreSlim(1);
|
||||
|
||||
private string remoteIdentity = string.Empty;
|
||||
public string RemoteIdentity => remoteIdentity;
|
||||
private string? remoteIdentity;
|
||||
public string RemoteIdentity => remoteIdentity ??= string.Empty;
|
||||
|
||||
public event Action Connected;
|
||||
public event Action Disconnected;
|
||||
public event Action? Connected;
|
||||
public event Action? Disconnected;
|
||||
|
||||
private ILogger Logger { get; }
|
||||
|
||||
|
@ -87,7 +87,7 @@ namespace GodotTools.IdeMessaging
|
|||
{
|
||||
var decoder = new MessageDecoder();
|
||||
|
||||
string messageLine;
|
||||
string? messageLine;
|
||||
while ((messageLine = await ReadLine()) != null)
|
||||
{
|
||||
var state = decoder.Decode(messageLine, out var msg);
|
||||
|
@ -105,7 +105,7 @@ namespace GodotTools.IdeMessaging
|
|||
|
||||
try
|
||||
{
|
||||
if (msg.Kind == MessageKind.Request)
|
||||
if (msg!.Kind == MessageKind.Request)
|
||||
{
|
||||
var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger);
|
||||
await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent));
|
||||
|
@ -163,9 +163,9 @@ namespace GodotTools.IdeMessaging
|
|||
return false;
|
||||
}
|
||||
|
||||
string peerHandshake = await readHandshakeTask;
|
||||
string? peerHandshake = await readHandshakeTask;
|
||||
|
||||
if (handshake == null || !handshake.IsValidPeerHandshake(peerHandshake, out remoteIdentity, Logger))
|
||||
if (peerHandshake == null || !handshake.IsValidPeerHandshake(peerHandshake, out remoteIdentity, Logger))
|
||||
{
|
||||
Logger.LogError("Received invalid handshake: " + peerHandshake);
|
||||
return false;
|
||||
|
@ -179,7 +179,7 @@ namespace GodotTools.IdeMessaging
|
|||
return true;
|
||||
}
|
||||
|
||||
private async Task<string> ReadLine()
|
||||
private async Task<string?> ReadLine()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -216,7 +216,7 @@ namespace GodotTools.IdeMessaging
|
|||
return WriteLine(builder.ToString());
|
||||
}
|
||||
|
||||
public async Task<TResponse> SendRequest<TResponse>(string id, string body)
|
||||
public async Task<TResponse?> SendRequest<TResponse>(string id, string body)
|
||||
where TResponse : Response, new()
|
||||
{
|
||||
ResponseAwaiter responseAwaiter;
|
||||
|
@ -243,7 +243,7 @@ namespace GodotTools.IdeMessaging
|
|||
|
||||
private async Task<bool> WriteLine(string text)
|
||||
{
|
||||
if (clientWriter == null || IsDisposed || !IsTcpClientConnected)
|
||||
if (IsDisposed || !IsTcpClientConnected)
|
||||
return false;
|
||||
|
||||
using (await writeSem.UseAsync())
|
||||
|
@ -290,9 +290,9 @@ namespace GodotTools.IdeMessaging
|
|||
Disconnected?.Invoke();
|
||||
}
|
||||
|
||||
clientReader?.Dispose();
|
||||
clientWriter?.Dispose();
|
||||
((IDisposable)tcpClient)?.Dispose();
|
||||
clientReader.Dispose();
|
||||
clientWriter.Dispose();
|
||||
((IDisposable)tcpClient).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace GodotTools.IdeMessaging.Requests
|
||||
|
@ -38,7 +39,7 @@ namespace GodotTools.IdeMessaging.Requests
|
|||
}
|
||||
|
||||
public CompletionKind Kind { get; set; }
|
||||
public string ScriptFile { get; set; }
|
||||
public string ScriptFile { get; set; } = string.Empty;
|
||||
|
||||
public new const string Id = "CodeCompletion";
|
||||
|
||||
|
@ -50,8 +51,8 @@ namespace GodotTools.IdeMessaging.Requests
|
|||
public sealed class CodeCompletionResponse : Response
|
||||
{
|
||||
public CodeCompletionRequest.CompletionKind Kind;
|
||||
public string ScriptFile { get; set; }
|
||||
public string[] Suggestions { get; set; }
|
||||
public string ScriptFile { get; set; } = string.Empty;
|
||||
public string[] Suggestions { get; set; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public sealed class PlayRequest : Request
|
||||
|
@ -82,7 +83,7 @@ namespace GodotTools.IdeMessaging.Requests
|
|||
|
||||
public sealed class DebugPlayRequest : Request
|
||||
{
|
||||
public string DebuggerHost { get; set; }
|
||||
public string DebuggerHost { get; set; } = string.Empty;
|
||||
public int DebuggerPort { get; set; }
|
||||
public bool? BuildBeforePlaying { get; set; }
|
||||
|
||||
|
@ -99,7 +100,7 @@ namespace GodotTools.IdeMessaging.Requests
|
|||
|
||||
public sealed class OpenFileRequest : Request
|
||||
{
|
||||
public string File { get; set; }
|
||||
public string File { get; set; } = string.Empty;
|
||||
public int? Line { get; set; }
|
||||
public int? Column { get; set; }
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace GodotTools.IdeMessaging
|
|||
public override void SetResult(MessageContent content)
|
||||
{
|
||||
if (content.Status == MessageStatus.Ok)
|
||||
SetResult(JsonConvert.DeserializeObject<T>(content.Body));
|
||||
SetResult(JsonConvert.DeserializeObject<T>(content.Body)!);
|
||||
else
|
||||
SetResult(new T { Status = content.Status });
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// ReSharper disable ParameterHidesMember
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
|
@ -5,9 +8,9 @@ namespace GodotTools.IdeMessaging.Utils
|
|||
{
|
||||
public class NotifyAwaiter<T> : INotifyCompletion
|
||||
{
|
||||
private Action continuation;
|
||||
private Exception exception;
|
||||
private T result;
|
||||
private Action? continuation;
|
||||
private Exception? exception;
|
||||
private T? result;
|
||||
|
||||
public bool IsCompleted { get; private set; }
|
||||
|
||||
|
@ -15,7 +18,7 @@ namespace GodotTools.IdeMessaging.Utils
|
|||
{
|
||||
if (exception != null)
|
||||
throw exception;
|
||||
return result;
|
||||
return result!;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="Exists('$(SolutionDir)/../../../../bin/GodotSharp/Api/Debug/GodotSharp.dll') And ('$(GodotPlatform)' == 'windows' Or ('$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT'))">
|
||||
<OutputPath>$(SolutionDir)/../../../../bin/GodotSharp/Tools</OutputPath>
|
||||
|
|
|
@ -53,6 +53,12 @@ namespace GodotTools.OpenVisualStudio
|
|||
{
|
||||
// Launch of VS 2022 failed, fallback to 2019
|
||||
dte = TryVisualStudioLaunch("VisualStudio.DTE.16.0");
|
||||
|
||||
if (dte == null)
|
||||
{
|
||||
Console.Error.WriteLine("Visual Studio not found");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
dte.UserControl = true;
|
||||
|
@ -137,12 +143,12 @@ namespace GodotTools.OpenVisualStudio
|
|||
return 0;
|
||||
}
|
||||
|
||||
private static DTE TryVisualStudioLaunch(string version)
|
||||
private static DTE? TryVisualStudioLaunch(string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
var visualStudioDteType = Type.GetTypeFromProgID(version, throwOnError: true);
|
||||
var dte = (DTE)Activator.CreateInstance(visualStudioDteType);
|
||||
var dte = (DTE?)Activator.CreateInstance(visualStudioDteType!);
|
||||
|
||||
return dte;
|
||||
}
|
||||
|
@ -152,7 +158,7 @@ namespace GodotTools.OpenVisualStudio
|
|||
}
|
||||
}
|
||||
|
||||
private static DTE FindInstanceEditingSolution(string solutionPath)
|
||||
private static DTE? FindInstanceEditingSolution(string solutionPath)
|
||||
{
|
||||
if (GetRunningObjectTable(0, out IRunningObjectTable pprot) != 0)
|
||||
return null;
|
||||
|
@ -218,7 +224,7 @@ namespace GodotTools.OpenVisualStudio
|
|||
// Class containing the IOleMessageFilter
|
||||
// thread error-handling functions
|
||||
|
||||
private static IOleMessageFilter _oldFilter;
|
||||
private static IOleMessageFilter? _oldFilter;
|
||||
|
||||
// Start the filter
|
||||
public static void Register()
|
||||
|
@ -268,7 +274,7 @@ namespace GodotTools.OpenVisualStudio
|
|||
|
||||
// Implement the IOleMessageFilter interface
|
||||
[DllImport("ole32.dll")]
|
||||
private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
|
||||
private static extern int CoRegisterMessageFilter(IOleMessageFilter? newFilter, out IOleMessageFilter? oldFilter);
|
||||
}
|
||||
|
||||
[ComImport(), Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
|
|
|
@ -34,22 +34,23 @@ EndProject";
|
|||
@" {{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU
|
||||
{{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU";
|
||||
|
||||
private string _directoryPath;
|
||||
private readonly Dictionary<string, ProjectInfo> _projects = new Dictionary<string, ProjectInfo>();
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public string DirectoryPath
|
||||
{
|
||||
get => _directoryPath;
|
||||
set => _directoryPath = value.IsAbsolutePath() ? value : Path.GetFullPath(value);
|
||||
}
|
||||
public string DirectoryPath { get; }
|
||||
|
||||
public class ProjectInfo
|
||||
{
|
||||
public string Guid;
|
||||
public string PathRelativeToSolution;
|
||||
public List<string> Configs = new List<string>();
|
||||
public string Guid { get; }
|
||||
public string PathRelativeToSolution { get; }
|
||||
public List<string> Configs { get; }
|
||||
|
||||
public ProjectInfo(string guid, string pathRelativeToSolution, List<string> configs)
|
||||
{
|
||||
Guid = guid;
|
||||
PathRelativeToSolution = pathRelativeToSolution;
|
||||
Configs = configs;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddNewProject(string name, ProjectInfo projectInfo)
|
||||
|
@ -117,9 +118,10 @@ EndProject";
|
|||
File.WriteAllText(solutionPath, content, Encoding.UTF8); // UTF-8 with BOM
|
||||
}
|
||||
|
||||
public DotNetSolution(string name)
|
||||
public DotNetSolution(string name, string directoryPath)
|
||||
{
|
||||
Name = name;
|
||||
DirectoryPath = directoryPath.IsAbsolutePath() ? directoryPath : Path.GetFullPath(directoryPath);
|
||||
}
|
||||
|
||||
public static void MigrateFromOldConfigNames(string slnPath)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="15.1.548" ExcludeAssets="runtime" />
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace GodotTools.ProjectEditor
|
|||
public static void MSBuildLocatorRegisterMSBuildPath(string msbuildPath)
|
||||
=> MSBuildLocator.RegisterMSBuildPath(msbuildPath);
|
||||
|
||||
public static MSBuildProject Open(string path)
|
||||
public static MSBuildProject? Open(string path)
|
||||
{
|
||||
var root = ProjectRootElement.Open(path);
|
||||
return root != null ? new MSBuildProject(root) : null;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable enable
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
public class BuildDiagnostic
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using GodotTools.Internals;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
[Serializable]
|
||||
|
@ -25,7 +24,7 @@ namespace GodotTools.Build
|
|||
|
||||
public string LogsDirPath => GodotSharpDirs.LogsDirPathFor(Solution, Configuration);
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is BuildInfo other &&
|
||||
other.Solution == Solution &&
|
||||
|
|
|
@ -11,18 +11,18 @@ namespace GodotTools.Build
|
|||
{
|
||||
public static class BuildManager
|
||||
{
|
||||
private static BuildInfo _buildInProgress;
|
||||
private static BuildInfo? _buildInProgress;
|
||||
|
||||
public const string MsBuildIssuesFileName = "msbuild_issues.csv";
|
||||
private const string MsBuildLogFileName = "msbuild_log.txt";
|
||||
|
||||
public delegate void BuildLaunchFailedEventHandler(BuildInfo buildInfo, string reason);
|
||||
|
||||
public static event BuildLaunchFailedEventHandler BuildLaunchFailed;
|
||||
public static event Action<BuildInfo> BuildStarted;
|
||||
public static event Action<BuildResult> BuildFinished;
|
||||
public static event Action<string> StdOutputReceived;
|
||||
public static event Action<string> StdErrorReceived;
|
||||
public static event BuildLaunchFailedEventHandler? BuildLaunchFailed;
|
||||
public static event Action<BuildInfo>? BuildStarted;
|
||||
public static event Action<BuildResult>? BuildFinished;
|
||||
public static event Action<string?>? StdOutputReceived;
|
||||
public static event Action<string?>? StdErrorReceived;
|
||||
|
||||
public static DateTime LastValidBuildDateTime { get; private set; }
|
||||
|
||||
|
@ -274,8 +274,8 @@ namespace GodotTools.Build
|
|||
}
|
||||
|
||||
private static BuildInfo CreateBuildInfo(
|
||||
[DisallowNull] string configuration,
|
||||
[AllowNull] string platform = null,
|
||||
string configuration,
|
||||
string? platform = null,
|
||||
bool rebuild = false,
|
||||
bool onlyClean = false
|
||||
)
|
||||
|
@ -294,10 +294,10 @@ namespace GodotTools.Build
|
|||
}
|
||||
|
||||
private static BuildInfo CreatePublishBuildInfo(
|
||||
[DisallowNull] string configuration,
|
||||
[DisallowNull] string platform,
|
||||
[DisallowNull] string runtimeIdentifier,
|
||||
[DisallowNull] string publishOutputDir,
|
||||
string configuration,
|
||||
string platform,
|
||||
string runtimeIdentifier,
|
||||
string publishOutputDir,
|
||||
bool includeDebugSymbols = true
|
||||
)
|
||||
{
|
||||
|
@ -319,20 +319,20 @@ namespace GodotTools.Build
|
|||
}
|
||||
|
||||
public static bool BuildProjectBlocking(
|
||||
[DisallowNull] string configuration,
|
||||
[AllowNull] string platform = null,
|
||||
string configuration,
|
||||
string? platform = null,
|
||||
bool rebuild = false
|
||||
) => BuildProjectBlocking(CreateBuildInfo(configuration, platform, rebuild));
|
||||
|
||||
public static bool CleanProjectBlocking(
|
||||
[DisallowNull] string configuration,
|
||||
[AllowNull] string platform = null
|
||||
string configuration,
|
||||
string? platform = null
|
||||
) => CleanProjectBlocking(CreateBuildInfo(configuration, platform, rebuild: false, onlyClean: true));
|
||||
|
||||
public static bool PublishProjectBlocking(
|
||||
[DisallowNull] string configuration,
|
||||
[DisallowNull] string platform,
|
||||
[DisallowNull] string runtimeIdentifier,
|
||||
string configuration,
|
||||
string platform,
|
||||
string runtimeIdentifier,
|
||||
string publishOutputDir,
|
||||
bool includeDebugSymbols = true
|
||||
) => PublishProjectBlocking(CreatePublishBuildInfo(configuration,
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
using Godot;
|
||||
using static GodotTools.Internals.Globals;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
public partial class BuildOutputView : HBoxContainer
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
using Godot;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
public class BuildProblemsFilter
|
||||
|
|
|
@ -8,8 +8,6 @@ using GodotTools.Internals;
|
|||
using static GodotTools.Internals.Globals;
|
||||
using FileAccess = Godot.FileAccess;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
public partial class BuildProblemsView : HBoxContainer
|
||||
|
@ -244,7 +242,9 @@ namespace GodotTools.Build
|
|||
if (string.IsNullOrEmpty(projectDir))
|
||||
return;
|
||||
|
||||
string file = Path.Combine(projectDir.SimplifyGodotPath(), diagnostic.File.SimplifyGodotPath());
|
||||
string? file = !string.IsNullOrEmpty(diagnostic.File) ?
|
||||
Path.Combine(projectDir.SimplifyGodotPath(), diagnostic.File.SimplifyGodotPath()) :
|
||||
null;
|
||||
|
||||
if (!File.Exists(file))
|
||||
return;
|
||||
|
|
|
@ -17,10 +17,10 @@ namespace GodotTools.Build
|
|||
{
|
||||
public static class BuildSystem
|
||||
{
|
||||
private static Process LaunchBuild(BuildInfo buildInfo, Action<string> stdOutHandler,
|
||||
Action<string> stdErrHandler)
|
||||
private static Process LaunchBuild(BuildInfo buildInfo, Action<string?>? stdOutHandler,
|
||||
Action<string?>? stdErrHandler)
|
||||
{
|
||||
string dotnetPath = DotNetFinder.FindDotNetExe();
|
||||
string? dotnetPath = DotNetFinder.FindDotNetExe();
|
||||
|
||||
if (dotnetPath == null)
|
||||
throw new FileNotFoundException("Cannot find the dotnet executable.");
|
||||
|
@ -67,7 +67,7 @@ namespace GodotTools.Build
|
|||
return process;
|
||||
}
|
||||
|
||||
public static int Build(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
|
||||
public static int Build(BuildInfo buildInfo, Action<string?>? stdOutHandler, Action<string?>? stdErrHandler)
|
||||
{
|
||||
using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
|
||||
{
|
||||
|
@ -77,8 +77,8 @@ namespace GodotTools.Build
|
|||
}
|
||||
}
|
||||
|
||||
public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string> stdOutHandler,
|
||||
Action<string> stdErrHandler)
|
||||
public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string?>? stdOutHandler,
|
||||
Action<string?>? stdErrHandler)
|
||||
{
|
||||
using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
|
||||
{
|
||||
|
@ -88,10 +88,10 @@ namespace GodotTools.Build
|
|||
}
|
||||
}
|
||||
|
||||
private static Process LaunchPublish(BuildInfo buildInfo, Action<string> stdOutHandler,
|
||||
Action<string> stdErrHandler)
|
||||
private static Process LaunchPublish(BuildInfo buildInfo, Action<string?>? stdOutHandler,
|
||||
Action<string?>? stdErrHandler)
|
||||
{
|
||||
string dotnetPath = DotNetFinder.FindDotNetExe();
|
||||
string? dotnetPath = DotNetFinder.FindDotNetExe();
|
||||
|
||||
if (dotnetPath == null)
|
||||
throw new FileNotFoundException("Cannot find the dotnet executable.");
|
||||
|
@ -137,7 +137,7 @@ namespace GodotTools.Build
|
|||
return process;
|
||||
}
|
||||
|
||||
public static int Publish(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
|
||||
public static int Publish(BuildInfo buildInfo, Action<string?>? stdOutHandler, Action<string?>? stdErrHandler)
|
||||
{
|
||||
using (var process = LaunchPublish(buildInfo, stdOutHandler, stdErrHandler))
|
||||
{
|
||||
|
@ -297,7 +297,7 @@ namespace GodotTools.Build
|
|||
}
|
||||
|
||||
private static Process DoGenerateXCFramework(List<string> outputPaths, string xcFrameworkPath,
|
||||
Action<string> stdOutHandler, Action<string> stdErrHandler)
|
||||
Action<string?>? stdOutHandler, Action<string?>? stdErrHandler)
|
||||
{
|
||||
if (Directory.Exists(xcFrameworkPath))
|
||||
{
|
||||
|
@ -341,7 +341,7 @@ namespace GodotTools.Build
|
|||
return process;
|
||||
}
|
||||
|
||||
public static int GenerateXCFramework(List<string> outputPaths, string xcFrameworkPath, Action<string> stdOutHandler, Action<string> stdErrHandler)
|
||||
public static int GenerateXCFramework(List<string> outputPaths, string xcFrameworkPath, Action<string?>? stdOutHandler, Action<string?>? stdErrHandler)
|
||||
{
|
||||
using (var process = DoGenerateXCFramework(outputPaths, xcFrameworkPath, stdOutHandler, stdErrHandler))
|
||||
{
|
||||
|
|
|
@ -5,15 +5,13 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using OS = GodotTools.Utils.OS;
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
public static class DotNetFinder
|
||||
{
|
||||
[CanBeNull]
|
||||
public static string FindDotNetExe()
|
||||
public static string? FindDotNetExe()
|
||||
{
|
||||
// In the future, this method may do more than just search in PATH. We could look in
|
||||
// known locations or use Godot's linked nethost to search from the hostfxr location.
|
||||
|
@ -40,14 +38,14 @@ namespace GodotTools.Build
|
|||
|
||||
public static bool TryFindDotNetSdk(
|
||||
Version expectedVersion,
|
||||
[NotNullWhen(true)] out Version version,
|
||||
[NotNullWhen(true)] out string path
|
||||
[NotNullWhen(true)] out Version? version,
|
||||
[NotNullWhen(true)] out string? path
|
||||
)
|
||||
{
|
||||
version = null;
|
||||
path = null;
|
||||
|
||||
string dotNetExe = FindDotNetExe();
|
||||
string? dotNetExe = FindDotNetExe();
|
||||
|
||||
if (string.IsNullOrEmpty(dotNetExe))
|
||||
return false;
|
||||
|
@ -86,8 +84,8 @@ namespace GodotTools.Build
|
|||
process.BeginOutputReadLine();
|
||||
process.WaitForExit();
|
||||
|
||||
Version latestVersionMatch = null;
|
||||
string matchPath = null;
|
||||
Version? latestVersionMatch = null;
|
||||
string? matchPath = null;
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
|
|
|
@ -5,8 +5,6 @@ using GodotTools.Internals;
|
|||
using static GodotTools.Internals.Globals;
|
||||
using File = GodotTools.Utils.File;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GodotTools.Build
|
||||
{
|
||||
public partial class MSBuildPanel : MarginContainer, ISerializationListener
|
||||
|
@ -183,7 +181,7 @@ namespace GodotTools.Build
|
|||
}
|
||||
}
|
||||
|
||||
private void StdOutputReceived(string text)
|
||||
private void StdOutputReceived(string? text)
|
||||
{
|
||||
lock (_pendingBuildLogTextLock)
|
||||
{
|
||||
|
@ -193,7 +191,7 @@ namespace GodotTools.Build
|
|||
}
|
||||
}
|
||||
|
||||
private void StdErrorReceived(string text)
|
||||
private void StdErrorReceived(string? text)
|
||||
{
|
||||
lock (_pendingBuildLogTextLock)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
@ -73,7 +74,7 @@ namespace GodotTools.Export
|
|||
};
|
||||
}
|
||||
|
||||
private string _maybeLastExportError;
|
||||
private string? _maybeLastExportError;
|
||||
|
||||
// With this method we can override how a file is exported in the PCK
|
||||
public override void _ExportFile(string path, string type, string[] features)
|
||||
|
@ -135,7 +136,7 @@ namespace GodotTools.Export
|
|||
if (!ProjectContainsDotNet())
|
||||
return;
|
||||
|
||||
if (!DeterminePlatformFromFeatures(features, out string platform))
|
||||
if (!DeterminePlatformFromFeatures(features, out string? platform))
|
||||
throw new NotSupportedException("Target platform not supported.");
|
||||
|
||||
if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS }
|
||||
|
@ -327,7 +328,7 @@ namespace GodotTools.Export
|
|||
AddSharedObject(path, tags: null,
|
||||
Path.Join(projectDataDirName,
|
||||
Path.GetRelativePath(publishOutputDir,
|
||||
Path.GetDirectoryName(path))));
|
||||
Path.GetDirectoryName(path)!)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -450,7 +451,7 @@ namespace GodotTools.Export
|
|||
}
|
||||
}
|
||||
|
||||
private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
|
||||
private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, [NotNullWhen(true)] out string? platform)
|
||||
{
|
||||
foreach (var feature in features)
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace GodotTools.Export
|
|||
{
|
||||
public static class XcodeHelper
|
||||
{
|
||||
private static string _XcodePath = null;
|
||||
private static string? _XcodePath = null;
|
||||
|
||||
public static string XcodePath
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ namespace GodotTools.Export
|
|||
}
|
||||
}
|
||||
|
||||
private static string FindSelectedXcode()
|
||||
private static string? FindSelectedXcode()
|
||||
{
|
||||
var outputWrapper = new Godot.Collections.Array();
|
||||
|
||||
|
@ -40,9 +40,9 @@ namespace GodotTools.Export
|
|||
return null;
|
||||
}
|
||||
|
||||
public static string FindXcode()
|
||||
public static string? FindXcode()
|
||||
{
|
||||
string selectedXcode = FindSelectedXcode();
|
||||
string? selectedXcode = FindSelectedXcode();
|
||||
if (selectedXcode != null)
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(selectedXcode, "Contents", "Developer")))
|
||||
|
@ -50,10 +50,10 @@ namespace GodotTools.Export
|
|||
|
||||
// The path already pointed to Contents/Developer
|
||||
var dirInfo = new DirectoryInfo(selectedXcode);
|
||||
if (dirInfo.Name != "Developer" || dirInfo.Parent.Name != "Contents")
|
||||
if (dirInfo is not { Parent.Name: "Contents", Name: "Developer" })
|
||||
{
|
||||
Console.WriteLine(Path.GetDirectoryName(selectedXcode));
|
||||
Console.WriteLine(System.IO.Directory.GetParent(selectedXcode).Name);
|
||||
Console.WriteLine(System.IO.Directory.GetParent(selectedXcode)?.Name);
|
||||
Console.Error.WriteLine("Unrecognized path for selected Xcode");
|
||||
}
|
||||
else
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace GodotTools
|
|||
public const string ProblemsLayout = "dotnet/build/problems_layout";
|
||||
}
|
||||
|
||||
#nullable disable
|
||||
private EditorSettings _editorSettings;
|
||||
|
||||
private PopupMenu _menuPopup;
|
||||
|
@ -51,6 +52,7 @@ namespace GodotTools
|
|||
public GodotIdeManager GodotIdeManager { get; private set; }
|
||||
|
||||
public MSBuildPanel MSBuildPanel { get; private set; }
|
||||
#nullable enable
|
||||
|
||||
public bool SkipBuildBeforePlaying { get; set; } = false;
|
||||
|
||||
|
@ -67,29 +69,23 @@ namespace GodotTools
|
|||
|
||||
private bool CreateProjectSolution()
|
||||
{
|
||||
string errorMessage = null;
|
||||
string? errorMessage = null;
|
||||
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2))
|
||||
{
|
||||
pr.Step("Generating C# project...".TTR());
|
||||
|
||||
string csprojDir = Path.GetDirectoryName(GodotSharpDirs.ProjectCsProjPath);
|
||||
string slnDir = Path.GetDirectoryName(GodotSharpDirs.ProjectSlnPath);
|
||||
string csprojDir = Path.GetDirectoryName(GodotSharpDirs.ProjectCsProjPath)!;
|
||||
string slnDir = Path.GetDirectoryName(GodotSharpDirs.ProjectSlnPath)!;
|
||||
string name = GodotSharpDirs.ProjectAssemblyName;
|
||||
string guid = CsProjOperations.GenerateGameProject(csprojDir, name);
|
||||
|
||||
if (guid.Length > 0)
|
||||
{
|
||||
var solution = new DotNetSolution(name)
|
||||
{
|
||||
DirectoryPath = slnDir
|
||||
};
|
||||
var solution = new DotNetSolution(name, slnDir);
|
||||
|
||||
var projectInfo = new DotNetSolution.ProjectInfo
|
||||
{
|
||||
Guid = guid,
|
||||
PathRelativeToSolution = Path.GetRelativePath(slnDir, GodotSharpDirs.ProjectCsProjPath),
|
||||
Configs = new List<string> { "Debug", "ExportDebug", "ExportRelease" }
|
||||
};
|
||||
var projectInfo = new DotNetSolution.ProjectInfo(guid,
|
||||
Path.GetRelativePath(slnDir, GodotSharpDirs.ProjectCsProjPath),
|
||||
new List<string> { "Debug", "ExportDebug", "ExportRelease" });
|
||||
|
||||
solution.AddNewProject(name, projectInfo);
|
||||
|
||||
|
@ -342,7 +338,7 @@ namespace GodotTools
|
|||
}
|
||||
}
|
||||
|
||||
args.Add(Path.GetDirectoryName(GodotSharpDirs.ProjectSlnPath));
|
||||
args.Add(Path.GetDirectoryName(GodotSharpDirs.ProjectSlnPath)!);
|
||||
|
||||
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
|
||||
|
||||
|
@ -459,7 +455,7 @@ namespace GodotTools
|
|||
|
||||
// First we try to find the .NET Sdk ourselves to make sure we get the
|
||||
// correct version first, otherwise pick the latest.
|
||||
if (DotNetFinder.TryFindDotNetSdk(dotNetSdkSearchVersion, out var sdkVersion, out string sdkPath))
|
||||
if (DotNetFinder.TryFindDotNetSdk(dotNetSdkSearchVersion, out var sdkVersion, out string? sdkPath))
|
||||
{
|
||||
if (Godot.OS.IsStdOutVerbose())
|
||||
Console.WriteLine($"Found .NET Sdk version '{sdkVersion}': {sdkPath}");
|
||||
|
@ -694,8 +690,9 @@ namespace GodotTools
|
|||
}
|
||||
|
||||
// Singleton
|
||||
|
||||
#nullable disable
|
||||
public static GodotSharpEditor Instance { get; private set; }
|
||||
#nullable enable
|
||||
|
||||
[UsedImplicitly]
|
||||
private static IntPtr InternalCreateInstance(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<TargetFramework>net6.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<!-- The Godot editor uses the Debug Godot API assemblies -->
|
||||
<GodotApiConfiguration>Debug</GodotApiConfiguration>
|
||||
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
|
||||
|
|
|
@ -7,7 +7,9 @@ namespace GodotTools
|
|||
{
|
||||
public partial class HotReloadAssemblyWatcher : Node
|
||||
{
|
||||
#nullable disable
|
||||
private Timer _watchTimer;
|
||||
#nullable enable
|
||||
|
||||
public override void _Notification(int what)
|
||||
{
|
||||
|
|
|
@ -10,10 +10,10 @@ namespace GodotTools.Ides
|
|||
{
|
||||
public sealed partial class GodotIdeManager : Node, ISerializationListener
|
||||
{
|
||||
private MessagingServer _messagingServer;
|
||||
private MessagingServer? _messagingServer;
|
||||
|
||||
private MonoDevelop.Instance _monoDevelInstance;
|
||||
private MonoDevelop.Instance _vsForMacInstance;
|
||||
private MonoDevelop.Instance? _monoDevelInstance;
|
||||
private MonoDevelop.Instance? _vsForMacInstance;
|
||||
|
||||
private MessagingServer GetRunningOrNewServer()
|
||||
{
|
||||
|
@ -59,7 +59,7 @@ namespace GodotTools.Ides
|
|||
switch (editorId)
|
||||
{
|
||||
case ExternalEditorId.None:
|
||||
return null;
|
||||
return string.Empty;
|
||||
case ExternalEditorId.VisualStudio:
|
||||
return "VisualStudio";
|
||||
case ExternalEditorId.VsCode:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
@ -97,9 +98,9 @@ namespace GodotTools.Ides
|
|||
foreach (var connection in Peers)
|
||||
connection.Dispose();
|
||||
Peers.Clear();
|
||||
_listener?.Stop();
|
||||
_listener.Stop();
|
||||
|
||||
_metaFile?.Dispose();
|
||||
_metaFile.Dispose();
|
||||
|
||||
File.Delete(_metaFilePath);
|
||||
}
|
||||
|
@ -122,7 +123,7 @@ namespace GodotTools.Ides
|
|||
_listener = new TcpListener(new IPEndPoint(IPAddress.Loopback, port: 0));
|
||||
_listener.Start();
|
||||
|
||||
int port = ((IPEndPoint)_listener.Server.LocalEndPoint).Port;
|
||||
int port = ((IPEndPoint?)_listener.Server.LocalEndPoint)?.Port ?? 0;
|
||||
using (var metaFileWriter = new StreamWriter(_metaFile, Encoding.UTF8))
|
||||
{
|
||||
metaFileWriter.WriteLine(port);
|
||||
|
@ -235,7 +236,7 @@ namespace GodotTools.Ides
|
|||
|
||||
public string GetHandshakeLine(string identity) => $"{_serverHandshakeBase},{identity}";
|
||||
|
||||
public bool IsValidPeerHandshake(string handshake, out string identity, ILogger logger)
|
||||
public bool IsValidPeerHandshake(string handshake, [NotNullWhen(true)] out string? identity, ILogger logger)
|
||||
{
|
||||
identity = null;
|
||||
|
||||
|
@ -311,12 +312,12 @@ namespace GodotTools.Ides
|
|||
[DebugPlayRequest.Id] = async (peer, content) =>
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<DebugPlayRequest>(content.Body);
|
||||
return await HandleDebugPlay(request);
|
||||
return await HandleDebugPlay(request!);
|
||||
},
|
||||
[StopPlayRequest.Id] = async (peer, content) =>
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<StopPlayRequest>(content.Body);
|
||||
return await HandleStopPlay(request);
|
||||
return await HandleStopPlay(request!);
|
||||
},
|
||||
[ReloadScriptsRequest.Id] = async (peer, content) =>
|
||||
{
|
||||
|
@ -326,7 +327,7 @@ namespace GodotTools.Ides
|
|||
[CodeCompletionRequest.Id] = async (peer, content) =>
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<CodeCompletionRequest>(content.Body);
|
||||
return await HandleCodeCompletionRequest(request);
|
||||
return await HandleCodeCompletionRequest(request!);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -383,7 +384,7 @@ namespace GodotTools.Ides
|
|||
{
|
||||
// This is needed if the "resource path" part of the path is case insensitive.
|
||||
// However, it doesn't fix resource loading if the rest of the path is also case insensitive.
|
||||
string scriptFileLocalized = FsPathUtils.LocalizePathWithCaseChecked(request.ScriptFile);
|
||||
string? scriptFileLocalized = FsPathUtils.LocalizePathWithCaseChecked(request.ScriptFile);
|
||||
|
||||
// The node API can only be called from the main thread.
|
||||
await Godot.Engine.GetMainLoop().ToSignal(Godot.Engine.GetMainLoop(), "process_frame");
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace GodotTools.Ides.MonoDevelop
|
|||
private readonly string _solutionFile;
|
||||
private readonly EditorId _editorId;
|
||||
|
||||
private Process _process;
|
||||
private Process? _process;
|
||||
|
||||
public bool IsRunning => _process != null && !_process.HasExited;
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
@ -24,7 +24,7 @@ namespace GodotTools.Ides.MonoDevelop
|
|||
|
||||
var args = new List<string>();
|
||||
|
||||
string command;
|
||||
string? command;
|
||||
|
||||
if (OS.IsMacOS)
|
||||
{
|
||||
|
@ -136,6 +136,8 @@ namespace GodotTools.Ides.MonoDevelop
|
|||
{EditorId.MonoDevelop, "monodevelop"}
|
||||
};
|
||||
}
|
||||
ExecutableNames ??= new Dictionary<EditorId, string>();
|
||||
BundleIds ??= new Dictionary<EditorId, string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,12 @@ public class RiderLocatorEnvironment : IRiderLocatorEnvironment
|
|||
}
|
||||
}
|
||||
|
||||
public T FromJson<T>(string json)
|
||||
public T? FromJson<T>(string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
|
||||
public void Info(string message, Exception e = null)
|
||||
public void Info(string message, Exception? e = null)
|
||||
{
|
||||
if (e == null)
|
||||
GD.Print(message);
|
||||
|
@ -33,7 +33,7 @@ public class RiderLocatorEnvironment : IRiderLocatorEnvironment
|
|||
GD.Print(message, e);
|
||||
}
|
||||
|
||||
public void Warn(string message, Exception e = null)
|
||||
public void Warn(string message, Exception? e = null)
|
||||
{
|
||||
if (e == null)
|
||||
GD.PushWarning(message);
|
||||
|
@ -41,7 +41,7 @@ public class RiderLocatorEnvironment : IRiderLocatorEnvironment
|
|||
GD.PushWarning(message, e);
|
||||
}
|
||||
|
||||
public void Error(string message, Exception e = null)
|
||||
public void Error(string message, Exception? e = null)
|
||||
{
|
||||
if (e == null)
|
||||
GD.PushError(message);
|
||||
|
@ -49,7 +49,7 @@ public class RiderLocatorEnvironment : IRiderLocatorEnvironment
|
|||
GD.PushError(message, e);
|
||||
}
|
||||
|
||||
public void Verbose(string message, Exception e = null)
|
||||
public void Verbose(string message, Exception? e = null)
|
||||
{
|
||||
// do nothing, since IDK how to write only to the log, without spamming the output
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ using Godot;
|
|||
using GodotTools.Internals;
|
||||
using JetBrains.Rider.PathLocator;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GodotTools.Ides.Rider
|
||||
{
|
||||
public static class RiderPathManager
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using Godot;
|
||||
using Godot.NativeInterop;
|
||||
|
@ -59,16 +60,17 @@ namespace GodotTools.Internals
|
|||
}
|
||||
}
|
||||
|
||||
[MemberNotNull("_projectAssemblyName", "_projectSlnPath", "_projectCsProjPath")]
|
||||
public static void DetermineProjectLocation()
|
||||
{
|
||||
_projectAssemblyName = (string)ProjectSettings.GetSetting("dotnet/project/assembly_name");
|
||||
_projectAssemblyName = (string?)ProjectSettings.GetSetting("dotnet/project/assembly_name");
|
||||
if (string.IsNullOrEmpty(_projectAssemblyName))
|
||||
{
|
||||
_projectAssemblyName = CSharpProjectName;
|
||||
ProjectSettings.SetSetting("dotnet/project/assembly_name", _projectAssemblyName);
|
||||
}
|
||||
|
||||
string slnParentDir = (string)ProjectSettings.GetSetting("dotnet/project/solution_directory");
|
||||
string? slnParentDir = (string?)ProjectSettings.GetSetting("dotnet/project/solution_directory");
|
||||
if (string.IsNullOrEmpty(slnParentDir))
|
||||
slnParentDir = "res://";
|
||||
else if (!slnParentDir.StartsWith("res://"))
|
||||
|
@ -84,9 +86,9 @@ namespace GodotTools.Internals
|
|||
string.Concat(_projectAssemblyName, ".csproj"));
|
||||
}
|
||||
|
||||
private static string _projectAssemblyName;
|
||||
private static string _projectSlnPath;
|
||||
private static string _projectCsProjPath;
|
||||
private static string? _projectAssemblyName;
|
||||
private static string? _projectSlnPath;
|
||||
private static string? _projectCsProjPath;
|
||||
|
||||
public static string ProjectAssemblyName
|
||||
{
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
|
||||
namespace GodotTools.Utils
|
||||
{
|
||||
public static class CollectionExtensions
|
||||
{
|
||||
public static T SelectFirstNotNull<T>(this IEnumerable<T> enumerable, Func<T, T> predicate, T orElse = null)
|
||||
[return: NotNullIfNotNull("orElse")]
|
||||
public static T? SelectFirstNotNull<T>(this IEnumerable<T> enumerable, Func<T, T?> predicate, T? orElse = null)
|
||||
where T : class
|
||||
{
|
||||
foreach (T elem in enumerable)
|
||||
{
|
||||
T result = predicate(elem);
|
||||
T? result = predicate(elem);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
|
@ -21,7 +23,7 @@ namespace GodotTools.Utils
|
|||
|
||||
public static IEnumerable<string> EnumerateLines(this TextReader textReader)
|
||||
{
|
||||
string line;
|
||||
string? line;
|
||||
while ((line = textReader.ReadLine()) != null)
|
||||
yield return line;
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@ namespace GodotTools.Utils
|
|||
return childPathNorm.PathStartsWithAlreadyNorm(parentPathNorm);
|
||||
}
|
||||
|
||||
[return: MaybeNull]
|
||||
public static string LocalizePathWithCaseChecked(string path)
|
||||
public static string? LocalizePathWithCaseChecked(string path)
|
||||
{
|
||||
string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar;
|
||||
string resourcePathNorm = ResourcePath.NormalizePath() + Path.DirectorySeparatorChar;
|
||||
|
|
|
@ -150,8 +150,7 @@ namespace GodotTools.Utils
|
|||
|
||||
public static char PathSep => IsWindows ? ';' : ':';
|
||||
|
||||
[return: MaybeNull]
|
||||
public static string PathWhich([NotNull] string name)
|
||||
public static string? PathWhich(string name)
|
||||
{
|
||||
if (IsWindows)
|
||||
return PathWhichWindows(name);
|
||||
|
@ -159,12 +158,11 @@ namespace GodotTools.Utils
|
|||
return PathWhichUnix(name);
|
||||
}
|
||||
|
||||
[return: MaybeNull]
|
||||
private static string PathWhichWindows([NotNull] string name)
|
||||
private static string? PathWhichWindows(string name)
|
||||
{
|
||||
string[] windowsExts =
|
||||
Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
|
||||
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
|
||||
string[]? pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
|
||||
char[] invalidPathChars = Path.GetInvalidPathChars();
|
||||
|
||||
var searchDirs = new List<string>();
|
||||
|
@ -196,10 +194,9 @@ namespace GodotTools.Utils
|
|||
select path + ext).FirstOrDefault(File.Exists);
|
||||
}
|
||||
|
||||
[return: MaybeNull]
|
||||
private static string PathWhichUnix([NotNull] string name)
|
||||
private static string? PathWhichUnix(string name)
|
||||
{
|
||||
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
|
||||
string[]? pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
|
||||
char[] invalidPathChars = Path.GetInvalidPathChars();
|
||||
|
||||
var searchDirs = new List<string>();
|
||||
|
@ -238,7 +235,7 @@ namespace GodotTools.Utils
|
|||
foreach (string arg in arguments)
|
||||
startInfo.ArgumentList.Add(arg);
|
||||
|
||||
using Process process = Process.Start(startInfo);
|
||||
using Process? process = Process.Start(startInfo);
|
||||
|
||||
if (process == null)
|
||||
throw new InvalidOperationException("No process was started.");
|
||||
|
@ -315,7 +312,7 @@ namespace GodotTools.Utils
|
|||
|
||||
public static StringBuilder GetCommandLineDisplay(
|
||||
this ProcessStartInfo startInfo,
|
||||
StringBuilder optionalBuilder = null
|
||||
StringBuilder? optionalBuilder = null
|
||||
)
|
||||
{
|
||||
var builder = optionalBuilder ?? new StringBuilder();
|
||||
|
|
Loading…
Reference in New Issue