From 6541e6f36f5a039bfd92197143cd409c08256f42 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Fri, 23 Oct 2026 20:33:02 +0300 Subject: [PATCH 01/14] [Events] Run the handler once for every raise of an external event --- CHANGELOG.md | 7 ++ README.md | 5 + .../External/Events/AsyncExternalEvent.cs | 62 ++++-------- ...yncExternalEventCore{TArgument,TResult}.cs | 96 +++++++++++++++++++ .../External/Events/AsyncExternalEvent{T}.cs | 65 ++++--------- .../AsyncRequestExternalEvent{T,TResult}.cs | 60 +++--------- .../AsyncRequestExternalEvent{TResult}.cs | 57 +++-------- .../External/Events/ExternalEvent{T}.cs | 33 +++++-- .../External/Events/VoidResult.cs | 8 ++ .../AsyncExternalEventTests.cs | 65 +++++++++++++ .../AsyncRequestExternalEventTests.cs | 63 ++++++++++++ .../ExternalEventTests.cs | 46 +++++++++ 12 files changed, 381 insertions(+), 186 deletions(-) create mode 100644 source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs create mode 100644 source/Nice3point.Revit.Toolkit/External/Events/VoidResult.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 78bc11e..cf4b609 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 2027.1.0 + +### External Events + +- `ExternalEvent` and the asynchronous external events run the handler once for every raise, with the argument of that raise, in the order of the raises. Each `RaiseAsync()` task completes with the result or the exception of its own run. A raise that arrived before Revit ran the previous one used to overwrite its argument, and the earlier `RaiseAsync()` task never completed. +- `ExternalEvent` without an argument runs once for all raises that wait, as Revit runs a raised event. + # 2027.0.2 Performance optimizations for Revit 2027: diff --git a/README.md b/README.md index 4e08a6d..e32e54a 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,11 @@ so you can create them anywhere in your code and on any thread. Use `Raise()` when the caller does not need to wait, or `RaiseAsync()` when it needs completion or a return value. For generated event properties, see the [ExternalEvent attribute](#externalevent-attribute). +One event can serve several callers, such as two modeless windows or a background service and a window. +Every `Raise(argument)` and every `RaiseAsync()` call runs the handler once, with its own argument, in the order of the calls. +Each `RaiseAsync()` task completes with the result or the exception of its own run. +`ExternalEvent` without an argument runs once for all raises that are still waiting, the same way Revit runs a raised event. + #### ExternalEvent A synchronous external event that queues the handler via the Revit external event mechanism. diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs index 4f32512..6dd7517 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs @@ -11,21 +11,16 @@ namespace Nice3point.Revit.Toolkit.External; [PublicAPI] public sealed class AsyncExternalEvent : ExternalEventHandler, IAsyncExternalEvent { - private readonly Action? _handler; + private readonly AsyncExternalEventCore _core; private readonly ExternalEventOptions _options; - private readonly Action? _uiHandler; - private TaskCompletionSource? _taskCompletionSource; /// /// Initializes a new instance of the class. /// /// The execution logic. /// Thrown if is . - public AsyncExternalEvent(Action handler) + public AsyncExternalEvent(Action handler) : this(handler, ExternalEventOptions.None) { - ArgumentNullException.ThrowIfNull(handler); - - _handler = handler; } /// @@ -38,7 +33,11 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) { ArgumentNullException.ThrowIfNull(handler); - _handler = handler; + _core = new((_, _) => + { + handler(); + return default; + }); _options = options; } @@ -48,11 +47,8 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) /// /// The execution logic that receives the current . /// Thrown if is . - public AsyncExternalEvent(Action handler) + public AsyncExternalEvent(Action handler) : this(handler, ExternalEventOptions.None) { - ArgumentNullException.ThrowIfNull(handler); - - _uiHandler = handler; } /// @@ -66,7 +62,11 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions op { ArgumentNullException.ThrowIfNull(handler); - _uiHandler = handler; + _core = new((uiApplication, _) => + { + handler(uiApplication); + return default; + }); _options = options; } @@ -78,6 +78,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions op /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

+ /// Each call executes the handler once, in the order of the calls, and the returned task completes with the outcome of that execution.

/// When is specified and Revit is in API mode, /// the handler is executed directly on the calling thread instead of being queued. /// @@ -85,47 +86,24 @@ public Task RaiseAsync() { if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { - try - { - ExecuteHandler(RevitContext.UiApplication); - return Task.CompletedTask; - } - catch (Exception exception) - { - return Task.FromException(exception); - } + return _core.Invoke(RevitContext.UiApplication, default); } - _taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var task = _core.Enqueue(default); Raise(); - return _taskCompletionSource.Task; + return task; } /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { - try - { - ExecuteHandler(uiApplication); - _taskCompletionSource?.SetResult(); - } - catch (Exception exception) - { - _taskCompletionSource?.SetException(exception); - } - } + _core.InvokeNext(uiApplication); - private void ExecuteHandler(UIApplication uiApplication) - { - if (_uiHandler is not null) - { - _uiHandler.Invoke(uiApplication); - } - else + if (_core.HasPendingOperations) { - _handler!.Invoke(); + Raise(); } } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs new file mode 100644 index 0000000..2454ecb --- /dev/null +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs @@ -0,0 +1,96 @@ +using System.Collections.Concurrent; +using Autodesk.Revit.UI; + +// ReSharper disable once CheckNamespace +namespace Nice3point.Revit.Toolkit.External; + +/// +/// Provides the queue of raises of an asynchronous external event, the execution of each raise, and the completion of its task. +/// +/// The type of the argument passed to the handler. +/// The type of the result produced by the handler. +/// The execution logic of the external event. +/// +/// Revit runs a pending external event once however often it is raised. +/// The owning event raises itself again while is , and each raise executes in its own event cycle. +/// +internal sealed class AsyncExternalEventCore(Func handler) +{ + private readonly ConcurrentQueue _operations = new(); + + /// + /// Gets a value indicating whether a raise waits for its execution. + /// + public bool HasPendingOperations => !_operations.IsEmpty; + + /// + /// Executes the handler on the calling thread. + /// + /// The current instance. + /// The argument to pass to the handler. + /// A completed task that contains the result or the exception of the handler. + public Task Invoke(UIApplication uiApplication, TArgument argument) + { + try + { + return Task.FromResult(handler(uiApplication, argument)); + } + catch (Exception exception) + { + return Task.FromException(exception); + } + } + + /// + /// Adds a raise to the end of the queue. + /// + /// The argument to pass to the handler. + /// A task that completes with the result or the exception of the handler once executes the raise. + public Task Enqueue(TArgument argument) + { + var operation = new Operation(argument); + _operations.Enqueue(operation); + + return operation.Task; + } + + /// + /// Executes the handler for the oldest raise in the queue and completes its task. + /// + /// The current instance. + /// An empty queue executes nothing. + public void InvokeNext(UIApplication uiApplication) + { + if (!_operations.TryDequeue(out var operation)) + { + return; + } + + try + { + operation.SetResult(handler(uiApplication, operation.Argument)); + } + catch (Exception exception) + { + operation.SetException(exception); + } + } + + private sealed class Operation(TArgument argument) + { + private readonly TaskCompletionSource _completionSource = new(TaskCreationOptions.RunContinuationsAsynchronously); + + public TArgument Argument { get; } = argument; + public Task Task => _completionSource.Task; + + public void SetResult(TResult result) + { + _completionSource.TrySetResult(result); + } + + public void SetException(Exception exception) + { + _completionSource.TrySetException(exception); + } + } +} diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs index de2534e..6f35e4c 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs @@ -12,22 +12,16 @@ namespace Nice3point.Revit.Toolkit.External; [PublicAPI] public sealed class AsyncExternalEvent : ExternalEventHandler, IAsyncExternalEvent { - private readonly Action? _handler; + private readonly AsyncExternalEventCore _core; private readonly ExternalEventOptions _options; - private readonly Action? _uiHandler; - private T _argument = default!; - private TaskCompletionSource? _taskCompletionSource; /// /// Initializes a new instance of the class. /// /// The execution logic that receives an argument of type . /// Thrown if is . - public AsyncExternalEvent(Action handler) + public AsyncExternalEvent(Action handler) : this(handler, ExternalEventOptions.None) { - ArgumentNullException.ThrowIfNull(handler); - - _handler = handler; } /// @@ -40,7 +34,11 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) { ArgumentNullException.ThrowIfNull(handler); - _handler = handler; + _core = new((_, argument) => + { + handler(argument); + return default; + }); _options = options; } @@ -50,11 +48,8 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) /// /// The execution logic that receives the current and an argument of type . /// Thrown if is . - public AsyncExternalEvent(Action handler) + public AsyncExternalEvent(Action handler) : this(handler, ExternalEventOptions.None) { - ArgumentNullException.ThrowIfNull(handler); - - _uiHandler = handler; } /// @@ -68,7 +63,11 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions { ArgumentNullException.ThrowIfNull(handler); - _uiHandler = handler; + _core = new((uiApplication, argument) => + { + handler(uiApplication, argument); + return default; + }); _options = options; } @@ -82,56 +81,32 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

+ /// Each call executes the handler once with its own argument, in the order of the calls, and the returned task completes with the outcome of that execution.

/// When is specified and Revit is in API mode, /// the handler is executed directly on the calling thread instead of being queued. /// public Task RaiseAsync(T argument) { - _argument = argument; - if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { - try - { - ExecuteHandler(RevitContext.UiApplication); - return Task.CompletedTask; - } - catch (Exception exception) - { - return Task.FromException(exception); - } + return _core.Invoke(RevitContext.UiApplication, argument); } - _taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var task = _core.Enqueue(argument); Raise(); - return _taskCompletionSource.Task; + return task; } /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { - try - { - ExecuteHandler(uiApplication); - _taskCompletionSource?.SetResult(); - } - catch (Exception exception) - { - _taskCompletionSource?.SetException(exception); - } - } + _core.InvokeNext(uiApplication); - private void ExecuteHandler(UIApplication uiApplication) - { - if (_uiHandler is not null) - { - _uiHandler.Invoke(uiApplication, _argument); - } - else + if (_core.HasPendingOperations) { - _handler!.Invoke(_argument); + Raise(); } } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs index 1c2ea3a..9480e1a 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs @@ -14,22 +14,16 @@ namespace Nice3point.Revit.Toolkit.External; [PublicAPI] public sealed class AsyncRequestExternalEvent : ExternalEventHandler, IAsyncRequestExternalEvent { - private readonly Func? _handler; + private readonly AsyncExternalEventCore _core; private readonly ExternalEventOptions _options; - private readonly Func? _uiHandler; - private T _argument = default!; - private TaskCompletionSource? _taskCompletionSource; /// /// Initializes a new instance of the class. /// /// The execution logic that receives an argument of type and returns a result of type . /// Thrown if is . - public AsyncRequestExternalEvent(Func handler) + public AsyncRequestExternalEvent(Func handler) : this(handler, ExternalEventOptions.None) { - ArgumentNullException.ThrowIfNull(handler); - - _handler = handler; } /// @@ -42,7 +36,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalEventOptions { ArgumentNullException.ThrowIfNull(handler); - _handler = handler; + _core = new((_, argument) => handler(argument)); _options = options; } @@ -52,11 +46,8 @@ public AsyncRequestExternalEvent(Func handler, ExternalEventOptions /// /// The execution logic that receives the current and an argument of type , returning a result of type . /// Thrown if is . - public AsyncRequestExternalEvent(Func handler) + public AsyncRequestExternalEvent(Func handler) : this(handler, ExternalEventOptions.None) { - ArgumentNullException.ThrowIfNull(handler); - - _uiHandler = handler; } /// @@ -70,7 +61,7 @@ public AsyncRequestExternalEvent(Func handler, Extern { ArgumentNullException.ThrowIfNull(handler); - _uiHandler = handler; + _core = new(handler); _options = options; } @@ -84,59 +75,32 @@ public AsyncRequestExternalEvent(Func handler, Extern /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

+ /// Each call executes the handler once with its own argument, in the order of the calls, and the returned task completes with the outcome of that execution.

/// When is specified and Revit is in API mode, /// the handler is executed directly on the calling thread instead of being queued. /// public Task RaiseAsync(T argument) { - _argument = argument; - if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { - try - { - var result = ExecuteHandler(RevitContext.UiApplication); - return Task.FromResult(result); - } - catch (Exception exception) - { - return Task.FromException(exception); - } + return _core.Invoke(RevitContext.UiApplication, argument); } - _taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var task = _core.Enqueue(argument); Raise(); - return _taskCompletionSource.Task; + return task; } /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { - try - { - var result = ExecuteHandler(uiApplication); - _taskCompletionSource?.SetResult(result); - } - catch (Exception exception) - { - _taskCompletionSource?.SetException(exception); - } - } + _core.InvokeNext(uiApplication); - private TResult ExecuteHandler(UIApplication uiApplication) - { - TResult result; - if (_uiHandler is not null) + if (_core.HasPendingOperations) { - result = _uiHandler.Invoke(uiApplication, _argument); + Raise(); } - else - { - result = _handler!.Invoke(_argument); - } - - return result; } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs index c4f6008..cfd832a 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs @@ -12,21 +12,16 @@ namespace Nice3point.Revit.Toolkit.External; [PublicAPI] public sealed class AsyncRequestExternalEvent : ExternalEventHandler, IAsyncRequestExternalEvent { - private readonly Func? _handler; + private readonly AsyncExternalEventCore _core; private readonly ExternalEventOptions _options; - private readonly Func? _uiHandler; - private TaskCompletionSource? _taskCompletionSource; /// /// Initializes a new instance of the class. /// /// The execution logic that returns a result of type . /// Thrown if is . - public AsyncRequestExternalEvent(Func handler) + public AsyncRequestExternalEvent(Func handler) : this(handler, ExternalEventOptions.None) { - ArgumentNullException.ThrowIfNull(handler); - - _handler = handler; } /// @@ -39,7 +34,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalEventOptions opt { ArgumentNullException.ThrowIfNull(handler); - _handler = handler; + _core = new((_, _) => handler()); _options = options; } @@ -49,11 +44,8 @@ public AsyncRequestExternalEvent(Func handler, ExternalEventOptions opt /// /// The execution logic that receives the current and returns a result of type . /// Thrown if is . - public AsyncRequestExternalEvent(Func handler) + public AsyncRequestExternalEvent(Func handler) : this(handler, ExternalEventOptions.None) { - ArgumentNullException.ThrowIfNull(handler); - - _uiHandler = handler; } /// @@ -67,7 +59,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalE { ArgumentNullException.ThrowIfNull(handler); - _uiHandler = handler; + _core = new((uiApplication, _) => handler(uiApplication)); _options = options; } @@ -79,6 +71,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalE /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

+ /// Each call executes the handler once, in the order of the calls, and the returned task completes with the outcome of that execution.

/// When is specified and Revit is in API mode, /// the handler is executed directly on the calling thread instead of being queued. /// @@ -86,50 +79,24 @@ public Task RaiseAsync() { if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { - try - { - var result = ExecuteHandler(RevitContext.UiApplication); - return Task.FromResult(result); - } - catch (Exception exception) - { - return Task.FromException(exception); - } + return _core.Invoke(RevitContext.UiApplication, default); } - _taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var task = _core.Enqueue(default); Raise(); - return _taskCompletionSource.Task; + return task; } /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { - try - { - var result = ExecuteHandler(uiApplication); - _taskCompletionSource?.SetResult(result); - } - catch (Exception exception) - { - _taskCompletionSource?.SetException(exception); - } - } + _core.InvokeNext(uiApplication); - private TResult ExecuteHandler(UIApplication uiApplication) - { - TResult result; - if (_uiHandler is not null) + if (_core.HasPendingOperations) { - result = _uiHandler.Invoke(uiApplication); + Raise(); } - else - { - result = _handler!.Invoke(); - } - - return result; } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs index 0005873..106b121 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System.ComponentModel; using Autodesk.Revit.UI; @@ -13,10 +14,10 @@ namespace Nice3point.Revit.Toolkit.External; [PublicAPI] public class ExternalEvent : ExternalEventHandler, IExternalEvent { + private readonly ConcurrentQueue _arguments = new(); private readonly Action? _handler; private readonly ExternalEventOptions _options; private readonly Action? _uiHandler; - private T _argument = default!; /// /// Initializes a new instance of the class. @@ -86,33 +87,53 @@ public ExternalEvent(Action handler, ExternalEventOptions opti /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

+ /// Each call executes the handler once with its own argument, in the order of the calls.

/// When is specified and Revit is in API mode, /// the handler is executed directly on the calling thread instead of being queued. /// public ExternalEventRequest Raise(T argument) { - _argument = argument; - if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { - Execute(RevitContext.UiApplication); + Invoke(RevitContext.UiApplication, argument); return ExternalEventRequest.Accepted; } + _arguments.Enqueue(argument); return base.Raise(); } /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) + { + if (!_arguments.TryDequeue(out var argument)) + { + return; + } + + try + { + Invoke(uiApplication, argument); + } + finally + { + if (!_arguments.IsEmpty) + { + base.Raise(); + } + } + } + + private void Invoke(UIApplication uiApplication, T argument) { if (_uiHandler is not null) { - _uiHandler.Invoke(uiApplication, _argument); + _uiHandler.Invoke(uiApplication, argument); } else { - _handler!.Invoke(_argument); + _handler!.Invoke(argument); } } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/VoidResult.cs b/source/Nice3point.Revit.Toolkit/External/Events/VoidResult.cs new file mode 100644 index 0000000..f19e9cd --- /dev/null +++ b/source/Nice3point.Revit.Toolkit/External/Events/VoidResult.cs @@ -0,0 +1,8 @@ +// ReSharper disable once CheckNamespace + +namespace Nice3point.Revit.Toolkit.External; + +/// +/// Represents the absent argument or result of an asynchronous external event. +/// +internal readonly struct VoidResult; diff --git a/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs index 7d90566..16bcd63 100644 --- a/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs +++ b/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs @@ -1,6 +1,7 @@ using Autodesk.Revit.UI; using Nice3point.Revit.Toolkit.External; using Nice3point.TUnit.Revit; +using TUnit.Assertions.Enums; namespace Nice3point.Revit.Toolkit.Tests; @@ -178,4 +179,68 @@ public async Task RaiseAsync_SequentialRaises_ExecutesEveryRaise(CancellationTok // Assert await Assert.That(executionCount).IsEqualTo(3); } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_RaisesBeforeTheEventCycle_ExecutesTheHandlerForEveryRaise(CancellationToken cancellationToken) + { + // Arrange + var executionCount = 0; + var externalEvent = new AsyncExternalEvent(() => executionCount++); + + // Act + var firstTask = externalEvent.RaiseAsync(); + var secondTask = externalEvent.RaiseAsync(); + await Task.WhenAll(firstTask, secondTask).WaitAsync(cancellationToken); + + // Assert + await Assert.That(executionCount).IsEqualTo(2); + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_GenericRaisesBeforeTheEventCycle_ExecutesTheHandlerForEachArgumentInOrder(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var externalEvent = new AsyncExternalEvent(receivedArguments.Add); + + // Act + var firstTask = externalEvent.RaiseAsync(1); + var secondTask = externalEvent.RaiseAsync(2); + var thirdTask = externalEvent.RaiseAsync(3); + await Task.WhenAll(firstTask, secondTask, thirdTask).WaitAsync(cancellationToken); + + // Assert + await Assert.That(receivedArguments).IsEquivalentTo([1, 2, 3], CollectionOrdering.Matching); + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_GenericFirstRaiseThrows_CompletesTheFollowingRaise(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var externalEvent = new AsyncExternalEvent(argument => + { + receivedArguments.Add(argument); + if (argument == 1) + { + throw new InvalidOperationException("Handler failure"); + } + }); + + // Act + var failingTask = externalEvent.RaiseAsync(1); + var followingTask = externalEvent.RaiseAsync(2); + await followingTask.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(() => failingTask.WaitAsync(cancellationToken)).Throws(); + await Assert.That(followingTask.Status).IsEqualTo(TaskStatus.RanToCompletion); + await Assert.That(receivedArguments).IsEquivalentTo([1, 2], CollectionOrdering.Matching); + } + } } diff --git a/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs index 678df9b..392dd8f 100644 --- a/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs +++ b/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs @@ -1,5 +1,6 @@ using Nice3point.Revit.Toolkit.External; using Nice3point.TUnit.Revit; +using TUnit.Assertions.Enums; namespace Nice3point.Revit.Toolkit.Tests; @@ -147,4 +148,66 @@ public async Task RaiseAsync_ArgumentAllowDirectInvocation_CompletesOnRaiseWithT await Assert.That(await raiseTask).IsEqualTo(42); } } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_RaisesBeforeTheEventCycle_CompletesEachTaskWithItsOwnResult(CancellationToken cancellationToken) + { + // Arrange + var executionCount = 0; + var externalEvent = new AsyncRequestExternalEvent(() => ++executionCount); + + // Act + var firstTask = externalEvent.RaiseAsync(); + var secondTask = externalEvent.RaiseAsync(); + var results = await Task.WhenAll(firstTask, secondTask).WaitAsync(cancellationToken); + + // Assert + await Assert.That(results).IsEquivalentTo([1, 2], CollectionOrdering.Matching); + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_ArgumentRaisesBeforeTheEventCycle_CompletesEachTaskWithTheResultOfItsArgument(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var externalEvent = new AsyncRequestExternalEvent(argument => + { + receivedArguments.Add(argument); + return argument; + }); + + // Act + var firstTask = externalEvent.RaiseAsync(1); + var secondTask = externalEvent.RaiseAsync(2); + var results = await Task.WhenAll(firstTask, secondTask).WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(results).IsEquivalentTo([1, 2], CollectionOrdering.Matching); + await Assert.That(receivedArguments).IsEquivalentTo([1, 2], CollectionOrdering.Matching); + } + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_ArgumentFirstRaiseThrows_CompletesTheFollowingRaiseWithItsResult(CancellationToken cancellationToken) + { + // Arrange + var externalEvent = new AsyncRequestExternalEvent(argument => argument == 1 ? throw new InvalidOperationException("Handler failure") : argument); + + // Act + var failingTask = externalEvent.RaiseAsync(1); + var followingTask = externalEvent.RaiseAsync(2); + var followingResult = await followingTask.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(() => failingTask.WaitAsync(cancellationToken)).Throws(); + await Assert.That(followingResult).IsEqualTo(2); + } + } } diff --git a/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventTests.cs index e25f081..623eb96 100644 --- a/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventTests.cs +++ b/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventTests.cs @@ -1,6 +1,7 @@ using Autodesk.Revit.UI; using Nice3point.Revit.Toolkit.External; using Nice3point.TUnit.Revit; +using TUnit.Assertions.Enums; using ExternalEvent = Nice3point.Revit.Toolkit.External.ExternalEvent; namespace Nice3point.Revit.Toolkit.Tests; @@ -127,6 +128,51 @@ public async Task Raise_GenericAllowDirectInvocation_ExecutesOnRaiseWithTheArgum await Assert.That(receivedArguments).IsEquivalentTo([1, 2]); } + [Test] + [Timeout(10_000)] + public async Task Raise_RaisesBeforeTheEventCycle_ExecutesTheHandlerOnce(CancellationToken cancellationToken) + { + // Arrange + var executionCount = 0; + var externalEvent = new ExternalEvent(() => executionCount++); + var eventCycle = new AsyncExternalEvent(() => { }); + + // Act + externalEvent.Raise(); + externalEvent.Raise(); + await eventCycle.RaiseAsync().WaitAsync(cancellationToken); + await eventCycle.RaiseAsync().WaitAsync(cancellationToken); + + // Assert + await Assert.That(executionCount).IsEqualTo(1); + } + + [Test] + [Timeout(10_000)] + public async Task Raise_GenericRaisesBeforeTheEventCycle_ExecutesTheHandlerForEachArgumentInOrder(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var execution = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var externalEvent = new ExternalEvent(argument => + { + receivedArguments.Add(argument); + if (receivedArguments.Count == 3) + { + execution.SetResult(); + } + }); + + // Act + externalEvent.Raise(1); + externalEvent.Raise(2); + externalEvent.Raise(3); + await execution.Task.WaitAsync(cancellationToken); + + // Assert + await Assert.That(receivedArguments).IsEquivalentTo([1, 2, 3], CollectionOrdering.Matching); + } + [Test] public async Task GetName_DefaultIdentifier_ReturnsTheTypeName() { From 224365ff212786555fcefd0e3b4b7ee88b0d010e Mon Sep 17 00:00:00 2001 From: Nice3point Date: Fri, 23 Oct 2026 20:39:54 +0300 Subject: [PATCH 02/14] [Events] Cancel a pending raise of an asynchronous external event --- CHANGELOG.md | 20 ++ README.md | 51 ++++ .../External/Events/AsyncExternalEvent.cs | 50 +++- ...yncExternalEventCore{TArgument,TResult}.cs | 97 ++++++- .../External/Events/AsyncExternalEvent{T}.cs | 50 +++- .../AsyncRequestExternalEvent{T,TResult}.cs | 44 ++- .../AsyncRequestExternalEvent{TResult}.cs | 46 +++- .../Events/Interfaces/IAsyncExternalEvent.cs | 7 +- .../Interfaces/IAsyncExternalEvent{T}.cs | 7 +- .../IAsyncRequestExternalEvent{T,TResult}.cs | 7 +- .../IAsyncRequestExternalEvent{TResult}.cs | 7 +- .../AsyncExternalEventTests.cs | 111 ++++++++ .../AsyncRequestExternalEventTests.cs | 250 ++++++++++++++++++ 13 files changed, 706 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf4b609..77ed6d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ - `ExternalEvent` and the asynchronous external events run the handler once for every raise, with the argument of that raise, in the order of the raises. Each `RaiseAsync()` task completes with the result or the exception of its own run. A raise that arrived before Revit ran the previous one used to overwrite its argument, and the earlier `RaiseAsync()` task never completed. - `ExternalEvent` without an argument runs once for all raises that wait, as Revit runs a raised event. +- `RaiseAsync()` of the asynchronous external events and their interfaces accepts an optional `CancellationToken`. A token canceled before Revit executes the handler withdraws the raise, and the task is canceled at once. A running handler is never interrupted. With `ExternalEventOptions.AllowDirectInvocation` in API mode, the token is checked once before the handler runs. +- New constructors of `AsyncExternalEvent`, `AsyncExternalEvent`, `AsyncRequestExternalEvent`, and `AsyncRequestExternalEvent` accept a handler that receives the `UIApplication`, the argument, and the `CancellationToken` of the raise. An `OperationCanceledException` thrown for that token cancels the task. + +```c# +private readonly AsyncExternalEvent> _deleteWindowsAsyncEvent = new((application, elementIds, cancellationToken) => +{ + foreach (var elementId in elementIds) + { + cancellationToken.ThrowIfCancellationRequested(); + application.ActiveUIDocument.Document.Delete(elementId); + } +}); + +using var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)); +await _deleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); +``` + +### Breaking Changes + +- The `RaiseAsync` methods of `IAsyncExternalEvent`, `IAsyncExternalEvent`, `IAsyncRequestExternalEvent`, and `IAsyncRequestExternalEvent` take an optional `CancellationToken` parameter. Source code that calls them compiles unchanged. Add-ins compiled against an earlier version are rebuilt, and custom implementations of the interfaces add the parameter. # 2027.0.2 diff --git a/README.md b/README.md index e32e54a..5f14c37 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Package included by default in [Revit Templates](https://github.com/Nice3point/R * [AsyncExternalEvent\](#asyncexternaleventt) * [AsyncRequestExternalEvent\](#asyncrequestexternaleventtresult) * [AsyncRequestExternalEvent\](#asyncrequestexternaleventt-tresult) + * [Cancellation](#cancellation) * [ExternalEventOptions](#externaleventoptions) * [ExternalEvent attribute](#externalevent-attribute) * [Context](#context) @@ -360,6 +361,56 @@ private async Task DeleteWindowAsync(ElementId elementId) > Await asynchronous events. Blocking a pending event with `.Wait()`, `.Result`, or `.GetAwaiter().GetResult()` on the Revit main thread prevents Revit from processing it and can deadlock. > The event handler itself remains synchronous; perform asynchronous I/O before or after raising the event. +#### Cancellation + +`RaiseAsync()` accepts a `CancellationToken`. +A modal dialog or a running command keeps Revit from executing external events, and the token lets the caller stop waiting: + +```c# +private async Task DeleteWindowAsync(ElementId elementId) +{ + using var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + + try + { + await _deleteWindowAsyncEvent.RaiseAsync(elementId, cancellationSource.Token); + } + catch (OperationCanceledException) + { + //Revit was busy for 10 seconds, the delegate never executes + } +} +``` + +The token affects the raise as follows: + +- A token canceled before Revit executes the delegate withdraws the raise: the delegate doesn't run, and the task is canceled at once. +- A token canceled while the delegate runs doesn't interrupt it, and the task completes with the outcome of the delegate. +- The other raises of the same event run as if the canceled raise didn't exist. + +To stop a long-running delegate, accept the token as the last parameter of the delegate: + +```c# +private readonly AsyncExternalEvent> _deleteWindowsAsyncEvent = new((application, elementIds, cancellationToken) => +{ + var document = application.ActiveUIDocument.Document; + using var transaction = new Transaction(document, "Delete windows"); + transaction.Start(); + + foreach (var elementId in elementIds) + { + cancellationToken.ThrowIfCancellationRequested(); + document.Delete(elementId); + } + + transaction.Commit(); +}); +``` + +An `OperationCanceledException` thrown for the token rolls back the transaction and cancels the task. +The delegate observes a cancellation requested from another thread, such as a timeout or a remote client. +A cancel button of a modeless window runs on the Revit thread and takes effect only after the delegate returns. + #### ExternalEventOptions You can configure the behavior of external events using `ExternalEventOptions`. diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs index 6dd7517..1ab12cb 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs @@ -33,7 +33,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) { ArgumentNullException.ThrowIfNull(handler); - _core = new((_, _) => + _core = new((_, _, _) => { handler(); return default; @@ -62,7 +62,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions op { ArgumentNullException.ThrowIfNull(handler); - _core = new((uiApplication, _) => + _core = new((uiApplication, _, _) => { handler(uiApplication); return default; @@ -70,26 +70,64 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions op _options = options; } + /// + /// Initializes a new instance of the class + /// with access to the instance and the cancellation token of the raise. + /// + /// The execution logic that receives the current and the token passed to . + /// Thrown if is . + public AsyncExternalEvent(Action handler) : this(handler, ExternalEventOptions.None) + { + } + + /// + /// Initializes a new instance of the class + /// with access to the instance and the cancellation token of the raise. + /// + /// The execution logic that receives the current and the token passed to . + /// The options to use to configure the external event. + /// Thrown if is . + public AsyncExternalEvent(Action handler, ExternalEventOptions options) + { + ArgumentNullException.ThrowIfNull(handler); + + _core = new((uiApplication, _, cancellationToken) => + { + handler(uiApplication, cancellationToken); + return default; + }); + _options = options; + } + /// /// Raises the external event asynchronously, instructing Revit to execute the handler within the Revit API context. /// + /// The token to monitor for cancellation requests. /// The representing the async operation being executed. /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

/// Each call executes the handler once, in the order of the calls, and the returned task completes with the outcome of that execution.

+ /// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. + /// A cancellation requested while the handler executes doesn't interrupt it. + /// A handler that receives the token can throw an for it, and the task is then canceled.

/// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. ///
- public Task RaiseAsync() + public Task RaiseAsync(CancellationToken cancellationToken = default) { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { - return _core.Invoke(RevitContext.UiApplication, default); + return _core.Invoke(RevitContext.UiApplication, default, cancellationToken); } - var task = _core.Enqueue(default); + var task = _core.Enqueue(default, cancellationToken); Raise(); return task; diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs index 2454ecb..9451c01 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using Autodesk.Revit.UI; // ReSharper disable once CheckNamespace @@ -12,9 +13,11 @@ namespace Nice3point.Revit.Toolkit.External; /// The execution logic of the external event. /// /// Revit runs a pending external event once however often it is raised. -/// The owning event raises itself again while is , and each raise executes in its own event cycle. +/// The owning event raises itself again while is , and each raise executes in its own event cycle.
+/// A raise whose token is canceled before its execution starts is canceled at once and never executes. +/// Once the execution starts, the cancellation of the token no longer affects the raise. ///
-internal sealed class AsyncExternalEventCore(Func handler) +internal sealed class AsyncExternalEventCore(Func handler) { private readonly ConcurrentQueue _operations = new(); @@ -28,12 +31,17 @@ internal sealed class AsyncExternalEventCore(Func /// The current instance. /// The argument to pass to the handler. - /// A completed task that contains the result or the exception of the handler. - public Task Invoke(UIApplication uiApplication, TArgument argument) + /// The token to pass to the handler. + /// A completed task that contains the result or the exception of the handler, or a canceled task when the handler throws an for . + public Task Invoke(UIApplication uiApplication, TArgument argument, CancellationToken cancellationToken) { try { - return Task.FromResult(handler(uiApplication, argument)); + return Task.FromResult(handler(uiApplication, argument, cancellationToken)); + } + catch (OperationCanceledException exception) when (IsCanceledBy(exception, cancellationToken)) + { + return Task.FromCanceled(cancellationToken); } catch (Exception exception) { @@ -45,30 +53,31 @@ public Task Invoke(UIApplication uiApplication, TArgument argument) /// Adds a raise to the end of the queue. ///
/// The argument to pass to the handler. - /// A task that completes with the result or the exception of the handler once executes the raise. - public Task Enqueue(TArgument argument) + /// The token that withdraws the raise before its execution, passed to the handler. + /// A task that completes with the outcome of the handler once executes the raise, or a task canceled at the cancellation of before the execution. + public Task Enqueue(TArgument argument, CancellationToken cancellationToken) { - var operation = new Operation(argument); + var operation = new Operation(argument, cancellationToken); _operations.Enqueue(operation); return operation.Task; } /// - /// Executes the handler for the oldest raise in the queue and completes its task. + /// Executes the handler for the oldest raise in the queue that is not canceled and completes its task. /// /// The current instance. - /// An empty queue executes nothing. + /// A queue without a raise to execute executes nothing. public void InvokeNext(UIApplication uiApplication) { - if (!_operations.TryDequeue(out var operation)) + if (!TryStartNext(out var operation)) { return; } try { - operation.SetResult(handler(uiApplication, operation.Argument)); + operation.SetResult(handler(uiApplication, operation.Argument, operation.CancellationToken)); } catch (Exception exception) { @@ -76,13 +85,56 @@ public void InvokeNext(UIApplication uiApplication) } } - private sealed class Operation(TArgument argument) + private bool TryStartNext([NotNullWhen(true)] out Operation? operation) + { + while (_operations.TryDequeue(out operation)) + { + if (operation.TryStart()) + { + return true; + } + } + + return false; + } + + private static bool IsCanceledBy(OperationCanceledException exception, CancellationToken cancellationToken) + { + return cancellationToken.IsCancellationRequested && exception.CancellationToken == cancellationToken; + } + + private sealed class Operation { + private const int Pending = 0; + private const int Started = 1; + private const int Canceled = 2; + private readonly TaskCompletionSource _completionSource = new(TaskCreationOptions.RunContinuationsAsynchronously); + private CancellationTokenRegistration _cancellationRegistration; + private int _state; - public TArgument Argument { get; } = argument; + public Operation(TArgument argument, CancellationToken cancellationToken) + { + Argument = argument; + CancellationToken = cancellationToken; + _cancellationRegistration = cancellationToken.Register(static state => ((Operation)state!).Cancel(), this); + } + + public TArgument Argument { get; private set; } + public CancellationToken CancellationToken { get; } public Task Task => _completionSource.Task; + public bool TryStart() + { + if (Interlocked.CompareExchange(ref _state, Started, Pending) != Pending) + { + return false; + } + + _cancellationRegistration.Dispose(); + return true; + } + public void SetResult(TResult result) { _completionSource.TrySetResult(result); @@ -90,7 +142,24 @@ public void SetResult(TResult result) public void SetException(Exception exception) { + if (exception is OperationCanceledException canceledException && IsCanceledBy(canceledException, CancellationToken)) + { + _completionSource.TrySetCanceled(CancellationToken); + return; + } + _completionSource.TrySetException(exception); } + + private void Cancel() + { + if (Interlocked.CompareExchange(ref _state, Canceled, Pending) != Pending) + { + return; + } + + Argument = default!; + _completionSource.TrySetCanceled(CancellationToken); + } } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs index 6f35e4c..a750ed1 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs @@ -34,7 +34,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) { ArgumentNullException.ThrowIfNull(handler); - _core = new((_, argument) => + _core = new((_, argument, _) => { handler(argument); return default; @@ -63,7 +63,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions { ArgumentNullException.ThrowIfNull(handler); - _core = new((uiApplication, argument) => + _core = new((uiApplication, argument, _) => { handler(uiApplication, argument); return default; @@ -71,28 +71,66 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions _options = options; } + /// + /// Initializes a new instance of the class + /// with access to the instance and the cancellation token of the raise. + /// + /// The execution logic that receives the current , an argument of type , and the token passed to . + /// Thrown if is . + public AsyncExternalEvent(Action handler) : this(handler, ExternalEventOptions.None) + { + } + + /// + /// Initializes a new instance of the class + /// with access to the instance and the cancellation token of the raise. + /// + /// The execution logic that receives the current , an argument of type , and the token passed to . + /// The options to use to configure the external event. + /// Thrown if is . + public AsyncExternalEvent(Action handler, ExternalEventOptions options) + { + ArgumentNullException.ThrowIfNull(handler); + + _core = new((uiApplication, argument, cancellationToken) => + { + handler(uiApplication, argument, cancellationToken); + return default; + }); + _options = options; + } + /// /// Raises the external event asynchronously with the specified argument, /// instructing Revit to execute the handler within the Revit API context. /// /// The argument to pass to the handler. + /// The token to monitor for cancellation requests. /// The representing the async operation being executed. /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

/// Each call executes the handler once with its own argument, in the order of the calls, and the returned task completes with the outcome of that execution.

+ /// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. + /// A cancellation requested while the handler executes doesn't interrupt it. + /// A handler that receives the token can throw an for it, and the task is then canceled.

/// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. ///
- public Task RaiseAsync(T argument) + public Task RaiseAsync(T argument, CancellationToken cancellationToken = default) { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { - return _core.Invoke(RevitContext.UiApplication, argument); + return _core.Invoke(RevitContext.UiApplication, argument, cancellationToken); } - var task = _core.Enqueue(argument); + var task = _core.Enqueue(argument, cancellationToken); Raise(); return task; diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs index 9480e1a..854c933 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs @@ -36,7 +36,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalEventOptions { ArgumentNullException.ThrowIfNull(handler); - _core = new((_, argument) => handler(argument)); + _core = new((_, argument, _) => handler(argument)); _options = options; } @@ -61,6 +61,31 @@ public AsyncRequestExternalEvent(Func handler, Extern { ArgumentNullException.ThrowIfNull(handler); + _core = new((uiApplication, argument, _) => handler(uiApplication, argument)); + _options = options; + } + + /// + /// Initializes a new instance of the class + /// with access to the instance and the cancellation token of the raise. + /// + /// The execution logic that receives the current , an argument of type , and the token passed to , returning a result of type . + /// Thrown if is . + public AsyncRequestExternalEvent(Func handler) : this(handler, ExternalEventOptions.None) + { + } + + /// + /// Initializes a new instance of the class + /// with access to the instance and the cancellation token of the raise. + /// + /// The execution logic that receives the current , an argument of type , and the token passed to , returning a result of type . + /// The options to use to configure the external event. + /// Thrown if is . + public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options) + { + ArgumentNullException.ThrowIfNull(handler); + _core = new(handler); _options = options; } @@ -70,23 +95,32 @@ public AsyncRequestExternalEvent(Func handler, Extern /// instructing Revit to execute the handler within the Revit API context. ///
/// The argument to pass to the handler. + /// The token to monitor for cancellation requests. /// The representing the async operation being executed, containing the result of type . /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

/// Each call executes the handler once with its own argument, in the order of the calls, and the returned task completes with the outcome of that execution.

+ /// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. + /// A cancellation requested while the handler executes doesn't interrupt it, and the task completes with its result. + /// A handler that receives the token can throw an for it, and the task is then canceled.

/// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. ///
- public Task RaiseAsync(T argument) + public Task RaiseAsync(T argument, CancellationToken cancellationToken = default) { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { - return _core.Invoke(RevitContext.UiApplication, argument); + return _core.Invoke(RevitContext.UiApplication, argument, cancellationToken); } - var task = _core.Enqueue(argument); + var task = _core.Enqueue(argument, cancellationToken); Raise(); return task; diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs index cfd832a..070b6b9 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs @@ -34,7 +34,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalEventOptions opt { ArgumentNullException.ThrowIfNull(handler); - _core = new((_, _) => handler()); + _core = new((_, _, _) => handler()); _options = options; } @@ -59,30 +59,64 @@ public AsyncRequestExternalEvent(Func handler, ExternalE { ArgumentNullException.ThrowIfNull(handler); - _core = new((uiApplication, _) => handler(uiApplication)); + _core = new((uiApplication, _, _) => handler(uiApplication)); + _options = options; + } + + /// + /// Initializes a new instance of the class + /// with access to the instance and the cancellation token of the raise. + /// + /// The execution logic that receives the current and the token passed to , returning a result of type . + /// Thrown if is . + public AsyncRequestExternalEvent(Func handler) : this(handler, ExternalEventOptions.None) + { + } + + /// + /// Initializes a new instance of the class + /// with access to the instance and the cancellation token of the raise. + /// + /// The execution logic that receives the current and the token passed to , returning a result of type . + /// The options to use to configure the external event. + /// Thrown if is . + public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options) + { + ArgumentNullException.ThrowIfNull(handler); + + _core = new((uiApplication, _, cancellationToken) => handler(uiApplication, cancellationToken)); _options = options; } /// /// Raises the external event asynchronously, instructing Revit to execute the handler within the Revit API context. /// + /// The token to monitor for cancellation requests. /// The representing the async operation being executed, containing the result of type . /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

/// Each call executes the handler once, in the order of the calls, and the returned task completes with the outcome of that execution.

+ /// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. + /// A cancellation requested while the handler executes doesn't interrupt it, and the task completes with its result. + /// A handler that receives the token can throw an for it, and the task is then canceled.

/// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. ///
- public Task RaiseAsync() + public Task RaiseAsync(CancellationToken cancellationToken = default) { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { - return _core.Invoke(RevitContext.UiApplication, default); + return _core.Invoke(RevitContext.UiApplication, default, cancellationToken); } - var task = _core.Enqueue(default); + var task = _core.Enqueue(default, cancellationToken); Raise(); return task; diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs index 3f5341b..c51f0a1 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs @@ -11,6 +11,11 @@ public interface IAsyncExternalEvent /// /// Raises (signals) the external event, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. /// + /// The token to monitor for cancellation requests. /// The representing the async operation being executed. - Task RaiseAsync(); + /// + /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. + /// A cancellation requested while the handler executes doesn't interrupt it. + /// + Task RaiseAsync(CancellationToken cancellationToken = default); } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs index bd0c40c..a5a9e1b 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs @@ -13,6 +13,11 @@ public interface IAsyncExternalEvent /// Raises (signals) the external event with the specified argument, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. ///
/// The argument to pass to the handler. + /// The token to monitor for cancellation requests. /// The representing the async operation being executed. - Task RaiseAsync(T argument); + /// + /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. + /// A cancellation requested while the handler executes doesn't interrupt it. + /// + Task RaiseAsync(T argument, CancellationToken cancellationToken = default); } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs index 5a777ca..eabc799 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs @@ -15,6 +15,11 @@ public interface IAsyncRequestExternalEvent /// Raises (signals) the external event with the specified argument, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. ///
/// The argument to pass to the handler. + /// The token to monitor for cancellation requests. /// The representing the async operation being executed, containing the result of type . - Task RaiseAsync(T argument); + /// + /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. + /// A cancellation requested while the handler executes doesn't interrupt it. + /// + Task RaiseAsync(T argument, CancellationToken cancellationToken = default); } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs index 87fc058..3920d18 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs @@ -13,6 +13,11 @@ public interface IAsyncRequestExternalEvent /// /// Raises (signals) the external event, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. /// + /// The token to monitor for cancellation requests. /// The representing the async operation being executed, containing the result of type . - Task RaiseAsync(); + /// + /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. + /// A cancellation requested while the handler executes doesn't interrupt it. + /// + Task RaiseAsync(CancellationToken cancellationToken = default); } diff --git a/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs index 16bcd63..5fac7d9 100644 --- a/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs +++ b/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs @@ -243,4 +243,115 @@ public async Task RaiseAsync_GenericFirstRaiseThrows_CompletesTheFollowingRaise( await Assert.That(receivedArguments).IsEquivalentTo([1, 2], CollectionOrdering.Matching); } } + + [Test] + public async Task RaiseAsync_CanceledToken_ReturnsCanceledTask() + { + // Arrange + var externalEvent = new AsyncExternalEvent(() => { }); + using var cancellationSource = new CancellationTokenSource(); + cancellationSource.Cancel(); + + // Act + var raiseTask = externalEvent.RaiseAsync(cancellationSource.Token); + + // Assert + await Assert.That(raiseTask.IsCanceled).IsTrue(); + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_TokenCanceledBeforeTheEventCycle_CancelsTheTaskWithoutExecution(CancellationToken cancellationToken) + { + // Arrange + var executionCount = 0; + var externalEvent = new AsyncExternalEvent(() => executionCount++); + using var cancellationSource = new CancellationTokenSource(); + + // Act + var canceledTask = externalEvent.RaiseAsync(cancellationSource.Token); + var followingTask = externalEvent.RaiseAsync(); + cancellationSource.Cancel(); + await followingTask.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(canceledTask.IsCanceled).IsTrue(); + await Assert.That(executionCount).IsEqualTo(1); + } + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_TokenUiApplicationHandler_ReceivesTheUiApplicationAndToken(CancellationToken cancellationToken) + { + // Arrange + UIApplication? receivedApplication = null; + CancellationToken receivedToken = default; + using var cancellationSource = new CancellationTokenSource(); + var externalEvent = new AsyncExternalEvent((uiApplication, raiseToken) => + { + receivedApplication = uiApplication; + receivedToken = raiseToken; + }); + + // Act + await externalEvent.RaiseAsync(cancellationSource.Token).WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(receivedApplication!.MainWindowHandle).IsEqualTo(UiApplication.MainWindowHandle); + await Assert.That(receivedToken).IsEqualTo(cancellationSource.Token); + } + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_GenericTokenCanceledBeforeTheEventCycle_ExecutesOnlyTheFollowingRaise(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var externalEvent = new AsyncExternalEvent(receivedArguments.Add); + using var cancellationSource = new CancellationTokenSource(); + + // Act + var canceledTask = externalEvent.RaiseAsync(1, cancellationSource.Token); + var followingTask = externalEvent.RaiseAsync(2); + cancellationSource.Cancel(); + await followingTask.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(canceledTask.IsCanceled).IsTrue(); + await Assert.That(receivedArguments).IsEquivalentTo([2]); + } + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_GenericTokenHandler_ReceivesTheArgumentAndToken(CancellationToken cancellationToken) + { + // Arrange + var receivedArgument = 0; + CancellationToken receivedToken = default; + using var cancellationSource = new CancellationTokenSource(); + var externalEvent = new AsyncExternalEvent((_, argument, raiseToken) => + { + receivedArgument = argument; + receivedToken = raiseToken; + }); + + // Act + await externalEvent.RaiseAsync(42, cancellationSource.Token).WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(receivedArgument).IsEqualTo(42); + await Assert.That(receivedToken).IsEqualTo(cancellationSource.Token); + } + } } diff --git a/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs index 392dd8f..0ef7b42 100644 --- a/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs +++ b/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs @@ -210,4 +210,254 @@ public async Task RaiseAsync_ArgumentFirstRaiseThrows_CompletesTheFollowingRaise await Assert.That(followingResult).IsEqualTo(2); } } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_CanceledToken_ReturnsCanceledTaskWithoutExecution(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var externalEvent = new AsyncRequestExternalEvent(argument => + { + receivedArguments.Add(argument); + return argument; + }); + + using var cancellationSource = new CancellationTokenSource(); + cancellationSource.Cancel(); + + // Act + var canceledTask = externalEvent.RaiseAsync(1, cancellationSource.Token); + var followingResult = await externalEvent.RaiseAsync(2).WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(canceledTask.IsCanceled).IsTrue(); + await Assert.That(followingResult).IsEqualTo(2); + await Assert.That(receivedArguments).IsEquivalentTo([2]); + } + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_TokenCanceledBeforeTheEventCycle_CancelsTheTaskAtOnceAndExecutesTheFollowingRaise(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var externalEvent = new AsyncRequestExternalEvent(argument => + { + receivedArguments.Add(argument); + return argument; + }); + + using var cancellationSource = new CancellationTokenSource(); + + // Act + var canceledTask = externalEvent.RaiseAsync(1, cancellationSource.Token); + var followingTask = externalEvent.RaiseAsync(2); + cancellationSource.Cancel(); + var isCanceledOnCancel = canceledTask.IsCanceled; + var followingResult = await followingTask.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(isCanceledOnCancel).IsTrue(); + await Assert.That(followingResult).IsEqualTo(2); + await Assert.That(receivedArguments).IsEquivalentTo([2]); + } + } + + [Test] + public async Task RaiseAsync_TokenCanceledBeforeTheEventCycle_CancelsTheTaskWithThatToken() + { + // Arrange + var externalEvent = new AsyncRequestExternalEvent(argument => argument); + using var cancellationSource = new CancellationTokenSource(); + + // Act + var canceledTask = externalEvent.RaiseAsync(1, cancellationSource.Token); + cancellationSource.Cancel(); + + // Assert + var exception = await Assert.That(() => canceledTask).Throws(); + await Assert.That(exception!.CancellationToken).IsEqualTo(cancellationSource.Token); + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_TokenCanceledWhileTheHandlerExecutes_CompletesWithTheHandlerResult(CancellationToken cancellationToken) + { + // Arrange + using var cancellationSource = new CancellationTokenSource(); + var externalEvent = new AsyncRequestExternalEvent(argument => + { + cancellationSource.Cancel(); + return argument; + }); + + // Act + var result = await externalEvent.RaiseAsync(42, cancellationSource.Token).WaitAsync(cancellationToken); + + // Assert + await Assert.That(result).IsEqualTo(42); + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_TokenHandler_ReceivesTheTokenOfTheRaise(CancellationToken cancellationToken) + { + // Arrange + using var cancellationSource = new CancellationTokenSource(); + var externalEvent = new AsyncRequestExternalEvent((_, _, raiseToken) => raiseToken); + + // Act + var receivedToken = await externalEvent.RaiseAsync(42, cancellationSource.Token).WaitAsync(cancellationToken); + + // Assert + await Assert.That(receivedToken).IsEqualTo(cancellationSource.Token); + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_TokenHandlerThrowsForItsToken_CancelsTheTask(CancellationToken cancellationToken) + { + // Arrange + using var cancellationSource = new CancellationTokenSource(); + var externalEvent = new AsyncRequestExternalEvent((_, argument, raiseToken) => + { + cancellationSource.Cancel(); + raiseToken.ThrowIfCancellationRequested(); + return argument; + }); + + // Act + var raiseTask = externalEvent.RaiseAsync(42, cancellationSource.Token); + + // Assert + var exception = await Assert.That(() => raiseTask.WaitAsync(cancellationToken)).Throws(); + await Assert.That(exception!.CancellationToken).IsEqualTo(cancellationSource.Token); + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_HandlerThrowsForAnotherToken_FaultsTheTask(CancellationToken cancellationToken) + { + // Arrange + using var raiseCancellationSource = new CancellationTokenSource(); + using var otherCancellationSource = new CancellationTokenSource(); + otherCancellationSource.Cancel(); + var externalEvent = new AsyncRequestExternalEvent(_ => throw new OperationCanceledException(otherCancellationSource.Token)); + + // Act + var raiseTask = externalEvent.RaiseAsync(42, raiseCancellationSource.Token); + + // Assert + await Assert.That(() => raiseTask.WaitAsync(cancellationToken)).Throws(); + await Assert.That(raiseTask.IsFaulted).IsTrue(); + } + + [Test] + public async Task RaiseAsync_AllowDirectInvocationCanceledToken_ReturnsCanceledTaskWithoutExecution() + { + // Arrange + var executionCount = 0; + var externalEvent = new AsyncRequestExternalEvent(argument => argument + executionCount++, ExternalEventOptions.AllowDirectInvocation); + using var cancellationSource = new CancellationTokenSource(); + cancellationSource.Cancel(); + + // Act + var raiseTask = externalEvent.RaiseAsync(42, cancellationSource.Token); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(raiseTask.IsCanceled).IsTrue(); + await Assert.That(executionCount).IsZero(); + } + } + + [Test] + public async Task RaiseAsync_AllowDirectInvocationTokenCanceledWhileTheHandlerExecutes_CompletesWithTheHandlerResult() + { + // Arrange + using var cancellationSource = new CancellationTokenSource(); + var externalEvent = new AsyncRequestExternalEvent(argument => + { + cancellationSource.Cancel(); + return argument; + }, ExternalEventOptions.AllowDirectInvocation); + + // Act + var raiseTask = externalEvent.RaiseAsync(42, cancellationSource.Token); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(raiseTask.Status).IsEqualTo(TaskStatus.RanToCompletion); + await Assert.That(await raiseTask).IsEqualTo(42); + } + } + + [Test] + public async Task RaiseAsync_AllowDirectInvocationTokenHandlerThrowsForItsToken_ReturnsCanceledTask() + { + // Arrange + using var cancellationSource = new CancellationTokenSource(); + var externalEvent = new AsyncRequestExternalEvent((_, argument, raiseToken) => + { + cancellationSource.Cancel(); + raiseToken.ThrowIfCancellationRequested(); + return argument; + }, ExternalEventOptions.AllowDirectInvocation); + + // Act + var raiseTask = externalEvent.RaiseAsync(42, cancellationSource.Token); + + // Assert + await Assert.That(raiseTask.IsCanceled).IsTrue(); + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_TokenUiApplicationHandler_ReceivesTheUiApplicationAndToken(CancellationToken cancellationToken) + { + // Arrange + using var cancellationSource = new CancellationTokenSource(); + var externalEvent = new AsyncRequestExternalEvent<(IntPtr MainWindowHandle, CancellationToken Token)>((uiApplication, raiseToken) => (uiApplication.MainWindowHandle, raiseToken)); + + // Act + var (mainWindowHandle, receivedToken) = await externalEvent.RaiseAsync(cancellationSource.Token).WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(mainWindowHandle).IsEqualTo(UiApplication.MainWindowHandle); + await Assert.That(receivedToken).IsEqualTo(cancellationSource.Token); + } + } + + [Test] + [Timeout(10_000)] + public async Task RaiseAsync_TokenCanceledBeforeTheEventCycle_CancelsTheTaskWithoutExecution(CancellationToken cancellationToken) + { + // Arrange + var executionCount = 0; + var externalEvent = new AsyncRequestExternalEvent(() => ++executionCount); + using var cancellationSource = new CancellationTokenSource(); + + // Act + var canceledTask = externalEvent.RaiseAsync(cancellationSource.Token); + var followingTask = externalEvent.RaiseAsync(); + cancellationSource.Cancel(); + var followingResult = await followingTask.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(canceledTask.IsCanceled).IsTrue(); + await Assert.That(followingResult).IsEqualTo(1); + } + } } From a8a72aea2e1133a083bf8cb362e338f6c7da0527 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Fri, 23 Oct 2026 20:50:08 +0300 Subject: [PATCH 03/14] [Generator] Accept a cancellation token in generated events and their handlers --- CHANGELOG.md | 23 ++++ README.md | 40 ++++++- .../ExternalEventExtensionContainer.cs | 5 +- .../ExternalEventExtensionSignature.cs | 8 +- .../Analysis/ExternalEventExtractor.cs | 49 +++----- .../Analysis/ExternalEventParameters.cs | 50 +++++++++ .../Emission/ExternalEventWriter.cs | 106 +++++++++++++++--- .../ExternalEvents/ExternalEventDefinition.cs | 1 + .../ExternalEvents/ExternalEventTypeNames.cs | 1 + .../Attributes/ExternalEventAttribute.cs | 12 +- .../ExternalEventGeneratorExecutionTests.cs | 45 ++++++++ .../ExternalEventGeneratorNameBindingTests.cs | 30 +++++ .../ExternalEventGeneratorRegressionTests.cs | 31 +++++ .../ExternalEventGeneratorTests.cs | 74 +++++++++--- .../Fixtures/ExternalEvents.cs | 65 +++++++---- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 67 +++++++++++ ...ellationToken.2CA1BB3A517766C8.verified.cs | 46 ++++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...r.Model`1.Run.BFAFF1DAC26910EF.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...r.Model`1.Run.BFAFF1DAC26910EF.verified.cs | 4 +- ...mer.field.Run.6145C3C7A0D4FA0F.verified.cs | 4 +- ...eld.Model.Run.8F529F91CA4A8D2A.verified.cs | 4 +- ...l.application.3E69A3B91C661309.verified.cs | 4 +- ...er.Model.args.2DAA7B354C035418.verified.cs | 4 +- ...Model.handler.3D61EBDA00260524.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...1.Inner`1.Run.4DB1B2D9FFC22275.verified.cs | 4 +- ...r.Model.event.D890DA5C45B99396.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.class.Run.6B49BE1219B268DF.verified.cs | 4 +- ...r.Model`1.Run.BFAFF1DAC26910EF.verified.cs | 4 +- ...1.Model`1.Run.463E66F10AD83318.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 58 ++++++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 60 ++++++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 58 ++++++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 67 +++++++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 67 +++++++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 25 +++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 42 +++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 25 +++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 28 +++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 42 +++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 28 +++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 43 +++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 67 +++++++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 43 +++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 46 ++++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 67 +++++++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 46 ++++++++ ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 4 +- 70 files changed, 1443 insertions(+), 166 deletions(-) create mode 100644 source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventParameters.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Argument#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Method#Consumer.Model.cancellationToken.2CA1BB3A517766C8.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=BeforeLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=NullableLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs create mode 100644 tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ed6d8..84e1ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,29 @@ using var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds( await _deleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); ``` +### ExternalEvent Source Generator + +- A handler can declare a `CancellationToken` as its last parameter, after the optional `UIApplication` and the arguments. The asynchronous event passes it the token of `RaiseAsync()`, and the synchronous event passes `CancellationToken.None`. A `CancellationToken` in any other position remains an argument. +- The generated `RaiseAsync()` extension methods for several arguments accept an optional `CancellationToken`. + +```c# +[ExternalEvent] +private void DeleteWindows(UIApplication application, ICollection elementIds, CancellationToken cancellationToken) +{ + foreach (var elementId in elementIds) + { + cancellationToken.ThrowIfCancellationRequested(); + application.ActiveUIDocument.Document.Delete(elementId); + } +} + +// Generates: +// IExternalEvent> DeleteWindowsEvent +// IAsyncExternalEvent> DeleteWindowsAsyncEvent + +await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationToken); +``` + ### Breaking Changes - The `RaiseAsync` methods of `IAsyncExternalEvent`, `IAsyncExternalEvent`, `IAsyncRequestExternalEvent`, and `IAsyncRequestExternalEvent` take an optional `CancellationToken` parameter. Source code that calls them compiles unchanged. Add-ins compiled against an earlier version are rebuilt, and custom implementations of the interfaces add the parameter. diff --git a/README.md b/README.md index 5f14c37..c80bf50 100644 --- a/README.md +++ b/README.md @@ -614,9 +614,9 @@ public partial class MyViewModel public static partial class MyViewModelExtensions { - public static global::System.Threading.Tasks.Task RaiseAsync(this IAsyncRequestExternalEvent externalEvent, Level level, UV coordinate) + public static global::System.Threading.Tasks.Task RaiseAsync(this IAsyncRequestExternalEvent externalEvent, Level level, UV coordinate, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new MyViewModel.CreateRoomArgs(level, coordinate)); + return externalEvent.RaiseAsync(new MyViewModel.CreateRoomArgs(level, coordinate), cancellationToken); } } ``` @@ -626,6 +626,42 @@ If initialization throws, the next access retries it. +**Methods with CancellationToken parameter** + +Every generated `RaiseAsync()` accepts a `CancellationToken`, as described in [Cancellation](#cancellation). +Place `CancellationToken` last when the handler needs that token. +The event supplies it, the same way as `UIApplication`, and the token isn't one of the event arguments: + +```c# +[ExternalEvent] +private void DeleteWindows(UIApplication application, ICollection elementIds, CancellationToken cancellationToken) +{ + var document = application.ActiveUIDocument.Document; + using var transaction = new Transaction(document, "Delete windows"); + transaction.Start(); + + foreach (var elementId in elementIds) + { + cancellationToken.ThrowIfCancellationRequested(); + document.Delete(elementId); + } + + transaction.Commit(); +} + +// Generates: +// IExternalEvent> DeleteWindowsEvent +// IAsyncExternalEvent> DeleteWindowsAsyncEvent +``` + +```c# +using var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)); +await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); +``` + +`DeleteWindowsAsyncEvent` passes the token of `RaiseAsync()` to the handler, and `DeleteWindowsEvent` passes `CancellationToken.None`. +A `CancellationToken` in any other position is a regular argument. + **Enabling direct invocation** Set `AllowDirectInvocation` to execute the handler directly when Revit is already in API mode. diff --git a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtensionContainer.cs b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtensionContainer.cs index 8c6508c..969ebc3 100644 --- a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtensionContainer.cs +++ b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtensionContainer.cs @@ -8,10 +8,7 @@ internal static class ExternalEventExtensionContainer { public static (string ClassAccessibility, string? ConflictingName) Resolve(IMethodSymbol method, CancellationToken cancellationToken) { - var hasUiApplicationParameter = method.Parameters.Length > 0 && - method.Parameters[0].Type.WithNullableAnnotation(NullableAnnotation.NotAnnotated).ToDisplayString() == ExternalEventTypeNames.UiApplication.WithoutGlobalPrefix; - - if (method.Parameters.Length - (hasUiApplicationParameter ? 1 : 0) <= 1) + if (ExternalEventParameters.GetArgumentParameters(method).Count() <= 1) { return ("public", null); } diff --git a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtensionSignature.cs b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtensionSignature.cs index e9b463b..6620cae 100644 --- a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtensionSignature.cs +++ b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtensionSignature.cs @@ -14,7 +14,7 @@ internal sealed class ExternalEventExtensionSignature private readonly Dictionary _typeParameterNames = new(SymbolEqualityComparer.Default); - public ExternalEventExtensionDefinition Create(IMethodSymbol method, bool hasUiApplicationParameter, CancellationToken cancellationToken) + public ExternalEventExtensionDefinition Create(IMethodSymbol method, CancellationToken cancellationToken) { var containingTypes = new Stack(); for (var containingType = method.ContainingType; containingType is not null; containingType = containingType.ContainingType) @@ -24,7 +24,8 @@ public ExternalEventExtensionDefinition Create(IMethodSymbol method, bool hasUiA var reservedParameterNames = new HashSet(method.Parameters.Select(static parameter => parameter.Name), StringComparer.Ordinal) { - "externalEvent" + "externalEvent", + "cancellationToken" }; var typeParameters = ImmutableArray.CreateBuilder(); @@ -54,8 +55,7 @@ public ExternalEventExtensionDefinition Create(IMethodSymbol method, bool hasUiA typeParameters.ToImmutable(), constraints.ToString(), $"{Format(method.ContainingType, TypeFormat)}.{method.Name}Args", method.ReturnsVoid ? null : Format(method.ReturnType, TypeFormat), - method.Parameters - .Skip(hasUiApplicationParameter ? 1 : 0) + ExternalEventParameters.GetArgumentParameters(method) .Select(parameter => Format(parameter.Type, TypeFormat)) .ToImmutableArray()); } diff --git a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtractor.cs b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtractor.cs index d324566..3be3634 100644 --- a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtractor.cs +++ b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventExtractor.cs @@ -119,16 +119,13 @@ private static bool ValidateMethod(IMethodSymbol methodSymbol, INamedTypeSymbol private static string[] GetGeneratedMemberNames(IMethodSymbol method) { - var hasUiApplicationParameter = method.Parameters.Length > 0 && - method.Parameters[0].Type.ToDisplayString(NullableFlowFormat) == ExternalEventTypeNames.UiApplication.WithoutGlobalPrefix; - var generatedMemberNames = new List { $"{method.Name}AsyncEvent" }; if (method.ReturnsVoid) { generatedMemberNames.Add($"{method.Name}Event"); } - if (method.Parameters.Length - (hasUiApplicationParameter ? 1 : 0) > 1) + if (ExternalEventParameters.GetArgumentParameters(method).Count() > 1) { generatedMemberNames.Add($"{method.Name}Args"); } @@ -140,9 +137,11 @@ private static ExternalEventDefinition CreateEventDefinition(IMethodSymbol metho { var allowDirectInvocation = attribute.GetNamedArgument("AllowDirectInvocation", fallback: false); - var (hasUiApplicationParameter, extraParameters) = ClassifyParameters(methodSymbol); + var hasUiApplicationParameter = ExternalEventParameters.HasUiApplicationParameter(methodSymbol); + var hasCancellationTokenParameter = ExternalEventParameters.HasCancellationTokenParameter(methodSymbol); + var extraParameters = ClassifyParameters(methodSymbol); var (returnsVoid, returnType) = ExtractReturnType(methodSymbol); - var delegateType = BuildDelegateType(methodSymbol, hasUiApplicationParameter, returnsVoid, returnType, extraParameters); + var delegateType = BuildDelegateType(methodSymbol, returnsVoid, returnType); var typeHierarchy = GetTypeHierarchy(methodSymbol, cancellationToken); var containingNamespace = GetNamespace(methodSymbol.ContainingType); @@ -156,10 +155,11 @@ private static ExternalEventDefinition CreateEventDefinition(IMethodSymbol metho returnType, delegateType, hasUiApplicationParameter, + hasCancellationTokenParameter, allowDirectInvocation, typeHierarchy, extraParameters, - new ExternalEventExtensionSignature().Create(methodSymbol, hasUiApplicationParameter, cancellationToken), + new ExternalEventExtensionSignature().Create(methodSymbol, cancellationToken), methodSymbol.ContainingType.GetMembers().Select(static member => member.Name).ToImmutableArray()); } @@ -252,29 +252,20 @@ private static bool HasDuplicateOverloads(IMethodSymbol methodSymbol, INamedType return false; } - private static (bool HasUiApplicationParameter, EquatableArray ExtraParameters) ClassifyParameters(IMethodSymbol methodSymbol) + private static EquatableArray ClassifyParameters(IMethodSymbol methodSymbol) { - var parameters = methodSymbol.Parameters; - var hasUiApplicationParameter = false; var extraParameters = ImmutableArray.CreateBuilder(); var recordPropertyNames = GetReservedRecordPropertyNames(methodSymbol); - for (var parameterIndex = 0; parameterIndex < parameters.Length; parameterIndex++) + foreach (var parameter in ExternalEventParameters.GetArgumentParameters(methodSymbol)) { - var parameter = parameters[parameterIndex]; - if (parameterIndex == 0 && parameter.Type.ToDisplayString(NullableFlowFormat) == ExternalEventTypeNames.UiApplication.WithoutGlobalPrefix) - { - hasUiApplicationParameter = true; - continue; - } - var propertyName = char.ToUpperInvariant(parameter.Name[0]) + parameter.Name[1..]; propertyName = CSharpIdentifier.Reserve(propertyName, recordPropertyNames); var parameterTypeFullyQualified = parameter.Type.GetFullyQualifiedNameWithNullabilityAnnotations(); extraParameters.Add(new ExternalEventParameter(parameter.Name, parameterTypeFullyQualified, propertyName)); } - return (hasUiApplicationParameter, extraParameters.ToImmutable()); + return extraParameters.ToImmutable(); } private static HashSet GetReservedRecordPropertyNames(IMethodSymbol methodSymbol) @@ -302,23 +293,11 @@ private static (bool IsVoidReturn, string? ReturnTypeFullyQualified) ExtractRetu return (false, methodSymbol.ReturnType.GetFullyQualifiedNameWithNullabilityAnnotations()); } - private static string BuildDelegateType( - IMethodSymbol methodSymbol, - bool hasUiApplicationParameter, - bool returnsVoid, - string? fullyQualifiedReturnType, - EquatableArray extraParameters) + private static string BuildDelegateType(IMethodSymbol methodSymbol, bool returnsVoid, string? fullyQualifiedReturnType) { - var parameterTypes = new List(); - if (hasUiApplicationParameter) - { - parameterTypes.Add(methodSymbol.Parameters[0].Type.GetFullyQualifiedNameWithNullabilityAnnotations()); - } - - foreach (var parameter in extraParameters) - { - parameterTypes.Add(parameter.FullyQualifiedType); - } + var parameterTypes = methodSymbol.Parameters + .Select(static parameter => parameter.Type.GetFullyQualifiedNameWithNullabilityAnnotations()) + .ToList(); if (returnsVoid) { diff --git a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventParameters.cs b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventParameters.cs new file mode 100644 index 0000000..f833075 --- /dev/null +++ b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Analysis/ExternalEventParameters.cs @@ -0,0 +1,50 @@ +using Microsoft.CodeAnalysis; +using Nice3point.Revit.Toolkit.SourceGenerators.CSharp; + +namespace Nice3point.Revit.Toolkit.SourceGenerators.ExternalEvents.Analysis; + +/// +/// Classifies the parameters of a method marked with [ExternalEvent]. +/// +/// +/// The event supplies a leading UIApplication parameter and a trailing CancellationToken parameter. +/// The parameters between them are the arguments the caller passes when raising the event. +/// +internal static class ExternalEventParameters +{ + private static readonly SymbolDisplayFormat TypeNameFormat = new( + SymbolDisplayGlobalNamespaceStyle.Omitted, + SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); + + /// + /// Determines whether the first parameter of the method receives the current UIApplication. + /// + public static bool HasUiApplicationParameter(IMethodSymbol method) + { + return method.Parameters.Length > 0 && IsType(method.Parameters[0].Type, ExternalEventTypeNames.UiApplication); + } + + /// + /// Determines whether the last parameter of the method receives the cancellation token of the raise. + /// + public static bool HasCancellationTokenParameter(IMethodSymbol method) + { + var firstArgumentIndex = HasUiApplicationParameter(method) ? 1 : 0; + return method.Parameters.Length > firstArgumentIndex && IsType(method.Parameters[^1].Type, ExternalEventTypeNames.CancellationToken); + } + + /// + /// Gets the parameters the caller passes when raising the event, in declaration order. + /// + public static IEnumerable GetArgumentParameters(IMethodSymbol method) + { + var firstArgumentIndex = HasUiApplicationParameter(method) ? 1 : 0; + var argumentCount = method.Parameters.Length - firstArgumentIndex - (HasCancellationTokenParameter(method) ? 1 : 0); + return method.Parameters.Skip(firstArgumentIndex).Take(argumentCount); + } + + private static bool IsType(ITypeSymbol type, FullyQualifiedTypeName typeName) + { + return type.ToDisplayString(TypeNameFormat) == typeName.WithoutGlobalPrefix; + } +} diff --git a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs index fe27b7c..53abdd1 100644 --- a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs +++ b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs @@ -123,17 +123,22 @@ private static void WriteEventProperty(CodeWriter writer, ExternalEventDefinitio var eventType = FormatGenericType(eventTypeName.WithGlobalPrefix, typeArguments); var interfaceType = FormatGenericType(interfaceTypeName.WithGlobalPrefix, typeArguments); - var captureHandler = eventDefinition is { IsStatic: false, ExtraParameters.Length: > 1 } && eventDefinition.TypeHierarchy[^1].Keyword is "struct" or "record struct"; + var usesHandlerDelegate = UsesHandlerDelegate(eventDefinition, propertyKind); + var captureHandler = !usesHandlerDelegate && !eventDefinition.IsStatic && eventDefinition.TypeHierarchy[^1].Keyword is "struct" or "record struct"; var reservedLocalNames = new HashSet(eventDefinition.TypeHierarchy.SelectMany(type => type.TypeParameters), StringComparer.Ordinal) { eventDefinition.MethodName }; var handlerName = captureHandler ? CSharpIdentifier.Reserve("handler", reservedLocalNames) : CSharpIdentifier.Escape(eventDefinition.MethodName); - var applicationName = CSharpIdentifier.Reserve("application", reservedLocalNames); - var argumentsName = CSharpIdentifier.Reserve("args", reservedLocalNames); + var lambdaParameterNames = new LambdaParameterNames + { + Application = CSharpIdentifier.Reserve("application", reservedLocalNames), + Arguments = CSharpIdentifier.Reserve("args", reservedLocalNames), + CancellationToken = CSharpIdentifier.Reserve("cancellationToken", reservedLocalNames) + }; - var initializer = BuildPropertyInitializer(eventDefinition, eventType, optionsArgument, handlerName, applicationName, argumentsName); + var initializer = BuildPropertyInitializer(eventDefinition, propertyKind, eventType, optionsArgument, handlerName, lambdaParameterNames); var eventProperty = new EventPropertyDefinition(staticModifier, interfaceType, propertySuffix, initializer, captureHandler ? handlerName : null); WritePropertyWithBackingField(writer, eventDefinition, useFieldKeyword, useLockType, eventProperty); @@ -223,7 +228,7 @@ private static void WriteRaiseExtensionMethod(CodeWriter writer, ExternalEventDe } var parameters = BuildExtensionMethodParameters(eventDefinition); - var recordArguments = BuildRecordConstructorArguments(eventDefinition); + var raiseArguments = $"new {qualifiedRecordType}({BuildRecordConstructorArguments(eventDefinition)})"; var typeParameters = FormatTypeParameters(eventDefinition.Extension.TypeParameters); var genericMethodName = FormatGenericType(methodName, typeParameters); @@ -234,11 +239,18 @@ private static void WriteRaiseExtensionMethod(CodeWriter writer, ExternalEventDe var receiverName = CSharpIdentifier.Reserve("externalEvent", parameterNames); var accessibility = GetMostRestrictiveAccessibility(eventDefinition.TypeHierarchy); + if (isAsync) + { + var cancellationTokenName = CSharpIdentifier.Reserve("cancellationToken", parameterNames); + parameters = $"{parameters}, {ExternalEventTypeNames.CancellationToken.WithGlobalPrefix} {cancellationTokenName} = default"; + raiseArguments = $"{raiseArguments}, {cancellationTokenName}"; + } + WriteExcludeFromCodeCoverageAttributes(writer); WriteGeneratedCodeAttributes(writer); using (writer.BeginBlock($"{accessibility} static {returnType} {genericMethodName}(this {interfaceType} {receiverName}, {parameters}){constraints}")) { - writer.AppendLine($"return {receiverName}.{methodName}(new {qualifiedRecordType}({recordArguments}));"); + writer.AppendLine($"return {receiverName}.{methodName}({raiseArguments});"); } } @@ -270,41 +282,98 @@ private static void WriteGeneratedCodeAttributes(CodeWriter writer) return argType ?? returnType; } - private static string BuildHandlerInvocation(ExternalEventDefinition eventDefinition, string argumentsName, string handlerName, string applicationName) + private static string BuildHandlerInvocation(ExternalEventDefinition eventDefinition, EventPropertyKind propertyKind, string handlerName, LambdaParameterNames parameterNames) { var arguments = new List(); if (eventDefinition.HasUiApplicationParameter) { - arguments.Add(applicationName); + arguments.Add(parameterNames.Application); } - foreach (var parameter in eventDefinition.ExtraParameters) + if (eventDefinition.ExtraParameters.Length == 1) { - arguments.Add($"{argumentsName}.{CSharpIdentifier.Escape(parameter.RecordPropertyName)}"); + arguments.Add(parameterNames.Arguments); + } + else + { + foreach (var parameter in eventDefinition.ExtraParameters) + { + arguments.Add($"{parameterNames.Arguments}.{CSharpIdentifier.Escape(parameter.RecordPropertyName)}"); + } + } + + if (eventDefinition.HasCancellationTokenParameter) + { + arguments.Add(propertyKind == EventPropertyKind.Sync ? $"{ExternalEventTypeNames.CancellationToken.WithGlobalPrefix}.None" : parameterNames.CancellationToken); } return $"{handlerName}({string.Join(", ", arguments)})"; } + private static string BuildLambdaParameters(ExternalEventDefinition eventDefinition, EventPropertyKind propertyKind, LambdaParameterNames parameterNames) + { + var receivesCancellationToken = eventDefinition.HasCancellationTokenParameter && propertyKind != EventPropertyKind.Sync; + + var parameters = new List(); + if (eventDefinition.HasUiApplicationParameter || receivesCancellationToken) + { + parameters.Add(parameterNames.Application); + } + + if (eventDefinition.ExtraParameters.Length > 0) + { + parameters.Add(parameterNames.Arguments); + } + + if (receivesCancellationToken) + { + parameters.Add(parameterNames.CancellationToken); + } + + return string.Join(", ", parameters); + } + private static EventInitializer BuildPropertyInitializer( ExternalEventDefinition eventDefinition, + EventPropertyKind propertyKind, string eventType, string optionsArgument, string handlerName, - string applicationName, - string argumentsName) + LambdaParameterNames parameterNames) { - if (eventDefinition.ExtraParameters.Length < 2) + if (UsesHandlerDelegate(eventDefinition, propertyKind)) { return new EventInitializer($"new {eventType}(new {eventDefinition.FullyQualifiedDelegateType}({handlerName})", LambdaBody: null, optionsArgument); } - var handlerInvocation = BuildHandlerInvocation(eventDefinition, argumentsName, handlerName, applicationName); - var lambdaParameters = eventDefinition.HasUiApplicationParameter ? $"{applicationName}, {argumentsName}" : argumentsName; + var handlerInvocation = BuildHandlerInvocation(eventDefinition, propertyKind, handlerName, parameterNames); + var lambdaParameters = BuildLambdaParameters(eventDefinition, propertyKind, parameterNames); var returnKeyword = eventDefinition.ReturnsVoid ? string.Empty : "return "; return new EventInitializer($"new {eventType}(({lambdaParameters}) =>", $"{returnKeyword}{handlerInvocation};", optionsArgument); } + /// + /// Determines whether the handler delegate matches a constructor of the event as declared. + /// + /// + /// Several arguments arrive as one generated record, and a handler that receives a cancellation token matches only the asynchronous constructors that pass the UIApplication too. + /// Every other handler is adapted with a lambda. + /// + private static bool UsesHandlerDelegate(ExternalEventDefinition eventDefinition, EventPropertyKind propertyKind) + { + if (eventDefinition.ExtraParameters.Length > 1) + { + return false; + } + + if (!eventDefinition.HasCancellationTokenParameter) + { + return true; + } + + return propertyKind != EventPropertyKind.Sync && eventDefinition.HasUiApplicationParameter; + } + private static string BuildExtensionMethodParameters(ExternalEventDefinition eventDefinition) { var parameters = new List(); @@ -384,6 +453,13 @@ private static string FormatGenericType(string typeName, string? typeArguments) private readonly record struct EventInitializer(string Expression, string? LambdaBody, string OptionsArgument); + private readonly record struct LambdaParameterNames + { + public required string Application { get; init; } + public required string Arguments { get; init; } + public required string CancellationToken { get; init; } + } + private enum EventPropertyKind { Sync, diff --git a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/ExternalEventDefinition.cs b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/ExternalEventDefinition.cs index 829c4d9..8d054f1 100644 --- a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/ExternalEventDefinition.cs +++ b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/ExternalEventDefinition.cs @@ -15,6 +15,7 @@ internal sealed record ExternalEventDefinition( string? FullyQualifiedReturnType, string FullyQualifiedDelegateType, bool HasUiApplicationParameter, + bool HasCancellationTokenParameter, bool AllowDirectInvocation, EquatableArray TypeHierarchy, EquatableArray ExtraParameters, diff --git a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/ExternalEventTypeNames.cs b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/ExternalEventTypeNames.cs index 35363ad..e95d98f 100644 --- a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/ExternalEventTypeNames.cs +++ b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/ExternalEventTypeNames.cs @@ -22,6 +22,7 @@ internal static class ExternalEventTypeNames public static readonly FullyQualifiedTypeName ExternalEventRequest = "Autodesk.Revit.UI.ExternalEventRequest"; // System types + public static readonly FullyQualifiedTypeName CancellationToken = "System.Threading.CancellationToken"; public static readonly FullyQualifiedTypeName Task = "System.Threading.Tasks.Task"; public static readonly FullyQualifiedTypeName TaskGeneric = "System.Threading.Tasks.Task`1"; } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs b/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs index b94838f..553a67d 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs @@ -74,9 +74,19 @@ namespace Nice3point.Revit.Toolkit.External; /// /// // Extension methods: /// public static ExternalEventRequest Raise(this IExternalEvent<DeleteInstancesArgs> externalEvent, BuiltInCategory category, int count); -/// public static Task RaiseAsync(this IAsyncExternalEvent<DeleteInstancesArgs> externalEvent, BuiltInCategory category, int count); +/// public static Task RaiseAsync(this IAsyncExternalEvent<DeleteInstancesArgs> externalEvent, BuiltInCategory category, int count, CancellationToken cancellationToken = default); /// /// +/// +/// Methods with a cancellation token. A method can declare a as its last parameter, +/// after the optional UIApplication and the extra parameters. The token is not an extra parameter: +/// +/// void Method(UIApplication, ElementId, CancellationToken); +/// +/// Will generate an property and an property with an ElementId argument. +/// The asynchronous property passes the token of RaiseAsync to the method, and the synchronous property passes . +/// A in any other position is an extra parameter. +/// ///
[PublicAPI] [AttributeUsage(AttributeTargets.Method, Inherited = false)] diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorExecutionTests.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorExecutionTests.cs index 369f1cf..809e5c0 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorExecutionTests.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorExecutionTests.cs @@ -1,6 +1,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; using Nice3point.Revit.Toolkit.SourceGenerators.Tests.Helpers; +using TUnit.Assertions.Enums; namespace Nice3point.Revit.Toolkit.SourceGenerators.Tests; @@ -175,4 +176,48 @@ await second.RunAsyncEvent.RaiseAsync(2), await Assert.That(result[1]).IsEqualTo(22); await Assert.That(result[2]).IsZero(); } + + [Test] + public async Task CancellationTokenHandler_ReceivesTheTokenOfTheRaiseAsync() + { + const string source = """ + using System.Threading; + using System.Threading.Tasks; + using Nice3point.Revit.Toolkit.External; + + namespace Consumer; + + public partial class Model + { + public CancellationToken ReceivedToken; + + [ExternalEvent] + private bool Run(int quantity, string label, CancellationToken cancellationToken) => cancellationToken.CanBeCanceled; + + [ExternalEvent] + private void Notify(CancellationToken cancellationToken) => ReceivedToken = cancellationToken; + } + + public static class Probe + { + public static async Task RunAsync() + { + using var cancellationSource = new CancellationTokenSource(); + var model = new Model(); + var extensionToken = await model.RunAsyncEvent.RaiseAsync(42, "Walls", cancellationSource.Token); + var recordToken = await model.RunAsyncEvent.RaiseAsync(new Model.RunArgs(42, "Walls"), cancellationSource.Token); + var omittedToken = await model.RunAsyncEvent.RaiseAsync(42, "Walls"); + model.NotifyEvent.Raise(); + var synchronousToken = model.ReceivedToken.CanBeCanceled; + await model.NotifyAsyncEvent.RaiseAsync(cancellationSource.Token); + return new[] { extensionToken, recordToken, omittedToken, synchronousToken, model.ReceivedToken == cancellationSource.Token }; + } + } + """; + + var (driver, input) = await GeneratorTest.RunAsync(source); + var result = await GeneratorTest.ExecuteAsync(driver, input); + + await Assert.That(result).IsEquivalentTo([true, true, false, false, true], CollectionOrdering.Matching); + } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorNameBindingTests.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorNameBindingTests.cs index cb7a6d4..960abb1 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorNameBindingTests.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorNameBindingTests.cs @@ -38,6 +38,36 @@ public void UseGeneratedMembers() await Verify(driver.GetRunResult()).UseParameters(methodName); } + [Test] + [Arguments("Method", "cancellationToken", "System.Threading.CancellationToken token", "")] + [Arguments("Argument", "Run", "int cancellationToken, int count, System.Threading.CancellationToken token", "2, 5")] + public async Task CancellationTokenName_KeepsDistinctBindingsAsync(string scenario, string methodName, string parameters, string arguments) + { + var cancellableArguments = arguments.Length == 0 ? "System.Threading.CancellationToken.None" : $"{arguments}, System.Threading.CancellationToken.None"; + var source = $$""" + using Nice3point.Revit.Toolkit.External; + + namespace Consumer; + + public partial class Model + { + [ExternalEvent] + private void {{methodName}}({{parameters}}) { } + + public void UseGeneratedMembers() + { + _ = {{methodName}}Event.Raise({{arguments}}); + _ = {{methodName}}AsyncEvent.RaiseAsync({{cancellableArguments}}); + } + } + """; + + var (driver, _) = await GeneratorTest.RunAsync(source); + + await Assert.That(driver.GetRunResult().GeneratedTrees.Length).IsEqualTo(1); + await Verify(driver.GetRunResult()).UseParameters(scenario); + } + [Test] [Arguments("clone")] [Arguments("equals")] diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorRegressionTests.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorRegressionTests.cs index a320c14..b77137e 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorRegressionTests.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorRegressionTests.cs @@ -201,6 +201,37 @@ private void Run(int quantity, string label) { } await Verify(driver.GetRunResult()).UseParameters(declaration, languageVersion); } + [Test] + [Arguments("struct", LanguageVersion.CSharp13)] + [Arguments("struct", LanguageVersion.CSharp14)] + [Arguments("record struct", LanguageVersion.CSharp14)] + public async Task StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync(string declaration, LanguageVersion languageVersion) + { + var source = $$""" + using System.Threading; + using Nice3point.Revit.Toolkit.External; + + namespace Consumer; + + public partial {{declaration}} Model + { + [ExternalEvent] + private void Run(int quantity, CancellationToken cancellationToken) { } + + public void UseGeneratedMembers() + { + _ = RunEvent.Raise(42); + _ = RunAsyncEvent.RaiseAsync(42, CancellationToken.None); + } + } + """; + + var (driver, _) = await GeneratorTest.RunAsync(source, languageVersion: languageVersion); + + await Assert.That(driver.GetRunResult().GeneratedTrees).Count().IsEqualTo(1); + await Verify(driver.GetRunResult()).UseParameters(declaration, languageVersion); + } + [Test] [Arguments("Capitalization", "int value, int Value")] [Arguments("ExtensionReceiver", "int externalEvent, int quantity")] diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorTests.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorTests.cs index 7f77492..90b50f9 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorTests.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExternalEventGeneratorTests.cs @@ -8,19 +8,31 @@ namespace Nice3point.Revit.Toolkit.SourceGenerators.Tests; public sealed class ExternalEventGeneratorTests { [Test] - [Arguments("Void", false, false, 0)] - [Arguments("VoidApplication", false, true, 0)] - [Arguments("VoidArgument", false, false, 1)] - [Arguments("VoidApplicationArgument", false, true, 1)] - [Arguments("VoidArguments", false, false, 2)] - [Arguments("VoidApplicationArguments", false, true, 2)] - [Arguments("Result", true, false, 0)] - [Arguments("ResultApplication", true, true, 0)] - [Arguments("ResultArgument", true, false, 1)] - [Arguments("ResultApplicationArgument", true, true, 1)] - [Arguments("ResultArguments", true, false, 2)] - [Arguments("ResultApplicationArguments", true, true, 2)] - public async Task MethodSignature_GeneratesCompilableEventsAsync(string scenario, bool returnsValue, bool hasApplication, int argumentCount) + [Arguments("Void", false, false, 0, false)] + [Arguments("VoidApplication", false, true, 0, false)] + [Arguments("VoidArgument", false, false, 1, false)] + [Arguments("VoidApplicationArgument", false, true, 1, false)] + [Arguments("VoidArguments", false, false, 2, false)] + [Arguments("VoidApplicationArguments", false, true, 2, false)] + [Arguments("Result", true, false, 0, false)] + [Arguments("ResultApplication", true, true, 0, false)] + [Arguments("ResultArgument", true, false, 1, false)] + [Arguments("ResultApplicationArgument", true, true, 1, false)] + [Arguments("ResultArguments", true, false, 2, false)] + [Arguments("ResultApplicationArguments", true, true, 2, false)] + [Arguments("VoidToken", false, false, 0, true)] + [Arguments("VoidApplicationToken", false, true, 0, true)] + [Arguments("VoidArgumentToken", false, false, 1, true)] + [Arguments("VoidApplicationArgumentToken", false, true, 1, true)] + [Arguments("VoidArgumentsToken", false, false, 2, true)] + [Arguments("VoidApplicationArgumentsToken", false, true, 2, true)] + [Arguments("ResultToken", true, false, 0, true)] + [Arguments("ResultApplicationToken", true, true, 0, true)] + [Arguments("ResultArgumentToken", true, false, 1, true)] + [Arguments("ResultApplicationArgumentToken", true, true, 1, true)] + [Arguments("ResultArgumentsToken", true, false, 2, true)] + [Arguments("ResultApplicationArgumentsToken", true, true, 2, true)] + public async Task MethodSignature_GeneratesCompilableEventsAsync(string scenario, bool returnsValue, bool hasApplication, int argumentCount, bool hasCancellationToken) { var parameters = new List(); if (hasApplication) @@ -38,6 +50,11 @@ public async Task MethodSignature_GeneratesCompilableEventsAsync(string scenario parameters.Add("string label"); } + if (hasCancellationToken) + { + parameters.Add("System.Threading.CancellationToken cancellationToken"); + } + var arguments = argumentCount switch { 0 => string.Empty, @@ -45,6 +62,7 @@ public async Task MethodSignature_GeneratesCompilableEventsAsync(string scenario _ => "42, \"Walls\"" }; + var cancellableArguments = argumentCount == 0 ? "System.Threading.CancellationToken.None" : $"{arguments}, System.Threading.CancellationToken.None"; var returnType = returnsValue ? "int" : "void"; var body = returnsValue ? "=> 42;" : "{ }"; var synchronousCall = returnsValue ? string.Empty : $"_ = RunEvent.Raise({arguments});"; @@ -62,6 +80,7 @@ public void UseGeneratedMembers() { {{synchronousCall}} _ = RunAsyncEvent.RaiseAsync({{arguments}}); + _ = RunAsyncEvent.RaiseAsync({{cancellableArguments}}); } } """; @@ -357,6 +376,35 @@ public void UseGeneratedMembers(UIApplication application) await Verify(driver.GetRunResult()); } + [Test] + [Arguments("BeforeLastParameter", "System.Threading.CancellationToken token, int quantity", "System.Threading.CancellationToken.None, 42")] + [Arguments("NullableLastParameter", "int quantity, System.Threading.CancellationToken? token", "42, null")] + public async Task CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync(string scenario, string parameters, string arguments) + { + var source = $$""" + using Nice3point.Revit.Toolkit.External; + + namespace Consumer; + + public partial class Model + { + [ExternalEvent] + private void Run({{parameters}}) { } + + public void UseGeneratedMembers() + { + _ = RunEvent.Raise({{arguments}}); + _ = RunAsyncEvent.RaiseAsync({{arguments}}, System.Threading.CancellationToken.None); + } + } + """; + + var (driver, _) = await GeneratorTest.RunAsync(source); + + await Assert.That(driver.GetRunResult().GeneratedTrees).Count().IsEqualTo(1); + await Verify(driver.GetRunResult()).UseParameters(scenario); + } + [Test] public async Task UnrelatedEdit_ReusesGeneratedOutputAsync() { diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Fixtures/ExternalEvents.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Fixtures/ExternalEvents.cs index b7b83d0..a77582a 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Fixtures/ExternalEvents.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Fixtures/ExternalEvents.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Autodesk.Revit.UI @@ -31,10 +32,10 @@ public enum ExternalEventOptions { None = 0, AllowDirectInvocation = 1 } public interface IExternalEvent { Autodesk.Revit.UI.ExternalEventRequest Raise(); } public interface IExternalEvent { Autodesk.Revit.UI.ExternalEventRequest Raise(T args); } - public interface IAsyncExternalEvent { Task RaiseAsync(); } - public interface IAsyncExternalEvent { Task RaiseAsync(T args); } - public interface IAsyncRequestExternalEvent { Task RaiseAsync(); } - public interface IAsyncRequestExternalEvent { Task RaiseAsync(T args); } + public interface IAsyncExternalEvent { Task RaiseAsync(CancellationToken cancellationToken = default); } + public interface IAsyncExternalEvent { Task RaiseAsync(T args, CancellationToken cancellationToken = default); } + public interface IAsyncRequestExternalEvent { Task RaiseAsync(CancellationToken cancellationToken = default); } + public interface IAsyncRequestExternalEvent { Task RaiseAsync(T args, CancellationToken cancellationToken = default); } public class ExternalEvent : IExternalEvent { @@ -56,37 +57,61 @@ public ExternalEvent(Action handler, Externa public class AsyncExternalEvent : IAsyncExternalEvent { - private readonly Action _handler; - public AsyncExternalEvent(Action handler, ExternalEventOptions options = default) { _handler = handler; EventConstruction.Observer?.Invoke(); } + private readonly Action _handler; + public AsyncExternalEvent(Action handler, ExternalEventOptions options = default) + : this((application, cancellationToken) => handler(), options) { } public AsyncExternalEvent(Action handler, ExternalEventOptions options = default) - : this(() => handler(new Autodesk.Revit.UI.UIApplication()), options) { } - public Task RaiseAsync() { _handler(); return Task.CompletedTask; } + : this((application, cancellationToken) => handler(application), options) { } + public AsyncExternalEvent(Action handler, ExternalEventOptions options = default) + { + _handler = cancellationToken => handler(new Autodesk.Revit.UI.UIApplication(), cancellationToken); + EventConstruction.Observer?.Invoke(); + } + public Task RaiseAsync(CancellationToken cancellationToken = default) { _handler(cancellationToken); return Task.CompletedTask; } } public class AsyncExternalEvent : IAsyncExternalEvent { - private readonly Action _handler; - public AsyncExternalEvent(Action handler, ExternalEventOptions options = default) { _handler = handler; EventConstruction.Observer?.Invoke(); } + private readonly Action _handler; + public AsyncExternalEvent(Action handler, ExternalEventOptions options = default) + : this((application, args, cancellationToken) => handler(args), options) { } public AsyncExternalEvent(Action handler, ExternalEventOptions options = default) - : this(args => handler(new Autodesk.Revit.UI.UIApplication(), args), options) { } - public Task RaiseAsync(T args) { _handler(args); return Task.CompletedTask; } + : this((application, args, cancellationToken) => handler(application, args), options) { } + public AsyncExternalEvent(Action handler, ExternalEventOptions options = default) + { + _handler = (args, cancellationToken) => handler(new Autodesk.Revit.UI.UIApplication(), args, cancellationToken); + EventConstruction.Observer?.Invoke(); + } + public Task RaiseAsync(T args, CancellationToken cancellationToken = default) { _handler(args, cancellationToken); return Task.CompletedTask; } } public class AsyncRequestExternalEvent : IAsyncRequestExternalEvent { - private readonly Func _handler; - public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options = default) { _handler = handler; EventConstruction.Observer?.Invoke(); } + private readonly Func _handler; + public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options = default) + : this((application, cancellationToken) => handler(), options) { } public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options = default) - : this(() => handler(new Autodesk.Revit.UI.UIApplication()), options) { } - public Task RaiseAsync() => Task.FromResult(_handler()); + : this((application, cancellationToken) => handler(application), options) { } + public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options = default) + { + _handler = cancellationToken => handler(new Autodesk.Revit.UI.UIApplication(), cancellationToken); + EventConstruction.Observer?.Invoke(); + } + public Task RaiseAsync(CancellationToken cancellationToken = default) => Task.FromResult(_handler(cancellationToken)); } public class AsyncRequestExternalEvent : IAsyncRequestExternalEvent { - private readonly Func _handler; - public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options = default) { _handler = handler; EventConstruction.Observer?.Invoke(); } + private readonly Func _handler; + public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options = default) + : this((application, args, cancellationToken) => handler(args), options) { } public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options = default) - : this(args => handler(new Autodesk.Revit.UI.UIApplication(), args), options) { } - public Task RaiseAsync(T args) => Task.FromResult(_handler(args)); + : this((application, args, cancellationToken) => handler(application, args), options) { } + public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options = default) + { + _handler = (args, cancellationToken) => handler(new Autodesk.Revit.UI.UIApplication(), args, cancellationToken); + EventConstruction.Observer?.Invoke(); + } + public Task RaiseAsync(T args, CancellationToken cancellationToken = default) => Task.FromResult(_handler(args, cancellationToken)); } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Argument#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Argument#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..fc20d34 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Argument#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,67 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.ExternalEvent((args) => + { + Run(args.CancellationToken, args.Count, global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((application, args, cancellationToken) => + { + Run(args.CancellationToken, args.Count, cancellationToken); + }); + } + } + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public sealed record RunArgs(int CancellationToken, int Count); + } + + public static partial class ModelExtensions + { + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::Autodesk.Revit.UI.ExternalEventRequest Raise(this global::Nice3point.Revit.Toolkit.External.IExternalEvent externalEvent, int cancellationToken, int count) + { + return externalEvent.Raise(new global::Consumer.Model.RunArgs(cancellationToken, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int cancellationToken, int count, global::System.Threading.CancellationToken cancellationToken1 = default) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(cancellationToken, count), cancellationToken1); + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Method#Consumer.Model.cancellationToken.2CA1BB3A517766C8.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Method#Consumer.Model.cancellationToken.2CA1BB3A517766C8.verified.cs new file mode 100644 index 0000000..d593329 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Method#Consumer.Model.cancellationToken.2CA1BB3A517766C8.verified.cs @@ -0,0 +1,46 @@ +//HintName: Consumer.Model.cancellationToken.2CA1BB3A517766C8.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _cancellationTokenEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent cancellationTokenEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _cancellationTokenEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.ExternalEvent(() => + { + cancellationToken(global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _cancellationTokenAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent cancellationTokenAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _cancellationTokenAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((application, cancellationToken1) => + { + cancellationToken(cancellationToken1); + }); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index c5cda72..8297d0e 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - internal static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count) + internal static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs index c88f45f..5f76eae 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 1f6380b..49c960b 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs index 3d90e28..ff0c796 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - internal static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int count) + internal static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=Consumer_typeName=field#Consumer.field.Run.6145C3C7A0D4FA0F.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=Consumer_typeName=field#Consumer.field.Run.6145C3C7A0D4FA0F.verified.cs index e93a076..a75a73a 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=Consumer_typeName=field#Consumer.field.Run.6145C3C7A0D4FA0F.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=Consumer_typeName=field#Consumer.field.Run.6145C3C7A0D4FA0F.verified.cs @@ -59,9 +59,9 @@ public static partial class @fieldExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.field.RunArgs(value, count)); + return externalEvent.RaiseAsync(new global::Consumer.field.RunArgs(value, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=field_typeName=Model#field.Model.Run.8F529F91CA4A8D2A.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=field_typeName=Model#field.Model.Run.8F529F91CA4A8D2A.verified.cs index f12981b..b0c1675 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=field_typeName=Model#field.Model.Run.8F529F91CA4A8D2A.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=field_typeName=Model#field.Model.Run.8F529F91CA4A8D2A.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::field.Model.RunArgs(value, count)); + return externalEvent.RaiseAsync(new global::field.Model.RunArgs(value, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=application#Consumer.Model.application.3E69A3B91C661309.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=application#Consumer.Model.application.3E69A3B91C661309.verified.cs index 45c7f5c..5bb36f1 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=application#Consumer.Model.application.3E69A3B91C661309.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=application#Consumer.Model.application.3E69A3B91C661309.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.applicationArgs(first, second)); + return externalEvent.RaiseAsync(new global::Consumer.Model.applicationArgs(first, second), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=args#Consumer.Model.args.2DAA7B354C035418.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=args#Consumer.Model.args.2DAA7B354C035418.verified.cs index 9922726..8e228f5 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=args#Consumer.Model.args.2DAA7B354C035418.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=args#Consumer.Model.args.2DAA7B354C035418.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.argsArgs(first, second)); + return externalEvent.RaiseAsync(new global::Consumer.Model.argsArgs(first, second), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=handler#Consumer.Model.handler.3D61EBDA00260524.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=handler#Consumer.Model.handler.3D61EBDA00260524.verified.cs index 36ccf02..9b4b293 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=handler#Consumer.Model.handler.3D61EBDA00260524.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=handler#Consumer.Model.handler.3D61EBDA00260524.verified.cs @@ -71,9 +71,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.handlerArgs(first, second)); + return externalEvent.RaiseAsync(new global::Consumer.Model.handlerArgs(first, second), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=clone#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=clone#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 3c8aeca..ab293b8 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=clone#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=clone#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int clone, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int clone, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(clone, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(clone, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=deconstruct#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=deconstruct#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index b22458d..b111ac5 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=deconstruct#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=deconstruct#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int deconstruct, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int deconstruct, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(deconstruct, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(deconstruct, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equalityContract#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equalityContract#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index bcc9780..45cdcc9 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equalityContract#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equalityContract#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int equalityContract, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int equalityContract, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(equalityContract, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(equalityContract, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equals#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equals#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index af1721a..d73c5ac 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equals#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equals#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int @equals, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int @equals, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(@equals, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(@equals, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=getHashCode#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=getHashCode#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 5728630..2ab58dc 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=getHashCode#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=getHashCode#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int getHashCode, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int getHashCode, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(getHashCode, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(getHashCode, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=printMembers#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=printMembers#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index c3baca6..0e2d2fc 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=printMembers#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=printMembers#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int printMembers, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int printMembers, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(printMembers, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(printMembers, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=toString#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=toString#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 3f4197b..197af53 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=toString#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=toString#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int toString, int count) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int toString, int count, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(toString, count)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(toString, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.ShadowedGenericParameter_PreservesDistinctExtensionTypeArgumentsAsync#Consumer.Outer`1.Inner`1.Run.4DB1B2D9FFC22275.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.ShadowedGenericParameter_PreservesDistinctExtensionTypeArgumentsAsync#Consumer.Outer`1.Inner`1.Run.4DB1B2D9FFC22275.verified.cs index d220622..c16ff38 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.ShadowedGenericParameter_PreservesDistinctExtensionTypeArgumentsAsync#Consumer.Outer`1.Inner`1.Run.4DB1B2D9FFC22275.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.ShadowedGenericParameter_PreservesDistinctExtensionTypeArgumentsAsync#Consumer.Outer`1.Inner`1.Run.4DB1B2D9FFC22275.verified.cs @@ -62,9 +62,9 @@ public static partial class OuterExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.Inner.RunArgs> externalEvent, TValue1 value, int count) where TValue : class where TValue1 : struct + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.Inner.RunArgs> externalEvent, TValue1 value, int count, global::System.Threading.CancellationToken cancellationToken = default) where TValue : class where TValue1 : struct { - return externalEvent.RaiseAsync(new global::Consumer.Outer.Inner.RunArgs(value, count)); + return externalEvent.RaiseAsync(new global::Consumer.Outer.Inner.RunArgs(value, count), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Method#Consumer.Model.event.D890DA5C45B99396.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Method#Consumer.Model.event.D890DA5C45B99396.verified.cs index 3f9631c..dd21d1c 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Method#Consumer.Model.event.D890DA5C45B99396.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Method#Consumer.Model.event.D890DA5C45B99396.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.eventArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.eventArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Parameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Parameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 2e3d52d..1a766fe 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Parameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Parameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int @event, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int @event, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(@event, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(@event, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Type#Consumer.class.Run.6B49BE1219B268DF.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Type#Consumer.class.Run.6B49BE1219B268DF.verified.cs index 39f6ed3..3c8733f 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Type#Consumer.class.Run.6B49BE1219B268DF.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Type#Consumer.class.Run.6B49BE1219B268DF.verified.cs @@ -59,9 +59,9 @@ public static partial class @classExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.@class.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.@class.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.GenericArguments_PreserveExtensionConstraintsAsync#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.GenericArguments_PreserveExtensionConstraintsAsync#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs index d96b34c..f7a402b 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.GenericArguments_PreserveExtensionConstraintsAsync#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.GenericArguments_PreserveExtensionConstraintsAsync#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int quantity) where TValue : class, global::System.IDisposable, new() + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int quantity, global::System.Threading.CancellationToken cancellationToken = default) where TValue : class, global::System.IDisposable, new() { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, quantity)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, quantity), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.NestedGenericType_PreservesHierarchyAsync#Consumer.Outer`1.Model`1.Run.463E66F10AD83318.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.NestedGenericType_PreservesHierarchyAsync#Consumer.Outer`1.Model`1.Run.463E66F10AD83318.verified.cs index 332a67b..707104e 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.NestedGenericType_PreservesHierarchyAsync#Consumer.Outer`1.Model`1.Run.463E66F10AD83318.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.NestedGenericType_PreservesHierarchyAsync#Consumer.Outer`1.Model`1.Run.463E66F10AD83318.verified.cs @@ -37,9 +37,9 @@ public static partial class OuterExtensions { [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent.Model.RunArgs, TResult> externalEvent, TValue value, TResult result) where TValue : class where TResult : struct + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent.Model.RunArgs, TResult> externalEvent, TValue value, TResult result, global::System.Threading.CancellationToken cancellationToken = default) where TValue : class where TResult : struct { - return externalEvent.RaiseAsync(new global::Consumer.Outer.Model.RunArgs(value, result)); + return externalEvent.RaiseAsync(new global::Consumer.Outer.Model.RunArgs(value, result), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=Capitalization#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=Capitalization#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 19936e0..2c567e8 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=Capitalization#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=Capitalization#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int Value) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int Value, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, Value)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, Value), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=ExtensionReceiver#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=ExtensionReceiver#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 902a87b..d6794ed 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=ExtensionReceiver#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=ExtensionReceiver#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent1, int externalEvent, int quantity) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent1, int externalEvent, int quantity, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent1.RaiseAsync(new global::Consumer.Model.RunArgs(externalEvent, quantity)); + return externalEvent1.RaiseAsync(new global::Consumer.Model.RunArgs(externalEvent, quantity), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..f17ff8a --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,58 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial record struct Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + if (field is not null) + { + return field; + } + + var handler = new global::System.Action(Run); + return field = new global::Nice3point.Revit.Toolkit.External.ExternalEvent((args) => + { + handler(args, global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + if (field is not null) + { + return field; + } + + var handler = new global::System.Action(Run); + return field = new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((application, args, cancellationToken) => + { + handler(args, cancellationToken); + }); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..dbc645a --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,60 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial struct Model + { + private object? _RunEventLock; + private global::Nice3point.Revit.Toolkit.External.IExternalEvent? _RunEventBackingField; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + if (_RunEventBackingField is not null) + { + return _RunEventBackingField; + } + + var handler = new global::System.Action(Run); + return _RunEventBackingField = new global::Nice3point.Revit.Toolkit.External.ExternalEvent((args) => + { + handler(args, global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _RunAsyncEventLock; + private global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent? _RunAsyncEventBackingField; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + if (_RunAsyncEventBackingField is not null) + { + return _RunAsyncEventBackingField; + } + + var handler = new global::System.Action(Run); + return _RunAsyncEventBackingField = new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((application, args, cancellationToken) => + { + handler(args, cancellationToken); + }); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..1accf15 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithCancellationTokenGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,58 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial struct Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + if (field is not null) + { + return field; + } + + var handler = new global::System.Action(Run); + return field = new global::Nice3point.Revit.Toolkit.External.ExternalEvent((args) => + { + handler(args, global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + if (field is not null) + { + return field; + } + + var handler = new global::System.Action(Run); + return field = new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((application, args, cancellationToken) => + { + handler(args, cancellationToken); + }); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 9b02f2d..e0fe3ab 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -73,9 +73,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index d9ce893..1e198bb 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -71,9 +71,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 324b978..239a841 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -73,9 +73,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 7d9bee5..7e07e32 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -71,9 +71,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordDisabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordDisabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 125d2ba..654c79d 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordDisabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordDisabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordEnabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordEnabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 70a5ef9..7d6a627 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordEnabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordEnabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=BeforeLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=BeforeLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..19f4da5 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=BeforeLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,67 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.ExternalEvent((args) => + { + Run(args.Token, args.Quantity); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((args) => + { + Run(args.Token, args.Quantity); + }); + } + } + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public sealed record RunArgs(global::System.Threading.CancellationToken Token, int Quantity); + } + + public static partial class ModelExtensions + { + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::Autodesk.Revit.UI.ExternalEventRequest Raise(this global::Nice3point.Revit.Toolkit.External.IExternalEvent externalEvent, global::System.Threading.CancellationToken token, int quantity) + { + return externalEvent.Raise(new global::Consumer.Model.RunArgs(token, quantity)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, global::System.Threading.CancellationToken token, int quantity, global::System.Threading.CancellationToken cancellationToken = default) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(token, quantity), cancellationToken); + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=NullableLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=NullableLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..829c329 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=NullableLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,67 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.ExternalEvent((args) => + { + Run(args.Quantity, args.Token); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((args) => + { + Run(args.Quantity, args.Token); + }); + } + } + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public sealed record RunArgs(int Quantity, global::System.Threading.CancellationToken? Token); + } + + public static partial class ModelExtensions + { + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::Autodesk.Revit.UI.ExternalEventRequest Raise(this global::Nice3point.Revit.Toolkit.External.IExternalEvent externalEvent, int quantity, global::System.Threading.CancellationToken? token) + { + return externalEvent.Raise(new global::Consumer.Model.RunArgs(quantity, token)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, global::System.Threading.CancellationToken? token, global::System.Threading.CancellationToken cancellationToken = default) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, token), cancellationToken); + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..fb5e120 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,25 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncRequestExternalEvent(new global::System.Func(Run)); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index cf4b4d6..eefad42 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -34,9 +34,9 @@ public static partial class ModelExtensions { [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..b0c093a --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,42 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncRequestExternalEvent((application, args, cancellationToken) => + { + return Run(application, args.Quantity, args.Label, cancellationToken); + }); + } + } + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public sealed record RunArgs(int Quantity, string Label); + } + + public static partial class ModelExtensions + { + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..142086a --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,25 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncRequestExternalEvent(new global::System.Func(Run)); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..54ee371 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,28 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncRequestExternalEvent((application, args, cancellationToken) => + { + return Run(args, cancellationToken); + }); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 3d3385a..c478a2a 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -34,9 +34,9 @@ public static partial class ModelExtensions { [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..afd8dd9 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,42 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncRequestExternalEvent((application, args, cancellationToken) => + { + return Run(args.Quantity, args.Label, cancellationToken); + }); + } + } + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public sealed record RunArgs(int Quantity, string Label); + } + + public static partial class ModelExtensions + { + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..fb54bf2 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,28 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncRequestExternalEvent((application, cancellationToken) => + { + return Run(cancellationToken); + }); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..f3700db --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,43 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.ExternalEvent((application, args) => + { + Run(application, args, global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent(new global::System.Action(Run)); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 184540b..1e40d10 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..e0e8d83 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,67 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.ExternalEvent((application, args) => + { + Run(application, args.Quantity, args.Label, global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((application, args, cancellationToken) => + { + Run(application, args.Quantity, args.Label, cancellationToken); + }); + } + } + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public sealed record RunArgs(int Quantity, string Label); + } + + public static partial class ModelExtensions + { + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::Autodesk.Revit.UI.ExternalEventRequest Raise(this global::Nice3point.Revit.Toolkit.External.IExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.Raise(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..a16621e --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,43 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.ExternalEvent((application) => + { + Run(application, global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent(new global::System.Action(Run)); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..9ce14d9 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,46 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.ExternalEvent((args) => + { + Run(args, global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((application, args, cancellationToken) => + { + Run(args, cancellationToken); + }); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 125d2ba..654c79d 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..0198da0 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,67 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.ExternalEvent((args) => + { + Run(args.Quantity, args.Label, global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((application, args, cancellationToken) => + { + Run(args.Quantity, args.Label, cancellationToken); + }); + } + } + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public sealed record RunArgs(int Quantity, string Label); + } + + public static partial class ModelExtensions + { + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::Autodesk.Revit.UI.ExternalEventRequest Raise(this global::Nice3point.Revit.Toolkit.External.IExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.Raise(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs new file mode 100644 index 0000000..a7a9834 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -0,0 +1,46 @@ +//HintName: Consumer.Model.Run.C0F8FDCA0BFA30FC.cs +// +#pragma warning disable +#nullable enable + +namespace Consumer +{ + public partial class Model + { + private object? _RunEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IExternalEvent RunEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.ExternalEvent(() => + { + Run(global::System.Threading.CancellationToken.None); + }); + } + } + } + + private object? _RunAsyncEventLock; + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent RunAsyncEvent + { + get + { + lock (global::System.Threading.LazyInitializer.EnsureInitialized(ref _RunAsyncEventLock)) + { + return field ??= new global::Nice3point.Revit.Toolkit.External.AsyncExternalEvent((application, cancellationToken) => + { + Run(cancellationToken); + }); + } + } + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.NullableSignature_PreservesAnnotationsAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.NullableSignature_PreservesAnnotationsAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 7d5d392..ddd58cb 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.NullableSignature_PreservesAnnotationsAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.NullableSignature_PreservesAnnotationsAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -34,9 +34,9 @@ public static partial class ModelExtensions { [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, string? label, int? quantity) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, string? label, int? quantity, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(label, quantity)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(label, quantity), cancellationToken); } } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.UIApplicationAfterFirstParameter_RemainsUserArgumentAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.UIApplicationAfterFirstParameter_RemainsUserArgumentAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 30846e4..176d864 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.UIApplicationAfterFirstParameter_RemainsUserArgumentAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.UIApplicationAfterFirstParameter_RemainsUserArgumentAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,9 +59,9 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, global::Autodesk.Revit.UI.UIApplication application) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, global::Autodesk.Revit.UI.UIApplication application, global::System.Threading.CancellationToken cancellationToken = default) { - return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, application)); + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, application), cancellationToken); } } } From ce712bb5ead501016dd15ecf9b610cdc372721eb Mon Sep 17 00:00:00 2001 From: Nice3point Date: Fri, 23 Oct 2026 21:15:51 +0300 Subject: [PATCH 04/14] [Events] Withdraw a raise that Revit refuses --- CHANGELOG.md | 3 +- README.md | 1 + .../External/Events/AsyncExternalEvent.cs | 26 ++--- ...yncExternalEventCore{TArgument,TResult}.cs | 104 +++++++++++------- .../External/Events/AsyncExternalEvent{T}.cs | 26 ++--- .../AsyncRequestExternalEvent{T,TResult}.cs | 26 ++--- .../AsyncRequestExternalEvent{TResult}.cs | 26 ++--- .../Events/ExternalEventCore{TArgument}.cs | 96 ++++++++++++++++ .../Events/ExternalEventRequestExtensions.cs | 22 ++++ .../External/Events/ExternalEvent{T}.cs | 56 ++-------- .../Nice3point.Revit.Toolkit.csproj | 4 + .../AsyncExternalEventCoreTests.cs | 75 +++++++++++++ .../ExternalEventCoreTests.cs | 67 +++++++++++ 13 files changed, 369 insertions(+), 163 deletions(-) create mode 100644 source/Nice3point.Revit.Toolkit/External/Events/ExternalEventCore{TArgument}.cs create mode 100644 source/Nice3point.Revit.Toolkit/External/Events/ExternalEventRequestExtensions.cs create mode 100644 tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventCoreTests.cs create mode 100644 tests/Nice3point.Revit.Toolkit.Tests/ExternalEventCoreTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 84e1ea2..3b4d2db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ -# 2027.1.0 +# 2027.0.3 ### External Events - `ExternalEvent` and the asynchronous external events run the handler once for every raise, with the argument of that raise, in the order of the raises. Each `RaiseAsync()` task completes with the result or the exception of its own run. A raise that arrived before Revit ran the previous one used to overwrite its argument, and the earlier `RaiseAsync()` task never completed. - `ExternalEvent` without an argument runs once for all raises that wait, as Revit runs a raised event. +- A raise that Revit refuses with `ExternalEventRequest.Denied` or `ExternalEventRequest.TimedOut` never runs. `Raise(argument)` returns that result, and the `RaiseAsync()` task fails with an `InvalidOperationException`. A refused raise used to stay queued and run with the next raise of the event. - `RaiseAsync()` of the asynchronous external events and their interfaces accepts an optional `CancellationToken`. A token canceled before Revit executes the handler withdraws the raise, and the task is canceled at once. A running handler is never interrupted. With `ExternalEventOptions.AllowDirectInvocation` in API mode, the token is checked once before the handler runs. - New constructors of `AsyncExternalEvent`, `AsyncExternalEvent`, `AsyncRequestExternalEvent`, and `AsyncRequestExternalEvent` accept a handler that receives the `UIApplication`, the argument, and the `CancellationToken` of the raise. An `OperationCanceledException` thrown for that token cancels the task. diff --git a/README.md b/README.md index c80bf50..c9b1fba 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,7 @@ One event can serve several callers, such as two modeless windows or a backgroun Every `Raise(argument)` and every `RaiseAsync()` call runs the handler once, with its own argument, in the order of the calls. Each `RaiseAsync()` task completes with the result or the exception of its own run. `ExternalEvent` without an argument runs once for all raises that are still waiting, the same way Revit runs a raised event. +A raise that Revit refuses with `Denied` or `TimedOut` never runs: `Raise(argument)` returns that result, and the `RaiseAsync()` task fails with an `InvalidOperationException`. #### ExternalEvent diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs index 1ab12cb..f8d3e0e 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs @@ -37,7 +37,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) { handler(); return default; - }); + }, base.Raise); _options = options; } @@ -66,7 +66,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions op { handler(uiApplication); return default; - }); + }, base.Raise); _options = options; } @@ -95,7 +95,7 @@ public AsyncExternalEvent(Action handler, Exte { handler(uiApplication, cancellationToken); return default; - }); + }, base.Raise); _options = options; } @@ -108,7 +108,8 @@ public AsyncExternalEvent(Action handler, Exte /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

- /// Each call executes the handler once, in the order of the calls, and the returned task completes with the outcome of that execution.

+ /// Each call executes the handler once, in the order of the calls, and the returned task completes with the outcome of that execution. + /// A call that Revit refuses with or never executes the handler, and the task fails with an .

/// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. /// A cancellation requested while the handler executes doesn't interrupt it. /// A handler that receives the token can throw an for it, and the task is then canceled.

@@ -117,31 +118,18 @@ public AsyncExternalEvent(Action handler, Exte /// public Task RaiseAsync(CancellationToken cancellationToken = default) { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { return _core.Invoke(RevitContext.UiApplication, default, cancellationToken); } - var task = _core.Enqueue(default, cancellationToken); - Raise(); - - return task; + return _core.RaiseAsync(default, cancellationToken); } /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { - _core.InvokeNext(uiApplication); - - if (_core.HasPendingOperations) - { - Raise(); - } + _core.Execute(uiApplication); } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs index 9451c01..fc797ea 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs @@ -11,30 +11,30 @@ namespace Nice3point.Revit.Toolkit.External; /// The type of the argument passed to the handler. /// The type of the result produced by the handler. /// The execution logic of the external event. +/// The function that raises the Revit external event. /// /// Revit runs a pending external event once however often it is raised. -/// The owning event raises itself again while is , and each raise executes in its own event cycle.
-/// A raise whose token is canceled before its execution starts is canceled at once and never executes. -/// Once the execution starts, the cancellation of the token no longer affects the raise. +/// Each execution runs the oldest raise and raises the event again for the rest, and a refused raise runs the rest in the same execution.
+/// The execution, the cancellation, and the refusal of a raise compete for the reservation of its completion, and only the one that reserves it completes the task. ///
-internal sealed class AsyncExternalEventCore(Func handler) +internal sealed class AsyncExternalEventCore(Func handler, Func raise) { private readonly ConcurrentQueue _operations = new(); - /// - /// Gets a value indicating whether a raise waits for its execution. - /// - public bool HasPendingOperations => !_operations.IsEmpty; - /// /// Executes the handler on the calling thread. /// /// The current instance. /// The argument to pass to the handler. /// The token to pass to the handler. - /// A completed task that contains the result or the exception of the handler, or a canceled task when the handler throws an for . + /// A completed task that contains the outcome of the handler, or a canceled task when is canceled before the handler executes. public Task Invoke(UIApplication uiApplication, TArgument argument, CancellationToken cancellationToken) { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + try { return Task.FromResult(handler(uiApplication, argument, cancellationToken)); @@ -50,46 +50,62 @@ public Task Invoke(UIApplication uiApplication, TArgument argument, Can } /// - /// Adds a raise to the end of the queue. + /// Adds a raise to the end of the queue and raises the Revit external event. /// /// The argument to pass to the handler. /// The token that withdraws the raise before its execution, passed to the handler. - /// A task that completes with the outcome of the handler once executes the raise, or a task canceled at the cancellation of before the execution. - public Task Enqueue(TArgument argument, CancellationToken cancellationToken) + /// A task that completes with the outcome of the handler, or a task canceled at the cancellation of before the execution. + /// A raise that Revit refuses leaves the queue unexecuted, and its task fails with an . + public Task RaiseAsync(TArgument argument, CancellationToken cancellationToken) { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + var operation = new Operation(argument, cancellationToken); _operations.Enqueue(operation); + var request = raise(); + if (request.IsRefused) + { + operation.TrySetException(new InvalidOperationException($"Revit did not accept the external event request and returned the {request} result. Raise the external event again.")); + } + return operation.Task; } /// - /// Executes the handler for the oldest raise in the queue that is not canceled and completes its task. + /// Executes the oldest raise in the queue that is not canceled and raises the Revit external event again for the rest. /// /// The current instance. - /// A queue without a raise to execute executes nothing. - public void InvokeNext(UIApplication uiApplication) + public void Execute(UIApplication uiApplication) { - if (!TryStartNext(out var operation)) + while (TryDequeueAndReserveCompletion(out var operation)) { - return; - } + var executesNextRaise = !_operations.IsEmpty && raise().IsRefused; - try - { - operation.SetResult(handler(uiApplication, operation.Argument, operation.CancellationToken)); - } - catch (Exception exception) - { - operation.SetException(exception); + try + { + operation.DangerousSetResult(handler(uiApplication, operation.Argument, operation.CancellationToken)); + } + catch (Exception exception) + { + operation.DangerousSetException(exception); + } + + if (!executesNextRaise) + { + return; + } } } - private bool TryStartNext([NotNullWhen(true)] out Operation? operation) + private bool TryDequeueAndReserveCompletion([NotNullWhen(true)] out Operation? operation) { while (_operations.TryDequeue(out operation)) { - if (operation.TryStart()) + if (operation.TryReserveCompletion()) { return true; } @@ -105,28 +121,24 @@ private static bool IsCanceledBy(OperationCanceledException exception, Cancellat private sealed class Operation { - private const int Pending = 0; - private const int Started = 1; - private const int Canceled = 2; - private readonly TaskCompletionSource _completionSource = new(TaskCreationOptions.RunContinuationsAsynchronously); private CancellationTokenRegistration _cancellationRegistration; - private int _state; + private int _completionReserved; public Operation(TArgument argument, CancellationToken cancellationToken) { Argument = argument; CancellationToken = cancellationToken; - _cancellationRegistration = cancellationToken.Register(static state => ((Operation)state!).Cancel(), this); + _cancellationRegistration = cancellationToken.Register(static state => ((Operation)state!).TrySetCanceled(), this); } public TArgument Argument { get; private set; } public CancellationToken CancellationToken { get; } public Task Task => _completionSource.Task; - public bool TryStart() + public bool TryReserveCompletion() { - if (Interlocked.CompareExchange(ref _state, Started, Pending) != Pending) + if (Interlocked.Exchange(ref _completionReserved, 1) != 0) { return false; } @@ -135,12 +147,24 @@ public bool TryStart() return true; } - public void SetResult(TResult result) + public bool TrySetException(Exception exception) + { + if (!TryReserveCompletion()) + { + return false; + } + + Argument = default!; + _completionSource.TrySetException(exception); + return true; + } + + public void DangerousSetResult(TResult result) { _completionSource.TrySetResult(result); } - public void SetException(Exception exception) + public void DangerousSetException(Exception exception) { if (exception is OperationCanceledException canceledException && IsCanceledBy(canceledException, CancellationToken)) { @@ -151,9 +175,9 @@ public void SetException(Exception exception) _completionSource.TrySetException(exception); } - private void Cancel() + private void TrySetCanceled() { - if (Interlocked.CompareExchange(ref _state, Canceled, Pending) != Pending) + if (!TryReserveCompletion()) { return; } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs index a750ed1..4f6ddbd 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs @@ -38,7 +38,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) { handler(argument); return default; - }); + }, base.Raise); _options = options; } @@ -67,7 +67,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions { handler(uiApplication, argument); return default; - }); + }, base.Raise); _options = options; } @@ -96,7 +96,7 @@ public AsyncExternalEvent(Action handler, E { handler(uiApplication, argument, cancellationToken); return default; - }); + }, base.Raise); _options = options; } @@ -111,7 +111,8 @@ public AsyncExternalEvent(Action handler, E /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

- /// Each call executes the handler once with its own argument, in the order of the calls, and the returned task completes with the outcome of that execution.

+ /// Each call executes the handler once with its own argument, in the order of the calls, and the returned task completes with the outcome of that execution. + /// A call that Revit refuses with or never executes the handler, and the task fails with an .

/// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. /// A cancellation requested while the handler executes doesn't interrupt it. /// A handler that receives the token can throw an for it, and the task is then canceled.

@@ -120,31 +121,18 @@ public AsyncExternalEvent(Action handler, E /// public Task RaiseAsync(T argument, CancellationToken cancellationToken = default) { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { return _core.Invoke(RevitContext.UiApplication, argument, cancellationToken); } - var task = _core.Enqueue(argument, cancellationToken); - Raise(); - - return task; + return _core.RaiseAsync(argument, cancellationToken); } /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { - _core.InvokeNext(uiApplication); - - if (_core.HasPendingOperations) - { - Raise(); - } + _core.Execute(uiApplication); } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs index 854c933..24c1888 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs @@ -36,7 +36,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalEventOptions { ArgumentNullException.ThrowIfNull(handler); - _core = new((_, argument, _) => handler(argument)); + _core = new((_, argument, _) => handler(argument), base.Raise); _options = options; } @@ -61,7 +61,7 @@ public AsyncRequestExternalEvent(Func handler, Extern { ArgumentNullException.ThrowIfNull(handler); - _core = new((uiApplication, argument, _) => handler(uiApplication, argument)); + _core = new((uiApplication, argument, _) => handler(uiApplication, argument), base.Raise); _options = options; } @@ -86,7 +86,7 @@ public AsyncRequestExternalEvent(Func
- /// Each call executes the handler once with its own argument, in the order of the calls, and the returned task completes with the outcome of that execution.

+ /// Each call executes the handler once with its own argument, in the order of the calls, and the returned task completes with the outcome of that execution. + /// A call that Revit refuses with or never executes the handler, and the task fails with an .

/// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. /// A cancellation requested while the handler executes doesn't interrupt it, and the task completes with its result. /// A handler that receives the token can throw an for it, and the task is then canceled.

@@ -110,31 +111,18 @@ public AsyncRequestExternalEvent(Func public Task RaiseAsync(T argument, CancellationToken cancellationToken = default) { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { return _core.Invoke(RevitContext.UiApplication, argument, cancellationToken); } - var task = _core.Enqueue(argument, cancellationToken); - Raise(); - - return task; + return _core.RaiseAsync(argument, cancellationToken); } /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { - _core.InvokeNext(uiApplication); - - if (_core.HasPendingOperations) - { - Raise(); - } + _core.Execute(uiApplication); } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs index 070b6b9..8c95f17 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs @@ -34,7 +34,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalEventOptions opt { ArgumentNullException.ThrowIfNull(handler); - _core = new((_, _, _) => handler()); + _core = new((_, _, _) => handler(), base.Raise); _options = options; } @@ -59,7 +59,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalE { ArgumentNullException.ThrowIfNull(handler); - _core = new((uiApplication, _, _) => handler(uiApplication)); + _core = new((uiApplication, _, _) => handler(uiApplication), base.Raise); _options = options; } @@ -84,7 +84,7 @@ public AsyncRequestExternalEvent(Func { ArgumentNullException.ThrowIfNull(handler); - _core = new((uiApplication, _, cancellationToken) => handler(uiApplication, cancellationToken)); + _core = new((uiApplication, _, cancellationToken) => handler(uiApplication, cancellationToken), base.Raise); _options = options; } @@ -97,7 +97,8 @@ public AsyncRequestExternalEvent(Func /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

- /// Each call executes the handler once, in the order of the calls, and the returned task completes with the outcome of that execution.

+ /// Each call executes the handler once, in the order of the calls, and the returned task completes with the outcome of that execution. + /// A call that Revit refuses with or never executes the handler, and the task fails with an .

/// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. /// A cancellation requested while the handler executes doesn't interrupt it, and the task completes with its result. /// A handler that receives the token can throw an for it, and the task is then canceled.

@@ -106,31 +107,18 @@ public AsyncRequestExternalEvent(Func /// public Task RaiseAsync(CancellationToken cancellationToken = default) { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { return _core.Invoke(RevitContext.UiApplication, default, cancellationToken); } - var task = _core.Enqueue(default, cancellationToken); - Raise(); - - return task; + return _core.RaiseAsync(default, cancellationToken); } /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { - _core.InvokeNext(uiApplication); - - if (_core.HasPendingOperations) - { - Raise(); - } + _core.Execute(uiApplication); } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventCore{TArgument}.cs b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventCore{TArgument}.cs new file mode 100644 index 0000000..52b289b --- /dev/null +++ b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventCore{TArgument}.cs @@ -0,0 +1,96 @@ +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using Autodesk.Revit.UI; + +// ReSharper disable once CheckNamespace +namespace Nice3point.Revit.Toolkit.External; + +/// +/// Provides the queue of raises of an external event with an argument and the execution of each raise. +/// +/// The type of the argument passed to the handler. +/// The execution logic of the external event. +/// The function that raises the Revit external event. +/// +/// Revit runs a pending external event once however often it is raised. +/// Each execution runs the oldest raise and raises the event again for the rest, and a refused raise runs the rest in the same execution.
+/// The execution and the refusal of a raise compete for its reservation, and a raise that Revit refuses never executes. +///
+internal sealed class ExternalEventCore(Action handler, Func raise) +{ + private readonly ConcurrentQueue _operations = new(); + + /// + /// Executes the handler on the calling thread. + /// + /// The current instance. + /// The argument to pass to the handler. + public void Invoke(UIApplication uiApplication, TArgument argument) + { + handler(uiApplication, argument); + } + + /// + /// Adds a raise to the end of the queue and raises the Revit external event. + /// + /// The argument to pass to the handler. + /// The result of the request to raise the Revit external event. + /// A raise that Revit refuses leaves the queue unexecuted. + public ExternalEventRequest Raise(TArgument argument) + { + var operation = new Operation(argument); + _operations.Enqueue(operation); + + var request = raise(); + if (request.IsRefused) + { + operation.TryReserveExecution(); + } + + return request; + } + + /// + /// Executes the oldest raise in the queue and raises the Revit external event again for the rest. + /// + /// The current instance. + /// An exception of the handler propagates to the caller. + public void Execute(UIApplication uiApplication) + { + while (TryDequeueAndReserveExecution(out var operation)) + { + var executesNextRaise = !_operations.IsEmpty && raise().IsRefused; + handler(uiApplication, operation.Argument); + + if (!executesNextRaise) + { + return; + } + } + } + + private bool TryDequeueAndReserveExecution([NotNullWhen(true)] out Operation? operation) + { + while (_operations.TryDequeue(out operation)) + { + if (operation.TryReserveExecution()) + { + return true; + } + } + + return false; + } + + private sealed class Operation(TArgument argument) + { + private int _executionReserved; + + public TArgument Argument { get; } = argument; + + public bool TryReserveExecution() + { + return Interlocked.Exchange(ref _executionReserved, 1) == 0; + } + } +} diff --git a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventRequestExtensions.cs b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventRequestExtensions.cs new file mode 100644 index 0000000..d59c3c6 --- /dev/null +++ b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventRequestExtensions.cs @@ -0,0 +1,22 @@ +using Autodesk.Revit.UI; + +// ReSharper disable once CheckNamespace +namespace Nice3point.Revit.Toolkit.External; + +/// +/// Contains extension members for . +/// +internal static class ExternalEventRequestExtensions +{ + /// + /// Defines extensions for . + /// + /// The result of a request to raise an external event. + extension(ExternalEventRequest request) + { + /// + /// Gets a value indicating whether Revit refused to raise the external event, leaving no execution scheduled for the request. + /// + public bool IsRefused => request is ExternalEventRequest.Denied or ExternalEventRequest.TimedOut; + } +} diff --git a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs index 106b121..7e298d7 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs @@ -1,4 +1,3 @@ -using System.Collections.Concurrent; using System.ComponentModel; using Autodesk.Revit.UI; @@ -14,21 +13,16 @@ namespace Nice3point.Revit.Toolkit.External; [PublicAPI] public class ExternalEvent : ExternalEventHandler, IExternalEvent { - private readonly ConcurrentQueue _arguments = new(); - private readonly Action? _handler; + private readonly ExternalEventCore _core; private readonly ExternalEventOptions _options; - private readonly Action? _uiHandler; /// /// Initializes a new instance of the class. /// /// The execution logic that receives an argument of type . /// Thrown if is . - public ExternalEvent(Action handler) + public ExternalEvent(Action handler) : this(handler, ExternalEventOptions.None) { - ArgumentNullException.ThrowIfNull(handler); - - _handler = handler; } /// @@ -41,7 +35,7 @@ public ExternalEvent(Action handler, ExternalEventOptions options) { ArgumentNullException.ThrowIfNull(handler); - _handler = handler; + _core = new((_, argument) => handler(argument), base.Raise); _options = options; } @@ -51,11 +45,8 @@ public ExternalEvent(Action handler, ExternalEventOptions options) /// /// The execution logic that receives the current and an argument of type . /// Thrown if is . - public ExternalEvent(Action handler) + public ExternalEvent(Action handler) : this(handler, ExternalEventOptions.None) { - ArgumentNullException.ThrowIfNull(handler); - - _uiHandler = handler; } /// @@ -69,7 +60,7 @@ public ExternalEvent(Action handler, ExternalEventOptions opti { ArgumentNullException.ThrowIfNull(handler); - _uiHandler = handler; + _core = new(handler, base.Raise); _options = options; } @@ -87,7 +78,8 @@ public ExternalEvent(Action handler, ExternalEventOptions opti /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

- /// Each call executes the handler once with its own argument, in the order of the calls.

+ /// Each call executes the handler once with its own argument, in the order of the calls. + /// A call that returns or never executes the handler.

/// When is specified and Revit is in API mode, /// the handler is executed directly on the calling thread instead of being queued. /// @@ -95,45 +87,17 @@ public ExternalEventRequest Raise(T argument) { if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { - Invoke(RevitContext.UiApplication, argument); + _core.Invoke(RevitContext.UiApplication, argument); return ExternalEventRequest.Accepted; } - _arguments.Enqueue(argument); - return base.Raise(); + return _core.Raise(argument); } /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { - if (!_arguments.TryDequeue(out var argument)) - { - return; - } - - try - { - Invoke(uiApplication, argument); - } - finally - { - if (!_arguments.IsEmpty) - { - base.Raise(); - } - } - } - - private void Invoke(UIApplication uiApplication, T argument) - { - if (_uiHandler is not null) - { - _uiHandler.Invoke(uiApplication, argument); - } - else - { - _handler!.Invoke(argument); - } + _core.Execute(uiApplication); } } diff --git a/source/Nice3point.Revit.Toolkit/Nice3point.Revit.Toolkit.csproj b/source/Nice3point.Revit.Toolkit/Nice3point.Revit.Toolkit.csproj index 7d56518..901a560 100644 --- a/source/Nice3point.Revit.Toolkit/Nice3point.Revit.Toolkit.csproj +++ b/source/Nice3point.Revit.Toolkit/Nice3point.Revit.Toolkit.csproj @@ -38,6 +38,10 @@ + + + + diff --git a/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventCoreTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventCoreTests.cs new file mode 100644 index 0000000..5203cb9 --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventCoreTests.cs @@ -0,0 +1,75 @@ +using Autodesk.Revit.UI; +using Nice3point.Revit.Toolkit.External; +using Nice3point.TUnit.Revit; +using TUnit.Assertions.Enums; + +namespace Nice3point.Revit.Toolkit.Tests; + +public sealed class AsyncExternalEventCoreTests : RevitApiUiTest +{ + [Test] + [Arguments(nameof(ExternalEventRequest.Denied))] + [Arguments(nameof(ExternalEventRequest.TimedOut))] + public async Task RaiseAsync_RefusedRequest_FaultsTheTaskWithoutExecution(string refusalName) + { + // Arrange + var refusal = (ExternalEventRequest)Enum.Parse(typeof(ExternalEventRequest), refusalName); + var executionCount = 0; + var core = new AsyncExternalEventCore((_, argument, _) => argument + executionCount++, () => refusal); + + // Act + var raiseTask = core.RaiseAsync(42, CancellationToken.None); + core.Execute(null!); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(() => raiseTask).Throws(); + await Assert.That(executionCount).IsZero(); + } + } + + [Test] + public async Task Execute_RefusedRaiseForTheRest_ExecutesTheRestInTheSameExecution() + { + // Arrange + var raiseResults = new Queue([ExternalEventRequest.Accepted, ExternalEventRequest.Pending, ExternalEventRequest.Pending, ExternalEventRequest.Denied, ExternalEventRequest.Denied]); + var core = new AsyncExternalEventCore((_, argument, _) => argument, raiseResults.Dequeue); + var firstTask = core.RaiseAsync(1, CancellationToken.None); + var secondTask = core.RaiseAsync(2, CancellationToken.None); + var thirdTask = core.RaiseAsync(3, CancellationToken.None); + + // Act + core.Execute(null!); + + // Assert + var results = await Task.WhenAll(firstTask, secondTask, thirdTask); + await Assert.That(results).IsEquivalentTo([1, 2, 3], CollectionOrdering.Matching); + } + + [Test] + public async Task Execute_AcceptedRaiseForTheRest_ExecutesTheOldestRaise() + { + // Arrange + var raiseCount = 0; + var core = new AsyncExternalEventCore((_, argument, _) => argument, () => + { + raiseCount++; + return ExternalEventRequest.Accepted; + }); + + var firstTask = core.RaiseAsync(1, CancellationToken.None); + var secondTask = core.RaiseAsync(2, CancellationToken.None); + + // Act + core.Execute(null!); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(firstTask.Status).IsEqualTo(TaskStatus.RanToCompletion); + await Assert.That(secondTask.IsCompleted).IsFalse(); + await Assert.That(raiseCount).IsEqualTo(3); + } + } +} diff --git a/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventCoreTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventCoreTests.cs new file mode 100644 index 0000000..154228e --- /dev/null +++ b/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventCoreTests.cs @@ -0,0 +1,67 @@ +using Autodesk.Revit.UI; +using Nice3point.Revit.Toolkit.External; +using Nice3point.TUnit.Revit; +using TUnit.Assertions.Enums; + +namespace Nice3point.Revit.Toolkit.Tests; + +public sealed class ExternalEventCoreTests : RevitApiUiTest +{ + [Test] + [Arguments(nameof(ExternalEventRequest.Denied))] + [Arguments(nameof(ExternalEventRequest.TimedOut))] + public async Task Raise_RefusedRequest_NeverExecutesTheArgument(string refusalName) + { + // Arrange + var refusal = (ExternalEventRequest)Enum.Parse(typeof(ExternalEventRequest), refusalName); + var raiseResults = new Queue([refusal, ExternalEventRequest.Accepted]); + var receivedArguments = new List(); + var core = new ExternalEventCore((_, argument) => receivedArguments.Add(argument), raiseResults.Dequeue); + + // Act + var refusedRequest = core.Raise(1); + var acceptedRequest = core.Raise(2); + core.Execute(null!); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(refusedRequest).IsEqualTo(refusal); + await Assert.That(acceptedRequest).IsEqualTo(ExternalEventRequest.Accepted); + await Assert.That(receivedArguments).IsEquivalentTo([2]); + } + } + + [Test] + public async Task Execute_RefusedRaiseForTheRest_ExecutesTheRestInTheSameExecution() + { + // Arrange + var raiseResults = new Queue([ExternalEventRequest.Accepted, ExternalEventRequest.Pending, ExternalEventRequest.TimedOut]); + var receivedArguments = new List(); + var core = new ExternalEventCore((_, argument) => receivedArguments.Add(argument), raiseResults.Dequeue); + core.Raise(1); + core.Raise(2); + + // Act + core.Execute(null!); + + // Assert + await Assert.That(receivedArguments).IsEquivalentTo([1, 2], CollectionOrdering.Matching); + } + + [Test] + public async Task Execute_AcceptedRaiseForTheRest_ExecutesTheOldestRaise() + { + // Arrange + var receivedArguments = new List(); + var core = new ExternalEventCore((_, argument) => receivedArguments.Add(argument), () => ExternalEventRequest.Accepted); + core.Raise(1); + core.Raise(2); + + // Act + core.Execute(null!); + + // Assert + await Assert.That(receivedArguments).IsEquivalentTo([1]); + } +} From 50e771ec877cc8e29b4d440974c76b14f937a0b1 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Fri, 23 Oct 2026 21:21:42 +0300 Subject: [PATCH 05/14] [Events][Generator] Add the cancellation token to RaiseAsync as an overload --- CHANGELOG.md | 6 +++--- README.md | 11 +++++++--- .../Emission/ExternalEventWriter.cs | 21 +++++++++++++------ .../External/Events/AsyncExternalEvent.cs | 16 +++++++++++--- .../External/Events/AsyncExternalEvent{T}.cs | 18 +++++++++++++--- .../AsyncRequestExternalEvent{T,TResult}.cs | 20 ++++++++++++++---- .../AsyncRequestExternalEvent{TResult}.cs | 18 ++++++++++++---- .../Attributes/ExternalEventAttribute.cs | 3 ++- .../Events/Interfaces/IAsyncExternalEvent.cs | 8 ++++++- .../Interfaces/IAsyncExternalEvent{T}.cs | 9 +++++++- .../IAsyncRequestExternalEvent{T,TResult}.cs | 9 +++++++- .../IAsyncRequestExternalEvent{TResult}.cs | 8 ++++++- .../ExtensionContainerTests.cs | 2 +- .../Fixtures/ExternalEvents.cs | 20 +++++++++++------- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...r.Model`1.Run.BFAFF1DAC26910EF.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...r.Model`1.Run.BFAFF1DAC26910EF.verified.cs | 9 +++++++- ...mer.field.Run.6145C3C7A0D4FA0F.verified.cs | 9 +++++++- ...eld.Model.Run.8F529F91CA4A8D2A.verified.cs | 9 +++++++- ...l.application.3E69A3B91C661309.verified.cs | 9 +++++++- ...er.Model.args.2DAA7B354C035418.verified.cs | 9 +++++++- ...Model.handler.3D61EBDA00260524.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...1.Inner`1.Run.4DB1B2D9FFC22275.verified.cs | 9 +++++++- ...r.Model.event.D890DA5C45B99396.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.class.Run.6B49BE1219B268DF.verified.cs | 9 +++++++- ...r.Model`1.Run.BFAFF1DAC26910EF.verified.cs | 9 +++++++- ...1.Model`1.Run.463E66F10AD83318.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- ...mer.Model.Run.C0F8FDCA0BFA30FC.verified.cs | 9 +++++++- 57 files changed, 473 insertions(+), 83 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b4d2db..49224fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - `ExternalEvent` and the asynchronous external events run the handler once for every raise, with the argument of that raise, in the order of the raises. Each `RaiseAsync()` task completes with the result or the exception of its own run. A raise that arrived before Revit ran the previous one used to overwrite its argument, and the earlier `RaiseAsync()` task never completed. - `ExternalEvent` without an argument runs once for all raises that wait, as Revit runs a raised event. - A raise that Revit refuses with `ExternalEventRequest.Denied` or `ExternalEventRequest.TimedOut` never runs. `Raise(argument)` returns that result, and the `RaiseAsync()` task fails with an `InvalidOperationException`. A refused raise used to stay queued and run with the next raise of the event. -- `RaiseAsync()` of the asynchronous external events and their interfaces accepts an optional `CancellationToken`. A token canceled before Revit executes the handler withdraws the raise, and the task is canceled at once. A running handler is never interrupted. With `ExternalEventOptions.AllowDirectInvocation` in API mode, the token is checked once before the handler runs. +- `RaiseAsync()` of the asynchronous external events and their interfaces has an overload that accepts a `CancellationToken`. A token canceled before Revit executes the handler withdraws the raise, and the task is canceled at once. A running handler is never interrupted. With `ExternalEventOptions.AllowDirectInvocation` in API mode, the token is checked once before the handler runs. - New constructors of `AsyncExternalEvent`, `AsyncExternalEvent`, `AsyncRequestExternalEvent`, and `AsyncRequestExternalEvent` accept a handler that receives the `UIApplication`, the argument, and the `CancellationToken` of the raise. An `OperationCanceledException` thrown for that token cancels the task. ```c# @@ -25,7 +25,7 @@ await _deleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); ### ExternalEvent Source Generator - A handler can declare a `CancellationToken` as its last parameter, after the optional `UIApplication` and the arguments. The asynchronous event passes it the token of `RaiseAsync()`, and the synchronous event passes `CancellationToken.None`. A `CancellationToken` in any other position remains an argument. -- The generated `RaiseAsync()` extension methods for several arguments accept an optional `CancellationToken`. +- The generated `RaiseAsync()` extension methods for several arguments have an overload that accepts a `CancellationToken`. ```c# [ExternalEvent] @@ -47,7 +47,7 @@ await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationToken); ### Breaking Changes -- The `RaiseAsync` methods of `IAsyncExternalEvent`, `IAsyncExternalEvent`, `IAsyncRequestExternalEvent`, and `IAsyncRequestExternalEvent` take an optional `CancellationToken` parameter. Source code that calls them compiles unchanged. Add-ins compiled against an earlier version are rebuilt, and custom implementations of the interfaces add the parameter. +- `IAsyncExternalEvent`, `IAsyncExternalEvent`, `IAsyncRequestExternalEvent`, and `IAsyncRequestExternalEvent` declare a `RaiseAsync` overload with a `CancellationToken`. Code that calls these interfaces runs unchanged, and custom implementations of the interfaces implement the overload. # 2027.0.2 diff --git a/README.md b/README.md index c9b1fba..cbcea2f 100644 --- a/README.md +++ b/README.md @@ -364,7 +364,7 @@ private async Task DeleteWindowAsync(ElementId elementId) #### Cancellation -`RaiseAsync()` accepts a `CancellationToken`. +Every `RaiseAsync()` method has an overload that accepts a `CancellationToken`. A modal dialog or a running command keeps Revit from executing external events, and the token lets the caller stop waiting: ```c# @@ -615,7 +615,12 @@ public partial class MyViewModel public static partial class MyViewModelExtensions { - public static global::System.Threading.Tasks.Task RaiseAsync(this IAsyncRequestExternalEvent externalEvent, Level level, UV coordinate, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this IAsyncRequestExternalEvent externalEvent, Level level, UV coordinate) + { + return externalEvent.RaiseAsync(new MyViewModel.CreateRoomArgs(level, coordinate)); + } + + public static global::System.Threading.Tasks.Task RaiseAsync(this IAsyncRequestExternalEvent externalEvent, Level level, UV coordinate, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new MyViewModel.CreateRoomArgs(level, coordinate), cancellationToken); } @@ -629,7 +634,7 @@ If initialization throws, the next access retries it. **Methods with CancellationToken parameter** -Every generated `RaiseAsync()` accepts a `CancellationToken`, as described in [Cancellation](#cancellation). +Every generated `RaiseAsync()` method has an overload that accepts a `CancellationToken`, as described in [Cancellation](#cancellation). Place `CancellationToken` last when the handler needs that token. The event supplies it, the same way as `UIApplication`, and the token isn't one of the event arguments: diff --git a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs index 53abdd1..820ee41 100644 --- a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs +++ b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs @@ -102,11 +102,13 @@ private static void WriteExtensionClass(CodeWriter writer, ExternalEventDefiniti { if (eventDefinition.ReturnsVoid) { - WriteRaiseExtensionMethod(writer, eventDefinition, qualifiedRecordType, isAsync: false); + WriteRaiseExtensionMethod(writer, eventDefinition, qualifiedRecordType, RaiseMethodKind.Raise); writer.AppendLine(); } - WriteRaiseExtensionMethod(writer, eventDefinition, qualifiedRecordType, isAsync: true); + WriteRaiseExtensionMethod(writer, eventDefinition, qualifiedRecordType, RaiseMethodKind.RaiseAsync); + writer.AppendLine(); + WriteRaiseExtensionMethod(writer, eventDefinition, qualifiedRecordType, RaiseMethodKind.CancellableRaiseAsync); } } @@ -201,13 +203,13 @@ private static void WritePropertyInitialization(CodeWriter writer, string assign } } - private static void WriteRaiseExtensionMethod(CodeWriter writer, ExternalEventDefinition eventDefinition, string qualifiedRecordType, bool isAsync) + private static void WriteRaiseExtensionMethod(CodeWriter writer, ExternalEventDefinition eventDefinition, string qualifiedRecordType, RaiseMethodKind methodKind) { string interfaceType; string returnType; string methodName; - if (!isAsync) + if (methodKind == RaiseMethodKind.Raise) { interfaceType = $"{ExternalEventTypeNames.ExternalEventInterface.WithGlobalPrefix}<{qualifiedRecordType}>"; returnType = ExternalEventTypeNames.ExternalEventRequest.WithGlobalPrefix; @@ -239,10 +241,10 @@ private static void WriteRaiseExtensionMethod(CodeWriter writer, ExternalEventDe var receiverName = CSharpIdentifier.Reserve("externalEvent", parameterNames); var accessibility = GetMostRestrictiveAccessibility(eventDefinition.TypeHierarchy); - if (isAsync) + if (methodKind == RaiseMethodKind.CancellableRaiseAsync) { var cancellationTokenName = CSharpIdentifier.Reserve("cancellationToken", parameterNames); - parameters = $"{parameters}, {ExternalEventTypeNames.CancellationToken.WithGlobalPrefix} {cancellationTokenName} = default"; + parameters = $"{parameters}, {ExternalEventTypeNames.CancellationToken.WithGlobalPrefix} {cancellationTokenName}"; raiseArguments = $"{raiseArguments}, {cancellationTokenName}"; } @@ -460,6 +462,13 @@ private readonly record struct LambdaParameterNames public required string CancellationToken { get; init; } } + private enum RaiseMethodKind + { + Raise, + RaiseAsync, + CancellableRaiseAsync + } + private enum EventPropertyKind { Sync, diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs index f8d3e0e..d411c7d 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs @@ -74,7 +74,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions op /// Initializes a new instance of the class /// with access to the instance and the cancellation token of the raise. ///
- /// The execution logic that receives the current and the token passed to . + /// The execution logic that receives the current and the token passed to . /// Thrown if is . public AsyncExternalEvent(Action handler) : this(handler, ExternalEventOptions.None) { @@ -84,7 +84,7 @@ public AsyncExternalEvent(Action handler) : th /// Initializes a new instance of the class /// with access to the instance and the cancellation token of the raise. /// - /// The execution logic that receives the current and the token passed to . + /// The execution logic that receives the current and the token passed to . /// The options to use to configure the external event. /// Thrown if is . public AsyncExternalEvent(Action handler, ExternalEventOptions options) @@ -99,6 +99,16 @@ public AsyncExternalEvent(Action handler, Exte _options = options; } + /// + /// Raises the external event asynchronously, instructing Revit to execute the handler within the Revit API context. + /// + /// The representing the async operation being executed. + /// This overload passes to . + public Task RaiseAsync() + { + return RaiseAsync(CancellationToken.None); + } + /// /// Raises the external event asynchronously, instructing Revit to execute the handler within the Revit API context. /// @@ -116,7 +126,7 @@ public AsyncExternalEvent(Action handler, Exte /// When is specified and Revit is in API mode, /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. /// - public Task RaiseAsync(CancellationToken cancellationToken = default) + public Task RaiseAsync(CancellationToken cancellationToken) { if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs index 4f6ddbd..7330030 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs @@ -75,7 +75,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions /// Initializes a new instance of the class /// with access to the instance and the cancellation token of the raise. /// - /// The execution logic that receives the current , an argument of type , and the token passed to . + /// The execution logic that receives the current , an argument of type , and the token passed to . /// Thrown if is . public AsyncExternalEvent(Action handler) : this(handler, ExternalEventOptions.None) { @@ -85,7 +85,7 @@ public AsyncExternalEvent(Action handler) : /// Initializes a new instance of the class /// with access to the instance and the cancellation token of the raise. /// - /// The execution logic that receives the current , an argument of type , and the token passed to . + /// The execution logic that receives the current , an argument of type , and the token passed to . /// The options to use to configure the external event. /// Thrown if is . public AsyncExternalEvent(Action handler, ExternalEventOptions options) @@ -100,6 +100,18 @@ public AsyncExternalEvent(Action handler, E _options = options; } + /// + /// Raises the external event asynchronously with the specified argument, + /// instructing Revit to execute the handler within the Revit API context. + /// + /// The argument to pass to the handler. + /// The representing the async operation being executed. + /// This overload passes to . + public Task RaiseAsync(T argument) + { + return RaiseAsync(argument, CancellationToken.None); + } + /// /// Raises the external event asynchronously with the specified argument, /// instructing Revit to execute the handler within the Revit API context. @@ -119,7 +131,7 @@ public AsyncExternalEvent(Action handler, E /// When is specified and Revit is in API mode, /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. /// - public Task RaiseAsync(T argument, CancellationToken cancellationToken = default) + public Task RaiseAsync(T argument, CancellationToken cancellationToken) { if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs index 24c1888..2476d56 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs @@ -7,7 +7,7 @@ namespace Nice3point.Revit.Toolkit.External; /// /// A generic asynchronous external event expanding /// that accepts an argument of type -/// and returns a result of type via . +/// and returns a result of type via . /// /// The type of the argument passed to the handler. /// The type of the result produced by the handler. @@ -69,7 +69,7 @@ public AsyncRequestExternalEvent(Func handler, Extern /// Initializes a new instance of the class /// with access to the instance and the cancellation token of the raise. /// - /// The execution logic that receives the current , an argument of type , and the token passed to , returning a result of type . + /// The execution logic that receives the current , an argument of type , and the token passed to , returning a result of type . /// Thrown if is . public AsyncRequestExternalEvent(Func handler) : this(handler, ExternalEventOptions.None) { @@ -79,7 +79,7 @@ public AsyncRequestExternalEvent(Func class /// with access to the instance and the cancellation token of the raise. /// - /// The execution logic that receives the current , an argument of type , and the token passed to , returning a result of type . + /// The execution logic that receives the current , an argument of type , and the token passed to , returning a result of type . /// The options to use to configure the external event. /// Thrown if is . public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options) @@ -90,6 +90,18 @@ public AsyncRequestExternalEvent(Func + /// Raises the external event asynchronously with the specified argument, + /// instructing Revit to execute the handler within the Revit API context. + /// + /// The argument to pass to the handler. + /// The representing the async operation being executed, containing the result of type . + /// This overload passes to . + public Task RaiseAsync(T argument) + { + return RaiseAsync(argument, CancellationToken.None); + } + /// /// Raises the external event asynchronously with the specified argument, /// instructing Revit to execute the handler within the Revit API context. @@ -109,7 +121,7 @@ public AsyncRequestExternalEvent(Func is specified and Revit is in API mode, /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. /// - public Task RaiseAsync(T argument, CancellationToken cancellationToken = default) + public Task RaiseAsync(T argument, CancellationToken cancellationToken) { if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs index 8c95f17..bc5ab4f 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs @@ -6,7 +6,7 @@ namespace Nice3point.Revit.Toolkit.External; /// /// A generic asynchronous external event expanding -/// to return a result of type via . +/// to return a result of type via . /// /// The type of the result produced by the handler. [PublicAPI] @@ -67,7 +67,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalE /// Initializes a new instance of the class /// with access to the instance and the cancellation token of the raise. /// - /// The execution logic that receives the current and the token passed to , returning a result of type . + /// The execution logic that receives the current and the token passed to , returning a result of type . /// Thrown if is . public AsyncRequestExternalEvent(Func handler) : this(handler, ExternalEventOptions.None) { @@ -77,7 +77,7 @@ public AsyncRequestExternalEvent(Func /// Initializes a new instance of the class /// with access to the instance and the cancellation token of the raise. /// - /// The execution logic that receives the current and the token passed to , returning a result of type . + /// The execution logic that receives the current and the token passed to , returning a result of type . /// The options to use to configure the external event. /// Thrown if is . public AsyncRequestExternalEvent(Func handler, ExternalEventOptions options) @@ -88,6 +88,16 @@ public AsyncRequestExternalEvent(Func _options = options; } + /// + /// Raises the external event asynchronously, instructing Revit to execute the handler within the Revit API context. + /// + /// The representing the async operation being executed, containing the result of type . + /// This overload passes to . + public Task RaiseAsync() + { + return RaiseAsync(CancellationToken.None); + } + /// /// Raises the external event asynchronously, instructing Revit to execute the handler within the Revit API context. /// @@ -105,7 +115,7 @@ public AsyncRequestExternalEvent(Func /// When is specified and Revit is in API mode, /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. /// - public Task RaiseAsync(CancellationToken cancellationToken = default) + public Task RaiseAsync(CancellationToken cancellationToken) { if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs b/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs index 553a67d..73b9b01 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs @@ -74,7 +74,8 @@ namespace Nice3point.Revit.Toolkit.External; /// /// // Extension methods: /// public static ExternalEventRequest Raise(this IExternalEvent<DeleteInstancesArgs> externalEvent, BuiltInCategory category, int count); -/// public static Task RaiseAsync(this IAsyncExternalEvent<DeleteInstancesArgs> externalEvent, BuiltInCategory category, int count, CancellationToken cancellationToken = default); +/// public static Task RaiseAsync(this IAsyncExternalEvent<DeleteInstancesArgs> externalEvent, BuiltInCategory category, int count); +/// public static Task RaiseAsync(this IAsyncExternalEvent<DeleteInstancesArgs> externalEvent, BuiltInCategory category, int count, CancellationToken cancellationToken); /// /// /// diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs index c51f0a1..d4a31cb 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs @@ -8,6 +8,12 @@ namespace Nice3point.Revit.Toolkit.External; [PublicAPI] public interface IAsyncExternalEvent { + /// + /// Raises (signals) the external event, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. + /// + /// The representing the async operation being executed. + Task RaiseAsync(); + /// /// Raises (signals) the external event, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. /// @@ -17,5 +23,5 @@ public interface IAsyncExternalEvent /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. /// A cancellation requested while the handler executes doesn't interrupt it. /// - Task RaiseAsync(CancellationToken cancellationToken = default); + Task RaiseAsync(CancellationToken cancellationToken); } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs index a5a9e1b..2c6b0e0 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs @@ -9,6 +9,13 @@ namespace Nice3point.Revit.Toolkit.External; [PublicAPI] public interface IAsyncExternalEvent { + /// + /// Raises (signals) the external event with the specified argument, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. + /// + /// The argument to pass to the handler. + /// The representing the async operation being executed. + Task RaiseAsync(T argument); + /// /// Raises (signals) the external event with the specified argument, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. /// @@ -19,5 +26,5 @@ public interface IAsyncExternalEvent /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. /// A cancellation requested while the handler executes doesn't interrupt it. /// - Task RaiseAsync(T argument, CancellationToken cancellationToken = default); + Task RaiseAsync(T argument, CancellationToken cancellationToken); } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs index eabc799..48ff98e 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs @@ -11,6 +11,13 @@ namespace Nice3point.Revit.Toolkit.External; [PublicAPI] public interface IAsyncRequestExternalEvent { + /// + /// Raises (signals) the external event with the specified argument, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. + /// + /// The argument to pass to the handler. + /// The representing the async operation being executed, containing the result of type . + Task RaiseAsync(T argument); + /// /// Raises (signals) the external event with the specified argument, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. /// @@ -21,5 +28,5 @@ public interface IAsyncRequestExternalEvent /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. /// A cancellation requested while the handler executes doesn't interrupt it. /// - Task RaiseAsync(T argument, CancellationToken cancellationToken = default); + Task RaiseAsync(T argument, CancellationToken cancellationToken); } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs index 3920d18..10e4eaa 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs @@ -10,6 +10,12 @@ namespace Nice3point.Revit.Toolkit.External; [PublicAPI] public interface IAsyncRequestExternalEvent { + /// + /// Raises (signals) the external event, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. + /// + /// The representing the async operation being executed, containing the result of type . + Task RaiseAsync(); + /// /// Raises (signals) the external event, instructing Revit to execute the handler within the Revit API context and wait for completion asynchronously. /// @@ -19,5 +25,5 @@ public interface IAsyncRequestExternalEvent /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. /// A cancellation requested while the handler executes doesn't interrupt it. /// - Task RaiseAsync(CancellationToken cancellationToken = default); + Task RaiseAsync(CancellationToken cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExtensionContainerTests.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExtensionContainerTests.cs index 4abda90..ea7de7d 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExtensionContainerTests.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/ExtensionContainerTests.cs @@ -58,7 +58,7 @@ [ExternalEvent] private void Run(int first, int second) { } var generatedContainer = output.GetTypeByMetadataName("Consumer.ModelExtensions")!; await Assert.That(generatedContainer.DeclaredAccessibility).IsEqualTo(containerAccessibility == "public" ? Accessibility.Public : Accessibility.Internal); await Assert.That(generatedContainer.GetMembers("Raise")).Count().IsEqualTo(1); - await Assert.That(generatedContainer.GetMembers("RaiseAsync")).Count().IsEqualTo(1); + await Assert.That(generatedContainer.GetMembers("RaiseAsync")).Count().IsEqualTo(2); } [Test] diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Fixtures/ExternalEvents.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Fixtures/ExternalEvents.cs index a77582a..5adf8cc 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Fixtures/ExternalEvents.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Fixtures/ExternalEvents.cs @@ -32,10 +32,10 @@ public enum ExternalEventOptions { None = 0, AllowDirectInvocation = 1 } public interface IExternalEvent { Autodesk.Revit.UI.ExternalEventRequest Raise(); } public interface IExternalEvent { Autodesk.Revit.UI.ExternalEventRequest Raise(T args); } - public interface IAsyncExternalEvent { Task RaiseAsync(CancellationToken cancellationToken = default); } - public interface IAsyncExternalEvent { Task RaiseAsync(T args, CancellationToken cancellationToken = default); } - public interface IAsyncRequestExternalEvent { Task RaiseAsync(CancellationToken cancellationToken = default); } - public interface IAsyncRequestExternalEvent { Task RaiseAsync(T args, CancellationToken cancellationToken = default); } + public interface IAsyncExternalEvent { Task RaiseAsync(); Task RaiseAsync(CancellationToken cancellationToken); } + public interface IAsyncExternalEvent { Task RaiseAsync(T args); Task RaiseAsync(T args, CancellationToken cancellationToken); } + public interface IAsyncRequestExternalEvent { Task RaiseAsync(); Task RaiseAsync(CancellationToken cancellationToken); } + public interface IAsyncRequestExternalEvent { Task RaiseAsync(T args); Task RaiseAsync(T args, CancellationToken cancellationToken); } public class ExternalEvent : IExternalEvent { @@ -67,7 +67,8 @@ public AsyncExternalEvent(Action handler(new Autodesk.Revit.UI.UIApplication(), cancellationToken); EventConstruction.Observer?.Invoke(); } - public Task RaiseAsync(CancellationToken cancellationToken = default) { _handler(cancellationToken); return Task.CompletedTask; } + public Task RaiseAsync() => RaiseAsync(CancellationToken.None); + public Task RaiseAsync(CancellationToken cancellationToken) { _handler(cancellationToken); return Task.CompletedTask; } } public class AsyncExternalEvent : IAsyncExternalEvent @@ -82,7 +83,8 @@ public AsyncExternalEvent(Action handler(new Autodesk.Revit.UI.UIApplication(), args, cancellationToken); EventConstruction.Observer?.Invoke(); } - public Task RaiseAsync(T args, CancellationToken cancellationToken = default) { _handler(args, cancellationToken); return Task.CompletedTask; } + public Task RaiseAsync(T args) => RaiseAsync(args, CancellationToken.None); + public Task RaiseAsync(T args, CancellationToken cancellationToken) { _handler(args, cancellationToken); return Task.CompletedTask; } } public class AsyncRequestExternalEvent : IAsyncRequestExternalEvent @@ -97,7 +99,8 @@ public AsyncRequestExternalEvent(Func handler(new Autodesk.Revit.UI.UIApplication(), cancellationToken); EventConstruction.Observer?.Invoke(); } - public Task RaiseAsync(CancellationToken cancellationToken = default) => Task.FromResult(_handler(cancellationToken)); + public Task RaiseAsync() => RaiseAsync(CancellationToken.None); + public Task RaiseAsync(CancellationToken cancellationToken) => Task.FromResult(_handler(cancellationToken)); } public class AsyncRequestExternalEvent : IAsyncRequestExternalEvent @@ -112,6 +115,7 @@ public AsyncRequestExternalEvent(Func handler(new Autodesk.Revit.UI.UIApplication(), args, cancellationToken); EventConstruction.Observer?.Invoke(); } - public Task RaiseAsync(T args, CancellationToken cancellationToken = default) => Task.FromResult(_handler(args, cancellationToken)); + public Task RaiseAsync(T args) => RaiseAsync(args, CancellationToken.None); + public Task RaiseAsync(T args, CancellationToken cancellationToken) => Task.FromResult(_handler(args, cancellationToken)); } } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Argument#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Argument#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index fc20d34..26460b1 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Argument#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.CancellationTokenName_KeepsDistinctBindingsAsync_scenario=Argument#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int cancellationToken, int count, global::System.Threading.CancellationToken cancellationToken1 = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int cancellationToken, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(cancellationToken, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int cancellationToken, int count, global::System.Threading.CancellationToken cancellationToken1) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(cancellationToken, count), cancellationToken1); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 8297d0e..aac9c12 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - internal static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken = default) + internal static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + internal static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs index 5f76eae..eb4269a 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=internal_genericAccessibility=public#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 49c960b..87bc08f 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs index ff0c796..bc222e9 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.DifferentGenericArities_PreserveIndependentExtensionAccessibilityAsync_ordinaryAccessibility=public_genericAccessibility=internal#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - internal static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int count, global::System.Threading.CancellationToken cancellationToken = default) + internal static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + internal static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=Consumer_typeName=field#Consumer.field.Run.6145C3C7A0D4FA0F.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=Consumer_typeName=field#Consumer.field.Run.6145C3C7A0D4FA0F.verified.cs index a75a73a..0e09a0f 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=Consumer_typeName=field#Consumer.field.Run.6145C3C7A0D4FA0F.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=Consumer_typeName=field#Consumer.field.Run.6145C3C7A0D4FA0F.verified.cs @@ -59,7 +59,14 @@ public static partial class @fieldExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.field.RunArgs(value, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.field.RunArgs(value, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=field_typeName=Model#field.Model.Run.8F529F91CA4A8D2A.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=field_typeName=Model#field.Model.Run.8F529F91CA4A8D2A.verified.cs index b0c1675..26b6785 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=field_typeName=Model#field.Model.Run.8F529F91CA4A8D2A.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.FieldContextualKeyword_PreservesNamespaceAndTypeBindingAsync_namespaceName=field_typeName=Model#field.Model.Run.8F529F91CA4A8D2A.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count) + { + return externalEvent.RaiseAsync(new global::field.Model.RunArgs(value, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::field.Model.RunArgs(value, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=application#Consumer.Model.application.3E69A3B91C661309.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=application#Consumer.Model.application.3E69A3B91C661309.verified.cs index 5bb36f1..2d97e7d 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=application#Consumer.Model.application.3E69A3B91C661309.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=application#Consumer.Model.application.3E69A3B91C661309.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.applicationArgs(first, second)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.applicationArgs(first, second), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=args#Consumer.Model.args.2DAA7B354C035418.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=args#Consumer.Model.args.2DAA7B354C035418.verified.cs index 8e228f5..ee35fcb 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=args#Consumer.Model.args.2DAA7B354C035418.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=args#Consumer.Model.args.2DAA7B354C035418.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.argsArgs(first, second)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.argsArgs(first, second), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=handler#Consumer.Model.handler.3D61EBDA00260524.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=handler#Consumer.Model.handler.3D61EBDA00260524.verified.cs index 9b4b293..c7119a2 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=handler#Consumer.Model.handler.3D61EBDA00260524.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.MethodName_MatchesGeneratedLocalNameWithoutShadowingAsync_methodName=handler#Consumer.Model.handler.3D61EBDA00260524.verified.cs @@ -71,7 +71,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.handlerArgs(first, second)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int first, int second, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.handlerArgs(first, second), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=clone#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=clone#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index ab293b8..3dadbe2 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=clone#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=clone#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int clone, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int clone, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(clone, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int clone, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(clone, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=deconstruct#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=deconstruct#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index b111ac5..e20cfd0 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=deconstruct#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=deconstruct#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int deconstruct, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int deconstruct, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(deconstruct, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int deconstruct, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(deconstruct, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equalityContract#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equalityContract#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 45cdcc9..c4775df 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equalityContract#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equalityContract#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int equalityContract, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int equalityContract, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(equalityContract, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int equalityContract, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(equalityContract, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equals#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equals#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index d73c5ac..72ab724 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equals#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=equals#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int @equals, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int @equals, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(@equals, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int @equals, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(@equals, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=getHashCode#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=getHashCode#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 2ab58dc..57be0f0 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=getHashCode#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=getHashCode#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int getHashCode, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int getHashCode, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(getHashCode, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int getHashCode, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(getHashCode, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=printMembers#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=printMembers#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 0e2d2fc..30ca409 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=printMembers#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=printMembers#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int printMembers, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int printMembers, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(printMembers, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int printMembers, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(printMembers, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=toString#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=toString#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 197af53..d618dfd 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=toString#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.RecordArgumentName_DoesNotCollideWithSynthesizedMembersAsync_parameterName=toString#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int toString, int count, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int toString, int count) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(toString, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int toString, int count, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(toString, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.ShadowedGenericParameter_PreservesDistinctExtensionTypeArgumentsAsync#Consumer.Outer`1.Inner`1.Run.4DB1B2D9FFC22275.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.ShadowedGenericParameter_PreservesDistinctExtensionTypeArgumentsAsync#Consumer.Outer`1.Inner`1.Run.4DB1B2D9FFC22275.verified.cs index c16ff38..ffbbd06 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.ShadowedGenericParameter_PreservesDistinctExtensionTypeArgumentsAsync#Consumer.Outer`1.Inner`1.Run.4DB1B2D9FFC22275.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorNameBindingTests.ShadowedGenericParameter_PreservesDistinctExtensionTypeArgumentsAsync#Consumer.Outer`1.Inner`1.Run.4DB1B2D9FFC22275.verified.cs @@ -62,7 +62,14 @@ public static partial class OuterExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.Inner.RunArgs> externalEvent, TValue1 value, int count, global::System.Threading.CancellationToken cancellationToken = default) where TValue : class where TValue1 : struct + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.Inner.RunArgs> externalEvent, TValue1 value, int count) where TValue : class where TValue1 : struct + { + return externalEvent.RaiseAsync(new global::Consumer.Outer.Inner.RunArgs(value, count)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.Inner.RunArgs> externalEvent, TValue1 value, int count, global::System.Threading.CancellationToken cancellationToken) where TValue : class where TValue1 : struct { return externalEvent.RaiseAsync(new global::Consumer.Outer.Inner.RunArgs(value, count), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Method#Consumer.Model.event.D890DA5C45B99396.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Method#Consumer.Model.event.D890DA5C45B99396.verified.cs index dd21d1c..fa38fa2 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Method#Consumer.Model.event.D890DA5C45B99396.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Method#Consumer.Model.event.D890DA5C45B99396.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.eventArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.eventArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Parameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Parameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 1a766fe..1479537 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Parameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Parameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int @event, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int @event, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(@event, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int @event, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(@event, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Type#Consumer.class.Run.6B49BE1219B268DF.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Type#Consumer.class.Run.6B49BE1219B268DF.verified.cs index 3c8733f..0f13f50 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Type#Consumer.class.Run.6B49BE1219B268DF.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.EscapedIdentifiers_GenerateCompilableEventsAsync_scenario=Type#Consumer.class.Run.6B49BE1219B268DF.verified.cs @@ -59,7 +59,14 @@ public static partial class @classExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.@class.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.@class.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.GenericArguments_PreserveExtensionConstraintsAsync#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.GenericArguments_PreserveExtensionConstraintsAsync#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs index f7a402b..6bdff6e 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.GenericArguments_PreserveExtensionConstraintsAsync#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.GenericArguments_PreserveExtensionConstraintsAsync#Consumer.Model`1.Run.BFAFF1DAC26910EF.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int quantity, global::System.Threading.CancellationToken cancellationToken = default) where TValue : class, global::System.IDisposable, new() + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int quantity) where TValue : class, global::System.IDisposable, new() + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, quantity)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent.RunArgs> externalEvent, TValue value, int quantity, global::System.Threading.CancellationToken cancellationToken) where TValue : class, global::System.IDisposable, new() { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, quantity), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.NestedGenericType_PreservesHierarchyAsync#Consumer.Outer`1.Model`1.Run.463E66F10AD83318.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.NestedGenericType_PreservesHierarchyAsync#Consumer.Outer`1.Model`1.Run.463E66F10AD83318.verified.cs index 707104e..a2dc99b 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.NestedGenericType_PreservesHierarchyAsync#Consumer.Outer`1.Model`1.Run.463E66F10AD83318.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.NestedGenericType_PreservesHierarchyAsync#Consumer.Outer`1.Model`1.Run.463E66F10AD83318.verified.cs @@ -37,7 +37,14 @@ public static partial class OuterExtensions { [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent.Model.RunArgs, TResult> externalEvent, TValue value, TResult result, global::System.Threading.CancellationToken cancellationToken = default) where TValue : class where TResult : struct + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent.Model.RunArgs, TResult> externalEvent, TValue value, TResult result) where TValue : class where TResult : struct + { + return externalEvent.RaiseAsync(new global::Consumer.Outer.Model.RunArgs(value, result)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent.Model.RunArgs, TResult> externalEvent, TValue value, TResult result, global::System.Threading.CancellationToken cancellationToken) where TValue : class where TResult : struct { return externalEvent.RaiseAsync(new global::Consumer.Outer.Model.RunArgs(value, result), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=Capitalization#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=Capitalization#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 2c567e8..22f62e8 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=Capitalization#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=Capitalization#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int Value, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int Value) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, Value)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int value, int Value, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(value, Value), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=ExtensionReceiver#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=ExtensionReceiver#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index d6794ed..833643b 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=ExtensionReceiver#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.ParameterNames_PreserveDistinctBindingsAsync_scenario=ExtensionReceiver#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent1, int externalEvent, int quantity, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent1, int externalEvent, int quantity) + { + return externalEvent1.RaiseAsync(new global::Consumer.Model.RunArgs(externalEvent, quantity)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent1, int externalEvent, int quantity, global::System.Threading.CancellationToken cancellationToken) { return externalEvent1.RaiseAsync(new global::Consumer.Model.RunArgs(externalEvent, quantity), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index e0fe3ab..58052b1 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -73,7 +73,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 1e198bb..94f978e 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=record struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -71,7 +71,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 239a841..519964b 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp13#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -73,7 +73,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 7e07e32..e7a4d70 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorRegressionTests.StructInstanceMethod_WithMultipleArgumentsGeneratesCompilableEventsAsync_declaration=struct_languageVersion=CSharp14#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -71,7 +71,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordDisabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordDisabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 654c79d..c9fedc7 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordDisabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordDisabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordEnabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordEnabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 7d6a627..f1e5e9e 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordEnabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.AllowDirectInvocation_PreservesOptionsAsync_scenario=RecordEnabled#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=BeforeLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=BeforeLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 19f4da5..b401a80 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=BeforeLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=BeforeLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, global::System.Threading.CancellationToken token, int quantity, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, global::System.Threading.CancellationToken token, int quantity) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(token, quantity)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, global::System.Threading.CancellationToken token, int quantity, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(token, quantity), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=NullableLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=NullableLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 829c329..da3b8f2 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=NullableLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.CancellationTokenOutsideTheLastParameter_RemainsUserArgumentAsync_scenario=NullableLastParameter#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, global::System.Threading.CancellationToken? token, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, global::System.Threading.CancellationToken? token) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, token)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, global::System.Threading.CancellationToken? token, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, token), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index eefad42..c4aeb20 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -34,7 +34,14 @@ public static partial class ModelExtensions { [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index b0c093a..e1a1e2f 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -34,7 +34,14 @@ public static partial class ModelExtensions { [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index c478a2a..7090380 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -34,7 +34,14 @@ public static partial class ModelExtensions { [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index afd8dd9..57e36b2 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=ResultArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -34,7 +34,14 @@ public static partial class ModelExtensions { [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 1e40d10..409b80f 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index e0e8d83..f034b76 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidApplicationArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 654c79d..c9fedc7 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArguments#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 0198da0..967aa3b 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.MethodSignature_GeneratesCompilableEventsAsync_scenario=VoidArgumentsToken#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, string label, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, label), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.NullableSignature_PreservesAnnotationsAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.NullableSignature_PreservesAnnotationsAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index ddd58cb..451855d 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.NullableSignature_PreservesAnnotationsAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.NullableSignature_PreservesAnnotationsAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -34,7 +34,14 @@ public static partial class ModelExtensions { [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, string? label, int? quantity, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, string? label, int? quantity) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(label, quantity)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncRequestExternalEvent externalEvent, string? label, int? quantity, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(label, quantity), cancellationToken); } diff --git a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.UIApplicationAfterFirstParameter_RemainsUserArgumentAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.UIApplicationAfterFirstParameter_RemainsUserArgumentAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs index 176d864..ed7a1b6 100644 --- a/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.UIApplicationAfterFirstParameter_RemainsUserArgumentAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs +++ b/tests/Nice3point.Revit.Toolkit.SourceGenerators.Tests/Snapshots/ExternalEventGeneratorTests.UIApplicationAfterFirstParameter_RemainsUserArgumentAsync#Consumer.Model.Run.C0F8FDCA0BFA30FC.verified.cs @@ -59,7 +59,14 @@ public static partial class ModelExtensions [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] - public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, global::Autodesk.Revit.UI.UIApplication application, global::System.Threading.CancellationToken cancellationToken = default) + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, global::Autodesk.Revit.UI.UIApplication application) + { + return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, application)); + } + + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCode("Nice3point.Revit.Toolkit.SourceGenerators.ExternalEventGenerator", "1.0.0.0")] + public static global::System.Threading.Tasks.Task RaiseAsync(this global::Nice3point.Revit.Toolkit.External.IAsyncExternalEvent externalEvent, int quantity, global::Autodesk.Revit.UI.UIApplication application, global::System.Threading.CancellationToken cancellationToken) { return externalEvent.RaiseAsync(new global::Consumer.Model.RunArgs(quantity, application), cancellationToken); } From 7ae74ee7d713f34b7889e97d3e7d5057caf21fa2 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Fri, 23 Oct 2026 21:39:21 +0300 Subject: [PATCH 06/14] [Docs] Reword the queue and cancellation documentation --- CHANGELOG.md | 54 +++++++++++++----- README.md | 56 +++++++++++++------ .../Emission/ExternalEventWriter.cs | 3 +- .../External/Events/AsyncExternalEvent.cs | 12 ++-- ...yncExternalEventCore{TArgument,TResult}.cs | 8 ++- .../External/Events/AsyncExternalEvent{T}.cs | 12 ++-- .../AsyncRequestExternalEvent{T,TResult}.cs | 14 +++-- .../AsyncRequestExternalEvent{TResult}.cs | 14 +++-- .../Attributes/ExternalEventAttribute.cs | 12 ++-- .../Events/ExternalEventCore{TArgument}.cs | 6 +- .../Events/ExternalEventRequestExtensions.cs | 2 +- .../Events/Interfaces/IAsyncExternalEvent.cs | 2 +- .../Interfaces/IAsyncExternalEvent{T}.cs | 2 +- .../IAsyncRequestExternalEvent{T,TResult}.cs | 2 +- .../IAsyncRequestExternalEvent{TResult}.cs | 2 +- 15 files changed, 132 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49224fe..1d1b5ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,53 +2,79 @@ ### External Events -- `ExternalEvent` and the asynchronous external events run the handler once for every raise, with the argument of that raise, in the order of the raises. Each `RaiseAsync()` task completes with the result or the exception of its own run. A raise that arrived before Revit ran the previous one used to overwrite its argument, and the earlier `RaiseAsync()` task never completed. -- `ExternalEvent` without an argument runs once for all raises that wait, as Revit runs a raised event. -- A raise that Revit refuses with `ExternalEventRequest.Denied` or `ExternalEventRequest.TimedOut` never runs. `Raise(argument)` returns that result, and the `RaiseAsync()` task fails with an `InvalidOperationException`. A refused raise used to stay queued and run with the next raise of the event. -- `RaiseAsync()` of the asynchronous external events and their interfaces has an overload that accepts a `CancellationToken`. A token canceled before Revit executes the handler withdraws the raise, and the task is canceled at once. A running handler is never interrupted. With `ExternalEventOptions.AllowDirectInvocation` in API mode, the token is checked once before the handler runs. +- `ExternalEvent` and the asynchronous external events run the handler once for every raise, with the argument of that raise, in the order of the raises. Each `RaiseAsync()` task completes with the result or the exception of its own run. Previously, a raise that arrived before Revit ran the previous one overwrote its argument. The earlier `RaiseAsync()` task never completed. +- `ExternalEvent` without an argument runs once for all waiting raises, as Revit runs a raised event. +- A raise that Revit refuses with `ExternalEventRequest.Denied` or `ExternalEventRequest.TimedOut` never runs. Its `RaiseAsync()` task fails with an `InvalidOperationException`. +- `RaiseAsync()` of the asynchronous external events and their interfaces has an overload that accepts a `CancellationToken`. A token canceled before Revit executes the handler cancels the task at once without executing the handler. A running handler is never interrupted. With `ExternalEventOptions.AllowDirectInvocation` in API mode, the token is checked once before the handler runs. - New constructors of `AsyncExternalEvent`, `AsyncExternalEvent`, `AsyncRequestExternalEvent`, and `AsyncRequestExternalEvent` accept a handler that receives the `UIApplication`, the argument, and the `CancellationToken` of the raise. An `OperationCanceledException` thrown for that token cancels the task. ```c# private readonly AsyncExternalEvent> _deleteWindowsAsyncEvent = new((application, elementIds, cancellationToken) => { + var document = application.ActiveUIDocument.Document; + using var transaction = new Transaction(document, "Delete windows"); + transaction.Start(); + foreach (var elementId in elementIds) { cancellationToken.ThrowIfCancellationRequested(); - application.ActiveUIDocument.Document.Delete(elementId); + document.Delete(elementId); } + + transaction.Commit(); }); -using var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)); -await _deleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); +private async Task DeleteWindowsAsync(ICollection elementIds) +{ + using var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + + try + { + await _deleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); + } + catch (OperationCanceledException) + { + //Revit was busy for 10 seconds, or the handler stopped on the token + } +} ``` ### ExternalEvent Source Generator -- A handler can declare a `CancellationToken` as its last parameter, after the optional `UIApplication` and the arguments. The asynchronous event passes it the token of `RaiseAsync()`, and the synchronous event passes `CancellationToken.None`. A `CancellationToken` in any other position remains an argument. +- A handler can declare a `CancellationToken` as its last parameter. The asynchronous event passes it the token of `RaiseAsync()`. The synchronous event passes `CancellationToken.None`. A `CancellationToken` in any other position remains an argument. - The generated `RaiseAsync()` extension methods for several arguments have an overload that accepts a `CancellationToken`. ```c# [ExternalEvent] private void DeleteWindows(UIApplication application, ICollection elementIds, CancellationToken cancellationToken) { + var document = application.ActiveUIDocument.Document; + using var transaction = new Transaction(document, "Delete windows"); + transaction.Start(); + foreach (var elementId in elementIds) { cancellationToken.ThrowIfCancellationRequested(); - application.ActiveUIDocument.Document.Delete(elementId); + document.Delete(elementId); } + + transaction.Commit(); } // Generates: // IExternalEvent> DeleteWindowsEvent // IAsyncExternalEvent> DeleteWindowsAsyncEvent -await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationToken); +try +{ + await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationToken); +} +catch (OperationCanceledException) +{ + //The raise was canceled before Revit executed it, or the handler stopped on the token +} ``` -### Breaking Changes - -- `IAsyncExternalEvent`, `IAsyncExternalEvent`, `IAsyncRequestExternalEvent`, and `IAsyncRequestExternalEvent` declare a `RaiseAsync` overload with a `CancellationToken`. Code that calls these interfaces runs unchanged, and custom implementations of the interfaces implement the overload. - # 2027.0.2 Performance optimizations for Revit 2027: diff --git a/README.md b/README.md index cbcea2f..1eb657a 100644 --- a/README.md +++ b/README.md @@ -201,11 +201,14 @@ so you can create them anywhere in your code and on any thread. Use `Raise()` when the caller does not need to wait, or `RaiseAsync()` when it needs completion or a return value. For generated event properties, see the [ExternalEvent attribute](#externalevent-attribute). -One event can serve several callers, such as two modeless windows or a background service and a window. -Every `Raise(argument)` and every `RaiseAsync()` call runs the handler once, with its own argument, in the order of the calls. +Several callers can share one event, such as two modeless windows, or a background service and a window. +Each `Raise(argument)` and `RaiseAsync()` call runs the handler once with its own argument, in the order of the calls. Each `RaiseAsync()` task completes with the result or the exception of its own run. -`ExternalEvent` without an argument runs once for all raises that are still waiting, the same way Revit runs a raised event. -A raise that Revit refuses with `Denied` or `TimedOut` never runs: `Raise(argument)` returns that result, and the `RaiseAsync()` task fails with an `InvalidOperationException`. +`ExternalEvent` without an argument runs once for all waiting raises, the same way Revit runs a raised event. + +A raise that Revit refuses with `Denied` or `TimedOut` never runs. +`Raise(argument)` returns that result to the caller. +The `RaiseAsync()` task fails with an `InvalidOperationException`. #### ExternalEvent @@ -365,7 +368,8 @@ private async Task DeleteWindowAsync(ElementId elementId) #### Cancellation Every `RaiseAsync()` method has an overload that accepts a `CancellationToken`. -A modal dialog or a running command keeps Revit from executing external events, and the token lets the caller stop waiting: +A modal dialog or a running command keeps Revit from executing external events. +The token lets the caller stop waiting for such an event: ```c# private async Task DeleteWindowAsync(ElementId elementId) @@ -383,13 +387,16 @@ private async Task DeleteWindowAsync(ElementId elementId) } ``` -The token affects the raise as follows: +The moment of the cancellation decides what happens to the raise: + +| Token canceled | Delegate | Task | +|---------------------------------|-----------------|--------------------------------------------| +| Before Revit executes the raise | Doesn't run | Canceled at once | +| While the delegate runs | Runs to its end | Completes with the outcome of the delegate | -- A token canceled before Revit executes the delegate withdraws the raise: the delegate doesn't run, and the task is canceled at once. -- A token canceled while the delegate runs doesn't interrupt it, and the task completes with the outcome of the delegate. -- The other raises of the same event run as if the canceled raise didn't exist. +The other raises of the same event run as usual. -To stop a long-running delegate, accept the token as the last parameter of the delegate: +To stop a long-running delegate, accept the token as its last parameter: ```c# private readonly AsyncExternalEvent> _deleteWindowsAsyncEvent = new((application, elementIds, cancellationToken) => @@ -408,9 +415,12 @@ private readonly AsyncExternalEvent> _deleteWindowsAsyncE }); ``` -An `OperationCanceledException` thrown for the token rolls back the transaction and cancels the task. +An `OperationCanceledException` thrown for the token cancels the task. +The `using` statement rolls back the transaction that the delegate didn't commit. + The delegate observes a cancellation requested from another thread, such as a timeout or a remote client. -A cancel button of a modeless window runs on the Revit thread and takes effect only after the delegate returns. +A cancel button of a modeless window runs on the Revit thread. +Its cancellation takes effect only after the delegate returns. #### ExternalEventOptions @@ -634,9 +644,8 @@ If initialization throws, the next access retries it. **Methods with CancellationToken parameter** -Every generated `RaiseAsync()` method has an overload that accepts a `CancellationToken`, as described in [Cancellation](#cancellation). -Place `CancellationToken` last when the handler needs that token. -The event supplies it, the same way as `UIApplication`, and the token isn't one of the event arguments: +Declare a `CancellationToken` as the last parameter when the handler needs the token of `RaiseAsync()`. +The token isn't one of the event arguments: ```c# [ExternalEvent] @@ -660,13 +669,26 @@ private void DeleteWindows(UIApplication application, ICollection ele // IAsyncExternalEvent> DeleteWindowsAsyncEvent ``` +Every generated `RaiseAsync()` method has an overload that accepts a `CancellationToken`. +A canceled raise throws an `OperationCanceledException` from `await`: + ```c# using var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)); -await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); + +try +{ + await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); +} +catch (OperationCanceledException) +{ + //Revit was busy for 10 seconds, or the handler stopped on the token +} ``` -`DeleteWindowsAsyncEvent` passes the token of `RaiseAsync()` to the handler, and `DeleteWindowsEvent` passes `CancellationToken.None`. +`DeleteWindowsAsyncEvent` passes the token of `RaiseAsync()` to the handler. +`DeleteWindowsEvent` passes `CancellationToken.None`. A `CancellationToken` in any other position is a regular argument. +[Cancellation](#cancellation) describes when the token takes effect. **Enabling direct invocation** diff --git a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs index 820ee41..6fb8c77 100644 --- a/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs +++ b/source/Nice3point.Revit.Toolkit.SourceGenerators/ExternalEvents/Emission/ExternalEventWriter.cs @@ -358,7 +358,8 @@ private static EventInitializer BuildPropertyInitializer( /// Determines whether the handler delegate matches a constructor of the event as declared. /// /// - /// Several arguments arrive as one generated record, and a handler that receives a cancellation token matches only the asynchronous constructors that pass the UIApplication too. + /// Several arguments arrive as one generated record. + /// A handler that receives a cancellation token matches only the asynchronous constructors that pass the UIApplication too. /// Every other handler is adapted with a lambda. /// private static bool UsesHandlerDelegate(ExternalEventDefinition eventDefinition, EventPropertyKind propertyKind) diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs index d411c7d..54630e1 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs @@ -118,13 +118,15 @@ public Task RaiseAsync() /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

- /// Each call executes the handler once, in the order of the calls, and the returned task completes with the outcome of that execution. - /// A call that Revit refuses with or never executes the handler, and the task fails with an .

- /// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. + /// Each call executes the handler once, in the order of the calls. + /// The returned task completes with the outcome of that execution. + /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. /// A cancellation requested while the handler executes doesn't interrupt it. - /// A handler that receives the token can throw an for it, and the task is then canceled.

+ /// An that the handler throws for the token cancels the task.

/// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. + /// the handler is executed directly on the calling thread instead of being queued. + /// The token is checked once, before the handler executes. /// public Task RaiseAsync(CancellationToken cancellationToken) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs index fc797ea..6dfba16 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs @@ -14,8 +14,10 @@ namespace Nice3point.Revit.Toolkit.External; /// The function that raises the Revit external event. /// /// Revit runs a pending external event once however often it is raised. -/// Each execution runs the oldest raise and raises the event again for the rest, and a refused raise runs the rest in the same execution.
-/// The execution, the cancellation, and the refusal of a raise compete for the reservation of its completion, and only the one that reserves it completes the task. +/// Each execution runs the oldest raise and raises the event again for the rest. +/// When Revit refuses that raise, the rest runs in the same execution.
+/// The execution, the cancellation, and the refusal of a raise compete for the reservation of its completion. +/// Only the one that reserves it completes the task. ///
internal sealed class AsyncExternalEventCore(Func handler, Func raise) { @@ -55,7 +57,7 @@ public Task Invoke(UIApplication uiApplication, TArgument argument, Can /// The argument to pass to the handler. /// The token that withdraws the raise before its execution, passed to the handler. /// A task that completes with the outcome of the handler, or a task canceled at the cancellation of before the execution. - /// A raise that Revit refuses leaves the queue unexecuted, and its task fails with an . + /// A raise that Revit refuses fails its task with an without executing. public Task RaiseAsync(TArgument argument, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs index 7330030..9ad827f 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs @@ -123,13 +123,15 @@ public Task RaiseAsync(T argument) /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

- /// Each call executes the handler once with its own argument, in the order of the calls, and the returned task completes with the outcome of that execution. - /// A call that Revit refuses with or never executes the handler, and the task fails with an .

- /// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. + /// Each call executes the handler once with its own argument, in the order of the calls. + /// The returned task completes with the outcome of that execution. + /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. /// A cancellation requested while the handler executes doesn't interrupt it. - /// A handler that receives the token can throw an for it, and the task is then canceled.

+ /// An that the handler throws for the token cancels the task.

/// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. + /// the handler is executed directly on the calling thread instead of being queued. + /// The token is checked once, before the handler executes. /// public Task RaiseAsync(T argument, CancellationToken cancellationToken) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs index 2476d56..47cfc7f 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs @@ -113,13 +113,15 @@ public Task RaiseAsync(T argument) /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

- /// Each call executes the handler once with its own argument, in the order of the calls, and the returned task completes with the outcome of that execution. - /// A call that Revit refuses with or never executes the handler, and the task fails with an .

- /// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. - /// A cancellation requested while the handler executes doesn't interrupt it, and the task completes with its result. - /// A handler that receives the token can throw an for it, and the task is then canceled.

+ /// Each call executes the handler once with its own argument, in the order of the calls. + /// The returned task completes with the outcome of that execution. + /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. + /// A cancellation requested while the handler executes doesn't interrupt it. + /// An that the handler throws for the token cancels the task.

/// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. + /// the handler is executed directly on the calling thread instead of being queued. + /// The token is checked once, before the handler executes. /// public Task RaiseAsync(T argument, CancellationToken cancellationToken) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs index bc5ab4f..56c3e1a 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs @@ -107,13 +107,15 @@ public Task RaiseAsync() /// Revit will wait until it is ready to process the event and then it will execute its event handler. /// Revit processes external events only when no other commands or edit modes are currently active in Revit, /// which is the same policy like the one that applies to evoking external commands.

- /// Each call executes the handler once, in the order of the calls, and the returned task completes with the outcome of that execution. - /// A call that Revit refuses with or never executes the handler, and the task fails with an .

- /// A cancellation requested before Revit executes the handler withdraws the call: the handler doesn't execute, and the task is canceled at once. - /// A cancellation requested while the handler executes doesn't interrupt it, and the task completes with its result. - /// A handler that receives the token can throw an for it, and the task is then canceled.

+ /// Each call executes the handler once, in the order of the calls. + /// The returned task completes with the outcome of that execution. + /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. + /// A cancellation requested while the handler executes doesn't interrupt it. + /// An that the handler throws for the token cancels the task.

/// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued, and the token is checked once before the handler executes. + /// the handler is executed directly on the calling thread instead of being queued. + /// The token is checked once, before the handler executes. /// public Task RaiseAsync(CancellationToken cancellationToken) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs b/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs index 73b9b01..4a472ca 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Attributes/ExternalEventAttribute.cs @@ -79,13 +79,15 @@ namespace Nice3point.Revit.Toolkit.External; /// ///
/// -/// Methods with a cancellation token. A method can declare a as its last parameter, -/// after the optional UIApplication and the extra parameters. The token is not an extra parameter: +/// Methods with a cancellation token. +/// A method can declare a as its last parameter. +/// The token is not an extra parameter: /// -/// void Method(UIApplication, ElementId, CancellationToken); -/// +/// void Method(ElementId, CancellationToken); +/// /// Will generate an property and an property with an ElementId argument. -/// The asynchronous property passes the token of RaiseAsync to the method, and the synchronous property passes . +/// The asynchronous property passes the token of RaiseAsync to the method. +/// The synchronous property passes . /// A in any other position is an extra parameter. /// /// diff --git a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventCore{TArgument}.cs b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventCore{TArgument}.cs index 52b289b..b9cb020 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventCore{TArgument}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventCore{TArgument}.cs @@ -13,8 +13,10 @@ namespace Nice3point.Revit.Toolkit.External; /// The function that raises the Revit external event. /// /// Revit runs a pending external event once however often it is raised. -/// Each execution runs the oldest raise and raises the event again for the rest, and a refused raise runs the rest in the same execution.
-/// The execution and the refusal of a raise compete for its reservation, and a raise that Revit refuses never executes. +/// Each execution runs the oldest raise and raises the event again for the rest. +/// When Revit refuses that raise, the rest runs in the same execution.
+/// The execution and the refusal of a raise compete for its reservation. +/// A raise that Revit refuses never executes. ///
internal sealed class ExternalEventCore(Action handler, Func raise) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventRequestExtensions.cs b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventRequestExtensions.cs index d59c3c6..0a91a60 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventRequestExtensions.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEventRequestExtensions.cs @@ -15,7 +15,7 @@ internal static class ExternalEventRequestExtensions extension(ExternalEventRequest request) { /// - /// Gets a value indicating whether Revit refused to raise the external event, leaving no execution scheduled for the request. + /// Gets a value indicating whether Revit refused to raise the external event. /// public bool IsRefused => request is ExternalEventRequest.Denied or ExternalEventRequest.TimedOut; } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs index d4a31cb..eac5567 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent.cs @@ -20,7 +20,7 @@ public interface IAsyncExternalEvent /// The token to monitor for cancellation requests. /// The representing the async operation being executed. /// - /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. + /// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. /// A cancellation requested while the handler executes doesn't interrupt it. /// Task RaiseAsync(CancellationToken cancellationToken); diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs index 2c6b0e0..e00ea7d 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncExternalEvent{T}.cs @@ -23,7 +23,7 @@ public interface IAsyncExternalEvent /// The token to monitor for cancellation requests. /// The representing the async operation being executed. /// - /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. + /// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. /// A cancellation requested while the handler executes doesn't interrupt it. /// Task RaiseAsync(T argument, CancellationToken cancellationToken); diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs index 48ff98e..9369dea 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{T,TResult}.cs @@ -25,7 +25,7 @@ public interface IAsyncRequestExternalEvent /// The token to monitor for cancellation requests. /// The representing the async operation being executed, containing the result of type . /// - /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. + /// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. /// A cancellation requested while the handler executes doesn't interrupt it. /// Task RaiseAsync(T argument, CancellationToken cancellationToken); diff --git a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs index 10e4eaa..d3c4ab8 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/Interfaces/IAsyncRequestExternalEvent{TResult}.cs @@ -22,7 +22,7 @@ public interface IAsyncRequestExternalEvent /// The token to monitor for cancellation requests. /// The representing the async operation being executed, containing the result of type . /// - /// A cancellation requested before Revit executes the handler withdraws the raise, and the task is canceled at once. + /// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. /// A cancellation requested while the handler executes doesn't interrupt it. /// Task RaiseAsync(CancellationToken cancellationToken); From fe6334a23720911286715ac50c4a19fece623483 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sat, 24 Oct 2026 02:32:55 +0300 Subject: [PATCH 07/14] [Events] Queue parameterless external event calls --- CHANGELOG.md | 76 ++------------ README.md | 68 +++++-------- .../External/Events/AsyncExternalEvent.cs | 25 ++++- ...yncExternalEventCore{TArgument,TResult}.cs | 57 +++++++---- .../External/Events/AsyncExternalEvent{T}.cs | 39 +++++++- .../AsyncRequestExternalEvent{T,TResult}.cs | 36 ++++++- .../AsyncRequestExternalEvent{TResult}.cs | 22 ++++- .../External/Events/ExternalEvent{T}.cs | 23 +++++ .../AsyncExternalEventCoreTests.cs | 82 +++++++++++++++ .../AsyncExternalEventTests.cs | 88 +++++++++++++++++ .../AsyncRequestExternalEventTests.cs | 99 +++++++++++++++++++ .../ExternalEventTests.cs | 36 +++++++ 12 files changed, 518 insertions(+), 133 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d1b5ab..592646e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,78 +2,20 @@ ### External Events -- `ExternalEvent` and the asynchronous external events run the handler once for every raise, with the argument of that raise, in the order of the raises. Each `RaiseAsync()` task completes with the result or the exception of its own run. Previously, a raise that arrived before Revit ran the previous one overwrote its argument. The earlier `RaiseAsync()` task never completed. -- `ExternalEvent` without an argument runs once for all waiting raises, as Revit runs a raised event. -- A raise that Revit refuses with `ExternalEventRequest.Denied` or `ExternalEventRequest.TimedOut` never runs. Its `RaiseAsync()` task fails with an `InvalidOperationException`. -- `RaiseAsync()` of the asynchronous external events and their interfaces has an overload that accepts a `CancellationToken`. A token canceled before Revit executes the handler cancels the task at once without executing the handler. A running handler is never interrupted. With `ExternalEventOptions.AllowDirectInvocation` in API mode, the token is checked once before the handler runs. -- New constructors of `AsyncExternalEvent`, `AsyncExternalEvent`, `AsyncRequestExternalEvent`, and `AsyncRequestExternalEvent` accept a handler that receives the `UIApplication`, the argument, and the `CancellationToken` of the raise. An `OperationCanceledException` thrown for that token cancels the task. - -```c# -private readonly AsyncExternalEvent> _deleteWindowsAsyncEvent = new((application, elementIds, cancellationToken) => -{ - var document = application.ActiveUIDocument.Document; - using var transaction = new Transaction(document, "Delete windows"); - transaction.Start(); - - foreach (var elementId in elementIds) - { - cancellationToken.ThrowIfCancellationRequested(); - document.Delete(elementId); - } - - transaction.Commit(); -}); - -private async Task DeleteWindowsAsync(ICollection elementIds) -{ - using var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - - try - { - await _deleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); - } - catch (OperationCanceledException) - { - //Revit was busy for 10 seconds, or the handler stopped on the token - } -} -``` +- `ExternalEvent` and the asynchronous external events execute each queued raise with its own argument. Each `RaiseAsync()` task completes with the result or exception of its handler. Previously, concurrent raises overwrote the argument and left earlier tasks incomplete. +- `Raise()` on asynchronous events queues the handler without waiting for completion. On events with an argument, it uses the last supplied argument, or `default(T)` before the first call. Handler exceptions propagate to Revit. +- Requests refused with `ExternalEventRequest.Denied` or `ExternalEventRequest.TimedOut` skip the handler. `Raise()` returns the refusal; `RaiseAsync()` returns a task faulted with `InvalidOperationException`. +- New `RaiseAsync()` overloads accept a `CancellationToken` to cancel a pending execution. Token-aware constructors pass the token to the handler for cooperative cancellation during execution. ### ExternalEvent Source Generator -- A handler can declare a `CancellationToken` as its last parameter. The asynchronous event passes it the token of `RaiseAsync()`. The synchronous event passes `CancellationToken.None`. A `CancellationToken` in any other position remains an argument. -- The generated `RaiseAsync()` extension methods for several arguments have an overload that accepts a `CancellationToken`. - -```c# -[ExternalEvent] -private void DeleteWindows(UIApplication application, ICollection elementIds, CancellationToken cancellationToken) -{ - var document = application.ActiveUIDocument.Document; - using var transaction = new Transaction(document, "Delete windows"); - transaction.Start(); - - foreach (var elementId in elementIds) - { - cancellationToken.ThrowIfCancellationRequested(); - document.Delete(elementId); - } - - transaction.Commit(); -} +- A trailing `CancellationToken` parameter receives the token of `RaiseAsync()` and is excluded from the event arguments. Synchronous events pass `CancellationToken.None`. +- Generated `RaiseAsync()` extension methods include an overload with a `CancellationToken`. -// Generates: -// IExternalEvent> DeleteWindowsEvent -// IAsyncExternalEvent> DeleteWindowsAsyncEvent +### Breaking Changes -try -{ - await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationToken); -} -catch (OperationCanceledException) -{ - //The raise was canceled before Revit executed it, or the handler stopped on the token -} -``` +- Custom implementations of the asynchronous event interfaces must implement the overload with a `CancellationToken`. +- Recompiling a handler with a trailing `CancellationToken` changes its generated argument types. For `void Run(CancellationToken token)`, replace `RunEvent.Raise(token)` with `RunEvent.Raise()`. `RunAsyncEvent.RaiseAsync(token)` passes the cancellation token to the handler. # 2027.0.2 diff --git a/README.md b/README.md index 1eb657a..2bdb3a8 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,11 @@ A raise that Revit refuses with `Denied` or `TimedOut` never runs. `Raise(argument)` returns that result to the caller. The `RaiseAsync()` task fails with an `InvalidOperationException`. +The asynchronous event classes also expose `Raise()` to queue the handler without waiting for completion. +For events with an argument, this overload uses the last supplied argument, or `default(T)` before the first call. +It always queues the handler, including when `AllowDirectInvocation` is enabled, and passes `CancellationToken.None`. +Handler exceptions propagate to Revit; `RaiseAsync()` reports them through its task. + #### ExternalEvent A synchronous external event that queues the handler via the Revit external event mechanism. @@ -368,8 +373,7 @@ private async Task DeleteWindowAsync(ElementId elementId) #### Cancellation Every `RaiseAsync()` method has an overload that accepts a `CancellationToken`. -A modal dialog or a running command keeps Revit from executing external events. -The token lets the caller stop waiting for such an event: +Pass a cancellation token to cancel a pending execution while Revit is busy with a modal dialog or a running command: ```c# private async Task DeleteWindowAsync(ElementId elementId) @@ -382,19 +386,14 @@ private async Task DeleteWindowAsync(ElementId elementId) } catch (OperationCanceledException) { - //Revit was busy for 10 seconds, the delegate never executes + //The pending execution was canceled } } ``` -The moment of the cancellation decides what happens to the raise: - -| Token canceled | Delegate | Task | -|---------------------------------|-----------------|--------------------------------------------| -| Before Revit executes the raise | Doesn't run | Canceled at once | -| While the delegate runs | Runs to its end | Completes with the outcome of the delegate | - -The other raises of the same event run as usual. +Cancellation before execution cancels the task without running the delegate. +Cancellation during execution does not interrupt the delegate; the task completes with its result or exception. +Other queued calls are unaffected. To stop a long-running delegate, accept the token as its last parameter: @@ -415,8 +414,8 @@ private readonly AsyncExternalEvent> _deleteWindowsAsyncE }); ``` -An `OperationCanceledException` thrown for the token cancels the task. -The `using` statement rolls back the transaction that the delegate didn't commit. +When the token is canceled, `ThrowIfCancellationRequested()` stops the delegate and cancels the task. +Disposing the uncommitted transaction rolls back the changes. The delegate observes a cancellation requested from another thread, such as a timeout or a remote client. A cancel button of a modeless window runs on the Revit thread. @@ -644,51 +643,30 @@ If initialization throws, the next access retries it. **Methods with CancellationToken parameter** -Declare a `CancellationToken` as the last parameter when the handler needs the token of `RaiseAsync()`. -The token isn't one of the event arguments: +Declare a `CancellationToken` as the last parameter to receive the token passed to `RaiseAsync()`. +The generator excludes it from the event arguments: ```c# [ExternalEvent] -private void DeleteWindows(UIApplication application, ICollection elementIds, CancellationToken cancellationToken) +private void DeleteWindow(UIApplication application, ElementId elementId, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); var document = application.ActiveUIDocument.Document; - using var transaction = new Transaction(document, "Delete windows"); + using var transaction = new Transaction(document, "Delete window"); transaction.Start(); - - foreach (var elementId in elementIds) - { - cancellationToken.ThrowIfCancellationRequested(); - document.Delete(elementId); - } - + document.Delete(elementId); transaction.Commit(); } // Generates: -// IExternalEvent> DeleteWindowsEvent -// IAsyncExternalEvent> DeleteWindowsAsyncEvent -``` - -Every generated `RaiseAsync()` method has an overload that accepts a `CancellationToken`. -A canceled raise throws an `OperationCanceledException` from `await`: - -```c# -using var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - -try -{ - await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); -} -catch (OperationCanceledException) -{ - //Revit was busy for 10 seconds, or the handler stopped on the token -} +// IExternalEvent DeleteWindowEvent +// IAsyncExternalEvent DeleteWindowAsyncEvent ``` -`DeleteWindowsAsyncEvent` passes the token of `RaiseAsync()` to the handler. -`DeleteWindowsEvent` passes `CancellationToken.None`. +Call `await DeleteWindowAsyncEvent.RaiseAsync(elementId, cancellationToken)` to pass the token to the handler. +`DeleteWindowEvent.Raise(elementId)` passes `CancellationToken.None`. A `CancellationToken` in any other position is a regular argument. -[Cancellation](#cancellation) describes when the token takes effect. +See [Cancellation](#cancellation) for cancellation behavior and exception handling. **Enabling direct invocation** diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs index 54630e1..8e1d82b 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs @@ -38,6 +38,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) handler(); return default; }, base.Raise); + _options = options; } @@ -67,6 +68,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions op handler(uiApplication); return default; }, base.Raise); + _options = options; } @@ -96,6 +98,7 @@ public AsyncExternalEvent(Action handler, Exte handler(uiApplication, cancellationToken); return default; }, base.Raise); + _options = options; } @@ -103,7 +106,17 @@ public AsyncExternalEvent(Action handler, Exte /// Raises the external event asynchronously, instructing Revit to execute the handler within the Revit API context. /// /// The representing the async operation being executed. - /// This overload passes to . + /// + /// Revit will wait until it is ready to process the event and then it will execute its event handler. + /// Revit processes external events only when no other commands or edit modes are currently active in Revit, + /// which is the same policy like the one that applies to evoking external commands.

+ /// Each call executes the handler once, in the order of the calls. + /// The returned task completes with the outcome of that execution. + /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// When is specified and Revit is in API mode, + /// the handler is executed directly on the calling thread instead of being queued. + /// This overload passes to . + ///
public Task RaiseAsync() { return RaiseAsync(CancellationToken.None); @@ -138,6 +151,16 @@ public Task RaiseAsync(CancellationToken cancellationToken) return _core.RaiseAsync(default, cancellationToken); } + /// + /// + /// Each call queues a separate execution without waiting for completion. + /// The handler receives . An exception propagates to Revit. + /// + public override ExternalEventRequest Raise() + { + return _core.Raise(default); + } + /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs index 6dfba16..6be2cfb 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs @@ -23,6 +23,17 @@ internal sealed class AsyncExternalEventCore(Func _operations = new(); + /// + /// Adds a raise to the end of the queue without a completion task and raises the Revit external event. + /// + /// The argument to pass to the handler. + /// The result of the request to raise the Revit external event. + /// An exception of the handler propagates to Revit. + public ExternalEventRequest Raise(TArgument argument) + { + return Enqueue(new Operation(argument)); + } + /// /// Executes the handler on the calling thread. /// @@ -66,15 +77,9 @@ public Task RaiseAsync(TArgument argument, CancellationToken cancellati } var operation = new Operation(argument, cancellationToken); - _operations.Enqueue(operation); + Enqueue(operation); - var request = raise(); - if (request.IsRefused) - { - operation.TrySetException(new InvalidOperationException($"Revit did not accept the external event request and returned the {request} result. Raise the external event again.")); - } - - return operation.Task; + return operation.Task!; } /// @@ -91,7 +96,7 @@ public void Execute(UIApplication uiApplication) { operation.DangerousSetResult(handler(uiApplication, operation.Argument, operation.CancellationToken)); } - catch (Exception exception) + catch (Exception exception) when (operation.Task is not null) { operation.DangerousSetException(exception); } @@ -103,6 +108,19 @@ public void Execute(UIApplication uiApplication) } } + private ExternalEventRequest Enqueue(Operation operation) + { + _operations.Enqueue(operation); + + var request = raise(); + if (request.IsRefused) + { + operation.TrySetException(new InvalidOperationException($"Revit did not accept the external event request and returned the {request} result. Raise the external event again.")); + } + + return request; + } + private bool TryDequeueAndReserveCompletion([NotNullWhen(true)] out Operation? operation) { while (_operations.TryDequeue(out operation)) @@ -123,20 +141,25 @@ private static bool IsCanceledBy(OperationCanceledException exception, Cancellat private sealed class Operation { - private readonly TaskCompletionSource _completionSource = new(TaskCreationOptions.RunContinuationsAsynchronously); + private readonly TaskCompletionSource? _completionSource; private CancellationTokenRegistration _cancellationRegistration; private int _completionReserved; - public Operation(TArgument argument, CancellationToken cancellationToken) + public Operation(TArgument argument) { Argument = argument; + } + + public Operation(TArgument argument, CancellationToken cancellationToken) : this(argument) + { + _completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); CancellationToken = cancellationToken; _cancellationRegistration = cancellationToken.Register(static state => ((Operation)state!).TrySetCanceled(), this); } public TArgument Argument { get; private set; } public CancellationToken CancellationToken { get; } - public Task Task => _completionSource.Task; + public Task? Task => _completionSource?.Task; public bool TryReserveCompletion() { @@ -157,24 +180,24 @@ public bool TrySetException(Exception exception) } Argument = default!; - _completionSource.TrySetException(exception); + _completionSource?.TrySetException(exception); return true; } public void DangerousSetResult(TResult result) { - _completionSource.TrySetResult(result); + _completionSource?.TrySetResult(result); } public void DangerousSetException(Exception exception) { if (exception is OperationCanceledException canceledException && IsCanceledBy(canceledException, CancellationToken)) { - _completionSource.TrySetCanceled(CancellationToken); + _completionSource!.TrySetCanceled(CancellationToken); return; } - _completionSource.TrySetException(exception); + _completionSource!.TrySetException(exception); } private void TrySetCanceled() @@ -185,7 +208,7 @@ private void TrySetCanceled() } Argument = default!; - _completionSource.TrySetCanceled(CancellationToken); + _completionSource!.TrySetCanceled(CancellationToken); } } } diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs index 9ad827f..dbc211a 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs @@ -14,6 +14,8 @@ public sealed class AsyncExternalEvent : ExternalEventHandler, IAsyncExternal { private readonly AsyncExternalEventCore _core; private readonly ExternalEventOptions _options; + private readonly Lock _argumentLock = new(); + private T _argument = default!; /// /// Initializes a new instance of the class. @@ -39,6 +41,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) handler(argument); return default; }, base.Raise); + _options = options; } @@ -68,6 +71,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions handler(uiApplication, argument); return default; }, base.Raise); + _options = options; } @@ -97,6 +101,7 @@ public AsyncExternalEvent(Action handler, E handler(uiApplication, argument, cancellationToken); return default; }, base.Raise); + _options = options; } @@ -106,7 +111,17 @@ public AsyncExternalEvent(Action handler, E /// /// The argument to pass to the handler. /// The representing the async operation being executed. - /// This overload passes to . + /// + /// Revit will wait until it is ready to process the event and then it will execute its event handler. + /// Revit processes external events only when no other commands or edit modes are currently active in Revit, + /// which is the same policy like the one that applies to evoking external commands.

+ /// Each call executes the handler once with its own argument, in the order of the calls. + /// The returned task completes with the outcome of that execution. + /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// When is specified and Revit is in API mode, + /// the handler is executed directly on the calling thread instead of being queued. + /// This overload passes to . + ///
public Task RaiseAsync(T argument) { return RaiseAsync(argument, CancellationToken.None); @@ -135,6 +150,11 @@ public Task RaiseAsync(T argument) /// public Task RaiseAsync(T argument, CancellationToken cancellationToken) { + lock (_argumentLock) + { + _argument = argument; + } + if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { return _core.Invoke(RevitContext.UiApplication, argument, cancellationToken); @@ -143,6 +163,23 @@ public Task RaiseAsync(T argument, CancellationToken cancellationToken) return _core.RaiseAsync(argument, cancellationToken); } + /// + /// + /// Each call queues a separate execution with the last argument passed to , + /// or the default value of before the first call. + /// The handler receives . An exception propagates to Revit. + /// + public override ExternalEventRequest Raise() + { + T argument; + lock (_argumentLock) + { + argument = _argument; + } + + return _core.Raise(argument); + } + /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs index 47cfc7f..d875615 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs @@ -16,6 +16,8 @@ public sealed class AsyncRequestExternalEvent : ExternalEventHandler { private readonly AsyncExternalEventCore _core; private readonly ExternalEventOptions _options; + private readonly Lock _argumentLock = new(); + private T _argument = default!; /// /// Initializes a new instance of the class. @@ -96,7 +98,17 @@ public AsyncRequestExternalEvent(Func /// The argument to pass to the handler. /// The representing the async operation being executed, containing the result of type . - /// This overload passes to . + /// + /// Revit will wait until it is ready to process the event and then it will execute its event handler. + /// Revit processes external events only when no other commands or edit modes are currently active in Revit, + /// which is the same policy like the one that applies to evoking external commands.

+ /// Each call executes the handler once with its own argument, in the order of the calls. + /// The returned task completes with the outcome of that execution. + /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// When is specified and Revit is in API mode, + /// the handler is executed directly on the calling thread instead of being queued. + /// This overload passes to . + ///
public Task RaiseAsync(T argument) { return RaiseAsync(argument, CancellationToken.None); @@ -125,6 +137,11 @@ public Task RaiseAsync(T argument) /// public Task RaiseAsync(T argument, CancellationToken cancellationToken) { + lock (_argumentLock) + { + _argument = argument; + } + if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { return _core.Invoke(RevitContext.UiApplication, argument, cancellationToken); @@ -133,6 +150,23 @@ public Task RaiseAsync(T argument, CancellationToken cancellationToken) return _core.RaiseAsync(argument, cancellationToken); } + /// + /// + /// Each call queues a separate execution with the last argument passed to , + /// or the default value of before the first call. + /// The handler receives ; its result is discarded and an exception propagates to Revit. + /// + public override ExternalEventRequest Raise() + { + T argument; + lock (_argumentLock) + { + argument = _argument; + } + + return _core.Raise(argument); + } + /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs index 56c3e1a..22a055d 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs @@ -92,7 +92,17 @@ public AsyncRequestExternalEvent(Func /// Raises the external event asynchronously, instructing Revit to execute the handler within the Revit API context. ///
/// The representing the async operation being executed, containing the result of type . - /// This overload passes to . + /// + /// Revit will wait until it is ready to process the event and then it will execute its event handler. + /// Revit processes external events only when no other commands or edit modes are currently active in Revit, + /// which is the same policy like the one that applies to evoking external commands.

+ /// Each call executes the handler once, in the order of the calls. + /// The returned task completes with the outcome of that execution. + /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// When is specified and Revit is in API mode, + /// the handler is executed directly on the calling thread instead of being queued. + /// This overload passes to . + ///
public Task RaiseAsync() { return RaiseAsync(CancellationToken.None); @@ -127,6 +137,16 @@ public Task RaiseAsync(CancellationToken cancellationToken) return _core.RaiseAsync(default, cancellationToken); } + /// + /// + /// Each call queues a separate execution without waiting for completion. + /// The handler receives ; its result is discarded and an exception propagates to Revit. + /// + public override ExternalEventRequest Raise() + { + return _core.Raise(default); + } + /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) diff --git a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs index 7e298d7..347ef27 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs @@ -15,6 +15,8 @@ public class ExternalEvent : ExternalEventHandler, IExternalEvent { private readonly ExternalEventCore _core; private readonly ExternalEventOptions _options; + private readonly Lock _argumentLock = new(); + private T _argument = default!; /// /// Initializes a new instance of the class. @@ -85,6 +87,11 @@ public ExternalEvent(Action handler, ExternalEventOptions opti /// public ExternalEventRequest Raise(T argument) { + lock (_argumentLock) + { + _argument = argument; + } + if ((_options & ExternalEventOptions.AllowDirectInvocation) != 0 && RevitContext.IsRevitInApiMode) { _core.Invoke(RevitContext.UiApplication, argument); @@ -94,6 +101,22 @@ public ExternalEventRequest Raise(T argument) return _core.Raise(argument); } + /// + /// + /// Each call queues a separate execution with the last argument passed to , + /// or the default value of before the first call. + /// + public override ExternalEventRequest Raise() + { + T argument; + lock (_argumentLock) + { + argument = _argument; + } + + return _core.Raise(argument); + } + /// Callback invoked by Revit. Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) diff --git a/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventCoreTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventCoreTests.cs index 5203cb9..6f9a7e2 100644 --- a/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventCoreTests.cs +++ b/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventCoreTests.cs @@ -72,4 +72,86 @@ public async Task Execute_AcceptedRaiseForTheRest_ExecutesTheOldestRaise() await Assert.That(raiseCount).IsEqualTo(3); } } + + [Test] + [Arguments(nameof(ExternalEventRequest.Denied))] + [Arguments(nameof(ExternalEventRequest.TimedOut))] + public async Task Raise_RefusedRequest_ReturnsTheRefusalWithoutExecution(string refusalName) + { + // Arrange + var refusal = (ExternalEventRequest)Enum.Parse(typeof(ExternalEventRequest), refusalName); + var executionCount = 0; + var core = new AsyncExternalEventCore((_, argument, _) => argument + executionCount++, () => refusal); + + // Act + var request = core.Raise(42); + core.Execute(null!); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(request).IsEqualTo(refusal); + await Assert.That(executionCount).IsZero(); + } + } + + [Test] + public async Task Raise_BetweenAsyncRaises_PreservesTheirResultsAndExecutionOrder() + { + // Arrange + var receivedArguments = new List(); + var receivedTokens = new List(); + using var cancellationSource = new CancellationTokenSource(); + var core = new AsyncExternalEventCore((_, argument, token) => + { + receivedArguments.Add(argument); + receivedTokens.Add(token); + return argument; + }, () => ExternalEventRequest.Accepted); + + // Act + var firstTask = core.RaiseAsync(1, cancellationSource.Token); + var request = core.Raise(2); + var lastTask = core.RaiseAsync(3, cancellationSource.Token); + core.Execute(null!); + core.Execute(null!); + var isLastTaskCompletedAfterSecondExecution = lastTask.IsCompleted; + core.Execute(null!); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(request).IsEqualTo(ExternalEventRequest.Accepted); + await Assert.That(await firstTask).IsEqualTo(1); + await Assert.That(await lastTask).IsEqualTo(3); + await Assert.That(isLastTaskCompletedAfterSecondExecution).IsFalse(); + await Assert.That(receivedArguments).IsEquivalentTo([1, 2, 3], CollectionOrdering.Matching); + await Assert.That(receivedTokens).IsEquivalentTo([cancellationSource.Token, CancellationToken.None, cancellationSource.Token], CollectionOrdering.Matching); + } + } + + [Test] + public async Task Execute_RaiseHandlerThrows_PropagatesTheExceptionAndSchedulesTheFollowingRaise() + { + // Arrange + var failure = new InvalidOperationException("Handler failure"); + var raiseCount = 0; + var core = new AsyncExternalEventCore((_, argument, _) => argument == 1 ? throw failure : argument, () => + { + raiseCount++; + return ExternalEventRequest.Accepted; + }); + + core.Raise(1); + var followingTask = core.RaiseAsync(2, CancellationToken.None); + + // Act & Assert + var exception = await Assert.That(() => core.Execute(null!)).Throws(); + await Assert.That(exception).IsSameReferenceAs(failure); + await Assert.That(raiseCount).IsEqualTo(3); + await Assert.That(followingTask.IsCompleted).IsFalse(); + + core.Execute(null!); + await Assert.That(await followingTask).IsEqualTo(2); + } } diff --git a/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs index 5fac7d9..f86136f 100644 --- a/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs +++ b/tests/Nice3point.Revit.Toolkit.Tests/AsyncExternalEventTests.cs @@ -354,4 +354,92 @@ public async Task RaiseAsync_GenericTokenHandler_ReceivesTheArgumentAndToken(Can await Assert.That(receivedToken).IsEqualTo(cancellationSource.Token); } } + + [Test] + [Timeout(10_000)] + public async Task Raise_AllowDirectInvocation_QueuesEveryCallWithoutWaiting(CancellationToken cancellationToken) + { + // Arrange + var receivedTokens = new List(); + var execution = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + ExternalEventHandler externalEvent = new AsyncExternalEvent((_, token) => + { + receivedTokens.Add(token); + if (receivedTokens.Count == 2) + { + execution.SetResult(); + } + }, ExternalEventOptions.AllowDirectInvocation); + + // Act + var firstRequest = externalEvent.Raise(); + var secondRequest = externalEvent.Raise(); + var executionCountOnRaise = receivedTokens.Count; + await execution.Task.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(firstRequest).IsEqualTo(ExternalEventRequest.Accepted); + await Assert.That(secondRequest).IsEqualTo(ExternalEventRequest.Pending); + await Assert.That(executionCountOnRaise).IsZero(); + await Assert.That(receivedTokens).IsEquivalentTo([CancellationToken.None, CancellationToken.None], CollectionOrdering.Matching); + } + } + + [Test] + [Timeout(10_000)] + public async Task Raise_GenericMixedRaises_QueuesTheLastArgumentWithoutChangingAsyncCompletion(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var externalEvent = new AsyncExternalEvent(receivedArguments.Add); + + // Act + var firstRequest = externalEvent.Raise(); + var firstTask = externalEvent.RaiseAsync(1); + externalEvent.Raise(); + var secondTask = externalEvent.RaiseAsync(2); + externalEvent.Raise(); + var lastTask = externalEvent.RaiseAsync(3); + await Task.WhenAll(firstTask, secondTask, lastTask).WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(firstRequest).IsEqualTo(ExternalEventRequest.Accepted); + await Assert.That(receivedArguments).IsEquivalentTo([0, 1, 1, 2, 2, 3], CollectionOrdering.Matching); + } + } + + [Test] + [Timeout(10_000)] + public async Task Raise_GenericAfterDirectInvocation_QueuesTheLastArgument(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var execution = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var externalEvent = new AsyncExternalEvent(argument => + { + receivedArguments.Add(argument); + if (receivedArguments.Count == 2) + { + execution.SetResult(); + } + }, ExternalEventOptions.AllowDirectInvocation); + + await externalEvent.RaiseAsync(42).WaitAsync(cancellationToken); + + // Act + externalEvent.Raise(); + var executionCountOnRaise = receivedArguments.Count; + await execution.Task.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(executionCountOnRaise).IsEqualTo(1); + await Assert.That(receivedArguments).IsEquivalentTo([42, 42], CollectionOrdering.Matching); + } + } } diff --git a/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs index 0ef7b42..2533853 100644 --- a/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs +++ b/tests/Nice3point.Revit.Toolkit.Tests/AsyncRequestExternalEventTests.cs @@ -460,4 +460,103 @@ public async Task RaiseAsync_TokenCanceledBeforeTheEventCycle_CancelsTheTaskWith await Assert.That(followingResult).IsEqualTo(1); } } + + [Test] + [Timeout(10_000)] + public async Task Raise_AllowDirectInvocation_QueuesEveryCallWithoutWaiting(CancellationToken cancellationToken) + { + // Arrange + var receivedTokens = new List(); + var execution = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + ExternalEventHandler externalEvent = new AsyncRequestExternalEvent((_, token) => + { + receivedTokens.Add(token); + if (receivedTokens.Count == 2) + { + execution.SetResult(); + } + + return receivedTokens.Count; + }, ExternalEventOptions.AllowDirectInvocation); + + // Act + var firstRequest = externalEvent.Raise(); + var secondRequest = externalEvent.Raise(); + var executionCountOnRaise = receivedTokens.Count; + await execution.Task.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(firstRequest).IsEqualTo(Autodesk.Revit.UI.ExternalEventRequest.Accepted); + await Assert.That(secondRequest).IsEqualTo(Autodesk.Revit.UI.ExternalEventRequest.Pending); + await Assert.That(executionCountOnRaise).IsZero(); + await Assert.That(receivedTokens).IsEquivalentTo([CancellationToken.None, CancellationToken.None], CollectionOrdering.Matching); + } + } + + [Test] + [Timeout(10_000)] + public async Task Raise_GenericMixedRaises_QueuesTheLastArgumentWithoutChangingAsyncCompletion(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var externalEvent = new AsyncRequestExternalEvent(argument => + { + receivedArguments.Add(argument); + return argument; + }); + + // Act + var firstRequest = externalEvent.Raise(); + var firstTask = externalEvent.RaiseAsync(1); + externalEvent.Raise(); + var secondTask = externalEvent.RaiseAsync(2); + externalEvent.Raise(); + var lastTask = externalEvent.RaiseAsync(3); + await Task.WhenAll(firstTask, secondTask, lastTask).WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(firstRequest).IsEqualTo(Autodesk.Revit.UI.ExternalEventRequest.Accepted); + await Assert.That(await firstTask).IsEqualTo(1); + await Assert.That(await secondTask).IsEqualTo(2); + await Assert.That(await lastTask).IsEqualTo(3); + await Assert.That(receivedArguments).IsEquivalentTo([0, 1, 1, 2, 2, 3], CollectionOrdering.Matching); + } + } + + [Test] + [Timeout(10_000)] + public async Task Raise_GenericAfterDirectInvocation_QueuesTheLastArgument(CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var execution = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var externalEvent = new AsyncRequestExternalEvent(argument => + { + receivedArguments.Add(argument); + if (receivedArguments.Count == 2) + { + execution.SetResult(); + } + + return argument; + }, ExternalEventOptions.AllowDirectInvocation); + + await externalEvent.RaiseAsync(42).WaitAsync(cancellationToken); + + // Act + externalEvent.Raise(); + var executionCountOnRaise = receivedArguments.Count; + await execution.Task.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(executionCountOnRaise).IsEqualTo(1); + await Assert.That(receivedArguments).IsEquivalentTo([42, 42], CollectionOrdering.Matching); + } + } } diff --git a/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventTests.cs b/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventTests.cs index 623eb96..05a1a09 100644 --- a/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventTests.cs +++ b/tests/Nice3point.Revit.Toolkit.Tests/ExternalEventTests.cs @@ -192,4 +192,40 @@ public async Task Constructor_NullHandler_ThrowsArgumentNullException() // Act & Assert await Assert.That(() => new ExternalEvent((Action)null!)).Throws(); } + + [Test] + [Arguments(false)] + [Arguments(true)] + [Timeout(10_000)] + public async Task Raise_GenericWithoutArgument_QueuesTheLastArgument(bool allowDirectInvocation, CancellationToken cancellationToken) + { + // Arrange + var receivedArguments = new List(); + var execution = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var options = allowDirectInvocation ? ExternalEventOptions.AllowDirectInvocation : ExternalEventOptions.None; + int[] expectedArguments = allowDirectInvocation ? [42, 0, 42] : [0, 42, 42]; + var externalEvent = new ExternalEvent(argument => + { + receivedArguments.Add(argument); + if (receivedArguments.Count == 3) + { + execution.SetResult(); + } + }, options); + + // Act + var firstRequest = externalEvent.Raise(); + externalEvent.Raise(42); + externalEvent.Raise(); + var executionCountOnRaise = receivedArguments.Count; + await execution.Task.WaitAsync(cancellationToken); + + // Assert + using (Assert.Multiple()) + { + await Assert.That(firstRequest).IsEqualTo(ExternalEventRequest.Accepted); + await Assert.That(executionCountOnRaise).IsEqualTo(allowDirectInvocation ? 1 : 0); + await Assert.That(receivedArguments).IsEquivalentTo(expectedArguments, CollectionOrdering.Matching); + } + } } From a5414b4cafb16302872d6a2a896dbddb48ef5c39 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sat, 24 Oct 2026 12:48:21 +0300 Subject: [PATCH 08/14] [Docs] Clarify cancellation examples and XML formatting --- CHANGELOG.md | 38 +++++++++++++-- .../External/Events/AsyncExternalEvent.cs | 39 +++++++-------- .../External/Events/AsyncExternalEvent{T}.cs | 48 ++++++++----------- .../AsyncRequestExternalEvent{T,TResult}.cs | 46 +++++++----------- .../AsyncRequestExternalEvent{TResult}.cs | 36 ++++++-------- .../External/Events/ExternalEvent.cs | 27 +++++------ .../External/Events/ExternalEvent{T}.cs | 32 +++++-------- 7 files changed, 124 insertions(+), 142 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 592646e..af66928 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,42 @@ - A trailing `CancellationToken` parameter receives the token of `RaiseAsync()` and is excluded from the event arguments. Synchronous events pass `CancellationToken.None`. - Generated `RaiseAsync()` extension methods include an overload with a `CancellationToken`. -### Breaking Changes +Pass a cancellation token to the generated event to cancel a pending execution or stop the handler cooperatively: -- Custom implementations of the asynchronous event interfaces must implement the overload with a `CancellationToken`. -- Recompiling a handler with a trailing `CancellationToken` changes its generated argument types. For `void Run(CancellationToken token)`, replace `RunEvent.Raise(token)` with `RunEvent.Raise()`. `RunAsyncEvent.RaiseAsync(token)` passes the cancellation token to the handler. +```c# +public partial class MyViewModel +{ + [ExternalEvent] + private void DeleteWindows(UIApplication application, ICollection elementIds, CancellationToken cancellationToken) + { + var document = application.ActiveUIDocument.Document; + using var transaction = new Transaction(document, "Delete windows"); + transaction.Start(); + + foreach (var elementId in elementIds) + { + cancellationToken.ThrowIfCancellationRequested(); + document.Delete(elementId); + } + + transaction.Commit(); + } + + private async Task DeleteWindowsAsync(ICollection elementIds) + { + using var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + + try + { + await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationSource.Token); + } + catch (OperationCanceledException) + { + //The execution was canceled; uncommitted changes are rolled back + } + } +} +``` # 2027.0.2 diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs index 8e1d82b..a7ca0d1 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs @@ -5,8 +5,7 @@ namespace Nice3point.Revit.Toolkit.External; /// -/// An asynchronous external event expanding -/// with the ability to await completion of the handler execution. +/// An asynchronous external event expanding with the ability to await completion of the handler execution. /// [PublicAPI] public sealed class AsyncExternalEvent : ExternalEventHandler, IAsyncExternalEvent @@ -43,8 +42,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current . /// Thrown if is . @@ -53,8 +51,7 @@ public AsyncExternalEvent(Action handler) : this(handler, Externa } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current . /// The options to use to configure the external event. @@ -73,8 +70,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions op } /// - /// Initializes a new instance of the class - /// with access to the instance and the cancellation token of the raise. + /// Initializes a new instance of the class with access to the instance and the cancellation token of the raise. /// /// The execution logic that receives the current and the token passed to . /// Thrown if is . @@ -83,8 +79,7 @@ public AsyncExternalEvent(Action handler) : th } /// - /// Initializes a new instance of the class - /// with access to the instance and the cancellation token of the raise. + /// Initializes a new instance of the class with access to the instance and the cancellation token of the raise. /// /// The execution logic that receives the current and the token passed to . /// The options to use to configure the external event. @@ -108,13 +103,11 @@ public AsyncExternalEvent(Action handler, Exte /// The representing the async operation being executed. /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. - /// Revit processes external events only when no other commands or edit modes are currently active in Revit, - /// which is the same policy like the one that applies to evoking external commands.

+ /// Revit processes external events only when no other commands or edit modes are currently active in Revit, which is the same policy like the one that applies to evoking external commands.
/// Each call executes the handler once, in the order of the calls. /// The returned task completes with the outcome of that execution. - /// A call that Revit refuses with or fails its task with an without executing the handler.

- /// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// A call that Revit refuses with or fails its task with an without executing the handler.
+ /// When is specified and Revit is in API mode, the handler is executed directly on the calling thread instead of being queued. /// This overload passes to . ///
public Task RaiseAsync() @@ -129,16 +122,14 @@ public Task RaiseAsync() /// The representing the async operation being executed. /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. - /// Revit processes external events only when no other commands or edit modes are currently active in Revit, - /// which is the same policy like the one that applies to evoking external commands.

+ /// Revit processes external events only when no other commands or edit modes are currently active in Revit, which is the same policy like the one that applies to evoking external commands.
/// Each call executes the handler once, in the order of the calls. /// The returned task completes with the outcome of that execution. - /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// A call that Revit refuses with or fails its task with an without executing the handler.
/// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. /// A cancellation requested while the handler executes doesn't interrupt it. - /// An that the handler throws for the token cancels the task.

- /// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// An that the handler throws for the token cancels the task.
+ /// When is specified and Revit is in API mode, the handler is executed directly on the calling thread instead of being queued. /// The token is checked once, before the handler executes. ///
public Task RaiseAsync(CancellationToken cancellationToken) @@ -154,14 +145,16 @@ public Task RaiseAsync(CancellationToken cancellationToken) /// /// /// Each call queues a separate execution without waiting for completion. - /// The handler receives . An exception propagates to Revit. + /// The handler receives . + /// An exception propagates to Revit. /// public override ExternalEventRequest Raise() { return _core.Raise(default); } - /// Callback invoked by Revit. Not intended to be called in user code. + /// Callback invoked by Revit. + /// Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs index dbc211a..8573150 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs @@ -5,8 +5,7 @@ namespace Nice3point.Revit.Toolkit.External; /// -/// A generic asynchronous external event expanding -/// that accepts an argument of type . +/// A generic asynchronous external event expanding that accepts an argument of type . /// /// The type of the argument passed to the handler. [PublicAPI] @@ -46,8 +45,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions options) } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current and an argument of type . /// Thrown if is . @@ -56,8 +54,7 @@ public AsyncExternalEvent(Action handler) : this(handler, Exte } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current and an argument of type . /// The options to use to configure the external event. @@ -76,8 +73,7 @@ public AsyncExternalEvent(Action handler, ExternalEventOptions } /// - /// Initializes a new instance of the class - /// with access to the instance and the cancellation token of the raise. + /// Initializes a new instance of the class with access to the instance and the cancellation token of the raise. /// /// The execution logic that receives the current , an argument of type , and the token passed to . /// Thrown if is . @@ -86,8 +82,7 @@ public AsyncExternalEvent(Action handler) : } /// - /// Initializes a new instance of the class - /// with access to the instance and the cancellation token of the raise. + /// Initializes a new instance of the class with access to the instance and the cancellation token of the raise. /// /// The execution logic that receives the current , an argument of type , and the token passed to . /// The options to use to configure the external event. @@ -106,20 +101,17 @@ public AsyncExternalEvent(Action handler, E } /// - /// Raises the external event asynchronously with the specified argument, - /// instructing Revit to execute the handler within the Revit API context. + /// Raises the external event asynchronously with the specified argument, instructing Revit to execute the handler within the Revit API context. /// /// The argument to pass to the handler. /// The representing the async operation being executed. /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. - /// Revit processes external events only when no other commands or edit modes are currently active in Revit, - /// which is the same policy like the one that applies to evoking external commands.

+ /// Revit processes external events only when no other commands or edit modes are currently active in Revit, which is the same policy like the one that applies to evoking external commands.
/// Each call executes the handler once with its own argument, in the order of the calls. /// The returned task completes with the outcome of that execution. - /// A call that Revit refuses with or fails its task with an without executing the handler.

- /// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// A call that Revit refuses with or fails its task with an without executing the handler.
+ /// When is specified and Revit is in API mode, the handler is executed directly on the calling thread instead of being queued. /// This overload passes to . ///
public Task RaiseAsync(T argument) @@ -128,24 +120,21 @@ public Task RaiseAsync(T argument) } /// - /// Raises the external event asynchronously with the specified argument, - /// instructing Revit to execute the handler within the Revit API context. + /// Raises the external event asynchronously with the specified argument, instructing Revit to execute the handler within the Revit API context. /// /// The argument to pass to the handler. /// The token to monitor for cancellation requests. /// The representing the async operation being executed. /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. - /// Revit processes external events only when no other commands or edit modes are currently active in Revit, - /// which is the same policy like the one that applies to evoking external commands.

+ /// Revit processes external events only when no other commands or edit modes are currently active in Revit, which is the same policy like the one that applies to evoking external commands.
/// Each call executes the handler once with its own argument, in the order of the calls. /// The returned task completes with the outcome of that execution. - /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// A call that Revit refuses with or fails its task with an without executing the handler.
/// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. /// A cancellation requested while the handler executes doesn't interrupt it. - /// An that the handler throws for the token cancels the task.

- /// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// An that the handler throws for the token cancels the task.
+ /// When is specified and Revit is in API mode, the handler is executed directly on the calling thread instead of being queued. /// The token is checked once, before the handler executes. ///
public Task RaiseAsync(T argument, CancellationToken cancellationToken) @@ -165,9 +154,9 @@ public Task RaiseAsync(T argument, CancellationToken cancellationToken) /// /// - /// Each call queues a separate execution with the last argument passed to , - /// or the default value of before the first call. - /// The handler receives . An exception propagates to Revit. + /// Each call queues a separate execution with the last argument passed to , or the default value of before the first call. + /// The handler receives . + /// An exception propagates to Revit. /// public override ExternalEventRequest Raise() { @@ -180,7 +169,8 @@ public override ExternalEventRequest Raise() return _core.Raise(argument); } - /// Callback invoked by Revit. Not intended to be called in user code. + /// Callback invoked by Revit. + /// Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs index d875615..b83e1e6 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs @@ -5,9 +5,7 @@ namespace Nice3point.Revit.Toolkit.External; /// -/// A generic asynchronous external event expanding -/// that accepts an argument of type -/// and returns a result of type via . +/// A generic asynchronous external event expanding that accepts an argument of type and returns a result of type via . /// /// The type of the argument passed to the handler. /// The type of the result produced by the handler. @@ -43,8 +41,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalEventOptions } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current and an argument of type , returning a result of type . /// Thrown if is . @@ -53,8 +50,7 @@ public AsyncRequestExternalEvent(Func handler) : this } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current and an argument of type , returning a result of type . /// The options to use to configure the external event. @@ -68,8 +64,7 @@ public AsyncRequestExternalEvent(Func handler, Extern } /// - /// Initializes a new instance of the class - /// with access to the instance and the cancellation token of the raise. + /// Initializes a new instance of the class with access to the instance and the cancellation token of the raise. /// /// The execution logic that receives the current , an argument of type , and the token passed to , returning a result of type . /// Thrown if is . @@ -78,8 +73,7 @@ public AsyncRequestExternalEvent(Func - /// Initializes a new instance of the class - /// with access to the instance and the cancellation token of the raise. + /// Initializes a new instance of the class with access to the instance and the cancellation token of the raise. ///
/// The execution logic that receives the current , an argument of type , and the token passed to , returning a result of type . /// The options to use to configure the external event. @@ -93,20 +87,17 @@ public AsyncRequestExternalEvent(Func - /// Raises the external event asynchronously with the specified argument, - /// instructing Revit to execute the handler within the Revit API context. + /// Raises the external event asynchronously with the specified argument, instructing Revit to execute the handler within the Revit API context. ///
/// The argument to pass to the handler. /// The representing the async operation being executed, containing the result of type . /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. - /// Revit processes external events only when no other commands or edit modes are currently active in Revit, - /// which is the same policy like the one that applies to evoking external commands.

+ /// Revit processes external events only when no other commands or edit modes are currently active in Revit, which is the same policy like the one that applies to evoking external commands.
/// Each call executes the handler once with its own argument, in the order of the calls. /// The returned task completes with the outcome of that execution. - /// A call that Revit refuses with or fails its task with an without executing the handler.

- /// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// A call that Revit refuses with or fails its task with an without executing the handler.
+ /// When is specified and Revit is in API mode, the handler is executed directly on the calling thread instead of being queued. /// This overload passes to . ///
public Task RaiseAsync(T argument) @@ -115,24 +106,21 @@ public Task RaiseAsync(T argument) } /// - /// Raises the external event asynchronously with the specified argument, - /// instructing Revit to execute the handler within the Revit API context. + /// Raises the external event asynchronously with the specified argument, instructing Revit to execute the handler within the Revit API context. /// /// The argument to pass to the handler. /// The token to monitor for cancellation requests. /// The representing the async operation being executed, containing the result of type . /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. - /// Revit processes external events only when no other commands or edit modes are currently active in Revit, - /// which is the same policy like the one that applies to evoking external commands.

+ /// Revit processes external events only when no other commands or edit modes are currently active in Revit, which is the same policy like the one that applies to evoking external commands.
/// Each call executes the handler once with its own argument, in the order of the calls. /// The returned task completes with the outcome of that execution. - /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// A call that Revit refuses with or fails its task with an without executing the handler.
/// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. /// A cancellation requested while the handler executes doesn't interrupt it. - /// An that the handler throws for the token cancels the task.

- /// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// An that the handler throws for the token cancels the task.
+ /// When is specified and Revit is in API mode, the handler is executed directly on the calling thread instead of being queued. /// The token is checked once, before the handler executes. ///
public Task RaiseAsync(T argument, CancellationToken cancellationToken) @@ -152,8 +140,7 @@ public Task RaiseAsync(T argument, CancellationToken cancellationToken) /// /// - /// Each call queues a separate execution with the last argument passed to , - /// or the default value of before the first call. + /// Each call queues a separate execution with the last argument passed to , or the default value of before the first call. /// The handler receives ; its result is discarded and an exception propagates to Revit. /// public override ExternalEventRequest Raise() @@ -167,7 +154,8 @@ public override ExternalEventRequest Raise() return _core.Raise(argument); } - /// Callback invoked by Revit. Not intended to be called in user code. + /// Callback invoked by Revit. + /// Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs index 22a055d..91f691e 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs @@ -5,8 +5,7 @@ namespace Nice3point.Revit.Toolkit.External; /// -/// A generic asynchronous external event expanding -/// to return a result of type via . +/// A generic asynchronous external event expanding to return a result of type via . /// /// The type of the result produced by the handler. [PublicAPI] @@ -39,8 +38,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalEventOptions opt } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current and returns a result of type . /// Thrown if is . @@ -49,8 +47,7 @@ public AsyncRequestExternalEvent(Func handler) : this(ha } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current and returns a result of type . /// The options to use to configure the external event. @@ -64,8 +61,7 @@ public AsyncRequestExternalEvent(Func handler, ExternalE } /// - /// Initializes a new instance of the class - /// with access to the instance and the cancellation token of the raise. + /// Initializes a new instance of the class with access to the instance and the cancellation token of the raise. /// /// The execution logic that receives the current and the token passed to , returning a result of type . /// Thrown if is . @@ -74,8 +70,7 @@ public AsyncRequestExternalEvent(Func } /// - /// Initializes a new instance of the class - /// with access to the instance and the cancellation token of the raise. + /// Initializes a new instance of the class with access to the instance and the cancellation token of the raise. /// /// The execution logic that receives the current and the token passed to , returning a result of type . /// The options to use to configure the external event. @@ -94,13 +89,11 @@ public AsyncRequestExternalEvent(Func /// The representing the async operation being executed, containing the result of type . /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. - /// Revit processes external events only when no other commands or edit modes are currently active in Revit, - /// which is the same policy like the one that applies to evoking external commands.

+ /// Revit processes external events only when no other commands or edit modes are currently active in Revit, which is the same policy like the one that applies to evoking external commands.
/// Each call executes the handler once, in the order of the calls. /// The returned task completes with the outcome of that execution. - /// A call that Revit refuses with or fails its task with an without executing the handler.

- /// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// A call that Revit refuses with or fails its task with an without executing the handler.
+ /// When is specified and Revit is in API mode, the handler is executed directly on the calling thread instead of being queued. /// This overload passes to . ///
public Task RaiseAsync() @@ -115,16 +108,14 @@ public Task RaiseAsync() /// The representing the async operation being executed, containing the result of type . /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. - /// Revit processes external events only when no other commands or edit modes are currently active in Revit, - /// which is the same policy like the one that applies to evoking external commands.

+ /// Revit processes external events only when no other commands or edit modes are currently active in Revit, which is the same policy like the one that applies to evoking external commands.
/// Each call executes the handler once, in the order of the calls. /// The returned task completes with the outcome of that execution. - /// A call that Revit refuses with or fails its task with an without executing the handler.

+ /// A call that Revit refuses with or fails its task with an without executing the handler.
/// A cancellation requested before Revit executes the handler cancels the task at once without executing the handler. /// A cancellation requested while the handler executes doesn't interrupt it. - /// An that the handler throws for the token cancels the task.

- /// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// An that the handler throws for the token cancels the task.
+ /// When is specified and Revit is in API mode, the handler is executed directly on the calling thread instead of being queued. /// The token is checked once, before the handler executes. ///
public Task RaiseAsync(CancellationToken cancellationToken) @@ -147,7 +138,8 @@ public override ExternalEventRequest Raise() return _core.Raise(default); } - /// Callback invoked by Revit. Not intended to be called in user code. + /// Callback invoked by Revit. + /// Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent.cs index 836c96f..1c4ff96 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent.cs @@ -5,10 +5,9 @@ namespace Nice3point.Revit.Toolkit.External; /// -/// An external event whose sole purpose is to relay its functionality to other -/// objects by invoking delegates. The default behavior queues the handler -/// via the Revit external event mechanism. This type allows you to accept -/// a parameter in the callback overload. +/// An external event whose sole purpose is to relay its functionality to other objects by invoking delegates. +/// The default behavior queues the handler via the Revit external event mechanism. +/// This type allows you to accept a parameter in the callback overload. /// [PublicAPI] public class ExternalEvent : ExternalEventHandler, IExternalEvent @@ -44,8 +43,7 @@ public ExternalEvent(Action handler, ExternalEventOptions options) } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current . /// Thrown if is . @@ -57,8 +55,7 @@ public ExternalEvent(Action handler) } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current . /// The options to use to configure the external event. @@ -75,16 +72,13 @@ public ExternalEvent(Action handler, ExternalEventOptions options /// Raises (signals) the external event, instructing Revit to execute the handler within the Revit API context. /// /// - /// The result of event raising request. If the request is 'Accepted', - /// the event would be added to the event queue and its handler will - /// be executed in the next event-processing cycle. + /// The result of event raising request. + /// If the request is 'Accepted', the event would be added to the event queue and its handler will be executed in the next event-processing cycle. /// /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. - /// Revit processes external events only when no other commands or edit modes are currently active in Revit, - /// which is the same policy like the one that applies to evoking external commands.

- /// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// Revit processes external events only when no other commands or edit modes are currently active in Revit, which is the same policy like the one that applies to evoking external commands.
+ /// When is specified and Revit is in API mode, the handler is executed directly on the calling thread instead of being queued. ///
public override ExternalEventRequest Raise() { @@ -97,7 +91,8 @@ public override ExternalEventRequest Raise() return base.Raise(); } - /// Callback invoked by Revit. Not intended to be called in user code. + /// Callback invoked by Revit. + /// Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs index 347ef27..68b48c5 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent{T}.cs @@ -5,9 +5,7 @@ namespace Nice3point.Revit.Toolkit.External; /// -/// A generic synchronous external event expanding -/// that accepts an argument of type -/// and relays its functionality by invoking delegates within the Revit API context. +/// A generic synchronous external event expanding that accepts an argument of type and relays its functionality by invoking delegates within the Revit API context. /// /// The type of the argument passed to the handler. [PublicAPI] @@ -42,8 +40,7 @@ public ExternalEvent(Action handler, ExternalEventOptions options) } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current and an argument of type . /// Thrown if is . @@ -52,8 +49,7 @@ public ExternalEvent(Action handler) : this(handler, ExternalE } /// - /// Initializes a new instance of the class - /// with access to the instance. + /// Initializes a new instance of the class with access to the instance. /// /// The execution logic that receives the current and an argument of type . /// The options to use to configure the external event. @@ -67,23 +63,19 @@ public ExternalEvent(Action handler, ExternalEventOptions opti } /// - /// Raises (signals) the external event with the specified argument, - /// instructing Revit to execute the handler within the Revit API context. + /// Raises (signals) the external event with the specified argument, instructing Revit to execute the handler within the Revit API context. /// /// The argument to pass to the handler. /// - /// The result of event raising request. If the request is 'Accepted', - /// the event would be added to the event queue and its handler will - /// be executed in the next event-processing cycle. + /// The result of event raising request. + /// If the request is 'Accepted', the event would be added to the event queue and its handler will be executed in the next event-processing cycle. /// /// /// Revit will wait until it is ready to process the event and then it will execute its event handler. - /// Revit processes external events only when no other commands or edit modes are currently active in Revit, - /// which is the same policy like the one that applies to evoking external commands.

+ /// Revit processes external events only when no other commands or edit modes are currently active in Revit, which is the same policy like the one that applies to evoking external commands.
/// Each call executes the handler once with its own argument, in the order of the calls. - /// A call that returns or never executes the handler.

- /// When is specified and Revit is in API mode, - /// the handler is executed directly on the calling thread instead of being queued. + /// A call that returns or never executes the handler.
+ /// When is specified and Revit is in API mode, the handler is executed directly on the calling thread instead of being queued. ///
public ExternalEventRequest Raise(T argument) { @@ -103,8 +95,7 @@ public ExternalEventRequest Raise(T argument) /// /// - /// Each call queues a separate execution with the last argument passed to , - /// or the default value of before the first call. + /// Each call queues a separate execution with the last argument passed to , or the default value of before the first call. /// public override ExternalEventRequest Raise() { @@ -117,7 +108,8 @@ public override ExternalEventRequest Raise() return _core.Raise(argument); } - /// Callback invoked by Revit. Not intended to be called in user code. + /// Callback invoked by Revit. + /// Not intended to be called in user code. [EditorBrowsable(EditorBrowsableState.Never)] public override void Execute(UIApplication uiApplication) { From 74ade9bfe82a6c04f466c210a8546574eba81fe6 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sat, 24 Oct 2026 12:51:59 +0300 Subject: [PATCH 09/14] [Analyzers] Record diagnostics shipped in 2027.0.1 --- .../AnalyzerReleases.Shipped.md | 9 +++++++++ .../AnalyzerReleases.Unshipped.md | 7 ------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/source/Nice3point.Revit.Toolkit.Analyzers/AnalyzerReleases.Shipped.md b/source/Nice3point.Revit.Toolkit.Analyzers/AnalyzerReleases.Shipped.md index cb41149..ae466d5 100644 --- a/source/Nice3point.Revit.Toolkit.Analyzers/AnalyzerReleases.Shipped.md +++ b/source/Nice3point.Revit.Toolkit.Analyzers/AnalyzerReleases.Shipped.md @@ -1,6 +1,15 @@ ; Shipped analyzer releases ; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md +## Release 2027.0.1 + +### New Rules + + Rule ID | Category | Severity | Notes +-----------|------------------------|----------|----------------------------------------------------------------------------- + RVTTK0006 | ExternalEventGenerator | Error | Reports signatures that cannot be represented by generated external events. + RVTTK0007 | ExternalEventGenerator | Error | Reports generated member names that conflict with another declaration. + ## Release 2027.0.0 ### New Rules diff --git a/source/Nice3point.Revit.Toolkit.Analyzers/AnalyzerReleases.Unshipped.md b/source/Nice3point.Revit.Toolkit.Analyzers/AnalyzerReleases.Unshipped.md index 4675920..f2b7fad 100644 --- a/source/Nice3point.Revit.Toolkit.Analyzers/AnalyzerReleases.Unshipped.md +++ b/source/Nice3point.Revit.Toolkit.Analyzers/AnalyzerReleases.Unshipped.md @@ -1,9 +1,2 @@ ; Unshipped analyzer release ; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md - -### New Rules - - Rule ID | Category | Severity | Notes ------------|------------------------|----------|----------------------------------------------------------------------------- - RVTTK0006 | ExternalEventGenerator | Error | Reports signatures that cannot be represented by generated external events. - RVTTK0007 | ExternalEventGenerator | Error | Reports generated member names that conflict with another declaration. From ecae55c37e971ca6851a27cc369eab09a5cefed4 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sat, 24 Oct 2026 12:56:12 +0300 Subject: [PATCH 10/14] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af66928..aca36c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ Pass a cancellation token to the generated event to cancel a pending execution or stop the handler cooperatively: ```c# -public partial class MyViewModel +public partial class MyRevitContext { [ExternalEvent] private void DeleteWindows(UIApplication application, ICollection elementIds, CancellationToken cancellationToken) From cf5168afebdf37bf3c7872cbcf7b0a47532e2729 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sat, 24 Oct 2026 10:13:53 +0300 Subject: [PATCH 11/14] [Docs] Move the compiler tooling fixes to the 2027.0.1 release notes --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aca36c4..616151c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,9 +58,9 @@ Performance optimizations for Revit 2027: # 2027.0.1 -- `AsyncExternalEvent.RaiseAsync()` with `ExternalEventOptions.AllowDirectInvocation` returns a faulted task when the handler throws inside the Revit API context. The exception was previously lost and the task completed successfully. +### External Events -# 2027.0.0 +- `AsyncExternalEvent.RaiseAsync()` with `ExternalEventOptions.AllowDirectInvocation` returns a faulted task when the handler throws inside the Revit API context. The exception was previously lost and the task completed successfully. ### Compiler Tooling Fixes @@ -76,6 +76,8 @@ Performance optimizations for Revit 2027: - Generated event properties use `System.Threading.Lock` when the consumer supports C# 13 and provides the type; other targets use an object lock. - Unsupported signatures report `RVTTK0006`; conflicting generated member names report `RVTTK0007`. +# 2027.0.0 + ### External Events New family of external event types replacing legacy `ActionEventHandler`, `AsyncEventHandler`: From af43a94f73e727e0fa633139b674cb7634e0d027 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sat, 24 Oct 2026 10:13:57 +0300 Subject: [PATCH 12/14] [Docs] Stop the generator example handler on the token inside the loop --- README.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2bdb3a8..35876aa 100644 --- a/README.md +++ b/README.md @@ -648,23 +648,28 @@ The generator excludes it from the event arguments: ```c# [ExternalEvent] -private void DeleteWindow(UIApplication application, ElementId elementId, CancellationToken cancellationToken) +private void DeleteWindows(UIApplication application, ICollection elementIds, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); var document = application.ActiveUIDocument.Document; - using var transaction = new Transaction(document, "Delete window"); + using var transaction = new Transaction(document, "Delete windows"); transaction.Start(); - document.Delete(elementId); + + foreach (var elementId in elementIds) + { + cancellationToken.ThrowIfCancellationRequested(); + document.Delete(elementId); + } + transaction.Commit(); } // Generates: -// IExternalEvent DeleteWindowEvent -// IAsyncExternalEvent DeleteWindowAsyncEvent +// IExternalEvent> DeleteWindowsEvent +// IAsyncExternalEvent> DeleteWindowsAsyncEvent ``` -Call `await DeleteWindowAsyncEvent.RaiseAsync(elementId, cancellationToken)` to pass the token to the handler. -`DeleteWindowEvent.Raise(elementId)` passes `CancellationToken.None`. +Call `await DeleteWindowsAsyncEvent.RaiseAsync(elementIds, cancellationToken)` to pass the token to the handler. +`DeleteWindowsEvent.Raise(elementIds)` passes `CancellationToken.None`. A `CancellationToken` in any other position is a regular argument. See [Cancellation](#cancellation) for cancellation behavior and exception handling. From 2464bd7881dad1f3be962e5977ab85e48ff16103 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sat, 24 Oct 2026 10:13:57 +0300 Subject: [PATCH 13/14] [Docs] Keep the parameterless raise of asynchronous events out of the guides --- CHANGELOG.md | 3 +-- README.md | 5 ----- .../External/Events/AsyncExternalEvent.cs | 2 +- .../Events/AsyncExternalEventCore{TArgument,TResult}.cs | 2 +- .../External/Events/AsyncExternalEvent{T}.cs | 2 +- .../External/Events/AsyncRequestExternalEvent{T,TResult}.cs | 3 ++- .../External/Events/AsyncRequestExternalEvent{TResult}.cs | 3 ++- 7 files changed, 8 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 616151c..0239b86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,7 @@ ### External Events - `ExternalEvent` and the asynchronous external events execute each queued raise with its own argument. Each `RaiseAsync()` task completes with the result or exception of its handler. Previously, concurrent raises overwrote the argument and left earlier tasks incomplete. -- `Raise()` on asynchronous events queues the handler without waiting for completion. On events with an argument, it uses the last supplied argument, or `default(T)` before the first call. Handler exceptions propagate to Revit. -- Requests refused with `ExternalEventRequest.Denied` or `ExternalEventRequest.TimedOut` skip the handler. `Raise()` returns the refusal; `RaiseAsync()` returns a task faulted with `InvalidOperationException`. +- Requests refused with `ExternalEventRequest.Denied` or `ExternalEventRequest.TimedOut` skip the handler. `Raise(argument)` returns the refusal; `RaiseAsync()` returns a task faulted with `InvalidOperationException`. - New `RaiseAsync()` overloads accept a `CancellationToken` to cancel a pending execution. Token-aware constructors pass the token to the handler for cooperative cancellation during execution. ### ExternalEvent Source Generator diff --git a/README.md b/README.md index 35876aa..79241ce 100644 --- a/README.md +++ b/README.md @@ -210,11 +210,6 @@ A raise that Revit refuses with `Denied` or `TimedOut` never runs. `Raise(argument)` returns that result to the caller. The `RaiseAsync()` task fails with an `InvalidOperationException`. -The asynchronous event classes also expose `Raise()` to queue the handler without waiting for completion. -For events with an argument, this overload uses the last supplied argument, or `default(T)` before the first call. -It always queues the handler, including when `AllowDirectInvocation` is enabled, and passes `CancellationToken.None`. -Handler exceptions propagate to Revit; `RaiseAsync()` reports them through its task. - #### ExternalEvent A synchronous external event that queues the handler via the Revit external event mechanism. diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs index a7ca0d1..ae6867c 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs @@ -146,7 +146,7 @@ public Task RaiseAsync(CancellationToken cancellationToken) /// /// Each call queues a separate execution without waiting for completion. /// The handler receives . - /// An exception propagates to Revit. + /// An exception of the handler doesn't reach the caller. /// public override ExternalEventRequest Raise() { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs index 6be2cfb..2426799 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEventCore{TArgument,TResult}.cs @@ -28,7 +28,7 @@ internal sealed class AsyncExternalEventCore(Func /// The argument to pass to the handler. /// The result of the request to raise the Revit external event. - /// An exception of the handler propagates to Revit. + /// An exception of the handler propagates from . public ExternalEventRequest Raise(TArgument argument) { return Enqueue(new Operation(argument)); diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs index 8573150..6ce93ee 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs @@ -156,7 +156,7 @@ public Task RaiseAsync(T argument, CancellationToken cancellationToken) /// /// Each call queues a separate execution with the last argument passed to , or the default value of before the first call. /// The handler receives . - /// An exception propagates to Revit. + /// An exception of the handler doesn't reach the caller. /// public override ExternalEventRequest Raise() { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs index b83e1e6..b75da8e 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs @@ -141,7 +141,8 @@ public Task RaiseAsync(T argument, CancellationToken cancellationToken) /// /// /// Each call queues a separate execution with the last argument passed to , or the default value of before the first call. - /// The handler receives ; its result is discarded and an exception propagates to Revit. + /// The handler receives ; its result is discarded. + /// An exception of the handler doesn't reach the caller. /// public override ExternalEventRequest Raise() { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs index 91f691e..3334a32 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs @@ -131,7 +131,8 @@ public Task RaiseAsync(CancellationToken cancellationToken) /// /// /// Each call queues a separate execution without waiting for completion. - /// The handler receives ; its result is discarded and an exception propagates to Revit. + /// The handler receives ; its result is discarded. + /// An exception of the handler doesn't reach the caller. /// public override ExternalEventRequest Raise() { From 4c08f89fa039f3a7972fe933325ebc5b169609f2 Mon Sep 17 00:00:00 2001 From: Nice3point Date: Sat, 24 Oct 2026 10:15:09 +0300 Subject: [PATCH 14/14] [Docs] Replace the hiding verbs in the external event documentation --- .../External/Events/AsyncExternalEvent.cs | 2 +- .../External/Events/AsyncExternalEvent{T}.cs | 2 +- .../External/Events/AsyncRequestExternalEvent{T,TResult}.cs | 2 +- .../External/Events/AsyncRequestExternalEvent{TResult}.cs | 2 +- .../Nice3point.Revit.Toolkit/External/Events/ExternalEvent.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs index ae6867c..5ea18a3 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent.cs @@ -146,7 +146,7 @@ public Task RaiseAsync(CancellationToken cancellationToken) /// /// Each call queues a separate execution without waiting for completion. /// The handler receives . - /// An exception of the handler doesn't reach the caller. + /// Revit catches an exception of the handler. /// public override ExternalEventRequest Raise() { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs index 6ce93ee..8aef493 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncExternalEvent{T}.cs @@ -156,7 +156,7 @@ public Task RaiseAsync(T argument, CancellationToken cancellationToken) /// /// Each call queues a separate execution with the last argument passed to , or the default value of before the first call. /// The handler receives . - /// An exception of the handler doesn't reach the caller. + /// Revit catches an exception of the handler. /// public override ExternalEventRequest Raise() { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs index b75da8e..e6c2484 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{T,TResult}.cs @@ -142,7 +142,7 @@ public Task RaiseAsync(T argument, CancellationToken cancellationToken) /// /// Each call queues a separate execution with the last argument passed to , or the default value of before the first call. /// The handler receives ; its result is discarded. - /// An exception of the handler doesn't reach the caller. + /// Revit catches an exception of the handler. /// public override ExternalEventRequest Raise() { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs index 3334a32..356d89c 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/AsyncRequestExternalEvent{TResult}.cs @@ -132,7 +132,7 @@ public Task RaiseAsync(CancellationToken cancellationToken) /// /// Each call queues a separate execution without waiting for completion. /// The handler receives ; its result is discarded. - /// An exception of the handler doesn't reach the caller. + /// Revit catches an exception of the handler. /// public override ExternalEventRequest Raise() { diff --git a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent.cs b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent.cs index 1c4ff96..a8f82d6 100644 --- a/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent.cs +++ b/source/Nice3point.Revit.Toolkit/External/Events/ExternalEvent.cs @@ -7,7 +7,7 @@ namespace Nice3point.Revit.Toolkit.External; /// /// An external event whose sole purpose is to relay its functionality to other objects by invoking delegates. /// The default behavior queues the handler via the Revit external event mechanism. -/// This type allows you to accept a parameter in the callback overload. +/// The constructor passes the current to the handler. /// [PublicAPI] public class ExternalEvent : ExternalEventHandler, IExternalEvent