欢迎光临散文网 会员登陆 & 注册

专栏C#代码高亮

2019-05-10 17:32 作者:九条可怜酱  | 我要投稿

B站专栏是没有自带代码高亮功能的,通过拷贝特定的html到专栏编辑器可以实现代码高亮。

可惜支持的颜色数量很少,只有专栏自带的文字颜色和prism.js中的几种颜色:

专栏自带的颜色

代码高亮效果如下:

using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using C = System.Console;
using static System.Console;

namespace MyNamespace {
 class Program {
   [Flags]
   enum RGB { R = 1, G = 2, B = 4 }

   [StructLayout(LayoutKind.Sequential)]
   struct Point : IEquatable<Point> {
     public static readonly Point Empty = default;

     public int X;
     public int Y;

     public Point(int x, int y) => (X, Y) = (x, y);

     public void Deconstruct(out int x, out int y)
       => (x, y) = (X, Y);

     bool IEquatable<Point>.Equals(Point other) {
       return X == other.X && Y == other.Y;
     }

     public override string ToString()
       => this switch {
         (0, 0) => "原点",
         { X: 1, Y: 0 } => "(1, 0)",
         Point{ X: 0, Y: 1 } => "(0, 1)",
         _ => $"({X}, {Y})"
       };

     public static implicit operator Point((int X, int Y) p)
       => new Point(p.X, p.Y);
   }

   delegate void PrintHandler(object obj);
   /// <summary>
   /// 参考<see cref="Console.WriteLine(object)"/>
   /// </summary>
   static readonly PrintHandler Print = Console.WriteLine;

   unsafe static void Main(string[] args) {
     PrintEnumNames<RGB>();

     Span<Point> points = stackalloc[] {
       new Point(1, 1),
       new Point{ X = 2, Y = 3 },
       Point.Empty,
     };
     PrintSpan(points);
     WriteLine(sizeof(Point));
     WriteLine(typeof(Point));
     WriteLine(nameof(Point));

     using var mem = new System.IO.MemoryStream();
     int i = 0;
     while (true) {
       if (i >= 256) break;
       else {
         mem.WriteByte((byte)i);
         i++;
       }
     }

     using (var mem2 = new System.IO.MemoryStream()) {
       for (i = 0; i <= 0xff; i++) {
         mem2.WriteByte((Byte)i);
       }
     }

     try {
       throw new Exception();
     } catch (Exception e) when (true) {
       switch (e) {
         case ArgumentException{ ParamName: nameof(args) }:
           Console.WriteLine("hello");
           break;
         default: throw e;
       }
     } finally {

     }
   }

   static void PrintSpan<T>(Span<T> structs) where T : unmanaged {
     foreach (ref readonly var s in structs) {
       Print(s);
     }
     unsafe {
       fixed (T* p = structs) {
         for (int i = 0; i < structs.Length; i++) {
           Print(p[i]);
         }
       }
     }
   }

   /// <summary>  
   /// 输出所有枚举成员名称  
   /// </summary>
   /// <typeparam name="TEnum">枚举类型</typeparam>
   static void PrintEnumNames<TEnum>() where TEnum : Enum {
     foreach (var name in Enum.GetNames(typeof(TEnum))) {
       C.WriteLine(name);
     }
   }

 }

 unsafe public static class FastMath {
   const int LogTableLevel = 13; // 64KB
   static readonly double* LogTable = (double*)
     Marshal.AllocHGlobal((1 << LogTableLevel) * sizeof(double));

   static FastMath() {
     const int N = 1 << LogTableLevel;
     for (int i = 0; i < N; i++) {
       LogTable[i] = Math.Log(1 + (double)i / N, 2);
     }
   }

   /// <summary>
   /// 快速log2
   /// </summary>
   /// <param name="x"><paramref name="x"/>必须大于0</param>
   [MethodImpl(MethodImplOptions.AggressiveInlining)]
   public static double Log2(double x) {
     const int N = 1 << LogTableLevel;
     ulong t = *(ulong*)&x;
     int exp = (int)(t >> 52) - 0x3ff;
     return LogTable[(t >> (52 - LogTableLevel)) & (N - 1)] + exp;
   }
 }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Calculator {
 using Expr = Expression;
 using ParamMap = Dictionary<string, ParameterExpression>;

 class Program {
   /// <summary>
   /// 表达式解析器
   /// </summary>
   /// <remarks>
   /// define
   ///   : param* ':' addSub
   ///   ;
   /// param
   ///   : Id
   ///   ;
   /// addSub
   ///   : mulDiv (('+'|'-') mulDiv)*
   ///   ;
   /// mulDiv
   ///   : pow (('*'|'/') pow)*
   ///   ;
   /// pow
   ///   : unary ('^' pow)?
   ///   ;
   /// unary
   ///   : ('+'|'-') unary
   ///   | atom
   ///   ;
   /// atom
   ///   : Id
   ///   | Number
   ///   ;
   /// </remarks>
   class Parser {
     public class ParseException : Exception {
       public ParseException(int index, string message)
         : base($"位置({index})解析错误,{message}") { }
     }

     static readonly MethodInfo powMethod = typeof(Math).GetMethod("Pow");
     /// <summary>
     /// 匹配Id或Number的正则表达式
     /// </summary>
     static readonly Regex idOrNumRegex
       = new Regex(@"^((?<id>[a-zA-Z_][a-zA-Z_\d]*)|(\d*\.\d+|\d+\.?)(e[+\-]?\d+)?)",
         RegexOptions.Compiled | RegexOptions.ExplicitCapture);


     readonly ParamMap @params = new ParamMap();
     readonly string expr;
     int i = 0;

     Parser(string expr) {
       this.expr = expr;
       ParseMulDiv = GenerateBinaryParser(ParsePow,
         ("*", (x, y) => Expr.Multiply(x, y)),
         ("/", (x, y) => Expr.Divide(x, y)));
       ParseAddSub = GenerateBinaryParser(ParseMulDiv,
         ("+", (x, y) => Expr.Add(x, y)),
         ("-", (x, y) => Expr.Subtract(x, y)));
     }

     /// <summary>
     /// 跳过空白字符
     /// </summary>
     void SkipWhiteSpace() {
       while (i < expr.Length && char.IsWhiteSpace(expr[i])) i++;
     }

     /// <summary>
     /// 当前匹配位置之后如果是<paramref name="s"/>则吃掉
     /// </summary>
     bool Eat(string s) {
       SkipWhiteSpace();
       if (i + s.Length <= expr.Length && expr.AsSpan(i, s.Length).SequenceEqual(s)) {
         i += s.Length;
         return true;
       }
       return false;
     }

     /// <summary>
     /// 生成左结合的二元表达式解析器
     /// </summary>
     /// <param name="nextParser">下一级解析器</param>
     /// <param name="ops"></param>
     Func<Expr> GenerateBinaryParser(Func<Expr> nextParser,
       params (string Op, Func<Expr, Expr, Expr> Creator)[] ops) {
       return () => {
         var left = nextParser();
         int index;
         while ((index = Array.FindIndex(ops, p => Eat(p.Op))) >= 0) {
           left = ops[index].Creator(left, nextParser());
         }
         return left;
       };
     }

     /// <summary>
     /// param
     /// </summary>
     ParameterExpression ParseParam() {
       SkipWhiteSpace();
       var group = idOrNumRegex.Match(expr, i, expr.Length - i).Groups["id"];
       if (group.Success) {
         i += group.Length;
         return Expr.Parameter(typeof(double), group.Value);
       }
       return null;
     }

     /// <summary>
     /// define
     /// </summary>
     LambdaExpression ParseDefine() {
       while (ParseParam() is ParameterExpression param) {
         @params.Add(param.Name, param);
       }
       if (!Eat(":")) throw new ParseException(i, "期望: ':'");
       return Expr.Lambda(ParseAddSub(), @params.Values);
     }

     /// <summary>
     /// addSub
     /// </summary>
     readonly Func<Expr> ParseAddSub;
     /// <summary>
     /// mulDiv
     /// </summary>
     readonly Func<Expr> ParseMulDiv;

     /// <summary>
     /// pow
     /// </summary>
     Expr ParsePow() {
       var left = ParseUnary();
       return Eat("^") ? Expr.Call(powMethod, left, ParsePow()) : left;
     }

     /// <summary>
     /// unary
     /// </summary>
     Expr ParseUnary() {
       if (Eat("-")) return Expr.Negate(ParseUnary());
       if (Eat("+")) return ParseUnary();
       return ParseAtom();
     }

     /// <summary>
     /// atom
     /// </summary>
     Expr ParseAtom() {
       SkipWhiteSpace();
       var m = idOrNumRegex.Match(expr, i, expr.Length - i);
       if (m.Success) {
         i += m.Length;
         if (m.Groups["id"].Success) {
           if (@params.TryGetValue(m.Groups["id"].Value, out var param)) {
             return param;
           }
           throw new ParseException(i, $"未定义的参数:{m.Groups["id"].Value}");
         } else {
           return Expr.Constant(double.Parse(m.Value), typeof(double));
         }
       } else if (Eat("(")) {
         var result = ParseAddSub();
         if (!Eat(")")) throw new ParseException(i, "期望: ')'");
         return result;
       }
       throw new ParseException(i, "期望: '(' 或 <参数> 或 <数字>");
     }

     public static LambdaExpression Parse(string expr) {
       var parser = new Parser(expr);
       var result = parser.ParseDefine();
       parser.SkipWhiteSpace();
       if (parser.i != expr.Length)
         throw new ParseException(parser.i, "未能完全解析表达式");
       return result;
     }
   }

   delegate double CalcHandler(params double[] args);

   static CalcHandler Lambda(string expr) {
     var lambda = Parser.Parse(expr);
     var paramCount = lambda.Parameters.Count;
     var paramsParam = Expr.Parameter(typeof(double[]));
     var invoke = Expr.Invoke(lambda, Enumerable.Range(0, paramCount)
       .Select(i => Expr.ArrayIndex(paramsParam, Expr.Constant(i))));
     return Expr.Lambda<CalcHandler>(invoke, paramsParam).Compile();
   }

   static TDelegate Lambda<TDelegate>(string expr) where TDelegate : Delegate
     => (TDelegate)Parser.Parse(expr).Compile();

   static void Main(string[] args) {
     static void Print(double value) => Console.WriteLine(value);

     Print(Lambda(": 233")()); // 233
     Print(Lambda("x y: x + y")(22, 33)); // 55
     Print(Lambda("a b c: -a - b - c")(1, 2, 3)); // -6
     Print(Lambda("x1 y1 x2 y2: ((x1 - x2) ^ 2 + (y1 - y2) ^ 2) ^ 0.5")(0, 0, 3, 4)); // 5
     Print(Lambda("x1 y1 x2 y2: (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)")(0, 0, 3, 4)); // 25
     Print(Lambda<Func<double>>(": 2 ^ 3 ^ 2")()); // 512
   }
 }
}


具体操作

首先到github上clone这个项目:https://github.com/ibukisaar/BilibiliCSharpHighlight

注意:该项目使用VS2019和.NET Core 3.0

使用如下代码生成html并保存到文件。

string code = File.ReadAllText("test.cs");
var highlight = new BilibiliCSharpHighlight(
 code, // 代码
 showError: true, // 如果代码有错误则提示
 withReferences: null // 添加dll引用
 );
var html = highlight.ToHtml();
// html保存到 Z:\highlight\csharp.html
File.WriteAllText(@"Z:\highlight\csharp.html",
$@"<html>
<head>
<link rel=""stylesheet"" type=""text/css"" href=""./prism.css"" />
</head>
<body>
<figure class=""code-box"">
<pre class="" language-csharp"" data-lang=""application/csharp@cs@CSharp""
><code class="" language-csharp"">{html}</code></pre>
</figure>
</body>
</html>
");

将css目录中的prism.css放到和csharp.html相同的目录。

使用浏览器预览csharp.html内容。

最后从浏览器选中代码片段并复制,粘贴到专栏编辑器即可。


专栏C#代码高亮的评论 (共 条)

分享到微博请遵守国家法律