2
* This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3
* http://www.gnu.org/licenses/gpl-3.0.html
6
* $Id: coderefactoring.cpp 10265 2015-05-15 10:56:43Z jenslody $
7
* $HeadURL: http://svn.code.sf.net/p/codeblocks/code/branches/release-16.xx/src/plugins/codecompletion/coderefactoring.cpp $
13
#include <wx/button.h>
16
#include <wx/statbmp.h>
17
#include <wx/stattext.h>
18
#include <wx/textdlg.h>
21
#include <cbproject.h>
22
#include <editorcolourset.h>
23
#include <editormanager.h>
24
#include <logmanager.h>
27
#include <wx/progdlg.h>
29
#include <cbstyledtextctrl.h>
30
#include <encodingdetector.h>
31
#include <searchresultslog.h>
33
#include "coderefactoring.h"
34
#include "nativeparser.h"
36
#define CC_CODEREFACTORING_DEBUG_OUTPUT 0
38
#if defined(CC_GLOBAL_DEBUG_OUTPUT)
39
#if CC_GLOBAL_DEBUG_OUTPUT == 1
40
#undef CC_CODEREFACTORING_DEBUG_OUTPUT
41
#define CC_CODEREFACTORING_DEBUG_OUTPUT 1
42
#elif CC_GLOBAL_DEBUG_OUTPUT == 2
43
#undef CC_CODEREFACTORING_DEBUG_OUTPUT
44
#define CC_CODEREFACTORING_DEBUG_OUTPUT 2
48
#if CC_CODEREFACTORING_DEBUG_OUTPUT == 1
49
#define TRACE(format, args...) \
50
CCLogger::Get()->DebugLog(F(format, ##args))
51
#define TRACE2(format, args...)
52
#elif CC_CODEREFACTORING_DEBUG_OUTPUT == 2
53
#define TRACE(format, args...) \
56
if (g_EnableDebugTrace) \
57
CCLogger::Get()->DebugLog(F(format, ##args)); \
60
#define TRACE2(format, args...) \
61
CCLogger::Get()->DebugLog(F(format, ##args))
63
#define TRACE(format, args...)
64
#define TRACE2(format, args...)
67
class ScopeDialog : public wxDialog
70
ScopeDialog(wxWindow* parent, const wxString& title) :
71
wxDialog(parent, wxID_ANY, title)
73
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
74
wxBoxSizer* infoSizer = new wxBoxSizer(wxHORIZONTAL);
75
wxString findImgFile = ConfigManager::GetDataFolder() + _T("/images/filefind.png");
76
wxStaticBitmap* findIco = new wxStaticBitmap(this, wxID_ANY, wxBitmap(wxImage(findImgFile)));
77
infoSizer->Add(findIco, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
78
wxStaticText* scopeText = new wxStaticText(this, wxID_ANY, _("Please choose the find scope for search tokens"));
79
infoSizer->Add(scopeText, 1, wxALL | wxALIGN_CENTER_VERTICAL,
80
wxDLG_UNIT(this, wxSize(5, 0)).GetWidth());
81
sizer->Add(infoSizer, 1, wxALL | wxALIGN_CENTER_HORIZONTAL, 5);
82
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
83
m_OpenFiles = new wxButton(this, ID_OPEN_FILES, _("&Open files"), wxDefaultPosition, wxDefaultSize, 0,
84
wxDefaultValidator, _T("ID_OPEN_FILES"));
85
m_OpenFiles->SetDefault();
86
btnSizer->Add(m_OpenFiles, 1, wxALL | wxALIGN_CENTER_VERTICAL, 5);
87
m_ProjectFiles = new wxButton(this, ID_PROJECT_FILES, _("&Project files"), wxDefaultPosition,
88
wxDefaultSize, 0, wxDefaultValidator, _T("ID_PROJECT_FILES"));
89
btnSizer->Add(m_ProjectFiles, 1, wxALL | wxALIGN_CENTER_VERTICAL, 5);
90
sizer->Add(btnSizer, 1, wxBOTTOM | wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
93
sizer->SetSizeHints(this);
96
Connect(ID_OPEN_FILES, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&ScopeDialog::OnOpenFilesClick);
97
Connect(ID_PROJECT_FILES, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&ScopeDialog::OnProjectFilesClick);
98
Connect(wxID_ANY, wxEVT_CLOSE_WINDOW, (wxObjectEventFunction)&ScopeDialog::OnClose);
102
static const long ID_OPEN_FILES;
103
static const long ID_PROJECT_FILES;
106
void OnClose(cb_unused wxCloseEvent& event) { EndDialog(wxID_CLOSE); }
107
void OnOpenFilesClick(cb_unused wxCommandEvent& event) { EndDialog(ID_OPEN_FILES);}
108
void OnProjectFilesClick(cb_unused wxCommandEvent& event) { EndDialog(ID_PROJECT_FILES); }
110
wxButton* m_OpenFiles;
111
wxButton* m_ProjectFiles;
114
const long ScopeDialog::ID_OPEN_FILES = wxNewId();
115
const long ScopeDialog::ID_PROJECT_FILES = wxNewId();
117
CodeRefactoring::CodeRefactoring(NativeParser& np) :
122
CodeRefactoring::~CodeRefactoring()
126
wxString CodeRefactoring::GetSymbolUnderCursor()
128
EditorManager* edMan = Manager::Get()->GetEditorManager();
129
cbEditor* editor = edMan->GetBuiltinActiveEditor();
131
return wxEmptyString;
133
cbStyledTextCtrl* control = editor->GetControl();
134
const int style = control->GetStyleAt(control->GetCurrentPos());
135
if (control->IsString(style) || control->IsComment(style))
136
return wxEmptyString;
138
if (!m_NativeParser.GetParser().Done())
140
wxString msg(_("The Parser is still parsing files."));
141
cbMessageBox(msg, _("Code Refactoring"), wxOK | wxICON_WARNING);
142
msg += m_NativeParser.GetParser().NotDoneReason();
143
CCLogger::Get()->DebugLog(msg);
145
return wxEmptyString;
148
const int pos = editor->GetControl()->GetCurrentPos();
149
const int start = editor->GetControl()->WordStartPosition(pos, true);
150
const int end = editor->GetControl()->WordEndPosition(pos, true);
151
return editor->GetControl()->GetTextRange(start, end);
154
bool CodeRefactoring::Parse()
156
cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
160
const wxString targetText = GetSymbolUnderCursor();
161
if (targetText.IsEmpty())
164
TokenIdxSet targetResult;
165
const int endOfWord = editor->GetControl()->WordEndPosition(editor->GetControl()->GetCurrentPos(), true);
166
m_NativeParser.MarkItemsByAI(targetResult, true, false, true, endOfWord);
167
if (targetResult.empty())
169
cbMessageBox(_("Symbol not found under cursor!"), _("Code Refactoring"), wxOK | wxICON_WARNING);
173
// handle local variables
174
bool isLocalVariable = false;
176
TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
178
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
180
const Token* token = tree->at(*targetResult.begin());
183
const Token* parent = tree->at(token->m_ParentIndex);
184
if (parent && parent->m_TokenKind == tkFunction)
185
isLocalVariable = true;
188
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
191
cbProject* project = m_NativeParser.GetProjectByEditor(editor);
192
if (isLocalVariable || !project)
193
files.Add(editor->GetFilename());
196
ScopeDialog scopeDlg(Manager::Get()->GetAppWindow(), _("Code Refactoring"));
197
const int ret = scopeDlg.ShowModal();
198
if (ret == ScopeDialog::ID_OPEN_FILES)
199
GetOpenedFiles(files);
200
else if (ret == ScopeDialog::ID_PROJECT_FILES)
201
GetAllProjectFiles(files, project);
209
size_t count = SearchInFiles(files, targetText);
211
count = VerifyResult(targetResult, targetText, isLocalVariable);
216
void CodeRefactoring::FindReferences()
222
void CodeRefactoring::RenameSymbols()
224
const wxString targetText = GetSymbolUnderCursor();
225
if (targetText.IsEmpty())
228
wxString replaceText = wxGetTextFromUser(_("Rename symbols under cursor"),
229
_("Code Refactoring"),
231
Manager::Get()->GetAppWindow());
232
if (!replaceText.IsEmpty() && replaceText != targetText && Parse())
234
DoRenameSymbols(targetText, replaceText);
239
size_t CodeRefactoring::SearchInFiles(const wxArrayString& files, const wxString& targetText)
241
EditorManager* edMan = Manager::Get()->GetEditorManager();
242
m_SearchDataMap.clear();
244
// now that list is filled, we'll search
245
wxWindow* parent = edMan->GetBuiltinActiveEditor()->GetParent();
246
cbStyledTextCtrl* control = new cbStyledTextCtrl(parent, wxID_ANY, wxDefaultPosition, wxSize(0, 0));
247
control->Show(false);
249
// let's create a progress dialog because it might take some time depending on the files count
250
wxProgressDialog* progress = new wxProgressDialog(_("Code Refactoring"),
251
_("Please wait while searching inside the project..."),
253
Manager::Get()->GetAppWindow(),
254
wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
255
PlaceWindow(progress);
257
for (size_t i = 0; i < files.GetCount(); ++i)
259
// update the progress bar
260
if (!progress->Update(i))
261
break; // user pressed "Cancel"
263
// check if the file is already opened in built-in editor and do search in it
264
cbEditor* ed = edMan->IsBuiltinOpen(files[i]);
266
control->SetText(ed->GetControl()->GetText());
267
else // else load the file in the control
269
EncodingDetector detector(files[i]);
270
if (!detector.IsOK())
272
control->SetText(detector.GetWxStr());
275
Find(control, files[i], targetText);
278
delete control; // done with it
279
delete progress; // done here too
281
return m_SearchDataMap.size();
284
size_t CodeRefactoring::VerifyResult(const TokenIdxSet& targetResult, const wxString& targetText,
285
bool isLocalVariable)
287
EditorManager* edMan = Manager::Get()->GetEditorManager();
288
cbEditor* editor = edMan->GetBuiltinActiveEditor();
292
const Token* parentOfLocalVariable = nullptr;
295
TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
297
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
299
const Token* token = tree->at(*targetResult.begin());
300
parentOfLocalVariable = tree->at(token->m_ParentIndex);
302
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
305
// now that list is filled, we'll search
306
cbStyledTextCtrl* control = new cbStyledTextCtrl(editor->GetParent(), wxID_ANY, wxDefaultPosition,
308
control->Show(false);
310
// styled the text to support control->GetStyleAt()
311
cbEditor::ApplyStyles(control);
312
EditorColourSet edColSet;
314
size_t totalCount = 0;
315
for (SearchDataMap::const_iterator it = m_SearchDataMap.begin(); it != m_SearchDataMap.end(); ++it)
316
totalCount += it->second.size();
318
// let's create a progress dialog because it might take some time depending on the files count
319
wxProgressDialog* progress = new wxProgressDialog(_("Code Refactoring"),
320
_("Please wait while verifying result..."),
322
Manager::Get()->GetAppWindow(),
323
wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
324
PlaceWindow(progress);
326
size_t task = totalCount;
328
bool userBreak = false;
329
for (SearchDataMap::iterator it = m_SearchDataMap.begin(); it != m_SearchDataMap.end();)
331
// check if the file is already opened in built-in editor and do search in it
332
cbEditor* ed = edMan->IsBuiltinOpen(it->first);
334
control->SetText(ed->GetControl()->GetText());
335
else // else load the file in the control
337
EncodingDetector detector(it->first);
338
if (!detector.IsOK())
340
task -= it->second.size();
341
m_SearchDataMap.erase(it++);
344
control->SetText(detector.GetWxStr());
347
// apply the corlor setting
348
edColSet.Apply(editor->GetLanguage(), control);
350
ccSearchData searchData = { control, it->first };
351
for (SearchDataList::iterator itList = it->second.begin(); itList != it->second.end();)
353
// update the progress bar
354
if (!progress->Update(totalCount - (--task)))
357
break; // user pressed "Cancel"
360
// skip string or comment
361
const int style = control->GetStyleAt(itList->pos);
362
if (control->IsString(style) || control->IsComment(style))
364
it->second.erase(itList++);
369
const int endOfWord = itList->pos + targetText.Len();
370
control->GotoPos(endOfWord);
371
m_NativeParser.MarkItemsByAI(&searchData, result, true, false, true, endOfWord);
374
it->second.erase(itList++);
379
TokenIdxSet::const_iterator findIter = targetResult.begin();
380
for (; findIter != targetResult.end(); ++findIter)
382
if (result.find(*findIter) != result.end())
386
if (findIter == targetResult.end()) // not found
387
it->second.erase(itList++);
390
// handle for local variable
393
bool do_continue = false;
395
TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
397
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
399
const Token* token = tree->at(*findIter);
402
const Token* parent = tree->at(token->m_ParentIndex);
403
if (parent != parentOfLocalVariable)
405
it->second.erase(itList++);
410
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
412
if (do_continue) continue;
419
if (it->second.empty())
420
m_SearchDataMap.erase(it++);
428
delete control; // done with it
429
delete progress; // done here too
431
return m_SearchDataMap.size();
434
void CodeRefactoring::Find(cbStyledTextCtrl* control, const wxString& file, const wxString& target)
436
const int end = control->GetLength();
442
int pos = control->FindText(start, end, target, wxSCI_FIND_WHOLEWORD | wxSCI_FIND_MATCHCASE, &lengthFound);
443
if (pos != wxSCI_INVALID_POSITION)
445
start = pos + lengthFound;
446
const int line = control->LineFromPosition(pos);
447
wxString text = control->GetLine(line).Trim(true).Trim(false);
448
m_SearchDataMap[file].push_back(crSearchData(pos, line + 1, text));
455
void CodeRefactoring::DoFindReferences()
457
cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
461
cbSearchResultsLog* searchLog = Manager::Get()->GetSearchResultLogger();
465
const wxString focusFile = editor->GetFilename();
466
const int focusLine = editor->GetControl()->GetCurrentLine() + 1;
467
wxFileName fn(focusFile);
468
const wxString basePath(fn.GetPath());
470
size_t focusIndex = 0;
473
searchLog->SetBasePath(basePath);
475
for (SearchDataMap::iterator it = m_SearchDataMap.begin(); it != m_SearchDataMap.end(); ++it)
477
for (SearchDataList::iterator itList = it->second.begin(); itList != it->second.end(); ++itList)
479
if (it->first == focusFile && itList->line == focusLine)
482
wxArrayString values;
483
wxFileName curFn(it->first);
484
curFn.MakeRelativeTo(basePath);
485
values.Add(curFn.GetFullPath());
486
values.Add(wxString::Format(_T("%d"), itList->line));
487
values.Add(itList->text);
488
searchLog->Append(values, Logger::info);
494
if (Manager::Get()->GetConfigManager(_T("message_manager"))->ReadBool(_T("/auto_show_search"), true))
496
CodeBlocksLogEvent evtSwitch(cbEVT_SWITCH_TO_LOG_WINDOW, searchLog);
497
CodeBlocksLogEvent evtShow(cbEVT_SHOW_LOG_MANAGER);
498
Manager::Get()->ProcessEvent(evtSwitch);
499
Manager::Get()->ProcessEvent(evtShow);
502
searchLog->FocusEntry(focusIndex);
505
void CodeRefactoring::DoRenameSymbols(const wxString& targetText, const wxString& replaceText)
507
EditorManager* edMan = Manager::Get()->GetEditorManager();
508
cbEditor* editor = edMan->GetBuiltinActiveEditor();
512
cbProject* project = m_NativeParser.GetProjectByEditor(editor);
513
for (SearchDataMap::iterator it = m_SearchDataMap.begin(); it != m_SearchDataMap.end(); ++it)
515
// check if the file is already opened in built-in editor and do search in it
516
cbEditor* ed = edMan->IsBuiltinOpen(it->first);
519
ProjectFile* pf = project ? project->GetFileByFilename(it->first) : 0;
520
ed = edMan->Open(it->first, it->second.front().pos, pf);
523
cbStyledTextCtrl* control = ed->GetControl();
524
control->BeginUndoAction();
526
for (SearchDataList::reverse_iterator rIter = it->second.rbegin(); rIter != it->second.rend(); ++rIter)
528
control->SetTargetStart(rIter->pos);
529
control->SetTargetEnd(rIter->pos + targetText.Len());
530
control->ReplaceTarget(replaceText);
531
// for find references
532
rIter->text.Replace(targetText, replaceText);
535
control->EndUndoAction();
539
void CodeRefactoring::GetAllProjectFiles(wxArrayString& files, cbProject* project)
544
// fill the search list with all the project files
545
for (FilesList::const_iterator it = project->GetFilesList().begin();
546
it != project->GetFilesList().end(); ++it)
548
ProjectFile* pf = *it;
552
ParserCommon::EFileType ft = ParserCommon::FileType(pf->relativeFilename);
553
if (ft != ParserCommon::ftOther)
554
files.Add(pf->file.GetFullPath());
558
void CodeRefactoring::GetOpenedFiles(wxArrayString& files)
560
EditorManager* edMan = Manager::Get()->GetEditorManager();
563
for (int i = 0; i < edMan->GetEditorsCount(); ++i)
564
files.Add(edMan->GetEditor(i)->GetFilename());