|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Immutable;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.ColorSchemes;
internal partial class ColorSchemeApplier
{
private static class ColorSchemeReader
{
private static readonly XmlReaderSettings s_xmlSettings = new() { DtdProcessing = DtdProcessing.Prohibit };
private const string RawColorType = nameof(__VSCOLORTYPE.CT_RAW);
private const string SystemColorType = nameof(__VSCOLORTYPE.CT_SYSCOLOR);
public static ColorScheme ReadColorScheme(Stream schemeStream)
{
using var xmlReader = XmlReader.Create(schemeStream, s_xmlSettings);
var schemeDocument = XDocument.Load(xmlReader);
var themes = schemeDocument
.Descendants("Theme")
.Select(ReadColorTheme);
return new ColorScheme(themes.ToImmutableArray());
}
private static ColorTheme ReadColorTheme(XElement themeElement)
{
var themeName = (string)themeElement.Attribute("Name");
var themeGuid = Guid.Parse((string)themeElement.Attribute("GUID"));
var categoryElement = themeElement.Descendants("Category").Single();
var category = ReadColorCategory(categoryElement);
return new ColorTheme(themeName, themeGuid, category);
}
private static ColorCategory ReadColorCategory(XElement categoryElement)
{
var categoryName = (string)categoryElement.Attribute("Name");
var categoryGuid = Guid.Parse((string)categoryElement.Attribute("GUID"));
var colorItems = categoryElement
.Descendants("Color")
.Select(ReadColorItem)
.WhereNotNull();
return new ColorCategory(categoryName, categoryGuid, colorItems.ToImmutableArray());
}
private static ColorItem? ReadColorItem(XElement colorElement)
{
var name = (string)colorElement.Attribute("Name");
var backgroundElement = colorElement.Descendants("Background").SingleOrDefault();
(var backgroundType, var backgroundColor) = backgroundElement is object
? ReadColor(backgroundElement)
: (__VSCOLORTYPE.CT_INVALID, (uint?)null);
var foregroundElement = colorElement.Descendants("Foreground").SingleOrDefault();
(var foregroundType, var foregroundColor) = foregroundElement is object
? ReadColor(foregroundElement)
: (__VSCOLORTYPE.CT_INVALID, (uint?)null);
if (backgroundElement is null && foregroundElement is null)
{
return null;
}
return new ColorItem(name, backgroundType, backgroundColor, foregroundType, foregroundColor);
}
private static (__VSCOLORTYPE Type, uint Color) ReadColor(XElement colorElement)
{
var colorType = (string)colorElement.Attribute("Type");
var sourceColor = (string)colorElement.Attribute("Source");
__VSCOLORTYPE type;
uint color;
if (colorType == RawColorType)
{
type = __VSCOLORTYPE.CT_RAW;
// The ColorableItemInfo returned by the FontAndColorStorage retuns RGB color information as 0x00BBGGRR.
// Optimize for color comparisons by converting ARGB to BGR by ignoring the alpha channel and reversing byte order.
var r = sourceColor.Substring(2, 2);
var g = sourceColor.Substring(4, 2);
var b = sourceColor.Substring(6, 2);
color = uint.Parse($"{b}{g}{r}", NumberStyles.HexNumber);
}
else if (colorType == SystemColorType)
{
type = __VSCOLORTYPE.CT_SYSCOLOR;
color = uint.Parse(sourceColor, NumberStyles.HexNumber);
}
else
{
throw ExceptionUtilities.UnexpectedValue(colorType);
}
return (type, color);
}
}
}
|