Skip to content

Commit

Permalink
Check in the initial Shell Copilot code base
Browse files Browse the repository at this point in the history
  • Loading branch information
daxian-dbw authored Sep 13, 2023
2 parents 37d3868 + 4b20d55 commit 6e94e8e
Show file tree
Hide file tree
Showing 87 changed files with 26,776 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
out/
bin/
obj/
.vs/
252 changes: 252 additions & 0 deletions shell/Markdown.VT/ColorCode.VT/VTSyntaxHighlighter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;

using ColorCode.Common;
using ColorCode.Parsing;
using ColorCode.Styling;

namespace ColorCode.VT;

/// <summary>
/// Creates a <see cref="VTSyntaxHighlighter"/>, for creating VT decorated string to display Syntax Highlighted code.
/// </summary>
public class VTSyntaxHighlighter : CodeColorizerBase
{
internal const string VTReset = "\x1b[0m";
internal const string VTItalic = "\x1b[3m";
internal const string VTBold = "\x1b[1m";

private readonly string _plainTextForeground;
private readonly string _plainTextBackground;

private readonly ILanguage[] _loadedLanguages;

/// <summary>
/// Creates a <see cref="VTSyntaxHighlighter"/>, for creating VT decorated string to display Syntax Highlighted code.
/// </summary>
/// <param name="styles">The Custom styles to Apply to the formatted Code.</param>
/// <param name="languageParser">The language parser that the <see cref="VTSyntaxHighlighter"/> instance will use for its lifetime.</param>
public VTSyntaxHighlighter(StyleDictionary styles = null, ILanguageParser languageParser = null)
: base(styles, languageParser)
{
if (styles.TryGetValue(ScopeName.PlainText, out Style style))
{
_plainTextForeground = style.Foreground.ToVTColor();
_plainTextBackground = style.Background.ToVTColor(isForeground: false);
}

_loadedLanguages = new ILanguage[0];
}

private TextWriter Writer { get; set; }

/// <summary>
/// Finds a loaded language by the specified identifier.
/// </summary>
public ILanguage FindLanguageById(string langId)
{
if (string.IsNullOrEmpty(langId))
{
return null;
}

foreach (ILanguage lang in _loadedLanguages)
{
if (lang.Id.ToLower() == langId.ToLower() || lang.HasAlias(langId))
{
return lang;
}
}

return Languages.FindById(langId);
}

/// <summary>
/// Creates the VT decorated string.
/// </summary>
/// <param name="sourceCode">The source code to colorize.</param>
/// <param name="language">The language to use to colorize the source code.</param>
/// <returns>VT decorated string.</returns>
public string GetVTString(string sourceCode, ILanguage language)
{
var buffer = new StringBuilder(sourceCode.Length * 2);
buffer.Append(_plainTextForeground).Append(_plainTextBackground);

using (TextWriter writer = new StringWriter(buffer))
{
Writer = writer;
languageParser.Parse(sourceCode, language, Write);
Writer.Flush();
}

buffer.Append(VTReset);
return buffer.ToString();
}

protected override void Write(string parsedSourceCode, IList<Scope> scopes)
{
var styleInsertions = new List<TextInsertion>();

foreach (Scope scope in scopes)
{
GetStyleInsertionsForCapturedStyle(scope, styleInsertions);
}

styleInsertions.SortStable((x, y) => x.Index.CompareTo(y.Index));

int offset = 0;

foreach (TextInsertion styleInsertion in styleInsertions)
{
var text = parsedSourceCode.AsSpan(offset, styleInsertion.Index - offset);
Writer.Write(text);
if (string.IsNullOrEmpty(styleInsertion.Text))
{
BuildSpanForCapturedStyle(styleInsertion.Scope);
}
else
{
Writer.Write(styleInsertion.Text);
}
offset = styleInsertion.Index;
}

Writer.Write(parsedSourceCode.AsSpan(offset));
}

private void GetStyleInsertionsForCapturedStyle(Scope scope, ICollection<TextInsertion> styleInsertions)
{
styleInsertions.Add(new TextInsertion
{
Index = scope.Index,
Scope = scope
});

foreach (Scope childScope in scope.Children)
{
GetStyleInsertionsForCapturedStyle(childScope, styleInsertions);
}

styleInsertions.Add(new TextInsertion
{
Index = scope.Index + scope.Length,
Text = $"{VTReset}{_plainTextForeground}{_plainTextBackground}"
});
}

private void BuildSpanForCapturedStyle(Scope scope)
{
string foreground = null;
string background = null;
bool italic = false;
bool bold = false;

if (Styles.Contains(scope.Name))
{
Style style = Styles[scope.Name];

foreground = style.Foreground;
background = style.Background;
italic = style.Italic;
bold = style.Bold;
}

foreground ??= _plainTextForeground;
background ??= _plainTextBackground;

WriteVTDecoration(foreground, background, italic, bold);
}

private void WriteVTDecoration(string foreground = null, string background = null, bool italic = false, bool bold = false)
{
if (!string.IsNullOrWhiteSpace(foreground))
{
Writer.Write(foreground.ToVTColor());
}

if (!string.IsNullOrWhiteSpace(background))
{
Writer.Write(background.ToVTColor(isForeground: false));
}

if (italic)
{
Writer.Write(VTItalic);
}

if (bold)
{
Writer.Write(VTBold);
}
}
}

public static class ColorExtensionMethods
{
public static string ToVTColor(this string color, bool isForeground = true)
{
if (color == null)
{
return null;
}

if (color.StartsWith("\x1b["))
{
return color;
}

if (color.StartsWith('#'))
{
var length = 6;
var start = color.Length - length;
var colorSpan = color.AsSpan(start, length);

if (int.TryParse(colorSpan, NumberStyles.HexNumber, provider: null, out int result))
{
return isForeground ? ForegroundFromRgb(result) : BackgroundFromRgb(result);
}
}

return null;
}

internal static string ForegroundFromRgb(int rgb)
{
byte red, green, blue;
blue = (byte)(rgb & 0xFF);
rgb >>= 8;
green = (byte)(rgb & 0xFF);
rgb >>= 8;
red = (byte)(rgb & 0xFF);

return $"\x1b[38;2;{red};{green};{blue}m";
}

public static string ForegroundFromRgb(byte red, byte green, byte blue)
{
return $"\x1b[38;2;{red};{green};{blue}m";
}

internal static string BackgroundFromRgb(int rgb)
{
byte red, green, blue;
blue = (byte)(rgb & 0xFF);
rgb >>= 8;
green = (byte)(rgb & 0xFF);
rgb >>= 8;
red = (byte)(rgb & 0xFF);

return $"\x1b[48;2;{red};{green};{blue}m";
}

public static string BackgroundFromRgb(byte red, byte green, byte blue)
{
return $"\x1b[48;2;{red};{green};{blue}m";
}
}
13 changes: 13 additions & 0 deletions shell/Markdown.VT/Markdown.VT.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Markdig" Version="0.31.0" />
<PackageReference Include="ColorCode.Core" Version="2.0.15" />
<PackageReference Include="Spectre.Console" Version="0.47.0" />
</ItemGroup>

</Project>
117 changes: 117 additions & 0 deletions shell/Markdown.VT/Render/Blocks/CodeBlockRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Text;

using ColorCode;
using ColorCode.Common;
using ColorCode.Styling;
using ColorCode.VT;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Syntax;

namespace Markdown.VT;

public class CodeBlockRenderer : VTObjectRenderer<CodeBlock>
{
private readonly VTSyntaxHighlighter _vtHighlighter;
private readonly string _plainTextForeground;
private readonly string _plainTextBackground;

public CodeBlockRenderer()
{
var styles = StyleDictionary.DefaultDark;
if (styles.TryGetValue(ScopeName.PlainText, out Style style))
{
_plainTextForeground = style.Foreground.ToVTColor();
_plainTextBackground = style.Background.ToVTColor(isForeground: false);
}
_vtHighlighter = new VTSyntaxHighlighter(styles);
}

protected override void Write(VTRenderer renderer, CodeBlock obj)
{
renderer.WriteLine($"{_plainTextForeground}{_plainTextBackground}");
renderer.PushIndentAndUpdateWidth(" ");

ILanguage language = null;
if (obj is FencedCodeBlock fencedCodeBlock && fencedCodeBlock.Info is string info)
{
string infoPrefix = (obj.Parser as FencedCodeBlockParser)?.InfoPrefix ?? FencedCodeBlockParser.DefaultInfoPrefix;
string langId = info.StartsWith(infoPrefix, StringComparison.Ordinal) ? info.Substring(infoPrefix.Length) : info;

language = _vtHighlighter.FindLanguageById(langId);
}

// Call the visitor with the original code.
string code = ExtractCode(obj);
renderer.Visitor?.VisitCodeBlock(code);

if (language is null)
{
renderer.WriteLeafRawLines(obj);
}
else
{
string vtText = _vtHighlighter.GetVTString(code, language);

int start = 0;
while (true)
{
if (start == vtText.Length)
{
break;
}

int nlIndex = vtText.IndexOf('\n', start);
int length = nlIndex is -1 ? vtText.Length - start : nlIndex - start + 1;
var span = vtText.AsSpan(start, length);

// Call 'WriteLine' explicitly to make sure the indentation is applied.
renderer.Write(span.Trim('\n'));
renderer.WriteLine();

if (nlIndex is -1)
{
break;
}

start = nlIndex + 1;
}
}

renderer.PopIndentAndUpdateWidth();
renderer.EnsureLine();
}

private static string ExtractCode(LeafBlock leafBlock)
{
var code = new StringBuilder();
var lines = leafBlock.Lines.Lines ?? Array.Empty<StringLine>();
var totalLines = lines.Length;

for (var index = 0; index < totalLines; index++)
{
var line = lines[index];
var slice = line.Slice;

if (slice.Text == null)
{
continue;
}

var lineText = slice.Text.Substring(slice.Start, slice.Length);

if (index > 0)
{
code.AppendLine();
}

code.Append(lineText);
}

return code.ToString();
}
}
Loading

0 comments on commit 6e94e8e

Please sign in to comment.