mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-06-12 03:36:27 +00:00
Improved voice recording shortcut labels (#800)
Some checks failed
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
Some checks failed
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
This commit is contained in:
parent
e9da7d31df
commit
1c2d243c1f
@ -3,7 +3,7 @@
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudIcon Icon="@this.Icon" Color="@this.IconColor"/>
|
||||
<MudText Typo="Typo.body1" Class="flex-grow-1">
|
||||
@if (string.IsNullOrWhiteSpace(this.Shortcut()))
|
||||
@if (string.IsNullOrWhiteSpace(this.Data.Value()))
|
||||
{
|
||||
@T("No shortcut configured")
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Tools.Rust;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@ -19,22 +18,10 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The current shortcut value.
|
||||
/// The shortcut binding data.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<string> Shortcut { get; set; } = () => string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// An action which is called when the shortcut was changed.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Action<string> ShortcutUpdate { get; set; } = _ => { };
|
||||
|
||||
/// <summary>
|
||||
/// The name/identifier of the shortcut (used for conflict detection and registration).
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Shortcut ShortcutId { get; init; }
|
||||
public ConfigurationShortcutData Data { get; set; } = ConfigurationShortcutData.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The icon to display.
|
||||
@ -60,10 +47,18 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
||||
|
||||
private string GetDisplayShortcut()
|
||||
{
|
||||
var shortcut = this.Shortcut();
|
||||
var shortcut = this.Data.Value();
|
||||
if (string.IsNullOrWhiteSpace(shortcut))
|
||||
return string.Empty;
|
||||
|
||||
var shortcutDisplayName = this.Data.DisplayName();
|
||||
var shortcutDisplaySource = this.Data.DisplaySource();
|
||||
if (!string.IsNullOrWhiteSpace(shortcutDisplayName)
|
||||
&& string.Equals(shortcutDisplaySource, shortcut, StringComparison.Ordinal))
|
||||
{
|
||||
return shortcutDisplayName;
|
||||
}
|
||||
|
||||
// Convert internal format to display format:
|
||||
return shortcut
|
||||
.Replace("CmdOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl")
|
||||
@ -80,8 +75,8 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
||||
{
|
||||
var dialogParameters = new DialogParameters<ShortcutDialog>
|
||||
{
|
||||
{ x => x.InitialShortcut, this.Shortcut() },
|
||||
{ x => x.ShortcutId, this.ShortcutId },
|
||||
{ x => x.InitialShortcut, this.Data.Value() },
|
||||
{ x => x.ShortcutId, this.Data.Id },
|
||||
};
|
||||
|
||||
var dialogReference = await this.DialogService.ShowAsync<ShortcutDialog>(
|
||||
@ -93,9 +88,17 @@ public partial class ConfigurationShortcut : ConfigurationBaseCore
|
||||
if (dialogResult is null || dialogResult.Canceled)
|
||||
return;
|
||||
|
||||
if (dialogResult.Data is string newShortcut)
|
||||
if (dialogResult.Data is ShortcutDialogResult shortcutResult)
|
||||
{
|
||||
this.ShortcutUpdate(newShortcut);
|
||||
this.Data.ValueUpdate(shortcutResult.Shortcut);
|
||||
this.Data.DisplayUpdate(shortcutResult.DisplayName, shortcutResult.DisplaySource);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
else if (dialogResult.Data is string newShortcut)
|
||||
{
|
||||
this.Data.ValueUpdate(newShortcut);
|
||||
this.Data.DisplayUpdate(string.Empty, string.Empty);
|
||||
await this.SettingsManager.StoreSettings();
|
||||
await this.InformAboutChange();
|
||||
}
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
using AIStudio.Tools.Rust;
|
||||
|
||||
namespace AIStudio.Components;
|
||||
|
||||
/// <summary>
|
||||
/// UI binding data for a configurable keyboard shortcut.
|
||||
/// </summary>
|
||||
public sealed class ConfigurationShortcutData
|
||||
{
|
||||
/// <summary>
|
||||
/// Empty shortcut binding.
|
||||
/// </summary>
|
||||
public static ConfigurationShortcutData Empty { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The name/identifier of the shortcut, used for conflict detection and registration.
|
||||
/// </summary>
|
||||
public Shortcut Id { get; init; } = Shortcut.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The current shortcut value.
|
||||
/// </summary>
|
||||
public Func<string> Value { get; init; } = () => string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// An action that is called when the shortcut was changed.
|
||||
/// </summary>
|
||||
public Action<string> ValueUpdate { get; init; } = _ => { };
|
||||
|
||||
/// <summary>
|
||||
/// The optional user-facing shortcut label.
|
||||
/// </summary>
|
||||
public Func<string> DisplayName { get; init; } = () => string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The canonical shortcut value the optional user-facing label belongs to.
|
||||
/// </summary>
|
||||
public Func<string> DisplaySource { get; init; } = () => string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// An action that is called when the user-facing shortcut label was changed.
|
||||
/// </summary>
|
||||
public Action<string, string> DisplayUpdate { get; init; } = (_, _) => { };
|
||||
}
|
||||
@ -37,7 +37,7 @@
|
||||
@if (PreviewFeatures.PRE_SPEECH_TO_TEXT_2026.IsEnabled(this.SettingsManager))
|
||||
{
|
||||
<ConfigurationSelect OptionDescription="@T("Select a transcription provider")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UseTranscriptionProvider)" Data="@this.GetFilteredTranscriptionProviders()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UseTranscriptionProvider = selectedValue)" OptionHelp="@T("Select a transcription provider for transcribing your voice. Without a selected provider, dictation and transcription features will be disabled.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UseTranscriptionProvider, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationShortcut ShortcutId="Shortcut.VOICE_RECORDING_TOGGLE" OptionDescription="@T("Voice recording shortcut")" Shortcut="@(() => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecording)" ShortcutUpdate="@(shortcut => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecording = shortcut)" OptionHelp="@T("The global keyboard shortcut for toggling voice recording. This shortcut works system-wide, even when the app is not focused.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ShortcutVoiceRecording, out var meta) && meta.IsLocked"/>
|
||||
<ConfigurationShortcut Data="@this.VoiceRecordingShortcut" OptionDescription="@T("Voice recording shortcut")" OptionHelp="@T("The global keyboard shortcut for toggling voice recording. This shortcut works system-wide, even when the app is not focused.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ShortcutVoiceRecording, out var meta) && meta.IsLocked"/>
|
||||
}
|
||||
|
||||
@if (this.SettingsManager.ConfigurationData.App.ShowAdminSettings)
|
||||
|
||||
@ -1,11 +1,22 @@
|
||||
using AIStudio.Provider;
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.Rust;
|
||||
|
||||
namespace AIStudio.Components.Settings;
|
||||
|
||||
public partial class SettingsPanelApp : SettingsPanelBase
|
||||
{
|
||||
private ConfigurationShortcutData VoiceRecordingShortcut => new()
|
||||
{
|
||||
Id = Shortcut.VOICE_RECORDING_TOGGLE,
|
||||
Value = () => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecording,
|
||||
ValueUpdate = shortcut => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecording = shortcut,
|
||||
DisplayName = () => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecordingDisplayName,
|
||||
DisplaySource = () => this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecordingDisplaySource,
|
||||
DisplayUpdate = this.UpdateShortcutVoiceRecordingDisplay,
|
||||
};
|
||||
|
||||
private async Task GenerateEncryptionSecret()
|
||||
{
|
||||
var secret = EnterpriseEncryption.GenerateSecret();
|
||||
@ -93,6 +104,12 @@ public partial class SettingsPanelApp : SettingsPanelBase
|
||||
this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedFeatures;
|
||||
}
|
||||
|
||||
private void UpdateShortcutVoiceRecordingDisplay(string displayName, string displaySource)
|
||||
{
|
||||
this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecordingDisplayName = displayName;
|
||||
this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecordingDisplaySource = displaySource;
|
||||
}
|
||||
|
||||
private async Task UpdateLangBehaviour(LangBehavior behavior)
|
||||
{
|
||||
this.SettingsManager.ConfigurationData.App.LanguageBehavior = behavior;
|
||||
|
||||
@ -34,6 +34,7 @@ public partial class ShortcutDialog : MSGComponentBase
|
||||
|
||||
private string currentShortcut = string.Empty;
|
||||
private string originalShortcut = string.Empty;
|
||||
private string currentDisplayName = string.Empty;
|
||||
private string validationMessage = string.Empty;
|
||||
private Severity validationSeverity = Severity.Info;
|
||||
private bool hasValidationError;
|
||||
@ -115,6 +116,7 @@ public partial class ShortcutDialog : MSGComponentBase
|
||||
{
|
||||
this.UpdateModifiers(e);
|
||||
this.currentKey = null;
|
||||
this.currentDisplayName = string.Empty;
|
||||
this.UpdateShortcutString();
|
||||
return;
|
||||
}
|
||||
@ -123,10 +125,12 @@ public partial class ShortcutDialog : MSGComponentBase
|
||||
|
||||
// Get the key:
|
||||
this.currentKey = TranslateKeyCode(e.Code);
|
||||
this.currentDisplayName = this.BuildDisplayShortcut(e.Key);
|
||||
|
||||
// Validate: must have at least one modifier + a key
|
||||
if (!this.hasCtrl && !this.hasShift && !this.hasAlt && !this.hasMeta)
|
||||
{
|
||||
this.currentDisplayName = string.Empty;
|
||||
this.validationMessage = T("Please include at least one modifier key (Ctrl, Shift, Alt, or Cmd).");
|
||||
this.validationSeverity = Severity.Warning;
|
||||
this.hasValidationError = true;
|
||||
@ -216,6 +220,9 @@ public partial class ShortcutDialog : MSGComponentBase
|
||||
|
||||
private string GetDisplayShortcut()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(this.currentDisplayName))
|
||||
return this.currentDisplayName;
|
||||
|
||||
// Convert internal format to display format:
|
||||
return this.currentShortcut
|
||||
.Replace("CmdOrControl", OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl")
|
||||
@ -225,6 +232,7 @@ public partial class ShortcutDialog : MSGComponentBase
|
||||
private void ClearShortcut()
|
||||
{
|
||||
this.currentShortcut = string.Empty;
|
||||
this.currentDisplayName = string.Empty;
|
||||
this.currentKey = null;
|
||||
this.hasCtrl = false;
|
||||
this.hasShift = false;
|
||||
@ -237,7 +245,17 @@ public partial class ShortcutDialog : MSGComponentBase
|
||||
|
||||
private void Cancel() => this.MudDialog.Cancel();
|
||||
|
||||
private void Confirm() => this.MudDialog.Close(DialogResult.Ok(this.currentShortcut));
|
||||
private void Confirm()
|
||||
{
|
||||
var displaySource = string.IsNullOrWhiteSpace(this.currentDisplayName)
|
||||
? string.Empty
|
||||
: this.currentShortcut;
|
||||
|
||||
this.MudDialog.Close(DialogResult.Ok(new ShortcutDialogResult(
|
||||
this.currentShortcut,
|
||||
this.currentDisplayName,
|
||||
displaySource)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the key code represents a modifier key.
|
||||
@ -377,6 +395,36 @@ public partial class ShortcutDialog : MSGComponentBase
|
||||
_ => code,
|
||||
};
|
||||
|
||||
private string BuildDisplayShortcut(string? key)
|
||||
{
|
||||
var displayKey = GetDisplayKey(key);
|
||||
if (string.IsNullOrWhiteSpace(displayKey))
|
||||
return string.Empty;
|
||||
|
||||
var parts = new List<string>();
|
||||
|
||||
if (this.hasCtrl)
|
||||
parts.Add(OperatingSystem.IsMacOS() ? "Cmd" : "Ctrl");
|
||||
|
||||
if (this.hasShift)
|
||||
parts.Add("Shift");
|
||||
|
||||
if (this.hasAlt)
|
||||
parts.Add("Alt");
|
||||
|
||||
parts.Add(displayKey);
|
||||
return string.Join("+", parts);
|
||||
}
|
||||
|
||||
private static string GetDisplayKey(string? key) => key switch
|
||||
{
|
||||
null or "" => string.Empty,
|
||||
" " => "Space",
|
||||
"Control" or "Shift" or "Alt" or "Meta" => string.Empty,
|
||||
_ when key.Length == 1 && key[0] >= 'a' && key[0] <= 'z' => key.ToUpperInvariant(),
|
||||
_ => key,
|
||||
};
|
||||
|
||||
private void HandleBlur()
|
||||
{
|
||||
// Re-focus the input field to keep capturing keys:
|
||||
|
||||
3
app/MindWork AI Studio/Dialogs/ShortcutDialogResult.cs
Normal file
3
app/MindWork AI Studio/Dialogs/ShortcutDialogResult.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace AIStudio.Dialogs;
|
||||
|
||||
public readonly record struct ShortcutDialogResult(string Shortcut, string DisplayName, string DisplaySource);
|
||||
@ -99,6 +99,16 @@ public sealed class DataApp(Expression<Func<Data, DataApp>>? configSelection = n
|
||||
/// </summary>
|
||||
public string ShortcutVoiceRecording { get; set; } = ManagedConfiguration.Register(configSelection, n => n.ShortcutVoiceRecording, string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// The user-facing label for the voice recording shortcut, based on the user's keyboard layout.
|
||||
/// </summary>
|
||||
public string ShortcutVoiceRecordingDisplayName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The canonical voice recording shortcut value this display label belongs to.
|
||||
/// </summary>
|
||||
public string ShortcutVoiceRecordingDisplaySource { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The HTTP timeout in seconds for external HTTP clients.
|
||||
/// </summary>
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
- Improved workspaces by highlighting the currently open chat in the workspace view.
|
||||
- Improved workspaces by adding a shortcut to start a new chat directly from each workspace row.
|
||||
- Improved workspaces by allowing new workspaces to be created while moving a chat.
|
||||
- Improved voice recording shortcut labels so they match the user's keyboard layout after being configured.
|
||||
- Improved the enterprise configuration details on the information page by showing where each configuration comes from and which configuration slot was used.
|
||||
- Fixed workspace creation and renaming to prevent new workspaces from using an existing name.
|
||||
- Fixed an issue on Microsoft Windows where reading attached documents could briefly open a terminal window while processing files.
|
||||
|
||||
@ -22,7 +22,7 @@ public sealed class EmptyStringAnalyzer : DiagnosticAnalyzer
|
||||
Use string.Empty instead of ""
|
||||
""";
|
||||
|
||||
private static readonly string DESCRIPTION = """Empty string literals ("") should be replaced with string.Empty for better code consistency and readability except in const contexts.""";
|
||||
private static readonly string DESCRIPTION = """Empty string literals ("") should be replaced with string.Empty for better code consistency and readability except in contexts requiring compile-time constants.""";
|
||||
|
||||
private const string CATEGORY = "Usage";
|
||||
|
||||
@ -43,20 +43,26 @@ public sealed class EmptyStringAnalyzer : DiagnosticAnalyzer
|
||||
if (stringLiteral.Token.ValueText != string.Empty)
|
||||
return;
|
||||
|
||||
if (IsInConstContext(stringLiteral))
|
||||
return;
|
||||
|
||||
if (IsInParameterDefaultValue(stringLiteral))
|
||||
if (RequiresCompileTimeConstant(stringLiteral))
|
||||
return;
|
||||
|
||||
var diagnostic = Diagnostic.Create(RULE, stringLiteral.GetLocation());
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
|
||||
private static bool IsInConstContext(LiteralExpressionSyntax stringLiteral)
|
||||
private static bool RequiresCompileTimeConstant(LiteralExpressionSyntax stringLiteral)
|
||||
{
|
||||
return IsInConstDeclarationInitializer(stringLiteral)
|
||||
|| IsInParameterDefaultValue(stringLiteral)
|
||||
|| IsInAttributeArgument(stringLiteral)
|
||||
|| IsInSwitchCaseLabel(stringLiteral)
|
||||
|| IsInConstantPattern(stringLiteral);
|
||||
}
|
||||
|
||||
private static bool IsInConstDeclarationInitializer(LiteralExpressionSyntax stringLiteral)
|
||||
{
|
||||
var variableDeclarator = stringLiteral.FirstAncestorOrSelf<VariableDeclaratorSyntax>();
|
||||
if (variableDeclarator is null)
|
||||
if (variableDeclarator?.Initializer is null || !ContainsNode(variableDeclarator.Initializer.Value, stringLiteral))
|
||||
return false;
|
||||
|
||||
var declaration = variableDeclarator.Parent?.Parent;
|
||||
@ -71,18 +77,30 @@ public sealed class EmptyStringAnalyzer : DiagnosticAnalyzer
|
||||
|
||||
private static bool IsInParameterDefaultValue(LiteralExpressionSyntax stringLiteral)
|
||||
{
|
||||
// Prüfen, ob das String-Literal Teil eines Parameter-Defaults ist
|
||||
var parameter = stringLiteral.FirstAncestorOrSelf<ParameterSyntax>();
|
||||
if (parameter is null)
|
||||
return false;
|
||||
return parameter?.Default is not null && ContainsNode(parameter.Default.Value, stringLiteral);
|
||||
}
|
||||
|
||||
// Überprüfen, ob das String-Literal im Default-Wert des Parameters verwendet wird
|
||||
if (parameter.Default is not null &&
|
||||
parameter.Default.Value == stringLiteral)
|
||||
private static bool IsInAttributeArgument(LiteralExpressionSyntax stringLiteral)
|
||||
{
|
||||
return true;
|
||||
var attributeArgument = stringLiteral.FirstAncestorOrSelf<AttributeArgumentSyntax>();
|
||||
return attributeArgument is not null && ContainsNode(attributeArgument.Expression, stringLiteral);
|
||||
}
|
||||
|
||||
return false;
|
||||
private static bool IsInSwitchCaseLabel(LiteralExpressionSyntax stringLiteral)
|
||||
{
|
||||
var caseSwitchLabel = stringLiteral.FirstAncestorOrSelf<CaseSwitchLabelSyntax>();
|
||||
return caseSwitchLabel is not null && ContainsNode(caseSwitchLabel.Value, stringLiteral);
|
||||
}
|
||||
|
||||
private static bool IsInConstantPattern(LiteralExpressionSyntax stringLiteral)
|
||||
{
|
||||
var constantPattern = stringLiteral.FirstAncestorOrSelf<ConstantPatternSyntax>();
|
||||
return constantPattern is not null && ContainsNode(constantPattern.Expression, stringLiteral);
|
||||
}
|
||||
|
||||
private static bool ContainsNode(SyntaxNode parent, SyntaxNode child)
|
||||
{
|
||||
return parent.SpanStart <= child.SpanStart && child.Span.End <= parent.Span.End;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user