Merge branch '1.5' into dotnet7

This commit is contained in:
Wojciech Figat
2023-01-23 14:47:12 +01:00
9 changed files with 387 additions and 122 deletions

View File

@@ -156,88 +156,126 @@ namespace FlaxEditor.CustomEditors.Editors
pickerData.IsEditing = false;
}
private static void OnAddButtonClicked(Tree tree, TreeNode parentNode, PickerData pickerData)
private static void OnAddTagButtonClicked(string input, Tree tree, TextBox textBox, DropPanel dropPanel, PickerData pickerData)
{
// Create new node
var nodeIndent = 16.0f;
var indentation = 0;
var parentTag = string.Empty;
if (parentNode.CustomArrowRect.HasValue)
{
indentation = (int)((parentNode.CustomArrowRect.Value.Location.X - 18) / nodeIndent) + 1;
var parentTagValue = (Tag)parentNode.Tag;
parentTag = parentTagValue.ToString();
}
var node = new TreeNodeWithAddons
{
ChildrenIndent = nodeIndent,
CullChildren = false,
ClipChildren = false,
TextMargin = new Margin(22.0f, 2.0f, 2.0f, 2.0f),
CustomArrowRect = new Rectangle(18 + indentation * nodeIndent, 2, 12, 12),
BackgroundColorSelected = Color.Transparent,
BackgroundColorHighlighted = parentNode.BackgroundColorHighlighted,
};
var checkbox = new CheckBox(32.0f + indentation * nodeIndent, 0)
{
Height = 16.0f,
IsScrollable = false,
Parent = node,
};
node.Addons.Add(checkbox);
checkbox.StateChanged += c => OnCheckboxEdited(node, c, pickerData);
var addButton = new Button(tree.Width - 16, 0, 14, 14)
{
Text = "+",
TooltipText = "Add subtag within this tag namespace",
IsScrollable = false,
BorderColor = Color.Transparent,
BackgroundColor = Color.Transparent,
Parent = node,
AnchorPreset = AnchorPresets.TopRight,
};
node.Addons.Add(addButton);
addButton.ButtonClicked += button => OnAddButtonClicked(tree, node, pickerData);
if (string.IsNullOrEmpty(input))
return;
// Link
node.Parent = parentNode;
node.IndexInParent = 0;
parentNode.Expand(true);
((Panel)tree.Parent.Parent).ScrollViewTo(node);
// Ensure that name is unique
if (Tags.List.Contains(input))
return;
// Start renaming the tag
var prefix = parentTag.Length != 0 ? parentTag + '.' : string.Empty;
var renameArea = node.HeaderRect;
if (renameArea.Location.X < nodeIndent)
{
// Fix root node's child renaming
renameArea.Location.X += nodeIndent;
renameArea.Size.X -= nodeIndent;
}
var dialog = RenamePopup.Show(node, renameArea, prefix, false);
var cursor = dialog.InputField.TextLength;
dialog.InputField.SelectionRange = new TextRange(cursor, cursor);
dialog.Validate = (popup, value) =>
{
// Ensure that name is unique
if (Tags.List.Contains(value))
return false;
var subInputs = input.Split('.');
string tagString = string.Empty;
string lastTagString = string.Empty;
// Ensure user entered direct subtag of the parent node
if (value.StartsWith(popup.InitialValue))
TreeNode parentNode = tree.GetChild<TreeNode>(); // Start at root
int parentCount = 0;
for (int i = 0; i < subInputs.Length; i++)
{
if (string.IsNullOrEmpty(subInputs[i]))
{
var name = value.Substring(popup.InitialValue.Length);
if (name.Length > 0 && name.IndexOf('.') == -1)
return true;
continue;
}
return false;
};
dialog.Renamed += popup =>
{
// Check all entered subtags and create any that dont exist
for (int j = 0; j <= i; j++)
{
tagString += j == 0 ? subInputs[j] : "." + subInputs[j];
}
if (string.IsNullOrEmpty(tagString))
{
tagString = string.Empty;
continue;
}
if (Tags.List.Contains(tagString))
{
// Find next parent node
foreach (var child in parentNode.Children)
{
if (!(child is TreeNode childNode))
continue;
var tagValue = (Tag)childNode.Tag;
if (!string.Equals(tagValue.ToString(), tagString))
continue;
parentNode = childNode;
parentCount += 1;
}
lastTagString = tagString;
tagString = string.Empty;
continue;
}
else if (subInputs.Length > 1)
{
// Find next parent node
foreach (var child in parentNode.Children)
{
if (!(child is TreeNode childNode))
continue;
var tagValue = (Tag)childNode.Tag;
if (!string.Equals(tagValue.ToString(), lastTagString))
continue;
parentNode = childNode;
parentCount += 1;
}
}
// Create new node
var nodeIndent = 16.0f;
var indentation = 0;
var parentTag = string.Empty;
if (parentNode.CustomArrowRect.HasValue)
{
indentation = (int)((parentNode.CustomArrowRect.Value.Location.X - 18) / nodeIndent) + 1;
var parentTagValue = (Tag)parentNode.Tag;
parentTag = parentTagValue.ToString();
}
var node = new TreeNodeWithAddons
{
ChildrenIndent = nodeIndent,
CullChildren = false,
ClipChildren = false,
TextMargin = new Margin(22.0f, 2.0f, 2.0f, 2.0f),
CustomArrowRect = new Rectangle(18 + indentation * nodeIndent, 2, 12, 12),
BackgroundColorSelected = Color.Transparent,
BackgroundColorHighlighted = parentNode.BackgroundColorHighlighted,
};
var checkbox = new CheckBox(32.0f + indentation * nodeIndent, 0)
{
Height = 16.0f,
IsScrollable = false,
Parent = node,
};
node.Addons.Add(checkbox);
checkbox.StateChanged += c => OnCheckboxEdited(node, c, pickerData);
var addButton = new Button(tree.Width - 16, 0, 14, 14)
{
Text = "+",
TooltipText = "Add subtag within this tag namespace",
IsScrollable = false,
BorderColor = Color.Transparent,
BackgroundColor = Color.Transparent,
Parent = node,
AnchorPreset = AnchorPresets.TopRight,
};
node.Addons.Add(addButton);
addButton.ButtonClicked += button => OnAddSubTagButtonClicked(((Tag)node.Tag).ToString(), textBox, dropPanel);
// Link
node.Parent = parentNode;
node.IndexInParent = 0;
parentNode.Expand(true);
((Panel)tree.Parent.Parent).ScrollViewTo(node);
// Get tag name
var tagName = popup.Text;
var tagShortName = tagName.Substring(popup.InitialValue.Length);
var tagName = tagString;
var tagShortName = tagName.Substring(parentCount == 0 ? lastTagString.Length : lastTagString.Length + 1);
if (tagShortName.Length == 0)
return;
@@ -256,24 +294,134 @@ namespace FlaxEditor.CustomEditors.Editors
// Update asset
var settingsObj = (LayersAndTagsSettings)settingsAsset.Instance;
settingsObj.Tags.Add(tagName);
settingsObj.Tags.Sort();
settingsAsset.SetInstance(settingsObj);
// Reload editor window to reflect new tag
assetWindow?.RefreshAsset();
}
};
dialog.Closed += popup =>
lastTagString = tagString;
tagString = string.Empty;
}
textBox.Text = string.Empty;
}
private static void OnAddSubTagButtonClicked(string parentTag, TextBox textBox, DropPanel dropPanel)
{
if (textBox == null || dropPanel == null || string.IsNullOrEmpty(parentTag))
{
// Remove temporary node if renaming was canceled
if (popup.InitialValue == popup.Text || popup.Text.Length == 0)
node.Dispose();
};
return;
}
dropPanel.Open();
textBox.Text = parentTag + ".";
textBox.Focus();
textBox.SelectionRange = new TextRange(textBox.Text.Length, textBox.Text.Length);
}
internal static ContextMenuBase CreatePicker(Tag value, Tag[] values, PickerData pickerData)
{
// Initialize search popup
var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 20.0f);
var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 40.0f);
// Add tag drop panel
var addTagDropPanel = new DropPanel
{
HeaderText = "Add Tag",
EnableDropDownIcon = true,
ArrowImageOpened = new SpriteBrush(FlaxEngine.GUI.Style.Current.ArrowDown),
ArrowImageClosed = new SpriteBrush(FlaxEngine.GUI.Style.Current.ArrowRight),
Parent = menu,
HeaderTextMargin = new Margin(2.0f),
AnchorPreset = AnchorPresets.TopLeft,
Bounds = new Rectangle(2, 2, menu.Width - 4, 30),
IsClosed = true,
ItemsMargin = new Margin(2.0f),
CloseAnimationTime = 0,
};
var tagNamePanel = new HorizontalPanel
{
Parent = addTagDropPanel,
AutoSize = false,
Bounds = new Rectangle(0, 0, addTagDropPanel.Width, 20.0f),
};
var nameLabel = new Label
{
Size = new Float2(addTagDropPanel.Width / 4, 0),
Parent = tagNamePanel,
Text = "Tag Name:",
};
var nameTextBox = new TextBox
{
IsMultiline = false,
WatermarkText = "X.Y.Z",
Size = new Float2(addTagDropPanel.Width * 0.7f, 0),
Parent = tagNamePanel,
EndEditOnClick = false,
};
bool uniqueText = true;
nameTextBox.TextChanged += () =>
{
// Ensure that name is unique
if (Tags.List.Contains(nameTextBox.Text))
uniqueText = false;
else
uniqueText = true;
var currentStyle = FlaxEngine.GUI.Style.Current;
if (uniqueText)
{
nameTextBox.BorderColor = Color.Transparent;
nameTextBox.BorderSelectedColor = currentStyle.BackgroundSelected;
}
else
{
var color = new Color(1.0f, 0.0f, 0.02745f, 1.0f);
nameTextBox.BorderColor = Color.Lerp(color, currentStyle.TextBoxBackground, 0.6f);
nameTextBox.BorderSelectedColor = color;
}
};
nameTextBox.EditEnd += () =>
{
if (!uniqueText)
return;
if (addTagDropPanel.IsClosed)
{
Debug.Log("Hit");
nameTextBox.BorderColor = Color.Transparent;
nameTextBox.BorderSelectedColor = FlaxEngine.GUI.Style.Current.BackgroundSelected;
return;
}
OnAddTagButtonClicked(nameTextBox.Text, tree, nameTextBox, addTagDropPanel, pickerData);
};
var addButtonPanel = new HorizontalPanel
{
Parent = addTagDropPanel,
AutoSize = false,
Bounds = new Rectangle(0, 0, addTagDropPanel.Width, 30.0f),
};
var buttonAddTag = new Button
{
Parent = addButtonPanel,
Size = new Float2(100, 20),
Text = "Add Tag",
AnchorPreset = AnchorPresets.MiddleCenter,
};
buttonAddTag.Clicked += () =>
{
if (!uniqueText)
return;
OnAddTagButtonClicked(nameTextBox.Text, tree, nameTextBox, addTagDropPanel, pickerData);
};
// Used for how far everything should drop when the drop down panel is opened
var dropPanelOpenHeight = tagNamePanel.Height + addButtonPanel.Height + 4;
// Create tree with tags hierarchy
tree.Margin = new Margin(-16.0f, 0.0f, -16.0f, -0.0f); // Hide root node
@@ -306,7 +454,7 @@ namespace FlaxEditor.CustomEditors.Editors
tagShortName = tagName.Substring(lastDotIndex + 1);
tagParentName = tagName.Substring(0, lastDotIndex);
}
// Create node
var node = new TreeNodeWithAddons
{
@@ -339,7 +487,7 @@ namespace FlaxEditor.CustomEditors.Editors
AnchorPreset = AnchorPresets.TopRight,
};
node.Addons.Add(addButton);
addButton.ButtonClicked += button => OnAddButtonClicked(tree, node, pickerData);
addButton.ButtonClicked += button => OnAddSubTagButtonClicked(((Tag)node.Tag).ToString(), nameTextBox, addTagDropPanel);
// Link to parent
{
@@ -360,10 +508,13 @@ namespace FlaxEditor.CustomEditors.Editors
{
Margin = new Margin(1.0f),
AutoSize = false,
Bounds = new Rectangle(0, 0, menu.Width, 20.0f),
Bounds = new Rectangle(0, 0, menu.Width - 4, 20.0f),
Parent = menu,
};
var buttonsSize = new Float2((menu.Width - buttonsPanel.Margin.Width) / 4.0f - buttonsPanel.Spacing, 18.0f);
buttonsPanel.Y += addTagDropPanel.HeaderHeight + 4;
buttonsPanel.X += 2;
var buttonsSize = new Float2((menu.Width - 2 - buttonsPanel.Margin.Width) / 3.0f - buttonsPanel.Spacing, 18.0f);
var buttonExpandAll = new Button
{
Size = buttonsSize,
@@ -382,20 +533,13 @@ namespace FlaxEditor.CustomEditors.Editors
root.CollapseAll(true);
root.Expand(true);
};
var buttonAddTag = new Button
var buttonClearAll = new Button
{
Size = buttonsSize,
Parent = buttonsPanel,
Text = "Add Tag",
Text = "Clear all",
};
buttonAddTag.Clicked += () => OnAddButtonClicked(tree, root, pickerData);
var buttonReset = new Button
{
Size = buttonsSize,
Parent = buttonsPanel,
Text = "Reset",
};
buttonReset.Clicked += () =>
buttonClearAll.Clicked += () =>
{
pickerData.IsEditing = true;
UncheckAll(root);
@@ -404,6 +548,48 @@ namespace FlaxEditor.CustomEditors.Editors
pickerData.SetValues?.Invoke(null);
};
// Move menu children to location when drop panel is opened and closed
addTagDropPanel.IsClosedChanged += panel =>
{
if (panel.IsClosed)
{
// Resize/ Move UI to fit space with drop down
foreach (var child in menu.Children)
{
if (child == panel)
continue;
// Expand panels so scrollbars work
if (child is Panel)
{
child.Bounds = new Rectangle(child.Bounds.X, child.Bounds.Y - dropPanelOpenHeight, child.Width, child.Height + dropPanelOpenHeight);
continue;
}
child.Y -= dropPanelOpenHeight;
nameTextBox.Text = string.Empty;
nameTextBox.Defocus();
}
}
else
{
// Resize/ Move UI to fit space with drop down
foreach (var child in menu.Children)
{
if (child == panel)
continue;
// Shrink panels so scrollbars work
if (child is Panel)
{
child.Bounds = new Rectangle(child.Bounds.X, child.Bounds.Y + dropPanelOpenHeight, child.Width, child.Height - dropPanelOpenHeight);
continue;
}
child.Y += dropPanelOpenHeight;
nameTextBox.Text = string.Empty;
}
}
};
// Setup search filter
searchBox.TextChanged += delegate
{

View File

@@ -94,6 +94,7 @@ namespace FlaxEditor.Windows
"Lukáš Jech",
"Jean-Baptiste Perrier",
"Chandler Cox",
"Ari Vuollet",
});
authors.Sort();
var authorsLabel = new Label(4, topParentControl.Bottom + 20, Width - 8, 70)

View File

@@ -313,7 +313,12 @@ namespace FlaxEditor.Windows.Assets
/// </summary>
public void RefreshAsset()
{
_isWaitingForLoaded = true;
if (_asset == null || _asset.WaitForLoaded())
return;
OnAssetLoaded();
MarkAsEdited();
Save();
}
/// <summary>

View File

@@ -816,15 +816,23 @@ namespace FlaxEditor.Windows
}
// Selected UI controls outline
bool drawAnySelectedControl = false;
for (var i = 0; i < Editor.Instance.SceneEditing.Selection.Count; i++)
{
if (Editor.Instance.SceneEditing.Selection[i].EditableObject is UIControl controlActor && controlActor && controlActor.Control != null)
{
if (!drawAnySelectedControl)
{
drawAnySelectedControl = true;
Render2D.PushTransform(ref _viewport._cachedTransform);
}
var control = controlActor.Control;
var bounds = Rectangle.FromPoints(control.PointToParent(_viewport, Float2.Zero), control.PointToParent(_viewport, control.Size));
Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize);
}
}
if (drawAnySelectedControl)
Render2D.PopTransform();
// Play mode hints and overlay
if (Editor.StateMachine.IsPlayMode)

View File

@@ -231,7 +231,7 @@ public:
/// <param name="data">Data to copy.</param>
void Copy(const DataContainer& data)
{
if (data.IsValid())
if (data.Length() != 0)
Copy(data.Get(), data.Length());
else
Release();
@@ -243,7 +243,7 @@ public:
/// <param name="data">Data to copy.</param>
void Copy(const Span<T>& data)
{
if (data.IsValid())
if (data.Length() != 0)
Copy(data.Get(), data.Length());
else
Release();

View File

@@ -897,6 +897,13 @@ void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
rpc.Info = *info;
const Span<byte> argsData(argsStream->GetBuffer(), argsStream->GetPosition());
rpc.ArgsData.Copy(argsData);
#if USE_EDITOR || !BUILD_RELEASE
auto it = Objects.Find(obj->GetID());
if (it == Objects.End())
{
LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", type.ToString(), String(name), obj->GetID());
}
#endif
ObjectsLock.Unlock();
}
@@ -980,6 +987,7 @@ void NetworkInternal::NetworkReplicatorUpdate()
CachedWriteStream = New<NetworkStream>();
const bool isClient = NetworkManager::IsClient();
const bool isServer = NetworkManager::IsServer();
const bool isHost = NetworkManager::IsHost();
NetworkStream* stream = CachedWriteStream;
NetworkPeer* peer = NetworkManager::Peer;
@@ -1279,10 +1287,10 @@ void NetworkInternal::NetworkReplicatorUpdate()
// Client -> Server
peer->EndSendMessage(channel, msg);
}
else if (e.Info.Client && isServer)
else if (e.Info.Client && (isServer || isHost))
{
// Server -> Client(s)
BuildCachedTargets(item);
BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, NetworkManager::LocalClientId);
peer->EndSendMessage(channel, msg, CachedTargets);
}
}

View File

@@ -379,17 +379,11 @@ namespace Flax.Build
// Reference module output binary
fileReferences.Add(Path.Combine(outputPath, dependencyModule.BinaryModuleName + ".CSharp.dll"));
}
foreach (var e in buildData.ReferenceBuilds)
var referencedBuild = buildData.FinReferenceBuildModule(dependencyModule.BinaryModuleName);
if (referencedBuild != null && !string.IsNullOrEmpty(referencedBuild.ManagedPath))
{
foreach (var q in e.Value.BuildInfo.BinaryModules)
{
if (q.Name == dependencyModule.BinaryModuleName && !string.IsNullOrEmpty(q.ManagedPath))
{
// Reference binary module build build for referenced target
fileReferences.Add(q.ManagedPath);
break;
}
}
// Reference binary module build build for referenced target
fileReferences.Add(referencedBuild.ManagedPath);
}
}
}

View File

@@ -805,13 +805,13 @@ namespace Flax.Build.Plugins
{
if (property.GetMethod == null)
{
Log.Error($"Missing getter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.");
MonoCecil.CompilationError($"Missing getter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
failed = true;
return;
}
if (property.SetMethod == null)
{
Log.Error($"Missing setter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.");
MonoCecil.CompilationError($"Missing setter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
failed = true;
return;
}
@@ -1090,7 +1090,12 @@ namespace Flax.Build.Plugins
else
{
// Unknown type
Log.Error($"Not supported type '{valueType.FullName}' on {(field?.Name ?? property.Name)} in {type.FullName} for automatic replication.");
if (property != null)
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {property.Name} in {type.FullName} for automatic replication.", property);
else if (field != null)
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {field.Name} in {type.FullName} for automatic replication.", field.Resolve());
else
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' for automatic replication.");
failed = true;
}
}
@@ -1212,13 +1217,13 @@ namespace Flax.Build.Plugins
// Validate RPC usage
if (method.IsAbstract)
{
Log.Error($"Not supported abstract RPC method '{method.FullName}'.");
MonoCecil.CompilationError($"Not supported abstract RPC method '{method.FullName}'.", method);
failed = true;
return;
}
if (method.IsVirtual)
{
Log.Error($"Not supported virtual RPC method '{method.FullName}'.");
MonoCecil.CompilationError($"Not supported virtual RPC method '{method.FullName}'.", method);
failed = true;
return;
}
@@ -1226,13 +1231,13 @@ namespace Flax.Build.Plugins
var voidType = module.TypeSystem.Void;
if (method.ReturnType != voidType)
{
Log.Error($"Not supported non-void RPC method '{method.FullName}'.");
MonoCecil.CompilationError($"Not supported non-void RPC method '{method.FullName}'.", method);
failed = true;
return;
}
if (method.IsStatic)
{
Log.Error($"Not supported static RPC method '{method.FullName}'.");
MonoCecil.CompilationError($"Not supported static RPC method '{method.FullName}'.", method);
failed = true;
return;
}
@@ -1241,19 +1246,25 @@ namespace Flax.Build.Plugins
methodRPC.Method = method;
methodRPC.IsServer = (bool)attribute.GetFieldValue("Server", false);
methodRPC.IsClient = (bool)attribute.GetFieldValue("Client", false);
methodRPC.Channel = (int)attribute.GetFieldValue("Channel", 4); // int as NetworkChannelType (default is ReliableOrdered=4)
if (attribute.HasConstructorArguments && attribute.ConstructorArguments.Count >= 3)
{
methodRPC.IsServer = (bool)attribute.ConstructorArguments[0].Value;
methodRPC.IsClient = (bool)attribute.ConstructorArguments[1].Value;
methodRPC.Channel = (int)attribute.ConstructorArguments[2].Value;
}
if (methodRPC.IsServer && methodRPC.IsClient)
{
Log.Error($"Network RPC {method.Name} in {type.FullName} cannot be both Server and Client.");
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} cannot be both Server and Client.", method);
failed = true;
return;
}
if (!methodRPC.IsServer && !methodRPC.IsClient)
{
Log.Error($"Network RPC {method.Name} in {type.FullName} needs to have Server or Client specifier.");
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} needs to have Server or Client specifier.", method);
failed = true;
return;
}
methodRPC.Channel = (int)attribute.GetFieldValue("Channel", 4); // int as NetworkChannelType (default is ReliableOrdered=4)
module.GetType("System.IntPtr", out var intPtrType);
module.GetType("FlaxEngine.Object", out var scriptingObjectType);
var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr");
@@ -1289,7 +1300,7 @@ namespace Flax.Build.Plugins
var parameter = method.Parameters[i];
if (parameter.IsOut)
{
Log.Error($"Network RPC {method.Name} in {type.FullName} parameter {parameter.Name} cannot be 'out'.");
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} parameter {parameter.Name} cannot be 'out'.", method);
failed = true;
return;
}

View File

@@ -11,6 +11,58 @@ namespace Flax.Build
/// </summary>
internal static class MonoCecil
{
public static void CompilationError(string message)
{
Log.Error(message);
}
public static void CompilationError(string message, MethodDefinition method)
{
if (method != null && method.DebugInformation.HasSequencePoints)
{
var sp = method.DebugInformation.SequencePoints[0];
message = $"{sp.Document.Url}({sp.StartLine},{sp.StartColumn},{sp.EndLine},{sp.EndColumn}): error: {message}";
}
Log.Error(message);
}
public static void CompilationError(string message, PropertyDefinition property)
{
if (property != null)
{
if (property.GetMethod != null)
{
CompilationError(message, property.GetMethod);
return;
}
else if (property.SetMethod != null)
{
CompilationError(message, property.SetMethod);
return;
}
}
Log.Error(message);
}
public static void CompilationError(string message, FieldDefinition field)
{
if (field != null && field.DeclaringType != null)
{
// Just include potential filename
var methods = field.DeclaringType.Methods;
if (methods != null && methods.Count != 0)
{
var method = methods[0];
if (method != null && method.DebugInformation.HasSequencePoints)
{
var sp = method.DebugInformation.SequencePoints[0];
message = $"{sp.Document.Url}({0},{0},{0},{0}): error: {message}";
}
}
}
Log.Error(message);
}
public static bool HasAttribute(this ICustomAttributeProvider type, string fullName)
{
return type.CustomAttributes.Any(x => x.AttributeType.FullName == fullName);