1 /*
2 * This file is part of the FortranProject plugin for Code::Blocks IDE
3 * and licensed under the GNU General Public License, version 3
4 * http://www.gnu.org/licenses/gpl-3.0.html
5 *
6 * Author: Darius Markauskas
7 *
8 */
9
10 #include <sdk.h> // Code::Blocks SDK
11 #ifndef CB_PRECOMP
12 #include <wx/filename.h>
13 #include <wx/tokenzr.h>
14 #include <wx/gdicmn.h>
15 #include <wx/xrc/xmlres.h>
16 #include <wx/event.h>
17 #include <wx/menu.h>
18 #include <wx/toolbar.h>
19 #include <wx/choicdlg.h>
20
21 #include <configurationpanel.h>
22 #include <manager.h>
23 #include <ccmanager.h>
24 #include <editorcolourset.h>
25 #include <editormanager.h>
26 #include <logmanager.h>
27 #include <projectmanager.h>
28 #include <cbstyledtextctrl.h>
29 #include <projectloader_hooks.h>
30 #include <editor_hooks.h>
31 #include <cbeditor.h>
32 #endif
33 #include <vector>
34
35 #include "fortranproject.h"
36 #include "fpoptionsdlg.h"
37 #include "fpoptionsprjdlg.h"
38 #include "jumptracker.h"
39 #include "changecase.h"
40 #include "tab2space.h"
41 #include "docblock.h"
42 #include "formatindent.h"
43 #include "bindto.h"
44 #include "ccsmartfilter.h"
45
46 // this auto-registers the plugin
47 namespace
48 {
49 PluginRegistrant<FortranProject> reg(_T("FortranProject"));
50 }
51
52
53 int idGotoDeclaration = wxNewId();
54 int idCodeCompleteTimer = wxNewId();
55 int idMenuJump = wxNewId();
56 int idMenuGotoDeclaration = wxNewId();
57 int idMenuJumpBack = wxNewId();
58 int idMenuJumpHome = wxNewId();
59 int idMenuJumpForward = wxNewId();
60 int idViewSymbolsBrowser = wxNewId();
61 int idMenuGenerateMakefile = wxNewId();
62 int idMenuChangeCase = wxNewId();
63 int idMenuTab2Space = wxNewId();
64 int idMenuFormatIndent = wxNewId();
65 int idMenuBindTo = wxNewId();
66 int idReparseEditorTimer = wxNewId();
67 int idShowCallTree = wxNewId();
68 int idShowCalledByTree = wxNewId();
69 int idViewCallTree = wxNewId();
70
71 #ifndef __WXMSW__
72 int idMenuEditPaste = XRCID("idEditPaste");
73 #endif
74
BEGIN_EVENT_TABLE(FortranProject,cbCodeCompletionPlugin)75 BEGIN_EVENT_TABLE(FortranProject, cbCodeCompletionPlugin)
76 EVT_UPDATE_UI(idViewSymbolsBrowser, FortranProject::OnUpdateUI)
77 EVT_UPDATE_UI(idViewCallTree, FortranProject::OnUpdateUICallTree)
78 EVT_MENU(idMenuGotoDeclaration, FortranProject::OnGotoDeclaration)
79 EVT_MENU(idMenuJumpBack, FortranProject::OnJumpBack)
80 EVT_MENU(idMenuJumpHome, FortranProject::OnJumpHome)
81 EVT_MENU(idMenuJumpForward, FortranProject::OnJumpForward)
82 EVT_MENU(idGotoDeclaration, FortranProject::OnGotoDeclaration)
83 EVT_MENU(idViewSymbolsBrowser, FortranProject::OnViewWorkspaceBrowser)
84 EVT_MENU(idViewCallTree, FortranProject::OnShowCallTreeView)
85 EVT_MENU(idMenuGenerateMakefile, FortranProject::OnGenerateMakefile)
86 EVT_MENU(idMenuChangeCase, FortranProject::OnChangeCase)
87 EVT_MENU(idMenuTab2Space, FortranProject::OnTab2Space)
88 EVT_MENU(idMenuFormatIndent, FortranProject::OnFormatIndent)
89 EVT_MENU(idMenuBindTo, FortranProject::OnBindTo)
90 EVT_MENU(idShowCallTree, FortranProject::OnShowCallTree)
91 EVT_MENU(idShowCalledByTree, FortranProject::OnShowCallTree)
92 #ifndef __WXMSW__
93 EVT_MENU(idMenuEditPaste, FortranProject::OnMenuEditPaste)
94 #endif
95 EVT_TIMER(idReparseEditorTimer, FortranProject::OnReparseEditorTimer)
96 EVT_TOOL(XRCID("idFortProjBack"), FortranProject::OnJumpBack)
97 EVT_TOOL(XRCID("idFortProjHome"), FortranProject::OnJumpHome)
98 EVT_TOOL(XRCID("idFortProjForward"), FortranProject::OnJumpForward)
99 END_EVENT_TABLE()
100
101 FortranProject::FortranProject() :
102 m_pNativeParser(0),
103 m_EditorHookId(0),
104 m_TimerCodeCompletion(this, idCodeCompleteTimer),
105 m_pCodeCompletionLastEditor(0),
106 m_pToolbar(0L),
107 m_ShowedCallTip(false),
108 m_WasCallTipActive(false),
109 m_IsAutoPopup(false),
110 m_ActiveCalltipsNest(0),
111 m_ActiveCalltipsPosition(-1),
112 m_CurrentLine(0),
113 m_pFortranLog(0L),
114 m_TimerReparseEditor(this, idReparseEditorTimer)
115 {
116 if(!Manager::LoadResource(_T("FortranProject.zip")))
117 {
118 NotifyMissingFile(_T("FortranProject.zip"));
119 }
120 }
121
~FortranProject()122 FortranProject::~FortranProject()
123 {
124 }
125
126
OnAttach()127 void FortranProject::OnAttach()
128 {
129 m_ViewMenu = 0;
130 m_FortranToolsMenu = 0;
131
132 m_pNativeParser = new NativeParserF(this);
133 m_pNativeParser->CreateWorkspaceBrowser();
134 m_LastPosForCodeCompletion = -1;
135
136 m_pKeywordsParser = new KeywordsParserF();
137
138 m_pCallTree = new CallTree(this);
139
140 RereadOptions();
141 LoadFortranKeywordImages();
142
143 // hook to project loading procedure
144 ProjectLoaderHooks::HookFunctorBase* fp_hook =
145 new ProjectLoaderHooks::HookFunctor<FortranProject>(this, &FortranProject::OnProjectLoadingHook);
146 m_ProjectLoadingHookID = ProjectLoaderHooks::RegisterHook(fp_hook);
147
148 // hook to editors
149 EditorHooks::HookFunctorBase* myhook = new EditorHooks::HookFunctor<FortranProject>(this, &FortranProject::EditorEventHook);
150 m_EditorHookId = EditorHooks::RegisterHook(myhook);
151
152 // register event sinks
153 Manager* pm = Manager::Get();
154
155 pm->RegisterEventSink(cbEVT_EDITOR_SAVE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnEditorSave));
156 pm->RegisterEventSink(cbEVT_EDITOR_ACTIVATED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnEditorActivated));
157 pm->RegisterEventSink(cbEVT_EDITOR_DEACTIVATED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnEditorDeactivated));
158 pm->RegisterEventSink(cbEVT_EDITOR_CLOSE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnEditorClose));
159
160 pm->RegisterEventSink(cbEVT_APP_STARTUP_DONE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnAppDoneStartup));
161 pm->RegisterEventSink(cbEVT_WORKSPACE_CHANGED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnWorkspaceChanged));
162 pm->RegisterEventSink(cbEVT_PROJECT_ACTIVATE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnProjectActivated));
163 pm->RegisterEventSink(cbEVT_PROJECT_CLOSE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnProjectClosed));
164 pm->RegisterEventSink(cbEVT_PROJECT_SAVE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnProjectSaved));
165 pm->RegisterEventSink(cbEVT_PROJECT_FILE_ADDED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnProjectFileAdded));
166 pm->RegisterEventSink(cbEVT_PROJECT_FILE_REMOVED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnProjectFileRemoved));
167 pm->RegisterEventSink(cbEVT_COMPILER_STARTED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnCompilerStarted));
168 pm->RegisterEventSink(cbEVT_CLEAN_PROJECT_STARTED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnCleanProjectStarted));
169 pm->RegisterEventSink(cbEVT_CLEAN_WORKSPACE_STARTED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnCleanWorkspaceStarted));
170
171 pm->RegisterEventSink(cbEVT_DEBUGGER_STARTED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnDebuggerStarted));
172 pm->RegisterEventSink(cbEVT_DEBUGGER_FINISHED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnDebuggerFinished));
173
174 pm->GetCCManager()->RegisterAutoLaunchChars(wxT("%"), this);
175
176 m_IsDebugging = false;
177 m_InitDone = true;
178 }
179
OnRelease(bool appShutDown)180 void FortranProject::OnRelease(bool appShutDown)
181 {
182 // unregister hook
183 // 'true' will delete the functor too
184 ProjectLoaderHooks::UnregisterHook(m_ProjectLoadingHookID, true);
185 EditorHooks::UnregisterHook(m_EditorHookId, true);
186
187 // remove registered event sinks
188 Manager::Get()->RemoveAllEventSinksFor(this);
189
190 if (m_pNativeParser)
191 {
192 delete m_pNativeParser;
193 }
194 if (m_pKeywordsParser)
195 {
196 delete m_pKeywordsParser;
197 }
198 if (m_pCallTree)
199 {
200 delete m_pCallTree;
201 }
202
203 RemoveLogWindow(appShutDown);
204
205 if (m_ViewMenu)
206 {
207 m_ViewMenu->Delete(idViewSymbolsBrowser);
208 m_ViewMenu->Delete(idViewCallTree);
209 }
210
211 if (m_FortranToolsMenu)
212 {
213 m_FortranToolsMenu->Delete(idMenuJump);
214 m_FortranToolsMenu->Delete(idMenuGenerateMakefile);
215 m_FortranToolsMenu->Delete(idMenuChangeCase);
216 m_FortranToolsMenu->Delete(idMenuTab2Space);
217 m_FortranToolsMenu->Delete(idMenuFormatIndent);
218 m_FortranToolsMenu->Delete(idMenuBindTo);
219 }
220 } // end of OnRelease
221
222
OnUpdateUI(wxUpdateUIEvent & event)223 void FortranProject::OnUpdateUI(wxUpdateUIEvent& event)
224 {
225 if (m_ViewMenu)
226 {
227 bool isVis = IsWindowReallyShown((wxWindow*)m_pNativeParser->GetWorkspaceBrowser());
228 m_ViewMenu->Check(idViewSymbolsBrowser, isVis);
229 }
230
231 event.Skip();
232 }
233
234
OnUpdateUICallTree(wxUpdateUIEvent & event)235 void FortranProject::OnUpdateUICallTree(wxUpdateUIEvent& event)
236 {
237 if (m_ViewMenu)
238 {
239 bool isVis = IsWindowReallyShown((wxWindow*)m_pCallTree->GetCallTreeView());
240 m_ViewMenu->Check(idViewCallTree, isVis);
241 }
242
243 event.Skip();
244 }
245
246
OnAppDoneStartup(CodeBlocksEvent & event)247 void FortranProject::OnAppDoneStartup(CodeBlocksEvent& event)
248 {
249 if (IsAttached())
250 {
251 m_InitDone = false;
252 // parse any projects opened through DDE or the command-line
253 m_pNativeParser->ForceReparseWorkspace();
254 m_InitDone = true;
255 }
256
257 if (m_pNativeParser->GetWorkspaceBrowser())
258 {
259 m_pNativeParser->GetWorkspaceBrowser()->UpdateSash();
260 }
261 event.Skip();
262 }
263
OnWorkspaceChanged(CodeBlocksEvent & event)264 void FortranProject::OnWorkspaceChanged(CodeBlocksEvent& event)
265 {
266 // EVT_WORKSPACE_CHANGED is a powerful event, it's sent after any project
267 // has finished loading or closing. It's the *LAST* event to be sent when
268 // the workspace has been changed, and it's not sent if the application is
269 // shutting down. So it's the ideal time to parse files and update your
270 // widgets.
271 if (IsAttached() && m_InitDone && !Manager::IsAppShuttingDown())
272 {
273 m_InitDone = false;
274 // Parse the projects
275 m_pNativeParser->ForceReparseWorkspace();
276 m_InitDone = true;
277 }
278 event.Skip();
279 }
280
OnProjectActivated(CodeBlocksEvent & event)281 void FortranProject::OnProjectActivated(CodeBlocksEvent& event)
282 {
283 // The Class browser shouldn't be updated if we're in the middle of loading/closing
284 // a project/workspace, because the class browser would need to be updated again.
285 // So we need to update it with the EVT_WORKSPACE_CHANGED event, which gets
286 // triggered after everything's finished loading/closing.
287
288 if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
289 {
290 m_pNativeParser->OnProjectActivated(event.GetProject());
291 }
292 event.Skip();
293 }
294
OnProjectClosed(CodeBlocksEvent & event)295 void FortranProject::OnProjectClosed(CodeBlocksEvent& event)
296 {
297 // After this, the Class Browser needs to be updated. It will happen
298 // when we receive the next EVT_PROJECT_ACTIVATED event.
299 // if (IsAttached() && m_InitDone)
300 // {
301 // m_pNativeParser->RemoveFromParser(event.GetProject());
302 // }
303
304 if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
305 {
306 m_pNativeParser->DelProjectSearchDirs(event.GetProject());
307 }
308
309 event.Skip();
310 }
311
OnProjectSaved(CodeBlocksEvent & event)312 void FortranProject::OnProjectSaved(CodeBlocksEvent& event)
313 {
314 // Do we need it for Fortran?
315 event.Skip();
316 }
317
OnProjectFileAdded(CodeBlocksEvent & event)318 void FortranProject::OnProjectFileAdded(CodeBlocksEvent& event)
319 {
320 if (IsAttached() && m_InitDone)
321 {
322 cbProject* cbp = event.GetProject();
323 if (cbp)
324 {
325 wxString pfn = cbp->GetFilename();
326 m_pNativeParser->AddFileToParser(pfn, event.GetString());
327 m_pNativeParser->UpdateWorkspaceBrowser();
328 }
329 }
330 event.Skip();
331 }
332
OnProjectFileRemoved(CodeBlocksEvent & event)333 void FortranProject::OnProjectFileRemoved(CodeBlocksEvent& event)
334 {
335 if (IsAttached() && m_InitDone)
336 {
337 m_pNativeParser->RemoveFileFromParser(event.GetString());
338 m_pNativeParser->UpdateWorkspaceBrowser();
339 }
340 event.Skip();
341 }
342
OnEditorSave(CodeBlocksEvent & event)343 void FortranProject::OnEditorSave(CodeBlocksEvent& event)
344 {
345 if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
346 {
347 EditorBase* eb = event.GetEditor();
348 cbEditor* editor = (eb && eb->IsBuiltinEditor()) ? static_cast<cbEditor*>(eb) : 0;
349 if (editor)
350 {
351 wxString projFN;
352 ProjectFile* pf = editor->GetProjectFile();
353 if (pf)
354 {
355 cbProject* cbp = pf->GetParentProject();
356 projFN = cbp->GetFilename();
357 }
358 m_pNativeParser->ReparseFile(projFN, editor->GetFilename());
359 m_pNativeParser->UpdateWorkspaceBrowser();
360 }
361 }
362 event.Skip();
363 }
364
OnEditorActivated(CodeBlocksEvent & event)365 void FortranProject::OnEditorActivated(CodeBlocksEvent& event)
366 {
367 if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
368 {
369 EditorBase* eb = event.GetEditor();
370 m_pNativeParser->OnEditorActivated(eb);
371
372 if (m_TimerReparseEditor.IsRunning())
373 m_TimerReparseEditor.Stop();
374 cbEditor* editor = (eb && eb->IsBuiltinEditor()) ? static_cast<cbEditor*>(eb) : 0;
375 if (editor && editor->GetModified())
376 m_TimerReparseEditor.Start(1500, wxTIMER_ONE_SHOT);
377
378 FortranSourceForm fsForm;
379 if (editor && m_pNativeParser->IsFileFortran(editor->GetShortName(), fsForm))
380 {
381 cbStyledTextCtrl* control = editor->GetControl();
382 m_ConstrHighlighter.ClearHighlighting(control, true);
383 m_ConstrHighlighter.DoWork(editor, fsForm);
384 }
385 }
386
387 event.Skip();
388 }
389
OnEditorDeactivated(CodeBlocksEvent & event)390 void FortranProject::OnEditorDeactivated(CodeBlocksEvent& event)
391 {
392 if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
393 {
394 EditorBase* eb = event.GetEditor();
395 cbEditor* editor = (eb && eb->IsBuiltinEditor()) ? static_cast<cbEditor*>(eb) : 0;
396 if (editor)
397 {
398 cbStyledTextCtrl* control = editor->GetControl();
399 m_ConstrHighlighter.ClearHighlighting(control);
400 }
401 }
402 event.Skip();
403 }
404
OnEditorClose(CodeBlocksEvent & event)405 void FortranProject::OnEditorClose(CodeBlocksEvent& event)
406 {
407 if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
408 {
409 EditorBase* eb = event.GetEditor();
410 m_pNativeParser->OnEditorClose(eb);
411 }
412
413 event.Skip();
414 }
415
OnCompilerStarted(CodeBlocksEvent & event)416 void FortranProject::OnCompilerStarted(CodeBlocksEvent& event)
417 {
418 event.Skip();
419 m_pNativeParser->UpdateWorkspaceFilesDependency();
420 }
421
OnCleanProjectStarted(CodeBlocksEvent & event)422 void FortranProject::OnCleanProjectStarted(CodeBlocksEvent& event)
423 {
424 event.Skip();
425 //Remove all *.mod files from obj folder
426 wxString targetName = event.GetBuildTargetName();
427 cbProject* pr = event.GetProject();
428 if (!pr)
429 return;
430 if (pr->IsMakefileCustom())
431 return;
432 ProjectBuildTarget* bTarget = pr->GetBuildTarget(targetName);
433 if (bTarget)
434 {
435 ProjectDependencies::RemoveModFiles(pr, bTarget, m_pNativeParser);
436 }
437 }
438
OnCleanWorkspaceStarted(CodeBlocksEvent & event)439 void FortranProject::OnCleanWorkspaceStarted(CodeBlocksEvent& event)
440 {
441 event.Skip();
442 //Remove all *.mod files from obj folder
443 ProjectDependencies::RemoveModFilesWS(m_pNativeParser);
444 }
445
BuildMenu(wxMenuBar * menuBar)446 void FortranProject::BuildMenu(wxMenuBar* menuBar)
447 {
448 if (!IsAttached())
449 return;
450
451 // add the fsymbolsbrowser window in the "View" menu
452 int idx = menuBar->FindMenu(_("&View"));
453 if (idx != wxNOT_FOUND)
454 {
455 m_ViewMenu = menuBar->GetMenu(idx);
456 wxMenuItemList& items = m_ViewMenu->GetMenuItems();
457 bool inserted = false;
458
459 // find the first separator and insert before it
460 for (size_t i = 0; i < items.GetCount(); ++i)
461 {
462 if (items[i]->IsSeparator())
463 {
464 m_ViewMenu->InsertCheckItem(i, idViewSymbolsBrowser, _("Fortran symbols browser"), _("Toggle displaying the fortran symbols browser"));
465 m_ViewMenu->InsertCheckItem(i, idViewCallTree, _("Fortran Call/Called-By tree"), _("Toggle displaying the fortran Call/Called-By tree window"));
466 inserted = true;
467 break;
468 }
469 }
470
471 // not found, just append
472 if (!inserted)
473 {
474 m_ViewMenu->AppendCheckItem(idViewSymbolsBrowser, _("Fortran symbols browser"), _("Toggle displaying the fortran symbols browser"));
475 m_ViewMenu->AppendCheckItem(idViewCallTree, _("Fortran call tree"), _("Toggle displaying the fortran Call/Called-By tree window"));
476 }
477 }
478 else
479 Manager::Get()->GetLogManager()->DebugLog(_T("FortranProject: Could not find View menu!"));
480
481
482 int pos = menuBar->FindMenu(_("Fortra&n"));
483 if (pos == wxNOT_FOUND)
484 {
485 pos = menuBar->FindMenu(_("&Tools"));
486 if (pos != wxNOT_FOUND)
487 {
488 m_FortranToolsMenu = new wxMenu();
489 menuBar->Insert(pos, m_FortranToolsMenu, _("Fortra&n"));
490 }
491 else
492 Manager::Get()->GetLogManager()->DebugLog(_T("FortranProject: Could not find Tools menu!"));
493 }
494 else
495 {
496 m_FortranToolsMenu = menuBar->GetMenu(pos);
497 }
498 if (m_FortranToolsMenu)
499 {
500 wxMenu* submenuJump = new wxMenu();
501 submenuJump->Append(idMenuGotoDeclaration, _("Jump to declaration"));
502 const int imageSize = Manager::Get()->GetImageSize(Manager::UIComponent::Menus);
503 const int uiScaleFactor = Manager::Get()->GetUIScaleFactor(Manager::UIComponent::Menus);
504 wxString prefix = ConfigManager::GetDataFolder() +
505 wxString::Format(_T("/images/fortranproject/%dx%d/"), imageSize, imageSize);
506
507 wxBitmap bmp_back = cbLoadBitmapScaled(prefix + _T("fprojectjumpback.png"), wxBITMAP_TYPE_PNG, uiScaleFactor);
508 wxBitmap bmp_home = cbLoadBitmapScaled(prefix + _T("fprojectjumphome.png"), wxBITMAP_TYPE_PNG, uiScaleFactor);
509 wxBitmap bmp_forward = cbLoadBitmapScaled(prefix + _T("fprojectjumpforward.png"), wxBITMAP_TYPE_PNG, uiScaleFactor);
510 wxMenuItem* itemJumpBack = new wxMenuItem(submenuJump, idMenuJumpBack, _("Jump back"));
511 itemJumpBack->SetBitmap(bmp_back);
512 wxMenuItem* itemJumpHome = new wxMenuItem(submenuJump, idMenuJumpHome, _("Jump last"));
513 itemJumpHome->SetBitmap(bmp_home);
514 wxMenuItem* itemJumpForward = new wxMenuItem(submenuJump, idMenuJumpForward, _("Jump forward"));
515 itemJumpForward->SetBitmap(bmp_forward);
516 submenuJump->Append(itemJumpBack);
517 submenuJump->Append(itemJumpHome);
518 submenuJump->Append(itemJumpForward);
519 submenuJump->Enable(idMenuJumpBack, false);
520 submenuJump->Enable(idMenuJumpHome, false);
521 submenuJump->Enable(idMenuJumpForward, false);
522
523 m_FortranToolsMenu->Insert(0, idMenuBindTo, _("Bind To..."));
524 m_FortranToolsMenu->Insert(0, idMenuFormatIndent, _("Format indent..."));
525 m_FortranToolsMenu->Insert(0, idMenuTab2Space, _("Tab2space..."));
526 m_FortranToolsMenu->Insert(0, idMenuChangeCase, _("Change case..."));
527 m_FortranToolsMenu->Insert(0, idMenuGenerateMakefile, _("Generate Makefile..."));
528 m_FortranToolsMenu->Insert(0, idMenuJump, _("Jump"), submenuJump);
529 }
530 }
531
CalcStcFontSize(cbStyledTextCtrl * stc)532 static int CalcStcFontSize(cbStyledTextCtrl *stc)
533 {
534 wxFont defaultFont = stc->StyleGetFont(wxSCI_STYLE_DEFAULT);
535 defaultFont.SetPointSize(defaultFont.GetPointSize() + stc->GetZoom());
536 int fontSize;
537 stc->GetTextExtent(wxT("A"), nullptr, &fontSize, nullptr, nullptr, &defaultFont);
538 return fontSize;
539 }
540
541 // invariant : on return true : NameUnderCursor is NOT empty
EditorHasNameUnderCursor(wxString & NameUnderCursor,bool & isOperator)542 static bool EditorHasNameUnderCursor(wxString& NameUnderCursor, bool& isOperator)
543 {
544 if(cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor())
545 {
546 isOperator = false;
547 cbStyledTextCtrl* control = ed->GetControl();
548 const int pos = control->GetCurrentPos();
549
550 int ws = control->WordStartPosition(pos, true);
551 int we = control->WordEndPosition(pos, true);
552 if (ws < we && ws > 0 && control->GetCharAt(ws-1) == '.'
553 && we < control->GetLength() && control->GetCharAt(we) == '.')
554 {
555 // maybe we have user defined operator .name. Take it as one word.
556 ws--;
557 we++;
558 isOperator = true;
559 }
560 const wxString txt = control->GetTextRange(ws, we);
561 if (!txt.IsEmpty())
562 {
563 NameUnderCursor = txt;
564 return true;
565 }
566 // Check if we at operator
567 wxString operatorsTxt = _T("=*/+-<>");
568 int opStart = pos;
569 for (int i=1; i<3 && pos-i>0; i++)
570 {
571 wxChar txt1 = control->GetCharAt(pos-i);
572 if (operatorsTxt.Contains(txt1))
573 opStart = pos-i;
574 else
575 break;
576 }
577 int opEnd = pos;
578 for (int i=0; i<3 && pos+i<control->GetLength(); i++)
579 {
580 wxChar txt1 = control->GetCharAt(pos+i);
581 if (operatorsTxt.Contains(txt1))
582 opEnd = pos+i+1;
583 else
584 break;
585 }
586 wxString opStr = control->GetTextRange(opStart, opEnd);
587 if (!opStr.IsEmpty())
588 {
589 NameUnderCursor = opStr;
590 isOperator = true;
591 return true;
592 }
593
594 }
595 return false;
596 } // end of EditorHasNameUnderCursor
597
BuildModuleMenu(const ModuleType type,wxMenu * menu,const FileTreeData * data)598 void FortranProject::BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data)
599 {
600 if (!menu || !IsAttached() || !m_InitDone)
601 return;
602 if (type == mtEditorManager)
603 {
604 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
605 if (!ed || !m_pNativeParser->IsFileFortran(ed->GetFilename()))
606 return;
607
608 wxString NameUnderCursor;
609 bool isOperator;
610 if(EditorHasNameUnderCursor(NameUnderCursor, isOperator))
611 {
612 wxString msg;
613 msg.Printf(_("Jump to '%s'"), NameUnderCursor.c_str());
614 menu->Insert(0, idGotoDeclaration, msg);
615
616 menu->Insert(1, wxID_SEPARATOR, wxEmptyString);
617 Manager::Get()->GetPluginManager()->RegisterFindMenuItems(true, 2);
618
619 if (!isOperator)
620 {
621 wxMenu* showsubmenu = new wxMenu();
622 showsubmenu->Append(idShowCallTree, _T("Call tree"));
623 showsubmenu->Append(idShowCalledByTree, _T("Called-By tree"));
624 menu->Insert(1, wxID_ANY, _("Show"), showsubmenu);
625 Manager::Get()->GetPluginManager()->RegisterFindMenuItems(true, 1);
626 }
627 }
628 }
629
630 }
631
OnGotoDeclaration(wxCommandEvent & event)632 void FortranProject::OnGotoDeclaration(wxCommandEvent& event)
633 {
634 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
635 if (!ed)
636 return;
637
638 cbStyledTextCtrl* control = ed->GetControl();
639 if (!control)
640 return;
641
642 wxString NameUnderCursor;
643 bool isOperator;
644 if(!EditorHasNameUnderCursor(NameUnderCursor, isOperator))
645 return;
646
647 // get the matching set
648 ParserF* pParser = m_pNativeParser->GetParser();
649 TokensArrayFlatClass tokensTmp;
650 TokensArrayFlat* result = tokensTmp.GetTokens();
651
652 wxString includeFilename = GetIncludeFilename(ed->GetControl());
653 if (!includeFilename.IsEmpty())
654 {
655 // was asked to jump to include file.
656 pParser->FindFile(includeFilename, *result);
657 }
658 else if (isOperator)
659 {
660 pParser->FindMatchOperatorTokensForJump(NameUnderCursor, *result);
661 }
662 else
663 {
664 pParser->FindMatchTokensForJump(ed, m_LogOnlyUseAssoc, false, *result);
665 // don't jump to intrinsic module
666 size_t ri = 0;
667 while (ri<result->GetCount())
668 {
669 if (result->Item(ri)->m_Filename.EndsWith(UnixFilename(_T("/images/fortranproject/fortran_intrinsic_modules.f90"))))
670 {
671 result->Item(ri)->Clear();
672 delete result->Item(ri);
673 result->RemoveAt(ri);
674 }
675 else
676 ri++;
677 }
678 }
679
680 if (result->GetCount() > 1)
681 {
682 // Remove result with the current line
683 wxString curfname = UnixFilename(ed->GetFilename());
684 unsigned int curlineIdx = control->LineFromPosition(control->GetCurrentPos());
685 for (size_t ri=0; ri<result->GetCount(); ri++)
686 {
687 if (result->Item(ri)->m_Filename.IsSameAs(curfname) &&
688 result->Item(ri)->m_LineStart == curlineIdx + 1)
689 {
690 result->Item(ri)->Clear();
691 delete result->Item(ri);
692 result->RemoveAt(ri);
693 break;
694 }
695 }
696 }
697
698 size_t count = std::min(result->GetCount(),m_MaxMatch);
699
700 TokenFlat* pToken = 0;
701 // one match
702 if (count == 1)
703 {
704 pToken = result->Item(0);
705 }
706 // if more than one match, display a selection dialog
707 else if (count > 1)
708 {
709 wxArrayString selections;
710 std::vector<int> idxItems;
711 for (size_t i=0; i<count; ++i)
712 {
713 wxFileName fn = wxFileName(result->Item(i)->m_Filename);
714 wxString inf;
715 if (result->Item(i)->m_TokenKind == tkUse && !result->Item(i)->m_Rename.IsEmpty())
716 {
717 inf = _T("use :: ") + result->Item(i)->m_DisplayName + _T(", ") + result->Item(i)->m_Rename;
718 }
719 else
720 {
721 inf = result->Item(i)->m_DisplayName + _T(" :: ") + result->Item(i)->GetTokenKindString();
722 }
723 inf += _T(" : ") + fn.GetFullName() + _T(" : ");
724 inf += wxString::Format(_T("%d"), int(result->Item(i)->m_LineStart));
725
726 if (selections.Index(inf) == wxNOT_FOUND)
727 {
728 selections.Add(inf);
729 idxItems.push_back(i);
730 }
731 }
732 if (selections.Count() > 1)
733 {
734 int sel = wxGetSingleChoiceIndex(_("Please make a selection:"), _("Multiple matches"), selections);
735 if (sel == -1)
736 return;
737 pToken = result->Item(idxItems[sel]);
738 }
739 else
740 {
741 pToken = result->Item(0);
742 }
743 }
744
745 if (pToken)
746 {
747 if (!GotoToken(pToken, ed))
748 cbMessageBox(wxString::Format(_("Declaration not found: %s"), NameUnderCursor.c_str()), _("Warning"), wxICON_WARNING);
749 }
750 else
751 {
752 cbMessageBox(wxString::Format(_("Not found: %s"), NameUnderCursor.c_str()), _("Warning"), wxICON_WARNING);
753 }
754
755 } // end of OnGotoDeclaration
756
757
GotoToken(TokenFlat * pToken,cbEditor * cured)758 bool FortranProject::GotoToken(TokenFlat* pToken, cbEditor* cured)
759 {
760 LineAddress jumpStart;
761 LineAddress jumpFinish;
762 if(cured)
763 {
764 cbStyledTextCtrl* control = cured->GetControl();
765 int curLine = control->LineFromPosition(control->GetCurrentPos());
766 jumpStart.Init(cured->GetFilename(), curLine, false);
767 }
768
769 if (cbEditor* newed = Manager::Get()->GetEditorManager()->Open(pToken->m_Filename))
770 {
771 newed->GotoLine(pToken->m_LineStart - 1);
772
773 // Track jump history
774 cbStyledTextCtrl* control = newed->GetControl();
775 int curLine = control->LineFromPosition(control->GetCurrentPos());
776 jumpFinish.Init(newed->GetFilename(), curLine, true);
777 m_pNativeParser->GetJumpTracker()->TakeJump(jumpStart, jumpFinish);
778 CheckEnableToolbar();
779 }
780 else
781 {
782 return false;
783 }
784 return true;
785 } // end of OnGotoToken
786
787
CodeCompletePreprocessor(int tknStart,int tknEnd,cbEditor * ed,std::vector<CCToken> & tokens)788 void FortranProject::CodeCompletePreprocessor(int tknStart, int tknEnd, cbEditor* ed, std::vector<CCToken>& tokens)
789 {
790 if (!IsAttached() || !m_InitDone)
791 return;
792
793 cbStyledTextCtrl* stc = ed->GetControl();
794 const wxString text = stc->GetTextRange(tknStart, tknEnd);
795
796 TokenF tp;
797 tp.m_TokenKind = tkPreprocessor;
798 int iidx = m_pNativeParser->GetTokenKindImageIdx(&tp);
799
800 wxStringVec macros;
801 macros.push_back(wxT("define"));
802 macros.push_back(wxT("elif"));
803 macros.push_back(wxT("elifdef"));
804 macros.push_back(wxT("elifndef"));
805 macros.push_back(wxT("else"));
806 macros.push_back(wxT("endif"));
807 macros.push_back(wxT("error"));
808 macros.push_back(wxT("if"));
809 macros.push_back(wxT("ifdef"));
810 macros.push_back(wxT("ifndef"));
811 macros.push_back(wxT("include"));
812 macros.push_back(wxT("line"));
813 macros.push_back(wxT("pragma"));
814 macros.push_back(wxT("undef"));
815 for (size_t i = 0; i < macros.size(); ++i)
816 {
817 if (text.IsEmpty() || macros[i][0] == text[0]) // ignore tokens that start with a different letter
818 tokens.push_back(CCToken(wxNOT_FOUND, macros[i], iidx));
819 }
820 stc->ClearRegisteredImages();
821 int fontSize = CalcStcFontSize(stc);
822 FPImageList fpImList(fontSize);
823 wxImageList* ilist = fpImList.GetImageList();
824 if (!ilist)
825 return;
826
827 stc->RegisterImage(iidx, ilist->GetBitmap(iidx));
828 }
829
DoCodeComplete(int caretPos,cbEditor * ed,std::vector<CCToken> & tokens)830 void FortranProject::DoCodeComplete(int caretPos, cbEditor* ed, std::vector<CCToken>& tokens)
831 {
832 if (!ed)
833 return;
834
835 cbStyledTextCtrl* control = ed->GetControl();
836 const int pos = control->GetCurrentPos();
837 const int lineIndentPos = control->GetLineIndentPosition(control->GetCurrentLine());
838 const wxChar lineFirstChar = control->GetCharAt(lineIndentPos);
839
840 int lineCur = control->LineFromPosition(pos);
841 int lineStartPos = control->PositionFromLine(lineCur);
842 wxString curLine = control->GetTextRange(lineStartPos,pos).Trim(false);
843
844 if (lineFirstChar == _T('!'))
845 {
846 wxString curLineLw = curLine.Lower();
847 if (!curLineLw.StartsWith(_T("!$ ")) && !curLineLw.StartsWith(_T("!$\t")) && !curLineLw.StartsWith(_T("!$omp")) && !curLineLw.StartsWith(_T("!$acc")))
848 return;
849 }
850 else
851 {
852 if (curLine.Find('!') != wxNOT_FOUND) // we are in comments
853 return;
854 }
855
856 int style = control->GetStyleAt(control->GetCurrentPos());
857 if (style != wxSCI_F_DEFAULT && style != wxSCI_F_WORD && style != wxSCI_F_WORD2 && style != wxSCI_F_WORD3
858 && style != wxSCI_F_OPERATOR && style != wxSCI_F_IDENTIFIER && style != wxSCI_F_OPERATOR2
859 && style != wxSCI_F_PREPROCESSOR )
860 return;
861
862 CodeComplete(caretPos, ed, tokens);
863 }
864
865
GetAutocompList(bool isAuto,cbEditor * ed,int & tknStart,int & tknEnd)866 std::vector<FortranProject::CCToken> FortranProject::GetAutocompList(bool isAuto, cbEditor* ed, int& tknStart, int& tknEnd)
867 {
868 std::vector<CCToken> tokens;
869
870 if (!IsAttached() || !m_InitDone)
871 return tokens;
872
873 if ( !ed
874 || !m_pNativeParser->IsFileFortran(ed->GetShortName())
875 || !Manager::Get()->GetConfigManager(_T("fortran_project"))->ReadBool(_T("/use_code_completion"), true))
876 return tokens;
877
878 cbStyledTextCtrl* stc = ed->GetControl();
879 const int style = stc->GetStyleAt(tknEnd);
880 const wxChar curChar = stc->GetCharAt(tknEnd - 1);
881
882 if (isAuto && curChar != wxT('%'))
883 return tokens;
884
885 const int lineIndentPos = stc->GetLineIndentPosition(stc->GetCurrentLine());
886 const wxChar lineFirstChar = stc->GetCharAt(lineIndentPos);
887
888 if (lineFirstChar == wxT('#'))
889 {
890 const int startPos = stc->WordStartPosition(lineIndentPos + 1, true);
891 const int endPos = stc->WordEndPosition(lineIndentPos + 1, true);
892 const wxString str = stc->GetTextRange(startPos, endPos);
893
894 if (endPos >= tknEnd && tknEnd > lineIndentPos)
895 CodeCompletePreprocessor(tknStart, tknEnd, ed, tokens);
896 return tokens;
897 }
898
899 if ( stc->IsString(style)
900 || stc->IsCharacter(style) )
901 {
902 return tokens;
903 }
904
905 DoCodeComplete(tknEnd, ed, tokens);
906 return tokens;
907 }
908
DoAutocomplete(const CCToken & token,cbEditor * ed)909 void FortranProject::DoAutocomplete(const CCToken& token, cbEditor* ed)
910 {
911 cbStyledTextCtrl* control = ed->GetControl();
912
913 wxString itemText = token.displayName.BeforeFirst(':');
914 control->AutoCompCancel();
915 int pos = control->GetCurrentPos();
916 int start = control->WordStartPosition(pos, true);
917 int endPos = control->WordEndPosition(pos, true);
918 const wxString& textUnder = control->GetTextRange(start, endPos);
919 bool replaceWord = false;
920 if (!textUnder.IsEmpty() && (start != pos || (pos != 0 && control->GetCharAt(pos-1) == _T('%'))))
921 {
922 TokensArrayFlat* ts = m_TokensCCList.GetTokens();
923 for (size_t i=0; i < ts->size(); ++i)
924 {
925 if (ts->Item(i)->m_DisplayName.IsSameAs(textUnder, false))
926 {
927 replaceWord = true;
928 break;
929 }
930 }
931 }
932 if (!replaceWord && pos != endPos)
933 {
934 const wxString& textUnderEnd = control->GetTextRange(pos, endPos);
935 if (itemText.EndsWith(textUnderEnd))
936 {
937 replaceWord = true;
938 }
939 }
940 if (textUnder.IsEmpty() || !textUnder.IsSameAs(itemText))
941 {
942 if (!replaceWord)
943 endPos = pos;
944 control->SetTargetStart(start);
945 control->SetTargetEnd(endPos);
946 control->ReplaceTarget(itemText);
947 }
948 control->GotoPos(start + itemText.size());
949
950 if (m_WasCallTipActive)
951 {
952 m_WasCallTipActive = false;
953 CodeBlocksEvent evt(cbEVT_SHOW_CALL_TIP);
954 Manager::Get()->ProcessEvent(evt);
955 }
956 }
957
958
EditorEventHook(cbEditor * editor,wxScintillaEvent & event)959 void FortranProject::EditorEventHook(cbEditor* editor, wxScintillaEvent& event)
960 {
961 if (!IsAttached() || !m_InitDone)
962 {
963 event.Skip();
964 return;
965 }
966
967 FortranSourceForm fsForm;
968 if (!m_pNativeParser->IsFileFortran(editor->GetShortName(), fsForm))
969 {
970 event.Skip();
971 return;
972 }
973
974 cbStyledTextCtrl* control = editor->GetControl();
975 wxEventType etyp = event.GetEventType();
976
977 if ( (etyp == wxEVT_SCI_CHARADDED)
978 && (!control->AutoCompActive()) ) // not already active autocompletion
979 {
980 wxChar ch = event.GetKey();
981 // update calltip highlight while we type
982 if (!control->CallTipActive())
983 m_ActiveCalltipsNest = 0;
984
985 // start calltip
986 if (ch == _T('('))
987 {
988 if (control->CallTipActive())
989 ++m_ActiveCalltipsNest;
990 }
991 // end calltip
992 else if (ch == _T(')'))
993 {
994 control->CallTipCancel();
995 if (m_ActiveCalltipsNest > 0)
996 {
997 --m_ActiveCalltipsNest;
998 CodeBlocksEvent evt(cbEVT_SHOW_CALL_TIP);
999 Manager::Get()->ProcessEvent(evt);
1000 }
1001 }
1002 }
1003 else if (etyp != wxEVT_SCI_CHARADDED && etyp != wxEVT_SCI_MODIFIED)
1004 {
1005 if (control->CallTipActive())
1006 {
1007 int curPos = control->GetCurrentPos();
1008 if (m_ActiveCalltipsPosition == -1)
1009 {
1010 m_ActiveCalltipsPosition = curPos;
1011 }
1012 else if (m_ActiveCalltipsPosition != curPos)
1013 {
1014 bool ctipsUpdate = true;
1015 if (m_ActiveCalltipsPosition+1 == curPos)
1016 {
1017 wxChar prevChar = control->GetCharAt(m_ActiveCalltipsPosition);
1018 wxChar curChar = control->GetCharAt(curPos);
1019 if (isalpha(prevChar) || prevChar == '_' || isdigit(prevChar))
1020 ctipsUpdate = false;
1021 else if (prevChar == ' ' && curChar == ' ')
1022 ctipsUpdate = false;
1023 }
1024 else if (m_ActiveCalltipsPosition-1 == curPos)
1025 {
1026 wxChar curChar = control->GetCharAt(curPos);
1027 if (isalpha(curChar) || curChar == '_' || isdigit(curChar) || curChar == ' ')
1028 ctipsUpdate = false;
1029 }
1030
1031 m_ActiveCalltipsPosition = curPos;
1032 if (ctipsUpdate)
1033 {
1034 CodeBlocksEvent evt(cbEVT_SHOW_CALL_TIP);
1035 Manager::Get()->ProcessEvent(evt);
1036 }
1037 }
1038 }
1039 else
1040 m_ActiveCalltipsPosition = -1;
1041 }
1042
1043 if( control->GetCurrentLine() != m_CurrentLine )
1044 {
1045 m_CurrentLine = control->GetCurrentLine();
1046 m_pNativeParser->MarkCurrentSymbol(false);
1047 }
1048
1049 if (etyp == wxEVT_SCI_MODIFIED && !m_TimerReparseEditor.IsRunning())
1050 m_TimerReparseEditor.Start(1500, wxTIMER_ONE_SHOT);
1051
1052 if (m_AutoInsertEnabled && etyp == wxEVT_SCI_CHARADDED)
1053 {
1054 wxChar ch = event.GetKey();
1055 if ((ch == _T('\n')) || ( (control->GetEOLMode() == wxSCI_EOL_CR) && (ch == _T('\r')) ))
1056 m_AutoInsert.MakeAutoInsert(editor);
1057 }
1058
1059 m_ConstrHighlighter.DoWork(editor, fsForm);
1060
1061 // allow others to handle this event
1062 event.Skip();
1063 }
1064
1065
OnViewWorkspaceBrowser(wxCommandEvent & event)1066 void FortranProject::OnViewWorkspaceBrowser(wxCommandEvent& event)
1067 {
1068 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("fortran_project"));
1069 if (!cfg->ReadBool(_T("/use_symbols_browser"), true))
1070 {
1071 cbMessageBox(_("The Fortran symbols browser is disabled in FortranProject options.\n"
1072 "Please enable it there first..."), _("Information"), wxICON_INFORMATION);
1073 return;
1074 }
1075 CodeBlocksDockEvent evt(event.IsChecked() ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW);
1076 evt.pWindow = (wxWindow*)m_pNativeParser->GetWorkspaceBrowser();
1077 Manager::Get()->ProcessEvent(evt);
1078 }
1079
1080
BuildToolBar(wxToolBar * toolBar)1081 bool FortranProject::BuildToolBar(wxToolBar* toolBar)
1082 {
1083 //The application is offering its toolbar for your plugin,
1084 //to add any toolbar items you want...
1085 //Append any items you need on the toolbar...
1086 //NotImplemented(_T("FortranProject::BuildToolBar()"));
1087
1088 //Build toolbar
1089 if (!IsAttached() || !toolBar)
1090 {
1091 return false;
1092 }
1093 int imSize = Manager::Get()->GetImageSize(Manager::UIComponent::Toolbars);
1094 wxString tbSStr;
1095 if (imSize <= 16)
1096 tbSStr = _T("_16x16");
1097 else if (imSize <= 20)
1098 tbSStr = _T("_20x20");
1099 else if (imSize <= 24)
1100 tbSStr = _T("_24x24");
1101 else if (imSize <= 28)
1102 tbSStr = _T("_28x28");
1103 else if (imSize <= 32)
1104 tbSStr = _T("_32x32");
1105 else if (imSize <= 40)
1106 tbSStr = _T("_40x40");
1107 else if (imSize <= 48)
1108 tbSStr = _T("_48x48");
1109 else if (imSize <= 56)
1110 tbSStr = _T("_56x56");
1111 else
1112 tbSStr = _T("_64x64");
1113
1114
1115 Manager::Get()->AddonToolBar(toolBar,_T("fortran_project_toolbar") + tbSStr);
1116 toolBar->Realize();
1117 m_pToolbar = toolBar;
1118 m_pToolbar->EnableTool(XRCID("idFortProjBack"), false);
1119 m_pToolbar->EnableTool(XRCID("idFortProjHome"), false);
1120 m_pToolbar->EnableTool(XRCID("idFortProjForward"), false);
1121 m_pToolbar->SetInitialSize();
1122
1123 return true;
1124 }
1125
1126
SortCCList(const wxString & first,const wxString & second)1127 static int SortCCList(const wxString& first, const wxString& second)
1128 {
1129 const wxChar* a = first.c_str();
1130 const wxChar* b = second.c_str();
1131 while (*a && *b)
1132 {
1133 if (*a != *b)
1134 {
1135 if ((*a == _T('?')) && (*b != _T('?')))
1136 return -1;
1137 else if ((*a != _T('?')) && (*b == _T('?')))
1138 return 1;
1139 else if ((*a == _T('?')) && (*b == _T('?')))
1140 return 0;
1141
1142 if ((*a == _T('_')) && (*b != _T('_')))
1143 return 1;
1144 else if ((*a != _T('_')) && (*b == _T('_')))
1145 return -1;
1146
1147 wxChar lowerA = wxTolower(*a);
1148 wxChar lowerB = wxTolower(*b);
1149
1150 if (lowerA != lowerB)
1151 return lowerA - lowerB;
1152 }
1153 a++;
1154 b++;
1155 }
1156 // Either *a or *b is null
1157 return *a - *b;
1158 }
1159
1160
CodeComplete(const int pos,cbEditor * ed,std::vector<CCToken> & tokens)1161 void FortranProject::CodeComplete(const int pos, cbEditor* ed, std::vector<CCToken>& tokens)
1162 {
1163 if (!IsAttached() || !m_InitDone)
1164 return;
1165
1166 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("fortran_project"));
1167
1168 ParserF* pParser = m_pNativeParser->GetParser();
1169 m_TokensCCList.Clear();
1170 TokensArrayFlat* result = m_TokensCCList.GetTokens();
1171
1172 cbStyledTextCtrl* control = ed->GetControl();
1173 const int start = control->WordStartPosition(pos, true);
1174 wxString NameUnderCursor = control->GetTextRange(start,pos);
1175 wxString NameUnderCursorLw = NameUnderCursor.Lower();
1176
1177 CompilerDirective pdir = cdNone;
1178 int lineCur = control->LineFromPosition(pos);
1179 int lineStartPos = control->PositionFromLine(lineCur);
1180 wxString curLine = control->GetTextRange(lineStartPos,pos).Trim(false).Lower();
1181
1182 if (curLine.StartsWith(_T("!$")))
1183 {
1184 if ((NameUnderCursorLw.IsSameAs(_T("omp")) || NameUnderCursorLw.IsSameAs(_T("acc"))) && start >= 2)
1185 {
1186 // Check if cursor is not direct after !$omp or !$acc
1187 wxString word = control->GetTextRange(start-2,pos).Lower();
1188 if (word.IsSameAs(_T("!$omp")) || word.IsSameAs(_T("!$acc")))
1189 return;
1190 }
1191 if (curLine.StartsWith(_T("!$omp")))
1192 pdir = cdOpenMP;
1193 else if (curLine.StartsWith(_T("!$acc")))
1194 pdir = cdOpenACC;
1195 else
1196 pdir = cdOther;
1197 }
1198
1199 bool isAfterPercent;
1200 int tokenKind;
1201 wxArrayString firstWords;
1202
1203 if (!pParser->FindMatchTokensForCodeCompletion(m_UseSmartCC, m_LogOnlyUseAssoc, m_LogOnlyPublicNames,
1204 NameUnderCursor, ed, *result, isAfterPercent, tokenKind, firstWords))
1205 return;
1206
1207 if (result->size() <= m_MaxMatch)
1208 {
1209 int fontSize = CalcStcFontSize(control);
1210
1211 FPImageList fpImList(fontSize);
1212 wxImageList* ilist = fpImList.GetImageList();
1213 if (!ilist)
1214 return;
1215 control->ClearRegisteredImages();
1216 int ilistImWidth = 0;
1217 int ilistImHeight = 0;
1218 ilist->GetSize(0, ilistImWidth, ilistImHeight);
1219
1220 tokens.reserve(result->size());
1221 wxArrayString items; items.Alloc(result->size());
1222 std::set<int> already_registered;
1223 std::set< wxString, std::less<wxString> > unique_strings; // check against this before inserting a new string in the list
1224 for (size_t i=0; i<result->GetCount(); ++i)
1225 {
1226 TokenFlat* token = result->Item(i);
1227 if (token->m_Name.StartsWith(_T("%%")) || token->m_Name.IsEmpty())
1228 continue;
1229
1230 wxString tmpstr = token->m_Name;
1231 if (m_LogShowTypeVariables && token->m_TokenKind == tkVariable)
1232 tmpstr << _T(": ") << token->m_PartFirst; // add type of variable
1233
1234 // check for unique_strings
1235 if (unique_strings.find(tmpstr) != unique_strings.end())
1236 continue;
1237
1238 unique_strings.insert(tmpstr);
1239 int iidx = m_pNativeParser->GetTokenKindImageIdx(token);
1240 if (already_registered.find(iidx) == already_registered.end())
1241 {
1242 if (iidx != -1)
1243 {
1244 control->RegisterImage(iidx, ilist->GetBitmap(iidx));
1245 already_registered.insert(iidx);
1246 }
1247 }
1248 wxString tmp;
1249 if (iidx != -1)
1250 {
1251 if (m_LogShowTypeVariables && token->m_TokenKind == tkVariable)
1252 tmp << token->m_DisplayName << _T(": ") << token->m_PartFirst;
1253 else
1254 tmp << token->m_DisplayName;
1255 }
1256 else
1257 tmp << token->m_DisplayName;
1258
1259 tokens.push_back(CCToken(i, tmp, token->m_DisplayName, 5, iidx));
1260 }
1261
1262 EditorColourSet* theme = ed->GetColourSet();
1263 if (theme && !isAfterPercent && (pdir == cdNone || pdir == cdOther) )
1264 {
1265 int iidx = ilist->GetImageCount();
1266 control->RegisterImage(iidx, GetFortranKeywordImage(ilistImHeight));
1267 // theme keywords
1268 HighlightLanguage lang = theme->GetLanguageForFilename(_T(".")+wxFileName(ed->GetFilename()).GetExt());
1269
1270 int kwcase = cfg->ReadInt(_T("/keywords_case"), 0);
1271 for (int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i)
1272 {
1273 if (!m_LexerKeywordsToInclude[i])
1274 continue;
1275
1276 int oldSize = result->size();
1277 wxString keywords = theme->GetKeywords(lang, i);
1278 wxStringTokenizer tkz(keywords, _T(" \t\r\n"), wxTOKEN_STRTOK);
1279 while (tkz.HasMoreTokens())
1280 {
1281 wxString kw = tkz.GetNextToken();
1282
1283 if ( (m_UseSmartCC && kw.Lower().StartsWith(NameUnderCursorLw) && m_pKeywordsParser->HasTokenSuitableKind(kw,tokenKind) &&
1284 CCSmartFilter::FitsToContext(kw, firstWords))
1285 || (!m_UseSmartCC && kw.Lower().StartsWith(NameUnderCursorLw)) )
1286 {
1287 // check for unique_strings
1288 if (unique_strings.find(kw) != unique_strings.end())
1289 continue;
1290 unique_strings.insert(kw);
1291
1292 switch (kwcase)
1293 {
1294 case 0:
1295 {
1296 break;
1297 }
1298 case 1:
1299 {
1300 kw = kw.MakeUpper();
1301 break;
1302 }
1303 case 2:
1304 {
1305 kw = kw.Mid(0,1).MakeUpper() + kw.Mid(1).MakeLower();
1306 break;
1307 }
1308 default :
1309 {
1310 kw = kw.MakeLower();
1311 break;
1312 }
1313 }
1314 m_pKeywordsParser->FindTokens(kw, *result);
1315 int newSize = result->size();
1316 if (newSize > oldSize && (result->Item(newSize-1)->m_TokenKind & tokenKind))
1317 {
1318 tokens.push_back(CCToken(newSize-1, kw, iidx));
1319 oldSize = newSize;
1320 }
1321 else
1322 tokens.push_back(CCToken(wxNOT_FOUND, kw, iidx));
1323 }
1324 }
1325 }
1326 }
1327 else if (pdir == cdOpenMP || pdir == cdOpenACC)
1328 {
1329 int iidx = ilist->GetImageCount();
1330 control->RegisterImage(iidx, GetFortranKeywordImage(ilistImHeight));
1331
1332 int kwcase = cfg->ReadInt(_T("/keywords_case"), 0);
1333 const wxArrayString* kwOMP = m_pKeywordsParser->GetKeywords(pdir);
1334 for (size_t i=0; i<kwOMP->size(); i++)
1335 {
1336 wxString kw = kwOMP->Item(i);
1337
1338 if (kw.Lower().StartsWith(NameUnderCursorLw))
1339 {
1340 switch (kwcase)
1341 {
1342 case 0:
1343 {
1344 break;
1345 }
1346 case 1:
1347 {
1348 kw = kw.MakeUpper();
1349 break;
1350 }
1351 case 2:
1352 {
1353 kw = kw.Mid(0,1).MakeUpper() + kw.Mid(1).MakeLower();
1354 break;
1355 }
1356 default :
1357 {
1358 kw = kw.MakeLower();
1359 break;
1360 }
1361 }
1362 tokens.push_back(CCToken(wxNOT_FOUND, kw, iidx));
1363 }
1364 }
1365 }
1366
1367 if (items.GetCount() == 0)
1368 {
1369 return;
1370 }
1371 items.Sort(SortCCList);
1372
1373 // Remove duplicate items
1374 size_t i=0;
1375 size_t count=items.Count() - 1;
1376 while (i < count)
1377 {
1378 if (items.Item(i)==items.Item(i+1))
1379 {
1380 items.RemoveAt(i);
1381 count--;
1382 }
1383 else
1384 i++;
1385 }
1386
1387 if (control->CallTipActive())
1388 {
1389 m_WasCallTipActive = true;
1390 }
1391
1392 control->AutoCompSetIgnoreCase(true);
1393 control->AutoCompSetCancelAtStart(true);
1394 control->AutoCompSetFillUps(wxEmptyString);
1395 control->AutoCompSetAutoHide(true);
1396 control->AutoCompSetDropRestOfWord(m_IsAutoPopup ? false : true);
1397 ed->GetControl()->AutoCompSetSeparator('\n');
1398 ed->GetControl()->AutoCompSetMaxWidth(80);
1399 ed->GetControl()->AutoCompSetMaxHeight(16);
1400 wxString final = GetStringFromArray(items, _T("\n"));
1401 final.Trim();
1402
1403 control->AutoCompShow(pos - start, final);
1404
1405 return;
1406 }
1407 else if (!control->CallTipActive())
1408 {
1409 wxString msg = _("Too many results.\n"
1410 "Please edit results' limit in code-completion options,\n"
1411 "or type at least one more character to narrow the scope down.");
1412 control->CallTipShow(control->GetCurrentPos(), msg);
1413 return;
1414 }
1415
1416 return;
1417 }
1418
GetProviderStatusFor(cbEditor * ed)1419 FortranProject::CCProviderStatus FortranProject::GetProviderStatusFor(cbEditor* ed)
1420 {
1421 if (ed && m_pNativeParser->IsFileFortran(ed->GetShortName()))
1422 return ccpsActive;
1423 return ccpsInactive;
1424 }
1425
GetCallTips(int pos,int style,cbEditor * ed,int & argsPos)1426 std::vector<FortranProject::CCCallTip> FortranProject::GetCallTips(int pos, int style, cbEditor* ed, int& argsPos)
1427 {
1428 argsPos = wxSCI_INVALID_POSITION;
1429 std::vector<FortranProject::CCCallTip> tips;
1430 if (!IsAttached() || !m_InitDone || !ed)
1431 return tips;
1432
1433 int hlStart = wxSCI_INVALID_POSITION;
1434 int hlEnd = wxSCI_INVALID_POSITION;
1435 int commas; // how many commas has the user typed so far?
1436 int commasPos; // how many commas until current position?
1437 wxArrayString callTips;
1438 wxArrayInt idxFuncSub;
1439 TokensArrayFlatClass tokensTmp;
1440 TokensArrayFlat* result = tokensTmp.GetTokens();
1441 TokenFlat* token = NULL;
1442 bool isAfterPercent = false;
1443
1444 wxString lastName;
1445 wxString argNameUnderCursor;
1446 m_pNativeParser->CollectInformationForCallTip(commas, commasPos, argNameUnderCursor, lastName, isAfterPercent, argsPos, result);
1447
1448 if (isAfterPercent && result->GetCount() > 0)
1449 {
1450 if (result->Item(0)->m_TokenKind == tkProcedure)
1451 {
1452 m_pNativeParser->GetCallTipsForTypeBoundProc(result, callTips);
1453 idxFuncSub.Add(1);
1454 }
1455 else if (result->Item(0)->m_TokenKind == tkInterface)
1456 m_pNativeParser->GetCallTipsForGenericTypeBoundProc(result, callTips, idxFuncSub);
1457 else if (result->Item(0)->m_TokenKind == tkVariable &&
1458 Manager::Get()->GetConfigManager(_T("fortran_project"))->ReadBool(_T("/call_tip_arrays"), true))
1459 {
1460 wxString callTip;
1461 m_pNativeParser->GetCallTipsForVariable(result->Item(0), callTip);
1462 if (!callTip.IsEmpty())
1463 callTips.Add(callTip);
1464 }
1465 }
1466 else if (!lastName.IsEmpty())
1467 {
1468 m_pNativeParser->GetCallTips(lastName, m_LogOnlyUseAssoc, m_LogOnlyPublicNames, callTips, result);
1469
1470 wxString kwName;
1471 if (lastName.IsSameAs(_T("open")))
1472 kwName = _T("__fortran_statement_") + lastName;
1473 else
1474 kwName = lastName;
1475 m_pKeywordsParser->GetCallTips(kwName, callTips, result);
1476 }
1477
1478 bool isUnique = true;
1479 wxString definition;
1480 for (unsigned int i = 0; i < callTips.GetCount(); ++i)
1481 {
1482 if (!callTips[i].IsEmpty()) // non-empty
1483 {
1484 if (!definition.IsEmpty())
1485 {
1486 isUnique = false;
1487 break;
1488 }
1489 definition << callTips[i];
1490 token = result->Item(i);
1491
1492 int nCommas = m_pNativeParser->CountCommas(callTips[i], 1, false);
1493 int commasDif = commas - nCommas;
1494 if (commasDif > 0)
1495 {
1496 for (int idif=0; idif< commasDif; idif++)
1497 {
1498 definition << _T(", *???*");
1499 }
1500 definition << _T(" ");
1501 }
1502 }
1503 }
1504
1505 if (isUnique && !argNameUnderCursor.IsEmpty())
1506 {
1507 // Determine number of commas before argNameUnderCursor
1508 int argidx = definition.Lower().Find(argNameUnderCursor.Lower());
1509 if (argidx != wxNOT_FOUND)
1510 {
1511 commasPos = m_pNativeParser->CountCommas(definition.Mid(0, argidx), 1, false);
1512 }
1513 }
1514
1515 if (!definition.IsEmpty() && isUnique && token && token->m_TokenKind == tkVariable)
1516 {
1517 m_pNativeParser->GetCallTipHighlight(definition, commasPos, hlStart, hlEnd);
1518 }
1519 else if (!definition.IsEmpty() && isUnique &&
1520 (!isAfterPercent || ( isAfterPercent && result->GetCount() >= 2 && (result->Item(0)->m_TokenKind == tkProcedure) )))
1521 {
1522 m_pNativeParser->GetCallTipHighlight(definition, commasPos, hlStart, hlEnd);
1523 if (isAfterPercent)
1524 token = result->Item(1);
1525
1526 if (token->m_TokenKind == tkSubroutine || token->m_TokenKind == tkFunction || token->m_TokenKind == tkType)
1527 {
1528 wxString argName = definition.Mid(hlStart,hlEnd-hlStart);
1529 argName = argName.BeforeFirst(_T(','));
1530 argName = argName.BeforeFirst(_T(')'));
1531 argName.Replace(_T("["),_T(" "));
1532 argName.Replace(_T("]"),_T(" "));
1533 argName.Trim().Trim(false);
1534
1535 wxString argDecl;
1536 wxString argDescription;
1537 bool found = m_pNativeParser->GetParser()->FindTokenDeclaration(*token, argName, argDecl, argDescription);
1538 if (!found)
1539 found = m_pKeywordsParser->GetParser()->FindTokenDeclaration(*token, argName, argDecl, argDescription);
1540 if (found)
1541 {
1542 definition << _T('\n') << argDecl;
1543 if (!argDescription.IsEmpty())
1544 definition << _T('\n') << _T("! ") << argDescription;
1545 }
1546 }
1547 }
1548 else if(!isUnique)
1549 {
1550 if ( (isAfterPercent && (callTips.GetCount() != idxFuncSub.GetCount()))
1551 || (!isAfterPercent && (callTips.GetCount() != result->GetCount())) )
1552 return tips;
1553
1554 if (lastName.IsEmpty())
1555 return tips;
1556
1557 for (size_t i=0; i < callTips.GetCount(); ++i)
1558 {
1559 definition = _T("");
1560 if (isAfterPercent)
1561 definition << result->Item(idxFuncSub[i])->m_DisplayName << _T('\n');
1562 else
1563 definition << result->Item(i)->m_DisplayName << _T('\n');
1564 int mStart = definition.length();
1565
1566 wxString ctdef = callTips.Item(i);
1567 int nCommas = m_pNativeParser->CountCommas(ctdef, 1, false);
1568 int commasDif = commas - nCommas;
1569 if (commasDif > 0)
1570 {
1571 for (int j=0; j<commasDif; j++)
1572 {
1573 ctdef << _T(", *???*");
1574 }
1575 ctdef << _T(" ");
1576 }
1577 definition << ctdef;
1578
1579 m_pNativeParser->GetCallTipHighlight(ctdef, commasPos, hlStart, hlEnd);
1580 if (isAfterPercent)
1581 token = result->Item(idxFuncSub[i]);
1582 else
1583 token = result->Item(i);
1584 if (token->m_TokenKind == tkSubroutine || token->m_TokenKind == tkFunction)
1585 {
1586 wxString argName = callTips.Item(i).Mid(hlStart,hlEnd-hlStart);
1587 argName = argName.BeforeFirst(_T(','));
1588 argName = argName.BeforeFirst(_T(')'));
1589 argName.Replace(_T("["),_T(" "));
1590 argName.Replace(_T("]"),_T(" "));
1591 argName.Trim().Trim(false);
1592
1593 wxString argDecl;
1594 wxString argDescription;
1595 if (m_pNativeParser->GetParser()->FindTokenDeclaration(*token, argName, argDecl, argDescription))
1596 {
1597 definition << _T('\n') << argDecl;
1598 if (!argDescription.IsEmpty())
1599 definition << _T('\n') << _T("! ") << argDescription;
1600 }
1601 }
1602 hlStart += mStart;
1603 hlEnd += mStart;
1604 if (!definition.IsEmpty())
1605 tips.push_back(CCCallTip(definition, hlStart, hlEnd));
1606 }
1607 }
1608
1609 if (isUnique && !definition.IsEmpty())
1610 tips.push_back(CCCallTip(definition, hlStart, hlEnd));
1611
1612 if (!tips.empty())
1613 {
1614 if (m_LogUseWindow && (!m_WasCallTipInfoLog || !m_LastCallTipName.IsSameAs(lastName)))
1615 ShowInfoLog(result, isAfterPercent);
1616
1617 m_ShowedCallTip = true;
1618 m_LastCallTipName = lastName;
1619 m_WasCallTipInfoLog = true;
1620 }
1621
1622 return tips;
1623 }
1624
GetTokenAt(int position,cbEditor * ed,bool & allowCallTip)1625 std::vector<FortranProject::CCToken> FortranProject::GetTokenAt(int position, cbEditor* ed, bool& allowCallTip)
1626 {
1627 // Get tokens for tooltip.
1628 std::vector<CCToken> tokens;
1629 if (!IsAttached() || !m_InitDone)
1630 return tokens;
1631
1632 cbEditor* edLoc = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
1633 if (ed != edLoc)
1634 return tokens;
1635
1636 if (!m_pNativeParser->IsFileFortran(ed->GetShortName()))
1637 return tokens;
1638
1639 allowCallTip = false;
1640
1641 cbStyledTextCtrl* control = ed->GetControl();
1642
1643 if ((m_ShowedCallTip && control->CallTipActive()) || control->AutoCompActive())
1644 return tokens;
1645
1646 const int style = control->GetStyleAt(position);
1647 if (style != wxSCI_F_DEFAULT && style != wxSCI_F_OPERATOR && style != wxSCI_F_IDENTIFIER
1648 && style != wxSCI_F_OPERATOR2 && style != wxSCI_F_WORD && style != wxSCI_F_WORD2
1649 && style != wxSCI_F_WORD3)
1650 return tokens;
1651
1652 int endOfWord = control->WordEndPosition(position, true);
1653 int startOfWord = control->WordStartPosition(position, true);
1654 wxString nameUnder = control->GetTextRange(startOfWord, endOfWord);
1655 if (nameUnder.IsEmpty())
1656 return tokens;
1657
1658 ParserF* pParser = m_pNativeParser->GetParser();
1659 TokensArrayFlatClass tokensTmp;
1660 TokensArrayFlat* result = tokensTmp.GetTokens();
1661
1662 bool isAfterPercent = false;
1663 pParser->FindMatchTokensForToolTip(nameUnder, endOfWord, ed, m_LogOnlyUseAssoc, m_LogOnlyPublicNames, *result, isAfterPercent);
1664 if (result->IsEmpty())
1665 m_pKeywordsParser->FindTokens(nameUnder, *result);
1666
1667 if (result->size() > 32 || result->size() == 0)
1668 return tokens;
1669
1670 bool type_bound = false;
1671 wxString msg;
1672 for (size_t i=0; i<result->GetCount(); ++i)
1673 {
1674 TokenFlat* token = result->Item(i);
1675 if (!token->m_Rename.IsEmpty())
1676 {
1677 msg << token->m_Rename << _T(" => ") << token->m_DisplayName << _T("\n");
1678 }
1679 if (token->m_TokenKind == tkVariable)
1680 {
1681 msg << token->m_TypeDefinition << _T(" :: ") << token->m_DisplayName << token->m_Args << _T("\n");
1682 }
1683 else if (token->m_TokenKind == tkType)
1684 {
1685 msg << _T("type: ") << token->m_DisplayName << _T("\n");
1686 }
1687 else if (token->m_TokenKind == tkSubroutine)
1688 {
1689 msg << _T("subroutine ") << token->m_DisplayName << token->m_Args << _T("\n");
1690 }
1691 else if (token->m_TokenKind == tkFunction)
1692 {
1693 if (!token->m_PartFirst.IsEmpty())
1694 {
1695 msg << token->m_PartFirst << _T(" ");
1696 }
1697 msg << _T("function ") << token->m_DisplayName << token->m_Args << _T("\n");
1698 }
1699 else if (token->m_TokenKind == tkProcedure)
1700 {
1701 TokenFlat* token2 = 0;
1702 if (result->GetCount() > i+1)
1703 {
1704 i++;
1705 token2 = result->Item(i);
1706 }
1707 pParser->FindTooltipForTypeBoundProc(msg, token, token2);
1708 type_bound = true;
1709 }
1710 else if (isAfterPercent && token->m_TokenKind == tkInterface)
1711 {
1712 while (i < result->GetCount())
1713 {
1714 token = result->Item(i);
1715 if (token->m_TokenKind == tkInterface)
1716 {
1717 wxString specNames = token->m_PartLast;
1718 specNames.Replace(_T(" "),_T(", "));
1719 msg << _T("generic :: ") << token->m_DisplayName << _T(" => ") << specNames << _T("\n");
1720 }
1721 i++;
1722 }
1723 type_bound = true;
1724 }
1725 else
1726 {
1727 msg << token->GetTokenKindString() << _T(" ") << token->m_DisplayName << token->m_Args << _T("\n");
1728 }
1729 wxString doc = HtmlDoc::GetDocForTooltip(token);
1730 if (!doc.IsEmpty())
1731 msg << _T("! ") << doc << _T("\n");
1732 }
1733 if (result->GetCount() == 1 && !type_bound)
1734 {
1735 if (!result->Item(0)->m_Filename.IsEmpty())
1736 {
1737 if (result->Item(0)->m_ParentTokenKind == tkModule)
1738 {
1739 msg << result->Item(0)->m_ParentDisplayName << _T(", ");
1740 }
1741 msg << result->Item(0)->m_Filename.AfterLast(wxFILE_SEP_PATH) << _T(":") << result->Item(0)->m_LineStart;
1742 }
1743 else
1744 msg.Trim();
1745 }
1746 else
1747 msg.Trim();
1748
1749 if (!m_IsDebugging)
1750 {
1751 tokens.push_back(CCToken(wxNOT_FOUND, msg));
1752 m_ShowedCallTip = false;
1753 }
1754
1755 if (result->GetCount() >= 1 && m_LogUseWindow)
1756 {
1757 ShowInfoLog(result, isAfterPercent);
1758 m_WasCallTipInfoLog = false;
1759 }
1760
1761 return tokens;
1762 }
1763
ShowInfoLog(TokensArrayFlat * result,bool isAfterPercent)1764 void FortranProject::ShowInfoLog(TokensArrayFlat* result, bool isAfterPercent)
1765 {
1766 if (!m_LogUseWindow)
1767 return;
1768 if (result->GetCount() == 0)
1769 return;
1770
1771 wxString logMsg;
1772 wxString fileNameOld;
1773
1774 if (!isAfterPercent)
1775 {
1776 unsigned int countMax = 20<result->GetCount() ? 20 : result->GetCount();
1777 for (unsigned int i=0; i < countMax; i++)
1778 {
1779 TokenFlat* token = result->Item(i);
1780 wxString logMsg1;
1781 bool readFile;
1782 if (token->m_Filename.IsSameAs(fileNameOld))
1783 {
1784 readFile = false;
1785 }
1786 else
1787 {
1788 readFile = true;
1789 fileNameOld = token->m_Filename;
1790 }
1791
1792 if (!token->m_Rename.IsEmpty())
1793 {
1794 logMsg << token->m_Rename << _T(" => ") << token->m_DisplayName << _T("\n");
1795 }
1796
1797 if (token->m_TokenKind == tkSubroutine || token->m_TokenKind == tkFunction || token->m_TokenKind == tkType)
1798 {
1799 if (m_pNativeParser->GetParser()->FindInfoLog(*token,m_LogComAbove,m_LogComBelow,m_LogDeclar,m_LogComVariab,logMsg1,readFile))
1800 {
1801 logMsg << logMsg1 << _T("\n\n");
1802 }
1803 }
1804 else if (token->m_TokenKind == tkInterface)
1805 {
1806 if (m_pNativeParser->GetParser()->GetTokenStr(*token, logMsg1))
1807 logMsg << logMsg1 << _T("\n\n");
1808
1809 fileNameOld.Empty();
1810 }
1811 }
1812 if (!logMsg.IsEmpty())
1813 {
1814 if (countMax < result->GetCount())
1815 {
1816 logMsg << wxString::Format(_T("!*********** %d more interfaces was not showed *************"),int(result->GetCount())-int(countMax));
1817 }
1818 WriteToLog(logMsg);
1819 }
1820 }
1821 else //isAfterPercent
1822 {
1823 if (result->Item(0)->m_TokenKind == tkProcedure)
1824 m_pNativeParser->GetParser()->FindInfoLogForTypeBoundProc(*result,m_LogComAbove,m_LogComBelow,m_LogDeclar,m_LogComVariab,logMsg);
1825 else if (result->Item(0)->m_TokenKind == tkInterface)
1826 m_pNativeParser->GetParser()->FindInfoLogForGenericTBProc(*result,m_LogComAbove,m_LogComBelow,m_LogDeclar,m_LogComVariab,logMsg);
1827 if (!logMsg.IsEmpty())
1828 {
1829 WriteToLog(logMsg);
1830 }
1831 }
1832 }
1833
1834
GetConfigurationPanel(wxWindow * parent)1835 cbConfigurationPanel* FortranProject::GetConfigurationPanel(wxWindow* parent)
1836 {
1837 FPOptionsDlg* dlg = new FPOptionsDlg(parent, m_pNativeParser, this);
1838 return dlg;
1839 }
1840
GetProjectConfigurationPanel(wxWindow * parent,cbProject * project)1841 cbConfigurationPanel* FortranProject::GetProjectConfigurationPanel(wxWindow* parent, cbProject* project)
1842 {
1843 if (!m_pNativeParser)
1844 return nullptr;
1845
1846 if (m_pNativeParser->HasFortranFiles(project))
1847 {
1848 FPOptionsProjectDlg* dlg = new FPOptionsProjectDlg(parent, project, m_pNativeParser);
1849 return dlg;
1850 }
1851 return nullptr;
1852 }
1853
OnProjectLoadingHook(cbProject * prj,TiXmlElement * elem,bool loading)1854 void FortranProject::OnProjectLoadingHook(cbProject* prj, TiXmlElement* elem, bool loading)
1855 {
1856 if (!prj || !elem)
1857 return; // ?! Should actually NOT happen...
1858
1859 if (loading)
1860 {
1861 // Project is loaded
1862 wxArrayString adirs;
1863 TiXmlElement* node = elem->FirstChildElement("fortran_project");
1864 if (node)
1865 {
1866 for(TiXmlElement* e = node->FirstChildElement("additional_dir"); e != NULL; e = e->NextSiblingElement("additional_dir"))
1867 {
1868 adirs.Add(cbC2U(e->Attribute("dir")));
1869 }
1870 }
1871 m_pNativeParser->SetProjectSearchDirs(prj, adirs);
1872 }
1873 else
1874 {
1875 // Hook called when saving project file
1876
1877 TiXmlElement* node = elem->FirstChildElement("fortran_project");
1878 if (!node)
1879 node = elem->InsertEndChild(TiXmlElement("fortran_project"))->ToElement();
1880 node->Clear();
1881
1882 wxArrayString adirs = m_pNativeParser->GetProjectSearchDirs(prj);
1883 for (size_t i=0; i<adirs.size(); ++i)
1884 {
1885 TiXmlElement* e = node->InsertEndChild(TiXmlElement("additional_dir"))->ToElement();
1886 e->SetAttribute("dir", cbU2C(adirs.Item(i)));
1887 }
1888 }
1889 }
1890
RereadOptions()1891 void FortranProject::RereadOptions()
1892 {
1893 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("fortran_project"));
1894
1895 m_LexerKeywordsToInclude[0] = cfg->ReadBool(_T("/lexer_keywords_set1"), true);
1896 m_LexerKeywordsToInclude[1] = cfg->ReadBool(_T("/lexer_keywords_set2"), true);
1897 m_LexerKeywordsToInclude[2] = cfg->ReadBool(_T("/lexer_keywords_set3"), false);
1898 m_LexerKeywordsToInclude[3] = cfg->ReadBool(_T("/lexer_keywords_set4"), false);
1899 m_LexerKeywordsToInclude[4] = cfg->ReadBool(_T("/lexer_keywords_set5"), false);
1900 m_LexerKeywordsToInclude[5] = cfg->ReadBool(_T("/lexer_keywords_set6"), false);
1901 m_LexerKeywordsToInclude[6] = cfg->ReadBool(_T("/lexer_keywords_set7"), false);
1902 m_LexerKeywordsToInclude[7] = cfg->ReadBool(_T("/lexer_keywords_set8"), false);
1903 m_LexerKeywordsToInclude[8] = cfg->ReadBool(_T("/lexer_keywords_set9"), false);
1904
1905 m_MaxMatch = cfg->ReadInt(_T("/max_matches"), 1000);
1906 if (m_MaxMatch < 1)
1907 m_MaxMatch = 1;
1908
1909 m_UseSmartCC = cfg->ReadBool(_T("/use_smart_code_completion"), true);
1910 m_LogOnlyUseAssoc = cfg->ReadBool(_T("/only_use_associated"), true);
1911 m_LogOnlyPublicNames = !cfg->ReadBool(_T("/show_hidden_entities"), false);
1912 m_LogShowTypeVariables = cfg->ReadBool(_T("/show_type_variables"), true);
1913
1914 m_LogUseWindow = cfg->ReadBool(_T("/use_log_window"), true);
1915 m_LogComAbove = cfg->ReadBool(_T("/include_comments_above"), true);
1916 m_LogComBelow = cfg->ReadBool(_T("/include_comments_below"), true);
1917 m_LogDeclar = cfg->ReadBool(_T("/include_declarations_log"), true);
1918 m_LogComVariab = cfg->ReadBool(_T("/include_log_comments_variable"), true);
1919
1920 int docsOpt = cfg->ReadInt(_T("/show_docs_window"), 1);
1921 if (docsOpt == 0)
1922 m_DocsShowOption = dsoAlways;
1923 else if (docsOpt == 1)
1924 m_DocsShowOption = dsoOnly;
1925 else
1926 m_DocsShowOption = dsoNot;
1927
1928 m_AutoInsertEnabled = cfg->ReadBool(_T("/auto_insert_enabled"), true);
1929 m_AutoInsert.ReadAIOptions();
1930
1931 if (!m_pFortranLog && m_LogUseWindow)
1932 {
1933 CreateLogWindow();
1934 }
1935 else if (m_pFortranLog && !m_LogUseWindow)
1936 {
1937 RemoveLogWindow(false);
1938 }
1939
1940 m_ConstrHighlighter.ReadOptions();
1941 }
1942
WriteToLog(const wxString & text)1943 void FortranProject::WriteToLog(const wxString& text)
1944 {
1945 if (m_pFortranLog)
1946 {
1947 m_pFortranLog->WriteToInfoWindow(text);
1948 }
1949 }
1950
CreateLogWindow()1951 void FortranProject::CreateLogWindow()
1952 {
1953 m_pFortranLog = new FInfoWindow();
1954 }
1955
RemoveLogWindow(bool appShutDown)1956 void FortranProject::RemoveLogWindow(bool appShutDown)
1957 {
1958 if (appShutDown)
1959 return;
1960
1961 if (m_pFortranLog)
1962 {
1963 m_pFortranLog->RemoveFromNotebook();
1964 m_pFortranLog = 0L;
1965 }
1966 }
1967
OnJumpBack(wxCommandEvent & event)1968 void FortranProject::OnJumpBack(wxCommandEvent& event)
1969 {
1970 JumpTracker* jTr = m_pNativeParser->GetJumpTracker();
1971
1972 if (!jTr->IsJumpBackEmpty())
1973 {
1974 jTr->MakeJumpBack();
1975 CheckEnableToolbar();
1976 JumpToLine(jTr->GetHomeAddress());
1977 }
1978 }
1979
OnJumpHome(wxCommandEvent & event)1980 void FortranProject::OnJumpHome(wxCommandEvent& event)
1981 {
1982 JumpTracker* jTr = m_pNativeParser->GetJumpTracker();
1983
1984 if (!jTr->IsJumpHomeEmpty())
1985 JumpToLine(jTr->GetHomeAddress());
1986 }
1987
OnJumpForward(wxCommandEvent & event)1988 void FortranProject::OnJumpForward(wxCommandEvent& event)
1989 {
1990 JumpTracker* jTr = m_pNativeParser->GetJumpTracker();
1991 if (!jTr->IsJumpForwardEmpty())
1992 {
1993 jTr->MakeJumpForward();
1994 CheckEnableToolbar();
1995 JumpToLine(jTr->GetHomeAddress());
1996 }
1997 }
1998
CheckEnableToolbar()1999 void FortranProject::CheckEnableToolbar()
2000 {
2001 m_pToolbar->EnableTool(XRCID("idFortProjBack"), !m_pNativeParser->GetJumpTracker()->IsJumpBackEmpty());
2002 m_pToolbar->EnableTool(XRCID("idFortProjHome"), !m_pNativeParser->GetJumpTracker()->IsJumpHomeEmpty());
2003 m_pToolbar->EnableTool(XRCID("idFortProjForward"), !m_pNativeParser->GetJumpTracker()->IsJumpForwardEmpty());
2004
2005 wxMenuItem* pJumpBack = m_FortranToolsMenu->FindItem(idMenuJumpBack);
2006 pJumpBack->Enable(!m_pNativeParser->GetJumpTracker()->IsJumpBackEmpty());
2007 wxMenuItem* pJumpHome = m_FortranToolsMenu->FindItem(idMenuJumpHome);
2008 pJumpHome->Enable(!m_pNativeParser->GetJumpTracker()->IsJumpHomeEmpty());
2009 wxMenuItem* pJumpForward = m_FortranToolsMenu->FindItem(idMenuJumpForward);
2010 pJumpForward->Enable(!m_pNativeParser->GetJumpTracker()->IsJumpForwardEmpty());
2011 }
2012
JumpToLine(const LineAddress & adr)2013 void FortranProject::JumpToLine(const LineAddress& adr)
2014 {
2015 if (!IsAttached() || Manager::IsAppShuttingDown())
2016 return;
2017
2018 EditorManager* edMan = Manager::Get()->GetEditorManager();
2019 if (cbEditor* ed = edMan->Open(adr.GetFilename()))
2020 {
2021 ed->GotoLine(adr.GetLineNumber());
2022 }
2023 }
2024
OnDebuggerStarted(CodeBlocksEvent & event)2025 void FortranProject::OnDebuggerStarted(CodeBlocksEvent& event)
2026 {
2027 event.Skip();
2028 m_IsDebugging = true;
2029 }
2030
OnDebuggerFinished(CodeBlocksEvent & event)2031 void FortranProject::OnDebuggerFinished(CodeBlocksEvent& event)
2032 {
2033 event.Skip();
2034 m_IsDebugging = false;
2035 }
2036
OnGenerateMakefile(wxCommandEvent & event)2037 void FortranProject::OnGenerateMakefile(wxCommandEvent& event)
2038 {
2039 m_pNativeParser->GenMakefile();
2040 }
2041
OnChangeCase(wxCommandEvent & event)2042 void FortranProject::OnChangeCase(wxCommandEvent& event)
2043 {
2044 ChangeCase changCaseDlg(Manager::Get()->GetAppWindow());
2045 changCaseDlg.ShowModal();
2046 }
2047
OnTab2Space(wxCommandEvent & event)2048 void FortranProject::OnTab2Space(wxCommandEvent& event)
2049 {
2050 Tab2Space tab2SpaceDlg(Manager::Get()->GetAppWindow());
2051 tab2SpaceDlg.ShowModal();
2052 }
2053
OnBindTo(wxCommandEvent & event)2054 void FortranProject::OnBindTo(wxCommandEvent& event)
2055 {
2056 cbProject* pr = Manager::Get()->GetProjectManager()->GetActiveProject();
2057 if (pr)
2058 pr->SaveAllFiles();
2059
2060 Bindto bindto(Manager::Get()->GetAppWindow(), m_pNativeParser->GetParser());
2061 bindto.ShowModal();
2062 }
2063
OnFormatIndent(wxCommandEvent & event)2064 void FortranProject::OnFormatIndent(wxCommandEvent& event)
2065 {
2066 FormatIndent indent;
2067 indent.Format();
2068 }
2069
OnReparseEditorTimer(wxTimerEvent & event)2070 void FortranProject::OnReparseEditorTimer(wxTimerEvent& event)
2071 {
2072 m_pNativeParser->ReparseCurrentEditor();
2073 }
2074
GetIncludeFilename(cbStyledTextCtrl * control)2075 wxString FortranProject::GetIncludeFilename(cbStyledTextCtrl* control)
2076 {
2077 if (!control)
2078 return wxEmptyString;
2079 wxString strName;
2080 int style = control->GetStyleAt(control->GetCurrentPos());
2081 if (style == wxSCI_F_STRING1 || style == wxSCI_F_STRING2 || style == wxSCI_F_PREPROCESSOR)
2082 {
2083 wxString curLine = control->GetCurLine().Lower();
2084 if (!curLine.Trim(false).StartsWith(_T("include")) &&
2085 !curLine.Trim(false).StartsWith(_T("#include")))
2086 return wxEmptyString;
2087
2088 int pos = control->GetCurrentPos();
2089 int lineCur = control->LineFromPosition(pos);
2090 int lineStartPos = control->PositionFromLine(lineCur);
2091 wxString strBefore = control->GetTextRange(lineStartPos, pos).Lower().Trim(false);
2092 int idx1 = strBefore.Find('"', true);
2093 int idx2 = strBefore.Find('\'', true);
2094 int idx3 = strBefore.Find('<', true);
2095 if ((idx1 == wxNOT_FOUND && idx2 == wxNOT_FOUND && idx3 == wxNOT_FOUND) ||
2096 (idx1 != wxNOT_FOUND && idx2 != wxNOT_FOUND) ||
2097 (idx1 != wxNOT_FOUND && idx3 != wxNOT_FOUND) ||
2098 (idx2 != wxNOT_FOUND && idx3 != wxNOT_FOUND))
2099 return wxEmptyString;
2100 int idx = (idx1 != wxNOT_FOUND) ? idx1 : idx2;
2101 idx = (idx != wxNOT_FOUND) ? idx : idx3;
2102 if (strBefore.Mid(0,idx).Trim().Trim(false) != _T("include") &&
2103 strBefore.Mid(0,idx).Trim().Trim(false) != _T("#include"))
2104 return wxEmptyString;
2105
2106 wxChar ch = curLine[idx];
2107 if (ch == '<')
2108 ch = '>';
2109 wxString strLast = curLine.Mid(strBefore.size());
2110 int idxL = strLast.Find(ch);
2111 if (idxL == wxNOT_FOUND)
2112 return wxEmptyString;
2113 int idxE = strBefore.size() + idxL;
2114 strName = curLine.Mid(idx+1, idxE-(idx+1)).Trim().Trim(false);
2115 }
2116 return strName;
2117 }
2118
OnMenuEditPaste(wxCommandEvent & event)2119 void FortranProject::OnMenuEditPaste(wxCommandEvent& event)
2120 {
2121 // Process clipboard data only if we have the focus
2122 if (!IsAttached() || !m_InitDone)
2123 {
2124 event.Skip();
2125 return;
2126 }
2127 if (m_pNativeParser->GetWorkspaceBrowser())
2128 m_pNativeParser->GetWorkspaceBrowser()->OnMenuEditPaste(event);
2129 else
2130 event.Skip();
2131 }
2132
GetDocumentation(const CCToken & tok)2133 wxString FortranProject::GetDocumentation(const CCToken& tok)
2134 {
2135 if (tok.id == wxNOT_FOUND || m_DocsShowOption == dsoNot)
2136 return wxEmptyString;
2137
2138 TokensArrayFlat* tokens = m_TokensCCList.GetTokens();
2139 if (tok.id >= int(tokens->GetCount()))
2140 return wxEmptyString;
2141
2142 wxString doc;
2143 TokenFlat* token = tokens->Item(tok.id);
2144
2145 bool hasDoc;
2146 doc = HtmlDoc::GenerateHtmlDoc(token, tok.id, hasDoc);
2147 if (m_DocsShowOption == dsoOnly && !hasDoc)
2148 return wxEmptyString;
2149
2150 return doc;
2151 }
2152
OnDocumentationLink(wxHtmlLinkEvent & event,bool & dismissPopup)2153 wxString FortranProject::OnDocumentationLink(wxHtmlLinkEvent& event, bool& dismissPopup)
2154 {
2155 bool isGoto = false;
2156 long int tokenIdx;
2157 wxString doc = HtmlDoc::OnDocumentationLink(event, dismissPopup, isGoto, tokenIdx);
2158
2159 if (isGoto)
2160 {
2161 TokensArrayFlat* tokens = m_TokensCCList.GetTokens();
2162 if (tokenIdx >= long(tokens->GetCount()))
2163 return wxEmptyString;
2164
2165 TokenFlat* pToken = tokens->Item(tokenIdx);
2166 if ( pToken->m_Filename.EndsWith(UnixFilename(_T("/fortranproject/fortran_intrinsic_modules.f90")))
2167 || pToken->m_Filename.EndsWith(UnixFilename(_T("/fortranproject/fortran_procedures.f90"))) )
2168 {
2169 // don't go to fortran_intrinsic_modules.f90
2170 dismissPopup = false;
2171 return doc;
2172 }
2173 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
2174 if (!ed)
2175 return doc;
2176 if (!GotoToken(pToken, ed))
2177 dismissPopup = false;
2178 }
2179 return doc;
2180 }
2181
OnShowCallTreeView(wxCommandEvent & event)2182 void FortranProject::OnShowCallTreeView(wxCommandEvent& event)
2183 {
2184 CodeBlocksDockEvent evt(event.IsChecked() ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW);
2185 evt.pWindow = (wxWindow*)m_pCallTree->GetCallTreeView();
2186 Manager::Get()->ProcessEvent(evt);
2187 }
2188
OnShowCallTree(wxCommandEvent & event)2189 void FortranProject::OnShowCallTree(wxCommandEvent& event)
2190 {
2191 bool showCallTree = false;
2192 if (event.GetId() == idShowCallTree)
2193 showCallTree = true;
2194
2195 ShowCallTree(showCallTree);
2196 }
2197
ShowCallTree(bool showCallTree)2198 void FortranProject::ShowCallTree(bool showCallTree)
2199 {
2200 cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
2201 if (!ed)
2202 return;
2203
2204 if (!m_pNativeParser->IsFileFortran(ed->GetFilename()))
2205 return;
2206
2207 // Make fortran keyword set
2208 std::set< wxString> keywordSet;
2209 EditorColourSet* theme = ed->GetColourSet();
2210 if (!theme)
2211 return;
2212
2213 HighlightLanguage lang = theme->GetLanguageForFilename(_T(".")+wxFileName(ed->GetFilename()).GetExt());
2214 for (int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i)
2215 {
2216 wxString keywords = theme->GetKeywords(lang, i);
2217 wxStringTokenizer tkz(keywords, _T(" \t\r\n"), wxTOKEN_STRTOK);
2218 while (tkz.HasMoreTokens())
2219 {
2220 wxString kw = tkz.GetNextToken();
2221 if (keywordSet.find(kw) != keywordSet.end())
2222 continue;
2223 keywordSet.insert(kw);
2224 }
2225 }
2226
2227 wxString NameUnderCursor;
2228 bool isOperator;
2229 if(!EditorHasNameUnderCursor(NameUnderCursor, isOperator))
2230 return;
2231 if (isOperator)
2232 return;
2233
2234 ParserF* pParser = m_pNativeParser->GetParser();
2235 m_pCallTree->BuildCallTree(ed, NameUnderCursor, pParser, keywordSet, showCallTree);
2236 }
2237
LoadFortranKeywordImages()2238 void FortranProject::LoadFortranKeywordImages()
2239 {
2240 wxString prefix = ConfigManager::GetDataFolder() + _T("/images/fortranproject/");
2241 m_FKImages[16] = cbLoadBitmap(prefix + _T("16x16/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2242 m_FKImages[20] = cbLoadBitmap(prefix + _T("20x20/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2243 m_FKImages[24] = cbLoadBitmap(prefix + _T("24x24/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2244 m_FKImages[28] = cbLoadBitmap(prefix + _T("28x28/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2245 m_FKImages[32] = cbLoadBitmap(prefix + _T("32x32/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2246 m_FKImages[40] = cbLoadBitmap(prefix + _T("40x40/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2247 m_FKImages[48] = cbLoadBitmap(prefix + _T("48x48/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2248 m_FKImages[56] = cbLoadBitmap(prefix + _T("56x56/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2249 m_FKImages[64] = cbLoadBitmap(prefix + _T("64x64/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2250 }
2251
GetFortranKeywordImage(int height)2252 wxBitmap FortranProject::GetFortranKeywordImage(int height)
2253 {
2254 if (height <= 16)
2255 return m_FKImages[16];
2256 else if (height <= 20)
2257 return m_FKImages[20];
2258 else if (height <= 24)
2259 return m_FKImages[24];
2260 else if (height <= 28)
2261 return m_FKImages[28];
2262 else if (height <= 32)
2263 return m_FKImages[32];
2264 else if (height <= 40)
2265 return m_FKImages[40];
2266 else if (height <= 48)
2267 return m_FKImages[48];
2268 else if (height <= 56)
2269 return m_FKImages[56];
2270
2271 return m_FKImages[64];
2272 }
2273