forked from microsoft/qsharp-compiler
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUtils.cs
246 lines (215 loc) · 9.93 KB
/
Utils.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Quantum.QsCompiler;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace Microsoft.Quantum.QsLanguageServer
{
// NB: The enum Microsoft.VisualStudio.LanguageServer.Protocol.ResourceOperationKind
// has some corrupt serialization attributes in some versions, so we
// need to ignore its custom JSON converter in favor of one that
// we define here. As per https://stackoverflow.com/questions/45643903/anyway-to-get-jsonconvert-serializeobject-to-ignore-the-jsonconverter-attribute
// one way to do so is to define our own custom contract resolver
// that ignores metadata on any property of type ResourceOperationKind.
internal sealed class ResourceOperationKindContractResolver : DefaultContractResolver
{
private readonly ResourceOperationKindConverter rokConverter = new ResourceOperationKindConverter();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.PropertyType == typeof(ResourceOperationKind[]))
{
property.Converter = this.rokConverter;
}
return property;
}
}
internal class ResourceOperationKindConverter : JsonConverter<ResourceOperationKind[]>
{
public override ResourceOperationKind[] ReadJson(JsonReader reader, Type objectType, ResourceOperationKind[]? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartArray)
{
throw new JsonSerializationException($"Expected array start, got {reader.TokenType}.");
}
var values = new List<ResourceOperationKind>();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
{
break;
}
values.Add(reader.Value switch
{
"create" => ResourceOperationKind.Create,
"delete" => ResourceOperationKind.Delete,
"rename" => ResourceOperationKind.Delete,
var badValue => throw new JsonSerializationException($"Could not deserialize {badValue} as ResourceOperationKind."),
});
}
return values.ToArray();
}
public override void WriteJson(JsonWriter writer, ResourceOperationKind[]? value, JsonSerializer serializer)
{
writer.WriteStartArray();
foreach (var element in value ?? Array.Empty<ResourceOperationKind>())
{
writer.WriteValue(element switch
{
ResourceOperationKind.Create => "create",
ResourceOperationKind.Delete => "delete",
ResourceOperationKind.Rename => "rename",
_ => throw new JsonSerializationException($"Could not serialize {value} as ResourceOperationKind."),
});
}
writer.WriteEndArray();
}
}
public static class Utils
{
/* language server tools -
* wrapping these into a try .. catch .. to make sure errors don't go unnoticed as they otherwise would
*/
public static readonly JsonSerializer JsonSerializer = new JsonSerializer()
{
ContractResolver = new ResourceOperationKindContractResolver(),
};
public static T? TryJTokenAs<T>(JToken arg)
where T : class =>
QsCompilerError.RaiseOnFailure(() => arg.ToObject<T>(JsonSerializer), "could not cast given JToken");
private static ShowMessageParams? AsMessageParams(string text, MessageType severity) =>
text == null ? null : new ShowMessageParams { Message = text, MessageType = severity };
/// <summary>
/// Shows the given text in the editor.
/// </summary>
internal static void ShowInWindow(this QsLanguageServer server, string text, MessageType severity)
{
var message = AsMessageParams(text, severity);
QsCompilerError.Verify(server != null && message != null, "cannot show message - given server or text was null");
_ = server.NotifyClientAsync(Methods.WindowShowMessageName, message);
}
/// <summary>
/// Shows a dialog window with options (actions) to the user, and returns the selected option (action).
/// </summary>
internal static async Task<MessageActionItem> ShowDialogInWindowAsync(this QsLanguageServer server, string text, MessageType severity, MessageActionItem[] actionItems)
{
var message =
new ShowMessageRequestParams()
{
Message = text,
MessageType = severity,
Actions = actionItems,
};
return await server.InvokeAsync<MessageActionItem>(Methods.WindowShowMessageRequestName, message);
}
/// <summary>
/// Logs the given text in the editor.
/// </summary>
internal static void LogToWindow(this QsLanguageServer server, string text, MessageType severity)
{
var message = AsMessageParams(text, severity);
QsCompilerError.Verify(server != null && message != null, "cannot log message - given server or text was null");
_ = server.NotifyClientAsync(Methods.WindowLogMessageName, message);
}
// tools related to project loading and file watching
/// <summary>
/// Attempts to apply the given mapper to each element in the given sequence.
/// Returns a new sequence consisting of all mapped elements for which the mapping succeeded as out parameter,
/// as well as a bool indicating whether the mapping succeeded for all elements.
/// The returned out parameter is non-null even if the mapping failed on some elements.
/// </summary>
internal static bool TryEnumerate<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> mapper,
out ImmutableArray<TResult> mapped)
{
var succeeded = true;
var enumerator = source.GetEnumerator();
T Try<T>(Func<T> getRes, T fallback)
{
try
{
return getRes();
}
catch
{
succeeded = false;
return fallback;
}
}
bool TryMoveNext() => Try(enumerator.MoveNext, false);
(bool, TResult) ApplyToCurrent() => Try(() => (true, mapper(enumerator.Current)), (false, default!));
var values = ImmutableArray.CreateBuilder<TResult>();
while (TryMoveNext())
{
var evaluated = ApplyToCurrent();
if (evaluated.Item1)
{
values.Add(evaluated.Item2);
}
}
mapped = values.ToImmutable();
return succeeded;
}
/// <summary>
/// Attempts to enumerate the given sequence.
/// Returns a new sequence consisting of all elements which could be accessed,
/// as well as a bool indicating whether the enumeration succeeded for all elements.
/// The returned out parameter is non-null even if access failed on some elements.
/// </summary>
internal static bool TryEnumerate<TSource>(this IEnumerable<TSource> source, out ImmutableArray<TSource> enumerated) =>
source.TryEnumerate(element => element, out enumerated);
/// <summary>
/// The given log function is applied to all errors and warning
/// raised by the ms build routine an instance of this class is given to.
/// </summary>
internal class MSBuildLogger : Logger
{
private readonly Action<string, MessageType> logToWindow;
internal MSBuildLogger(Action<string, MessageType> logToWindow) =>
this.logToWindow = logToWindow;
public override void Initialize(IEventSource eventSource)
{
eventSource.ErrorRaised += (sender, args) =>
this.logToWindow?.Invoke(
$"MSBuild error in {args.File}({args.LineNumber},{args.ColumnNumber}): {args.Message}",
MessageType.Error);
eventSource.WarningRaised += (sender, args) =>
this.logToWindow?.Invoke(
$"MSBuild warning in {args.File}({args.LineNumber},{args.ColumnNumber}): {args.Message}",
MessageType.Warning);
}
}
}
internal static class DotNetSdkHelper
{
private static readonly Regex DotNetVersionRegex = new Regex(@"^6\.0\.\d+", RegexOptions.Multiline | RegexOptions.Compiled);
public static bool? IsDotNet6Installed()
{
var process = Process.Start(new ProcessStartInfo
{
FileName = "dotnet",
Arguments = "--list-sdks",
RedirectStandardOutput = true,
});
if (process?.WaitForExit(3000) != true || process.ExitCode != 0)
{
return null;
}
var sdks = process.StandardOutput.ReadToEnd();
return DotNetVersionRegex.IsMatch(sdks);
}
}
}