Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add formatter for the charset .editorconfig option #330

Merged
merged 1 commit into from
Aug 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CodeFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ internal static class CodeFormatter
new WhitespaceFormatter(),
new FinalNewlineFormatter(),
new EndOfLineFormatter(),
new CharsetFormatter(),
}.ToImmutableArray();

public static async Task<WorkspaceFormatResult> FormatWorkspaceAsync(
Expand Down
71 changes: 71 additions & 0 deletions src/Formatters/CharsetFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.CodingConventions;

namespace Microsoft.CodeAnalysis.Tools.Formatters
{
internal sealed class CharsetFormatter : DocumentFormatter
{
protected override string FormatWarningDescription => Resources.Fix_file_encoding;

private static Encoding Utf8 => new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
private static Encoding Latin1 => Encoding.GetEncoding("iso-8859-1");

protected override Task<SourceText> FormatFileAsync(
Document document,
SourceText sourceText,
OptionSet options,
ICodingConventionsSnapshot codingConventions,
FormatOptions formatOptions,
ILogger logger,
CancellationToken cancellationToken)
{
return Task.Run(() =>
{
if (!TryGetCharset(codingConventions, out var encoding)
|| sourceText.Encoding.Equals(encoding))
{
return sourceText;
}

return SourceText.From(sourceText.ToString(), encoding);
});
}

private static bool TryGetCharset(ICodingConventionsSnapshot codingConventions, out Encoding encoding)
{
if (codingConventions.TryGetConventionValue("charset", out string charsetOption))
{
encoding = GetCharset(charsetOption);
return true;
}

encoding = null;
return false;
}

public static Encoding GetCharset(string charsetOption)
{
switch (charsetOption)
{
case "latin1":
return Latin1;
case "utf-8-bom":
return Encoding.UTF8; // UTF-8 with BOM Marker
case "utf-16be":
return Encoding.BigEndianUnicode; // Big Endian with BOM Marker
case "utf-16le":
return Encoding.Unicode; // Little Endian with BOM Marker
case "utf-8":
default:
return Utf8;
}
}
}
}
4 changes: 2 additions & 2 deletions src/Formatters/DocumentFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ protected abstract Task<SourceText> FormatFileAsync(
var originalSourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var formattedSourceText = await FormatFileAsync(document, originalSourceText, options, codingConventions, formatOptions, logger, cancellationToken).ConfigureAwait(false);

return !formattedSourceText.ContentEquals(originalSourceText)
return !formattedSourceText.ContentEquals(originalSourceText) || !formattedSourceText.Encoding.Equals(originalSourceText.Encoding)
? (originalSourceText, formattedSourceText)
: (originalSourceText, null);
}
Expand Down Expand Up @@ -117,7 +117,7 @@ private async Task<Solution> ApplyFileChangesAsync(
LogFormattingChanges(formatOptions.WorkspaceFilePath, document.FilePath, originalText, formattedText, formatOptions.ChangesAreErrors, logger);
}

formattedSolution = formattedSolution.WithDocumentText(document.Id, formattedText);
formattedSolution = formattedSolution.WithDocumentText(document.Id, formattedText, PreservationMode.PreserveIdentity);
}

return formattedSolution;
Expand Down
3 changes: 3 additions & 0 deletions src/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,7 @@
<data name="Fix_whitespace_formatting" xml:space="preserve">
<value>Fix whitespace formatting.</value>
</data>
<data name="Fix_file_encoding" xml:space="preserve">
<value>Fix file encoding.</value>
</data>
</root>
5 changes: 5 additions & 0 deletions src/xlf/Resources.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.ja.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.ko.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.pl.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.pt-BR.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.ru.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.tr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.zh-Hans.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/xlf/Resources.zh-Hant.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<target state="new">Fix end of line marker.</target>
<note />
</trans-unit>
<trans-unit id="Fix_file_encoding">
<source>Fix file encoding.</source>
<target state="new">Fix file encoding.</target>
<note />
</trans-unit>
<trans-unit id="Fix_whitespace_formatting">
<source>Fix whitespace formatting.</source>
<target state="new">Fix whitespace formatting.</target>
Expand Down
65 changes: 38 additions & 27 deletions tests/Formatters/AbstractFormatterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host.Mef;
Expand Down Expand Up @@ -66,50 +67,67 @@ protected AbstractFormatterTest()
/// </value>
public abstract string Language { get; }

private string TestCode
{
set
{
if (value != null)
{
TestState.Sources.Add(value);
}
}
}

private static ILogger Logger => new TestLogger();
private static EditorConfigOptionsApplier OptionsApplier => new EditorConfigOptionsApplier();

public SolutionState TestState { get; }

private protected async Task TestAsync(string testCode, string expectedCode, IReadOnlyDictionary<string, string> editorConfig)
private protected Task<SourceText> TestAsync(string testCode, string expectedCode, IReadOnlyDictionary<string, string> editorConfig)
{
return TestAsync(testCode, expectedCode, editorConfig, Encoding.UTF8);
}

private protected async Task<SourceText> TestAsync(string testCode, string expectedCode, IReadOnlyDictionary<string, string> editorConfig, Encoding encoding)
{
TestCode = testCode;
var text = SourceText.From(testCode, encoding);
TestState.Sources.Add(text);

var solution = GetSolution(TestState.Sources.ToArray(), TestState.AdditionalFiles.ToArray(), TestState.AdditionalReferences.ToArray());
var project = solution.Projects.Single();
var document = project.Documents.Single();
var options = (OptionSet)await document.GetOptionsAsync();
var formatOptions = new FormatOptions(
workspaceFilePath: project.FilePath,
isSolution: false,
logLevel: LogLevel.Trace,
saveFormattedFiles: false,
changesAreErrors: false,
filesToFormat: ImmutableHashSet.Create<string>(document.FilePath));

ICodingConventionsSnapshot codingConventions = new TestCodingConventionsSnapshot(editorConfig);
options = OptionsApplier.ApplyConventions(options, codingConventions, Language);
filesToFormat: ImmutableHashSet.Create(document.FilePath));

var filesToFormat = new[] { (document, options, codingConventions) }.ToImmutableArray();
var filesToFormat = await GetOnlyFileToFormatAsync(solution, editorConfig);

var formattedSolution = await Formatter.FormatAsync(solution, filesToFormat, formatOptions, Logger, default);
var formattedDocument = formattedSolution.Projects.Single().Documents.Single();
var formattedDocument = GetOnlyDocument(formattedSolution);
var formattedText = await formattedDocument.GetTextAsync();

Assert.Equal(expectedCode, formattedText.ToString());

return formattedText;
}

/// <summary>
/// Gets the only <see cref="Document"/> along with related options and conventions.
/// </summary>
/// <param name="solution">A Solution containing a single Project containing a single Document.</param>
/// <param name="editorConfig">The editorconfig to apply to the documents options set.</param>
/// <returns>The document contained within along with option set and coding conventions.</returns>
protected async Task<ImmutableArray<(Document, OptionSet, ICodingConventionsSnapshot)>> GetOnlyFileToFormatAsync(Solution solution, IReadOnlyDictionary<string, string> editorConfig)
{
var document = GetOnlyDocument(solution);
var options = (OptionSet)await document.GetOptionsAsync();

ICodingConventionsSnapshot codingConventions = new TestCodingConventionsSnapshot(editorConfig);
options = OptionsApplier.ApplyConventions(options, codingConventions, Language);

return ImmutableArray.Create((document, options, codingConventions));
}

/// <summary>
/// Gets the only <see cref="Document"/> contained within the only <see cref="Project"/> within the <see cref="Solution"/>.
/// </summary>
/// <param name="solution">A Solution containing a single Project containing a single Document.</param>
/// <returns>The document contained within.</returns>
public Document GetOnlyDocument(Solution solution) => solution.Projects.Single().Documents.Single();

/// <summary>
/// Gets the collection of inputs to provide to the XML documentation resolver.
/// </summary>
Expand All @@ -125,13 +143,6 @@ private protected async Task TestAsync(string testCode, string expectedCode, IRe
/// </summary>
public List<Func<OptionSet, OptionSet>> OptionsTransforms { get; } = new List<Func<OptionSet, OptionSet>>();

public Document GetTestDocument(string testCode)
{
TestCode = testCode;
var solution = GetSolution(TestState.Sources.ToArray(), TestState.AdditionalFiles.ToArray(), TestState.AdditionalReferences.ToArray());
return solution.Projects.Single().Documents.Single();
}

/// <summary>
/// Given an array of strings as sources and a language, turn them into a <see cref="Project"/> and return the
/// solution.
Expand Down
Loading