/// <summary> /// Returns a context value containing StringElements constructed from the TElement types /// that get found, plus a SmartPointerElement if the corresponding SmartPointerType is /// present, and the Natvis tokens to resolve in expressions involving those. /// </summary> FormatStringContext BuildFormatStringContext <TElement>( IVariableInformation variable, Func <TElement, IStringElement> stringElementConstructor) where TElement : class { VisualizerInfo visualizer = _visualizerScanner.FindType(variable); if (visualizer?.Visualizer.Items == null) { return(new FormatStringContext()); } object[] items = visualizer.Visualizer.Items; IEnumerable <IStringElement> stringElements = items.OfType <TElement>().Select(e => stringElementConstructor((TElement)e)); // Fall back to the smart pointee. stringElements = stringElements.Concat( items.OfType <SmartPointerType>() .Take(1) .Where(e => !string.IsNullOrWhiteSpace(e.Value)) .Select(e => new SmartPointerStringElement(e))); return(new FormatStringContext { StringElements = stringElements, NatvisScope = visualizer.NatvisScope }); }
VisualizerInfo Scan(string varTypeName, TypeName typeNameToFind) { // Iterate list in reverse order, so that files loaded later take precedence. // In particular, CustomVisualizers can be overridden by user files. // TODO: Ordering is a bit brittle, consider using another way to make // CustomVisualizers overridable, e.g. priority, a custom built-in flag or storing // built-in Natvis in a file that can be changed by users. _visualizerCache[varTypeName] = null; for (int index = _typeVisualizers.Count - 1; index >= 0; --index) { FileInfo fileInfo = _typeVisualizers[index]; // TODO: match on View, version, etc TypeInfo visualizer = fileInfo.Visualizers.Find(v => v.ParsedName.Match(typeNameToFind)); if (visualizer != null) { _visualizerCache[varTypeName] = new VisualizerInfo(visualizer.Visualizer, typeNameToFind); break; } } return(_visualizerCache[varTypeName]); }
internal bool MightHaveChildren(IVariableInformation variable) { VisualizerInfo visualizer = VisualizerScanner.FindType(variable); ExpandType expandType = visualizer?.GetExpandType(); if (expandType != null) { if (expandType.Items != null && expandType.Items.Length > 0) { return(true); } return(!expandType.HideRawView && variable.MightHaveChildren()); } SmartPointerType smartPointerType = visualizer?.GetSmartPointerType(); return(smartPointerType != null || variable.MightHaveChildren()); }
IChildAdapter CreateNatvisChildAdapter(VisualizerInfo visualizer, IVariableInformation variable) { ExpandType expandType = visualizer.GetExpandType(); if (expandType != null) { return(_natvisCollectionFactory.Create(variable, expandType, visualizer.NatvisScope)); } SmartPointerType smartPointerType = visualizer.GetSmartPointerType(); if (smartPointerType != null) { return(_smartPointerFactory.Create( variable, smartPointerType, visualizer.NatvisScope, variable.GetChildAdapter())); } return(variable.GetChildAdapter()); }
internal IChildAdapter GetChildAdapter(IVariableInformation variable) { VisualizerInfo visualizer = VisualizerScanner.FindType(variable); if (visualizer?.Visualizer.Items == null) { return(variable.GetChildAdapter()); } IChildAdapter childAdapter = CreateNatvisChildAdapter(visualizer, variable); // Special case for SSE registers. VS calls VariableInformationEnum.Count, even if // the SSE registers are not visible. Without this, we would have to expand all // children just to get the count, which slows down the register window a lot. if (variable.CustomVisualizer == CustomVisualizer.SSE || variable.CustomVisualizer == CustomVisualizer.SSE2) { return(new SseAdapter(visualizer.GetExpandType().Items.Length, childAdapter)); } return(childAdapter); }
/// <summary> /// Find Natvis visualizer by variable. /// </summary> public VisualizerInfo FindType(IVariableInformation variable) { // Check for custom visualizers first. if (variable.CustomVisualizer != CustomVisualizer.None) { // Custom visualizers are keyed by pseudo-types "$" + CustomVisualizer enum name. string pseudoTypeName = "$" + variable.CustomVisualizer; VisualizerInfo visualizer; if (_visualizerCache.TryGetValue(pseudoTypeName, out visualizer)) { _logger.Verbose(() => $"Selected cached custom Natvis Visualizer " + $"'{visualizer?.Visualizer.Name ?? "null"} '" + $"for custom visualizer '{variable.CustomVisualizer}'"); return(visualizer); } visualizer = Scan(pseudoTypeName, TypeName.Parse(pseudoTypeName)); if (visualizer != null) { _logger.Verbose(() => $"Selected Natvis Visualizer " + $"'{visualizer.Visualizer.Name}'" + $" for custom visualizer '{variable.CustomVisualizer}'"); return(visualizer); } } string initialTypeName = variable.TypeName; _logger.Verbose($"Finding Natvis Visualizer for type '{initialTypeName}'"); if (_visualizerCache.ContainsKey(initialTypeName)) { VisualizerInfo visualizer = _visualizerCache[variable.TypeName]; _logger.Verbose(() => $"Selected cached Natvis Visualizer " + $"'{visualizer?.Visualizer.Name ?? "null"}' for type " + $"'{initialTypeName}'"); return(visualizer); } uint count = 0; foreach (string typeName in variable.GetAllInheritedTypes()) { var parsedName = TypeName.Parse(typeName); if (parsedName == null) { break; } _logger.Verbose( () => $"Scanning for Natvis Visualizer for type '{parsedName.BaseName}' for " + $"variable of type '{initialTypeName}'"); VisualizerInfo visualizer = Scan(initialTypeName, parsedName); if (visualizer != null) { _logger.Verbose( () => $"Selected Natvis Visualizer '{visualizer.Visualizer.Name}'" + $" for type '{initialTypeName}'"); return(visualizer); } ++count; // Safety check to make sure we don't get in to an expensive loop. if (count > _maxInheritedTypes) { _logger.Warning($"The '{initialTypeName}' type exceeds the" + $" maximum number ({_maxInheritedTypes}) of searchable base " + $"classes."); break; } } _logger.Verbose($"No Natvis Visualizer found for type '{initialTypeName}'"); return(null); }