/// <summary> /// Runs clang-format on the current selection /// </summary> private void FormatSelection(OptionPageGrid options) { IWpfTextView view = Vsix.GetCurrentView(); if (view == null) { // We're not in a text view. return; } 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); }
private void FormatView(IWpfTextView view, OptionPageGrid options) { if (view == null) { // We're not in a text view. return; } string filePath = Vsix.GetDocumentPath(view); var path = Path.GetDirectoryName(filePath); string text = view.TextBuffer.CurrentSnapshot.GetText(); RunClangFormatAndApplyReplacements(text, 0, text.Length, path, filePath, options, view); }
private void FormatView(IWpfTextView view, OptionPageGrid options) { if (view == null) { // We're not in a text view. return; } string filePath = Vsix.GetDocumentPath(view); var path = Path.GetDirectoryName(filePath); string text = view.TextBuffer.CurrentSnapshot.GetText(); if (!text.EndsWith(Environment.NewLine)) { view.TextBuffer.Insert(view.TextBuffer.CurrentSnapshot.Length, Environment.NewLine); text += Environment.NewLine; } byte[] buffer = enc.GetBytes(text); RunClangFormatAndApplyReplacements(buffer, 0, buffer.Length, path, filePath, options, view); }
/// <summary> /// Runs clang-format on the current selection /// </summary> private void FormatSelection(OptionPageGrid options) { IWpfTextView view = Vsix.GetCurrentView(); if (view == null) { // We're not in a text view. return; } string text = view.TextBuffer.CurrentSnapshot.GetText(); int start = view.Selection.Start.Position.GetContainingLine().Start.Position; int end = view.Selection.End.Position.GetContainingLine().End.Position; if (start >= end) { return; } // convert the utf16 index to multi bytes index start = enc.GetByteCount(text.ToCharArray(), 0, start); end = enc.GetByteCount(text.ToCharArray(), 0, end); int length = end - start; if (length <= 0) { return; } // 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); byte[] buffer = enc.GetBytes(text); RunClangFormatAndApplyReplacements(buffer, start, length, path, filePath, options, view); }
/// <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. /// </summary> private static string RunClangFormat(byte[] buffer, int offset, int length, 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"; // 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("\"", "\\\""); string fallbackStyle = options.FallbackStyle.Replace("\"", "\\\""); process.StartInfo.Arguments = " -offset " + offset + " -length " + length + " -output-replacements-xml " + " -style \"" + style + "\"" + " -fallback-style \"" + fallbackStyle + "\""; if (options.SortIncludes) { process.StartInfo.Arguments += " -sort-includes "; } string assumeFilename = options.AssumeFilename; if (string.IsNullOrEmpty(assumeFilename)) { assumeFilename = filePath; } if (!string.IsNullOrEmpty(assumeFilename)) { process.StartInfo.Arguments += " -assume-filename \"" + assumeFilename + "\""; } process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardInput = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; if (path != null) { process.StartInfo.WorkingDirectory = path; } // We have to be careful when communicating via standard input / output, // as writes to the buffers will block until they are read from the other side. // Thus, we: // 1. Start the process - clang-format.exe will start to read the input from the // standard input. try { if (options.OutputDetailInfo) { Output("clang-format.exe "); Output(process.StartInfo.Arguments); } process.Start(); } catch (Exception e) { throw new Exception( "Cannot execute " + process.StartInfo.FileName + ".\n\"" + e.Message + "\".\nPlease make sure it is on the PATH."); } // 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.BaseStream.Write(buffer, 0, buffer.Length); // 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(); // 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(); // 5. clang-format is done, wait until it is fully shut down. process.WaitForExit(); if (process.ExitCode != 0) { // FIXME: If clang-format writes enough to the standard error stream to block, // we will never reach this point; instead, read the standard error asynchronously. throw new Exception(process.StandardError.ReadToEnd()); } if (options.OutputDetailInfo) { Output("\n"); Output(output); } return(output); }
private void RunClangFormatAndApplyReplacements(byte[] buffer, int offset, int length, string path, string filePath, OptionPageGrid options, IWpfTextView view) { try { string replacements = RunClangFormat(buffer, offset, length, path, filePath, options); ApplyClangFormatReplacements(replacements, view); } catch (Exception e) { var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); var id = Guid.Empty; int result; uiShell.ShowMessageBox( 0, ref id, "Error while running clang-format:", e.Message, string.Empty, 0, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, OLEMSGICON.OLEMSGICON_INFO, 0, out result); } }
private void FormatDocument(Document document, OptionPageGrid options) { FormatView(Vsix.GetDocumentView(document), options); }
/// <summary> /// Runs clang-format on the current document /// </summary> private void FormatDocument(OptionPageGrid options) { FormatView(Vsix.GetCurrentView(), options); }