in resharper/resharper-unity/src/Unity.Shaders/HlslSupport/Daemon/Highlightings/ShaderVariantHighlightStage.cs [53:326]
file class ShaderKeywordsHighlightProcess(IDaemonProcess process, IContextBoundSettingsStore settingsStore, CppFile file, ShaderProgramInfo shaderProgramInfo, ShaderApi shaderApi, ShaderPlatform shaderPlatform, ISet<string> enabledKeywords, IReadOnlyDictionary<string, PragmaCommand> pragmas, IInactiveShaderBranchHighlightFactory? inactiveShaderBranchHighlightFactory)
: IDaemonStageProcess, IRecursiveElementProcessor<IHighlightingConsumer>
{
private readonly Stack<BranchingState> myBranchingStack = new();
private BranchingState myBranching;
private ITreeNode? myDirectiveNode;
public IDaemonProcess DaemonProcess { get; } = process;
public void Execute(Action<DaemonStageResult> committer)
{
using var activityCookie = Interruption.Current.Add(new PollingInterruptionSource(() => DaemonProcess.InterruptFlag));
using var checkCookie = new CppEnsureCheckForInterruptCookie();
using var prioritisedCookie = new CppResolveMutexInterruptCookie(file);
using var compilationContextCookie = CompilationContextCookie.OverrideOrCreate(file.ResolveContext);
var consumer = new FilteringHighlightingConsumer(DaemonProcess.SourceFile, file, settingsStore);
try
{
file.ProcessDescendants(this, consumer);
}
catch (Exception e) when (!e.IsOperationCanceled())
{
e.AddSensitiveData("File", file.File.Location);
throw;
}
finally
{
myDirectiveNode = null;
}
committer(new DaemonStageResult(consumer.CollectHighlightings()));
}
public bool InteriorShouldBeProcessed(ITreeNode element, IHighlightingConsumer context) => element.NodeType != CppCompositeNodeTypes.MACRO_REF && (myDirectiveNode != null || element is not ICppDirective);
public bool IsProcessingFinished(IHighlightingConsumer context) => Interruption.Current.Check();
public void ProcessBeforeInterior(ITreeNode element, IHighlightingConsumer consumer)
{
if (myDirectiveNode == null)
{
switch (element)
{
case Directive { Head.NodeType: {} nodeType } directive:
if (nodeType == CppTokenNodeTypes.IFDEF_DIRECTIVE || nodeType == CppTokenNodeTypes.IFNDEF_DIRECTIVE || nodeType == CppTokenNodeTypes.IF_DIRECTIVE || nodeType == CppTokenNodeTypes.ELIF_DIRECTIVE)
myDirectiveNode = directive;
if (nodeType == CppTokenNodeTypes.IFDEF_DIRECTIVE || nodeType == CppTokenNodeTypes.IFNDEF_DIRECTIVE || nodeType == CppTokenNodeTypes.IF_DIRECTIVE)
{
myBranchingStack.Push(myBranching);
myBranching = new BranchingState();
}
else if (nodeType == CppTokenNodeTypes.ELIF_DIRECTIVE || nodeType == CppTokenNodeTypes.ELSE_DIRECTIVE)
{
HandleEndOfBranch(consumer);
myBranching.InactiveNode = null;
}
else if (nodeType == CppTokenNodeTypes.ENDIF_DIRECTIVE)
{
HandleEndOfBranch(consumer);
myBranching = myBranchingStack.TryPop();
}
break;
case PPPragmaDirective pragmaDirective:
VisitPragmaDirective(pragmaDirective, consumer);
break;
case { NodeType: {} nodeType } when nodeType == CppCompositeNodeTypes.CONDITIONALLY_NOT_COMPILED_FRAGMENT:
VisitInactiveNode(element);
break;
}
}
else
{
switch (element)
{
case MacroReference macroReference:
VisitMacroReference(macroReference, consumer);
break;
case CppIdentifierTokenNode identifier:
VisitIdentifier(identifier, consumer);
break;
}
}
}
public void ProcessAfterInterior(ITreeNode element, IHighlightingConsumer context)
{
if (ReferenceEquals(element, myDirectiveNode))
myDirectiveNode = null;
}
private void HandleEndOfBranch(IHighlightingConsumer consumer)
{
if (!(inactiveShaderBranchHighlightFactory != null
&& myBranching is { Keywords: {} keywords, InactiveNode: {} inactiveNode})) return;
var highlight = inactiveShaderBranchHighlightFactory.CreateInactiveShaderBranchHighlight(inactiveNode.GetDocumentRange(), keywords);
consumer.ConsumeHighlighting(new HighlightingInfo(highlight.CalculateRange(), highlight));
}
private void VisitPragmaDirective(PPPragmaDirective directive, IHighlightingConsumer consumer)
{
if (directive.PragmaNameNode is CppIdentifierTokenNode nameNode
&& pragmas.TryGetValue(nameNode.Name, out var pragmaCommand)
&& pragmaCommand is ShaderLabPragmaCommand { Info: { ShaderFeatureType: ShaderFeatureType.KeywordList or ShaderFeatureType.KeywordListWithDisabledVariantForSingleKeyword } info })
{
CppIdentifierTokenNode? enabledKeyword = null;
var items = new LocalList<(CppIdentifierTokenNode Keyword, bool Enabled)>();
for (var node = nameNode.GetNextMeaningfulSibling(); node != null && node.NodeType != CppTokenNodeTypes.END_OF_DIRECTIVE_CONTENT; node = node.GetNextMeaningfulSibling())
{
if (node is not CppIdentifierTokenNode { Name: var shaderKeyword } identifier) continue;
var enabled = enabledKeywords.Contains(shaderKeyword);
items.Add((identifier, enabled));
if (enabled && enabledKeyword == null)
enabledKeyword = identifier;
}
if (items.Count == 0)
return;
var index = 0;
IHighlighting highlighting;
if (enabledKeyword == null && (items.Count > 1 || info.ShaderFeatureType != ShaderFeatureType.KeywordListWithDisabledVariantForSingleKeyword))
{
highlighting = new ImplicitlyEnabledShaderKeywordHighlight(items[0].Keyword.Name, items[0].Keyword);
consumer.ConsumeHighlighting(new HighlightingInfo(highlighting.CalculateRange(), highlighting));
++index;
}
var suppressors = ImmutableArray.CreateBuilder<string>();
for (; index < items.Count; ++index)
{
var item = items[index];
var keyword = item.Keyword.Name;
if (item.Enabled)
{
highlighting = ReferenceEquals(item.Keyword, enabledKeyword) ? new EnabledShaderKeywordHighlight(keyword, item.Keyword) : new SuppressedShaderKeywordHighlight(keyword, item.Keyword, suppressors.MoveOrCopyToImmutableArray());
suppressors.Add(keyword);
}
else
highlighting = new DisabledShaderKeywordHighlight(keyword, item.Keyword);
consumer.ConsumeHighlighting(new HighlightingInfo(highlighting.CalculateRange(), highlighting));
}
}
}
private void VisitInactiveNode(ITreeNode inactiveNode)
{
if (myBranching.InactiveNode != null)
Logger.LogError("inactive node is already set");
myBranching.InactiveNode = inactiveNode;
}
private bool TryGetShaderFeatures(string symbol, out string keyword, out OneToListMap<string, ShaderFeature>.ValueCollection features)
{
keyword = symbol;
features = shaderProgramInfo.GetShaderFeatures(symbol);
if (features is { Count: 0 } && symbol.EndsWith(ShaderKeywordConventions.DECLARED_KEYWORD_SUFFIX))
{
keyword = symbol[^ShaderKeywordConventions.DECLARED_KEYWORD_SUFFIX.Length..];
features = shaderProgramInfo.GetShaderFeatures(symbol);
}
return features.Count > 0;
}
private void AddBranchKeyword(string keyword) => (myBranching.Keywords ??= new()).Add(keyword);
private void VisitMacroReference(MacroReference macroReference, IHighlightingConsumer consumer)
{
if (macroReference.GetReferencedSymbol() is { Substitution: "1", HasParameters: false } symbol
&& !symbol.Location.ContainingFile.IsValid())
{
var symbolName = symbol.Name;
bool isExplicit;
if (TryGetShaderFeatures(symbolName, out var shaderKeyword, out _))
{
symbolName = shaderKeyword;
isExplicit = enabledKeywords.Contains(shaderKeyword);
}
else if (ShaderDefineSymbolsRecognizer.Recognize(symbolName) is { } descriptor)
{
isExplicit = !descriptor.IsDefaultSymbol(symbolName);
}
else
return;
AddBranchKeyword(symbolName);
ShaderKeywordHighlight highlight = isExplicit ? new EnabledShaderKeywordHighlight(symbolName, macroReference) : new ImplicitlyEnabledShaderKeywordHighlight(symbolName, macroReference);
Consume(consumer, highlight);
}
}
private void VisitIdentifier(CppIdentifierTokenNode identifierNode, IHighlightingConsumer consumer)
{
var symbolName = identifierNode.Name;
if (TryGetShaderFeatures(symbolName, out var shaderKeyword, out var features))
{
AddBranchKeyword(shaderKeyword);
Consume(consumer, GetKeywordIdentifierHighlighting(shaderKeyword, identifierNode, features));
}
else if (ShaderDefineSymbolsRecognizer.Recognize(symbolName) is { } descriptor)
{
AddBranchKeyword(symbolName);
ConsumeShaderDefineSymbolIdentifier(symbolName, identifierNode, descriptor, consumer);
}
}
private void ConsumeShaderDefineSymbolIdentifier(string symbol, CppIdentifierTokenNode identifierNode, IShaderDefineSymbolDescriptor descriptor, IHighlightingConsumer consumer)
{
var isEnabled = descriptor switch
{
ShaderApiDefineSymbolDescriptor shaderApiDescriptor => shaderApiDescriptor.GetValue(symbol) == shaderApi,
ShaderPlatformDefineSymbolDescriptor shaderPlatformDescriptor => shaderPlatformDescriptor.GetValue(symbol) == shaderPlatform,
_ => false
};
if (!isEnabled)
Consume(consumer, new DisabledShaderKeywordHighlight(symbol, identifierNode));
else if (descriptor.IsDefaultSymbol(symbol))
Consume(consumer, new ImplicitlyEnabledShaderKeywordHighlight(symbol, identifierNode));
else
Consume(consumer, new EnabledShaderKeywordHighlight(symbol, identifierNode));
}
private IHighlighting GetKeywordIdentifierHighlighting(string keyword, CppIdentifierTokenNode identifierNode, OneToListMap<string, ShaderFeature>.ValueCollection features)
{
if (enabledKeywords.Contains(keyword))
{
var suppressors = ImmutableArray.CreateBuilder<string>();
foreach (var feature in features)
{
var suppressedInFeature = false;
foreach (var entry in feature.Entries)
{
if (entry.Keyword == keyword)
{
if (!suppressedInFeature)
return new EnabledShaderKeywordHighlight(keyword, identifierNode);
break;
}
if (enabledKeywords.Contains(entry.Keyword))
{
suppressors.Add(entry.Keyword);
suppressedInFeature = true;
}
}
}
return new SuppressedShaderKeywordHighlight(keyword, identifierNode, suppressors.MoveOrCopyToImmutableArray());
}
foreach (var feature in features)
{
if (feature is { AllowAllDisabled: false, Entries.Length: > 0 } && feature.Entries[0].Keyword == keyword)
{
var otherKeywordEnabled = false;
for (var i = 1; i < feature.Entries.Length; ++i)
{
if (enabledKeywords.Contains(feature.Entries[i].Keyword))
{
otherKeywordEnabled = true;
break;
}
}
if (!otherKeywordEnabled)
return new ImplicitlyEnabledShaderKeywordHighlight(keyword, identifierNode);
}
}
return new DisabledShaderKeywordHighlight(keyword, identifierNode);
}
private void Consume(IHighlightingConsumer consumer, IHighlighting highlighting) => consumer.ConsumeHighlighting(new HighlightingInfo(highlighting.CalculateRange(), highlighting));
}