~ubuntu-branches/ubuntu/wily/codelite/wily

« back to all changes in this revision

Viewing changes to LiteEditor/clang_code_completion.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessio Treglia
  • Date: 2010-07-29 19:42:47 UTC
  • mfrom: (0.2.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20100729194247-hqgxamd2yl8f1l7x
* New upstream release.
* Bump Standards.
* Refresh patches.
* Add license information about files under ./Debugger/

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "clang_code_completion.h"
 
2
#include "environmentconfig.h"
 
3
#include "processreaderthread.h"
 
4
#include "globals.h"
 
5
#include "jobqueue.h"
 
6
#include "fileextmanager.h"
 
7
#include <wx/tokenzr.h>
 
8
#include "ieditor.h"
 
9
#include "imanager.h"
 
10
#include "workspace.h"
 
11
#include "project.h"
 
12
#include <wx/filename.h>
 
13
#include <wx/dir.h>
 
14
#include <wx/arrstr.h>
 
15
#include "procutils.h"
 
16
 
 
17
static wxStopWatch gStopWatch;
 
18
 
 
19
BEGIN_EVENT_TABLE(ClangCodeCompletion, wxEvtHandler)
 
20
EVT_COMMAND(wxID_ANY, wxEVT_PROC_DATA_READ,  ClangCodeCompletion::OnProcessOutput)
 
21
EVT_COMMAND(wxID_ANY, wxEVT_PROC_TERMINATED, ClangCodeCompletion::OnProcessTerminated)
 
22
END_EVENT_TABLE()
 
23
 
 
24
ClangCodeCompletion* ClangCodeCompletion::ms_instance = 0;
 
25
 
 
26
ClangCodeCompletion::ClangCodeCompletion()
 
27
        : m_manager         (NULL)
 
28
        , m_process         (NULL)
 
29
        , m_activationPos   (wxNOT_FOUND)
 
30
        , m_activationEditor(NULL)
 
31
{
 
32
}
 
33
 
 
34
ClangCodeCompletion::~ClangCodeCompletion()
 
35
{
 
36
}
 
37
 
 
38
void ClangCodeCompletion::Initialize(IManager* manager)
 
39
{
 
40
        m_manager = manager;
 
41
}
 
42
 
 
43
ClangCodeCompletion* ClangCodeCompletion::Instance()
 
44
{
 
45
        if(ms_instance == 0) {
 
46
                ms_instance = new ClangCodeCompletion();
 
47
        }
 
48
        return ms_instance;
 
49
}
 
50
 
 
51
void ClangCodeCompletion::Release()
 
52
{
 
53
        if(ms_instance) {
 
54
                delete ms_instance;
 
55
        }
 
56
        ms_instance = 0;
 
57
}
 
58
 
 
59
void ClangCodeCompletion::CodeComplete(IEditor* editor)
 
60
{
 
61
        // TODO :: this code works but for now we disable it until we
 
62
        // add an option in the UI for it
 
63
#if 0
 
64
        // Sanity:
 
65
        if(!editor || !m_manager || !m_manager->GetWorkspace() || !m_manager->IsWorkspaceOpen())
 
66
                return;
 
67
        
 
68
        // clang process is already running
 
69
        if(m_process)
 
70
                return;
 
71
                
 
72
        // First, we need to build the command line
 
73
        // try to locate the basic include paths:
 
74
        wxArrayString args = GetStandardIncludePathsArgs();
 
75
 
 
76
        // Next apppend the user include paths
 
77
        wxString errMsg;
 
78
 
 
79
        // First, we need to find the currently active workspace configuration
 
80
        BuildMatrixPtr matrix = m_manager->GetWorkspace()->GetBuildMatrix();
 
81
        if(!matrix) {
 
82
                return;
 
83
        }
 
84
 
 
85
        wxString workspaceSelConf = matrix->GetSelectedConfigurationName();
 
86
 
 
87
        // Now that we got the selected workspace configuration, extract the related project configuration
 
88
        ProjectPtr proj =  m_manager->GetWorkspace()->FindProjectByName(editor->GetProjectName(), errMsg);
 
89
        if(!proj) {
 
90
                return;
 
91
        }
 
92
 
 
93
        wxString currentBuffer = editor->GetTextRange(0, editor->GetCurrentPosition());
 
94
        if(currentBuffer.IsEmpty())
 
95
                return;
 
96
 
 
97
        wxString projectSelConf = matrix->GetProjectSelectedConf(workspaceSelConf, proj->GetName());
 
98
        BuildConfigPtr dependProjbldConf = m_manager->GetWorkspace()->GetProjBuildConf(proj->GetName(), projectSelConf);
 
99
        if(dependProjbldConf && dependProjbldConf->IsCustomBuild() == false) {
 
100
                // Get the include paths and add them
 
101
                wxString projectIncludePaths = dependProjbldConf->GetIncludePath();
 
102
                wxArrayString projectIncludePathsArr = wxStringTokenize(projectIncludePaths, wxT(";"), wxTOKEN_STRTOK);
 
103
                for(size_t i=0; i<projectIncludePathsArr.GetCount(); i++) {
 
104
                        args.Add( wxString::Format(wxT("-I%s"), projectIncludePathsArr[i].c_str()) );
 
105
                }
 
106
 
 
107
                // get the compiler options and add them
 
108
                wxString projectCompileOptions = dependProjbldConf->GetCompileOptions();
 
109
                wxArrayString projectCompileOptionsArr = wxStringTokenize(projectCompileOptions, wxT(";"), wxTOKEN_STRTOK);
 
110
                for(size_t i=0; i<projectCompileOptionsArr.GetCount(); i++) {
 
111
                        wxString cmpOption (projectCompileOptionsArr.Item(i));
 
112
                        cmpOption.Trim().Trim(false);
 
113
 
 
114
                        wxString tmp;
 
115
                        // Expand backticks / $(shell ...) syntax supported by codelite
 
116
                        if(cmpOption.StartsWith(wxT("$(shell "), &tmp) || cmpOption.StartsWith(wxT("`"), &tmp)) {
 
117
                                cmpOption = tmp;
 
118
 
 
119
                                tmp.Clear();
 
120
                                if(cmpOption.EndsWith(wxT(")"), &tmp) || cmpOption.EndsWith(wxT("`"), &tmp)) {
 
121
                                        cmpOption = tmp;
 
122
                                }
 
123
 
 
124
                                if(m_backticks.find(cmpOption) == m_backticks.end()) {
 
125
 
 
126
                                        // Expand the backticks into their value
 
127
                                        wxArrayString outArr;
 
128
                                        // Apply the environment before executing the command
 
129
                                        EnvSetter setter( EnvironmentConfig::Instance() );
 
130
                                        ProcUtils::SafeExecuteCommand(cmpOption, outArr);
 
131
                                        wxString expandedValue;
 
132
                                        for(size_t j=0; j<outArr.size(); j++)
 
133
                                        {
 
134
                                                expandedValue << outArr.Item(j) << wxT(" ");
 
135
                                        }
 
136
                                        m_backticks[cmpOption] = expandedValue;
 
137
                                        cmpOption = expandedValue;
 
138
                                } else {
 
139
                                        cmpOption = m_backticks.find(cmpOption)->second;
 
140
                                }
 
141
                        }
 
142
                        args.Add( cmpOption );
 
143
                }
 
144
 
 
145
                // get the compiler preprocessor and add them as well
 
146
                wxString projectPreps = dependProjbldConf->GetPreprocessor();
 
147
                wxArrayString projectPrepsArr = wxStringTokenize(projectPreps, wxT(";"), wxTOKEN_STRTOK);
 
148
                for(size_t i=0; i<projectPrepsArr.GetCount(); i++) {
 
149
                        args.Add( wxString::Format(wxT("-D%s"), projectPrepsArr[i].c_str()) );
 
150
                }
 
151
 
 
152
                wxString filterWord;
 
153
 
 
154
                // Move backward until we found our -> or :: or .
 
155
                for(size_t i=currentBuffer.Length() - 1; i>0; i--) {
 
156
                        if(currentBuffer.EndsWith(wxT("->")) || currentBuffer.EndsWith(wxT(".")) || currentBuffer.EndsWith(wxT("::"))) {
 
157
                                break;
 
158
                        } else {
 
159
                                filterWord.Prepend(currentBuffer.GetChar(i));
 
160
                                currentBuffer.Truncate(currentBuffer.Length() - 1);
 
161
                        }
 
162
                }
 
163
 
 
164
                // Get the current line's starting pos
 
165
                int lineStartPos = editor->PosFromLine( editor->GetCurrentLine() );
 
166
                int column       = editor->GetCurrentPosition() - lineStartPos  + 1;
 
167
                int line         = editor->GetCurrentLine() + 1;
 
168
 
 
169
                column -= (int)filterWord.Length();
 
170
 
 
171
                // Create temp file
 
172
                m_tmpfile.clear();
 
173
                m_tmpfile << editor->GetFileName().GetPath(wxPATH_GET_SEPARATOR|wxPATH_GET_VOLUME) << editor->GetFileName().GetName() << wxT("_clang_tmp") << wxT(".cpp");
 
174
 
 
175
                wxString command;
 
176
 
 
177
                command << wxT("clang -w -fsyntax-only -Xclang -code-completion-at=");
 
178
                FileExtManager::FileType type = FileExtManager::GetType(editor->GetFileName().GetFullPath());
 
179
                if(type == FileExtManager::TypeSourceC || type == FileExtManager::TypeSourceCpp) {
 
180
                        if(!WriteFileWithBackup(m_tmpfile, currentBuffer, false)) {
 
181
                                wxLogMessage(wxT("Failed to write temp file: %s"), m_tmpfile.c_str());
 
182
                                return;
 
183
                        }
 
184
                        wxString completefileName;
 
185
                        completefileName << editor->GetFileName().GetName() << wxT("_clang_tmp") << wxT(".cpp");
 
186
                        command << completefileName << wxT(":") << line << wxT(":") << column << wxT(" ") << completefileName;
 
187
 
 
188
                } else {
 
189
                        wxString implFile;
 
190
                        implFile << wxT("#include <") << editor->GetFileName().GetFullName() << wxT(">\n");
 
191
                        if(!WriteFileWithBackup(m_tmpfile, implFile, false)) {
 
192
                                wxLogMessage(wxT("Failed to write temp file: %s"), implFile.c_str());
 
193
                                return;
 
194
                        }
 
195
                        wxString completefileName;
 
196
                        completefileName << editor->GetFileName().GetName() << wxT("_clang_tmp") << wxT(".cpp");
 
197
                        command << editor->GetFileName().GetFullPath() << wxT(":") << line << wxT(":") << column << wxT(" ") << completefileName;
 
198
                }
 
199
 
 
200
                // Add the arguments
 
201
                wxString argString;
 
202
                for(size_t i=0; i<args.size(); i++) {
 
203
                        argString << wxT(" ") << args.Item(i);
 
204
                }
 
205
 
 
206
                command << argString;
 
207
                if(m_process) {
 
208
                        DoCleanUp();
 
209
                }
 
210
                
 
211
                command.Replace(wxT("\n"), wxT(" "));
 
212
                command.Replace(wxT("\r"), wxT(" "));
 
213
                
 
214
                m_process = CreateAsyncProcess(this, command, editor->GetFileName().GetPath(wxPATH_GET_SEPARATOR|wxPATH_GET_VOLUME));
 
215
                if(! m_process ) {
 
216
                        wxLogMessage(wxT("Failed to start process: %s"), command.c_str());
 
217
                        return;
 
218
                }
 
219
                
 
220
                m_activationEditor = editor;
 
221
                m_activationPos    = lineStartPos + column - 1;
 
222
                
 
223
                gStopWatch.Start();
 
224
        }
 
225
#endif
 
226
}
 
227
 
 
228
wxArrayString ClangCodeCompletion::GetStandardIncludePathsArgs()
 
229
{
 
230
        static wxArrayString paths;
 
231
#ifdef __WXMSW__
 
232
        if(paths.IsEmpty() == false)
 
233
                return paths;
 
234
 
 
235
        wxString      standardIncludeBase;
 
236
        wxGetEnv(wxT("MINGW_INCL_HOME"), &standardIncludeBase);
 
237
        if (standardIncludeBase.IsEmpty() == false && wxDir::Exists(standardIncludeBase)) {
 
238
 
 
239
                wxString mingwBaseDir (standardIncludeBase );
 
240
 
 
241
                // "C:/MinGW-4.4.1/include"
 
242
                standardIncludeBase.Prepend(wxT("-I"));
 
243
                paths.Add(standardIncludeBase + wxT("\\include"));
 
244
                standardIncludeBase << wxT("\\lib\\gcc\\mingw32\\");
 
245
                mingwBaseDir        << wxT("\\lib\\gcc\\mingw32\\");
 
246
 
 
247
                long          highestVersion(0);
 
248
                wxString      sHighestVersion;
 
249
                wxArrayString files;
 
250
 
 
251
                if (wxDir::Exists( mingwBaseDir ) ) {
 
252
                        wxDir::GetAllFiles(mingwBaseDir, &files, wxEmptyString, wxDIR_DIRS|wxDIR_FILES);
 
253
 
 
254
                        //filter out all non-directories
 
255
                        for (size_t i=0; i<files.GetCount(); i++) {
 
256
                                wxFileName fn(files.Item(i));
 
257
 
 
258
                                wxString p = fn.GetPath().Mid( mingwBaseDir.Length() );
 
259
                                wxString tmp_p(p);
 
260
                                tmp_p.Replace(wxT("."), wxT(""));
 
261
                                long number(0);
 
262
                                tmp_p.ToLong( &number );
 
263
                                if (number && number > highestVersion) {
 
264
                                        sHighestVersion = p.BeforeFirst(wxFileName::GetPathSeparator());
 
265
                                        highestVersion  = number;
 
266
                                }
 
267
                        }
 
268
 
 
269
                        if (sHighestVersion.IsEmpty() == false) {
 
270
                                standardIncludeBase << sHighestVersion << wxT("\\include");
 
271
                                paths.Add( standardIncludeBase );
 
272
                                paths.Add( standardIncludeBase + wxT("\\c++") );
 
273
                                paths.Add( standardIncludeBase + wxT("\\c++\\mingw32") );
 
274
                        }
 
275
                }
 
276
        }
 
277
        return paths;
 
278
#else
 
279
        return paths;
 
280
#endif
 
281
}
 
282
 
 
283
void ClangCodeCompletion::OnProcessTerminated(wxCommandEvent& e)
 
284
{
 
285
        ProcessEventData *ped = (ProcessEventData*) e.GetClientData();
 
286
        delete ped;
 
287
        
 
288
        //wxLogMessage(wxT("clang process ended after %d ms"), gStopWatch.Time());
 
289
        
 
290
        // Parse the output
 
291
        if(m_output.IsEmpty() == false) {
 
292
                DoParseOutput();
 
293
        }
 
294
        DoCleanUp();
 
295
}
 
296
 
 
297
void ClangCodeCompletion::OnProcessOutput(wxCommandEvent& e)
 
298
{
 
299
        ProcessEventData *ped = (ProcessEventData*) e.GetClientData();
 
300
        if(ped) {
 
301
                m_output << ped->GetData();
 
302
                delete ped;
 
303
        }
 
304
        e.Skip();
 
305
}
 
306
 
 
307
void ClangCodeCompletion::DoParseOutput()
 
308
{
 
309
        // Sanity
 
310
        if(m_output.IsEmpty() || !m_activationEditor || m_activationPos == wxNOT_FOUND)
 
311
                return;
 
312
                
 
313
        wxArrayString entries = wxStringTokenize(m_output, wxT("\n\r"), wxTOKEN_STRTOK);
 
314
        std::vector<TagEntryPtr> tags;
 
315
        tags.reserve( entries.size() );
 
316
        
 
317
        for(size_t i=0; i<entries.GetCount(); i++) {
 
318
                entries.Item(i).Trim().Trim(false);
 
319
                if(entries.Item(i).IsEmpty())
 
320
                        continue;
 
321
                
 
322
                TagEntryPtr tag = ClangEntryToTagEntry( entries.Item(i) );
 
323
                if(tag) {
 
324
                        tags.push_back( tag );
 
325
                }
 
326
                
 
327
        }
 
328
        
 
329
        if(tags.empty() == false) {
 
330
                int curline = m_activationEditor->GetCurrentLine();
 
331
                int actline = m_activationEditor->LineFromPos(m_activationPos);
 
332
                if(curline != actline)
 
333
                        return;
 
334
                // we are still on the same line
 
335
                
 
336
                // We need to make sure that the caret is still infront of the completion char
 
337
                if(m_activationEditor->GetCurrentPosition() < m_activationPos)
 
338
                        return;
 
339
                
 
340
                // Get the text between the current position and the activation pos and filter all results
 
341
                // that dont match
 
342
                wxString filter = m_activationEditor->GetTextRange(m_activationPos, m_activationEditor->GetCurrentPosition());
 
343
                if(filter.IsEmpty() == false) {
 
344
                        filter.MakeLower();
 
345
                        
 
346
                        std::vector<TagEntryPtr> filteredTags;
 
347
                        filteredTags.reserve( tags.size() );
 
348
                        for(size_t i=0; i<tags.size(); i++) {
 
349
                                wxString n = tags.at(i)->GetName();
 
350
                                n.MakeLower();
 
351
                                
 
352
                                if(n.StartsWith(filter)) {
 
353
                                        filteredTags.push_back(tags.at(i));
 
354
                                }
 
355
                        }
 
356
                        
 
357
                        if(filteredTags.empty() == false) {
 
358
                                m_activationEditor->ShowCompletionBox(filteredTags, filter, NULL);
 
359
                        }
 
360
                } else {
 
361
                        m_activationEditor->ShowCompletionBox(tags, wxEmptyString, NULL);
 
362
                }
 
363
        }
 
364
}
 
365
 
 
366
TagEntryPtr ClangCodeCompletion::ClangEntryToTagEntry(const wxString& line)
 
367
{
 
368
        // an example of line:
 
369
        // COMPLETION: OpenFile : [#bool#]OpenFile(<#class wxString const &fileName#>{#, <#class wxString const &projectName#>{#, <#int lineno#>#}#})
 
370
        wxString tmp;
 
371
        if(line.StartsWith(wxT("COMPLETION: "), &tmp) == false)
 
372
                return NULL;
 
373
        
 
374
        if(line.Contains(wxT("(Hidden)")))
 
375
                return NULL;
 
376
        
 
377
        // Next comes the name of the entry
 
378
        wxString name = tmp.BeforeFirst(wxT(':'));
 
379
        name.Trim().Trim(false);
 
380
        
 
381
        if(name.IsEmpty())
 
382
                return NULL;
 
383
        
 
384
        TagEntry *t = new TagEntry();
 
385
        TagEntryPtr tag(t);
 
386
        tag->SetName( name );
 
387
        
 
388
        tmp = tmp.AfterFirst(wxT(':'));
 
389
        tmp.Trim().Trim(false);
 
390
        
 
391
        // determine the kind
 
392
        tag->SetKind(wxT("prototype")); // default
 
393
        
 
394
        if(tmp == name) {
 
395
                // this a type (enum/typedef/internal class)
 
396
                tag->SetKind(wxT("class"));
 
397
        } else if(tmp.Contains(wxT("("))) {
 
398
                // definitly a method, do nothing
 
399
        } else {
 
400
                tag->SetKind(wxT("member"));
 
401
        }
 
402
        
 
403
        return tag;
 
404
}
 
405
 
 
406
void ClangCodeCompletion::DoCleanUp()
 
407
{
 
408
        delete m_process;
 
409
        m_process = NULL;
 
410
        
 
411
        // remove the temporary file
 
412
        if(m_tmpfile.IsEmpty() == false) {
 
413
#ifndef __WXMSW__
 
414
                ::unlink( m_tmpfile.mb_str(wxConvUTF8).data() );
 
415
#endif
 
416
                wxRemoveFile( m_tmpfile );
 
417
                m_tmpfile.Clear();
 
418
        }
 
419
        
 
420
        m_output.Clear();
 
421
        m_activationEditor = NULL;
 
422
        m_activationPos    = wxNOT_FOUND;
 
423
}
 
424
 
 
425
void ClangCodeCompletion::CancelCodeComplete()
 
426
{
 
427
        DoCleanUp();
 
428
}