mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-15 01:25:40 -05:00
Export to Markdown Support (#1705)
* Adding file writer for Markdown tables. No testing yet. * Unit tests for the markdown writer * Wiring up the factory and and request types * Wiring up changes for Markdown serialization in serialization service * Couple last minute tweaks * Changes as per PR comments * Revert temp testing code. 🙈 * Fluent assertions in SerializationServiceTests.cs Co-authored-by: Ben Russell <russellben@microsoft.com>
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
|
||||
{
|
||||
/// <summary>
|
||||
/// Writer for exporting results to a Markdown table.
|
||||
/// </summary>
|
||||
public class SaveAsMarkdownFileStreamWriter : SaveAsStreamWriter
|
||||
{
|
||||
private const string Delimiter = "|";
|
||||
private static readonly Regex NewlineRegex = new Regex(@"(\r\n|\n|\r)", RegexOptions.Compiled);
|
||||
|
||||
private readonly Encoding _encoding;
|
||||
private readonly string _lineSeparator;
|
||||
|
||||
public SaveAsMarkdownFileStreamWriter(
|
||||
Stream stream,
|
||||
SaveResultsAsMarkdownRequestParams requestParams,
|
||||
IReadOnlyList<DbColumnWrapper> columns)
|
||||
: base(stream, requestParams, columns)
|
||||
{
|
||||
// Parse the request params
|
||||
this._lineSeparator = string.IsNullOrEmpty(requestParams.LineSeparator)
|
||||
? Environment.NewLine
|
||||
: requestParams.LineSeparator;
|
||||
this._encoding = ParseEncoding(requestParams.Encoding, Encoding.UTF8);
|
||||
|
||||
// Output the header if requested
|
||||
if (requestParams.IncludeHeaders)
|
||||
{
|
||||
// Write the column header
|
||||
IEnumerable<string> selectedColumnNames = columns.Skip(this.ColumnStartIndex)
|
||||
.Take(this.ColumnCount)
|
||||
.Select(c => EncodeMarkdownField(c.ColumnName));
|
||||
string headerLine = string.Join(Delimiter, selectedColumnNames);
|
||||
|
||||
this.WriteLine($"{Delimiter}{headerLine}{Delimiter}");
|
||||
|
||||
// Write the separator row
|
||||
var separatorBuilder = new StringBuilder(Delimiter);
|
||||
for (int i = 0; i < this.ColumnCount; i++)
|
||||
{
|
||||
separatorBuilder.Append($"---{Delimiter}");
|
||||
}
|
||||
|
||||
this.WriteLine(separatorBuilder.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteRow(IList<DbCellValue> row, IReadOnlyList<DbColumnWrapper> columns)
|
||||
{
|
||||
IEnumerable<string> selectedCells = row.Skip(this.ColumnStartIndex)
|
||||
.Take(this.ColumnCount)
|
||||
.Select(c => EncodeMarkdownField(c.DisplayValue));
|
||||
string rowLine = string.Join(Delimiter, selectedCells);
|
||||
|
||||
this.WriteLine($"{Delimiter}{rowLine}{Delimiter}");
|
||||
}
|
||||
|
||||
internal static string EncodeMarkdownField(string? field)
|
||||
{
|
||||
// Special case for nulls
|
||||
if (field == null)
|
||||
{
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
// Escape HTML entities, since Markdown supports inline HTML
|
||||
field = HttpUtility.HtmlEncode(field);
|
||||
|
||||
// Escape pipe delimiters
|
||||
field = field.Replace(@"|", @"\|");
|
||||
|
||||
// @TODO: Allow option to encode multiple whitespace characters as
|
||||
|
||||
// Replace newlines with br tags, since cell values must be single line
|
||||
field = NewlineRegex.Replace(field, @"<br />");
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
private void WriteLine(string line)
|
||||
{
|
||||
byte[] bytes = this._encoding.GetBytes(line + this._lineSeparator);
|
||||
this.FileStream.Write(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user