Skip to content
Permalink
Browse files

clang-format-vs : Fix Unicode formatting

Use UTF-8 for communication with clang-format and convert the
replacements offset/length to characters position/count.

Internally VisualStudio.Text.Editor.IWpfTextView use sequence of Unicode
characters encoded using UTF-16 and use characters position/count for
manipulating text.

Resolved "Error while running clang-format: Specified argument was out
of the range of valid values. Parameter name: replaceSpan".

Patch by empty2fill!

Differential revision: https://reviews.llvm.org/D70633
  • Loading branch information
Hans Wennborg
Hans Wennborg committed Nov 27, 2019
1 parent f1b1173 commit e20a1e486e144c88188bc7b420885d5326b39088
Showing with 20 additions and 11 deletions.
  1. +20 −11 clang/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
@@ -24,6 +24,7 @@
using System.Runtime.InteropServices;
using System.Xml.Linq;
using System.Linq;
using System.Text;

namespace LLVM.ClangFormat
{
@@ -292,16 +293,15 @@ private void FormatSelection(OptionPageGrid options)
string text = view.TextBuffer.CurrentSnapshot.GetText();
int start = view.Selection.Start.Position.GetContainingLine().Start.Position;
int end = view.Selection.End.Position.GetContainingLine().End.Position;
int length = end - start;


// clang-format doesn't support formatting a range that starts at the end
// of the file.
if (start >= text.Length && text.Length > 0)
start = text.Length - 1;
string path = Vsix.GetDocumentParent(view);
string filePath = Vsix.GetDocumentPath(view);

RunClangFormatAndApplyReplacements(text, start, length, path, filePath, options, view);
RunClangFormatAndApplyReplacements(text, start, end, path, filePath, options, view);
}

/// <summary>
@@ -336,11 +336,11 @@ private void FormatView(IWpfTextView view, OptionPageGrid options)
RunClangFormatAndApplyReplacements(text, 0, text.Length, path, filePath, options, view);
}

private void RunClangFormatAndApplyReplacements(string text, int offset, int length, string path, string filePath, OptionPageGrid options, IWpfTextView view)
private void RunClangFormatAndApplyReplacements(string text, int start, int end, string path, string filePath, OptionPageGrid options, IWpfTextView view)
{
try
{
string replacements = RunClangFormat(text, offset, length, path, filePath, options);
string replacements = RunClangFormat(text, start, end, path, filePath, options);
ApplyClangFormatReplacements(replacements, view);
}
catch (Exception e)
@@ -363,16 +363,19 @@ private void RunClangFormatAndApplyReplacements(string text, int offset, int len
/// <summary>
/// Runs the given text through clang-format and returns the replacements as XML.
///
/// Formats the text range starting at offset of the given length.
/// Formats the text in range start and end.
/// </summary>
private static string RunClangFormat(string text, int offset, int length, string path, string filePath, OptionPageGrid options)
private static string RunClangFormat(string text, int start, int end, string path, string filePath, OptionPageGrid options)
{
string vsixPath = Path.GetDirectoryName(
typeof(ClangFormatPackage).Assembly.Location);

System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = vsixPath + "\\clang-format.exe";
char[] chars = text.ToCharArray();
int offset = Encoding.UTF8.GetByteCount(chars, 0, start);
int length = Encoding.UTF8.GetByteCount(chars, 0, end) - offset;
// Poor man's escaping - this will not work when quotes are already escaped
// in the input (but we don't need more).
string style = options.Style.Replace("\"", "\\\"");
@@ -413,10 +416,11 @@ private static string RunClangFormat(string text, int offset, int length, string
// 2. We write everything to the standard output - this cannot block, as clang-format
// reads the full standard input before analyzing it without writing anything to the
// standard output.
process.StandardInput.Write(text);
StreamWriter utf8Writer = new StreamWriter(process.StandardInput.BaseStream, new UTF8Encoding(false));
utf8Writer.Write(text);
// 3. We notify clang-format that the input is done - after this point clang-format
// will start analyzing the input and eventually write the output.
process.StandardInput.Close();
utf8Writer.Close();
// 4. We must read clang-format's output before waiting for it to exit; clang-format
// will close the channel by exiting.
string output = process.StandardOutput.ReadToEnd();
@@ -440,13 +444,18 @@ private static void ApplyClangFormatReplacements(string replacements, IWpfTextVi
if (replacements.Length == 0)
return;

string text = view.TextBuffer.CurrentSnapshot.GetText();
byte[] bytes = Encoding.UTF8.GetBytes(text);

var root = XElement.Parse(replacements);
var edit = view.TextBuffer.CreateEdit();
foreach (XElement replacement in root.Descendants("replacement"))
{
int offset = int.Parse(replacement.Attribute("offset").Value);
int length = int.Parse(replacement.Attribute("length").Value);
var span = new Span(
int.Parse(replacement.Attribute("offset").Value),
int.Parse(replacement.Attribute("length").Value));
Encoding.UTF8.GetCharCount(bytes, 0, offset),
Encoding.UTF8.GetCharCount(bytes, offset, length));
edit.Replace(span, replacement.Value);
}
edit.Apply();

0 comments on commit e20a1e4

Please sign in to comment.
You can’t perform that action at this time.