Skip to content
Open
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
31 changes: 31 additions & 0 deletions DprintPluginRoslyn.Tests/WorkspaceTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Dprint.Plugins.Roslyn.Configuration;
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Dprint.Plugins.Roslyn;

Expand Down Expand Up @@ -45,4 +47,33 @@ public void Issue3_SetIndentWidth()
var diagnostics = workspace.GetDiagnostics(2);
Assert.That(diagnostics.Count, Is.EqualTo(0));
}

[Test]
public void FormatCode_WithBom_PreservesBomWhenContentChanges()
{
var workspace = new Workspace();
workspace.SetConfig(1, new(), new());
var bom = Encoding.UTF8.GetPreamble();
var content = Encoding.UTF8.GetBytes("namespace Test { }\n");
var input = bom.Concat(content).ToArray();

var result = workspace.GetFormatters(1).FormatCode("file.cs", input, null, CancellationToken.None);

Assert.That(result, Is.Not.Null);
Assert.That(result!.Take(3).ToArray(), Is.EqualTo(bom));
}

[Test]
public void FormatCode_WithBom_ReturnsNullWhenUnchanged()
{
var workspace = new Workspace();
workspace.SetConfig(1, new(), new());
var bom = Encoding.UTF8.GetPreamble();
var content = Encoding.UTF8.GetBytes("namespace Test { }\n");
var input = bom.Concat(content).ToArray();

var result = workspace.GetFormatters(1).FormatCode("file.cs", input, null, CancellationToken.None);

Assert.That(result, Is.Null);
}
}
6 changes: 3 additions & 3 deletions DprintPluginRoslyn/Formatters/CSharpCodeFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public bool ShouldFormat(string filePath)
return filePath.EndsWith(".cs", StringComparison.OrdinalIgnoreCase);
}

public byte[] FormatText(SourceText text, TextSpan? range, OptionSet options, CancellationToken token)
public SourceText FormatText(SourceText text, TextSpan? range, OptionSet options, CancellationToken token)
{
SyntaxNode formattedNode;

Expand All @@ -39,8 +39,8 @@ public byte[] FormatText(SourceText text, TextSpan? range, OptionSet options, Ca
formattedNode = Formatter.Format(root, range.Value, _workspace, options, token);
else
formattedNode = Formatter.Format(root, _workspace, options, token);
var result = formattedNode.GetText(text.Encoding, text.ChecksumAlgorithm);
return (result.Encoding ?? System.Text.Encoding.UTF8).GetBytes(result.ToString());

return formattedNode.GetText(text.Encoding, text.ChecksumAlgorithm);
}

public void ResolveConfiguration(ConfigurationResolutionContext context)
Expand Down
4 changes: 3 additions & 1 deletion DprintPluginRoslyn/Formatters/CodeFormatters.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Dprint.Plugins.Roslyn.Communication;
using Dprint.Plugins.Roslyn.Utils;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Text;
Expand Down Expand Up @@ -31,7 +32,8 @@ public CodeFormatters(ICodeFormatter[] codeFormatters, OptionSet options)
encoding: null // Let it auto-detect
);
var result = formatter.FormatText(sourceText, range, _options, token);
return result.SequenceEqual(code) ? null : result;
var bytes = result.GetBytes();
return bytes.SequenceEqual(code) ? null : bytes;
}

public Dictionary<string, object> GetResolvedConfig()
Expand Down
2 changes: 1 addition & 1 deletion DprintPluginRoslyn/Formatters/ICodeFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface ICodeFormatter
{
string RoslynLanguageName { get; }
bool ShouldFormat(string filePath);
byte[] FormatText(SourceText text, TextSpan? range, OptionSet options, CancellationToken token);
SourceText FormatText(SourceText text, TextSpan? range, OptionSet options, CancellationToken token);
void ResolveConfiguration(ConfigurationResolutionContext context);
IEnumerable<(string, object)> GetResolvedConfig(OptionSet options);
}
6 changes: 3 additions & 3 deletions DprintPluginRoslyn/Formatters/VisualBasicCodeFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public bool ShouldFormat(string filePath)
return filePath.EndsWith(".vb", StringComparison.OrdinalIgnoreCase);
}

public byte[] FormatText(SourceText text, TextSpan? range, OptionSet options, CancellationToken token)
public SourceText FormatText(SourceText text, TextSpan? range, OptionSet options, CancellationToken token)
{
SyntaxNode formattedNode;

Expand All @@ -37,8 +37,8 @@ public byte[] FormatText(SourceText text, TextSpan? range, OptionSet options, Ca
formattedNode = Formatter.Format(root, range.Value, _workspace, options, token);
else
formattedNode = Formatter.Format(root, _workspace, options, token);
var result = formattedNode.GetText(text.Encoding, text.ChecksumAlgorithm);
return (result.Encoding ?? System.Text.Encoding.UTF8).GetBytes(result.ToString());

return formattedNode.GetText(text.Encoding, text.ChecksumAlgorithm);
}

public void ResolveConfiguration(ConfigurationResolutionContext context)
Expand Down
16 changes: 16 additions & 0 deletions DprintPluginRoslyn/Utils/SourceTextExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.IO;
using Microsoft.CodeAnalysis.Text;

namespace Dprint.Plugins.Roslyn.Utils;

public static class SourceTextExtensions
{
public static byte[] GetBytes(this SourceText sourceText)
{
var ms = new MemoryStream();
using var sw = new StreamWriter(ms, sourceText.Encoding);
sw.Write(sourceText.ToString());
sw.Flush();
return ms.ToArray();
}
}
28 changes: 28 additions & 0 deletions test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,34 @@ mod test {
})
.await;
assert_eq!(result.unwrap(), Some("namespace Test {\n class Test { }\n}\n".to_string(),));

// BOM preservation: content needs formatting
let result = communicator
.format_text(ProcessPluginCommunicatorFormatRequest {
file_path: PathBuf::from("file.cs"),
file_text: "\u{FEFF}namespace Test { }\n".to_string(),
range: None,
config_id,
override_config: Default::default(),
on_host_format: Rc::new(|_| unreachable!()),
token: token.clone(),
})
.await;
assert_eq!(result.unwrap(), Some("\u{FEFF}namespace Test { }\n".to_string()));

// BOM preservation: already formatted, should be unchanged
let result = communicator
.format_text(ProcessPluginCommunicatorFormatRequest {
file_path: PathBuf::from("file.cs"),
file_text: "\u{FEFF}namespace Test { }\n".to_string(),
range: None,
config_id,
override_config: Default::default(),
on_host_format: Rc::new(|_| unreachable!()),
token: token.clone(),
})
.await;
assert_eq!(result.unwrap(), None);
}

let mut handles = Vec::new();
Expand Down