VS 插件和 Roslyn 增量生成器生成 Base64 预处理工具

VS 插件和 Roslyn 增量生成器生成 Base64 预处理工具

1. 前言

总之受人所托,具体编写过程可见 https://www.bilibili.com/video/BV1Gx4y1t7it

2. VS 插件版本

需要引入 NuGet 包 EnvDTE80

using EnvDTE;
using EnvDTE80;
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace base64it
{
    public static class ExtensionToMIMEConverter
    {
        // .jpg => image/jpeg
        // .jpeg => image/jpeg
        public static string Convert(string ext)
        {
            var extMineTypePairs = new[]
            {
               //(ext: ".jpg",  mimeType: "image/jpeg"),
               // alt 鼠标下拽
                (ext: ".jpg"    ,     mimeType: "image/jpeg"   ),
                (ext: ".jpeg"   ,     mimeType: "image/jpeg"    ),
                (ext: ".png"    ,     mimeType: "image/png"   ),
                (ext: ".gif"    ,     mimeType: "image/gif"  ) ,
            };

            var tuple = extMineTypePairs.FirstOrDefault(i => string.Equals(i.ext, ext, StringComparison.OrdinalIgnoreCase));

            if (string.IsNullOrWhiteSpace(tuple.ext)) return string.Empty;

            return tuple.mimeType;
        }

        public static string GetFileMimeType(string filePath)
        {
            var ext = Path.GetExtension(filePath);
            return Convert(ext);
        }
    }

    public static class DataURLConverter
    {
        public static string ConvertFromFile(string filePath)
        {
            var mimeType = ExtensionToMIMEConverter.GetFileMimeType(filePath);
            var bytes = File.ReadAllBytes(filePath);
            var base64Content = Convert.ToBase64String(bytes);

            return CreateDataURLBase64Format(mimeType, base64Content);
        }
        private static string CreateDataURLBase64Format(string mineType, string base64Content)
        {
            if (string.IsNullOrWhiteSpace(mineType)) return base64Content;
            return $"data:{mineType};base64,{base64Content}";
        }
    }

    internal class Program
    {

        static void Main(string[] args)
        {
            var dte = Marshal.GetActiveObject("VisualStudio.DTE") as DTE2;

            var toolWindows = dte.ToolWindows;

            var solutionExplorer = toolWindows.SolutionExplorer;
            var result = (solutionExplorer.SelectedItems as IEnumerable).OfType<UIHierarchyItem>();

            var selectedItem = result.FirstOrDefault();
            var projectItem = selectedItem.Object as ProjectItem;

            var selectedPath = projectItem.FileNames[0];

            var filePath = selectedPath;
            var base64DataUrlContent = DataURLConverter.ConvertFromFile(filePath);

            //Console.WriteLine(base64DataUrlContent);

            var base64TxtFilePath = GetBase64TxtFilePath(filePath);

            File.WriteAllText(base64TxtFilePath, base64DataUrlContent);

            System.Diagnostics.Process.Start(base64TxtFilePath);
        }

        private static string GetBase64TxtFilePath(string sourceFilePath)
        {
            var dir = Path.GetDirectoryName(sourceFilePath);
            var name = Path.GetFileNameWithoutExtension(sourceFilePath);
            return Path.Combine(dir, name + "base64.txt");
        }
    }
}

3. Roslyn 增量生成器

资源需要改成这种形式才能参与解析:
C# 分析器其它文件

using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace ResourceBase64CodeGen
{
    public static  class ExtensionToMIMEConverter
    {
        // .jpg => image/jpeg
        // .jpeg => image/jpeg
        public static string Convert(string ext)
        {
            var extMineTypePairs = new[]
            {
               //(ext: ".jpg",  mimeType: "image/jpeg"),
               // alt 鼠标下拽
                (ext: ".jpg"    ,     mimeType: "image/jpeg"   ),
                (ext: ".jpeg"   ,     mimeType: "image/jpeg"    ),
                (ext: ".png"    ,     mimeType: "image/png"   ),
                (ext: ".gif"    ,     mimeType: "image/gif"  ) ,
            };

            var tuple = extMineTypePairs.FirstOrDefault(i => string.Equals(i.ext, ext, StringComparison.OrdinalIgnoreCase));

            if (string.IsNullOrWhiteSpace(tuple.ext)) return string.Empty;

            return tuple.mimeType;
        }

        public static string GetFileMimeType(string filePath)
        {
            var ext = Path.GetExtension(filePath);
            return Convert(ext);
        }
    }

    public static class DataURLConverter
    {
        public static string ConvertFromFile(string filePath)
        {
            var mimeType = ExtensionToMIMEConverter.GetFileMimeType(filePath);
            var bytes = File.ReadAllBytes(filePath);
            var base64Content = Convert.ToBase64String(bytes);

            return CreateDataURLBase64Format(mimeType, base64Content);
        }
        private static string CreateDataURLBase64Format(string mineType, string base64Content)
        {
            if (string.IsNullOrWhiteSpace(mineType)) return base64Content;
            return $"data:{mineType};base64,{base64Content}";
        }
    }

    [Generator]
    public class ResourceBase64Generator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            var additionalFiles = context.AdditionalTextsProvider;

            context.RegisterSourceOutput(additionalFiles, (sourceProductionContext, file) =>
            {
                var csproj = GetCsProject(file.Path);
                CreateCode(sourceProductionContext, csproj, file.Path);
            });


            //context.AdditionalTextsProvider
        }

        public static void CreateCode(SourceProductionContext sourceProductionContext, string csproj, string resouceOrignPath)
        {
            GetNamespaceAndResourceName(csproj, resouceOrignPath, out var namespaceName, out var constName);
            var base64DataURL = DataURLConverter.ConvertFromFile(resouceOrignPath);
            var code = $@"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace {namespaceName}
{{
    public static partial class ResourceBase64
    {{
        public const string {constName} = ""{base64DataURL}"";
    }}
}}
";

            //Console.WriteLine(code);
            var autoCsName = $"{constName}.{namespaceName.Replace(".","_")}.cs";
            sourceProductionContext.AddSource(autoCsName, code);

        }

        private static void GetNamespaceAndResourceName(string csproj, string resourceOriginPath, out string namespaceName, out string constName)
        {
            // D:\Code\AvaloniaTest\Base64CodeGen\MainProject\MainProject.csproj
            // D:\Code\AvaloniaTest\Base64CodeGen\MainProject\Resources\miku.png
            // MainProject.Resources
            // miku

            var rootDir = Path.GetDirectoryName(csproj);
            // D:\Code\AvaloniaTest\Base64CodeGen\MainProject

            var resourcesDir = Path.GetDirectoryName(resourceOriginPath);
            // D:\Code\AvaloniaTest\Base64CodeGen\MainProject\Resources

            var subNamespaceToken = resourcesDir.Substring(rootDir.Length);
            var rootNamespaceToken = Path.GetFileNameWithoutExtension(csproj);

            var subNamespaceTokens = subNamespaceToken
                    .Split('/', '\\')
                    .Where(i=>string.IsNullOrWhiteSpace(i) == false);

            var list = new List<string>();
            list.Add(rootNamespaceToken);
            list.AddRange(subNamespaceTokens);

            namespaceName = string.Join(".", list);
            constName = Path.GetFileNameWithoutExtension(resourceOriginPath);
        }


        public static string GetCsProject(string originFile)
        {
            var parentFolder = Path.GetDirectoryName(originFile);
            var csproj = GetCsProjectWithFolder(parentFolder, 0);
            return csproj;
        }

        private static string GetCsProjectWithFolder(string folder, int depth)
        {
            if (depth >= 4) return string.Empty;
            if (HasCsProject(folder) ) return GetCsProjectInFolder(folder);
            var parentFolder = Path.GetDirectoryName(folder);
            return GetCsProjectWithFolder(parentFolder, depth + 1);
        }

        private static bool HasCsProject(string folder)
        {
            return !string.IsNullOrWhiteSpace(Directory.GetFiles(folder).FirstOrDefault(i => i.EndsWith(".csproj")));
        }

        private static string GetCsProjectInFolder(string folder)
        {
            return Directory.GetFiles(folder).FirstOrDefault(i => i.EndsWith(".csproj"));
        }

    }
}

posted @ 2024-06-21 18:51  fanbal  阅读(28)  评论(0)    收藏  举报