#include "ide.h" bool ReFormatJSON_XML(String& text, bool xml) { if(xml) { try { XmlNode n = ParseXML(text); text = AsXML(n); } catch(XmlError) { Exclamation("Error passing the XML!"); return false; } } else { Value v = ParseJSON(text); if(v.IsError()) { Exclamation("Error passing the JSON!"); return false; } text = AsJSON(v, true); } return true; } void Ide::FormatJSON_XML(bool xml) { int l, h; bool sel = editor.GetSelection(l, h); if((sel ? h - l : editor.GetLength()) > 75 * 1024 * 1024) { Exclamation("Too big to reformat"); return; } String text; if(sel) text = editor.GetSelection(); else { FindFile ff(editfile); if(!ff || ff.IsReadOnly()) { Exclamation("File is read only!"); return; } SaveFile(); text = LoadFile(editfile); } if(!ReFormatJSON_XML(text, xml)) return; editor.NextUndo(); if(sel) { editor.Remove(l, h - l); editor.SetSelection(l, l + editor.Insert(l, text)); } else { editor.Remove(0, editor.GetLength()); editor.Insert(0, text); editor.SetEditable(); editor.SetCursor(0); MakeTitle(); } } void Ide::FormatJSON() { FormatJSON_XML(false); } void Ide::FormatXML() { FormatJSON_XML(true); } String Ide::FindClangFormatPath(bool local) { String p; auto Check = [&](String dir) { while(dir.GetCount() > 3) { for(String fn : { ".clang-format", "_clang-format" }) { p = AppendFileName(dir, fn); if(FileExists(p)) return true; } dir = GetFileFolder(dir); } return false; }; if(Check(GetFileFolder(editfile))) return p; if(local) return Null; for(String dir : GetUppDirs()) if(Check(dir)) return p; return Null; } String ClangFormatPath() { return ConfigFile("clang_format"); } VectorMap ReadClangFormatFile(Stream& in) { VectorMap val; String master_id; while(!in.IsEof()) { String l = in.GetLine(); String id; bool nested = findarg(*l, ' ', '\t') >= 0; CParser p(l); try { if(p.IsId()) { String id = p.ReadId(); if(nested) id = master_id + ":" + id; else master_id = id; p.PassChar(':'); String value = TrimBoth(p.GetPtr()); int q = value.Find('#'); if(q >= 0) value.Trim(q); value = TrimBoth(value); if(value.GetCount()) val.GetAdd(id) = value; } } catch(CParser::Error) { continue; } } return val; } int ApplyChanges(LineEdit& editor, const String& new_content) { // apply changes so that undo works correctly Vector ln = Split(new_content, '\n', false); byte cs = ResolveCharset(editor.GetCharset()); for(String& s : ln) { s.TrimEnd("\r"); if(cs != CHARSET_UTF8) s = ToCharset(CHARSET_UTF8, s, cs); } Vector ln2; for(int i = 0; i < editor.GetLineCount(); i++) ln2.Add(editor.GetUtf8Line(i)); int lined = 0; // adjustment for source line int cursor = editor.GetCursor(); bool nu = true; for(const auto& ts : CompareLineMaps(ln2, ln)) { if(!ts.same) { if(nu) { editor.NextUndo(); nu = false; } int tpos = editor.GetPos(ts.start1 + lined); int tsz = editor.GetPos(ts.start1 + ts.count1 + lined) - tpos; String rtext; for(int i = 0; i < ts.count2; i++) rtext << ln[ts.start2 + i] << "\n"; if(tsz && rtext.GetCount()) { // we can remove the last '\n' to have more precise blue bars rtext.TrimLast(); tsz--; } if(tpos == editor.GetLength() && tsz == 0) { // inserting at the end of text rtext.TrimEnd("\n"); rtext = "\n" + rtext; } editor.Remove(tpos, tsz); cursor = editor.Insert(tpos, rtext) + tpos; lined += ts.count2 - ts.count1; } } return cursor; } String ReformatCpp(CodeEditor& editor, bool setcursor, bool prefer_clang_format) { if(editor.GetLength() > 1000000) return "File is too big to reformat."; String clang_format_path; if(prefer_clang_format) clang_format_path = TheIde()->FindClangFormatPath(); if(IsNull(clang_format_path)) { clang_format_path = ClangFormatPath(); FileIn in(clang_format_path); VectorMap fv = ReadClangFormatFile(in); if(IsNull(fv.Get("BasedOnStyle", Null))) clang_format_path = TheIde()->FindClangFormatPath(); } int64 l, h; bool sel = editor.GetSelection(l, h); String cmd = "clang-format --style=file "; if(sel) { l = editor.GetLine(l) + 1; h = editor.GetLine(h) + 1; cmd << "--lines=" << l << ":" << h << " "; } String temp_path = CacheFile(AsString(Random()) + AsString(Random()) + "_to_format.cpp"); { FileOut out(temp_path); editor.Save(out, CHARSET_UTF8, TextCtrl::LE_LF); if(out.IsError()) { Exclamation("Failed to save temporary file \1" + temp_path); return "Failed to save temporary file " + temp_path; } } SaveChangedFile(CacheFile(".clang-format"), LoadFile(clang_format_path)); String r; int code = HostSys(cmd + temp_path, r); DeleteFile(temp_path); if(code) { if(code < 0) return "Failed to start clang-format"; StringStream ss(r); String l; while(!ss.IsEof() && IsNull(l)) l = ss.GetLine(); int q = l.Find("error:"); if(q >= 0 && q < r.GetCount() - 2) return TrimLeft(l.Mid(q + 6)); return Nvl(l, "Unspecified error."); } int cursor = ApplyChanges(editor, r); if(setcursor) editor.SetCursor(cursor); return Null; } struct ClangFormat { const char *id; const char *type; } clang_format[] = { { "AccessModifierOffset", "@" }, // int { "AlignAfterOpenBracket", "Align:DontAlign:AlwaysBreak:BlockIndent" }, { "AlignArrayOfStructures", "Left:Right:None" }, { "AlignConsecutiveAssignments", "None:Consecutive:AcrossEmptyLines:AcrossComments:AcrossEmptyLinesAndComments" }, { "AlignConsecutiveBitFields", "None:Consecutive:AcrossEmptyLines:AcrossComments:AcrossEmptyLinesAndComments" }, { "AlignConsecutiveDeclarations", "None:Consecutive:AcrossEmptyLines:AcrossComments:AcrossEmptyLinesAndComments" }, { "AlignConsecutiveMacros", "None:Consecutive:AcrossEmptyLines:AcrossComments:AcrossEmptyLinesAndComments" }, { "AlignEscapedNewlines", "DontAlign:Left:Right" }, { "AlignOperands", "DontAlign:Align:AlignAfterOperator" }, { "AlignTrailingComments", "!" }, // bool { "AllowAllArgumentsOnNextLine", "!" }, { "AllowAllConstructorInitializersOnNextLine", "!" }, { "AllowAllParametersOfDeclarationOnNextLine", "!" }, { "AllowShortBlocksOnASingleLine", "Never:Empty:Always" }, { "AllowShortCaseLabelsOnASingleLine", "!" }, { "AllowShortEnumsOnASingleLine", "!" }, { "AllowShortFunctionsOnASingleLine", "None:InlineOnly:Empty:Inline:All" }, { "AllowShortIfStatementsOnASingleLine", "Never:WithoutElse:OnlyFirstIf:AllIfsAndElse" }, { "AllowShortLambdasOnASingleLine", "None:Empty:Inline:All" }, { "AllowShortLoopsOnASingleLine", "!" }, { "AlwaysBreakAfterDefinitionReturnType", "None:All:TopLevel" }, { "AlwaysBreakAfterReturnType", "None:All:TopLevel:AllDefinitions:TopLevelDefinitions" }, { "AlwaysBreakBeforeMultilineStrings", "!" }, { "AlwaysBreakTemplateDeclarations", "No:MultiLine:Yes" }, { "BinPackArguments", "!" }, { "BinPackParameters", "!" }, { "BitFieldColonSpacing", "Both:None:Before:After" }, { "BraceWrapping", "" }, { " AfterCaseLabel", "!" }, { " AfterControlStatement", "Never:MultiLine:Always" }, { " AfterEnum","!" }, { " AfterFunction","!" }, { " AfterNamespace","!" }, { " AfterObjCDeclaration","!" }, { " AfterStruct","!" }, { " AfterUnion","!" }, { " AfterExternBlock","!" }, { " BeforeCatch","!" }, { " BeforeElse","!" }, { " BeforeLambdaBody","!" }, { " BeforeWhile","!" }, { " IndentBraces","!" }, { " SplitEmptyFunction","!" }, { " SplitEmptyRecord","!" }, { " SplitEmptyNamespace","!" }, { "BreakAfterAttributes", "Always:Leave:Never" }, { "BreakBeforeBinaryOperators", "None:NonAssignment:All" }, { "BreakBeforeBraces", "Attach:Linux:Mozilla:Stroustrup:Allman:Whitesmiths:GNU:WebKit:Custom" }, { "BreakBeforeConceptDeclarations", "Never:Allowed:Always" }, { "BreakBeforeInlineASMColon", "Never:OnlyMultiline:Always" }, { "BreakBeforeTernaryOperators", "!" }, { "BreakConstructorInitializers", "BeforeColon:BeforeComma:AfterColon" }, { "BreakInheritanceList", "BeforeColon:BeforeComma:AfterColon:AfterComma" }, { "BreakStringLiterals", "!" }, { "ColumnLimit", "#" }, // unsigned { "CompactNamespaces", "!" }, { "ConstructorInitializerIndentWidth", "#" }, { "ContinuationIndentWidth", "#" }, { "Cpp11BracedListStyle", "!" }, { "DeriveLineEnding", "!" }, { "DerivePointerAlignment", "!" }, { "DisableFormat", "!" }, { "EmptyLineAfterAccessModifier", "Never:Leave:Always" }, { "EmptyLineBeforeAccessModifier", "Never:Leave:LogicalBlock:Always" }, { "ExperimentalAutoDetectBinPacking", "!" }, { "FixNamespaceComments", "!" }, { "IndentAccessModifiers", "!" }, { "IndentCaseBlocks", "!" }, { "IndentCaseLabels", "!" }, { "IndentExternBlock", "AfterExternBlock:NoIndent:Indent" }, { "IndentGotoLabels", "!" }, { "IndentPPDirectives", "None:AfterHash:BeforeHash" }, { "IndentRequiresClause", "!" }, { "IndentWidth", "#" }, { "IndentWrappedFunctionNames", "!" }, { "InsertBraces", "!" }, { "InsertNewlineAtEOF", "!" }, { "InsertTrailingCommas", "None:Wrapped" }, { "IntegerLiteralSeparator", "" }, { " Decimal", "@" }, { " Hex", "@" }, { " Binary", "@" }, { "KeepEmptyLinesAtTheStartOfBlocks", "!" }, { "LambdaBodyIndentation", "Signature:OuterScope" }, { "MaxEmptyLinesToKeep", "#" }, { "NamespaceIndentation", "None:Inner:All" }, { "PPIndentWidth", "@" }, { "PackConstructorInitializers", "Never:BinPack:CurrentLine:NextLine" }, { "PenaltyBreakAssignment", "#" }, { "PenaltyBreakBeforeFirstCallParameter", "#" }, { "PenaltyBreakComment", "#" }, { "PenaltyBreakFirstLessLess", "#" }, { "PenaltyBreakOpenParenthesis", "#" }, { "PenaltyBreakString", "#" }, { "PenaltyBreakTemplateDeclaration", "#" }, { "PenaltyExcessCharacter", "#" }, { "PenaltyIndentedWhitespace", "#" }, { "PenaltyReturnTypeOnItsOwnLine", "#" }, { "PointerAlignment", "Left:Right:Middle" }, { "QualifierAlignment", "Leave:Left:Right:Custom" }, { "ReferenceAlignment", "Pointer:Left:Right:Middle" }, { "ReflowComments", "!" }, { "RemoveBracesLLVM", "!" }, { "RemoveSemicolon", "!" }, { "RequiresClausePosition", "OwnLine:WithPreceding:WithFollowing:SingleLine" }, { "RequiresExpressionIndentation", "OuterScope:Keyword" }, { "SeparateDefinitionBlocks", "Leave:Always:Never" }, { "ShortNamespaceLines", "#" }, { "SortIncludes", "Never:CaseSensitive:CaseInsensitive" }, { "SortUsingDeclarations", "Never:Lexicographic:LexicographicNumeric" }, { "SpaceAfterCStyleCast", "!" }, { "SpaceAfterLogicalNot", "!" }, { "SpaceAfterTemplateKeyword", "!" }, { "SpaceAroundPointerQualifiers", "Default:Before:After:Both" }, { "SpaceBeforeAssignmentOperators", "!" }, { "SpaceBeforeCaseColon", "!" }, { "SpaceBeforeCpp11BracedList", "!" }, { "SpaceBeforeCtorInitializerColon", "!" }, { "SpaceBeforeInheritanceColon", "!" }, { "SpaceBeforeParens", "Never:ControlStatements:ControlStatementsExceptControlMacros:NonEmptyParentheses:Always:Custom" }, { "SpaceBeforeParensOptions", "" }, { " AfterControlStatements", "!" }, { " AfterForeachMacros", "!" }, { " AfterFunctionDeclarationName", "!" }, { " AfterFunctionDefinitionName", "!" }, { " AfterIfMacros", "!" }, { " AfterOverloadedOperator", "!" }, { " AfterRequiresInClause", "!" }, { " AfterRequiresInExpression", "!" }, { " BeforeNonEmptyParentheses", "!" }, { "SpaceBeforeRangeBasedForLoopColon", "!" }, { "SpaceBeforeSquareBrackets", "!" }, { "SpaceInEmptyBlock", "!" }, { "SpaceInEmptyParentheses", "!" }, { "SpacesBeforeTrailingComments", "#" }, { "SpacesInAngles", "Never:Always:Leave" }, { "SpacesInCStyleCastParentheses", "!" }, { "SpacesInConditionalStatement", "!" }, { "SpacesInContainerLiterals", "!" }, { "SpacesInLineCommentPrefix", "" }, { " Minimum", "#" }, { " Maximum", "#" }, { "SpacesInParentheses", "!" }, { "SpacesInSquareBrackets", "!" }, { "TabWidth", "#" }, { "UseTab", "Never:ForIndentation:ForContinuationAndIndentation:AlignWithSpaces:Always" }, #ifdef _DEBUG { "Test", "Never:ForIndentation:ForContinuationAndIndentation:AlignWithSpaces:Always" }, #endif }; struct ReformatDlg : WithReformatLayout { String editfile; String code; bool sel; int l, h; int lines_before, lines_after; Array option; Array