├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── CompleteSharp.cs ├── CompleteSharp.sublime-settings ├── Default.sublime-keymap ├── README.creole ├── build.py ├── completesharp.py └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | release 3 | *.exe 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sublimecompletioncommon"] 2 | path = sublimecompletioncommon 3 | url = git://github.com/quarnster/SublimeCompletionCommon.git 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Before opening up a new issue 2 | ---------------------------------- 3 | 4 | * Use the search functionality to see if there's already an issue number 5 | dealing with the problem. If there is, please comment in the existing 6 | issue. 7 | * Make sure that the issue hasn't already been fixed in the master branch. 8 | I don't create a new release of the plugin for every single commit so 9 | it is possible that the issue has already been fixed. 10 | * Provide a small stand alone test case where the issue is reproducible. 11 | * Make sure that the issue is with CompleteSharp specifically and not 12 | something that's broken in Sublime Text 2. 13 | 14 | Please do remember that I am not your personal tech support and issues related 15 | to configuring the plugin as appropriate for your system and target platform 16 | have a high chance as being closed immediately. I am just a single person and 17 | the time I allocate to this project is limited. You'll have a better 18 | chance asking on the [Sublime Text 2 forums](http://www.sublimetext.com/forum/), 19 | [Stackoverflow](http://stackoverflow.com/) or possibly 20 | [Twitter](http://twitter.com/) to reach a much larger audience. 21 | -------------------------------------------------------------------------------- /CompleteSharp.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012 Fredrik Ehnbom 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | using System; 24 | using System.Reflection; 25 | using System.Collections; 26 | using System.Collections.Generic; 27 | using System.Text.RegularExpressions; 28 | using System.IO; 29 | 30 | public class CompleteSharp 31 | { 32 | private static string sep = ";;--;;"; 33 | 34 | protected class MyAppDomain : MarshalByRefObject 35 | { 36 | class Hack : MarshalByRefObject 37 | { 38 | public Hack() 39 | { 40 | AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; 41 | } 42 | 43 | private int GetParameterExtent(string parameter) 44 | { 45 | int hookcount = 0; 46 | int ltgtcount = 0; 47 | for (int i = 0; i < parameter.Length; i++) 48 | { 49 | switch (parameter[i]) 50 | { 51 | case ']': 52 | { 53 | hookcount--; 54 | if (hookcount == 0 && ltgtcount == 0) 55 | { 56 | return i+1; 57 | } 58 | break; 59 | } 60 | case '[': hookcount++; break; 61 | case ',': 62 | { 63 | if (parameter[i] == ',' && hookcount == 0 && ltgtcount == 0) 64 | { 65 | return i; 66 | } 67 | break; 68 | } 69 | case '<': ltgtcount++; break; 70 | case '>': 71 | { 72 | ltgtcount--; 73 | if (hookcount == 0 && ltgtcount == 0) 74 | { 75 | return i+1; 76 | } 77 | break; 78 | } 79 | } 80 | } 81 | return parameter.Length; 82 | } 83 | private string[] SplitParameters(string parameters, bool fix=true) 84 | { 85 | List s = new List(); 86 | for (int i = 0; i < parameters.Length;) 87 | { 88 | int len = GetParameterExtent(parameters.Substring(i)); 89 | string toadd = parameters.Substring(i, len); 90 | while (toadd.Length >= 2 && toadd.StartsWith("[")) 91 | { 92 | toadd = toadd.Substring(1, toadd.Length-2); 93 | toadd = toadd.Substring(0, GetParameterExtent(toadd)); 94 | } 95 | if (fix && toadd.Trim().Length > 0) 96 | toadd = FixName(toadd); 97 | toadd = toadd.Trim(); 98 | if (toadd.Length > 0) 99 | s.Add(toadd); 100 | i += len; 101 | if (len == 0) 102 | i++; 103 | } 104 | return s.ToArray(); 105 | } 106 | 107 | private string ParseParameters(string parameters, int expected, bool insertion) 108 | { 109 | if (parameters.Length >= 2 && parameters.StartsWith("[")) 110 | { 111 | parameters = parameters.Substring(1, parameters.Length-2); 112 | } 113 | string[] para = null; 114 | if (parameters.Length > 0) 115 | { 116 | para = SplitParameters(parameters); 117 | } 118 | else 119 | { 120 | para = new string[expected]; 121 | for (int i = 0; i < expected; i++) 122 | { 123 | para[i] = "T" + (i+1); 124 | } 125 | } 126 | string ret = ""; 127 | for (int i = 0; i < para.Length; i++) 128 | { 129 | if (ret.Length > 0) 130 | ret += ", "; 131 | if (!insertion) 132 | { 133 | ret+= para[i]; 134 | } 135 | else 136 | { 137 | ret += "${" + (i+1) + ":" + para[i] + "}"; 138 | } 139 | } 140 | return ret; 141 | } 142 | private string FixName(string str, bool insertion=false) 143 | { 144 | int index = str.IndexOf('`'); 145 | if (index != -1) 146 | { 147 | Regex regex = new Regex("^\\s*([^`]+)\\`(\\d+)([^\\[]+)?(\\[.*\\])?"); 148 | Match m = regex.Match(str.Replace("$", "\\$")); 149 | string type = m.Groups[1].ToString(); 150 | int num = System.Int32.Parse(m.Groups[2].ToString()); 151 | string subclass = m.Groups[3].ToString(); 152 | string parameters = m.Groups[4].ToString(); 153 | if (subclass.Length > 0) 154 | { 155 | subclass = "." + subclass.Substring(1); 156 | } 157 | string extra = ""; 158 | while (parameters.EndsWith("[]")) 159 | { 160 | extra += "[]"; 161 | parameters = parameters.Substring(0, parameters.Length-2); 162 | } 163 | 164 | 165 | str = type + "<" + ParseParameters(parameters, num, insertion) + ">" + subclass + extra; 166 | } 167 | return str; 168 | } 169 | 170 | private string[] GetTemplateArguments(string template) 171 | { 172 | int index = template.IndexOf('<'); 173 | int index2 = template.LastIndexOf('>'); 174 | if (index != -1 && index2 != -1) 175 | { 176 | string args = template.Substring(index+1, index2-index-1); 177 | return SplitParameters(args, false); 178 | } 179 | return new string[0]; 180 | } 181 | 182 | private string GetBase(string fullname) 183 | { 184 | int index = fullname.IndexOf('<'); 185 | if (index == -1) 186 | return fullname; 187 | return fullname.Substring(0, index); 188 | } 189 | 190 | protected Type GetType(MyAppDomain ad, string basename, string[] templateParam) 191 | { 192 | if (templateParam.Length > 0 && basename.IndexOf('`') == -1) 193 | { 194 | basename += "`" + templateParam.Length; 195 | } 196 | Type[] subtypes = new Type[templateParam.Length]; 197 | for (int i = 0; i < subtypes.Length; i++) 198 | { 199 | string bn = GetBase(templateParam[i]); 200 | string[] args = GetTemplateArguments(templateParam[i]); 201 | subtypes[i] = GetType(ad, bn, args); 202 | } 203 | 204 | Type t = GetType(basename); 205 | 206 | if (t != null && subtypes.Length > 0) 207 | { 208 | try 209 | { 210 | Type t2 = t.MakeGenericType(subtypes); 211 | System.Console.Error.WriteLine("returning type2: " + t2.FullName); 212 | return t2; 213 | } 214 | catch (Exception e) 215 | { 216 | reportError(e); 217 | } 218 | } 219 | if (t != null) 220 | System.Console.Error.WriteLine("returning type: " + t.FullName); 221 | return t; 222 | } 223 | private enum Accessibility 224 | { 225 | NONE = (0<<0), 226 | STATIC = (1<<0), 227 | PRIVATE = (1<<1), 228 | PROTECTED = (1<<2), 229 | PUBLIC = (1<<3), 230 | INTERNAL = (1<<4) 231 | }; 232 | 233 | private int GetModifiers(MemberInfo m) 234 | { 235 | Accessibility modifiers = Accessibility.NONE; 236 | switch (m.MemberType) 237 | { 238 | case MemberTypes.Field: 239 | { 240 | FieldInfo f = (FieldInfo)m; 241 | if (f.IsPrivate) 242 | modifiers |= Accessibility.PRIVATE; 243 | if (f.IsPublic) 244 | modifiers |= Accessibility.PUBLIC; 245 | if (f.IsStatic) 246 | modifiers |= Accessibility.STATIC; 247 | if (!f.IsPublic && !f.IsPrivate) 248 | modifiers |= Accessibility.PROTECTED; 249 | break; 250 | } 251 | case MemberTypes.Method: 252 | { 253 | MethodInfo mi = (MethodInfo)m; 254 | if (mi.IsPrivate) 255 | modifiers |= Accessibility.PRIVATE; 256 | if (mi.IsPublic) 257 | modifiers |= Accessibility.PUBLIC; 258 | if (mi.IsStatic) 259 | modifiers |= Accessibility.STATIC; 260 | if (!mi.IsPublic && !mi.IsPrivate) 261 | modifiers |= Accessibility.PROTECTED; 262 | break; 263 | } 264 | case MemberTypes.Property: 265 | { 266 | PropertyInfo p = (PropertyInfo)m; 267 | foreach (MethodInfo mi in p.GetAccessors()) 268 | { 269 | modifiers |= (Accessibility)GetModifiers(mi); 270 | } 271 | break; 272 | } 273 | default: 274 | { 275 | modifiers = Accessibility.STATIC|Accessibility.PUBLIC; 276 | break; 277 | } 278 | } 279 | return (int) modifiers; 280 | } 281 | 282 | public MyAppDomain ad; 283 | 284 | 285 | Assembly TryLoad(string path, string name) 286 | { 287 | path = path.Replace("file://", ""); 288 | int idx1 = path.LastIndexOf('/'); 289 | int idx2 = path.LastIndexOf('\\'); 290 | int idx = Math.Max(idx1, idx2); 291 | if (idx != -1) 292 | { 293 | path = path.Substring(0, idx); 294 | } 295 | path = path + "/" + name + ".dll"; 296 | FileInfo fi = new FileInfo(path); 297 | if (fi.Exists) 298 | { 299 | return Load(path); 300 | } 301 | return null; 302 | } 303 | private Assembly ResolveAssembly(object sender, ResolveEventArgs args) 304 | { 305 | string name = args.Name.Substring(0, args.Name.IndexOf(",")); 306 | foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) 307 | { 308 | if (asm.FullName.Substring(0, asm.FullName.IndexOf(',')) == name) 309 | { 310 | return asm; 311 | } 312 | } 313 | // Assembly wasn't found, try and see if it's in the same directory 314 | // as one of the other already loaded assemblies. 315 | if (ad != null) 316 | { 317 | foreach (string asm in ad.assemblies) 318 | { 319 | Assembly ret = TryLoad(asm, name); 320 | if (ret != null) 321 | return ret; 322 | } 323 | } 324 | 325 | foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) 326 | { 327 | Assembly ret = TryLoad(asm.CodeBase, name); 328 | if (ret != null) 329 | return ret; 330 | } 331 | System.Console.Error.WriteLine("Couldn't find assembly: " + name); 332 | return null; 333 | } 334 | public Assembly Load(String str) 335 | { 336 | return Assembly.Load(File.ReadAllBytes(str)); 337 | } 338 | public Type GetType(string basename) 339 | { 340 | Type t = null; 341 | foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies()) 342 | { 343 | t = ass.GetType(basename); 344 | if (t != null) 345 | break; 346 | } 347 | return t; 348 | } 349 | public bool Execute(string[] args, ArrayList modules) 350 | { 351 | try 352 | { 353 | foreach (string s in args) 354 | { 355 | System.Console.Error.WriteLine("execute args: " + s); 356 | } 357 | Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); 358 | if (args[0] == "-quit") 359 | { 360 | return false; 361 | } 362 | else if (args[0] == "-findclass") 363 | { 364 | bool found = false; 365 | foreach (String mod in modules) 366 | { 367 | try 368 | { 369 | string fullname = args[1]; 370 | if (mod.Length > 0) 371 | { 372 | fullname = mod + "." + fullname; 373 | } 374 | System.Console.Error.WriteLine("Trying " + fullname); 375 | Type t2 = GetType(fullname); 376 | if (t2 != null) 377 | { 378 | string typename = t2.FullName; 379 | System.Console.WriteLine(typename); 380 | System.Console.Error.WriteLine(typename); 381 | found = true; 382 | break; 383 | } 384 | } 385 | catch (Exception e) 386 | { 387 | reportError(e); 388 | } 389 | } 390 | if (found) 391 | return true; 392 | // Probably a namespace then? 393 | 394 | foreach (Assembly asm in assemblies) 395 | { 396 | try 397 | { 398 | foreach (Type t3 in asm.GetTypes()) 399 | { 400 | if (t3.Namespace == null) 401 | continue; 402 | foreach (string mod in modules) 403 | { 404 | string test = args[1]; 405 | if (mod.Length > 0) 406 | test = mod + "." + test; 407 | if (t3.Namespace.StartsWith(test)) 408 | { 409 | System.Console.WriteLine(test); 410 | System.Console.Error.WriteLine(test); 411 | found = true; 412 | break; 413 | } 414 | } 415 | if (found) 416 | break; 417 | } 418 | if (found) 419 | break; 420 | } 421 | catch (Exception) 422 | { 423 | } 424 | } 425 | return true; 426 | } 427 | if (args.Length < 2) 428 | return true; 429 | int len = args.Length - 3; 430 | if (len < 0) 431 | len = 0; 432 | string[] templateParam = new string[len]; 433 | for (int i = 0; i < len; i++) 434 | { 435 | templateParam[i] = args[i+3]; 436 | } 437 | Type t = null; 438 | try 439 | { 440 | t = GetType(ad, args[1], templateParam); 441 | } 442 | catch (Exception e) 443 | { 444 | reportError(e); 445 | } 446 | 447 | if (args[0] == "-complete") 448 | { 449 | if (t != null) 450 | { 451 | foreach (MemberInfo m in t.GetMembers(BindingFlags.Instance|BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.FlattenHierarchy)) 452 | { 453 | switch (m.MemberType) 454 | { 455 | case MemberTypes.Event: 456 | case MemberTypes.Field: 457 | case MemberTypes.Method: 458 | case MemberTypes.Property: 459 | { 460 | string completion = null; 461 | try 462 | { 463 | completion = m.ToString(); 464 | } 465 | catch (Exception e) 466 | { 467 | System.Console.Error.WriteLine("Skipping this member as an exception was thrown when calling ToString:"); 468 | System.Console.Error.WriteLine(e.Message); 469 | // Seems that some state is messed up when this happens, so we need to 470 | // force a reload of the assemblies to make it work again. 471 | ad.forceReload = true; 472 | continue; 473 | } 474 | int index = completion.IndexOf(' '); 475 | string returnType = completion.Substring(0, index); 476 | completion = completion.Substring(index+1); 477 | 478 | string display = ""; 479 | index = completion.IndexOf('('); 480 | int index2 = completion.LastIndexOf(')'); 481 | if (index != -1 && index2 != -1) 482 | { 483 | string param = completion.Substring(index+1, index2-index-1); 484 | completion = completion.Substring(0, index+1); 485 | display = completion; 486 | string[] par = param.Split(new Char[]{','}); 487 | int addIndex = 1; 488 | index2 = completion[index-1] == ']' ? index-1 : -1; 489 | display = completion; 490 | bool first = true; 491 | string[] generics = null; 492 | if (index2 != -1) 493 | { 494 | index = completion.IndexOf('[')+1; 495 | // Generic method 496 | string generic = completion.Substring(index, index2-index); 497 | completion = completion.Substring(0, index-1) + "<"; 498 | display = completion; 499 | generics = generic.Split(new char[]{','}); 500 | foreach (string g in generics) 501 | { 502 | if (!first) 503 | { 504 | completion += ", "; 505 | display += ", "; 506 | } 507 | display += g; 508 | completion += "${" + addIndex + ":" + g + "}"; 509 | addIndex++; 510 | first = false; 511 | } 512 | display += ">("; 513 | completion += ">("; 514 | } 515 | first = true; 516 | foreach (string p in par) 517 | { 518 | string toadd = FixName(p.Trim()); 519 | if (toadd.Length > 0) 520 | { 521 | if (!first) 522 | { 523 | completion += ", "; 524 | display += ", "; 525 | } 526 | display += toadd; 527 | completion += "${" + addIndex + ":" + toadd + "}"; 528 | addIndex++; 529 | first = false; 530 | } 531 | } 532 | completion += ")"; 533 | display += ")"; 534 | } 535 | else 536 | { 537 | display = completion; 538 | } 539 | string insertion = completion; 540 | display += "\t" + FixName(returnType); 541 | 542 | System.Console.WriteLine(display + sep + insertion + sep + GetModifiers(m)); 543 | break; 544 | } 545 | } 546 | } 547 | } 548 | else 549 | { 550 | foreach (Assembly asm in assemblies) 551 | { 552 | try 553 | { 554 | foreach (Type t3 in asm.GetTypes()) 555 | { 556 | if (t3.Namespace == null) 557 | continue; 558 | if (t3.Namespace == args[1]) 559 | { 560 | System.Console.WriteLine(FixName(t3.Name) + "\tclass" + sep + FixName(t3.Name, true)); 561 | } 562 | else if (t3.Namespace != args[1] && t3.Namespace.StartsWith(args[1])) 563 | { 564 | string name = t3.Namespace.Substring(args[1].Length+1); 565 | int index = name.IndexOf('.'); 566 | if (index != -1) 567 | name = name.Substring(0, index); 568 | 569 | System.Console.WriteLine(name + "\tnamespace" + sep + name); 570 | } 571 | } 572 | } 573 | catch (Exception) 574 | { 575 | } 576 | } 577 | } 578 | } 579 | else if (args[0] == "-returntype") 580 | { 581 | if (t != null) 582 | { 583 | bool found = false; 584 | BindingFlags flags = BindingFlags.Instance|BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.FlattenHierarchy; 585 | string funcname = args[2]; 586 | Type[] generics = null; 587 | int index = funcname.IndexOf('<'); 588 | int index2 = funcname.LastIndexOf('>'); 589 | if (index != -1 && index2 != -1) 590 | { 591 | string[] generics2 = funcname.Substring(index+1, index2-index-1).Split(new Char[]{','}); 592 | funcname = funcname.Substring(0, index); 593 | generics = new Type[generics2.Length]; 594 | for (int i = 0; i < generics2.Length; i++) 595 | { 596 | generics[i] = GetType(generics2[i]); 597 | if (generics[i] == null) 598 | { 599 | generics = null; 600 | break; 601 | } 602 | } 603 | } 604 | // This isn't 100% correct, but an instance where two things 605 | // are named the same but return two different types would 606 | // be considered rare. 607 | foreach (MethodInfo m in t.GetMethods(flags)) 608 | { 609 | if (m.Name == funcname) 610 | { 611 | MethodInfo m2 = m; 612 | if (generics != null) 613 | { 614 | m2 = m2.MakeGenericMethod(generics); 615 | } 616 | System.Console.WriteLine(FixName(m2.ReturnType.FullName)); 617 | found = true; 618 | break; 619 | } 620 | } 621 | if (found) 622 | return true; 623 | foreach (FieldInfo f in t.GetFields(flags)) 624 | { 625 | if (f.Name == args[2]) 626 | { 627 | System.Console.WriteLine(FixName(f.FieldType.FullName)); 628 | found = true; 629 | break; 630 | } 631 | } 632 | if (found) 633 | return true; 634 | foreach (EventInfo e in t.GetEvents(flags)) 635 | { 636 | if (e.Name == args[2]) 637 | { 638 | System.Console.WriteLine(FixName(e.EventHandlerType.FullName)); 639 | found = true; 640 | break; 641 | } 642 | } 643 | if (found) 644 | return true; 645 | foreach (PropertyInfo p in t.GetProperties(flags)) 646 | { 647 | if (p.Name == args[2]) 648 | { 649 | System.Console.WriteLine(FixName(p.PropertyType.FullName)); 650 | found = true; 651 | break; 652 | } 653 | } 654 | } 655 | else 656 | { 657 | bool found = false; 658 | foreach (Assembly asm in assemblies) 659 | { 660 | try 661 | { 662 | foreach (Type t3 in asm.GetTypes()) 663 | { 664 | if (t3.Namespace == null) 665 | continue; 666 | if (t3.Namespace == args[1] && t3.Name == args[2]) 667 | { 668 | System.Console.WriteLine(FixName(t3.FullName)); 669 | found = true; 670 | break; 671 | } 672 | } 673 | if (found) 674 | break; 675 | } 676 | catch (Exception) 677 | { 678 | } 679 | } 680 | if (!found) 681 | { 682 | // It's a namespace we are completing. 683 | string ns = args[1] + "." + args[2]; 684 | foreach (Assembly asm in assemblies) 685 | { 686 | try 687 | { 688 | foreach (Type t3 in asm.GetTypes()) 689 | { 690 | if (t3.Namespace == null) 691 | continue; 692 | if (t3.Namespace.StartsWith(ns)) 693 | { 694 | System.Console.WriteLine(ns); 695 | found = true; 696 | break; 697 | } 698 | } 699 | if (found) 700 | break; 701 | } 702 | catch (Exception) 703 | {} 704 | } 705 | } 706 | } 707 | } 708 | } 709 | catch (Exception e) 710 | { 711 | reportError(e); 712 | } 713 | return true; 714 | } 715 | }; 716 | private AppDomain ad = null; 717 | private string[] assemblies; 718 | private DateTime[] times = null; 719 | public MyAppDomain(string[] arg) 720 | { 721 | assemblies = arg; 722 | times = new DateTime[arg.Length]; 723 | LoadAssemblies(); 724 | } 725 | 726 | public void LoadAssemblies() 727 | { 728 | if (ad != null) 729 | { 730 | System.Console.Error.WriteLine("Unloading domain"); 731 | AppDomain.Unload(ad); 732 | } 733 | 734 | ad = AppDomain.CreateDomain("MyAppDomain"); 735 | object o = ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "CompleteSharp+MyAppDomain+Hack"); 736 | Hack h = o as Hack; 737 | h.ad = this; 738 | int idx = 0; 739 | 740 | foreach (string a in assemblies) 741 | { 742 | try 743 | { 744 | FileInfo fi = new FileInfo(a); 745 | times[idx] = fi.LastWriteTime; 746 | idx++; 747 | 748 | System.Console.Error.WriteLine("Loading: " + a); 749 | h.Load(a); 750 | } 751 | catch (Exception e) 752 | { 753 | reportError(e); 754 | } 755 | } 756 | } 757 | 758 | public bool Execute(string[] args, ArrayList modules) 759 | { 760 | CheckUpdate(); 761 | object o = ad.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "CompleteSharp+MyAppDomain+Hack"); 762 | Hack h = o as Hack; 763 | h.ad = this; 764 | return h.Execute(args, modules); 765 | } 766 | 767 | bool forceReload = false; 768 | 769 | private void CheckUpdate() 770 | { 771 | // Yes, polling like this is a bit messy, however FileSystemWatcher didn't 772 | // work for me on OS X, there probably aren't that many assemblies to check 773 | // and the polling is only done after the user requests a completion, so 774 | // it shouldn't be too bad 775 | bool reload = forceReload; 776 | for (int i = 0; i < times.Length; i++) 777 | { 778 | try 779 | { 780 | FileInfo fi = new FileInfo(assemblies[i]); 781 | if (fi.LastWriteTime > times[i]) 782 | { 783 | System.Console.Error.WriteLine("changed: " + assemblies[i] + ", " + fi.LastWriteTime + ", " + times[i]); 784 | reload = true; 785 | break; 786 | } 787 | } 788 | catch (Exception e) 789 | { 790 | reportError(e); 791 | } 792 | } 793 | if (reload) 794 | { 795 | forceReload = false; 796 | LoadAssemblies(); 797 | } 798 | } 799 | } 800 | 801 | 802 | static void reportError(Exception e) 803 | { 804 | System.Console.Error.WriteLine("Exception: " + e.Message); 805 | System.Console.Error.WriteLine(e.StackTrace); 806 | System.Console.Error.WriteLine(sep); 807 | } 808 | 809 | public static void Main(string[] arg) 810 | { 811 | try 812 | { 813 | bool first = true; 814 | 815 | string[] argv = null; 816 | if (arg.Length > 0) 817 | { 818 | argv = arg[0].Split(new string[] {sep}, StringSplitOptions.None); 819 | } 820 | else 821 | { 822 | argv = new string[0]; 823 | } 824 | MyAppDomain ad = new MyAppDomain(argv); 825 | 826 | while (true) 827 | { 828 | try 829 | { 830 | if (!first) 831 | // Just to indicate that there's no more output from the command and we're ready for new input 832 | System.Console.WriteLine(sep); 833 | first = false; 834 | string command = System.Console.ReadLine(); 835 | System.Console.Error.WriteLine("got: " + command); 836 | if (command == null) 837 | break; 838 | string[] args = Regex.Split(command, sep); 839 | ArrayList modules = null; 840 | if (args[0] == "-findclass") 841 | { 842 | modules = new ArrayList(); 843 | string line = null; 844 | try 845 | { 846 | while ((line = System.Console.ReadLine()) != null) 847 | { 848 | if (line == sep) 849 | break; 850 | modules.Add(line); 851 | } 852 | } 853 | catch (Exception) 854 | {} 855 | } 856 | 857 | if (!ad.Execute(args, modules)) 858 | { 859 | break; 860 | } 861 | } 862 | catch (Exception e) 863 | { 864 | reportError(e); 865 | } 866 | } 867 | } 868 | catch (Exception e) 869 | { 870 | reportError(e); 871 | } 872 | } 873 | } 874 | 875 | -------------------------------------------------------------------------------- /CompleteSharp.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // You probably want to configure this to something of your own. 3 | // ${home}, ${env:}, ${project_path:} and ${folder:} tokens can be used in the completesharp_assemblies option. 4 | // 5 | // ${home} is replaced with the value of the HOME environment variable. 6 | // 7 | // ${env:} is replaced with the "variable" environment variable. 8 | // 9 | // ${project_path:} tries to find a file with the given name in all the registered project folders and 10 | // returns the first file found, or the original file name if none is found. 11 | // Example: ${project_path:main.cpp} tries to find a file named "main.cpp" relative 12 | // to the current project's folders. If none is found, it is replaced with "main.cpp". 13 | // 14 | // ${folder:} is replaced with the dirname of the given path. 15 | // Example: ${folder:/path/to/file} is replaced with "/path/to". 16 | // 17 | // Replacement is done sequentially, first all ${project_path:} are resolved, then ${home} and 18 | // ${env:} tokens, and then the ${folder:} are replaced, 19 | "completesharp_assemblies": [], 20 | 21 | // Regular expression filter used to filter the completion 22 | // suggestions 23 | "completesharp_filterregex": "^(get_|set_|op_|add_|remove_|<)", 24 | 25 | // When set to true will inhibit the completions suggested 26 | // by default by Sublime Text 2 if CompleteSharp can't 27 | // find anything to complete 28 | "completioncommon_inhibit_sublime_completions": true, 29 | 30 | // When set to true will shorten class names in the completion list 31 | // by removing the package name. 32 | "completioncommon_shorten_names": true 33 | } 34 | -------------------------------------------------------------------------------- /Default.sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["."], "command": "complete_sharp_dot_complete", 4 | "context": 5 | [ 6 | {"key": "completesharp.supported_language"}, 7 | {"key": "completesharp.dotcomplete"}, 8 | {"key": "completion_common.is_code"} 9 | ] 10 | } 11 | ] 12 | -------------------------------------------------------------------------------- /README.creole: -------------------------------------------------------------------------------- 1 | = Plugin discontinued = 2 | ** I don't intend to continue development of this plugin, so I've disabled the issues page. If something is broken, submit a pull request and I'll consider merging it. 3 | 4 | ** Eventually it will be replaced by https://github.com/quarnster/completion. If you'd like to see that move along quicker, submit a pull request and/or participate in its discussions. 5 | 6 | === Description === 7 | A plugin that provides C# code completion inside of Sublime Text 2. 8 | 9 | === Known issues and feature requests === 10 | Please go [[https://github.com/quarnster/CompleteSharp/issues?sort=created&direction=desc&state=open|here]] to see the currently known issues and feature request, or to file a new. 11 | 12 | === Prerequisites === 13 | # On Windows you need to have the .NET framework installed 14 | # On OS X/Linux you need to have mono installed and the mono binary in your path 15 | 16 | === Installation === 17 | * The easiest way to install CompleteSharp is via the excellent Package Control Plugin 18 | ** See http://wbond.net/sublime_packages/package_control#Installation 19 | *** Once package control has been installed, bring up the command palette (cmd+shift+P or ctrl+shift+P) 20 | *** Type Install and select "Package Control: Install Package" 21 | *** Select CompleteSharp from the list. Package Control will keep it automatically updated for you 22 | * If you want to manually install it using git instead: 23 | ** Go to your packages directory and type: 24 | *** git clone --recursive https://github.com/quarnster/CompleteSharp 25 | ** To update to the latest commit, use this command in the CompleteSharp directory: 26 | *** git pull && git submodule foreach --recursive git pull origin master 27 | * Once installed, you probably want to tweak the "completesharp_assemblies" setting, specifying the full path to extra assemblies to load 28 | 29 | === Usage === 30 | Once installed it'll try to provide reasonable completion suggestions when you trigger autocompletion. 31 | 32 | === License === 33 | This plugin is using the zlib license 34 | 35 | {{{ 36 | Copyright (c) 2012 Fredrik Ehnbom 37 | 38 | This software is provided 'as-is', without any express or implied 39 | warranty. In no event will the authors be held liable for any damages 40 | arising from the use of this software. 41 | 42 | Permission is granted to anyone to use this software for any purpose, 43 | including commercial applications, and to alter it and redistribute it 44 | freely, subject to the following restrictions: 45 | 46 | 1. The origin of this software must not be misrepresented; you must not 47 | claim that you wrote the original software. If you use this software 48 | in a product, an acknowledgment in the product documentation would be 49 | appreciated but is not required. 50 | 51 | 2. Altered source versions must be plainly marked as such, and must not be 52 | misrepresented as being the original software. 53 | 54 | 3. This notice may not be removed or altered from any source 55 | distribution. 56 | }}} 57 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import sys 4 | import subprocess 5 | import tempfile 6 | 7 | if __name__ == "__main__": 8 | def get(url): 9 | proc = subprocess.Popen("curl -s %s" % url, shell=True, stdout=subprocess.PIPE) 10 | stdout, stderr = proc.communicate() 11 | return stdout 12 | 13 | def run(cmd): 14 | assert(os.system(cmd) == 0) 15 | 16 | version = json.load(open('package.json'))['packages'][0]['platforms']['*'][0]['version'] 17 | run("mcs -platform:x86 CompleteSharp.cs") 18 | 19 | package_name = "CompleteSharp-%s.sublime-package" % version 20 | 21 | 22 | for arg in sys.argv[1:]: 23 | if arg == "--create": 24 | run("rm -rf release") 25 | run("mkdir release") 26 | run("cp -r sublimecompletioncommon release") 27 | run("find . -maxdepth 1 -type f -exec cp {} release \;") 28 | run("find release -name \".git*\" | xargs rm -rf") 29 | run("find release -name \"*.pyc\" -exec rm {} \;") 30 | run("find release -name \"unittest*\" -exec rm -f {} \;") 31 | run("rm -f release/build.py") 32 | run("cd release && zip -r %s *" % package_name) 33 | elif arg == "--upload": 34 | current_downloads = json.loads(get("https://api.github.com/repos/quarnster/CompleteSharp/downloads")) 35 | for download in current_downloads: 36 | assert download['name'] != package_name 37 | f = tempfile.NamedTemporaryFile() 38 | f.write("""{ "name": "%s", "size": %s}""" % (package_name, os.path.getsize("release/%s" % package_name))) 39 | f.flush() 40 | response = json.loads(get("-X POST -d @%s -u quarnster https://api.github.com/repos/quarnster/CompleteSharp/downloads" % f.name)) 41 | f.close() 42 | args = """\ 43 | -F "key=%s" \ 44 | -F "acl=%s" \ 45 | -F "success_action_status=201" \ 46 | -F "Filename=%s" \ 47 | -F "AWSAccessKeyId=%s" \ 48 | -F "Policy=%s" \ 49 | -F "Signature=%s" \ 50 | -F "Content-Type=%s" \ 51 | -F "file=@release/%s" \ 52 | https://github.s3.amazonaws.com/""" % (response["path"], response["acl"], response["name"], response["accesskeyid"], response["policy"], response["signature"], response["mime_type"], package_name) 53 | print(get(args)) 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /completesharp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2012 Fredrik Ehnbom 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | """ 23 | import sublime_plugin 24 | import sublime 25 | import os.path 26 | import re 27 | import glob 28 | import imp 29 | import sys 30 | 31 | completioncommon = imp.load_source("completioncommon", os.path.join(os.path.dirname(os.path.abspath(__file__)), "sublimecompletioncommon/completioncommon.py")) 32 | completioncommon.reload(completioncommon) 33 | 34 | 35 | class CompleteSharpDotComplete(completioncommon.CompletionCommonDotComplete): 36 | pass 37 | 38 | 39 | class CompleteSharpCompletion(completioncommon.CompletionCommon): 40 | def __init__(self): 41 | super(CompleteSharpCompletion, self).__init__("CompleteSharp.sublime-settings", os.path.dirname(os.path.abspath(__file__))) 42 | self.replacements = {"int": "System.Int32", "string": "System.String", "char": "System.Char", "void": "System.Void", "long": "System.Int64", "short": "System.Int16"} 43 | 44 | def find_absolute_of_type(self, data, full_data, type, template_args=[]): 45 | idx1 = type.find("+") 46 | idx2 = type.find("`") 47 | extra = "" 48 | if idx2 != -1 and idx1 != -1 and idx1 < idx2: 49 | end = type[idx2:] 50 | extra = type[idx1:idx2] 51 | type = "%s%s%s" % (type[:idx1], end, extra) 52 | if template_args != None and len(template_args): 53 | type += "`%d" % len(template_args) 54 | if type in self.replacements: 55 | type = self.replacements[type] 56 | return super(CompleteSharpCompletion, self).find_absolute_of_type(data, full_data, type) 57 | 58 | def error_thread(self): 59 | try: 60 | errors = "The following assemblies could not be loaded, please consider manually adding them to your completesharp_assemblies list:\n\n" 61 | while True: 62 | if self.completion_proc.poll() != None: 63 | break 64 | line = self.completion_proc.stderr.readline().decode(sys.getdefaultencoding()).strip() 65 | if line.startswith("Couldn't find assembly: "): 66 | asm = line[line.find(":")+2:] 67 | if not asm in errors: 68 | errors += asm + "\n" 69 | sublime.set_timeout(lambda: sublime.error_message(errors), 0) 70 | print("stderr: %s" % line) 71 | finally: 72 | pass 73 | 74 | def get_packages(self, data, thispackage, type): 75 | packages = re.findall("[ \t]*using[ \t]+(.*);", data) 76 | packages.append("System") 77 | packages.append("") 78 | return packages 79 | 80 | def filter(self, typename, var, isstatic, data, indata): 81 | filterregex = self.get_setting("completesharp_filterregex", r"^(get_|set_|op_|add_|remove_|<)") 82 | try: 83 | filterregex = re.compile(filterregex) 84 | except: 85 | sublime.error_message("Invalid filter regular expression. Please modify your completesharp_filterregex setting") 86 | filterregex = None 87 | ret = [] 88 | for d in indata: 89 | # get_ and set_ are mostly associated with properties 90 | if filterregex and filterregex.search(d[0]) != None: 91 | continue 92 | elif len(d) == 3 and self.is_static(d[2]) and var != None: 93 | continue 94 | ret.append(d) 95 | return super(CompleteSharpCompletion, self).filter(typename, var, isstatic, data, ret) 96 | 97 | def get_cmd(self): 98 | extra = self.get_setting("completesharp_assemblies", []) 99 | newextra = [] 100 | window = sublime.active_window() 101 | for path in extra: 102 | newextra.extend(glob.glob(self.expand_path(path, window, False))) 103 | extra = newextra 104 | cmd = "" 105 | q = "\"" 106 | if sublime.platform() != "windows": 107 | cmd = self.get_setting("completesharp_mono_path", "mono") + " " 108 | q = "\'" 109 | cmd = "%sCompleteSharp.exe %s%s%s" % (cmd, q, ";;--;;".join(extra), q) 110 | return cmd 111 | 112 | def is_supported_language(self, view): 113 | if view.is_scratch(): 114 | return False 115 | language = self.get_language(view) 116 | return language == "cs" 117 | 118 | comp = CompleteSharpCompletion() 119 | 120 | 121 | class CompleteSharp(sublime_plugin.EventListener): 122 | 123 | def on_query_completions(self, view, prefix, locations): 124 | ret = comp.on_query_completions(view, prefix, locations) 125 | return ret 126 | 127 | def on_query_context(self, view, key, operator, operand, match_all): 128 | if key == "completesharp.dotcomplete": 129 | return comp.get_setting(key.replace(".", "_"), True) 130 | elif key == "completesharp.supported_language": 131 | return comp.is_supported_language(view) 132 | else: 133 | return comp.on_query_context(view, key, operator, operand, match_all) 134 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema_version": "1.1", 3 | "packages": [ 4 | { 5 | "name": "CompleteSharp", 6 | "description": "C# completions for Sublime Text 2", 7 | "author": "Fredrik Ehnbom (quarnster)", 8 | "homepage": "http://github.com/quarnster/CompleteSharp", 9 | "last_modified": "2013-02-15 09:10:00", 10 | "platforms": { 11 | "*": [ 12 | { 13 | "version": "1.0.19", 14 | "url": "http://cloud.github.com/downloads/quarnster/CompleteSharp/CompleteSharp-1.0.19.sublime-package" 15 | } 16 | ] 17 | } 18 | } 19 | ] 20 | } 21 | --------------------------------------------------------------------------------