1 /*
2 * This file licensed under the GNU General Public License, version 3
3 * http://www.gnu.org/licenses/gpl-3.0.html
4 *
5 */
6
7 #include "nativeparserf.h"
8
9 #include <sdk.h>
10 #ifndef CB_PRECOMP
11 #include <wx/regex.h>
12 #include <wx/log.h>
13 #include <wx/string.h>
14 #include <wx/tokenzr.h>
15 #include <wx/dir.h>
16 #include <wx/wfstream.h>
17 #include <wx/stopwatch.h>
18
19 #include <manager.h>
20 #include <configmanager.h>
21 #include <editormanager.h>
22 #include <projectmanager.h>
23 #include <pluginmanager.h>
24 #include <logmanager.h>
25 #include <cbauibook.h>
26 #include <cbeditor.h>
27 #include <cbproject.h>
28 #include <cbexception.h>
29 #include <projectloader_hooks.h>
30 #include <cbstyledtextctrl.h>
31 #include <tinyxml/tinyxml.h>
32 #endif
33 #include <cctype>
34
35 #include "workspacebrowserf.h"
36 #include "workspacebrowserbuilder.h"
37 #include "parserf.h"
38 #include "makefilegen.h"
39 #include "bufferparserthread.h"
40 #include "adddirparserthread.h"
41
42 static wxCriticalSection s_CurrentBufferCritSect;
43
44
45 int idWSPThreadEvent = wxNewId();
46 int idADirPThreadEvent = wxNewId();
47 int idBPThreadEvent = wxNewId();
48 int idWorkspaceReparseTimer = wxNewId();
49 int idASearchDirsReparseTimer = wxNewId();
BEGIN_EVENT_TABLE(NativeParserF,wxEvtHandler)50 BEGIN_EVENT_TABLE(NativeParserF, wxEvtHandler)
51 EVT_COMMAND(idWSPThreadEvent, wxEVT_COMMAND_ENTER, NativeParserF::OnUpdateWorkspaceBrowser)
52 EVT_COMMAND(idADirPThreadEvent, wxEVT_COMMAND_ENTER, NativeParserF::OnUpdateADirTokens)
53 EVT_COMMAND(idBPThreadEvent, wxEVT_COMMAND_ENTER, NativeParserF::OnUpdateCurrentFileTokens)
54 EVT_TIMER(idWorkspaceReparseTimer, NativeParserF::OnReparseWorkspaceTimer)
55 EVT_TIMER(idASearchDirsReparseTimer, NativeParserF::OnASearchDirsReparseTimer)
56 END_EVENT_TABLE()
57
58 NativeParserF::NativeParserF(FortranProject* forproj)
59 : m_pWorkspaceBrowser(0),
60 m_WorkspaceBrowserIsFloating(false),
61 m_pFortranProject(forproj),
62 m_WorkspaceReparseTimer(this, idWorkspaceReparseTimer),
63 m_ThreadPool(this, wxNewId(), 2, 2 * 1024 * 1024),
64 m_ASearchDirsReparseTimer(this, idASearchDirsReparseTimer)
65 {
66 }
67
~NativeParserF()68 NativeParserF::~NativeParserF()
69 {
70 RemoveWorkspaceBrowser();
71 ClearParser();
72 }
73
CreateWorkspaceBrowser()74 void NativeParserF::CreateWorkspaceBrowser()
75 {
76 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("fortran_project"));
77 m_WorkspaceBrowserIsFloating = cfg->ReadBool(_T("/as_floating_window"), false);
78
79 if (cfg->ReadBool(_T("/use_symbols_browser"), true))
80 {
81 if (!m_pWorkspaceBrowser)
82 {
83 if (!m_WorkspaceBrowserIsFloating)
84 {
85 // make this a tab in projectmanager notebook
86 cbAuiNotebook* bk = Manager::Get()->GetProjectManager()->GetUI().GetNotebook();
87 m_pWorkspaceBrowser = new WorkspaceBrowserF(bk, this, &m_Parser);
88 Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->AddPage(m_pWorkspaceBrowser, _("FSymbols"));
89 }
90 else
91 {
92 // make this a free floating/docking window
93 m_pWorkspaceBrowser = new WorkspaceBrowserF(Manager::Get()->GetAppWindow(), this, &m_Parser);
94 CodeBlocksDockEvent evt(cbEVT_ADD_DOCK_WINDOW);
95
96 evt.name = _T("FSymbolsBrowser");
97 evt.title = _("FSymbols browser");
98 evt.pWindow = m_pWorkspaceBrowser;
99 evt.dockSide = CodeBlocksDockEvent::dsRight;
100 evt.desiredSize.Set(200, 250);
101 evt.floatingSize.Set(200, 250);
102 evt.minimumSize.Set(150, 150);
103 evt.shown = true;
104 evt.hideable = true;
105 Manager::Get()->ProcessEvent(evt);
106 }
107 m_pWorkspaceBrowser->UpdateSash();
108 }
109 }
110 }
111
GetWorkspaceBrowser()112 WorkspaceBrowserF* NativeParserF::GetWorkspaceBrowser()
113 {
114 return m_pWorkspaceBrowser;
115 }
116
RemoveWorkspaceBrowser()117 void NativeParserF::RemoveWorkspaceBrowser()
118 {
119 if (m_pWorkspaceBrowser)
120 {
121 if (!m_WorkspaceBrowserIsFloating)
122 {
123 int idx = Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->GetPageIndex(m_pWorkspaceBrowser);
124 if (idx != -1)
125 Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->RemovePage(idx);
126 }
127 else
128 {
129 CodeBlocksDockEvent evt(cbEVT_REMOVE_DOCK_WINDOW);
130 evt.pWindow = m_pWorkspaceBrowser;
131 Manager::Get()->ProcessEvent(evt);
132 }
133 m_pWorkspaceBrowser->Destroy();
134 }
135 m_pWorkspaceBrowser = 0L;
136 }
137
UpdateWorkspaceBrowser(bool selectCurrentSymbol)138 void NativeParserF::UpdateWorkspaceBrowser(bool selectCurrentSymbol)
139 {
140 if (m_pWorkspaceBrowser && !Manager::IsAppShuttingDown())
141 {
142 wxCriticalSectionLocker locker(s_CritSect);
143
144 m_pWorkspaceBrowser->UpdateView();
145 }
146 MarkCurrentSymbol(selectCurrentSymbol);
147 }
148
AddParser(cbProject * project)149 void NativeParserF::AddParser(cbProject* project)
150 {
151 if (!project)
152 return;
153
154 ParseProject(project);
155 }
156
ClearParser()157 void NativeParserF::ClearParser()
158 {
159 m_Parser.Clear();
160 }
161
RemoveFromParser(cbProject * project)162 void NativeParserF::RemoveFromParser(cbProject* project)
163 {
164 if (Manager::Get()->GetProjectManager()->GetProjects()->GetCount() == 0)
165 {
166 m_Parser.Clear();
167 UpdateWorkspaceBrowser();
168
169 return;
170 }
171 if (!project)
172 return;
173
174 for (FilesList::iterator it = project->GetFilesList().begin(); it != project->GetFilesList().end(); ++it)
175 {
176 ProjectFile* pf = *it;
177 m_Parser.RemoveFile(pf->file.GetFullPath());
178 }
179 RemoveProjectFilesDependency(project);
180 }
181
IsFileFortran(const wxString & filename)182 bool NativeParserF::IsFileFortran(const wxString& filename)
183 {
184 FortranSourceForm fsForm;
185 return IsFileFortran(filename, fsForm);
186 }
187
IsFileFortran(const wxString & filename,FortranSourceForm & fsForm)188 bool NativeParserF::IsFileFortran(const wxString& filename, FortranSourceForm& fsForm)
189 {
190 return m_Parser.IsFileFortran(filename, fsForm);
191 }
192
AddFileToParser(const wxString & projectFilename,const wxString & filename)193 void NativeParserF::AddFileToParser(const wxString& projectFilename, const wxString& filename)
194 {
195 FortranSourceForm fsForm;
196 if (IsFileFortran(filename, fsForm))
197 {
198 m_Parser.Reparse(projectFilename, filename, fsForm);
199 }
200 }
201
RemoveFileFromParser(const wxString & filename)202 void NativeParserF::RemoveFileFromParser(const wxString& filename)
203 {
204 m_Parser.RemoveFile(filename);
205 }
206
ParseProject(cbProject * project)207 void NativeParserF::ParseProject(cbProject* project)
208 {
209 wxArrayString files;
210 FortranSourceForm fsForm;
211 ArrayOfFortranSourceForm fileForms;
212 wxArrayString prFilenameArr;
213 wxString prFName = project->GetFilename();
214
215 for (FilesList::iterator it = project->GetFilesList().begin(); it != project->GetFilesList().end(); ++it)
216 {
217 ProjectFile* pf = *it;
218
219 if (IsFileFortran(pf->relativeFilename, fsForm))
220 {
221 files.Add(pf->file.GetFullPath());
222 fileForms.push_back(fsForm);
223 prFilenameArr.Add(prFName);
224 }
225 }
226 if (!files.IsEmpty())
227 {
228 m_Parser.BatchParse(prFilenameArr, files, fileForms);
229 }
230 }
231
ReparseFile(const wxString & projectFilename,const wxString & filename)232 void NativeParserF::ReparseFile(const wxString& projectFilename, const wxString& filename)
233 {
234 FortranSourceForm fsForm;
235 if (IsFileFortran(filename, fsForm))
236 m_Parser.Reparse(projectFilename, filename, fsForm);
237 }
238
ReparseProject(cbProject * project)239 void NativeParserF::ReparseProject(cbProject* project)
240 {
241 wxStopWatch sw;
242
243 if (project && !Manager::IsAppShuttingDown())
244 {
245 wxString projectFilename = project->GetFilename();
246 for (FilesList::iterator it = project->GetFilesList().begin(); it != project->GetFilesList().end(); ++it)
247 {
248 ProjectFile* pf = *it;
249 ReparseFile(projectFilename, pf->file.GetFullPath());
250 }
251 }
252
253 Manager::Get()->GetLogManager()->DebugLog(F(_T("NativeParserF::ReparseProject: Reparse poject took %d ms."), sw.Time()));
254 }
255
ForceReparseWorkspace()256 void NativeParserF::ForceReparseWorkspace()
257 {
258 if (Manager::IsAppShuttingDown())
259 return;
260
261 cbProject* project = Manager::Get()->GetProjectManager()->GetActiveProject();
262 if (project && m_pWorkspaceBrowser)
263 m_pWorkspaceBrowser->SetActiveProject(project);
264 m_WorkspaceReparseTimer.Start(500, wxTIMER_ONE_SHOT);
265 }
266
OnReparseWorkspaceTimer(wxTimerEvent & event)267 void NativeParserF::OnReparseWorkspaceTimer(wxTimerEvent& event)
268 {
269 if (Manager::IsAppShuttingDown())
270 return;
271
272 if (s_WorkspaceParserMutex.TryLock() == wxMUTEX_NO_ERROR)
273 {
274 MakeWSFileList();
275 s_WorkspaceParserMutex.Unlock();
276
277 WorkspaceParserThread* thread = new WorkspaceParserThread(this, idWSPThreadEvent);
278 m_ThreadPool.AddTask(thread, true);
279 }
280
281 OnASearchDirsReparseTimer(event);
282 }
283
MakeWSFileList()284 void NativeParserF::MakeWSFileList()
285 {
286 FortranSourceForm fsForm;
287 m_WSFiles.clear();
288 m_WSFileForms.clear();
289
290 ProjectsArray* projects = Manager::Get()->GetProjectManager()->GetProjects();
291 for (size_t i = 0; i < projects->GetCount(); ++i)
292 {
293 cbProject* proj = projects->Item(i);
294 wxString prFName = proj->GetFilename();
295
296 for (FilesList::iterator it = proj->GetFilesList().begin(); it != proj->GetFilesList().end(); ++it)
297 {
298 ProjectFile* pf = *it;
299
300 if (IsFileFortran(pf->relativeFilename, fsForm))
301 {
302 m_WSFiles.Add(pf->file.GetFullPath());
303 m_WSFileForms.push_back(fsForm);
304 m_WSFilePFN.push_back(prFName);
305 }
306 }
307 }
308 }
309
MakeADirFileList()310 void NativeParserF::MakeADirFileList()
311 {
312 FortranSourceForm fsForm;
313
314 m_ADirFiles.clear();
315 m_ADirFileForms.clear();
316 m_ADirFNameToProjMap.clear();
317
318 for (auto it=m_ASearchDirs.begin(); it != m_ASearchDirs.end(); ++it)
319 {
320 wxArrayString files;
321 wxArrayString* pDirs = &it->second;
322 for (size_t i=0; i<pDirs->size(); ++i)
323 {
324 wxDir::GetAllFiles(pDirs->Item(i), &files, wxEmptyString, wxDIR_FILES);
325 }
326
327 size_t nfiles = files.size();
328 for (size_t i=0; i<nfiles; i++)
329 {
330 if (IsFileFortran(files.Item(i), fsForm))
331 {
332 if (m_ADirFNameToProjMap.count(files.Item(i)) == 0)
333 {
334 m_ADirFiles.Add(files.Item(i));
335 m_ADirFileForms.push_back(fsForm);
336
337 wxArrayString prarr;
338 prarr.Add(it->first);
339 m_ADirFNameToProjMap[files.Item(i)] = prarr;
340 }
341 else
342 {
343 wxArrayString* prarr = &m_ADirFNameToProjMap[files.Item(i)];
344 prarr->Add(it->first);
345 }
346 }
347 }
348 }
349 }
350
GetWSFiles()351 wxArrayString* NativeParserF::GetWSFiles()
352 {
353 return &m_WSFiles;
354 }
355
GetWSFileForms()356 ArrayOfFortranSourceForm* NativeParserF::GetWSFileForms()
357 {
358 return &m_WSFileForms;
359 }
360
GetWSFileProjFilenames()361 wxArrayString* NativeParserF::GetWSFileProjFilenames()
362 {
363 return &m_WSFilePFN;
364 }
365
GetADirFiles()366 wxArrayString* NativeParserF::GetADirFiles()
367 {
368 return &m_ADirFiles;
369 }
370
GetADirFileForms()371 ArrayOfFortranSourceForm* NativeParserF::GetADirFileForms()
372 {
373 return &m_ADirFileForms;
374 }
375
OnUpdateWorkspaceBrowser(wxCommandEvent &)376 void NativeParserF::OnUpdateWorkspaceBrowser(wxCommandEvent& /*event*/)
377 {
378 m_Parser.ConnectToNewTokens();
379 UpdateWorkspaceBrowser();
380 }
381
OnUpdateADirTokens(wxCommandEvent &)382 void NativeParserF::OnUpdateADirTokens(wxCommandEvent& /*event*/)
383 {
384 m_Parser.ConnectToNewADirTokens();
385 }
386
OnProjectActivated(cbProject * prj)387 void NativeParserF::OnProjectActivated(cbProject* prj)
388 {
389 if (!m_pWorkspaceBrowser)
390 return;
391
392 m_pWorkspaceBrowser->SetActiveProject(prj);
393 UpdateWorkspaceBrowser();
394 }
395
OnEditorActivated(EditorBase * editor)396 void NativeParserF::OnEditorActivated(EditorBase* editor)
397 {
398 if (!m_pWorkspaceBrowser)
399 return;
400 cbEditor* ed = editor && editor->IsBuiltinEditor() ? static_cast<cbEditor*>(editor) : 0;
401 if (ed)
402 {
403 wxString filename = ed->GetFilename();
404 if (m_pWorkspaceBrowser->GetBrowserDisplayFilter() == bdfFile && !m_pWorkspaceBrowser->GetActiveFilename().IsSameAs(filename))
405 {
406 UpdateWorkspaceBrowser(true);
407 }
408 }
409 }
410
OnEditorClose(EditorBase * editor)411 void NativeParserF::OnEditorClose(EditorBase* editor)
412 {
413 cbEditor* ed = editor && editor->IsBuiltinEditor() ? static_cast<cbEditor*>(editor) : 0;
414 if (ed)
415 {
416 m_Parser.RemoveBuffer(ed->GetFilename());
417 }
418 }
419
UpdateWorkspaceFilesDependency()420 void NativeParserF::UpdateWorkspaceFilesDependency()
421 {
422 ClearWSDependency();
423 ProjectsArray* projects = Manager::Get()->GetProjectManager()->GetProjects();
424
425 for (size_t i = 0; i < projects->GetCount(); ++i)
426 {
427 cbProject* proj = projects->Item(i);
428 if (!proj->IsMakefileCustom())
429 UpdateProjectFilesDependency(proj);
430 }
431 }
432
UpdateProjectFilesDependency(cbProject * project)433 void NativeParserF::UpdateProjectFilesDependency(cbProject* project)
434 {
435 project->SaveAllFiles();
436
437 ProjectFilesArray pfs;
438 for (FilesList::iterator it = project->GetFilesList().begin(); it != project->GetFilesList().end(); ++it)
439 {
440 ProjectFile* pf = *it;
441 if (IsFileFortran(pf->relativeFilename))
442 {
443 pfs.push_back(pf);
444 }
445 }
446
447 wxString fn = project->GetFilename();
448 WSDependencyMap::iterator pos;
449 pos = m_WSDependency.find(fn);
450 if (pos == m_WSDependency.end())
451 {
452 pos = m_WSDependency.insert(std::make_pair(fn,new ProjectDependencies(project))).first;
453 }
454 if (pfs.size() > 0)
455 {
456 pos->second->MakeProjectFilesDependencies(pfs, m_Parser);
457 pos->second->EnsureUpToDateObjs();
458
459 for (size_t i=0; i<pfs.size(); i++)
460 {
461 wxString fn2 = pfs[i]->file.GetFullPath();
462 unsigned short int wt = pos->second->GetFileWeight(fn2);
463 pfs[i]->weight = wt;
464 }
465 if (pos->second->HasInfiniteDependences())
466 {
467 wxString msg = _T("Warning. FortranProject plugin:\n");
468 msg << _T(" 'It seems you have a circular dependency in Fortran files. Check your USE or INCLUDE statements.'");
469 Manager::Get()->GetLogManager()->Log(msg);
470 cbMessageBox(_("It seems you have a circular dependency in Fortran files. Check your USE or INCLUDE statements."),
471 _("Warning"));
472 }
473 }
474 }
475
ClearWSDependency()476 void NativeParserF::ClearWSDependency()
477 {
478 WSDependencyMap::iterator pos=m_WSDependency.begin();
479 while(pos != m_WSDependency.end())
480 {
481 ProjectDependencies* pd = pos->second;
482 pd->Clear();
483 delete pd;
484 pos++;
485 }
486 m_WSDependency.clear();
487 }
488
RemoveProjectFilesDependency(cbProject * project)489 void NativeParserF::RemoveProjectFilesDependency(cbProject* project)
490 {
491 if (m_WSDependency.count(project->GetFilename()))
492 {
493 ProjectDependencies* pd = m_WSDependency[project->GetFilename()];
494 pd->Clear();
495 delete pd;
496 }
497 }
498
GetParser()499 ParserF* NativeParserF::GetParser()
500 {
501 return &m_Parser;
502 }
503
GetTokenKindImageIdx(TokenF * token)504 int NativeParserF::GetTokenKindImageIdx(TokenF* token)
505 {
506 if (m_pWorkspaceBrowser)
507 return m_pWorkspaceBrowser->GetTokenKindImageIdx(token);
508 return 0;
509 }
510
511 // count commas in lineText (nesting parentheses)
CountCommas(const wxString & lineText,int start,bool nesting)512 int NativeParserF::CountCommas(const wxString& lineText, int start, bool nesting)
513 {
514 int commas = 0;
515 int nest = 0;
516 bool inA = false;
517 bool inDA = false;
518 while (true)
519 {
520 wxChar c = lineText.GetChar(start);
521 start++;
522 if (c == '\0')
523 break;
524 else if (nesting && (c == '(' || c == '[') && !inA && !inDA)
525 ++nest;
526 else if (nesting && (c == ')' || c == ']') && !inA && !inDA)
527 {
528 --nest;
529 if (nest < 0)
530 break;
531 }
532 else if (c == '\'' && !inA && !inDA)
533 inA = true;
534 else if (c == '\'' && inA)
535 inA = false;
536 else if (c == '"' && !inA && !inDA)
537 inDA = true;
538 else if (c == '"' && inDA)
539 inDA = false;
540 else if (c == ',' && nest == 0 && !inA && !inDA)
541 ++commas;
542 }
543 return commas;
544 }
545
GetLastName(const wxString & line)546 wxString NativeParserF::GetLastName(const wxString& line)
547 {
548 wxString name;
549 wxString tmp = line;
550 tmp.Trim();
551 if (tmp.IsEmpty())
552 return name;
553 int cur = tmp.Len() - 1;
554
555 while (cur >= 0)
556 {
557 wxChar cch = tmp.GetChar(cur);
558 if (!isalnum(cch) && (cch != '_'))
559 {
560 cur++;
561 break;
562 }
563 else
564 cur--;
565 }
566 if (cur < 0)
567 cur = 0;
568 name = tmp.Mid(cur);
569
570 return name;
571 }
572
CollectInformationForCallTip(int & commasAll,int & commasUntilPos,wxString & argNameUnderCursor,wxString & lastName,bool & isAfterPercent,int & argsPos,TokensArrayFlat * result)573 void NativeParserF::CollectInformationForCallTip(int& commasAll, int& commasUntilPos, wxString& argNameUnderCursor, wxString& lastName,
574 bool& isAfterPercent, int& argsPos, TokensArrayFlat* result)
575 {
576 wxString lineText; // string before '('
577 CountCommasInEditor(commasAll, commasUntilPos, lastName, lineText, argsPos);
578 if (lastName.IsEmpty())
579 return;
580
581 lineText.Trim();
582 wxString lineTextMinus = lineText.Mid(0,lineText.Len()-lastName.Len());
583 wxString beforLast = GetLastName(lineTextMinus);
584 if (beforLast.IsSameAs(_T("subroutine"),false) || beforLast.IsSameAs(_T("function"),false))
585 {
586 lastName = _T("");
587 return; // we don't want calltips during procedure declaration
588 }
589
590 isAfterPercent = false;
591 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
592 if(!ed)
593 return;
594
595 GetDummyVarName(ed, argNameUnderCursor);
596
597 m_Parser.ChangeLineIfRequired(ed, lineText);
598
599 lineText.Trim();
600 TokensArrayFlatClass tokensTemp;
601 TokensArrayFlat* resultTemp = tokensTemp.GetTokens();
602 if (!m_Parser.FindMatchTypeComponents(ed, lineText, *resultTemp, false, false, isAfterPercent, true))
603 return;
604 if (resultTemp->GetCount() > 0)
605 {
606 TokenFlat* token = resultTemp->Item(0); // we take only first added item
607 result->Add( new TokenFlat(token) );
608 if (token->m_TokenKind == tkProcedure)
609 {
610 wxString tokName;
611 if (!token->m_PartLast.IsEmpty())
612 tokName = token->m_PartLast;
613 else
614 tokName = token->m_Name;
615
616 TokensArrayFlatClass tokensTmp;
617 TokensArrayFlat* resultTmp = tokensTmp.GetTokens();
618 int kindMask = tkFunction | tkSubroutine;
619 int noInChildren = tkInterface | tkFunction | tkSubroutine;
620 bool found = m_Parser.FindMatchTokenInSameModule(token, tokName, *resultTmp, kindMask, noInChildren);
621 if (!found)
622 m_Parser.FindMatchTokensDeclared(tokName, *resultTmp, kindMask, false, noInChildren);
623 if (resultTmp->GetCount() > 0)
624 result->Add( new TokenFlat(resultTmp->Item(0)) );
625 }
626 else if (token->m_TokenKind == tkInterface)
627 {
628 m_Parser.FindGenericTypeBoudComponents(token, *result);
629 for (size_t i=1; i<resultTemp->GetCount(); i++)
630 {
631 if (resultTemp->Item(i)->m_TokenKind == tkInterface)
632 {
633 result->Add( new TokenFlat(resultTemp->Item(i)));
634 m_Parser.FindGenericTypeBoudComponents(resultTemp->Item(i), *result);
635 }
636 }
637 }
638 }
639
640 }
641
642
CountCommasInEditor(int & commasAll,int & commasUntilPos,wxString & lastName,wxString & lineText,int & pos)643 void NativeParserF::CountCommasInEditor(int& commasAll, int& commasUntilPos, wxString& lastName, wxString& lineText, int &pos)
644 {
645 commasAll = 0;
646 commasUntilPos = 0;
647 lastName = wxEmptyString;
648 int end = 0;
649 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
650 if(!ed)
651 return;
652
653 cbStyledTextCtrl* control = ed->GetControl();
654 if (!control)
655 return;
656 int line = control->GetCurrentLine();
657 lineText = control->GetLine(line);
658 pos = control->PositionFromLine(line);
659 end = control->GetCurrentPos() - pos;
660
661 lineText = lineText.BeforeFirst('!');
662 if (int(lineText.Len()) < end)
663 return; // we are in comments
664 //join lines first, if we are in the continuation line
665 FortranSourceForm fsForm;
666 IsFileFortran(ed->GetShortName(), fsForm);
667
668 if (fsForm == fsfFree)
669 {
670 int line2 = line - 1;
671 while (line2 > 0)
672 {
673 wxString lineTextPast = control->GetLine(line2).BeforeFirst('!');
674 lineTextPast = lineTextPast.Trim();
675 if (!lineTextPast.IsEmpty())
676 {
677 int idx = lineTextPast.Find('&', true);
678 if (idx == wxNOT_FOUND)
679 {
680 break;
681 }
682 else
683 {
684 lineText = lineTextPast.Mid(0,idx) + lineText;
685 end += idx;
686 pos = control->PositionFromLine(line2);
687 }
688 }
689 line2--;
690 }
691 }
692 else //fsfFixed
693 {
694 if (lineText.Len() >= 6)
695 {
696 wxChar contS = lineText.GetChar(5);
697 if (contS != ' ' && contS != '0')
698 {
699 lineText = lineText.Mid(6);
700 pos += 6;
701 end -= 6;
702 int line2 = line - 1;
703 while (line2 > 0)
704 {
705 wxString lineTextPast = control->GetLine(line2).BeforeFirst('!');
706 lineTextPast = lineTextPast.Trim();
707 if (!lineTextPast.IsEmpty())
708 {
709 lineText = lineTextPast + lineText;
710 end += lineTextPast.Len();
711 pos = control->PositionFromLine(line2);
712 if (lineTextPast.Len() >= 6)
713 {
714 wxChar contS2 = lineTextPast.GetChar(5);
715 if (contS2 == ' ' || contS2 == '0')
716 break;
717 else
718 {
719 lineText = lineText.Mid(6);
720 pos += 6;
721 end -= 6;
722 }
723 }
724 else
725 break;
726 }
727 line2--;
728 }
729 }
730 }
731 else
732 {
733 return;
734 }
735 }
736
737 wxString lineTextUntilPos = lineText.Mid(0,end);
738 int nest = 0;
739
740 while (end > 0)
741 {
742 --end;
743 if (lineText.GetChar(end) == ')')
744 --nest;
745 else if (lineText.GetChar(end) == '(')
746 {
747 ++nest;
748 if (nest > 0)
749 {
750 // count commas (nesting parentheses again) to see how far we 're in arguments
751 commasAll = CountCommas(lineText, end + 1);
752 commasUntilPos = CountCommas(lineTextUntilPos, end + 1);
753 break;
754 }
755 }
756 }
757 if (!end)
758 return;
759
760 lineText.Truncate(end);
761 pos += lineText.Len();
762 lastName = GetLastName(lineText);
763 }
764
GetDummyVarName(cbEditor * ed,wxString & lastDummyVar)765 void NativeParserF::GetDummyVarName(cbEditor* ed, wxString& lastDummyVar)
766 {
767 // Get dummy arg name like 'vnam' in 'call sub1(a, b, vnam=...'
768 cbStyledTextCtrl* control = ed->GetControl();
769 if (!control)
770 return;
771 int clin = control->GetCurrentLine();
772 int lpos = control->PositionFromLine(clin);
773 int cpos = control->GetCurrentPos();
774 cpos = control->WordEndPosition(cpos, true);
775 while (cpos < control->GetLength())
776 {
777 wxChar c = control->GetCharAt(cpos);
778 if (c == ' ' || c == '=')
779 cpos++;
780 else
781 break;
782 }
783 wxString line = control->GetTextRange(lpos, cpos);
784
785 if (line.Find('!') != wxNOT_FOUND)
786 return;
787
788 int asig = line.Find('=', true);
789 if (asig == wxNOT_FOUND)
790 return;
791
792 int endIdx = 0;
793 int nest = 0;
794 bool inA = false;
795 bool inDA = false;
796 for (int i=line.Len()-1; i>=0; --i)
797 {
798 wxChar c = line.GetChar(i);
799 if (c == '\'' && !inA && !inDA)
800 inA = true;
801 else if (c == '\'' && inA)
802 inA = false;
803 else if (c == '"' && !inA && !inDA)
804 inDA = true;
805 else if (c == '"' && inDA)
806 inDA = false;
807 else if ((c == ')' || c == ']') && !inA && !inDA)
808 nest++;
809 else if ((c == '(' || c == '[') && nest == 0 && !inA && !inDA)
810 break;
811 else if ((c == '(' || c == '[') && !inA && !inDA)
812 nest--;
813 else if (c == ',' && nest == 0 && !inA && !inDA)
814 break;
815 else if (c == '=' && nest == 0 && !inA && !inDA)
816 {
817 endIdx = i;
818 break;
819 }
820 }
821
822 if (endIdx == 0)
823 return;
824
825 lastDummyVar = GetLastName(line.Mid(0, endIdx));
826 }
827
GetCallTips(const wxString & name,bool onlyUseAssoc,bool onlyPublicNames,wxArrayString & callTips,TokensArrayFlat * result)828 void NativeParserF::GetCallTips(const wxString& name, bool onlyUseAssoc, bool onlyPublicNames, wxArrayString& callTips, TokensArrayFlat* result)
829 {
830 int tokKind;
831 if (Manager::Get()->GetConfigManager(_T("fortran_project"))->ReadBool(_T("/call_tip_arrays"), true))
832 tokKind = tkFunction | tkSubroutine | tkInterface | tkType | tkVariable;
833 else
834 tokKind = tkFunction | tkSubroutine | tkInterface | tkType;
835
836 int resCountOld = result->GetCount();
837 if (onlyUseAssoc)
838 {
839 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
840 if (!ed)
841 return;
842 m_Parser.FindUseAssociatedTokens(onlyPublicNames, ed, name, false, *result, tokKind, false);
843 int noChildrenOf = tkInterface | tkModule | tkSubmodule | tkFunction | tkSubroutine | tkProgram;
844 m_Parser.FindMatchTokensDeclared(name, *result, tokKind, false, noChildrenOf, false, true); // take global procedures only
845
846 if (tokKind & tkVariable)
847 {
848 TokensArrayFlatClass tokensTmp;
849 TokensArrayFlat* resultTmp = tokensTmp.GetTokens();
850 m_Parser.FindMatchDeclarationsInCurrentScope(name, ed, *resultTmp, false);
851 for (size_t i=0; i<resultTmp->GetCount(); i++)
852 {
853 if (resultTmp->Item(i)->m_TokenKind == tkVariable)
854 result->Add(new TokenFlat(resultTmp->Item(i)));
855 }
856 }
857 }
858 else
859 {
860 int noChildrenOf = tkInterface | tkFunction | tkSubroutine | tkProgram;
861 m_Parser.FindMatchTokensDeclared(name, *result, tokKind, false, noChildrenOf, onlyPublicNames);
862 }
863
864 int tokkindFS = tkFunction | tkSubroutine;
865 int resCount = result->GetCount();
866 for (int i=resCountOld; i<resCount; ++i)
867 {
868 if (result->Item(i)->m_ParentTokenKind == tkSubmodule && (result->Item(i)->m_TokenKind & tokkindFS))
869 {
870 for (int j=i+1; j<resCount; ++j)
871 {
872 if (result->Item(j)->m_ParentTokenKind == tkInterfaceExplicit &&
873 result->Item(j)->m_TokenKind == result->Item(i)->m_TokenKind &&
874 result->Item(j)->m_Name.IsSameAs(result->Item(i)->m_Name) )
875 {
876 result->RemoveAt(i);
877 resCount--;
878 i--;
879 break;
880 }
881 }
882 }
883 }
884
885 resCount = result->GetCount();
886 for (int i=resCountOld; i<resCount; ++i)
887 {
888 if (result->Item(i)->m_TokenKind == tkInterface)
889 {
890 m_Parser.FindChildrenOfInterface(result->Item(i), *result);
891 result->RemoveAt(i);
892 resCount--;
893 i--;
894 }
895 }
896
897 resCount = result->GetCount();
898 for (int i=resCountOld; i<resCount; ++i)
899 {
900 if (result->Item(i)->m_TokenKind == tkVariable)
901 {
902 wxString callTipArr;
903 GetCallTipsForVariable(result->Item(i), callTipArr);
904 if (!callTipArr.IsEmpty())
905 callTips.Add(callTipArr);
906 }
907 else if (result->Item(i)->m_TokenKind == tkType)
908 {
909 if (resCountOld+1 != int(result->GetCount()))
910 {
911 // remove 'type' if it is not unique
912 result->RemoveAt(i);
913 resCount--;
914 i--;
915 }
916 else
917 {
918 // Default structure-constructor
919 wxString callTipType;
920 GetCallTipsForType(result->Item(i), callTipType);
921 if (!callTipType.IsEmpty())
922 callTips.Add(callTipType);
923 else
924 {
925 result->RemoveAt(i);
926 resCount--;
927 i--;
928 }
929 }
930 }
931 else
932 callTips.Add(result->Item(i)->m_Args);
933 }
934 }
935
GetCallTipsForGenericTypeBoundProc(TokensArrayFlat * result,wxArrayString & callTips,wxArrayInt & idxFuncSub)936 void NativeParserF::GetCallTipsForGenericTypeBoundProc(TokensArrayFlat* result, wxArrayString& callTips, wxArrayInt& idxFuncSub)
937 {
938 if (result->GetCount() >= 3 && result->Item(0)->m_TokenKind == tkInterface)
939 {
940 int tokKind = tkFunction | tkSubroutine;
941 for (size_t i=1; i<result->GetCount()-1; i+=2)
942 {
943 if (result->Item(i)->m_TokenKind == tkInterface)
944 i++;
945 if (i+1 >= result->GetCount())
946 return;
947 if (result->Item(i)->m_TokenKind != tkProcedure || !(result->Item(i+1)->m_TokenKind & tokKind))
948 return;
949
950 TokensArrayFlatClass tokensTmpCl;
951 TokensArrayFlat* tokensTmp = tokensTmpCl.GetTokens();
952 tokensTmp->Add(new TokenFlat(result->Item(i)));
953 tokensTmp->Add(new TokenFlat(result->Item(i+1)));
954 GetCallTipsForTypeBoundProc(tokensTmp, callTips);
955 idxFuncSub.Add(i+1);
956 }
957 }
958 }
959
GetCallTipsForTypeBoundProc(TokensArrayFlat * result,wxArrayString & callTips)960 void NativeParserF::GetCallTipsForTypeBoundProc(TokensArrayFlat* result, wxArrayString& callTips)
961 {
962 if (result->GetCount() != 2)
963 return;
964 if (!(result->Item(0)->m_TokenKind == tkProcedure))
965 return;
966
967 int tokKind = tkFunction | tkSubroutine;
968 if (!(result->Item(1)->m_TokenKind & tokKind))
969 return;
970
971 TokenFlat tbProcTok(result->Item(0));
972 m_Parser.ChangeArgumentsTypeBoundProc(tbProcTok, result->Item(1));
973 callTips.Add(tbProcTok.m_Args);
974 }
975
GetCallTipsForVariable(TokenFlat * token,wxString & callTip)976 void NativeParserF::GetCallTipsForVariable(TokenFlat* token, wxString& callTip)
977 {
978 callTip = wxEmptyString;
979 if (!(token->m_TokenKind == tkVariable))
980 return;
981
982 int dstart = token->m_TypeDefinition.Lower().Find(_T("dimension"));
983 if (dstart != wxNOT_FOUND)
984 {
985 wxString dim = token->m_TypeDefinition.Mid(dstart+9);
986 if (dim.size() > 0 && dim[0] == '(')
987 {
988 int last = dim.Find(')');
989 if (last != wxNOT_FOUND)
990 callTip = dim.Mid(0,last+1);
991 }
992 }
993 else if (token->m_Args.StartsWith(_T("(")))
994 {
995 int last = token->m_Args.Find(')');
996 if (last != wxNOT_FOUND)
997 callTip = token->m_Args.Mid(0,last+1);
998 }
999 }
1000
GetCallTipsForType(TokenFlat * token,wxString & callTip)1001 void NativeParserF::GetCallTipsForType(TokenFlat* token, wxString& callTip)
1002 {
1003 callTip = wxEmptyString;
1004 if (!(token->m_TokenKind == tkType))
1005 return;
1006
1007 if (token->m_IsAbstract || !token->m_ExtendsType.IsEmpty()) // no default constructor for Abstract or Extended type
1008 return;
1009 TokensArrayFlatClass tokensTmp;
1010 TokensArrayFlat* resultTmp = tokensTmp.GetTokens();
1011 m_Parser.GetTypeComponentsInFile(token->m_Filename, token->m_LineStart, token->m_Name, resultTmp);
1012
1013 wxString names;
1014 for (size_t i=0; i<resultTmp->GetCount(); i++)
1015 {
1016 if (resultTmp->Item(i)->m_TokenKind != tkVariable)
1017 continue;
1018
1019 names << resultTmp->Item(i)->m_DisplayName << _T(", ");
1020 }
1021
1022 if (!names.IsEmpty())
1023 {
1024 callTip << _T("(") << names.Mid(0,names.Length()-2) << _T(")");
1025 }
1026 }
1027
1028 // set start and end to the calltip highlight region, based on commasWas (calculated in GetCallTips())
GetCallTipHighlight(const wxString & calltip,int commasWas,int & start,int & end)1029 void NativeParserF::GetCallTipHighlight(const wxString& calltip, int commasWas, int& start, int& end)
1030 {
1031 m_Parser.GetCallTipHighlight(calltip, commasWas, start, end);
1032 }
1033
MarkCurrentSymbol(bool selectCurrentSymbol)1034 void NativeParserF::MarkCurrentSymbol(bool selectCurrentSymbol)
1035 {
1036 if (!m_pWorkspaceBrowser || Manager::IsAppShuttingDown())
1037 return;
1038
1039 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
1040 if (!ed)
1041 return;
1042 wxString activeFilename = ed->GetFilename();
1043 if (activeFilename.IsEmpty())
1044 return;
1045 if (!IsFileFortran(activeFilename))
1046 return;
1047 cbStyledTextCtrl* control = ed->GetControl();
1048 int currentLine = control->GetCurrentLine() + 1;
1049
1050 wxCriticalSectionLocker locker(s_CritSect);
1051 wxString fname = UnixFilename(activeFilename);
1052 m_pWorkspaceBrowser->MarkSymbol(fname, currentLine);
1053 if (selectCurrentSymbol)
1054 m_pWorkspaceBrowser->SelectSymbol(fname, currentLine);
1055 }
1056
RereadOptions()1057 void NativeParserF::RereadOptions()
1058 {
1059 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("fortran_project"));
1060 // disabled?
1061 if (cfg->ReadBool(_("/use_symbols_browser"), true))
1062 {
1063 if (!m_pWorkspaceBrowser)
1064 {
1065 CreateWorkspaceBrowser();
1066 }
1067 // change class-browser docking settings
1068 else if (m_WorkspaceBrowserIsFloating != cfg->ReadBool(_T("/as_floating_window"), false))
1069 {
1070 RemoveWorkspaceBrowser();
1071 CreateWorkspaceBrowser();
1072 }
1073 else
1074 {
1075 m_pWorkspaceBrowser->RereadOptions();
1076 }
1077 UpdateWorkspaceBrowser();
1078 }
1079 else if (m_pWorkspaceBrowser)
1080 {
1081 RemoveWorkspaceBrowser();
1082 }
1083 else
1084 {
1085 //m_pWorkspaceBrowser->RereadOptions();
1086 }
1087
1088 m_Parser.RereadOptions();
1089 }
1090
GetJumpTracker()1091 JumpTracker* NativeParserF::GetJumpTracker()
1092 {
1093 return &m_JumpTracker;
1094 }
1095
GetFortranProject()1096 FortranProject* NativeParserF::GetFortranProject()
1097 {
1098 return m_pFortranProject;
1099 }
1100
GenMakefile()1101 void NativeParserF::GenMakefile()
1102 {
1103 cbProject* project = Manager::Get()->GetProjectManager()->GetActiveProject();
1104 if (!project)
1105 {
1106 Manager::Get()->GetLogManager()->Log(_T("No active project was found. Makefile was not generated."));
1107 cbMessageBox(_("No active project was found.\nMakefile was not generated."), _("Error"), wxICON_ERROR);
1108 return;
1109 }
1110
1111 UpdateProjectFilesDependency(project);
1112
1113 wxString fn = project->GetFilename();
1114 WSDependencyMap::iterator pos;
1115 pos = m_WSDependency.find(fn);
1116 if (pos == m_WSDependency.end())
1117 return;
1118
1119 if (pos->second->GetSizeFiles() > 0)
1120 MakefileGen::GenerateMakefile(project, pos->second, this);
1121 else
1122 {
1123 Manager::Get()->GetLogManager()->Log(_T("Active project doesn't have Fortran files."));
1124 cbMessageBox(_("Active project doesn't have Fortran files.\nMakefile was not generated."), _("Information"), wxICON_INFORMATION);
1125 }
1126 }
1127
GetCurrentBuffer(wxString & buffer,wxString & filename,wxString & projFilename)1128 void NativeParserF::GetCurrentBuffer(wxString& buffer, wxString& filename, wxString& projFilename)
1129 {
1130 wxCriticalSectionLocker locker(s_CurrentBufferCritSect);
1131 buffer = m_CurrentEditorBuffer;
1132 filename = m_CurrentEditorFilename;
1133 projFilename = m_CurrentEditorProjectFN;
1134 }
1135
ReparseCurrentEditor()1136 void NativeParserF::ReparseCurrentEditor()
1137 {
1138 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
1139 if(!ed)
1140 return;
1141 cbStyledTextCtrl* control = ed->GetControl();
1142 if (!control)
1143 return;
1144
1145 {
1146 wxCriticalSectionLocker locker(s_CurrentBufferCritSect);
1147 m_CurrentEditorBuffer = control->GetText();
1148 m_CurrentEditorFilename = ed->GetFilename();
1149
1150 ProjectFile* pf = ed->GetProjectFile();
1151 if (pf)
1152 {
1153 cbProject* pr = pf->GetParentProject();
1154 if (pr)
1155 {
1156 m_CurrentEditorProjectFN = pr->GetFilename();
1157 }
1158 }
1159 else
1160 m_CurrentEditorProjectFN = _T("");
1161 }
1162
1163 if (BufferParserThread::s_BPTInstances <= 1)
1164 {
1165 BufferParserThread* thread = new BufferParserThread(this, idBPThreadEvent);
1166 m_ThreadPool.AddTask(thread, true);
1167 }
1168 }
1169
OnUpdateCurrentFileTokens(wxCommandEvent &)1170 void NativeParserF::OnUpdateCurrentFileTokens(wxCommandEvent& /*event*/)
1171 {
1172 m_Parser.ConnectToNewCurrentTokens();
1173 }
1174
GetProjectSearchDirs(cbProject * project)1175 wxArrayString NativeParserF::GetProjectSearchDirs(cbProject* project)
1176 {
1177 wxArrayString dirs;
1178 if (!project)
1179 return dirs;
1180 wxString pfn = project->GetFilename();
1181 if (m_ASearchDirs.count(pfn) == 0)
1182 return dirs;
1183
1184 return m_ASearchDirs[pfn];
1185 }
1186
SetProjectSearchDirs(cbProject * project,wxArrayString & searchDirs)1187 void NativeParserF::SetProjectSearchDirs(cbProject* project, wxArrayString& searchDirs)
1188 {
1189 if (!project)
1190 return;
1191
1192 m_ASearchDirs[project->GetFilename()] = searchDirs;
1193 }
1194
HasFortranFiles(cbProject * project)1195 bool NativeParserF::HasFortranFiles(cbProject* project)
1196 {
1197 if (!project)
1198 return false;
1199
1200 wxString pfn = project->GetFilename();
1201 for (size_t i=0; i<m_WSFilePFN.size(); ++i)
1202 {
1203 if (m_WSFilePFN[i] == pfn)
1204 return true;
1205 }
1206 return false;
1207 }
1208
DelProjectSearchDirs(cbProject * project)1209 void NativeParserF::DelProjectSearchDirs(cbProject* project)
1210 {
1211 if (!project)
1212 return;
1213
1214 m_ASearchDirs.erase(project->GetFilename());
1215 }
1216
ForceReparseProjectSearchDirs()1217 void NativeParserF::ForceReparseProjectSearchDirs()
1218 {
1219 if (Manager::IsAppShuttingDown())
1220 return;
1221
1222 m_ASearchDirsReparseTimer.Start(1500, wxTIMER_ONE_SHOT);
1223 }
1224
OnASearchDirsReparseTimer(wxTimerEvent &)1225 void NativeParserF::OnASearchDirsReparseTimer(wxTimerEvent& /*event*/)
1226 {
1227 if (Manager::IsAppShuttingDown())
1228 return;
1229
1230 if (s_AdditionalDirParserMutex.TryLock() == wxMUTEX_NO_ERROR)
1231 {
1232 MakeADirFileList();
1233 s_AdditionalDirParserMutex.Unlock();
1234
1235 ADirParserThread* thread = new ADirParserThread(this, idADirPThreadEvent);
1236 m_ThreadPool.AddTask(thread, true);
1237 }
1238 }
1239