This commit is contained in:
Даниил Заикин
2026-06-17 20:44:53 +03:00
parent 9d153773c2
commit b133e7c656
1880 changed files with 244545 additions and 0 deletions
@@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Linq;
namespace QFSW.QC
{
/// <summary>
/// An IQcSuggestor that caches the created IQcSuggestion objects
/// </summary>
/// <typeparam name="TItem">The item type that suggestions are produced from.</typeparam>
public abstract class BasicCachedQcSuggestor<TItem> : IQcSuggestor
{
private readonly Dictionary<TItem, IQcSuggestion> _suggestionCache = new Dictionary<TItem, IQcSuggestion>();
/// <summary>
/// If suggestions can be produced for the provided context.
/// </summary>
/// <param name="context"></param>
/// <param name="options">Options used by the suggestor.</param>
/// <returns>If suggestions can be produced.</returns>
protected abstract bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options);
/// <summary>
/// Converts an item to a suggestion.
/// </summary>
/// <param name="item">The item to convert to a suggestion.</param>
/// <returns>The converted suggestion.</returns>
protected abstract IQcSuggestion ItemToSuggestion(TItem item);
/// <summary>
/// Gets the items for a given context.
/// </summary>
/// <param name="context">The context to produce items for.</param>
/// <param name="options">Options used by the suggestor.</param>
/// <returns>The items produced.</returns>
protected abstract IEnumerable<TItem> GetItems(SuggestionContext context, SuggestorOptions options);
/// <summary>
/// Determines if the provided suggestion matches the provided context.
/// Override to remove the filtering or add custom filtering.
/// </summary>
/// <param name="context">The context to test the suggestion against.</param>
/// <param name="suggestion">The suggestion to test.</param>
/// <param name="options">Options used to test the suggestion.</param>
/// <returns>If the suggestion matches the context.</returns>
protected virtual bool IsMatch(SuggestionContext context, IQcSuggestion suggestion, SuggestorOptions options)
{
return SuggestorUtilities.IsCompatible(context.Prompt, suggestion.PrimarySignature, options);
}
public IEnumerable<IQcSuggestion> GetSuggestions(SuggestionContext context, SuggestorOptions options)
{
if (!CanProvideSuggestions(context, options))
{
return Enumerable.Empty<IQcSuggestion>();
}
return GetItems(context, options)
.Select(ItemToSuggestionCached)
.Where(suggestion => IsMatch(context, suggestion, options));
}
private IQcSuggestion ItemToSuggestionCached(TItem item)
{
if (_suggestionCache.TryGetValue(item, out IQcSuggestion suggestion))
{
return suggestion;
}
return _suggestionCache[item] = ItemToSuggestion(item);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7a2b48636fac184aa202176dea0dab9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,51 @@
namespace QFSW.QC
{
/// <summary>
/// A suggestion that can be auto completed and displayed.
/// </summary>
public interface IQcSuggestion
{
/// <summary>
/// The full signature of the suggestion for display purposes.
/// </summary>
string FullSignature { get; }
/// <summary>
/// The primary component of the signature.
/// </summary>
string PrimarySignature { get; }
/// <summary>
/// The secondary component of the signature.
/// </summary>
string SecondarySignature { get; }
/// <summary>
/// Determines if the provided prompt matches this suggestion
/// </summary>
/// <param name="prompt">The prompt to check.</param>
/// <returns>If the prompt matches the suggestion.</returns>
bool MatchesPrompt(string prompt);
/// <summary>
/// Gets the completion value for this a prompt with this suggestion.
/// </summary>
/// <param name="prompt">The prompt to complete.</param>
/// <returns>The completion value.</returns>
string GetCompletion(string prompt);
/// <summary>
/// Gets the completion tail, similar to the secondary signature, for a prompt with this suggestion.
/// </summary>
/// <param name="prompt">The prompt to complete the tail for.</param>
/// <returns>The completion tail.</returns>
string GetCompletionTail(string prompt);
/// <summary>
/// Gets the inner suggestion context for this suggestion, allowing for further suggestions to be provided.
/// </summary>
/// <param name="context">The outer suggestion context.</param>
/// <returns>The inner suggestion context, null if none can be created.</returns>
SuggestionContext? GetInnerSuggestionContext(SuggestionContext context);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0db96f25927d64e46b8effecade0732d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
namespace QFSW.QC
{
/// <summary>
/// A filter that can remove suggestions produced by the QuantumSuggestor
/// </summary>
public interface IQcSuggestionFilter
{
/// <summary>
/// Determines if a suggestion should be permitted given the provided context.
/// </summary>
/// <param name="suggestion">The suggestion to query.</param>
/// <param name="context">The context for the suggestion.</param>
/// <returns>If the suggestion should be permitted.</returns>
bool IsSuggestionPermitted(IQcSuggestion suggestion, SuggestionContext context);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe8cbfb6d5020864fae6264d766ffa01
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace QFSW.QC
{
/// <summary>
/// A suggestor that is loaded by the QuantumSuggestor to suggest IQcSuggestions
/// </summary>
public interface IQcSuggestor
{
/// <summary>
/// Gets the suggestions for a given context.
/// </summary>
/// <param name="context">The context to provide suggestions for.</param>
/// <param name="options">Options used by the suggestor.</param>
/// <returns>The suggestions produced for the context.</returns>
IEnumerable<IQcSuggestion> GetSuggestions(SuggestionContext context, SuggestorOptions options);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0aeb09aef885a334a851c9793052aecd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
namespace QFSW.QC
{
/// <summary>
/// Base type for tags to use for the suggestion system.
/// Suggestors may check the context for the presence of different tags.
/// </summary>
public interface IQcSuggestorTag
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c77697a7053da34b9ef7d45aa2727d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,106 @@
using QFSW.QC.Comparators;
using System;
using System.Collections.Generic;
using System.Linq;
namespace QFSW.QC
{
/// <summary>
/// Provides a filtered and sorted list of suggestions for a given context using IQcSuggestors and IQcSuggestionFilter
/// </summary>
public class QuantumSuggestor
{
private readonly IQcSuggestor[] _suggestors;
private readonly IQcSuggestionFilter[] _suggestionFilters;
private readonly List<IQcSuggestion> _suggestionBuffer = new List<IQcSuggestion>();
/// <summary>
/// Creates a Quantum Suggestor with a custom set of suggestors an suggestion filters.
/// </summary>
/// <param name="suggestors">The IQcSuggestors to use in this Quantum Suggestor.</param>
/// /// <param name="suggestionFilters">The IQcSuggestionFilters to use in this Quantum Suggestor.</param>
public QuantumSuggestor(IEnumerable<IQcSuggestor> suggestors, IEnumerable<IQcSuggestionFilter> suggestionFilters)
{
_suggestors = suggestors.ToArray();
_suggestionFilters = suggestionFilters.ToArray();
}
/// <summary>
/// Creates a Quantum Suggestor with the default injected suggestors and suggestion filters.
/// </summary>
public QuantumSuggestor() : this(
new InjectionLoader<IQcSuggestor>().GetInjectedInstances(),
new InjectionLoader<IQcSuggestionFilter>().GetInjectedInstances())
{
}
/// <summary>
/// Gets suggestions for a given context.
/// </summary>
/// <param name="context">The context to get suggestions for.</param>
/// <param name="options">Options for the suggestor.</param>
/// <returns>The sorted and filtered suggestions for the provided context.</returns>
public IEnumerable<IQcSuggestion> GetSuggestions(SuggestionContext context, SuggestorOptions options)
{
PreprocessContext(ref context);
// Get and filter suggestions
IEnumerable<IQcSuggestion> suggestions =
_suggestors
.SelectMany(x => x.GetSuggestions(context, options))
.Where(x => IsSuggestionPermitted(x, context));
_suggestionBuffer.Clear();
_suggestionBuffer.AddRange(suggestions);
// Sort suggestions
AlphanumComparator comparator = new AlphanumComparator();
IOrderedEnumerable<IQcSuggestion> sortedSuggestions =
_suggestionBuffer
.OrderBy(x => x.PrimarySignature.Length)
.ThenBy(x => x.PrimarySignature, comparator)
.ThenBy(x => x.SecondarySignature.Length)
.ThenBy(x => x.SecondarySignature, comparator);
if (options.Fuzzy)
{
StringComparison comparisonType = options.CaseSensitive
? StringComparison.CurrentCulture
: StringComparison.CurrentCultureIgnoreCase;
sortedSuggestions = sortedSuggestions
.OrderBy(x => x.PrimarySignature.IndexOf(context.Prompt, comparisonType));
}
// Return suggestions to user
return sortedSuggestions;
}
private void PreprocessContext(ref SuggestionContext context)
{
// Strip the scope on the provided prompt in the context to improve suggestions
// We want to allow incomplete scope reduction on the prompt so that you get suggestions
// as if you had finished the scope properly
TextProcessing.ReduceScopeOptions options = TextProcessing.ReduceScopeOptions.Default;
options.ReduceIncompleteScope = true;
context.Prompt = context.Prompt.ReduceScope(options);
}
private bool IsSuggestionPermitted(IQcSuggestion suggestion, SuggestionContext context)
{
// LINQ alternative produces too much garbage
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (IQcSuggestionFilter filter in _suggestionFilters)
{
if (!filter.IsSuggestionPermitted(suggestion, context))
{
return false;
}
}
return true;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2234ee814efd4f24b9b6d922c0526de0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,58 @@
namespace QFSW.QC
{
/// <summary>
/// Raw suggestion of a given value.
/// </summary>
public class RawSuggestion : IQcSuggestion
{
private readonly string _value;
private readonly bool _singleLiteral;
private readonly string _completion;
public string FullSignature => _value;
public string PrimarySignature => _value;
public string SecondarySignature => string.Empty;
/// <summary>
/// Constructs a suggestion from the provided value.
/// </summary>
/// <param name="value">The value to suggest.</param>
/// <param name="singleLiteral">If the value should be treated as a single literal then "" will be used as necessary.</param>
public RawSuggestion(string value, bool singleLiteral = false)
{
_value = value;
_singleLiteral = singleLiteral;
_completion = _value;
if (_completion.CanSplitScoped(' ', '"', '"'))
{
_completion = $"\"{_completion}\"";
}
}
public bool MatchesPrompt(string prompt)
{
if (_singleLiteral)
{
prompt = prompt.Trim('"');
}
return prompt == _value;
}
public string GetCompletion(string prompt)
{
return _completion;
}
public string GetCompletionTail(string prompt)
{
return string.Empty;
}
public SuggestionContext? GetInnerSuggestionContext(SuggestionContext context)
{
return null;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77020387dd1961a4781323957a90dac1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
namespace QFSW.QC
{
/// <summary>
/// The context to provide suggestions for.
/// </summary>
public struct SuggestionContext
{
/// <summary>
/// The depth of the suggestion context.
/// </summary>
public int Depth;
/// <summary>
/// The prompt to generate suggestions for at this depth.
/// </summary>
public string Prompt;
/// <summary>
/// If any, a specific type to target when producing suggestions.
/// </summary>
public Type TargetType;
/// <summary>
/// Any tags added to the suggestion context that may be queried by suggestors.
/// </summary>
public IQcSuggestorTag[] Tags;
/// <summary>
/// Checks if the specified tag exists.
/// </summary>
/// <typeparam name="T">The tag to check.</typeparam>
/// <returns>If the tag exists.</returns>
public bool HasTag<T>() where T : IQcSuggestorTag
{
if (Tags == null)
{
return false;
}
// foreach loop has better performance
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (IQcSuggestorTag tag in Tags)
{
if (tag is T)
{
return true;
}
}
return false;
}
/// <summary>
/// Gets the specified tag from the context.
/// </summary>
/// <typeparam name="T">The tag to get from the context.</typeparam>
/// <returns>The tag if it could be found, otherwise a KeyNotFoundException exception is thrown.</returns>
public T GetTag<T>() where T : IQcSuggestorTag
{
if (Tags != null)
{
foreach (IQcSuggestorTag tag in Tags)
{
if (tag is T foundTag)
{
return foundTag;
}
}
}
throw new KeyNotFoundException($"No tags of type {typeof(T)} could be found.");
}
/// <summary>
/// Gets all instances of the specified tag from the context.
/// </summary>
/// <typeparam name="T">The tag to get from the context.</typeparam>
/// <returns>The tags in the context.</returns>
public IEnumerable<T> GetTags<T>() where T : IQcSuggestorTag
{
if (Tags != null)
{
foreach (IQcSuggestorTag tag in Tags)
{
if (tag is T foundTag)
{
yield return foundTag;
}
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 319e8165d4db42d488cb24106ce579f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,33 @@
using System.Collections.Generic;
namespace QFSW.QC
{
/// <summary>
/// A managed set of suggestions for a given context.
/// </summary>
public class SuggestionSet
{
/// <summary>
/// The context this suggestion set was produced for.
/// </summary>
public SuggestionContext Context;
/// <summary>
/// The index of the current selection in the set.
/// </summary>
public int SelectionIndex;
/// <summary>
/// The suggestions contained within the set.
/// </summary>
public readonly List<IQcSuggestion> Suggestions = new List<IQcSuggestion>();
/// <summary>
/// The currently selected suggestion in the set, if any.
/// </summary>
public IQcSuggestion CurrentSelection =>
SelectionIndex >= 0 && SelectionIndex < Suggestions.Count
? Suggestions[SelectionIndex]
: null;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c180cb50f5d8024180ce8015ea31999
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,323 @@
using QFSW.QC.Pooling;
using QFSW.QC.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace QFSW.QC
{
/// <summary>
/// Managed stack of suggestion sets updated from a user prompt.
/// Each suggestion set represents a layer of suggestions, allowing for nested suggestions.
/// </summary>
public class SuggestionStack
{
private readonly QuantumSuggestor _suggestor;
private readonly List<SuggestionSet> _suggestionSets = new List<SuggestionSet>();
private readonly Pool<SuggestionSet> _setPool = new Pool<SuggestionSet>();
private readonly StringBuilder _stringBuilder = new StringBuilder();
/// <summary>
/// The topmost valid suggestion set in the stack.
/// </summary>
public SuggestionSet TopmostSuggestionSet => _suggestionSets.LastOrDefault();
/// <summary>
/// The selected suggestion, if any, in the topmost suggestion set.
/// </summary>
public IQcSuggestion TopmostSuggestion => TopmostSuggestionSet?.CurrentSelection;
/// <summary>
/// Callback invoked when a new suggestion set is created.
/// </summary>
public event Action<SuggestionSet> OnSuggestionSetCreated;
/// <summary>
/// Creates a SuggestionStack with a default QuantumSuggestor
/// </summary>
public SuggestionStack() : this(new QuantumSuggestor())
{
}
/// <summary>
/// Creates a SuggestionStack with a user provided QuantumSuggestor
/// </summary>
/// <param name="suggestor">The QuantumSuggestor to use when creating suggestions for the stack.</param>
public SuggestionStack(QuantumSuggestor suggestor)
{
_suggestor = suggestor;
}
/// <summary>
/// Clears the stack of all suggestions.
/// </summary>
public void Clear()
{
while (PopSet()) { }
}
/// <summary>
/// Updates the stack to the new prompt.
/// </summary>
/// <param name="prompt">The prompt to update the stack with.</param>
/// <param name="options">Options to provide to the suggestor.</param>
public void UpdateStack(string prompt, SuggestorOptions options)
{
// Clear if empty
if (string.IsNullOrWhiteSpace(prompt))
{
Clear();
return;
}
PropagateContextChanges(prompt);
PopInvalidLayers();
BuildInitialLayer(prompt, options);
BuildNewLayers(options);
}
private SuggestionContext? GetInnerSuggestionContext(SuggestionSet set)
{
IQcSuggestion currentSuggestion = set.CurrentSelection;
SuggestionContext currentContext = set.Context;
return currentSuggestion?.GetInnerSuggestionContext(currentContext);
}
private void InvalidateLayersFrom(int index)
{
PopSets(_suggestionSets.Count - index);
}
private void PropagateContextChanges(string prompt)
{
if (_suggestionSets.Count == 0)
{
// Nothing to propagate
return;
}
// Initialize initial set with new prompt
_suggestionSets[0].Context.Prompt = prompt;
// Propagate context changes
for (int i = 0; i < _suggestionSets.Count - 1; i++)
{
SuggestionSet currentSet = _suggestionSets[i];
SuggestionContext? newNextContext = GetInnerSuggestionContext(currentSet);
if (newNextContext != null)
{
// Update context for existing layers
SuggestionSet nextSet = _suggestionSets[i + 1];
nextSet.Context = newNextContext.Value;
}
else
{
// Null context means the layer must be invalidated
InvalidateLayersFrom(i + 1);
}
}
}
private void PopInvalidLayers()
{
for (int i = 0; i < _suggestionSets.Count; i++)
{
SuggestionSet set = _suggestionSets[i];
SuggestionContext context = set.Context;
IQcSuggestion suggestion = set.CurrentSelection;
if (suggestion == null || !suggestion.MatchesPrompt(context.Prompt))
{
InvalidateLayersFrom(i);
}
}
}
private void BuildInitialLayer(string prompt, SuggestorOptions options)
{
if (_suggestionSets.Count == 0)
{
SuggestionContext context = new SuggestionContext
{
Prompt = prompt,
Depth = 0,
TargetType = null,
};
CreateLayer(context, options);
}
}
private void BuildNewLayers(SuggestorOptions options)
{
// Create new layers if possible
if (TopmostSuggestion != null)
{
SuggestionSet set = TopmostSuggestionSet;
SuggestionContext? newNextContext = GetInnerSuggestionContext(set);
if (newNextContext != null)
{
// Create new layer, then recursively try to build more
if (CreateLayer(newNextContext.Value, options))
{
BuildNewLayers(options);
}
}
}
}
private void TryAutoSelectSuggestion(SuggestionSet set, string prompt)
{
if (set.CurrentSelection != null)
{
// Don't try to select something if we already have a selection
return;
}
// Try to select the first item if it matches
IQcSuggestion candidate = set.Suggestions.FirstOrDefault();
if (candidate != null && candidate.MatchesPrompt(prompt))
{
set.SelectionIndex = 0;
}
}
private bool CreateLayer(SuggestionContext context, SuggestorOptions options)
{
// Get the suggestions and create the new set
IEnumerable<IQcSuggestion> suggestions =
_suggestor.GetSuggestions(context, options);
SuggestionSet set = PushSet();
set.Context = context;
set.Suggestions.AddRange(suggestions);
// Remove the new layer if its empty
if (set.Suggestions.Count == 0)
{
PopSet();
return false;
}
OnSuggestionSetCreated?.Invoke(set);
// Try to auto select a suggestion in the set
TryAutoSelectSuggestion(set, context.Prompt);
return true;
}
/// <summary>
/// Gets the current completion value of the stack.
/// </summary>
/// <returns>The combined completion value of the stack.</returns>
public string GetCompletion()
{
if (_suggestionSets.Count == 0)
{
return string.Empty;
}
IEnumerable<IQcSuggestion> suggestionChain =
_suggestionSets
.Select(x => x.CurrentSelection)
.Where(x => x != null);
SuggestionContext context = _suggestionSets[0].Context;
_stringBuilder.Clear();
foreach (IQcSuggestion suggestion in suggestionChain)
{
string part = context.Prompt;
SuggestionContext? newContext = suggestion.GetInnerSuggestionContext(context);
if (newContext != null)
{
_stringBuilder.Append(part, 0, part.Length - newContext.Value.Prompt.Length);
}
else
{
_stringBuilder.Append(suggestion.GetCompletion(part));
}
}
return _stringBuilder.ToString();
}
/// <summary>
/// Gets the current completion tail of the stack.
/// </summary>
/// <returns>The combined completion tail of the stack.</returns>
public string GetCompletionTail()
{
_stringBuilder.Clear();
foreach (SuggestionSet set in _suggestionSets.Reversed())
{
SuggestionContext context = set.Context;
_stringBuilder.Append(set.CurrentSelection?.GetCompletionTail(context.Prompt));
}
return _stringBuilder.ToString();
}
/// <summary>
/// Attempts to set the selected suggestion in the topmost set.
/// </summary>
/// <param name="suggestionIndex">The index of the suggestion in the topmost set to select.</param>
/// <returns>If the suggestion could be selected.</returns>
public bool SetSuggestionIndex(int suggestionIndex)
{
if (_suggestionSets.Count == 0)
{
return false;
}
if (suggestionIndex < 0 || suggestionIndex > TopmostSuggestionSet.Suggestions.Count)
{
return false;
}
TopmostSuggestionSet.SelectionIndex = suggestionIndex;
TopmostSuggestionSet.Context.Prompt = TopmostSuggestion.PrimarySignature;
return true;
}
private SuggestionSet PushSet()
{
SuggestionSet set = _setPool.GetObject();
set.SelectionIndex = -1;
set.Suggestions.Clear();
_suggestionSets.Add(set);
return set;
}
private bool PopSet()
{
if (_suggestionSets.Count > 0)
{
int removeIndex = _suggestionSets.Count - 1;
SuggestionSet set = _suggestionSets[removeIndex];
_suggestionSets.RemoveAt(removeIndex);
_setPool.Release(set);
return true;
}
return false;
}
private bool PopSets(int count)
{
bool successful = true;
while (successful && count-- > 0)
{
successful &= PopSet();
}
return successful;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6052e7f2cfec30345aa76c4322154c0e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,24 @@
namespace QFSW.QC
{
/// <summary>
/// Options used by the suggestor when producing suggestions.
/// </summary>
public struct SuggestorOptions
{
/// <summary>
/// If case of the prompt should be considered when producing suggestions.
/// </summary>
public bool CaseSensitive;
/// <summary>
/// If fuzzy searching should be used when producing suggestions.
/// </summary>
public bool Fuzzy;
/// <summary>
/// If overloads of the same suggestion should be collapsed into
/// a single suggestion with optional elements where possible.
/// </summary>
public bool CollapseOverloads;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2780a4abd4711f34faad34e752d4b278
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,13 @@
using System;
namespace QFSW.QC
{
/// <summary>
/// Base attribute for all IQcSuggestorTag sources.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)]
public abstract class SuggestorTagAttribute : Attribute
{
public abstract IQcSuggestorTag[] GetSuggestorTags();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ad821e4afc8f0904cbf3770ef3528fa9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,34 @@
using QFSW.QC.Utilities;
namespace QFSW.QC
{
/// <summary>
/// Common utilities used by the suggestion system.
/// </summary>
public static class SuggestorUtilities
{
/// <summary>
/// Determines if a prompt is compatible with a string suggestion.
/// </summary>
/// <param name="prompt">The prompt to test.</param>
/// <param name="suggestion">The string suggestion to test again.</param>
/// <param name="options">The options used by the suggestor.</param>
/// <returns>If the prompt is compatible.</returns>
public static bool IsCompatible(string prompt, string suggestion, SuggestorOptions options)
{
if (prompt.Length > suggestion.Length)
{
return false;
}
if (options.Fuzzy)
{
return options.CaseSensitive
? suggestion.Contains(prompt)
: suggestion.ContainsCaseInsensitive(prompt);
}
return suggestion.StartsWith(prompt, !options.CaseSensitive, null);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a881c697c3a6c3e46a5e20f241472b01
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b438b39ea80851c4ebad089e1e61aaa6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,28 @@
using System.Collections.Generic;
namespace QFSW.QC.Suggestors
{
public class BoolSuggestor : BasicCachedQcSuggestor<string>
{
private readonly string[] _values =
{
"true",
"false"
};
protected override bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options)
{
return context.TargetType == typeof(bool);
}
protected override IQcSuggestion ItemToSuggestion(string value)
{
return new RawSuggestion(value);
}
protected override IEnumerable<string> GetItems(SuggestionContext context, SuggestorOptions options)
{
return _values;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7d92f72d4245dd241a6011cd64ae3644
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Linq;
namespace QFSW.QC.Suggestors
{
public class CommandNameSuggestor : BasicCachedQcSuggestor<string>
{
protected override bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options)
{
return context.HasTag<Tags.CommandNameTag>()
&& !string.IsNullOrWhiteSpace(context.Prompt);
}
protected override IQcSuggestion ItemToSuggestion(string commandName)
{
return new RawSuggestion(commandName);
}
protected override IEnumerable<string> GetItems(SuggestionContext context, SuggestorOptions options)
{
string incompleteCommandName =
context.Prompt
.SplitScopedFirst(' ')
.SplitFirst('<');
return QuantumConsoleProcessor.GetUniqueCommands()
.Select(command => command.CommandName);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c09d30aec8067b41af36708c7808282
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,215 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
namespace QFSW.QC.Suggestors
{
public class CommandSuggestion : IQcSuggestion
{
private readonly CommandData _command;
private readonly string[] _paramNames;
private readonly int _numOptionalParams;
private readonly Dictionary<string, Type[]> _genericSignatureCache = new Dictionary<string, Type[]>();
private readonly Dictionary<ParameterInfo, IQcSuggestorTag[]> _parameterTagsCache = new Dictionary<ParameterInfo, IQcSuggestorTag[]>();
private readonly StringBuilder _stringBuilder = new StringBuilder();
private struct ParsedCommandNameInfo
{
public string RawName;
public string CommandName;
public string GenericSignature;
public string[] GenericArgNames;
}
private ParsedCommandNameInfo _currentCommandNameCache;
public string FullSignature => _command.CommandSignature;
public string PrimarySignature => _command.CommandName;
public string SecondarySignature { get; }
public CommandData Command => _command;
public CommandSuggestion(CommandData command, int numOptionalParams = 0)
{
_command = command;
_paramNames = _command.ParameterSignature.Split(' ');
_numOptionalParams = numOptionalParams;
for (int i = _paramNames.Length - _numOptionalParams; i < _paramNames.Length; i++)
{
_paramNames[i] = $"[{_paramNames[i]}]";
}
SecondarySignature = $"{_command.GenericSignature} {string.Join(" ", _paramNames)}";
}
public bool MatchesPrompt(string prompt)
{
UpdateCurrentCache(prompt);
return _currentCommandNameCache.CommandName == _command.CommandName;
}
public string GetCompletion(string prompt)
{
return _command.CommandName;
}
public string GetCompletionTail(string prompt)
{
UpdateCurrentCache(prompt);
_stringBuilder.Clear();
int numParamsInPrompt = prompt
.SplitScoped(' ')
.Count(x => !string.IsNullOrWhiteSpace(x)) - 1;
// Can't be less than zero params in the prompt
numParamsInPrompt = Mathf.Max(numParamsInPrompt, 0);
int numParamsToPrint = _command.ParamCount - numParamsInPrompt;
if (prompt == _currentCommandNameCache.CommandName)
{
_stringBuilder.Append(_command.GenericSignature);
}
// Print out the params not in the prompt
for (int i = 0; i < numParamsToPrint; i++)
{
// Add a space only if there's no whitespace trail already
if (i > 0 || !prompt.EndsWith(" "))
{
_stringBuilder.Append(' ');
}
int paramIdx = i + numParamsInPrompt;
_stringBuilder.Append(_paramNames[paramIdx]);
}
return _stringBuilder.ToString();
}
public SuggestionContext? GetInnerSuggestionContext(SuggestionContext context)
{
UpdateCurrentCache(context.Prompt);
// We want to consider this a new prompt if we've hit whitespace and we're not in an incomplete scope
bool emptyPromptEnd = context.Prompt.EndsWith(" ") && context.Prompt.GetMaxScopeDepthAtEnd() == 0;
string[] promptParts = context.Prompt
.SplitScoped(' ')
.Where(x => !string.IsNullOrWhiteSpace(x))
.ToArray();
int promptArgs = promptParts.Length - 1;
if (emptyPromptEnd)
{
promptArgs++;
}
if (promptArgs <= 0 || promptArgs > _command.ParamCount)
{
return null;
}
int paramIndex = promptArgs - 1;
SuggestionContext newContext = context;
newContext.Depth++;
newContext.TargetType = GetParameterType(paramIndex);
newContext.Tags = GetParameterTags(paramIndex);
newContext.Prompt = emptyPromptEnd
? string.Empty
: promptParts.LastOrDefault();
return newContext;
}
private void UpdateCurrentCache(string prompt)
{
string rawName = prompt.SplitScopedFirst(' ');
if (rawName != _currentCommandNameCache.RawName)
{
_currentCommandNameCache = ParseCommandNameInfo(rawName);
}
}
private ParsedCommandNameInfo ParseCommandNameInfo(string rawName)
{
string[] commandNameParts = rawName.Split(new[] { '<' }, 2);
ParsedCommandNameInfo info = new ParsedCommandNameInfo();
info.RawName = rawName;
info.CommandName = commandNameParts[0];
if (_command.IsGeneric)
{
info.GenericSignature = commandNameParts.Length > 1 ? $"<{commandNameParts[1]}" : "";
info.GenericArgNames = info.GenericSignature
.ReduceScope('<', '>')
.SplitScoped(',');
}
return info;
}
private Type[] ParseGenericTypes(ParsedCommandNameInfo commandNameInfo)
{
return commandNameInfo
.GenericArgNames
.Select(QuantumParser.ParseType)
.ToArray();
}
private Type[] GetParameterTypes(ParsedCommandNameInfo commandNameInfo)
{
// Return normal types if not generic
if (!_command.IsGeneric)
{
return _command.ParamTypes;
}
// Return cached if available
if (_genericSignatureCache.TryGetValue(commandNameInfo.GenericSignature, out Type[] paramTypes))
{
return paramTypes;
}
try
{
// Build types from generic types
Type[] genericTypes = ParseGenericTypes(_currentCommandNameCache);
paramTypes = _command.MakeGenericArguments(genericTypes);
}
catch
{
// Use normal types if unable to process generics
paramTypes = _command.ParamTypes;
}
return _genericSignatureCache[commandNameInfo.GenericSignature] = paramTypes;
}
private Type GetParameterType(int paramIndex)
{
Type[] paramTypes = GetParameterTypes(_currentCommandNameCache);
return paramTypes[paramIndex];
}
private IQcSuggestorTag[] GetParameterTags(int paramIndex)
{
ParameterInfo parameter = _command.MethodParamData[paramIndex];
if (_parameterTagsCache.TryGetValue(parameter, out IQcSuggestorTag[] tags))
{
return tags;
}
return _parameterTagsCache[parameter] =
parameter
.GetCustomAttributes<SuggestorTagAttribute>()
.SelectMany(x => x.GetSuggestorTags())
.ToArray();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a094f36512b47b429cba82b4de046c9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,121 @@
using System.Collections.Generic;
using System.Linq;
using QFSW.QC.Utilities;
namespace QFSW.QC.Suggestors
{
public struct CollapsedCommand
{
public CommandData Command;
public int NumOptionalParams;
public CollapsedCommand(CommandData command)
{
Command = command;
NumOptionalParams = 0;
}
}
public class CommandSuggestor : BasicCachedQcSuggestor<CollapsedCommand>
{
private readonly Dictionary<string, List<CommandData>> _commandGroups = new Dictionary<string, List<CommandData>>();
private readonly Stack<CollapsedCommand> _commandCollector = new Stack<CollapsedCommand>();
protected override bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options)
{
return context.Depth == 0;
}
protected override IQcSuggestion ItemToSuggestion(CollapsedCommand collapsedCommand)
{
return new CommandSuggestion(collapsedCommand.Command, collapsedCommand.NumOptionalParams);
}
protected override IEnumerable<CollapsedCommand> GetItems(SuggestionContext context, SuggestorOptions options)
{
string incompleteCommandName =
context.Prompt
.SplitScopedFirst(' ')
.SplitFirst('<');
IEnumerable<CommandData> commands = GetCommands(incompleteCommandName, options);
return options.CollapseOverloads
? CollapseCommands(commands)
: commands.Select(x => new CollapsedCommand(x));
}
public IEnumerable<CommandData> GetCommands(string incompleteCommandName, SuggestorOptions options)
{
if (string.IsNullOrWhiteSpace(incompleteCommandName))
{
return Enumerable.Empty<CommandData>();
}
return QuantumConsoleProcessor.GetAllCommands()
.Where(command => SuggestorUtilities.IsCompatible(incompleteCommandName, command.CommandName, options));
}
protected override bool IsMatch(SuggestionContext context, IQcSuggestion suggestion, SuggestorOptions options)
{
// Perform filtering in GetCommands
return true;
}
private IEnumerable<CollapsedCommand> CollapseCommands(IEnumerable<CommandData> commands)
{
// Reset the command groups but keep lists around for better memory performance
foreach (List<CommandData> commandGroup in _commandGroups.Values)
{
commandGroup.Clear();
}
// Allocate commands to their groups
foreach (CommandData command in commands)
{
if (!_commandGroups.TryGetValue(command.CommandName, out List<CommandData> commandGroup))
{
commandGroup = new List<CommandData>();
_commandGroups[command.CommandName] = commandGroup;
}
commandGroup.Add(command);
}
// For each group, iterate over commands from least to most parameters
// If the new candidate is the same as the previous candidate + 1 new parameter
// Then absorb the previous command as an optional argument, otherwise keep both
foreach (List<CommandData> commandGroup in _commandGroups.Values)
{
commandGroup.InsertionSortBy(x => x.ParamCount);
_commandCollector.Clear();
foreach (CommandData command in commandGroup)
{
CollapsedCommand newCandidate = new CollapsedCommand(command);
if (_commandCollector.Count > 0)
{
CollapsedCommand prevCandidate = _commandCollector.Peek();
CommandData newCommand = newCandidate.Command;
CommandData prevCommand = prevCandidate.Command;
if (newCommand.ParamCount == prevCommand.ParamCount + 1)
{
if (newCommand.ParameterSignature.StartsWith(prevCommand.ParameterSignature))
{
_commandCollector.Pop();
newCandidate.NumOptionalParams += 1 + prevCandidate.NumOptionalParams;
}
}
}
_commandCollector.Push(newCandidate);
}
foreach (CollapsedCommand collapsedCommand in _commandCollector)
{
yield return collapsedCommand;
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7a81fd548b8f63a409ff49dd984e0847
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,32 @@
using QFSW.QC.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace QFSW.QC.Suggestors
{
public class ComponentSuggestor : BasicCachedQcSuggestor<string>
{
protected override bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options)
{
Type targetType = context.TargetType;
return targetType != null
&& targetType.IsDerivedTypeOf(typeof(Component))
&& !targetType.IsGenericParameter;
}
protected override IQcSuggestion ItemToSuggestion(string name)
{
return new RawSuggestion(name, true);
}
protected override IEnumerable<string> GetItems(SuggestionContext context, SuggestorOptions options)
{
return Object.FindObjectsOfType(context.TargetType)
.Select(cmp => (Component) cmp)
.Select(cmp => cmp.gameObject.name);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9268967e70c604946bad8e88174c2db6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace QFSW.QC.Suggestors
{
public class EnumSuggestor : BasicCachedQcSuggestor<string>
{
private readonly Dictionary<Type, string[]> _enumCaseCache = new Dictionary<Type, string[]>();
protected override bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options)
{
Type targetType = context.TargetType;
return targetType != null
&& targetType.IsEnum;
}
protected override IQcSuggestion ItemToSuggestion(string item)
{
return new RawSuggestion(item);
}
protected override IEnumerable<string> GetItems(SuggestionContext context, SuggestorOptions options)
{
return GetEnumCases(context.TargetType);
}
private string[] GetEnumCases(Type enumType)
{
if (_enumCaseCache.TryGetValue(enumType, out string[] cachedEnumCases))
{
return cachedEnumCases;
}
string[] enumCases =
enumType.GetEnumNames()
.Select(x => x.ToString())
.ToArray();
return _enumCaseCache[enumType] = enumCases;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4db269b19a4ec5f48b6e97ac1ef7c183
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace QFSW.QC.Suggestors
{
public class GameObjectSuggestor : BasicCachedQcSuggestor<string>
{
protected override bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options)
{
return context.TargetType == typeof(GameObject);
}
protected override IQcSuggestion ItemToSuggestion(string name)
{
return new RawSuggestion(name, true);
}
protected override IEnumerable<string> GetItems(SuggestionContext context, SuggestorOptions options)
{
return Object.FindObjectsOfType<GameObject>()
.Select(obj => obj.name);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b03355cf0721c774fb78eb92d958d535
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace QFSW.QC.Suggestors
{
/// <summary>
/// Produces the available suggestions for the suggestion system.
/// </summary>
public class InlineSuggestor : IQcSuggestor
{
public IEnumerable<IQcSuggestion> GetSuggestions(SuggestionContext context, SuggestorOptions options)
{
foreach (Tags.InlineSuggestionsTag t in context.GetTags<Tags.InlineSuggestionsTag>())
{
foreach (string s in t.Suggestions)
{
yield return new RawSuggestion(s, singleLiteral: true);
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fed3d8fbe2fe15b4680532bcd9ac9d78
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Linq;
namespace QFSW.QC.Suggestors
{
public class MacroSuggestor : BasicCachedQcSuggestor<string>
{
protected override bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options)
{
return context.Prompt.StartsWith("#");
}
protected override IQcSuggestion ItemToSuggestion(string macro)
{
return new RawSuggestion($"#{macro}");
}
protected override IEnumerable<string> GetItems(SuggestionContext context, SuggestorOptions options)
{
return QuantumMacros.GetMacros()
.Select(x => x.Key);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76d43cae401d00140b4e5c5fd5bd17ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,15 @@
{
"name": "QFSW.QC.Suggestors",
"references": [
"QFSW.QC"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 786923d329e69bf40b3d855b3e56d0cc
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,30 @@
using QFSW.QC.Utilities;
using System.Collections.Generic;
using System.Linq;
namespace QFSW.QC.Suggestors
{
public class SceneNameSuggestor : BasicCachedQcSuggestor<string>
{
protected override bool CanProvideSuggestions(SuggestionContext context, SuggestorOptions options)
{
return context.HasTag<Tags.SceneNameTag>();
}
protected override IQcSuggestion ItemToSuggestion(string sceneName)
{
return new RawSuggestion(sceneName, true);
}
protected override IEnumerable<string> GetItems(SuggestionContext context, SuggestorOptions options)
{
if (context.GetTag<Tags.SceneNameTag>().LoadedOnly)
{
return SceneUtilities.GetLoadedScenes()
.Select(x => x.name);
}
return SceneUtilities.GetAllSceneNames();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9473b0454f2e70d4d80abe5e83c818dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bc2f0197bd226cd40bc028edbefb49c1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,20 @@
namespace QFSW.QC.Suggestors.Tags
{
public struct CommandNameTag : IQcSuggestorTag
{
}
/// <summary>
/// Specifies that command name values should be suggested for the parameter.
/// </summary>
public sealed class CommandNameAttribute : SuggestorTagAttribute
{
private readonly IQcSuggestorTag[] _tags = { new CommandNameTag() };
public override IQcSuggestorTag[] GetSuggestorTags()
{
return _tags;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 93d4e2dcc4c88c547a4f39af4a2a41e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Linq;
namespace QFSW.QC.Suggestors.Tags
{
/// <summary>
/// Object that holds all the inlined suggestions provided
/// to <see cref="SuggestionsAttribute"/>.
/// </summary>
public struct InlineSuggestionsTag : IQcSuggestorTag
{
public readonly IEnumerable<string> Suggestions;
public InlineSuggestionsTag(IEnumerable<string> suggestions)
{
Suggestions = suggestions;
}
}
/// <summary>
/// Provides suggestions for the parameter.
/// </summary>
public sealed class SuggestionsAttribute : SuggestorTagAttribute
{
private readonly IQcSuggestorTag[] _tags;
/// <param name="suggestions">String-convertible suggestions.</param>
public SuggestionsAttribute(params object[] suggestions)
{
InlineSuggestionsTag tag = new InlineSuggestionsTag(
suggestions.Select(o => o.ToString()));
_tags = new IQcSuggestorTag[] { tag };
}
public override IQcSuggestorTag[] GetSuggestorTags() => _tags;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a01c376fe68c3c3498d2b9e431470c5c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
namespace QFSW.QC.Suggestors.Tags
{
public struct SceneNameTag : IQcSuggestorTag
{
public bool LoadedOnly;
}
/// <summary>
/// Specifies that scene name values should be suggested for the parameter.
/// </summary>
public sealed class SceneNameAttribute : SuggestorTagAttribute
{
/// <summary>
/// If true, only loaded scenes will be suggested.
/// </summary>
public bool LoadedOnly
{
get => _tag.LoadedOnly;
set => _tag.LoadedOnly = value;
}
private SceneNameTag _tag;
public override IQcSuggestorTag[] GetSuggestorTags()
{
return new IQcSuggestorTag[] { _tag };
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9ba203ccdff5c994eb35ca0c491659cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: