From 2e77d71f1212215efb4eba5f209873a00abdd6ce Mon Sep 17 00:00:00 2001 From: Mirek Fidler Date: Wed, 1 Feb 2023 15:27:24 +0100 Subject: [PATCH] ide: clang-format integration --- .clang-format | 24 + uppbox/MakePosixInstall/scripts.cpp | 2 +- uppsrc/Core/LocalProcess.cpp | 2 +- uppsrc/ide/Android/AndroidManifest.cpp | 3 +- uppsrc/ide/Builders/Build.cpp | 6 +- uppsrc/ide/Core/Assembly.cpp | 19 - uppsrc/ide/Core/Core.h | 2 - uppsrc/ide/Core/Host.cpp | 16 +- uppsrc/ide/Core/Host.h | 1 + uppsrc/ide/FormatCode.cpp | 594 ++++++++++++++++++++++++- uppsrc/ide/Insert.cpp | 1 + uppsrc/ide/NewPackageFile.cpp | 10 +- uppsrc/ide/ide.h | 9 +- uppsrc/ide/ide.key | 14 +- uppsrc/ide/ide.lay | 12 + uppsrc/ide/ide.upp | 2 +- uppsrc/ide/idebar.cpp | 36 +- uppsrc/ide/idetool.cpp | 5 - upptst/Spin/Spin.lay | 30 +- 19 files changed, 698 insertions(+), 90 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..c37e1b808 --- /dev/null +++ b/.clang-format @@ -0,0 +1,24 @@ +# .clang-format file for U++ framework + +--- +BasedOnStyle: LLVM +UseTab: AlignWithSpaces +IndentWidth: 4 +TabWidth: 4 +ColumnLimit: 96 +--- +Language: Cpp +AccessModifierOffset: -4 +AllowShortFunctionsOnASingleLine: All +AlwaysBreakTemplateDeclarations: true +BreakBeforeBraces: Stroustrup +BreakConstructorInitializers: BeforeComma +CompactNamespaces: true +DerivePointerAlignment: false +IfMacros: ['ONCELOCK'] +PointerAlignment: Left +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterControlStatements: false +IndentAccessModifiers: false +IndentPPDirectives: None diff --git a/uppbox/MakePosixInstall/scripts.cpp b/uppbox/MakePosixInstall/scripts.cpp index 622f90883..81a2d12f9 100644 --- a/uppbox/MakePosixInstall/scripts.cpp +++ b/uppbox/MakePosixInstall/scripts.cpp @@ -12,7 +12,7 @@ AskContinue() uname=`uname` if [ -x "$(command -v apt-get)" ]; then - DEP="apt-get install g++ clang git make libgtk-3-dev libnotify-dev libbz2-dev libssl-dev xdotool" + DEP="apt-get install g++ clang git make libgtk-3-dev libnotify-dev libbz2-dev libssl-dev xdotool clang-format" elif [ -x "$(command -v yum)" ]; then DEP="yum install gcc-c++ clang git make gtk3-devel libnotify-devel bzip2-devel freetype-devel openssl-devel" elif [ -x "$(command -v dnf)" ]; then diff --git a/uppsrc/Core/LocalProcess.cpp b/uppsrc/Core/LocalProcess.cpp index a3195a5c6..69fcd55df 100644 --- a/uppsrc/Core/LocalProcess.cpp +++ b/uppsrc/Core/LocalProcess.cpp @@ -345,7 +345,7 @@ bool LocalProcess::DoStart(const char *command, const Vector *arg, bool execv(app_full, args.Begin()); LLOG("execve failed, errno = " << errno); // printf("Error running '%s', error code %d\n", command, errno); - exit(-errno); + abort(); // do not use exit here: it calls global destructors... return true; #endif } diff --git a/uppsrc/ide/Android/AndroidManifest.cpp b/uppsrc/ide/Android/AndroidManifest.cpp index 8bd75abbf..f13e2a61a 100644 --- a/uppsrc/ide/Android/AndroidManifest.cpp +++ b/uppsrc/ide/Android/AndroidManifest.cpp @@ -38,7 +38,8 @@ bool AndroidManifest::Parse() p.Skip(); } - } catch(const XmlError& e) { + } + catch(const XmlError& e) { Loge() << METHOD_NAME << "Failed to parse manifest file with error \"" + e + "\"."; return false; } diff --git a/uppsrc/ide/Builders/Build.cpp b/uppsrc/ide/Builders/Build.cpp index 0b69aa660..438795993 100644 --- a/uppsrc/ide/Builders/Build.cpp +++ b/uppsrc/ide/Builders/Build.cpp @@ -102,9 +102,9 @@ void MakeBuild::CreateHost(Host& host, const String& method, bool darkmode, bool VectorMap env = clone(Environment()); host.exedirs = SplitDirs(bm.Get("PATH", "") + ';' + env.Get("PATH", "")); #ifdef PLATFORM_WIN32 - String p = GetExeDirFile("bin/mingit/cmd"); - if(FileExists(p + "/git.exe")) - host.exedirs.Add(p); + host.AddExecutable(GetExeDirFile("bin/mingit/cmd"), "git.exe"); + host.AddExecutable(GetExeDirFile("bin/llvm/bin"), "clang-format.exe"); + env.GetAdd("PATH") = Join(host.exedirs, ";"); #else env.GetAdd("PATH") = Join(host.exedirs, ":"); diff --git a/uppsrc/ide/Core/Assembly.cpp b/uppsrc/ide/Core/Assembly.cpp index 776ef92a5..d2dc90a21 100644 --- a/uppsrc/ide/Core/Assembly.cpp +++ b/uppsrc/ide/Core/Assembly.cpp @@ -10,25 +10,6 @@ String LocalPath(const String& filename) return AppendFileName(GetLocalDir(), filename); } -Vector IgnoreList() -{ - Vector ignore; - const Workspace& wspc = GetIdeWorkspace(); - for(int i = 0; i < wspc.GetCount(); i++) { - const Package& pk = wspc.GetPackage(i); - for(int j = 0; j < pk.GetCount(); j++) - if(!pk[j].separator && pk[j] == "ignorelist") { - FileIn in(SourcePath(wspc[i], pk[j])); - while(in && !in.IsEof()) { - String s = in.GetLine(); - if(!s.IsEmpty()) - ignore.Add(s); - } - } - } - return ignore; -} - String FollowCygwinSymlink(const String& file) { for(String fn = file;;) { if(fn.IsEmpty()) diff --git a/uppsrc/ide/Core/Core.h b/uppsrc/ide/Core/Core.h index 55d19e2ec..c23be6730 100644 --- a/uppsrc/ide/Core/Core.h +++ b/uppsrc/ide/Core/Core.h @@ -365,8 +365,6 @@ String GetPackagePathNest(const String& path); String GetLocalDir(); String LocalPath(const String& filename); -Vector IgnoreList(); - bool IsFullDirectory(const String& d); bool IsFolder(const String& path); diff --git a/uppsrc/ide/Core/Host.cpp b/uppsrc/ide/Core/Host.cpp index 61f6e79dd..b348d14e8 100644 --- a/uppsrc/ide/Core/Host.cpp +++ b/uppsrc/ide/Core/Host.cpp @@ -1,9 +1,10 @@ #include "Core.h" -#define LLOG(x) - #include +#define LLOG(x) +#define METHOD_NAME "Host::" << UPP_FUNCTION_NAME << "(): " + Host::Host() { } @@ -381,6 +382,17 @@ const Vector& Host::GetExecutablesDirs() const return exedirs; } +void Host::AddExecutable(const String& dir, const String& exe) +{ + String p = dir + DIR_SEPS + exe; + if(!FileExists(p)) { + Loge() << METHOD_NAME << "Following executable file \"" << p << "\" doesn't exists."; + return; + } + + exedirs.Add(dir); +} + bool Host::HasPlatformFlag(const Index& cfg) { static const Index platformFlags = { diff --git a/uppsrc/ide/Core/Host.h b/uppsrc/ide/Core/Host.h index 3c6a692c2..0c4a3b2df 100644 --- a/uppsrc/ide/Core/Host.h +++ b/uppsrc/ide/Core/Host.h @@ -45,6 +45,7 @@ struct Host { void AddFlags(Index& cfg); const Vector& GetExecutablesDirs() const; + void AddExecutable(const String& dir, const String& exe); private: bool HasPlatformFlag(const Index& cfg); diff --git a/uppsrc/ide/FormatCode.cpp b/uppsrc/ide/FormatCode.cpp index f3f40734e..5e4b9c941 100644 --- a/uppsrc/ide/FormatCode.cpp +++ b/uppsrc/ide/FormatCode.cpp @@ -27,7 +27,7 @@ void Ide::FormatJSON_XML(bool xml) { int l, h; bool sel = editor.GetSelection(l, h); - if((sel ? h - l : editor.GetLength()) > 75*1024*1024) { + if((sel ? h - l : editor.GetLength()) > 75 * 1024 * 1024) { Exclamation("Too big to reformat"); return; } @@ -51,28 +51,588 @@ void Ide::FormatJSON_XML(bool xml) } } -void Ide::FormatJSON() +void Ide::FormatJSON() { FormatJSON_XML(false); } +void Ide::FormatXML() { FormatJSON_XML(true); } + +String Ide::FindClangFormatPath() { - FormatJSON_XML(false); + String p; + auto Check = [&](const String& dir) { + for(String fn : { ".clang-format", "_clang-format" }) { + p = AppendFileName(dir, fn); + if(FileExists(p)) + return true; + } + return false; + }; + + for(String dir = GetFileFolder(editfile); dir.GetCount() > 3; dir = GetFileFolder(dir)) + if(Check(dir)) + return p; + + for(String dir : GetUppDirs()) + if(Check(dir)) + return p; + + return Null; } -void Ide::FormatXML() +String ClangFormatPath() { - FormatJSON_XML(true); + return ConfigFile("clang_format"); } -void Ide::FormatJSON_XML_File(bool xml) +VectorMap ReadClangFormatFile(Stream& in) { - if(IsNull(editfile)) - return; - if(GetFileLength(editfile) >= 75*1024*1024) - Exclamation("Too big to reformat"); - SaveFile(); - String text = LoadFile(editfile); - if(!ReFormatJSON_XML(text, xml)) - return; - if(PromptYesNo("Overwrite \1" + editfile + "\1 with reformated " + (xml ? "XML" : "JSON") + "?")) { - Upp::SaveFile(editfile, text); - EditAsText(); + 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; +} + +String ReformatCpp(CodeEditor& editor, bool setcursor) +{ + String 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 "; + 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; + } + } + + cmd << "\"--style=file:" << clang_format_path << "\" "; + + String r; + int code = Sys(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."); + } + + Vector ln = Split(r, '\n', false); + for(String& s : ln) + s.TrimEnd("\r"); + int n = editor.GetLineCount(); + l = h = n; + for(int i = 0; i < n; i++) + if(i >= ln.GetCount() || editor.GetUtf8Line(i) != ln[i]) { + l = i; + break; + } + for(int i = 0; i < n; i++) + if(i >= ln.GetCount() || editor.GetUtf8Line(n - 1 - i) != ln[ln.GetCount() - 1 - i]) { + h = i; + break; + } + + if(l + h >= n) + return Null; + + editor.NextUndo(); + int from = editor.GetPos(l); + editor.Remove(from, editor.GetPos(editor.GetLineCount() - h) - from); + ln.Remove(0, l); + ln.Trim(ln.GetCount() - h); + int pos = editor.Insert(from, Join(ln, "\n") + "\n", CHARSET_UTF8); + if(setcursor) + editor.SetCursor(pos); + + 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; + + Array option; + Array