Skip to content

Commit e20a1e4

Browse files
committed
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
1 parent f1b1173 commit e20a1e4

File tree

1 file changed

+20
-11
lines changed

1 file changed

+20
-11
lines changed

clang/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
using System.Runtime.InteropServices;
2525
using System.Xml.Linq;
2626
using System.Linq;
27+
using System.Text;
2728

2829
namespace LLVM.ClangFormat
2930
{
@@ -292,16 +293,15 @@ private void FormatSelection(OptionPageGrid options)
292293
string text = view.TextBuffer.CurrentSnapshot.GetText();
293294
int start = view.Selection.Start.Position.GetContainingLine().Start.Position;
294295
int end = view.Selection.End.Position.GetContainingLine().End.Position;
295-
int length = end - start;
296-
296+
297297
// clang-format doesn't support formatting a range that starts at the end
298298
// of the file.
299299
if (start >= text.Length && text.Length > 0)
300300
start = text.Length - 1;
301301
string path = Vsix.GetDocumentParent(view);
302302
string filePath = Vsix.GetDocumentPath(view);
303303

304-
RunClangFormatAndApplyReplacements(text, start, length, path, filePath, options, view);
304+
RunClangFormatAndApplyReplacements(text, start, end, path, filePath, options, view);
305305
}
306306

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

339-
private void RunClangFormatAndApplyReplacements(string text, int offset, int length, string path, string filePath, OptionPageGrid options, IWpfTextView view)
339+
private void RunClangFormatAndApplyReplacements(string text, int start, int end, string path, string filePath, OptionPageGrid options, IWpfTextView view)
340340
{
341341
try
342342
{
343-
string replacements = RunClangFormat(text, offset, length, path, filePath, options);
343+
string replacements = RunClangFormat(text, start, end, path, filePath, options);
344344
ApplyClangFormatReplacements(replacements, view);
345345
}
346346
catch (Exception e)
@@ -363,16 +363,19 @@ private void RunClangFormatAndApplyReplacements(string text, int offset, int len
363363
/// <summary>
364364
/// Runs the given text through clang-format and returns the replacements as XML.
365365
///
366-
/// Formats the text range starting at offset of the given length.
366+
/// Formats the text in range start and end.
367367
/// </summary>
368-
private static string RunClangFormat(string text, int offset, int length, string path, string filePath, OptionPageGrid options)
368+
private static string RunClangFormat(string text, int start, int end, string path, string filePath, OptionPageGrid options)
369369
{
370370
string vsixPath = Path.GetDirectoryName(
371371
typeof(ClangFormatPackage).Assembly.Location);
372372

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

447+
string text = view.TextBuffer.CurrentSnapshot.GetText();
448+
byte[] bytes = Encoding.UTF8.GetBytes(text);
449+
443450
var root = XElement.Parse(replacements);
444451
var edit = view.TextBuffer.CreateEdit();
445452
foreach (XElement replacement in root.Descendants("replacement"))
446453
{
454+
int offset = int.Parse(replacement.Attribute("offset").Value);
455+
int length = int.Parse(replacement.Attribute("length").Value);
447456
var span = new Span(
448-
int.Parse(replacement.Attribute("offset").Value),
449-
int.Parse(replacement.Attribute("length").Value));
457+
Encoding.UTF8.GetCharCount(bytes, 0, offset),
458+
Encoding.UTF8.GetCharCount(bytes, offset, length));
450459
edit.Replace(span, replacement.Value);
451460
}
452461
edit.Apply();

0 commit comments

Comments
 (0)