~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric

« back to all changes in this revision

Viewing changes to src/addins/CBinding/Parser/TagDatabaseManager.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-06-27 17:03:13 UTC
  • mto: (1.8.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: james.westby@ubuntu.com-20110627170313-6cvz3s19x6e9hqe9
ImportĀ upstreamĀ versionĀ 2.5.92+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
using System;
34
34
using System.IO;
35
35
using System.Text;
 
36
using System.Linq;
36
37
using System.Text.RegularExpressions;
37
38
using System.Collections.Generic;
38
39
using System.Threading;
58
59
                private static TagDatabaseManager instance;
59
60
                private Queue<ProjectFilePair> parsingJobs = new Queue<ProjectFilePair> ();
60
61
                private Thread parsingThread;
 
62
                private CTagsManager ctags;
61
63
                
62
64
                public event ClassPadEventHandler FileUpdated;
63
65
                
78
80
                        }
79
81
                }
80
82
                
81
 
                bool DepsInstalled {
 
83
                public bool DepsInstalled {
82
84
                        get {
83
85
                                if (!checkedCtagsInstalled) {
84
86
                                        checkedCtagsInstalled = true;
85
 
                                        if (PropertyService.IsMac) {
86
 
                                                return false;
87
 
                                        }
 
87
                                        
88
88
                                        try {
89
 
                                                Runtime.ProcessService.StartProcess ("ctags", "--version", null, null).WaitForOutput ();
 
89
                                                var output = new StringWriter ();
 
90
                                                Runtime.ProcessService.StartProcess (CTagsManager.CTagsExecutable, "--version", null, output, null, null).WaitForExit ();
 
91
                                                if (PropertyService.IsMac && !output.ToString ().StartsWith ("Exuberant", StringComparison.Ordinal)) {
 
92
                                                        System.Console.WriteLine ("Fallback to OSX ctags");
 
93
                                                        ctags = new BsdCTagsManager ();
 
94
                                                } else {
 
95
                                                        ctags = new ExuberantCTagsManager ();
 
96
                                                }
90
97
                                        } catch {
91
98
                                                LoggingService.LogWarning ("Cannot update C/C++ tags database because exuberant ctags is not installed.");
92
99
                                                return false;
101
108
                                                ctagsInstalled = true;
102
109
                                        }
103
110
                                }
104
 
                                return ctagsInstalled;
 
111
                                return ctagsInstalled && ctags != null;
105
112
                        }
106
113
                        set {
107
114
                                //don't assume that the caller is correct :-)
230
237
                        return string.Empty;
231
238
                }
232
239
                
233
 
                private void UpdateSystemTags (Project project, string filename, string[] includedFiles)
 
240
                private void UpdateSystemTags (Project project, string filename, IEnumerable<string> includedFiles)
234
241
                {
 
242
                        if (!DepsInstalled)
 
243
                                return;
235
244
                        ProjectInformation info = ProjectInformationManager.Instance.Get (project);
236
245
                        List<FileInformation> files;
237
246
                        
255
264
                                        if (!contains) {
256
265
                                                FileInformation newFileInfo = new FileInformation (project, includedFile);
257
266
                                                files.Add (newFileInfo);
258
 
                                                FillFileInformation (newFileInfo);
 
267
                                                ctags.FillFileInformation (newFileInfo);
259
268
                                        }
260
269
                                        
261
270
                                        contains = false;
263
272
                        }
264
273
                }
265
274
                
266
 
                private void FillFileInformation (FileInformation fileInfo)
267
 
                {
268
 
                        if (!DepsInstalled)
269
 
                                return;
270
 
                        
271
 
                        string confdir = PropertyService.ConfigPath;
272
 
                        string tagFileName = Path.GetFileName (fileInfo.FileName) + ".tag";
273
 
                        string tagdir = Path.Combine (confdir, "system-tags");
274
 
                        string tagFullFileName = Path.Combine (tagdir, tagFileName);
275
 
                        string ctags_kinds = "--C++-kinds=+px";
276
 
                        
277
 
                        if (PropertyService.Get<bool> ("CBinding.ParseLocalVariables", true))
278
 
                                ctags_kinds += "l";
279
 
                        
280
 
                        string ctags_options = ctags_kinds + " --fields=+aStisk-fz --language-force=C++ --excmd=number --line-directives=yes -f '" + tagFullFileName + "' '" + fileInfo.FileName + "'";
281
 
                        
282
 
                        if (!Directory.Exists (tagdir))
283
 
                                Directory.CreateDirectory (tagdir);
284
 
                        
285
 
                        if (!File.Exists (tagFullFileName) || File.GetLastWriteTimeUtc (tagFullFileName) < File.GetLastWriteTimeUtc (fileInfo.FileName)) {
286
 
                                ProcessWrapper p = null;
287
 
                                System.IO.StringWriter output = null;
288
 
                                try {
289
 
                                        output = new System.IO.StringWriter ();
290
 
                                        
291
 
                                        p = Runtime.ProcessService.StartProcess ("ctags", ctags_options, null, null, output, null);
292
 
                                        p.WaitForOutput (10000);
293
 
                                        if (p.ExitCode != 0 || !File.Exists (tagFullFileName)) {
294
 
                                                LoggingService.LogError ("Ctags did not successfully populate the tags database '{0}' within ten seconds.\nOutput: {1}", tagFullFileName, output.ToString ());
295
 
                                                return;
296
 
                                        }
297
 
                                } catch (Exception ex) {
298
 
                                        throw new IOException ("Could not create tags database (You must have exuberant ctags installed).", ex);
299
 
                                } finally {
300
 
                                        if (output != null)
301
 
                                                output.Dispose ();
302
 
                                        if (p != null)
303
 
                                                p.Dispose ();
304
 
                                }
305
 
                        }
306
 
                        
307
 
                        string ctags_output;
308
 
                        string tagEntry;
309
 
                        
310
 
                        using (StreamReader reader = new StreamReader (tagFullFileName)) {
311
 
                                ctags_output = reader.ReadToEnd ();
312
 
                        }
313
 
                        
314
 
                        using (StringReader reader = new StringReader (ctags_output)) {
315
 
                                while ((tagEntry = reader.ReadLine ()) != null) {
316
 
                                        if (tagEntry.StartsWith ("!_")) continue;
317
 
                                        
318
 
                                        Tag tag = ParseTag (tagEntry);
319
 
                                        
320
 
                                        if (tag != null)
321
 
                                                AddInfo (fileInfo, tag, ctags_output);
322
 
                                }
323
 
                        }
324
 
                        
325
 
                        fileInfo.IsFilled = true;
326
 
                }
327
 
                
328
275
                private void ParsingThread ()
329
276
                {
330
277
                        try {
335
282
                                                p = parsingJobs.Dequeue ();
336
283
                                        }
337
284
                                        
338
 
                                        DoUpdateFileTags (p.Project, p.File);
 
285
                                        string[] headers = Headers (p.Project, p.File, false);
 
286
                                        ctags.DoUpdateFileTags (p.Project, p.File, headers);
 
287
                                        OnFileUpdated (new ClassPadEventArgs (p.Project));
 
288
                                        
 
289
                                        if (PropertyService.Get<bool> ("CBinding.ParseSystemTags", true))
 
290
                                                UpdateSystemTags (p.Project, p.File, Headers (p.Project, p.File, true).Except (headers));
 
291
                                        
 
292
                                        if (cache.Count > cache_size)
 
293
                                                cache.Clear ();
339
294
                                }
340
295
                        } catch (Exception ex) {
341
296
                                LoggingService.LogError ("Unhandled error updating parser database. Disabling C/C++ parsing.", ex);
342
297
                                DepsInstalled = false;
343
298
                                return;
344
299
                        }
345
 
//                      catch {
346
 
//                              LoggingService.LogError("Unexpected error while updating parser database. Disabling C/C++ parsing.");
347
 
//                              DepsInstalled = false;
348
 
//                      }
349
300
                }
350
301
                
351
302
                public void UpdateFileTags (Project project, string filename)
369
320
                        }
370
321
                }
371
322
                
372
 
                private void DoUpdateFileTags (Project project, string filename)
373
 
                {
374
 
                        if (!DepsInstalled)
375
 
                                return;
376
 
 
377
 
                        string[] headers = Headers (project, filename, false);
378
 
                        string[] system_headers = diff (Headers (project, filename, true), headers);
379
 
                        StringBuilder ctags_kinds = new StringBuilder ("--C++-kinds=+px");
380
 
                        
381
 
                        if (PropertyService.Get<bool> ("CBinding.ParseLocalVariables", true))
382
 
                                ctags_kinds.Append ("+l");
383
 
                        
384
 
                        // Maybe we should only ask for locals for 'local' files? (not external #includes?)
385
 
                        ctags_kinds.AppendFormat (" --fields=+aStisk-fz --language-force=C++ --excmd=number --line-directives=yes -f - '{0}'", filename);
386
 
                        foreach (string header in headers) {
387
 
                                ctags_kinds.AppendFormat (" '{0}'", header);
388
 
                        }
389
 
                        
390
 
                        string ctags_output = string.Empty;
391
 
 
392
 
                        ProcessWrapper p = null;
393
 
                        System.IO.StringWriter output = null, error = null;
394
 
                        try {
395
 
                                output = new System.IO.StringWriter ();
396
 
                                error = new System.IO.StringWriter ();
397
 
                                
398
 
                                p = Runtime.ProcessService.StartProcess ("ctags", ctags_kinds.ToString (), project.BaseDirectory, output, error, null);
399
 
                                p.WaitForOutput (10000);
400
 
                                if (p.ExitCode != 0) {
401
 
                                        LoggingService.LogError ("Ctags did not successfully populate the tags database from '{0}' within ten seconds.\nError output: {1}", filename, error.ToString ());
402
 
                                        return;
403
 
                                }
404
 
                                ctags_output = output.ToString ();
405
 
                        } catch (Exception ex) {
406
 
                                throw new IOException ("Could not create tags database (You must have exuberant ctags installed).", ex);
407
 
                        } finally {
408
 
                                if (output != null)
409
 
                                        output.Dispose ();
410
 
                                if (error != null)
411
 
                                        error.Dispose ();
412
 
                                if (p != null)
413
 
                                        p.Dispose ();
414
 
                        }
415
 
                        
416
 
                        ProjectInformation info = ProjectInformationManager.Instance.Get (project);
417
 
                        
418
 
                        lock (info) {
419
 
                                info.RemoveFileInfo (filename);
420
 
                                string tagEntry;
421
 
        
422
 
                                using (StringReader reader = new StringReader (ctags_output)) {
423
 
                                        while ((tagEntry = reader.ReadLine ()) != null) {
424
 
                                                if (tagEntry.StartsWith ("!_")) continue;
425
 
                                                
426
 
                                                Tag tag = ParseTag (tagEntry);
427
 
                                                
428
 
                                                if (tag != null)
429
 
                                                        AddInfo (info, tag, ctags_output);
430
 
                                        }
431
 
                                }
432
 
                        }
433
 
                        
434
 
                        OnFileUpdated (new ClassPadEventArgs (project));
435
 
                        
436
 
                        if (PropertyService.Get<bool> ("CBinding.ParseSystemTags", true))
437
 
                                UpdateSystemTags (project, filename, system_headers);
438
 
                        
439
 
                        if (cache.Count > cache_size)
440
 
                                cache.Clear ();
441
 
                }
442
 
                
443
 
                private void AddInfo (FileInformation info, Tag tag, string ctags_output)
444
 
                {
445
 
                        switch (tag.Kind)
446
 
                        {
447
 
                        case TagKind.Class:
448
 
                                Class c = new Class (tag, info.Project, ctags_output);
449
 
                                if (!info.Classes.Contains (c))
450
 
                                        info.Classes.Add (c);
451
 
                                break;
452
 
                        case TagKind.Enumeration:
453
 
                                Enumeration e = new Enumeration (tag, info.Project, ctags_output);
454
 
                                if (!info.Enumerations.Contains (e))
455
 
                                        info.Enumerations.Add (e);
456
 
                                break;
457
 
                        case TagKind.Enumerator:
458
 
                                Enumerator en= new Enumerator (tag, info.Project, ctags_output);
459
 
                                if (!info.Enumerators.Contains (en))
460
 
                                        info.Enumerators.Add (en);
461
 
                                break;
462
 
                        case TagKind.ExternalVariable:
463
 
                                break;
464
 
                        case TagKind.Function:
465
 
                                Function f = new Function (tag, info.Project, ctags_output);
466
 
                                if (!info.Functions.Contains (f))
467
 
                                        info.Functions.Add (f);
468
 
                                break;
469
 
                        case TagKind.Local:
470
 
                                Local lo = new Local (tag, info.Project, ctags_output);
471
 
                                if(!info.Locals.Contains (lo))
472
 
                                        info.Locals.Add (lo);
473
 
                                break;
474
 
                        case TagKind.Macro:
475
 
                                Macro m = new Macro (tag, info.Project);
476
 
                                if (!info.Macros.Contains (m))
477
 
                                        info.Macros.Add (m);
478
 
                                break;
479
 
                        case TagKind.Member:
480
 
                                Member me = new Member (tag, info.Project, ctags_output);
481
 
                                if (!info.Members.Contains (me))
482
 
                                        info.Members.Add (me);
483
 
                                break;
484
 
                        case TagKind.Namespace:
485
 
                                Namespace n = new Namespace (tag, info.Project, ctags_output);
486
 
                                if (!info.Namespaces.Contains (n))
487
 
                                        info.Namespaces.Add (n);
488
 
                                break;
489
 
                        case TagKind.Prototype:
490
 
                                Function fu = new Function (tag, info.Project, ctags_output);
491
 
                                if (!info.Functions.Contains (fu))
492
 
                                        info.Functions.Add (fu);
493
 
                                break;
494
 
                        case TagKind.Structure:
495
 
                                Structure s = new Structure (tag, info.Project, ctags_output);
496
 
                                if (!info.Structures.Contains (s))
497
 
                                        info.Structures.Add (s);
498
 
                                break;
499
 
                        case TagKind.Typedef:
500
 
                                Typedef t = new Typedef (tag, info.Project, ctags_output);
501
 
                                if (!info.Typedefs.Contains (t))
502
 
                                        info.Typedefs.Add (t);
503
 
                                break;
504
 
                        case TagKind.Union:
505
 
                                Union u = new Union (tag, info.Project, ctags_output);
506
 
                                if (!info.Unions.Contains (u))
507
 
                                        info.Unions.Add (u);
508
 
                                break;
509
 
                        case TagKind.Variable:
510
 
                                Variable v = new Variable (tag, info.Project);
511
 
                                if (!info.Variables.Contains (v))
512
 
                                        info.Variables.Add (v);
513
 
                                break;
514
 
                        default:
515
 
                                break;
516
 
                        }
517
 
                }
518
 
                
519
 
                private Tag ParseTag (string tagEntry)
520
 
                {
521
 
                        string file;
522
 
                        UInt64 line;
523
 
                        string name;
524
 
                        string tagField;
525
 
                        TagKind kind;
526
 
                        AccessModifier access = AccessModifier.Public;
527
 
                        string _class = null;
528
 
                        string _namespace = null;
529
 
                        string _struct = null;
530
 
                        string _union = null;
531
 
                        string _enum = null;
532
 
                        string signature = null;
533
 
                        
534
 
                        int i1 = tagEntry.IndexOf ('\t');
535
 
                        name = tagEntry.Substring (0, tagEntry.IndexOf ('\t'));
536
 
                        
537
 
                        i1 += 1;
538
 
                        int i2 = tagEntry.IndexOf ('\t', i1);
539
 
                        file = tagEntry.Substring (i1, i2 - i1);
540
 
                        
541
 
                        i1 = i2 + 1;
542
 
                        i2 = tagEntry.IndexOf (";\"", i1);
543
 
                        line = UInt64.Parse(tagEntry.Substring (i1, i2 - i1));
544
 
 
545
 
                        i1 = i2 + 3;    
546
 
                        kind = (TagKind)tagEntry[i1];
547
 
                        
548
 
                        i1 += 2;
549
 
                        tagField = (tagEntry.Length > i1? tagField = tagEntry.Substring(i1) : String.Empty);
550
 
                        
551
 
                        string[] fields = tagField.Split ('\t');
552
 
                        int index;
553
 
                        
554
 
                        foreach (string field in fields) {
555
 
                                index = field.IndexOf (':');
556
 
                                
557
 
                                // TODO: Support friend modifier
558
 
                                if (index > 0) {
559
 
                                        string key = field.Substring (0, index);
560
 
                                        string val = field.Substring (index + 1);
561
 
                                        
562
 
                                        switch (key) {
563
 
                                                case "access":
564
 
                                                        try {
565
 
                                                                access = (AccessModifier)System.Enum.Parse (typeof(AccessModifier), val, true);
566
 
                                                        } catch (ArgumentException) {
567
 
                                                        }
568
 
                                                        break;
569
 
                                                case "class":
570
 
                                                        _class = val;
571
 
                                                        break;
572
 
                                                case "namespace":
573
 
                                                        _namespace = val;
574
 
                                                        break;
575
 
                                                case "struct":
576
 
                                                        _struct = val;
577
 
                                                        break;
578
 
                                                case "union":
579
 
                                                        _union = val;
580
 
                                                        break;
581
 
                                                case "enum":
582
 
                                                        _enum = val;
583
 
                                                        break;
584
 
                                                case "signature":
585
 
                                                        signature = val;
586
 
                                                        break;
587
 
                                        }
588
 
                                }
589
 
                        }
590
 
                        
591
 
                        return new Tag (name, file, line, kind, access, _class, _namespace, _struct, _union, _enum, signature);
592
 
                }
593
 
                
594
323
                Tag BinarySearch (string[] ctags_lines, TagKind kind, string name)
595
324
                {
596
325
                        int low;
623
352
                                        bool eof = false;
624
353
                                        
625
354
                                        while (true) {
626
 
                                                Tag tag = ParseTag (entry);
 
355
                                                Tag tag = ctags.ParseTag (entry);
627
356
                                                
628
357
                                                if (tag == null)
629
358
                                                        return null;
719
448
                        OnFileUpdated(new ClassPadEventArgs (project));
720
449
                }
721
450
                
722
 
                private static string[] diff (string[] a1, string[] a2)
723
 
                {
724
 
                        List<string> res = new List<string> ();
725
 
                        List<string> right = new List<string> (a2);
726
 
                        
727
 
                        foreach (string s in a1) {
728
 
                                if (!right.Contains (s))
729
 
                                        res.Add (s);
730
 
                        }
731
 
                        
732
 
                        return res.ToArray ();
733
 
                }
734
 
                
735
451
                /// <summary>
736
452
                /// Wrapper method for the FileUpdated event.
737
453
                /// </summary>