diff --git a/Ide/Code/FindSymbol.cpp b/Ide/Code/FindSymbol.cpp --- a/Ide/Code/FindSymbol.cpp +++ b/Ide/Code/FindSymbol.cpp @@ -1,680 +1,683 @@ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/List.h" #include "lgi/common/Token.h" #include "lgi/common/EventTargetThread.h" #include "lgi/common/TextFile.h" #include "LgiIde.h" #include "FindSymbol.h" #include "ParserCommon.h" #if 1 #include "lgi/common/ParseCpp.h" #endif #include "resdefs.h" #define MSG_TIME_MS 1000 #define DEBUG_FIND_SYMBOL 0 #define DEBUG_NO_THREAD 1 -// #define DEBUG_FILE "FileSelect.cpp" +// #define DEBUG_FILE "RichTextEdit.h" int SYM_FILE_SENT = 0; class FindSymbolDlg : public LDialog { // AppWnd *App; LList *Lst; FindSymbolSystem *Sys; public: FindSymResult Result; FindSymbolDlg(LViewI *parent, FindSymbolSystem *sys); ~FindSymbolDlg(); int OnNotify(LViewI *v, LNotification n); void OnCreate(); bool OnViewKey(LView *v, LKey &k); LMessage::Result OnEvent(LMessage *m); }; int ScoreCmp(FindSymResult **a, FindSymResult **b) { return (*b)->Score - (*a)->Score; } #define USE_HASH 1 struct FindSymbolSystemPriv : public LEventTargetThread { struct FileSyms { LString Path; int Platforms; LString::Array *Inc; LArray Defs; bool IsSource; bool IsHeader; bool IsPython; bool IsJavascript; bool Parse(LAutoWString Source, bool Debug) { IsSource = false; IsHeader = false; IsPython = false; IsJavascript = false; Defs.Length(0); bool Status = false; char *Ext = LGetExtension(Path); if (Ext) { IsSource = !_stricmp(Ext, "c") || !_stricmp(Ext, "cpp") || !_stricmp(Ext, "cxx"); IsHeader = !_stricmp(Ext, "h") || !_stricmp(Ext, "hpp") || !_stricmp(Ext, "hxx"); IsPython = !_stricmp(Ext, "py"); IsJavascript = !_stricmp(Ext, "js"); if (IsSource || IsHeader) Status = BuildCppDefnList(Path, Source, Defs, DefnNone, Debug); else if (IsJavascript) Status = BuildJsDefnList(Path, Source, Defs, DefnNone, Debug); else if (IsPython) Status = BuildPyDefnList(Path, Source, Defs, DefnNone, Debug); } return Status; } }; int hApp; int MissingFiles = 0; LArray IncPaths; #if USE_HASH LHashTbl, FileSyms*> Files; #else LArray Files; #endif int Tasks = 0; uint64 MsgTs = 0; bool DoingProgress = false; FindSymbolSystemPriv(int appSinkHnd) : LEventTargetThread("FindSymbolSystemPriv"), hApp(appSinkHnd) { } ~FindSymbolSystemPriv() { // End the thread... EndThread(); // Clean up mem Files.DeleteObjects(); } void Log(const char *Fmt, ...) { va_list Arg; va_start(Arg, Fmt); LString s; s.Printf(Arg, Fmt); va_end(Arg); if (s.Length()) PostThreadEvent(hApp, M_APPEND_TEXT, (LMessage::Param)NewStr(s), AppWnd::BuildTab); } #if !USE_HASH int GetFileIndex(LString &Path) { for (unsigned i=0; iPath) return i; } return -1; } #endif bool AddFile(LString Path, int Platforms) { bool Debug = false; #ifdef DEBUG_FILE if ((Debug = Path.Find(DEBUG_FILE) >= 0)) printf("%s:%i - AddFile(%s)\n", _FL, Path.Get()); #endif // Already added? #if USE_HASH FileSyms *f = Files.Find(Path); if (f) { if (Platforms && f->Platforms == 0) f->Platforms = Platforms; return true; } #else int Idx = GetFileIndex(Path); if (Idx >= 0) { if (Platforms && Files[Idx]->Platforms == 0) Files[Idx]->Platforms = Platforms; return true; } FileSyms *f; #endif if (!LFileExists(Path)) { Log("Missing '%s'\n", Path.Get()); MissingFiles++; return false; } LAssert(!LIsRelativePath(Path)); // Setup the file sym data... f = new FileSyms; if (!f) return false; f->Path = Path; f->Platforms = Platforms; f->Inc = IncPaths.Length() ? IncPaths.Last() : NULL; #if USE_HASH Files.Add(Path, f); #else Files.Add(f); #endif // Parse for headers... LTextFile Tf; if (!Tf.Open(Path, O_READ) || Tf.GetSize() < 4) { LgiTrace("%s:%i - Error: LTextFile.Open(%s) failed.\n", _FL, Path.Get()); return false; } LAutoString Source = Tf.Read(); LArray Headers; LArray EmptyInc; if (BuildHeaderList(Source, Headers, f->Inc ? *f->Inc : EmptyInc, false)) { for (auto h: Headers) AddFile(h, 0); } Headers.DeleteArrays(); // Parse for symbols... #ifdef DEBUG_FILE if (Debug) printf("%s:%i - About to parse '%s'.\n", _FL, f->Path.Get()); #endif return f->Parse(LAutoWString(Utf8ToWide(Source)), Debug); } bool ReparseFile(LString Path) { #if USE_HASH - FileSyms *f = Files.Find(Path); - int Platform = f ? f->Platforms : 0; + FileSyms *f = Files.Find(Path); + int Platform = f ? f->Platforms : 0; #else - int Idx = GetFileIndex(Path); - int Platform = Idx >= 0 ? Files[Idx]->Platforms : 0; + int Idx = GetFileIndex(Path); + int Platform = Idx >= 0 ? Files[Idx]->Platforms : 0; #endif if (!RemoveFile(Path)) return false; return AddFile(Path, Platform); } bool RemoveFile(LString Path) { #if USE_HASH - FileSyms *f = Files.Find(Path); - if (!f) return false; - Files.Delete(Path); - delete f; + FileSyms *f = Files.Find(Path); + if (!f) return false; + Files.Delete(Path); + delete f; #else - int Idx = GetFileIndex(Path); - if (Idx < 0) return false; - delete Files[Idx]; - Files.DeleteAt(Idx); + int Idx = GetFileIndex(Path); + if (Idx < 0) return false; + delete Files[Idx]; + Files.DeleteAt(Idx); #endif return true; } LMessage::Result OnEvent(LMessage *Msg) { if (IsCancelled()) return -1; switch (Msg->Msg()) { case M_FIND_SYM_REQUEST: { LAutoPtr Req((FindSymRequest*)Msg->A()); int Platforms = Msg->B(); if (Req && Req->SinkHnd >= 0) { LString::Array p = Req->Str.SplitDelimit(" \t"); if (p.Length() == 0) break; LArray ClassMatches; LArray HdrMatches; LArray SrcMatches; // For each file... #if USE_HASH for (auto it : Files) { FileSyms *fs = it.value; #else for (unsigned f=0; fPath.Find(DEBUG_FILE) >= 0; if (Debug) printf("%s:%i - Searching '%s' with %i syms...\n", _FL, fs->Path.Get(), (int)fs->Defs.Length()); #endif // Check platforms... if ((fs->Platforms & Platforms) == 0) { + #ifdef DEBUG_FILE + printf("%s:%i - '%s' doesn't match platform: %x %x\n", _FL, fs->Path.Get(), fs->Platforms, Platforms); + #endif continue; } // For each symbol... for (unsigned i=0; iDefs.Length(); i++) { DefnInfo &Def = fs->Defs[i]; #ifdef DEBUG_FILE if (Debug) printf("%s:%i - '%s'\n", _FL, Def.Name.Get()); #endif // For each search term... bool Match = true; int ScoreSum = 0; for (unsigned n=0; nScore = ScoreSum; r->File = Def.File.Get(); r->Symbol = Def.Name.Get(); r->Line = Def.Line; if (Def.Type == DefnClass) ClassMatches.Add(r); else if (fs->IsHeader) HdrMatches.Add(r); else SrcMatches.Add(r); } } } } } ClassMatches.Sort(ScoreCmp); Req->Results.Add(ClassMatches); Req->Results.Add(HdrMatches); Req->Results.Add(SrcMatches); int Hnd = Req->SinkHnd; PostObject(Hnd, M_FIND_SYM_REQUEST, Req); } break; } case M_FIND_SYM_FILE: { LAutoPtr Params((FindSymbolSystem::SymFileParams*)Msg->A()); if (Params) { LString::Array Mime = LGetFileMimeType(Params->File).Split("/"); if (!Mime[0].Equals("image")) { if (Params->Action == FindSymbolSystem::FileAdd) AddFile(Params->File, Params->Platforms); else if (Params->Action == FindSymbolSystem::FileRemove) RemoveFile(Params->File); else if (Params->Action == FindSymbolSystem::FileReparse) ReparseFile(Params->File); } } SYM_FILE_SENT--; break; } case M_FIND_SYM_INC_PATHS: { LAutoPtr Paths((LString::Array*)Msg->A()); if (Paths) IncPaths.Add(Paths.Release()); break; } default: { LAssert(!"Implement handler for message."); break; } } auto Now = LCurrentTime(); // printf("Msg->Msg()=%i " LPrintfInt64 " %i\n", Msg->Msg(), MsgTs, (int)GetQueueSize()); if (Now - MsgTs > MSG_TIME_MS) { MsgTs = Now; DoingProgress = true; int Remaining = (int)(Tasks - GetQueueSize()); if (Remaining > 0) Log("FindSym: %i of %i (%.1f%%)\n", Remaining, Tasks, (double)Remaining * 100.0 / MAX(Tasks, 1)); } else if (GetQueueSize() == 0 && MsgTs) { if (DoingProgress) { Log("FindSym: Done.\n"); DoingProgress = false; } if (MissingFiles > 0) { Log("(%i files are missing)\n", MissingFiles); } MsgTs = 0; Tasks = 0; } return 0; } }; int AlphaCmp(LListItem *a, LListItem *b, NativeInt d) { return stricmp(a->GetText(0), b->GetText(0)); } FindSymbolDlg::FindSymbolDlg(LViewI *parent, FindSymbolSystem *sys) { Lst = NULL; Sys = sys; SetParent(parent); if (LoadFromResource(IDD_FIND_SYMBOL)) { MoveToCenter(); LViewI *f; if (GetViewById(IDC_STR, f)) f->Focus(true); GetViewById(IDC_RESULTS, Lst); } } FindSymbolDlg::~FindSymbolDlg() { } void FindSymbolDlg::OnCreate() { RegisterHook(this, LKeyEvents); } bool FindSymbolDlg::OnViewKey(LView *v, LKey &k) { switch (k.vkey) { case LK_UP: case LK_DOWN: case LK_PAGEDOWN: case LK_PAGEUP: { return Lst->OnKey(k); break; } } return false; } LMessage::Result FindSymbolDlg::OnEvent(LMessage *m) { switch (m->Msg()) { case M_FIND_SYM_REQUEST: { LAutoPtr Req((FindSymRequest*)m->A()); if (Req) { LString Str = GetCtrlName(IDC_STR); if (Str == Req->Str) { Lst->Empty(); List Ls; LString s; int CommonPathLen = 0; for (unsigned i=0; iResults.Length(); i++) { FindSymResult *r = Req->Results[i]; if (i) { char *a = s.Get(); char *a_end = strrchr(a, DIR_CHAR); char *b = r->File.Get(); char *b_end = strrchr(b, DIR_CHAR); int Common = 0; while ( *a && a <= a_end && *b && b <= b_end && ToLower(*a) == ToLower(*b)) { Common++; a++; b++; } if (i == 1) CommonPathLen = Common; else CommonPathLen = MIN(CommonPathLen, Common); } else s = r->File; } for (unsigned i=0; iResults.Length(); i++) { FindSymResult *r = Req->Results[i]; LListItem *it = new LListItem; if (it) { LString Ln; Ln.Printf("%i", r->Line); it->SetText(r->File.Get() + CommonPathLen, 0); it->SetText(Ln, 1); it->SetText(r->Symbol, 2); Ls.Insert(it); } } Lst->Insert(Ls); Lst->ResizeColumnsToContent(); } } break; } } return LDialog::OnEvent(m); } int FindSymbolDlg::OnNotify(LViewI *v, LNotification n) { switch (v->GetId()) { case IDC_STR: { if (n.Type != LNotifyReturnKey) { auto Str = v->Name(); if (Str && strlen(Str) > 2) { // Create a search Sys->Search(AddDispatch(), Str, true); } } break; } case IDC_RESULTS: { if (n.Type == LNotifyItemDoubleClick) { // Fall throu } else break; } case IDOK: { if (Lst) { LListItem *i = Lst->GetSelected(); if (i) { Result.File = i->GetText(0); Result.Line = atoi(i->GetText(1)); } } EndModal(true); break; } case IDCANCEL: { EndModal(false); break; } } return LDialog::OnNotify(v, n); } /////////////////////////////////////////////////////////////////////////// FindSymbolSystem::FindSymbolSystem(int AppHnd) { d = new FindSymbolSystemPriv(AppHnd); } FindSymbolSystem::~FindSymbolSystem() { delete d; } void FindSymbolSystem::OpenSearchDlg(LViewI *Parent, std::function Callback) { FindSymbolDlg *Dlg = new FindSymbolDlg(Parent, this); Dlg->DoModal([Dlg, Callback](auto d, auto code) { if (Callback) Callback(Dlg->Result); delete Dlg; }); } bool FindSymbolSystem::SetIncludePaths(LString::Array &Paths) { LString::Array *a = new LString::Array; if (!a) return false; *a = Paths; return d->PostEvent(M_FIND_SYM_INC_PATHS, (LMessage::Param)a); } bool FindSymbolSystem::OnFile(const char *Path, SymAction Action, int Platforms) { if (d->Tasks == 0) d->MsgTs = LCurrentTime(); d->Tasks++; LAutoPtr Params(new FindSymbolSystem::SymFileParams); Params->File = Path; Params->Action = Action; Params->Platforms = Platforms; if (d->PostObject(d->GetHandle(), M_FIND_SYM_FILE, Params)) { SYM_FILE_SENT++; } return false; } void FindSymbolSystem::Search(int ResultsSinkHnd, const char *SearchStr, int Platforms) { FindSymRequest *Req = new FindSymRequest(ResultsSinkHnd); if (Req) { Req->Str = SearchStr; d->PostEvent(M_FIND_SYM_REQUEST, (LMessage::Param)Req, (LMessage::Param)Platforms); } } diff --git a/Ide/Code/ParserCommon.h b/Ide/Code/ParserCommon.h --- a/Ide/Code/ParserCommon.h +++ b/Ide/Code/ParserCommon.h @@ -1,160 +1,165 @@ #ifndef _PARSER_COMMON_H_ #define _PARSER_COMMON_H_ #include "lgi/common/LexCpp.h" #define isword(s) (s && (isdigit(s) || isalpha(s) || (s) == '_') ) #define iswhite(s) (s && strchr(WhiteSpace, s) != 0) #define skipws(s) while (iswhite(*s)) s++; #define defnskipws(s) while (iswhite(*s)) { if (*s == '\n') Line++; s++; } #define defnskipsym(s) while (IsAlpha(*s) || IsDigit(*s) || strchr("_:.~", *s)) { s++; } #define IsValidVariableChar(ch) (IsAlpha(ch) || IsDigit(ch) || strchr("_", ch) != NULL) enum DefnType { DefnNone, DefnDefine = 0x1, DefnFunc = 0x2, DefnClass = 0x4, DefnEnum = 0x8, DefnEnumValue = 0x10, DefnTypedef = 0x20, DefnVariable = 0x40, }; extern bool ParseFunction(LRange &Return, LRange &Name, LRange &Args, const char *Defn); class DefnInfo { public: DefnType Type; LString Name; LString File; int Line; LRange FnName; DefnInfo() { Type = DefnNone; Line = 0; } DefnInfo(const DefnInfo &d) { Type = d.Type; Name = d.Name; File = d.File; Line = d.Line; } void Set(DefnType type, const char *file, LString s, int line) { if (s(0) == ')') printf("%s:%i - Unexpected ')'.\n", _FL); Type = type; File = file; Line = line; Name = s.Strip(); if (Name && Type == DefnFunc) { LRange Return, Args; - if (ParseFunction(Return, FnName, Args, Name)) + if (!ParseFunction(Return, FnName, Args, Name)) + { + printf("%s:%i - ParseFunction failed at %s:%i, %s\n", + _FL, LGetLeaf(file), line, s.Get()); + } + else { if (strlen(Name) > 42) { char *b = strchr(Name, '('); if (b) { if (strlen(b) > 5) { strcpy(b, "(...)"); } else { *b = 0; } } } } char *t; while ((t = strchr(Name, '\t'))) { *t = ' '; } } } int Find(const char *Str) { auto Slen = strlen(Str); LString Src; if (Type == DefnFunc) Src = Name(FnName.Start, FnName.End()); else Src = Name; char *Match = stristr(Src, Str); if (!Match) return 0; auto Idx = Match - Src.Get(); if (Idx < 0) return 0; int Score = 1; // Is it an exact match? bool Exact = ( // Start: Idx == 0 || !IsValidVariableChar(Src(Idx-1)) ) && ( Idx+Slen >= Name.Length() || !IsValidVariableChar(Src(Idx+Slen)) ); if (Exact) Score++; return Score; } }; extern const char *TypeToStr(DefnType t); extern bool BuildCppDefnList ( const char *FileName, char16 *Cpp, LArray &Funcs, /// Use DefnType bits int LimitTo, bool Debug = false ); extern bool BuildPyDefnList(const char *FileName, char16 *Source, LArray &Defns, int LimitTo, bool Debug = false); extern bool BuildJsDefnList(const char *FileName, char16 *Source, LArray &Defns, int LimitTo, bool Debug = false); inline bool BuildDefnList(const char *FileName, char16 *Source, LArray &Funcs, int LimitTo, bool Debug = false) { auto Ext = LGetExtension(FileName); auto Fn = BuildCppDefnList; if (Ext) { if (!stricmp(Ext, "py")) Fn = BuildPyDefnList; else if (!stricmp(Ext, "js")) Fn = BuildJsDefnList; } return Fn(FileName, Source, Funcs, LimitTo, Debug); } #endif diff --git a/Ide/Code/SimpleCppParser.cpp b/Ide/Code/SimpleCppParser.cpp --- a/Ide/Code/SimpleCppParser.cpp +++ b/Ide/Code/SimpleCppParser.cpp @@ -1,952 +1,982 @@ /* Known bugs: 1) Duplicate brackets in #defines, e.g: #if SOME_DEF if (expr) { #else if (different_expr) { #endif Breaks the bracket counting. Causing the depth to get out of sync with capture... Workaround: Move the brackets outside of the #define'd area if possible. 2) This syntax is wrongly assumed to be a structure: struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback) { ... } Can't find these symbols: do_recv (in api_msg.c) process_apcp_msg (in ape-apcp.c) recv_udp (in api_msg.c) Wrong position: lwip_select (out by 4 lines?) nad_apcp_send tcpip_callback_with_block appears 3 times? */ #include "lgi/common/Lgi.h" #include "lgi/common/DocView.h" #include "ParserCommon.h" -#if 0 -#define DEBUG_FILE "FileSelect.cpp" -#endif -#define DEBUG_LINE 400 +// #define DEBUG_FILE "Gdc2.h" +// #define DEBUG_LINE 65 const char *TypeToStr(DefnType t) { switch (t) { default: case DefnNone: return "DefnNone"; case DefnDefine: return "DefnDefine"; case DefnFunc: return "DefnFunc"; case DefnClass: return "DefnClass"; case DefnEnum: return "DefnEnum"; case DefnEnumValue: return "DefnEnumValue"; case DefnTypedef: return "DefnTypedef"; case DefnVariable: return "DefnVariable"; } } bool IsFirst(LArray &a, int depth) { if (depth == 0) return true; for (int i=0; i 1) return false; return true; } bool IsFuncNameChar(char c) { return strchr("_:=~[]<>-+", c) || IsAlpha(c) || IsDigit(c); } bool ParseFunction(LRange &Return, LRange &Name, LRange &Args, const char *Defn) { if (!Defn) return false; int Depth = 0; const char *c, *Last = NULL; for (c = Defn; *c; c++) { if (*c == '(') { if (Depth == 0) Last = c; Depth++; } else if (*c == ')') { if (Depth > 0) Depth--; else { LgiTrace("%s:%i - Fn parse error '%s' (%i)\n", _FL, Defn, (int)(c - Defn)); return false; } } } if (!Last) return false; Args.Start = Last - Defn; Args.Len = c - Last; while (Last > Defn && IsWhiteSpace(Last[-1])) Last--; while (Last > Defn && IsFuncNameChar(Last[-1])) Last--; Return.Start = 0; Return.Len = Last - Defn; Name.Start = Last - Defn; Name.Len = Args.Start - Name.Start; if (Name.Len == 0 || Args.Len == 0) { + /* LgiTrace("%s:%i - Fn parse empty section '%s' (%i,%i,%i)\n", _FL, Defn, (int)Return.Len, (int)Name.Len, (int)Args.Len); + */ return false; } return true; } bool SeekPtr(char16 *&s, char16 *end, int &Line) { if (s > end) { LAssert(0); return false; } while (s < end) { if (*s == '\n') Line++; s++; } return true; } DefnType GuessDefnType(char16 *def, bool debug) { // In the context of a class member, this could be a variable defn: // // bool myVar = true; // bool myVar = someFn(); // // or a function defn: // // bool myFunction(int someArg = 10); // // Try to guess which it is: int roundDepth = 0; int equalsAtZero = 0; for (auto s = def; *s; s++) { if (*s == '(') roundDepth++; else if (*s == ')') roundDepth--; else if (*s == '=') { if (roundDepth == 0) equalsAtZero++; } } /* if (equalsAtZero && debug) printf("equalsAtZero: %S\n", def); */ return equalsAtZero ? DefnVariable : DefnFunc; } bool BuildCppDefnList(const char *FileName, char16 *Cpp, LArray &Defns, int LimitTo, bool Debug) { if (!Cpp) return false; static char16 StrClass[] = {'c', 'l', 'a', 's', 's', 0}; static char16 StrStruct[] = {'s', 't', 'r', 'u', 'c', 't', 0}; static char16 StrEnum[] = {'e', 'n', 'u', 'm', 0}; static char16 StrOpenBracket[] = {'{', 0}; static char16 StrColon[] = {':', 0}; static char16 StrSemiColon[] = {';', 0}; static char16 StrDefine[] = {'d', 'e', 'f', 'i', 'n', 'e', 0}; static char16 StrExtern[] = {'e', 'x', 't', 'e', 'r', 'n', 0}; static char16 StrTypedef[] = {'t', 'y', 'p', 'e', 'd', 'e', 'f', 0}; static char16 StrC[] = {'\"', 'C', '\"', 0}; + static char16 StrHash[] = {'#', 0}; char WhiteSpace[] = " \t\r\n"; char16 *s = Cpp; char16 *LastDecl = s; int Depth = 0; int Line = 0; #ifdef DEBUG_LINE int PrevLine = 0; #endif int CaptureLevel = 0; int InClass = false; // true if we're in a class definition char16 *CurClassDecl = 0; bool IsEnum = 0, IsClass = false, IsStruct = false; bool FnEmit = false; // don't emit functions between a f(n) and the next '{' // they are only parent class initializers LArray ConditionalIndex; int ConditionalDepth = 0; bool ConditionalFirst = true; bool ConditionParsingErr = false; #ifdef DEBUG_FILE Debug |= FileName && stristr(FileName, DEBUG_FILE) != NULL; #endif while (s && *s) { // skip ws while (*s && strchr(" \t\r", *s)) s++; #ifdef DEBUG_LINE if (Debug) { if (Line >= DEBUG_LINE - 1) { int asd=0; } else if (PrevLine != Line) { PrevLine = Line; // LgiTrace("%s:%i '%.10S'\n", FileName, Line + 1, s); } } #endif // tackle decl switch (*s) { case 0: break; case '\n': { Line ++; s++; break; } case '#': { const char16 *Hash = s; s++; skipws(s) const char16 *End = s; while (*End && IsAlpha(*End)) End++; if ( ( LimitTo == DefnNone || (LimitTo & DefnDefine) != 0 ) && (End - s) == 6 && StrncmpW(StrDefine, s, 6) == 0 ) { s += 6; defnskipws(s); LexCpp(s, LexNoReturn); char16 r = *s; *s = 0; Defns.New().Set(DefnDefine, FileName, Hash, Line + 1); *s = r; } char16 *Eol = Strchr(s, '\n'); if (!Eol) Eol = s + Strlen(s); // bool IsIf = false, IsElse = false, IsElseIf = false; if ( ((End - s) == 2 && !Strncmp(L"if", s, End - s)) || ((End - s) == 5 && !Strncmp(L"ifdef", s, End - s)) || ((End - s) == 6 && !Strncmp(L"ifndef", s, End - s)) ) { ConditionalIndex[ConditionalDepth] = 1; ConditionalDepth++; ConditionalFirst = IsFirst(ConditionalIndex, ConditionalDepth); if (Debug) - LgiTrace("%s:%i - ConditionalDepth++=%i Line=%i\n%.*S\n", _FL, ConditionalDepth, Line+1, Eol - s + 1, s - 1); + LgiTrace("%s:%i - ConditionalDepth++=%i Line=%i: %.*S\n", _FL, ConditionalDepth, Line+1, Eol - s + 1, s - 1); } else if ( ((End - s) == 4 && !Strncmp(L"else", s, End - s)) || ((End - s) == 7 && !Strncmp(L"else if", s, 7)) ) { if (ConditionalDepth <= 0 && !ConditionParsingErr) { ConditionParsingErr = true; LgiTrace("%s:%i - Error parsing pre-processor conditions: %s:%i\n", _FL, FileName, Line+1); } if (ConditionalDepth > 0) { ConditionalIndex[ConditionalDepth-1]++; ConditionalFirst = IsFirst(ConditionalIndex, ConditionalDepth); if (Debug) - LgiTrace("%s:%i - ConditionalDepth=%i Idx++ Line=%i\n%.*S\n", _FL, ConditionalDepth, Line+1, Eol - s + 1, s - 1); + LgiTrace("%s:%i - ConditionalDepth=%i Idx++ Line=%i: %.*S\n", _FL, ConditionalDepth, Line+1, Eol - s + 1, s - 1); } } else if ( ((End - s) == 5 && !Strncmp(L"endif", s, End - s)) ) { if (ConditionalDepth <= 0 && !ConditionParsingErr) { ConditionParsingErr = true; LgiTrace("%s:%i - Error parsing pre-processor conditions: %s:%i\n", _FL, FileName, Line+1); } if (ConditionalDepth > 0) ConditionalDepth--; ConditionalFirst = IsFirst(ConditionalIndex, ConditionalDepth); if (Debug) - LgiTrace("%s:%i - ConditionalDepth--=%i Line=%i\n%.*S\n", _FL, ConditionalDepth, Line+1, Eol - s + 1, s - 1); + LgiTrace("%s:%i - ConditionalDepth--=%i Line=%i: %.*S\n", _FL, ConditionalDepth, Line+1, Eol - s + 1, s - 1); } while (*s) { if (*s == '\n') { // could be end of # command char Last = (s[-1] == '\r') ? s[-2] : s[-1]; if (Last != '\\') break; Line++; } s++; LastDecl = s; } break; } case '\"': case '\'': { char16 Delim = *s; s++; while (*s) { if (*s == Delim) { s++; break; } if (*s == '\\') s++; if (*s == '\n') Line++; s++; } break; } case '{': { s++; if (ConditionalFirst) Depth++; FnEmit = false; - #if 0 // def DEBUG_FILE + #ifdef DEBUG_FILE if (Debug) LgiTrace("%s:%i - FnEmit=%i Depth=%i @ line %i\n", _FL, FnEmit, Depth, Line+1); #endif break; } case '}': { s++; if (ConditionalFirst) { if (Depth > 0) { Depth--; - #if 0 // def DEBUG_FILE + #ifdef DEBUG_FILE if (Debug) LgiTrace("%s:%i - CaptureLevel=%i Depth=%i @ line %i\n", _FL, CaptureLevel, Depth, Line+1); #endif } else { - #if 0 // def DEBUG_FILE + #ifdef DEBUG_FILE if (Debug) LgiTrace("%s:%i - ERROR Depth already=%i @ line %i\n", _FL, Depth, Line+1); #endif } } defnskipws(s); LastDecl = s; if (IsEnum) { if (IsAlpha(*s)) // typedef'd enum? { LAutoWString t(LexCpp(s, LexStrdup)); if (t) Defns.New().Set(DefnEnum, FileName, t.Get(), Line + 1); } IsEnum = false; } break; } case ';': { s++; LastDecl = s; if (Depth == 0 && InClass) { // Check for typedef struct name char16 *Start = s - 1; while (Start > Cpp && Start[-1] != '}') Start--; LString TypeDef = LString(Start, s - Start - 1).Strip(); if (TypeDef.Length() > 0) { if (LimitTo == DefnNone || (LimitTo & DefnClass) != 0) { Defns.New().Set(DefnClass, FileName, TypeDef, Line + 1); } } // End the class def InClass = false; CaptureLevel = 0; - #if 0 // def DEBUG_FILE + #ifdef DEBUG_FILE if (Debug) LgiTrace("%s:%i - CaptureLevel=%i Depth=%i @ line %i\n", _FL, CaptureLevel, Depth, Line+1); #endif DeleteArray(CurClassDecl); } break; } case '/': { s++; if (*s == '/') { // one line comment while (*s && *s != '\n') s++; LastDecl = s; } else if (*s == '*') { // multi line comment s++; while (*s) { if (s[0] == '*' && s[1] == '/') { s += 2; break; } if (*s == '\n') Line++; s++; } LastDecl = s; } break; } case '(': { s++; if (Depth == CaptureLevel && !FnEmit && LastDecl && ConditionalFirst) { // function? // find start: char16 *Start = LastDecl; skipws(Start); if (Strnstr(Start, L"__attribute__", s - Start)) break; // find end (matching closing bracket) int RoundDepth = 1; char16 *End = s; while (*End) { if (*End == '/' && End[1] == '/') { End = Strchr(End, '\n'); if (!End) break; } if (*End == '(') { RoundDepth++; } else if (*End == ')') { if (--RoundDepth == 0) break; } End++; } if (End && *End) { End++; auto Buf = NewStrW(Start, End-Start); if (Buf) { // remove new-lines auto Out = Buf; for (auto In = Buf; *In; In++) { if (*In == '\r' || *In == '\n' || *In == '\t' || *In == ' ') { *Out++ = ' '; skipws(In); In--; } else if (In[0] == '/' && In[1] == '/') { In = StrchrW(In, '\n'); if (!In) break; } else { *Out++ = *In; } } *Out++ = 0; if (CurClassDecl) { char16 Str[1024]; ZeroObj(Str); StrncpyW(Str, Buf, CountOf(Str)); char16 *b = StrchrW(Str, '('); if (b) { // Skip whitespace between name and '(' while ( b > Str && strchr(WhiteSpace, b[-1]) ) { b--; } // Skip over to the start of the name.. while ( b > Str && b[-1] != '*' && b[-1] != '&' && !strchr(WhiteSpace, b[-1]) ) { b--; } auto ClsLen = StrlenW(CurClassDecl); auto BLen = StrlenW(b); if (ClsLen + BLen + 1 > CountOf(Str)) { LgiTrace("%s:%i - Defn too long: %i\n", _FL, ClsLen + BLen + 1); } else { memmove(b + ClsLen + 2, b, sizeof(*b) * (BLen+1)); memcpy(b, CurClassDecl, sizeof(*b) * ClsLen); b += ClsLen; *b++ = ':'; *b++ = ':'; DeleteArray(Buf); Buf = NewStrW(Str); } } } // cache f(n) def auto Type = GuessDefnType(Buf, Debug); if ( ( LimitTo == DefnNone || (LimitTo & Type) != 0 ) && *Buf != ')' ) Defns.New().Set(Type, FileName, Buf, Line + 1); DeleteArray(Buf); while (*End && !strchr(";:{#", *End)) End++; SeekPtr(s, End, Line); FnEmit = *End != ';'; #if 0 // def DEBUG_FILE if (Debug) LgiTrace("%s:%i - FnEmit=%i Depth=%i @ line %i\n", _FL, FnEmit, Depth, Line+1); #endif } } } + else + { + #ifdef DEBUG_FILE + if (Debug) + LgiTrace("%s:%i - Not attempting fn parse: depth=%i, capture=%i, fnEmit=%i, CondFirst=%i, %s:%i:%.20S\n", + _FL, Depth, CaptureLevel, FnEmit, ConditionalFirst, + LGetLeaf(FileName), Line, s-1); + #endif + } break; } default: { bool InTypedef = false; if (IsAlpha(*s) || IsDigit(*s) || *s == '_') { char16 *Start = s; s++; defnskipsym(s); auto TokLen = s - Start; if (TokLen == 6 && StrncmpW(StrExtern, Start, 6) == 0) { // extern "C" block char16 *t = LexCpp(s, LexStrdup); // "C" if (t && StrcmpW(t, StrC) == 0) { defnskipws(s); if (*s == '{') { Depth--; } } DeleteArray(t); } else if (TokLen == 7 && StrncmpW(StrTypedef, Start, 7) == 0) { // Typedef skipws(s); IsStruct = !Strnicmp(StrStruct, s, StrlenW(StrStruct)); IsClass = !Strnicmp(StrClass, s, StrlenW(StrClass)); if (IsStruct || IsClass) { Start = s; InTypedef = true; goto DefineStructClass; } IsEnum = !Strnicmp(StrEnum, s, StrlenW(StrEnum)); if (IsEnum) { Start = s; s += 4; goto DefineEnum; } LStringPipe p; char16 *i; for (i = Start; i && *i;) { switch (*i) { case ' ': case '\t': { p.Push(Start, i - Start); defnskipws(i); char16 sp[] = {' ', 0}; p.Push(sp); Start = i; break; } case '\n': Line++; // fall thru case '\r': { p.Push(Start, i - Start); i++; Start = i; break; } case '{': { p.Push(Start, i - Start); int Depth = 1; i++; while (*i && Depth > 0) { switch (*i) { case '{': Depth++; break; case '}': Depth--; break; case '\n': Line++; break; } i++; } Start = i; break; } case ';': { p.Push(Start, i - Start); s = i; i = 0; break; } default: { i++; break; } } } char16 *Typedef = p.NewStrW(); if (Typedef) { if (LimitTo == DefnNone || (LimitTo & DefnTypedef) != 0) { Defns.New().Set(DefnTypedef, FileName, Typedef, Line + 1); } DeleteArray(Typedef); } } else if ( ( TokLen == 5 && (IsClass = !StrncmpW(StrClass, Start, 5)) ) || ( TokLen == 6 && (IsStruct = !StrncmpW(StrStruct, Start, 6)) ) ) { DefineStructClass: - + // Class / Struct if (Depth == 0) { // Check if this is really a class/struct definition or just a reference char16 *next = s; - while (*next && !strchr(";(){", *next)) + while (*next) + { + // If we seek to the next line, check it's not a preprocessor directive. + if (*next == '\n') + { + next++; + while (*next && strchr(WhiteSpace, *next)) + next++; + if (*next == '#') + { + // Skip the processor line... + while (*next && *next != '\n') + next++; + } + continue; + } + else if (strchr(";(){", *next)) + break; + next++; - + } + if (*next == '{') { // Full definition InClass = true; CaptureLevel = 1; - #if 0 // def DEBUG_FILE + #ifdef DEBUG_FILE if (Debug) LgiTrace("%s:%i - CaptureLevel=%i Depth=%i @ line %i\n", _FL, CaptureLevel, Depth, Line+1); #endif char16 *n = Start + (IsClass ? StrlenW(StrClass) : StrlenW(StrStruct)), *t; List Tok; while (n && *n) { char16 *Last = n; if ((t = LexCpp(n, LexStrdup))) { if (StrcmpW(t, StrSemiColon) == 0) { DeleteArray(t); break; } - else if (StrcmpW(t, StrOpenBracket) == 0 || + else if (StrcmpW(t, StrHash) || + StrcmpW(t, StrOpenBracket) == 0 || StrcmpW(t, StrColon) == 0) { DeleteArray(CurClassDecl); CurClassDecl = *Tok.rbegin(); Tok.Delete(CurClassDecl); if (LimitTo == DefnNone || (LimitTo & DefnClass) != 0) { char16 r = *Last; *Last = 0; Defns.New().Set(DefnClass, FileName, Start, Line + 1); *Last = r; SeekPtr(s, next, Line); } DeleteArray(t); break; } else { Tok.Insert(t); } } else { LgiTrace("%s:%i - LexCpp failed at %s:%i.\n", _FL, FileName, Line+1); break; } } Tok.DeleteArrays(); } else if (InTypedef) { // Typedef'ing some other structure... // char16 *Start = s; LexCpp(s, LexNoReturn); defnskipws(s); LArray a; char16 *t; ssize_t StartRd = -1, EndRd = -1; while ((t = LexCpp(s, LexStrdup))) { if (StartRd < 0 && !StrcmpW(t, L"(")) StartRd = a.Length(); else if (EndRd < 0 && !StrcmpW(t, L")")) EndRd = a.Length(); if (!StrcmpW(t, StrSemiColon)) break; a.Add(t); } if (a.Length()) { auto iName = StartRd > 0 && EndRd > StartRd ? StartRd - 1 : a.Length() - 1; auto sName = a[iName]; Defns.New().Set(DefnTypedef, FileName, sName, Line + 1); a.DeleteArrays(); } } } } else if ( TokLen == 4 && StrncmpW(StrEnum, Start, 4) == 0 ) { DefineEnum: IsEnum = true; defnskipws(s); LAutoWString t(LexCpp(s, LexStrdup)); if (t && isalpha(*t)) { Defns.New().Set(DefnEnum, FileName, t.Get(), Line + 1); } } else if (IsEnum) { char16 r = *s; *s = 0; Defns.New().Set(DefnEnumValue, FileName, Start, Line + 1); *s = r; defnskipws(s); if (*s == '=') { s++; while (true) { defnskipws(s); defnskipsym(s); defnskipws(s); if (*s == 0 || *s == ',' || *s == '}') break; LAssert(*s != '\n'); s++; } } } if (s[-1] == ':') { LastDecl = s; } } else { s++; } break; } } } DeleteArray(CurClassDecl); if (Debug) { for (unsigned i=0; iType), def->File.Get(), def->Line, def->Name.Get()); } } return Defns.Length() > 0; } diff --git a/src/common/Lgi/WindowCommon.cpp b/src/common/Lgi/WindowCommon.cpp --- a/src/common/Lgi/WindowCommon.cpp +++ b/src/common/Lgi/WindowCommon.cpp @@ -1,208 +1,206 @@ // // LWindowCommon.cpp // LgiCarbon // // Created by Matthew Allen on 11/09/14. // // #include "lgi/common/Lgi.h" #include "lgi/common/Net.h" #include "lgi/common/DropFiles.h" void LWindow::ScaleSizeToDpi() { auto s = GetDpiScale(); auto p = GetPos(); p.SetSize( (int) (p.X() * s.x), (int) (p.Y() * s.y) ); SetPos(p); } void LWindow::BuildShortcuts(ShortcutMap &Map, LViewI *v) { if (!v) v = this; for (auto c: v->IterateViews()) { const char *n = c->Name(), *amp; if (n && (amp = strchr(n, '&'))) { amp++; if (*amp && *amp != '&') { uint8_t *i = (uint8_t*)amp; ssize_t sz = Strlen(amp); int32 ch = LgiUtf8To32(i, sz); if (ch) Map.Add(ToUpper(ch), c); } } BuildShortcuts(Map, c); } } void LWindow::MoveOnScreen() { LRect p = GetPos(); LArray Displays; LRect Screen(0, 0, -1, -1); if ( #if WINNATIVE !IsZoomed(Handle()) && !IsIconic(Handle()) && #endif LGetDisplays(Displays)) { int Best = -1; int Pixels = 0; int Close = 0x7fffffff; for (int i=0; ir); if (o.Valid()) { int Pix = o.X()*o.Y(); if (Best < 0 || Pix > Pixels) { Best = i; Pixels = Pix; } } else if (Pixels == 0) { int n = Displays[i]->r.Near(p); if (n < Close) { Best = i; Close = n; } } } if (Best >= 0) Screen = Displays[Best]->r; } if (!Screen.Valid()) Screen.Set(0, 0, GdcD->X()-1, GdcD->Y()-1); if (p.x2 >= Screen.x2) p.Offset(Screen.x2 - p.x2, 0); if (p.y2 >= Screen.y2) p.Offset(0, Screen.y2 - p.y2); if (p.x1 < Screen.x1) p.Offset(Screen.x1 - p.x1, 0); if (p.y1 < Screen.y1) p.Offset(0, Screen.y1 - p.y1); SetPos(p, true); Displays.DeleteObjects(); } void LWindow::MoveToCenter() { LRect Screen(0, 0, GdcD->X()-1, GdcD->Y()-1); LArray Displays; LRect p = GetPos(); p.Offset(-p.x1, -p.y1); if (LGetDisplays(Displays, &Screen) && Displays.Length() > 0) { GDisplayInfo *Dsp = NULL; for (auto d: Displays) { if (d->r.Overlap(&p)) { Dsp = d; break; } } if (!Dsp) goto ScreenPos; p.Offset(Dsp->r.x1 + ((Dsp->r.X() - p.X()) / 2), Dsp->r.y1 + ((Dsp->r.Y() - p.Y()) / 2)); } else { ScreenPos: p.Offset((Screen.X() - p.X()) / 2, (Screen.Y() - p.Y()) / 2); } - printf("center %s %s\n", p.GetStr(), Screen.GetStr()); - SetPos(p, true); Displays.DeleteObjects(); } void LWindow::MoveToMouse() { LMouse m; if (GetMouse(m, true)) { LRect p = GetPos(); p.Offset(-p.x1, -p.y1); p.Offset(m.x-(p.X()/2), m.y-(p.Y()/2)); SetPos(p, true); MoveOnScreen(); } } bool LWindow::MoveSameScreen(LViewI *View) { if (!View) { LAssert(0); return false; } auto Wnd = View->GetWindow(); LRect p = Wnd ? Wnd->GetPos() : View->GetPos(); int cx = p.x1 + (p.X() >> 4); int cy = p.y1 + (p.Y() >> 4); LRect np = GetPos(); np.Offset(cx - np.x1, cy - np.y1); SetPos(np); MoveOnScreen(); return true; } int LWindow::WillAccept(LDragFormats &Formats, LPoint Pt, int KeyState) { Formats.SupportsFileDrops(); return true; } int LWindow::OnDrop(LArray &Data, LPoint Pt, int KeyState) { int Status = DROPEFFECT_NONE; #ifdef DEBUG_DND LgiTrace("%s:%i - OnDrop Data=%i Pt=%i,%i Key=0x%x\n", _FL, Data.Length(), Pt.x, Pt.y, KeyState); #endif for (auto &dd: Data) { #ifdef DEBUG_DND LgiTrace("\tFmt=%s\n", dd.Format.Get()); #endif if (dd.IsFileDrop()) { LDropFiles Files(dd); if (Files.Length()) { Status = DROPEFFECT_COPY; OnReceiveFiles(Files); break; } } } return Status; }