[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/csharp-mode 3414c63 006/459: First check in of Cscomp -
From: |
ELPA Syncer |
Subject: |
[elpa] externals/csharp-mode 3414c63 006/459: First check in of Cscomp - C# code completion. |
Date: |
Sun, 22 Aug 2021 13:58:43 -0400 (EDT) |
branch: externals/csharp-mode
commit 3414c635896d0dd5243b0d9f9e4e4192c40aa3b2
Author: Dino Chiesa <dpchiesa@hotmail.com>
Commit: Dino Chiesa <dpchiesa@hotmail.com>
First check in of Cscomp - C# code completion.
---
CscompUtilities.cs | 1658 ++++++++++++++++++++++++++++++
Readme.txt | 69 ++
csharp-completion.el | 2792 ++++++++++++++++++++++++++++++++++++++++++++++++++
csharp-mode.el | 69 +-
csharp-shell.el | 363 +++++++
makefile | 28 +
6 files changed, 4947 insertions(+), 32 deletions(-)
diff --git a/CscompUtilities.cs b/CscompUtilities.cs
new file mode 100644
index 0000000..0b176f0
--- /dev/null
+++ b/CscompUtilities.cs
@@ -0,0 +1,1658 @@
+//#define CscompTrace
+
+// CscompUtilities.cs
+// ------------------------------------------------------------------
+//
+// Author: Dinoch
+// built on host: DINOCH-2
+// Created Mon Apr 21 08:40:47 2008
+//
+// Last Saved: <2010-May-24 17:17:19>
+//
+//
+// This thing defines an assembly containing one main class, a static
+// class that exposes only static methods. The assembly is intended to
+// run within Powershell, in an emacs inferior shell.
+//
+// Using csde-complete, when the user asks for code-completion on a
+// segment of code, csde-complete will invoke a command in the
+// powershell - which really is just invoking a method on this static
+// class.
+//
+// The logic in this assembly will then perform whatever is necessary:
+// reflect on the specified type, or qualify a name, and so on, and then
+// return the result information to the CSDE elisp logic.
+//
+// In broad strokes, you can think of this assembly as the thing that
+// performs .NET reflection, and sends the list of potential completions
+// to elisp, which presents a pop-up menu. There are a bunch of
+// supplementary tasks required, in order to make the "return the list
+// of potential completions" possible: for example, is the completion
+// being requested on a type? A namespace? is it a static method? A
+// property? and so on. All of these extra supporting functions are also
+// implemented as static methods on the main Utilities class.
+//
+// =======================================================
+//
+// compile with:
+// csc.exe /target:library /debug /out:CscompUtilities.dll
CscompUtilities.cs
+//
+// ------------------------------------------------------------------
+//
+// Copyright (c) 2008-2010 by Dino Chiesa
+// All rights reserved!
+//
+// ------------------------------------------------------------------
+
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Reflection;
+
+
+// to allow fast ngen
+[assembly: AssemblyTitle("CscompUtilities.cs")]
+[assembly: AssemblyDescription("an assembly to be loaded into powershell,
allows integration with emacs, code completion, etc.")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Dino Chiesa")]
+[assembly: AssemblyProduct("Tools")]
+[assembly: AssemblyCopyright("Copyright � Dino Chiesa 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: AssemblyVersion("1.2.0.4")]
+
+
+
+namespace Ionic.Cscomp
+{
+ public static class Utilities
+ {
+ private static readonly string DotNetDir=
"c:\\windows\\Microsoft.NET\\Framework\\v2.0.50727";
+
+ private static string[] BaseAssembliesToLoad = {
+ // must be strong names
+ "System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089",
+ "mscorlib, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089",
+ };
+
+
+ private static Dictionary<string,string> GacAssemblies =
+ new Dictionary<string,string>()
+ {
+ {"System.Xml","Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" },
+ {"System.Xml.Linq", "Version=3.5.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" },
+ {"System.Data", "Version=3.5.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" },
+ };
+
+
+// "Microsoft.JScript",
+// "Microsoft.VisualBasic",
+// "Microsoft.VisualBasic.Vsa",
+// "Microsoft.VisualC",
+// "Microsoft.Vsa",
+// "Microsoft.Vsa.Vb.CodeDOMPRocessor",
+// "System.Configuration.Install",
+// "System.Data",
+// "System.Design",
+// "System.DirectoryServices",
+// "System.Drawing",
+// "System.Drawing.Design",
+// "System.EnterpriseServices",
+// "System.Management",
+// "System.Messaging",
+// "System.Runtime.Remoting",
+// "System.Runtime.Serialization.Formatters.Soap",
+// "System.Security",
+// "System.ServiceProcess",
+// "System.Web",
+// "System.Web.RegularExpressions",
+// "System.Web.Services",
+// "System.Windows.Forms",
+
+ private static Dictionary<String,Object> _assembliesNotLoaded;
+ private static Dictionary<String,Assembly> _assembliesLoaded;
+ private static Dictionary<String,String> _assemblyForType;
+ private static Dictionary<String,List<String>> _typesForNamespace;
+ private static Dictionary<String,String> _fullNamesForShortNames;
+
+ static Utilities()
+ {
+ Tracing.SetupDebugConsole();
+ LoadAssembliesAndPopulateHashes();
+ }
+
+
+ private static Assembly AssemblyIsLoaded(string assemblyName)
+ {
+ // exact match
+ if (_assembliesLoaded.Keys.Contains(assemblyName))
+ return _assembliesLoaded[assemblyName];
+
+ // check for short name
+ if (!assemblyName.Contains(","))
+ {
+ foreach (var key in _assembliesLoaded.Keys)
+ {
+ int ix = key.LastIndexOf(',');
+ if (ix > 0)
+ {
+ var stub = key.Substring(0, ix);
+ if (assemblyName == stub)
+ return _assembliesLoaded[key];
+ }
+ }
+ }
+ return null;
+ }
+
+
+
+ private static String InternalLoadOneAssembly(string assemblyName)
+ {
+ // check if already loaeded
+ if (AssemblyIsLoaded(assemblyName)!=null)
+ return "t";
+
+ // doesn't appear to be loaded. Try to load it.
+
+ Assembly thisAssembly = null;
+ try
+ {
+ thisAssembly = TryLoadAssembly(assemblyName);
+
+ }
+ catch(Exception exc1)
+ {
+ _assembliesNotLoaded[assemblyName] = exc1;
+ return null;
+ }
+
+ if (thisAssembly == null)
+ {
+ _assembliesNotLoaded[assemblyName] = "Assembly was null.";
+ return null;
+ }
+
+ _assembliesLoaded.Add(assemblyName, thisAssembly);
+
+ Module[] ma = thisAssembly.GetModules();
+ if (ma != null)
+ {
+ List<String> list;
+
+ for (int k = 0; k < ma.Length; k++)
+ {
+ try
+ {
+ if (ma[k] == null) continue;
+
+ Type[] types = ma[k].GetTypes();
+ if (types == null) continue;
+
+ foreach (Type t in types)
+ {
+ try
+ {
+ if (t == null) continue;
+ String ns = t.Namespace;
+ if (ns == null) ns = String.Empty;
+
+ if (_typesForNamespace.ContainsKey(ns))
+ list= (List<String>)
_typesForNamespace[ns];
+ else
+ {
+ list= new List<String>();
+ _typesForNamespace[ns]= list;
+ }
+
+ // sometimes we get duplicate types
+ if (!list.Contains(t.FullName))
+ {
+ _assemblyForType[t.FullName]= assemblyName;
+ list.Add(t.FullName);
+ _fullNamesForShortNames[t.Name] =
t.FullName;
+ }
+ }
+ catch(ReflectionTypeLoadException)
+ {
+ //Response.Write("Problem with : " +
t.FullName);
+ continue;
+ }
+ }
+ }
+ catch(Exception)
+ {
+ continue;
+ }
+ }
+ }
+
+ return assemblyName;
+ }
+
+
+ private static void LoadAssembliesAndPopulateHashes()
+ {
+ _assembliesNotLoaded = new Dictionary<String,Object>();
+ _assemblyForType = new Dictionary<String,String>();
+ _typesForNamespace = new Dictionary<String,List<String>>();
+ _fullNamesForShortNames = new Dictionary<String,String>();
+ _assembliesLoaded = new Dictionary<String,Assembly>();
+
+ for (int i=0; i<BaseAssembliesToLoad.Length; ++i)
+ InternalLoadOneAssembly(BaseAssembliesToLoad[i]);
+
+ Alphabetize();
+ }
+
+
+ private static void Alphabetize()
+ {
+ foreach (string key in _typesForNamespace.Keys)
+ {
+ _typesForNamespace[key].Sort();
+ }
+ }
+
+
+ public static String GetTypeInfo(String typeName)
+ {
+ string q= null;
+ String[] s = null;
+ try
+ {
+ q= QualifyType(typeName);
+ s = q.Replace(")","").Replace("(","").Split(" ".ToCharArray(),
3);
+ return GetTypeInfo(s[1].Replace("\"",""),
s[2].Replace("\"",""));
+
+ }
+ catch (System.Exception exc1)
+ {
+ System.Console.WriteLine("uncaught exception {0}",
exc1.ToString());
+ System.Console.WriteLine("q= {0}", q);
+ System.Console.WriteLine("s.Length= {0}", s.Length);
+ throw ;
+ }
+ }
+
+
+ public static String GetTypeInfo(String typeName, String assemblyName)
+ {
+ try
+ {
+ if (_assemblyForType.Keys.Contains(typeName) &&
+ _assemblyForType[typeName] == assemblyName &&
+ _assembliesLoaded.Keys.Contains(assemblyName))
+ {
+ Assembly a2 = _assembliesLoaded[assemblyName];
+ Ionic.Cscomp.TypeInfo ti2= new Ionic.Cscomp.TypeInfo(a2,
typeName);
+ return ti2.AsSexp();
+ }
+
+ // Load from a strongname, eg
+ // "System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
+
+ Assembly a= Assembly.Load(assemblyName);
+
+ if ((a == null) && (System.IO.File.Exists(assemblyName)))
+ a= Assembly.LoadFrom(assemblyName);
+
+ if (a == null)
+ {
+ System.Console.Error.WriteLine("Cannot load that
assembly");
+ return null;
+ }
+
+ Ionic.Cscomp.TypeInfo ti= new Ionic.Cscomp.TypeInfo(a,
typeName);
+ return ti.AsSexp();
+ }
+ catch(TypeLoadException e2)
+ {
+ Console.Error.WriteLine("TypeLoadException: Could not load
type: \"{0}\"\n{1}", typeName, e2);
+
+ return null;
+ }
+ catch (Exception e1)
+ {
+ Console.Error.WriteLine("Exception: type '{0}'\n{1}",
typeName, e1);
+ return null;
+ }
+ }
+
+
+
+ private static Assembly TryLoadAssembly(String assemblyName)
+ {
+ Assembly a= null;
+
+ if (assemblyName.Contains(','))
+ {
+ // smells like a fully-qualified name
+ a= Assembly.Load(assemblyName);
+ }
+ else if (assemblyName.EndsWith(".dll") ||
+ assemblyName.EndsWith(".exe"))
+ {
+ if (System.IO.File.Exists(assemblyName))
+ a= Assembly.LoadFrom(assemblyName) ;
+ else
+ {
+ // try finding a DLL by that name in the .net2.0 directory.
+ string GuessedDll = String.Format("{0}\\{1}", DotNetDir,
assemblyName);
+ if (System.IO.File.Exists(GuessedDll))
+ a= Assembly.LoadFrom(GuessedDll) ;
+ }
+ }
+ else
+ {
+ // try finding a DLL by that name in the .net2.0 directory.
+ string GuessedDll = String.Format("{0}\\{1}.dll", DotNetDir,
assemblyName);
+ if (System.IO.File.Exists(GuessedDll))
+ a= Assembly.LoadFrom(GuessedDll) ;
+ }
+
+ return a;
+ }
+
+
+
+ private static System.Type TryLoadType(String theTypeName, String
assemblyName)
+ {
+ System.Type t= null;
+ Assembly a= TryLoadAssembly(assemblyName);
+ if (a != null)
+ t = a.GetType(theTypeName, false, true);
+ return t; // maybe null
+ }
+
+
+
+
+ public static String LoadOneAssembly (string name)
+ {
+ Tracing.Trace("LoadOneAssembly: {0}", name);
+ string r = InternalLoadOneAssembly(name);
+ Alphabetize();
+
+ if (r == null) return "nil";
+ if (r == "t") return r;
+ return "\"" + r + "\"";
+ }
+
+
+
+ public static String ListLoadedAssemblies ()
+ {
+ string atoms = String.Join("\" \"",
_assembliesLoaded.Keys.ToArray());
+ return "(list \"" + atoms + "\")";
+ }
+
+
+ /// <summary>
+ /// Gets the version of the assembly, in a string form.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Returns a quoted string, suitable for use as a lisp
+ /// s-expression. Example: "1.2.0.4"
+ /// </para>
+ /// </remarks>
+ /// <returns>
+ /// The quoted version string
+ /// </returns>
+ public static String Version ()
+ {
+ return "\"" +
+
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()
+ "\"";
+ }
+
+
+ public static String ListKnownTypes()
+ {
+ string atoms = String.Join("\" \"",
_assemblyForType.Keys.ToArray());
+ return "(list \"" + atoms + "\")" ;
+ }
+
+
+
+ /// <summary>
+ /// Gets all the known completions in the given namespace.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The completions include all the types, and all the child
namespaces.
+ /// So, for ns "System", the completion list will include
System.Delegate
+ /// as well as System.Diagnostics
+ /// </para>
+ /// </remarks>
+ /// <returns>
+ /// </returns>
+ public static String GetCompletionsForNamespace(string ns)
+ {
+ if (String.IsNullOrEmpty(ns)) return null;
+
+ if (!_typesForNamespace.ContainsKey(ns))
+ {
+ //System.Console.WriteLine("t4n no contain '{0}'", ns);
+ return null;
+ }
+
+ var result= new System.Text.StringBuilder();
+ int len = ns.Length+1;
+ result.Append("(list \"").Append(ns).Append("\" (list 'types (list
");
+ foreach (var t in _typesForNamespace[ns])
+ {
+ //System.Console.WriteLine(" " + t.Substring(len));
+ result.Append("\"").Append(t.Substring(len)).Append("\" ");
+ }
+ result.Append("))");
+
+ var childlist = new List<String>();
+ foreach (var key in _typesForNamespace.Keys)
+ {
+ if (key.StartsWith(ns) && !key.Equals(ns))
+ {
+ var child = key.Substring(len);
+ var p = child.IndexOf('.');
+ if (p > 0)
+ child = child.Substring(0,p);
+ if (!childlist.Contains(child))
+ childlist.Add(child);
+ }
+ }
+
+ if (childlist.Count() > 0)
+ {
+ result.Append(" (list 'namespaces (list ");
+ foreach (var c in childlist)
+ result.Append("\"").Append(c).Append("\" ");
+ result.Append("))");
+ }
+
+ result.Append(")");
+ return result.ToString();
+ }
+
+
+ private static string Escape(string s)
+ {
+ return s.Replace("\"", "\\\"");
+ }
+
+
+ /// <summary>
+ /// Qualify a name
+ /// </summary>
+ /// <returns>
+ /// ("type" fulltypename) if the name is a type
+ /// ("namespace" parentNamespace) if the name is a namespace
+ /// ("unknown" name) if the name is a namespace
+ /// </returns>
+ public static String QualifyName(String name)
+ {
+ var suffix = "." + name;
+ IEnumerable<String> collection;
+
+// if (!name.Contains("."))
+// {
+// // no dot in the name = assume short name
+// collection = _fullNamesForShortNames.Keys;
+//
+// // check for exact match in the keys
+// foreach (var key in collection)
+// {
+// if (key.Equals(name))
+// return String.Format("(list \"type\" \"{0}\")",
+// _fullNamesForShortNames[key]);
+// }
+//
+// return String.Format("(list \"unknown\" \"{0}\")",name);
+// }
+
+
+ if (Verbose)
+ System.Console.WriteLine("checking name: {0}", name);
+
+
+ // look for exact match on a fully-qualified typename
+ collection = _fullNamesForShortNames.Values;
+ foreach (var value in collection)
+ {
+ if (value.Equals(name))
+ return String.Format("(list \"type\" \"{0}\")", value);
+ }
+
+ // look for ends-with match on a fully-qualified typename
+ foreach (var value in collection)
+ {
+ if (value.EndsWith(suffix))
+ return String.Format("(list \"type\" \"{0}\")", value);
+ }
+
+ // now check for exact match on known namespaces...
+ collection = _typesForNamespace.Keys;
+ foreach (var key in collection)
+ {
+ if (key.Equals(name))
+ return String.Format("(list \"namespace\" \"{0}\")", name);
+ }
+
+ // match on the last segment of the namespace. Eg, if name is
"Diagnostics",
+ // should match on "System.Diagnostics".
+ foreach (var key in collection)
+ {
+ if (key.EndsWith(suffix))
+ return String.Format("(list \"namespace\" \"{0}\")", key);
+ }
+
+ // Finally, check the names of common namespaces that map to
assemblies.
+ // If found, load the assembly.
+ collection = GacAssemblies.Keys;
+ foreach (var key in collection)
+ {
+ if (key.Equals(name))
+ {
+ LoadOneAssembly(key + ", " + GacAssemblies[key]);
+ return String.Format("(list \"namespace\" \"{0}\")", name);
+ }
+ }
+
+ return String.Format("(list \"unknown\" \"{0}\")", Escape(name));
+ }
+
+
+
+ /// <summary>
+ /// Return all possible matches on a given symbol fragment.
+ /// </summary>
+ ///
+ /// <param name='fragment'>
+ /// the fragment of the name to match on.
+ /// </param>
+ ///
+ /// <param name='namespaces'>
+ /// a comma-separated list of namespaces
+ /// </param>
+ ///
+ /// <returns>
+ /// a list containing pairs of all possible completions.
+ /// eg, if completing on Ba?, maybe return:
+ /// (list
+ /// ("type" "Foo.Bar")
+ /// ("type" "Utils.Bands")
+ /// ("namespace" "Barrels")
+ /// )
+ /// </returns>
+ public static String GetMatches(String fragment, String namespaces)
+ {
+ List<String> responseSet = new List<String>();
+ var reTypeStub = "\\." + fragment + ".*$";
+ var reNamespace = "^" + fragment + ".*$";
+ IEnumerable<String> collection;
+ Tracing.Trace("checking fragment: {0}", fragment);
+
+ // look for types with short names that begin with the fragment
+ collection = _fullNamesForShortNames.Values;
+ foreach (string ns in namespaces.Split(','))
+ {
+ foreach (var value in collection)
+ {
+ Match match = Regex.Match(value,"^"+ns+reTypeStub);
+ if (match.Success)
+ responseSet.Add(String.Format("(list \"type\"
\"{0}\")", value));
+ }
+ }
+
+ // look for namespaces that begin with the fragment
+ collection = _typesForNamespace.Keys;
+ foreach (var key in collection)
+ {
+ Match match = Regex.Match(key,reNamespace);
+ if (match.Success)
+ responseSet.Add(String.Format("(list \"namespace\"
\"{0}\")", key));
+
+ // I think maybe we want to exclude child namespaces. . .
+ // maybe later.
+ }
+
+ if (responseSet.Count == 0)
+ return "nil";
+
+ string items = String.Join(" ", responseSet.ToArray());
+ return "(list " + items + ")";
+ }
+
+
+ /// <summary>
+ /// Qualifies the type name.
+ /// </summary>
+ /// <param name='typeName'>
+ /// the name of the type, possibly a short name, like "Console" or
"Stream",
+ /// and possibly a long name like System.IO.Stream
+ /// </param>
+ /// <returns>
+ /// sexp: (list "fulltypename" "assemblyname") or nil if the type
is not known
+ /// The assembly name
+ /// </returns>
+ public static String QualifyType(String typeName)
+ {
+ var name = typeName;
+ string stub = null;
+ System.Text.StringBuilder residual = null;
+ int repeats = 0;
+
+ Tracing.Trace("QualifyType: {0}", typeName);
+
+ Match match = Regex.Match(name,"(.+`[1-9])\\[.+\\]$");
+ if (match.Success)
+ name = match.Groups[1].Value.ToString();
+
+ name = name.Trim();
+ Tracing.Trace("QualifyType: name '{0}'", name);
+
+ if (!name.Contains("."))
+ {
+ Tracing.Trace("QualifyType: name contains no dot");
+ Tracing.Trace("QualifyType: examining {0} keys",
+ _fullNamesForShortNames.Keys.Count);
+
+ foreach (var key in _fullNamesForShortNames.Keys)
+ {
+ if (key.Equals(name))
+ {
+ return
+ String.Format("(list \"{0}\" \"{1}\")",
+ _fullNamesForShortNames[key],
+
_assemblyForType[_fullNamesForShortNames[key]]);
+ }
+ }
+ }
+
+ // it may be a fully-qualified type name
+ Tracing.Trace("QualifyType: examining {0} full names",
+ _fullNamesForShortNames.Values.Count);
+
+ foreach (var value in _fullNamesForShortNames.Values)
+ {
+ if (stub == null || !value.StartsWith(stub))
+ {
+ int ix = value.LastIndexOf('.');
+ stub= (ix > 0)
+ ? value.Substring(0, ix)
+ : value ;
+ repeats = 0;
+ if (residual!=null)
+ {
+ var r = residual.ToString();
+ if (!String.IsNullOrEmpty(r))
+ Tracing.Trace(" {0}", r);
+ }
+
+ residual = new System.Text.StringBuilder();
+ }
+ else
+ {
+ residual.Append(".");
+ repeats++;
+ }
+ if (repeats == 0)
+ Tracing.Trace(" check: {0}.*", stub);
+
+ // exact match
+ if (value.Equals(name))
+ {
+ return
+ String.Format("(list \"{0}\" \"{1}\")",
+ value,
+ _assemblyForType[value]);
+ }
+ }
+
+ return "nil";
+ }
+
+
+
+
+ public static string GetConstructors (string typeName)
+ {
+ Tracing.Trace("GetConstructors: {0}", typeName);
+ if (!_assemblyForType.Keys.Contains(typeName))
+ return "nil";
+
+ Assembly a = AssemblyIsLoaded(_assemblyForType[typeName]);
+ if (a==null)
+ return "nil";
+
+ var tinfo= new Ionic.Cscomp.TypeInfo(a, typeName);
+ return tinfo.GetConstructorsSexp();
+ }
+
+
+
+ public static string GetTypeGivenVarDecl (string csharpVarDeclaration)
+ {
+ return GetTypeGivenVarDecl(csharpVarDeclaration, null, null, -1,
+ "Foo1", // classname
+ "", "");
+ }
+
+
+ /// <summary>
+ /// Get the type of the Nth variable in the declaration list.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// This method compiles the provided variable declaration list
+ /// into an assembly, and then performs reflection on the result.
+ /// </para>
+ /// </remarks>
+ ///
+ /// <param name='csharpVarDeclaration'>
+ /// a fragment of csharp code to compile. It should contain one or
+ /// more variable declarations.
+ /// </param>
+ /// <param name='varIndex'>
+ /// the number of the variable to determine the type of.
+ /// There's no way to interrogate the type of a local var by
+ /// name. That information is not in the assembly metadata -
+ /// it's in the PDB, and it's not easily accessible. But, the
+ /// MSIL apparently lists the local variables in the order in
+ /// which they were declared. So we can use the index to find
+ /// the desired local var.
+ /// </param>
+ /// <param name='namespaces'>
+ /// a comma-separated list of namespaces
+ /// </param>
+ /// <param name='references'>
+ /// a comma-separated list of assembly references
+ /// </param>
+ /// <param name='classname'>
+ /// name of the class to use. Helpful in satisfying static
references.
+ /// </param>
+ /// <param name='arglist'>
+ /// a comma-separated list of method types and arguments. This is
+ /// a legal fragment of C# code, suitable to put right into the
method
+ /// signature.
+ /// </param>
+ public static string GetTypeGivenVarDecl (string csharpVarDeclaration,
+ String namespaces,
+ String assemblyReferences,
+ int varIndex,
+ String classname,
+ String arglist,
+ String instanceMembers)
+ {
+ try
+ {
+ Tracing.Trace("GetTypeGivenVarDecl {0}", csharpVarDeclaration);
+ foreach (string aName in namespaces.Split(','))
+ {
+ Tracing.Trace("Autoload assy {0}", aName);
+ InternalLoadOneAssembly(aName);
+ }
+
+ Object compileResult = CompileFragment(csharpVarDeclaration,
+ namespaces,
+ assemblyReferences,
+ classname,
+ arglist,
+ instanceMembers);
+
+ MethodInfo methodInfo = compileResult as MethodInfo;
+ if (methodInfo == null)
+ {
+ String[] emsgs = (String[]) compileResult;
+ var estring = (emsgs!=null)
+ ? String.Join("\" \"", emsgs)
+ : String.Format("unknown error (cr = {0})",
+
(compileResult==null)?"-null-":compileResult.ToString());
+ return String.Format("(list \"error\" \"{0}\")",
estring.Replace("\\", "\\\\"));
+ }
+
+
+ var res = GetTypeOfNthLocalVar(methodInfo,
varIndex).ToString();
+ res = String.Format("(list \"type\" \"{0}\")", res);
+
+ Tracing.Trace("GetTypeGivenVarDecl: result {0}", res);
+
+ return res;
+
+ }
+ catch (System.Exception exc1)
+ {
+ Tracing.Trace("GetTypeGivenVarDecl Exception: {0}",
exc1.ToString());
+ return String.Format ("(list \"exception\" \"{0}\")",
exc1.Message);
+ }
+ }
+
+
+
+
+ private static string GetAssemblyPathForNamespace(String ns)
+ {
+ Assembly a = AssemblyIsLoaded(ns);
+ if (a!=null)
+ return a.Location;
+ return null;
+ }
+
+ private static string GetUsingClauses(IEnumerable<String> namespaces)
+ {
+ string s = "";
+ foreach (var ns in namespaces)
+ {
+ if (!String.IsNullOrEmpty(ns))
+ s += "using " + ns + ";\n";
+ }
+ return s;
+ }
+
+
+
+ /// <summary>
+ /// Compile a C# var declaration fragment.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The goal is to determine the types of the local variables,
+ /// so that we can do completion on them.
+ /// </para>
+ /// </remarks>
+ /// <param name='fragment'>
+ /// a comma-separated list of namespaces
+ /// </param>
+ /// <param name='namespaces'>
+ /// a comma-separated list of namespaces
+ /// </param>
+ /// <param name='references'>
+ /// a comma-separated list of assembly references
+ /// </param>
+ /// <param name='classname'>
+ /// name of the class to use. Helpful in satisfying static
references.
+ /// </param>
+ /// <param name='arglist'>
+ /// a C# fragment, representing an arglist, suitable for
+ /// framing within parens for the method declaration.
+ /// </param>
+ /// <returns>
+ /// a MethodInfo, if success. Otherwise, an array of Strings
containing
+ /// error messages.
+ /// </returns>
+ private static Object CompileFragment(string fragment,
+ String namespaces,
+ String references,
+ string classname,
+ String arglist,
+ String instanceMembers)
+ {
+ string nsname = "N"+Path.GetRandomFileName().Replace(".","");
+ string methodname = "M"+Path.GetRandomFileName().Replace(".","");
+ //string classname = Path.GetRandomFileName().Replace(".","");
+ string usingBlock = (String.IsNullOrEmpty(namespaces))
+ ? "using System;\n"
+ : GetUsingClauses(namespaces.Split(','));
+ string literalSource =
+ usingBlock +
+ "\nnamespace " + nsname + " {" +
+ "\n class " + classname + " {" +
+ "\n " + instanceMembers.Replace("; ",";\n ")
+ .Replace("} ","}\n ")
+ .Replace(";}","; }") +
+ "\n void "+ methodname + " (" + arglist + ") {"+
+ "\n " + fragment.Replace(";",";\n ") +
+ "}\n }\n}\n";
+
+ if (Verbose)
+ System.Console.WriteLine("code to compile:\n{0}",
literalSource);
+
+ Tracing.Trace("code to compile:\n{0}", literalSource);
+
+ try
+ {
+ var csharp = new Microsoft.CSharp.CSharpCodeProvider(new
Dictionary<String, String> { { "CompilerVersion", "v3.5" } });
+
+ var cp = new System.CodeDom.Compiler.CompilerParameters();
+ cp.GenerateInMemory = true;
+ cp.GenerateExecutable = false;
+ cp.IncludeDebugInformation = true;
+
+ if (!String.IsNullOrEmpty(references))
+ foreach (string ra in references.Split(','))
+ cp.ReferencedAssemblies.Add(ra);
+
+ // do I need to worry about duplicates?
+ foreach (string aName in namespaces.Split(','))
+ {
+ Tracing.Trace("maybe add Ref: {0}", aName);
+ var path = GetAssemblyPathForNamespace(aName);
+ if (path!=null)
+ cp.ReferencedAssemblies.Add(path);
+ }
+
+ System.CodeDom.Compiler.CompilerResults cr =
+ csharp.CompileAssemblyFromSource(cp,literalSource);
+ if (cr == null)
+ {
+ var e = new List<String>();
+ Tracing.Trace("CompilerResults == null");
+ e.Add("CompilerResults == null");
+ return e.ToArray();
+ }
+
+ foreach (string s in cr.Output)
+ {
+ if (Verbose)
+ System.Console.WriteLine(s);
+ Tracing.Trace(s);
+ }
+
+ if (cr.Errors.Count != 0)
+ {
+ var e = new List<String>();
+ e.Add(String.Format("Errors.Count = {0}",
cr.Errors.Count));
+ e.Add(cr.Errors[0].ToString());
+ foreach(var error in cr.Errors)
+ Tracing.Trace(error.ToString());
+ return e.ToArray();
+ }
+
+ var a = cr.CompiledAssembly;
+ MethodInfo mi = a.GetType(nsname + "." + classname)
+ .GetMethod(methodname, BindingFlags.Instance |
BindingFlags.NonPublic);
+
+ return mi;
+ }
+ catch (System.Exception e1)
+ {
+ var e = new List<String>();
+ e.Add("Exception during compile: " + e1.Message );
+ Tracing.Trace("{0}", e1.ToString());
+ return e.ToArray();
+ }
+ }
+
+
+
+ /// <summary>
+ /// Get the type of the Nth local variable in the given method.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Reflection doesn't provide access to var names. And,
+ /// some variables listed in LocalVariables are synthetic -
+ /// generated by the compiler. But apparently the IL does
+ /// list the format local vars first, and in the order in
+ /// which they are declared. So, to retrieve a local var,
+ /// rely on the count.
+ /// </para>
+ /// </remarks>
+ private static Type GetTypeOfNthLocalVar(MethodInfo methodInfo, int ix)
+ {
+ MethodBody body = methodInfo.GetMethodBody();
+ var localVars = body.LocalVariables;
+ Type ft = null;
+ int c = 0;
+ foreach (LocalVariableInfo lvi in localVars)
+ {
+ if (Verbose)
+ System.Console.WriteLine("Local var [{0}] {1}",
+ c, lvi.LocalType.ToString());
+ if (c==ix || ix < 0)
+ ft = lvi.LocalType;
+ c++;
+ }
+ return ft;
+ }
+
+ private static bool _Verbose;
+ public static bool Verbose
+ {
+ get
+ {
+ return _Verbose;
+ }
+ set
+ {
+ if (value)
+ {
+ // turn on
+ Tracing.Trace("Verbose = true");
+ }
+ _Verbose = value;
+ }
+ }
+
+
+ }
+
+
+
+ public class TypeInfo
+ {
+ internal TypeInfo(Assembly a, string typeName)
+ {
+ mt = a.GetType(typeName, false, true);
+ if ( mt == null )
+ throw new Exception(String.Format("Cannot get that type
({0})", typeName));
+ }
+
+ /// <summary>
+ /// Returns the TypeInfo as a lisp S-expression.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// It looks like this:
+ ///
+ /// </para>
+ /// </remarks>
+ internal String AsSexp()
+ {
+ var result= new System.Text.StringBuilder();
+ result.Append("(list \"").Append(mt.FullName).Append("\" 'type
(list ");
+
+ // properties
+ var props = GetPropertiesInfo();
+ if (props != null)
+ {
+ foreach (string s in props)
+ result.Append(s);
+ }
+
+ result.Append(") (list ");
+
+ // methods
+ var meth = GetMethodsInfo();
+ if (meth != null)
+ {
+ foreach (string s in meth)
+ result.Append(s);
+
+ }
+ result.Append(") (list ");
+
+
+ // fields
+ var fields = GetFieldsInfo();
+ if (fields != null)
+ {
+ foreach (string s in fields)
+ result.Append(s);
+
+ }
+ result.Append(")");
+
+ // events? constructors?
+
+ result.Append(")");
+ return result.ToString();
+ }
+
+ // public String LastError { get { return lastError;} }
+ // public Type TheType { get { return mt; } }
+ // public String FQN { get { return (mt!=null) ?
mt.AssemblyQualifiedName: null; } }
+ // public String Name { get { return (mt!=null) ? mt.Name:
null;} }
+ // public String FullName { get { return (mt != null) ?
mt.FullName : null;} }
+ // public String Status { get { return status;} }
+
+ // public String Href
+ // {
+ // set { theHref= value; }
+ // get { return theHref; }
+ // }
+
+ private static Type ReferencedType(Type t)
+ {
+ return (t.HasElementType) ? ReferencedType(t.GetElementType()) : t
;
+ }
+
+ private static String EmitOneType(Type t1)
+ {
+ return t1.ToString();
+ }
+
+
+ private static String EmitOneTypeWithInterfaces(Type t1)
+ {
+ Type t= ReferencedType(t1);
+ var result= new System.Text.StringBuilder();
+ result.Append(t1.ToString());
+
+ Type[] it= t.GetInterfaces();
+ if (it.Length > 0 )
+ {
+ int j=0;
+ for (int i=0; i < it.Length; i++)
+ {
+ if ((t.BaseType!=null) &&
!(it[i].IsAssignableFrom(t.BaseType)))
+ {
+ if (j==0) result.Append(" : ");
+ else result.Append(", ");
+ result.Append(EmitOneType(it[i]));
+ j++;
+ }
+ }
+ }
+
+ return result.ToString();
+ }
+
+
+
+ private string ParamString(System.Reflection.ParameterInfo[] pi, bool
isProperty)
+ {
+ if (pi.Length==0) return "nil";
+
+ var sb= new System.Text.StringBuilder("(list ");
+ int j;
+ String openBracket= "(";
+ String closeBracket= ")";
+ if (isProperty)
+ {
+ openBracket= "[";
+ closeBracket= "]";
+ }
+
+ for (j=0; j < pi.Length; j++)
+ {
+ // ParameterAttributes Attributes = pi[j].Attributes;
+ // if (Attributes != ParameterAttributes.None)
+ // sb.Append("[").Append(Attributes.ToString()).Append("]
"); // eg, In,Out,Optional
+
+ sb.Append("\"").Append(pi[j].ParameterType.ToString())
+ .Append(" ")
+ .Append(pi[j].Name)
+ .Append("\" ");
+ }
+ if (isProperty)
+ {
+ if (j!=0) sb.Append(closeBracket);
+ }
+ else
+ {
+ if (j==0) sb.Append(openBracket);
+ sb.Append(closeBracket);
+ }
+
+ return sb.ToString();
+ }
+
+
+ private string NewParamString(System.Reflection.ParameterInfo[] pi)
+ {
+ if (pi.Length==0) return "nil";
+ var sb= new System.Text.StringBuilder("(list ");
+ for (int j=0; j < pi.Length; j++)
+ {
+ sb.Append("(list \"")
+ .Append(pi[j].Name)
+ .Append("\" 'variable (list :type \"")
+ .Append(pi[j].ParameterType.ToString())
+ .Append("\")) ");
+ }
+ sb.Append(")");
+ return sb.ToString();
+ }
+
+
+ private static String EmitAttributes(Type t) {
+ System.Text.StringBuilder result= new System.Text.StringBuilder();
+ if (t.IsSerializable) result.Append("[serializable]<br>\n ");
+ if (t.IsPublic) result.Append("public ");
+ if (t.IsAbstract) result.Append("abstract ");
+ if (t.IsSealed) result.Append("sealed ");
+ if (t.IsClass) result.Append("class ");
+ if (t.IsEnum) result.Append("enum ");
+ if (t.IsInterface) result.Append("interface ");
+ return result.ToString();
+ }
+
+
+ internal String GetConstructorsSexp()
+ {
+ if (mt==null) return "nil";
+ System.Reflection.ConstructorInfo[] cia= mt.GetConstructors();
+ if (cia==null) return "nil";
+ if (cia.Length==0) return "nil";
+
+ Tracing.Trace("{1}: Found {0} constructors", cia.Length,
mt.FullName);
+
+ var sb1 = new System.Text.StringBuilder();
+ sb1.Append("(list \"").Append(mt.FullName)
+ .Append("\" 'type (list :constructors (list\n");
+
+ foreach (ConstructorInfo ci in cia)
+ {
+ sb1.Append(" (list :typemodifiers ")
+ .Append(NewMethodBaseModifiers(ci))
+ .Append(" :arguments ")
+ .Append(NewParamString(ci.GetParameters()))
+ .Append(")\n");
+ }
+ sb1.Append(")))");
+ return sb1.ToString();
+ }
+
+
+
+ private String PropertyModifiers(PropertyInfo p)
+ {
+ var sb= new System.Text.StringBuilder("(cons 'typemodifiers (list
");
+ System.Reflection.MethodInfo mi= null;
+
+ if (p.GetGetMethod() != null)
+ {
+ mi= p.GetGetMethod();
+ if (p.GetSetMethod() == null)
+ sb.Append("\"readonly\" ");
+ }
+ else if (p.GetSetMethod() != null) {
+ mi= p.GetSetMethod();
+ sb.Append("\"writeonly\" ");
+ }
+
+ if (mi != null) {
+ if (mi.IsPublic)
+ sb.Append("\"public\" ");
+ if (mi.IsPrivate)
+ sb.Append("\"private\" ");
+ if (mi.IsFamily)
+ sb.Append("\"protected\" ");
+
+ if (mi.IsStatic)
+ sb.Append("\"static\" ");
+ }
+
+ sb.Append("))");
+ return sb.ToString();
+ }
+
+
+ private String[] GetPropertiesInfo()
+ {
+ if (mt==null) return null;
+ System.Reflection.PropertyInfo[] pi= mt.GetProperties();
+ if (pi==null) return null;
+ if (pi.Length==0) return null;
+
+ var a= new List<String>();
+ System.Text.StringBuilder sb1;
+ foreach (PropertyInfo p in pi) {
+
+ sb1 = new System.Text.StringBuilder();
+ sb1.Append("(list \"").Append(p.Name).Append("\" 'property ");
+
+ sb1.Append("\"").Append(p.PropertyType.ToString()).Append("\"
");
+
+ sb1.Append(PropertyModifiers(p));
+
+ sb1.Append(")");
+
+ a.Add(sb1.ToString());
+ }
+ return a.ToArray();
+ }
+
+
+
+ private String EmitMethodAttrs(MethodInfo m) {
+ System.Text.StringBuilder result= new System.Text.StringBuilder();
+
+ if (m.IsPublic)
+ result.Append("public ");
+
+ if (m.IsFamily)
+ result.Append("protected ");
+
+ if (m.IsPrivate)
+ result.Append("private ");
+
+ if (m.IsAbstract)
+ result.Append("abstract ");
+
+ if (m.IsStatic)
+ result.Append("static ");
+
+ if (m.IsFinal)
+ result.Append("final ");
+
+ return result.ToString();
+ }
+
+
+
+ public String[] GetMethodsInfo()
+ {
+ System.Reflection.MethodInfo[] mi= mt.GetMethods();
+ System.Array.Sort(mi,new MpfComparer());
+ var a= new List<String>();
+ System.Text.StringBuilder sb1;
+ foreach (MethodInfo m in mi)
+ {
+ sb1= null;
+
+ if (m.IsPrivate) continue;
+
+ // special name denotes???? I don't know.
+ if (!m.IsSpecialName)
+ {
+ // it's a method:
+ sb1 = new System.Text.StringBuilder(" (list \"");
+ sb1.Append(m.Name).Append("\" 'method ")
+
.Append("\"").Append(m.ReturnType.ToString()).Append("\" ")
+ .Append(ParamString(m.GetParameters(), false))
+ .Append(MethodBaseModifiers(m))
+ .Append(")");
+ }
+
+ if (sb1 != null)
+ a.Add(sb1.ToString());
+ }
+ return a.ToArray();
+ }
+
+
+
+ private string MethodBaseModifiers(MethodBase mi)
+ {
+ var sb= new System.Text.StringBuilder(" (cons 'typemodifiers (list
");
+ if (mi.IsFinal)
+ sb.Append("\"sealed\" ");
+ if (mi.IsPublic)
+ sb.Append("\"public\" ");
+ if (mi.IsPrivate)
+ sb.Append("\"private\" ");
+ if (mi.IsFamily)
+ sb.Append("\"protected\" ");
+ if (mi.IsStatic)
+ sb.Append("\"static\" ");
+ sb.Append("))");
+ return sb.ToString();
+ }
+
+
+ private string NewMethodBaseModifiers(MethodBase mi)
+ {
+ var sb= new System.Text.StringBuilder(" (list ");
+ if (mi.IsFinal)
+ sb.Append("\"sealed\" ");
+ if (mi.IsPublic)
+ sb.Append("\"public\" ");
+ if (mi.IsPrivate)
+ sb.Append("\"private\" ");
+ if (mi.IsFamily)
+ sb.Append("\"protected\" ");
+ if (mi.IsStatic)
+ sb.Append("\"static\" ");
+ sb.Append(")");
+ return sb.ToString();
+ }
+
+
+ // private void BuildClassHierarchy(Type t) {
+ // classHierarchy.Push(t);
+ // if (t.BaseType != null)
+ // BuildClassHierarchy(t.BaseType);
+ // }
+
+
+ // public String GetHierarchyHtml() {
+ // if (mt == null) return null;
+ // classHierarchy= new Stack();
+ // BuildClassHierarchy(mt);
+ // int c= classHierarchy.Count;
+ // System.Text.StringBuilder sb= new
System.Text.StringBuilder("\n");
+ // int i=0;
+ // while (classHierarchy.Count != 0) {
+ // sb.Append("<div class='elt'>");
+ // if (i!=0) sb.Append("+ ");
+ //
sb.Append(EmitOneTypeWithInterfaces((Type)classHierarchy.Pop())).Append("\n");
+ // i++;
+ // }
+ // for (i=0; i < c; i++) sb.Append("</div>");
+
+ // return sb.ToString();
+ // }
+
+
+
+
+ // public String[] GetPropertiesHtml() {
+ // if (mt == null) return null;
+ // System.Reflection.PropertyInfo[] pi= mt.GetProperties();
+ // System.Array.Sort(pi,new myComparer()); // by name
+ // ArrayList a= new ArrayList();
+ // System.Text.StringBuilder sb1;
+ // foreach (PropertyInfo p in pi) {
+ // try{
+ // sb1 = new System.Text.StringBuilder();
+ // sb1.Append(" ").Append(PropAttrsString(p));
+ // sb1.Append(" ").Append(EmitOneType(p.PropertyType));
+ // sb1.Append(" <b>").Append(p.Name).Append("</b>");
+ // AppendParams(sb1,p.GetIndexParameters(), true);
+ // }
+ // catch(Exception e){
+ // a.Add(e.Message);
+ // continue;
+ // }
+ // a.Add(sb1.ToString());
+ // }
+ // return (String[]) a.ToArray(typeof(String));
+ // }
+
+
+
+ private static String FieldAttrsString(FieldInfo f)
+ {
+ var sb= new System.Text.StringBuilder(" (cons 'typemodifiers (list
");
+ if (f.IsPublic)
+ sb.Append("\"public\" ");
+ if (f.IsFamily)
+ sb.Append("\"protected\" ");
+ if (f.IsPrivate)
+ sb.Append("\"private\" ");
+ if (f.IsLiteral)
+ sb.Append("\"const\" ");
+ if (f.IsStatic)
+ sb.Append("\"static\" ");
+ sb.Append("))");
+ return sb.ToString();
+ }
+
+
+ public String[] GetFieldsInfo()
+ {
+ if (mt == null) return null;
+ System.Reflection.FieldInfo[] fi= mt.GetFields();
+ System.Array.Sort(fi,new MpfComparer()); // by name
+ var a= new List<String>();
+ System.Text.StringBuilder sb1;
+ foreach (FieldInfo f in fi)
+ {
+ if (!f.IsPrivate)
+ {
+ try
+ {
+ sb1 = new System.Text.StringBuilder(" (list \"");
+ sb1.Append(f.Name).Append("\" 'field ")
+
.Append("\"").Append(f.FieldType.ToString()).Append("\" ")
+ .Append(FieldAttrsString(f))
+ .Append(")");
+ }
+ catch(Exception e){
+ a.Add(e.Message);
+ continue;
+ }
+ if (sb1 != null)
+ a.Add(sb1.ToString());
+ }
+ }
+ return a.ToArray();
+ }
+
+ private System.Type mt;
+ }
+
+
+
+ public class MpfComparer : System.Collections.IComparer
+ {
+ static Type mi= typeof(MethodInfo);
+ static Type pi= typeof(PropertyInfo);
+ static Type fi= typeof(FieldInfo);
+
+ public int Compare (Object x, Object y)
+ {
+ if (mi.IsInstanceOfType(x)) {
+ MethodInfo i1= (MethodInfo) x;
+ MethodInfo i2= (MethodInfo) y;
+ return i1.Name.CompareTo(i2.Name);
+ }
+
+ if (pi.IsInstanceOfType(x)) {
+ PropertyInfo i1= (PropertyInfo) x;
+ PropertyInfo i2= (PropertyInfo) y;
+ return i1.Name.CompareTo(i2.Name);
+ }
+ if (fi.IsInstanceOfType(x)) {
+ FieldInfo i1= (FieldInfo) x;
+ FieldInfo i2= (FieldInfo) y;
+ return i1.Name.CompareTo(i2.Name);
+ }
+
+ return 0;
+ }
+ }
+
+ internal static class Tracing
+ {
+ #if UseCopyData
+ private static Ionic.CopyData.Transceiver transceiver;
+ #endif
+
+ [Conditional("CscompTrace")]
+ public static void SetupDebugConsole()
+ {
+ #if UseCopyData
+
+ // use object initializer syntax
+ System.Diagnostics.Process p3 = new System.Diagnostics.Process
+ {
+ StartInfo =
+ {
+ FileName =
"c:\\dinoch\\vsp\\UnitTestProgressMonitor\\UnitTestProgressMonitor\\bin\\Debug\\UnitTestProgressMonitor.exe",
+ Arguments = "-channel CscompShell",
+ CreateNoWindow = false,
+ UseShellExecute = false
+ }
+ };
+ p3.Start();
+
+ // wait for the process to start?
+ System.Threading.Thread.Sleep(650);
+
+ transceiver = new Ionic.CopyData.Transceiver();
+ transceiver.Channel = "CscompShell";
+
+ transceiver.Send("title CSDE Shell Trace Monitor");
+
+ System.Threading.Thread.Sleep(400);
+ transceiver.Send("log Hello from CscompShell");
+ #endif
+ }
+
+
+ [Conditional("CscompTrace")]
+ public static void Trace(string format, params object[] args)
+ {
+ #if UseCopyData
+ var s2 = String.Format(format, args);
+ transceiver.Send("log " + s2);
+ #endif
+ }
+
+ }
+
+}
+
+
+
+/*
+
+
[System.Reflection.Assembly]::LoadFrom("c:\\dinoch\\dev\\dotnet\\CscompUtilities.dll");
+ [Ionic.Cscomp.Utilities]::Verbose = $TRUE
+
+ [Ionic.Cscomp.Utilities]::GetTypeGivenVarDecl(' var foo = "this is a
string"; var fred = new System.Collections.Generic.List<String> { foo,
foo.Length.ToString() }; var z = fred.Count; var mmm = count + 8; var nnn =
mmm.ToString() +
this.InstanceMethod1(count);','System,System.Xml,System.IO','',4,'CsharpCompletion','int
count','private static int staticField1 = default(int); string
InstanceMethod1(int index) { return default(string);} ')
+
+
+ [Ionic.Cscomp.Utilities]::GetConstructors("System.String");
+
+ [Ionic.Cscomp.Utilities]::GetConstructors("System.DateTimeKind");
+
+ [Ionic.Cscomp.Utilities]::GetMatches("DateT", "System,System.IO");
+
+
+
+ [Ionic.Cscomp.Utilities]::QualifyType("System.Collections.Generic.List`1");
+
+ [Ionic.Cscomp.Utilities]::GetTypeInfo("System.Collections.Generic.List");
+
+ [Ionic.Cscomp.Utilities]::GetTypeGivenVarDecl('var x = String.Format("{0}",
928);' )
+
+ [Ionic.Cscomp.Utilities]::GetTypeGivenVarDecl('var x = new
System.Collections.Generic.List<string>();')
+
+
+
+[Ionic.Cscomp.Utilities]::GetTypeGivenVarDecl(' var doo = "Tra la la"; var
fred = new System.Collections.Generic.List<String>
+ {
+ doo.Length.ToString()
+ };');
+
+[Ionic.Cscomp.Utilities]::GetTypeGivenVarDecl(' var doo = "Tra la la"; var
fred = new System.Collections.Generic.List<String> { doo.Length.ToString() };',
$null, $null, 2);
+
+
+
+
[Ionic.Cscomp.Utilities]::GetTypeInfo("System.Data.SqlClient.SqlCommand","System.Data,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
+
+ [Ionic.Cscomp.Utilities]::GetTypeInfo("System.Xml.XmlReader","System.Xml,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
+
+
+ [Ionic.Cscomp.Utilities]::GetTypeInfo("System.DateTime");
+
+ [Ionic.Cscomp.Utilities]::QualifyType("System.DateTime");
+
+ [Ionic.Cscomp.Utilities]::QualifyType("XmlSerializer");
+
+ [Ionic.Cscomp.Utilities]::QualifyType("System.Xml.XmlReader");
+
+ [Ionic.Cscomp.Utilities]::QualifyType("System.This.Type.Does.Not.Exist");
+
+
+ [Ionic.Cscomp.Utilities]::LoadOneAssembly("Ionic.Zip.dll");
+
+ [Ionic.Cscomp.Utilities]::LoadOneAssembly("System.Xml");
+
+ [Ionic.Cscomp.Utilities]::QualifyName("System.Xml");
+
+
[Ionic.Cscomp.Utilities]::GetCompletionsForNamespace("System.Collections.Generic")
+
+ [Ionic.Cscomp.Utilities]::ListLoadedAssemblies();
+
+
+ [Ionic.Cscomp.Utilities]::ListKnownTypes();
+
+
+ [Ionic.Cscomp.Utilities]::Version();
+
+
+ ;(debug-on-entry 'csde-complete-referenced-assemblies-list)
+
+ (semantic-brute-find-tag-by-class 'using)
+
+*/
+
+
diff --git a/Readme.txt b/Readme.txt
new file mode 100644
index 0000000..888c57f
--- /dev/null
+++ b/Readme.txt
@@ -0,0 +1,69 @@
+Mon, 24 May 2010 17:21
+
+This is the readme for csharp-mode.
+
+You can use csharp-mode alone. To do so,
+
+
+ put this in your .emacs:
+
+ (autoload 'csharp-mode "csharp-mode" "Major mode for editing C# code." t)
+
+ or:
+
+ (require 'csharp-mode)
+
+
+ AND:
+
+ (setq auto-mode-alist
+ (append '(("\\.cs$" . csharp-mode)) auto-mode-alist))
+ (defun my-csharp-mode-fn ()
+ "function that runs when csharp-mode is initialized for a buffer."
+ ...insert your code here...
+ ...most commonly, your custom key bindings ...
+ )
+ (add-hook 'csharp-mode-hook 'my-csharp-mode-fn t)
+
+
+=======================================================
+
+You can also take advantage of C# code completion.
+To do so, put csharp-completion.el, csharp-shell.el , and powershell.el
+on your load-path.
+
+You must also have semantic, from the CEDET package, on your load path.
+
+Put the CscompUtilities.dll in the same location as csharp-shell.el.
+
+Put this in your .emacs file:
+
+
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; C# code completion (requires CEDET semantic)
+
+ (setq load-path
+ (append '("~/users/dinoch/elisp/cedet/semantic"
+ "~/users/dinoch/elisp/cedet/semantic/bovine"
+ "~/users/dinoch/elisp/cedet/common"
+ "~/users/dinoch/elisp/cedet/eieio"
+ "~/users/dinoch/elisp/cedet/contrib"
+ ) load-path ))
+
+ (load "semantic")
+ (load "semantic-load")
+ (load "wisent-csharp")
+
+ (require 'csharp-completion)
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+In your csharp-mode-hook, bind a key to the cscomp completion trigger.
+Like this:
+
+ ;; C# code completion
+ (local-set-key "\M-\\" 'cscomp-complete-at-point)
+ (local-set-key "\M-\." 'cscomp-complete-at-point-menu)
+
+
+
diff --git a/csharp-completion.el b/csharp-completion.el
new file mode 100644
index 0000000..2f7401d
--- /dev/null
+++ b/csharp-completion.el
@@ -0,0 +1,2792 @@
+;;; csharp-completion.el -- Smart code completion for C#
+;;
+;; Author: Dino Chiesa <dpchiesa@hotmail.com>
+;; Maintainer: Dino Chiesa <dpchiesa@hotmail.com>
+;; Created: April 2010
+;; Modified: April 2010
+;; Version: 0.1.5
+;; Keywords: c# languages oop mode
+;; X-URL: http://code.google.com/p/csharpmode/
+;;
+;;
+;;; Commentary:
+;;
+;; This is a code-completion or "intellisense" package for C#. The
+;; scope of this module is much smaller that a full "development
+;; envvironment". It does smart code completion for C#, in
+;; emacs. It does not do font-lock, indenting, debugging,
+;; compiling, profiling, and so on.
+;;
+;; To use it, place the cursor after a partially-completed
+;; statement, and invoke `cscomp-complete-at-point'. Normally this
+;; would be bound to a particular keystroke, like M-. This module
+;; will insert the first completion that matches. If multiple
+;; completions are possible, calling the completion function again
+;; will cycle through the possibilities, similar to the way
+;; dabbrev-mode works.
+;;
+;; You can also call `cscomp-complete-at-point-menu', and get a popup
+;; menu of the completion choices.
+;;
+;; There are 2 complementary sources of information for the
+;; completions: introspection into compiled .NET class libraries
+;; (like the base class library), and parse analysis from the
+;; semantic.el package, which is part of CEDET. In the typical
+;; case, this module uses both of those sources of information,
+;; together.
+;;
+;; The reflection is done by an inferior powershell shell running
+;; within emacs, that has loaded a custom .NET assembly. The
+;; library exposes static methods that perform type reflection,
+;; using the capabilities of the System.Reflection namespace. These
+;; methods then return strings which are lisp s-expressions that
+;; can be eval'd, resulting in structures containing information
+;; for a given type - including the available methods, fields and
+;; properties, and the attributes on same. This piece of the
+;; puzzle is called the "CscompShell".
+;;
+;; As an example, suppose your code has a local variable of type
+;; System.Xml.XmlDocument, named doc. Suppose the user asks for
+;; completion on that variable. The module uses the semantic parse
+;; data to identify the name and type of the local variable. It then
+;; sends a "GetTypeInfo" command to the CscompShell, passing
+;; System.Xml.XmlDocument. The CscompShell returns an s-expression
+;; enumerating the fields, methods and properties for that type.
+;; This s-expression is then used to populate the completion list.
+;;
+;;
+;; Here's a survey of the situations in which this module can offer
+;; completions:
+;;
+;; a. names of local variables, instance variables, and method arguments.
+;;
+;; void Method1(String longArgumentName)
+;; {
+;; long?
+;; }
+;;
+;; b. Methods, fields and properties (m/f/p) on a local variable
+;; with known type, on method arguments of known type, or on
+;; instance variables of known type:
+;;
+;; String x = "this is a string";
+;; x.?
+;;
+;; c. M/f/p on local variables with var type:
+;;
+;; var x = "this is a string";
+;; x.?
+;;
+;; d. Cascading local variable declarations of var type:
+;;
+;; var s = "This is a string";
+;; var length = s.Length;
+;; var radix = length.?
+;;
+;; e. completion on generic types:
+;;
+;; var x = new List<String>();
+;; x.?
+;;
+;; f. completion on local variables that are initialized
+;; from instance methods and variables.
+;;
+;; void method1()
+;; {
+;; var length = this.InstanceMethod();
+;; length.?
+;; }
+;;
+;; g. constructor completion, provide template when completing
+;;
+;; var x = new System.String(?
+;;
+;; h. constructor completion as above, with unqualified type.
+;;
+;; var x = new TimeSpan(?
+;;
+;; i. finding constructors among qualified and unqualified types
+;;
+;; var x = new TimeS?
+;;
+;; j. complete static methods on known types,
+;; whether fully qualified or unqualified.
+;;
+;; String.Jo?
+;;
+;; k. present template fpr static methods on known types,
+;; whether fully qualified or unqualified.
+;;
+;; String.Join(?
+;;
+;; l. Immediate values.
+;;
+;; 7.C?
+;;
+;; m. other compound Expressions:
+;;
+;; Path.GetRandomFileName().Normalize().?
+;;
+;;
+;;
+;; =======================================================
+;;
+;; Dependencies:
+;;
+;; cc-mode 5.31.?
+;;
+;; semantic.el 1.0pre7
+;;
+;; optionally, yasnippet, for snippet insertion. If the user has
+;; yasnippet loaded, then this completion module will insert
+;; a snippet (template) when the user selects a Method from the
+;; completion list menu. The method snippet will have all the
+;; method parameters as placeholders; the developer then types over
+;; those placeholders to supply the actual method parameters.
+;; If yasnippet is not loaded, then the completion is just
+;; the method name, and the developer has to fill in the
+;; param-list himself.
+;;
+;; PowerShell, and a separate DLL that must run in Powershell. That
+;; DLL is implemented in C#.
+;;
+;;
+;;
+;;
+;; Known bugs/problems :
+;;
+;; 1. The module does not do completion on anonymous (var) types in
+;; for loops.
+;;
+;;
+;;
+;; TODO :
+;;
+;; make an installer.
+;;
+;; Please send any comments, bugs, or upgrade requests to
+;; Dino Chiesa (dpchiesa@hotmail.com)
+;;
+
+
+(require 'csharp-shell)
+
+(require 'semantic-idle) ;; for ... reparsing a buffer
+
+
+;; Design notes:
+;;
+;; Tue, 04 May 2010 10:47
+;;
+;; This completion depends on the semantic package for parsing a C#
+;; buffer. That gives the module a way to interrogate the names and
+;; types of local and instance variables, in order to do completion on
+;; them.
+;;
+;; Semantic also provides the list of using statements for a C# module,
+;; which tells us which assemblies we need to search in, for
+;; completions. These are then loaded into CscompShell for interrogation.
+;;
+
+
+
+(defvar cscomp-current-list nil
+ "The list of all the completion. Each element of the list is
+either a string, or a list which the car is the possible completion,
+and the cadr is an additional information about this completion.")
+
+(defvar cscomp-current-list-index nil
+ "An index to an element in cscomp-current-list. This is used to
+cycle the list.")
+
+(defvar cscomp-current-fragment nil
+ "The current fragment we're trying to complete. This is used to trim the
thing that gets inserted.")
+
+(defvar cscomp-current-beginning (make-marker)
+ "The beginning of the region where the last completion was inserted.")
+
+(defvar cscomp-current-end (make-marker)
+ "The end of the region where the last completion was inserted.")
+
+(defvar cscomp-typeinfo-cache nil)
+
+(defcustom cscomp-typeinfo-cache-size 150
+ "The max size of completion buffer cache, in entries."
+ :group 'cscomp
+ :type 'integer)
+
+
+
+(defun cscomp-referenced-assemblies-list ()
+ "Return the list of .NET namespaces referenced in the
+current buffer via using statements. It uses the semantic parser
+table to find the 'using' statements. "
+ (interactive)
+ (if (not (semantic-active-p)) (semantic-new-buffer-fcn))
+ (let* ((tokens (semantic-fetch-tags))
+ ;;(usings (semantic-find-nonterminal-by-token (quote include) tokens))
+ (usings (semantic-brute-find-tag-by-class 'include tokens)))
+ (cscomp-log 3 "cscomp-referenced-assemblies-list: found using statements:
'%s'" usings)
+ (mapcar 'car usings)))
+
+
+
+(defun cscomp-instance-vars ()
+ "Return the list of instance variables in a C# module.
+This uses the semantic parser table to find the variable
+declarations.
+
+The return value is a list of semantic tags. Looks like:
+
+\((\"flavor\" variable
+ (:type \"int\")
+ (reparse-symbol class_member_declaration) #<overlay from 580 to
595 in a.cs>)
+ (\"flicky\" variable
+ (:type \"String\")
+ (reparse-symbol class_member_declaration) #<overlay from 604 to
636 in a.cs>)
+ (\"label\" variable
+ (:type \"String\")
+ (reparse-symbol class_member_declaration) #<overlay from 645 to
669 in a.cs>))
+
+"
+
+ (if (not (semantic-active-p)) (semantic-new-buffer-fcn))
+ (semantic-fetch-tags)
+ (let* ((class (cscomp-find-enclosing-csharp-class)) ;; the enclosing
class
+ (tokens (semantic-tag-type-members class)) ;; the
members of that class
+ (vars (semantic-brute-find-tag-by-class 'variable tokens))) ;; the
members that are variables
+ (cscomp-log 3 "cscomp-instance-vars: found instance vars: '%s'" vars)
+ vars))
+
+
+
+
+(defun cscomp-instance-members ()
+ "Return the list of instance members in a C# module.
+This uses the semantic parser table to find the memebr
+declarations.
+
+The return value is a list of semantic tags. Looks like:
+
+\((\"flavor\" variable
+ (:type \"int\")
+ (reparse-symbol class_member_declaration) #<overlay from 580 to
595 in a.cs>)
+ (\"Hello\" function
+ (:type \"String\")
+ (reparse-symbol class_member_declaration) #<overlay from 604 to
636 in a.cs>)
+ (\"label\" variable
+ (:type \"String\")
+ (reparse-symbol class_member_declaration) #<overlay from 645 to
669 in a.cs>))
+
+"
+
+ (if (not (semantic-active-p)) (semantic-new-buffer-fcn))
+ (semantic-fetch-tags)
+ (let ((class (cscomp-find-enclosing-csharp-class))) ;; the enclosing class
+ (semantic-tag-type-members class))) ;; the members
of that class
+
+
+
+
+(defun cscomp--find-matching-tags (name-fragment tagset &optional local)
+ "Return the list of tags from a given set, that
+match NAME-FRAGMENT. This is used by `cscomp-matching-local-vars',
+`cscomp-matching-instance-vars',
+`cscomp-matching-instance-members'.
+"
+ (let ((var-label (if local "(Variable) " "(Field) " ))
+ result)
+
+ (if tagset
+ (progn
+ (while tagset
+ (let* ((tag (car tagset))
+ (member-name (semantic-tag-name tag))
+ (member-type (semantic-tag-type tag))
+ (member-clazz (semantic-tag-class tag))
+ )
+
+ (if (eq 0 (string-match name-fragment member-name))
+ (let ((descrip
+ (cond
+ ((string= member-clazz "variable")
+ (concat var-label member-type))
+
+ ((string= member-clazz "function")
+ (let* ((arglist (semantic-tag-get-attribute tag
:arguments))
+ (modifiers (semantic-tag-modifiers tag))
+ (arg-descrip
+ (if (> (length arglist) 0)
+ (concat "("
+ (mapconcat
+ '(lambda (x) (concat
(semantic-tag-type x)
+ " "
+
(semantic-tag-name x)))
+ arglist ", ")
+ ")")
+ "()")))
+
+ (concat "(Method) "
+ " " (mapconcat 'identity modifiers " ") " "
+ arg-descrip
+ " returns "
+ member-type)))
+
+ (t ""))))
+
+
+ (cscomp-log 2 "cscomp-matching-tags: found %s (%s)"
+ member-name member-type)
+
+ (setq result (cons (list member-name descrip) result)))))
+
+ (setq tagset (cdr tagset)))
+ (cscomp-sort-completion-list result))
+ nil)))
+
+
+
+
+(defun cscomp-matching-instance-vars (name-fragment)
+ "Return the list of instance variables in a C# module, that
+match NAME-FRAGMENT. See also, `cscomp-matching-local-vars'.
+
+"
+ (cscomp--find-matching-tags name-fragment (cscomp-instance-vars)))
+
+
+
+
+(defun cscomp-matching-instance-members (name-fragment)
+ "Return the list of instance memebrs in a C# module, that
+match NAME-FRAGMENT.
+
+See also, `cscomp-matching-local-vars',
+`cscomp-matching-instance-vars'.
+
+"
+ (cscomp--find-matching-tags name-fragment (cscomp-instance-members)))
+
+
+
+(defun cscomp-matching-local-vars (name-fragment)
+ "Use the semantic lex/analysis results to find local variables
+that match the given NAME-FRAGMENT.
+
+See also, `cscomp-matching-instance-members',
+`cscomp-matching-instance-vars'.
+
+"
+ (cscomp-start-stripped-semantic)
+ (cscomp--find-matching-tags name-fragment (semantic-get-all-local-variables)
t))
+
+
+
+(defun cscomp-find-enclosing-csharp-class (&optional posn)
+ "returns a tag of type 'type (in other words, a c# class or struct) that
+encloses POSN, or point if POSN is nil. If there is no enclosing 'type,
+then return nil.
+"
+;; This isn't quite correct. At this time, the C# namespace defn gets
+;; parsed as a type. I couldn't figure how to get namespace to get
+;; parsed as a new 'namespace tag. Therefore, this fn can sometimes
+;; return a tag corresponding to a namespace, as opposed to a tag
+;; corresponding to a bonafide type.
+
+(let ((nar (semantic-current-tag-of-class 'type)))
+ nar))
+
+
+
+;; (defun cscomp-find-semantic-things (tag-class)
+;; "Search for tags in semantic parse state."
+;; (interactive "sTag type: ")
+;; (if (not (semantic-active-p)) (semantic-new-buffer-fcn))
+;; (let* ((tokens (semantic-fetch-tags))
+;; (matches (cscomp-find-by-class tag-class tokens t)))
+;; (if matches
+;; (while matches
+;; (let ((one (car matches)))
+;; (setq matches (cdr matches))))
+;; nil
+;; )))
+
+
+
+(defun cscomp-find-by-class (cls streamorbuffer &optional search-parts
search-includes)
+ "Find all tags with class CLS within STREAMORBUFFER.
+CLS is a string which is the name of the class of the tags returned, such as
+include, variable, type.
+See `semantic-tag-class'.
+Optional argument SEARCH-PARTS and SEARCH-INCLUDES are passed to
+`semantic-brute-find-tag-by-function'."
+ (semantic-brute-find-tag-by-function
+ (lambda (tag)
+ (let ((class (semantic-tag-class tag)))
+ (string= class cls)))
+ streamorbuffer search-parts search-includes))
+
+
+
+;; (lambda (tag)
+;; (let ((class (semantic-tag-class tag)))
+;; (if (and (listp ts)
+;; (or (= (length ts) 1)
+;; (string= (semantic-tag-class ts) type)))
+;;
+;; (setq ts (semantic-tag-name ts)))
+;; (equal type ts)))
+;; streamorbuffer search-parts search-includes))
+
+
+
+(defun cscomp-debugonly-list-all-local-variables ()
+ "fiddle with local variables and semantic."
+ (interactive)
+ (cscomp-start-stripped-semantic)
+ (semantic-lex (point-min) (point-max) 100)
+ (let ((locals (semantic-get-all-local-variables)))
+ (mapcar '(lambda (x)
+ (cscomp-log 3 "local var: name(%s) type(%s) pos(%s)"
+ (car x) (cadr (nth 2 x))
+ (nth 4 x)))
+ locals)))
+
+
+
+;; (defun cscomp-variables-in-scope ()
+;; "Return the list of variables currently in scope.
+;; It uses the semantic parser table to find them."
+;; (interactive)
+;; (if (not (semantic-active-p)) (semantic-new-buffer-fcn))
+;; (let* ((tokens (semantic-fetch-tags))
+;; (vars (semantic-brute-find-tag-by-class 'variable tokens)))
+;; (cscomp-log 3 "cscomp-variables-in-scope: found variables: '%s'" vars)
+;; (mapcar 'car vars)))
+
+
+;; (defun cscomp-valid-csharp-declaration-at (point varname)
+;; "Verify that a POINT starts a valid csharp declaration
+;; for the VARNAME variable."
+;; (save-excursion
+;; (goto-char point)
+;; (if (looking-at
+;; (concat "\\([A-Za-z0-9_.\177-\377]+\\)[ \t\n\r]+"
+;; (cscomp-double-backquotes varname)
+;; "[ \t\n\r]*[;=]"))
+;; (match-string 1)
+;; nil)))
+
+
+
+
+;; (defun cscomp-double-backslashes (varname)
+;; "Build a new string identical to VARNAME, except that every backslash
+;; `\' is doubled, so that it can be used in a regex expression.
+;; "
+;; (let (result (idx 0) (len (length varname)) curcar)
+;; (while (< idx len)
+;; (setq curcar (elt varname idx))
+;; (setq result (concat result (if (eq curcar ?\\)
+;; "\\\\"
+;; (make-string 1 curcar))))
+;; (setq idx (1+ idx)))
+;; result))
+
+
+
+
+;; (defun cscomp-declared-type-of (name)
+;; "Find in the current buffer the csharp type of the variable NAME. The
+;; function returns a string containing the name of the class, or nil
+;; otherwise. This function does not give the fully-qualified csharp class
+;; name, it just returns the type as it is declared."
+;; (save-excursion
+;; (let (found res pos orgpt resname)
+;; (while (and (not found)
+;; (search-backward name nil t))
+;; (setq pos (point))
+;; (backward-word 1)
+;; (setq resname (cscomp-valid-csharp-declaration-at (point) name))
+;; (goto-char pos)
+;; (forward-char -1)
+;; (if resname
+;; (progn (setq res resname)
+;; (setq found t))))
+;; res)))
+
+
+;; (defun cscomp-filter-fqn (importlist)
+;; "Filter all the fully-qualified classnames in the import list. It uses
+;; the knowledge that those classnames are at the beginning of the list,
+;; so that it can stops at the first package import (with a star `*' at
+;; the end of the declaration)."
+;; (if importlist
+;; (if (string= "*" (car (cdr (car importlist))))
+;; importlist
+;; (cscomp-filter-fqn (cdr importlist)))))
+
+
+
+(defun cscomp-escape-string-for-powershell (arg)
+ "Powershell uses the backquote for an escape char. This fn
+escapes the backquote for a string that will eventually be sent
+to powershell (CscompShell).
+
+I think this is only necessary when the arg is submitted to
+powershell within double-quotes. If the arg is within single
+quotes, backquotes do not need to be escaped.
+
+"
+ (let ((matcho (string-match "\\(.+\\)`\\(.+\\)" arg)))
+ (if matcho
+ (concat
+ (substring arg (match-beginning 1) (match-end 1))
+ "``"
+ (substring arg (match-beginning 2) (match-end 2)))
+ arg)))
+
+
+
+
+
+(defun cscomp-send-string-to-shell (command-string)
+ "sends a string to cscompshell function, returns the result."
+ (let ((result (csharp-shell-exec-and-eval-result command-string)))
+ (cscomp-log 2 "send-string-to-shell (%s) result(%s)" command-string
(prin1-to-string result))
+ ;;(if (and result (listp result)) result nil)
+ result
+ ))
+
+
+
+(defun cscomp-invoke-shell-fn (fn arg)
+ "invokes a 1-arg CscompShell function, returns the result."
+
+ ;; need to use single-quotes here around the arg, because in
+ ;; some cases the arg can have double-quotes in it.
+
+ (let* ((escaped-arg (cscomp-escape-string-for-powershell arg)))
+ (cscomp-send-string-to-shell (concat "[Ionic.Cscomp.Utilities]::" fn "(\'"
escaped-arg "\')"))))
+
+
+
+
+(defun cscomp-type-exists (typename)
+ "Determines if the given type is known by the CscompShell. You can provide
a short type name, or a fully-qualified name. You must have loaded the
assembly into the shell, for it to be known. See `cscomp-load-assembly'."
+ (interactive "sType name: ")
+ (cscomp-invoke-shell-fn "QualifyType" typename))
+
+
+
+(defun cscomp-get-members-of-class (semantic-tag)
+ "Gets the members of the class denoted by the SEMANTIC-TAG.
+If SEMANTIC-TAG is nil, then this function gets the closest type
+containing point, and gets the members of that.
+
+"
+ (if (null semantic-tag) (setq semantic-tag (cscomp-get-current-class)))
+ (if (eq (cadr semantic-tag) 'type)
+ (semantic-tag-type-members semantic-tag)
+ nil))
+
+
+
+
+(defun cscomp-get-current-class (&optional posn)
+ "Return the semantic tag for the current class or type in scope at POSN.
+"
+ (interactive)
+ (save-excursion
+ (if posn (goto-char posn))
+ (semantic-fetch-tags)
+ (let ((containing-type (semantic-current-tag-of-class 'type)))
+ containing-type)))
+
+
+(defun cscomp-produce-csharp-arglist-block-from-dbrecord (arglist)
+ "Produces an argument list block, suitable for framing within parens
+in a method declaration, from ARGLIST, a list of local arguments obtained from
+`semantic-get-local-arguments'.
+
+When the format of ARGLIST is like this:
+
+ ((\"count\"
+ variable
+ (:type \"int\")
+ (:filename \"c:/dinoch/dev/dotnet/CsharpCompletion.cs\"
+ reparse-symbol formal_parameters)
+ [621 630])
+ (\"melvin\"
+ variable
+ (:type \"string\")
+ (:filename \"c:/dinoch/dev/dotnet/CsharpCompletion.cs\"
+ reparse-symbol formal_parameters)
+ [631 645]))
+
+The return value is like this:
+
+ int count, string melvin
+
+When the arglist is empty, the return value is a string of zero length.
+
+
+"
+ (let ((fragment ""))
+ (while arglist
+ (let* ((x (car arglist))
+ (var-name (car x))
+ (var-type (cadr (nth 2 x))))
+
+ (setq fragment (concat fragment var-type " " var-name)
+ arglist (cdr arglist))
+ (if arglist
+ (setq fragment (concat fragment ", ")))))
+ fragment))
+
+
+
+
+(defun cscomp-produce-instance-member-code-fragment (member-list)
+ "Produce a C# fragment that defines placeholder instance members,
+to be inserted into a class template which is then compiled, so that
+Cscomp can inspect the resulting IL to determine the type of a local var
+on which the user is asking for completion.
+
+Cscomp uses the compiler to determine the type of the var. It
+dynamically generates and compiles a class with the same variable
+declaration as the code being edited.
+
+The initialization of the var may depend on instance members. If
+so, the compiled class must provide instance members of the
+correct type to satisfy those dependencies. This function
+generates C# code to provide those instance members.
+
+For input that looks like this:
+
+ ((\"staticField1\" variable
+ (:typemodifiers (\"private\" \"static\") :type \"int\")
+ (reparse-symbol class_member_declaration)
+ #<overlay from 605 to 642 in CsharpCompletion.cs>)
+ (\"InstanceMethod1\" function
+ (:arguments (...) :type \"string\")
+ (reparse-symbol class_member_declaration)
+ #<overlay from 652 to 741 in CsharpCompletion.cs>)
+ (\"Run\" function
+ (:typemodifiers (\"public\") :arguments (...) :type \"void\")
+ (reparse-symbol class_member_declaration)
+ #<overlay from 752 to 1487 in CsharpCompletion.cs>)
+ (\"Main\" function
+ (:typemodifiers (\"public\" \"static\") :arguments (...) :type \"void\")
+ (reparse-symbol class_member_declaration)
+ #<overlay from 1497 to 1806 in CsharpCompletion.cs>)
+ )
+
+The output will look like this:
+
+ private static int staticField1 = default(int);
+ string InstanceMethid(...) { return default(string); }
+
+Any void methods will not be emitted because they cannot affect the
+types of local variables declared in methods.
+
+"
+ (let ((synthetic-code ""))
+
+ (while member-list
+ (let* ((x (car member-list))
+ (member-name (car x))
+ (member-flavor (cadr x))
+ (member-type (semantic-tag-get-attribute x :type))
+ (member-modifiers (semantic-tag-get-attribute x :typemodifiers))
+ one-frag
+ )
+ ;; (message "n(%s) f(%s) t(%s)"
+ ;; member-name
+ ;; member-flavor
+ ;; member-type)
+
+ (setq one-frag
+ (cond
+
+ ((string= member-type "void") ;; the member is a void type
+ "") ;; emit nothing, don't need it.
+ ;; it's possible we might need it, in which case,
+ ;; we can just emit an empty fn.
+
+ ((eq member-flavor 'function) ;; it's a method
+ (concat
+ (mapconcat 'identity member-modifiers " ")
+ " "
+ member-type
+ " "
+ member-name
+ "("
+ (cscomp-produce-csharp-arglist-block-from-dbrecord
+ (semantic-tag-get-attribute x :arguments))
+ ") {"
+ (if member-type
+ (concat
+ " return default("
+ member-type
+ ");")
+ "")
+ "} "))
+
+
+ ((eq member-flavor 'variable) ;; it's an instance variable
+
+ (concat
+ (mapconcat 'identity member-modifiers " ")
+ " "
+ member-type
+ " "
+ member-name
+ " = default("
+ member-type
+ "); "))
+
+ (t
+ "")))
+
+ (setq synthetic-code (concat synthetic-code one-frag)))
+ (setq member-list (cdr member-list)))
+ synthetic-code))
+
+
+(defun cscomp-consolidate-whitespace (s)
+ "Collapse consecutive whitespace characters in the given string S
+to a single space.
+"
+ ;; trim leading spaces
+ (if (string-match "^\\([ \t\n\r\f\v]+\\)" s)
+ (setq s (substring s (match-end 1))))
+
+ ;; collapse multiple whitespace into one
+ (while (string-match "\\([ \t\n\r\f\v]\\{2,\\}\\)" s)
+ (setq s
+ (concat
+ (substring s 0 (match-beginning 1))
+ " "
+ (substring s (match-end 1)))))
+ s)
+
+
+(defun cscomp-escape-single-quotes (s)
+ "Escape single-quotes in the given string S. This is for use within
+powershell.
+"
+ ;; escape single-quotes
+ (while (string-match "\\(.+\\)'\\(.+\\)" s)
+ (setq s
+ (concat
+ (substring s 0 (match-beginning 1))
+ "`'"
+ (substring s (match-end 1)))))
+ s)
+
+
+;;(setq cscomp-log-level 2)
+
+
+(defun cscomp-get-var-type-given-decl (var-declaration
+ var-index
+ classname
+ arglist
+ instance-members)
+ "Determines the type of the var declared in the given declaration.
+VAR-DECLARATION is the C# code that declares all the local vars up to
+and including the local var of interest.
+
+VAR-INDEX is the zero-based index of the local arg in that list,
+that is of interest. The initialization of that local var may
+depend on the prior local vars, which is why we need the entire
+var declaration list.
+
+CLASSNAME is the name of the class in which the local vars
+appear. This is used in the generated (synthetic) code, in case
+there is a reference to a class-static member.
+
+ARGLIST is a string with the arglist for the method that contains the
+local variable in question. This will satisfy dependencies on
+local arguments in the initializer for the var, if any.
+
+INSTANCE-MEMBERS is a C# code fragment defining instance members for the
+synthetic class. This will satisfy dependencies on instance members in
+the initializer for the var, if any.
+
+"
+ (let* ((massaged-decl
+ (cscomp-escape-single-quotes
+ (cscomp-consolidate-whitespace var-declaration)))
+ (namespaces (mapconcat 'identity (cscomp-referenced-assemblies-list)
","))
+ (command-string
+ (concat "[Ionic.Cscomp.Utilities]::GetTypeGivenVarDecl('"
+ massaged-decl
+ "','"
+ namespaces
+ "','" ;; for now, no additional assembly references
+ "',"
+ (number-to-string var-index)
+ ",'"
+ classname
+ "','"
+ arglist
+ "','"
+ (cscomp-consolidate-whitespace instance-members)
+ "')" )))
+ (cscomp-send-string-to-shell command-string)))
+
+
+
+
+(defun cscomp-get-completions-for-namespace (ns)
+ "Gets a list of known completions (types and child namespaces)
+for the given namespace. You must have loaded an assembly containing
+types from the namespace, into the shell, for it to be known.
+See `cscomp-load-assembly'."
+ (interactive "sNamespace: ")
+ (cscomp-invoke-shell-fn "GetCompletionsForNamespace" ns))
+
+
+
+(defun cscomp-qualify-name (name)
+ "determines if the thing is a type, namespace, or ...?
+
+Result is
+
+ (list \"type\" name)
+
+ or
+
+ (list \"namespace\" name)
+
+ or
+
+ (list \"unknown\" name)
+
+"
+
+ (interactive "sname to qualify: ")
+ (cscomp-invoke-shell-fn "QualifyName" name))
+
+
+(defun cscomp-get-matches (fragment)
+ "returns a list of all possible matches on a given partial name.
+The return value is like this:
+
+ (list (list \"type\" \"fullNameOfType\")
+ (list \"namespace\" \"FullNameOfNamespace\"))
+
+"
+ (interactive "sfragment to match on: ")
+
+ (let ((namespaces (mapconcat 'identity (cscomp-referenced-assemblies-list)
",")))
+
+ (cscomp-send-string-to-shell
+ (concat "[Ionic.Cscomp.Utilities]::GetMatches('"
+ fragment
+ "','"
+ namespaces
+ "')"))))
+
+
+
+(defun cscomp-load-additional-assemblies (lib-list)
+"Loads a set of assemblies into the csharp-shell, which then allows Cscomp
+to do completion (etc) on the types in those libraries."
+ (mapcar 'cscomp-load-assembly lib-list))
+
+
+(defun cscomp-load-assembly (lib)
+ "Loads a assembly into the csharp-shell, which then allows Cscomp
+to do completion (etc) on the types in that library."
+ (interactive "sLibrary: ")
+ (cscomp-invoke-shell-fn "LoadOneAssembly" lib))
+
+
+
+(defun cscomp-get-qualified-name (name)
+ "Guess the fully qualified name of the class NAME, using the
+list of referenced assemblies. It returns a string if the fqn
+was found, or null otherwise."
+ (interactive "sType name: ")
+ (cscomp-log 1 "get-qualified-name (%s)" name)
+ (let ((result (cscomp-type-exists name)))
+ (if result result
+ ;; else
+ (let ((usinglist (cscomp-referenced-assemblies-list))
+ fullname namespace )
+
+ ;; usinglist is like this:
+ ;; ("System" "System.Collections" "System.Collections.Generic"
"System.Reflection")
+
+ (setq result nil)
+ (while usinglist
+ (setq namespace (car usinglist))
+ (cscomp-load-assembly namespace)
+ (setq fullname (concat namespace "." name))
+ (cscomp-log 2 "checking this type: '%s'" fullname)
+ (if (cscomp-type-exists fullname)
+ (setq result fullname
+ usinglist nil)
+ (setq usinglist (cdr usinglist))))
+
+ (cscomp-log 1 "get-qualified-name rtns: '%s'" result)
+
+ result))))
+
+
+
+;; (defun cscomp-flush-typeinfo-cache ()
+;; "Flushes all entries in the completion cache"
+;; (interactive)
+;; (setq cscomp-typeinfo-cache nil))
+;;
+;;
+;; (defun cscomp-flush-classes-in-cache (class-list)
+;; "Flushes all the classes in CLASS-LIST as entries of cache."
+;; (let ((temp (nth 0 cscomp-typeinfo-cache))
+;; (index -1)
+;; (found nil)
+;; (class (car class-list)))
+;; (while class
+;; (while (and temp (not found))
+;; (setq index (1+ index))
+;; (setq temp (nth index cscomp-typeinfo-cache))
+;; (if (string= (car temp) class)
+;; (setq found t)))
+;; (if found
+;; (setq cscomp-typeinfo-cache
+;; (nthcdr (1+ index) cscomp-typeinfo-cache)))
+;; (setq class-list (cdr class-list))
+;; (setq class (car class-list))
+;; (setq found nil))))
+
+
+(defun cscomp-add-to-typeinfo-cache (name typeinfo)
+ (let (new-entry new-list)
+ (if (nth cscomp-typeinfo-cache-size cscomp-typeinfo-cache)
+ (progn
+ (setq new-entry (list name typeinfo))
+ (setq new-list (list new-entry nil))
+ (setcdr new-list (cdr cscomp-typeinfo-cache))
+ (setq cscomp-typeinfo-cache new-list)
+ (cscomp-log 1 "cache is full") )
+ ;;else
+ (setq cscomp-typeinfo-cache
+ (append
+ cscomp-typeinfo-cache
+ (list (list name typeinfo)))))))
+
+
+(defun cscomp-get-typeinfo-from-cache (name)
+ (let ((temp (nth 0 cscomp-typeinfo-cache)) (index -1) (found nil))
+ (while (and temp (not found))
+ (setq index (1+ index))
+ (cscomp-log 2 "looking at cache item %d" index)
+ (setq temp (nth index cscomp-typeinfo-cache))
+ (if (string= (car temp) name)
+ (setq found t)))
+ (if found
+ (progn
+ (cscomp-log 3 "cscomp-get-typeinfo-from-cache: HIT name(%s) r(%s)"
+ name (prin1-to-string (nth 1 temp)))
+ (nth 1 temp))
+ (cscomp-log 1 "cscomp-get-typeinfo-from-cache: MISS name(%s)" name)
+ nil)))
+
+
+(defun cscomp-get-typeinfo (name)
+ "Return the class info list for the class NAME. This function first
+checks to see if the class info is cached. If so, it returns the
+cached class info. Otherwise, it creates the type info list. Each
+element of the list returned by this function is itself a list whose
+car is a possible completion and whose cdr gives additional
+informations on the completion - the property type, or the param
+list and return type for a method, etc."
+ (interactive "sTypename: ")
+
+ (cscomp-log 2 "cscomp-get-typeinfo name(%s)...trying cache..." name)
+
+ (let ((type-info (cscomp-get-typeinfo-from-cache name))
+ (usinglist (cscomp-referenced-assemblies-list))
+ namespace
+ qualified-type )
+
+ ;; load all the assemblies mentioned in the using clauses
+ (while usinglist
+ (setq namespace (car usinglist))
+ (cscomp-load-assembly namespace)
+ (setq usinglist (cdr usinglist)))
+
+ (if (null type-info)
+ (progn
+ (setq qualified-type
+ (csharp-shell-exec-and-eval-result (concat
+ "[Ionic.Cscomp.Utilities]::QualifyType('"
+ ;;(cscomp-escape-string-for-powershell
name)
+ ;; dont need to escape if using single
quotes
+ name
+ "')")))
+
+ (cscomp-log 1 "cscomp-get-typeinfo...(%s)..." (prin1-to-string
qualified-type))
+
+ (if qualified-type
+ (setq type-info
+ (csharp-shell-exec-and-eval-result (concat
"[Ionic.Cscomp.Utilities]::GetTypeInfo('"
+ (car qualified-type) ; type
name
+ "', '"
+ (cadr qualified-type) ;
assembly name
+ "')" ))))
+ (if type-info
+ (cscomp-add-to-typeinfo-cache name type-info))))
+
+ type-info))
+
+
+(defun cscomp-get-type-ctors (type-name)
+ "Retrieve constructors from CscompShell for the type named by TYPE-NAME.
+"
+ (interactive "sType name: ")
+ (cscomp-invoke-shell-fn "GetConstructors" type-name))
+
+
+
+
+(defun cscomp-split-by-dots (s)
+ "When the string contains a dot, this fn returns a 2-element
+list (TOKEN1 TOKEN2). TOKEN1 is the substring of s that precedes
+the last dot. TOKEN2 is the substring that follows the last dot.
+
+When the string does not contain a dot, this fn returns a
+2-element list in which the first element is nil and the 2nd
+element is the entire string.
+
+"
+ (cscomp-log 2 "cscomp-split-by-dots: s[%s]" s)
+ (if (string-match "\\(.*\\)\\.\\(.*\\)" s)
+ (let ((result (list (match-string 1 s) (match-string 2 s))))
+ (cscomp-log 2 "cscomp-split-by-dots: %s" (prin1-to-string result))
+ result)
+ (list nil s))) ;; it's a single atom
+
+
+
+
+(defun cscomp-parse-csharp-expression-before-point ()
+ "Parses the text at point, and returns the results.
+
+The retval is a list (POSN (TOKEN1 TOKEN2)) , where POSN is the position
+in the buffer of the beginning of TOKEN1 and TOKEN1 and TOKEN2
+are the two tokens surrounding the prior dot.
+
+For example, suppose System.Diagnostics.D were the name at
+point. This function would return the list
+
+ (888 (\"System.Diagnostics\" \"D\"))
+
+...if 888 was the position of the beginning of the word System.
+
+It's just an exercise in syntactically-aware string parsing.
+
+If the text preceding point doesn't look like two tokens, this fn
+returns nil.
+
+"
+ (cscomp-log 2 "parse-csharp-expression-before-point: point(%d)" (point))
+
+ (interactive)
+ (save-excursion
+ (let ((opoint (point))
+ (cycle-count 0)
+ (dot-count 0)
+ m1
+ (regex "[-\\+\\*\\/%,;( })=\\.]")
+ snip done
+ (paren-depth 0)
+ ;; (skip-back '(lambda ()
+ ;; (skip-chars-backward "\t ")
+ ;; (backward-char)))
+ )
+
+
+ (while (not done)
+
+ (skip-chars-backward "\t ")
+
+ (cond
+
+ ((and (eq cycle-count 0) ;; first time through
+ (eq (char-before) 40)) ;; 40 = open paren - means we want fn
completion
+ (backward-char)) ;; backup
+
+ ((eq (char-before) 34) ;; 34 = doublequote
+ (backward-char 1) ;; move back into the quoted string
+ (let ((limits (c-literal-limits)))
+ (if (consp limits)
+ (c-safe (goto-char (car limits)))))) ;; goto beginning of
literal string
+
+ ;; ((eq (char-before) 41) ;; 41 = close paren - means
we want fn completion
+ ;; (incf paren-depth)
+ ;; (backward-char))
+ ;;
+ ;; ((and
+ ;; (eq (char-before) 40) ;; 40 = open paren
+ ;; (> paren-depth 0)) ;; we have a pending close
paren (moving backwards)
+ ;; (decf paren-depth)
+ ;; ;;(funcall skip-back))
+ ;; (backward-char))
+
+ ;; ((or
+ ;; (eq (char-before) 32) ;; space
+ ;; (eq (char-before) ?\t)) ;; tab
+ ;; (funcall skip-back))
+
+ ((re-search-backward regex nil t)
+ (cond
+
+ ((eq (char-after) 41) ;; 41 = close paren - means we want fn
completion
+ (incf paren-depth)
+ t)
+
+ ((and
+ (eq (char-after) 40) ;; 40 = open paren
+ (> paren-depth 0)) ;; we have a pending close paren (moving
backwards)
+ (decf paren-depth)
+ ;;(funcall skip-back))
+ t)
+
+
+ ((or
+ (eq (char-after) 32) ;; space
+ (eq (char-after) ?\t)) ;; tab
+ t) ;; do nothing
+
+ ((eq (char-after) ?.)
+ (setq m1 (point)
+ regex "[-\\+\\*\\/%,;( })=]"))
+
+ (t
+ (setq done t))))
+
+ (t
+ (backward-char)))
+
+ (incf cycle-count)) ;; count of steps backward
+
+ (setq snip
+ (buffer-substring-no-properties (1+ (point)) opoint))
+ (cscomp-log 2 "parse-expression-before-point: B snip(%s)" snip)
+ (list (1+ (point)) (cscomp-split-by-dots snip)))
+ ))
+
+
+
+
+(defun cscomp-qualify-local-var (symbol opoint)
+ "Use the semantic lex/analysis results to classify the
+name as a local variable. Returns a list, 1st elt is the
+variable type, and the second elt is a vector, containing
+the position of its definition.
+
+See also `cscomp-qualify-instance-var'.
+"
+ (cscomp-start-stripped-semantic)
+ ;;(semantic-lex (point-min) (point-max) 100)
+ (let ((locals (semantic-get-local-variables))
+ (args (semantic-get-local-arguments))
+ (result nil)
+ (decl-count 0)
+ (prior-var-decls ""))
+
+ (while (and locals (not result))
+ (let* ((x (car locals))
+ (var-name (car x))
+ (var-type (cadr (nth 2 x)))
+ (var-pos (nth 4 x))) ;; pair: start and end of var decl
+
+ ;; The simple string= test will give a false positive if there's
+ ;; a foreach loop variable in a prior
+ ;; foreach loop with the same name as the one the user
+ ;; wants completion on.
+ ;;
+ ;; This is only an issue with two or more foreach loops, each of
+ ;; which create a separate naming scope, and which have the same
+ ;; variable name. All those foreach variables will be reported
+ ;; as "local variables" by semantic. But, not all of them are in
+ ;; scope for the completion we're performing right now.
+ ;;
+ ;; This needs to do something more intelligent with
+ ;; the declaration. If I had a way to interrogate the scope of
+ ;; the variable decl, that would help. But right now I don't have
+ ;; that.
+
+ ;; I think I need a better understanding of the semantic.el
+ ;; package.
+ ;; =======================================================
+
+
+ ;; if this decl ends *before* the point at which the user is
+ ;; asking for completion.
+ (if (< (elt var-pos 1) opoint)
+
+ ;; If this var decl is the same name as the one
+ ;; we want.
+ (if (string= var-name symbol)
+ (progn
+
+ ;; Handle var types - need to determine the actual type.
+ ;;
+ ;; To do that, compile the var declaration, then inspect the
IL
+ ;; to determine the var types.
+ ;;
+ ;; This engine will determine the var type, in some portion%
of
+ ;; the cases.
+ ;;
+ ;; It will handle:
+ ;;
+ ;; - simple var declarations that have no dependencies on
other vars
+ ;; - cascaded var decls that depend on other local vars.
+ ;;
+ ;; For now, it will not handle:
+ ;;
+ ;; - var that depends on a method argument
+ ;; - var whose initialization depends on an instance var
+ ;; - var decls in foreach loops
+ ;;
+
+ ;; if the type of the variable is "var"
+ (if (string= var-type "var")
+ (let* ((this-decl
+ (buffer-substring-no-properties (elt var-pos 0)
(elt var-pos 1)))
+
+ (containing-type (cscomp-get-current-class))
+ (member-list (cscomp-get-members-of-class
containing-type))
+ (name-of-containing-type (car containing-type))
+
+ (inferred-type
+ (cscomp-get-var-type-given-decl
+ (concat prior-var-decls " " this-decl)
+ decl-count
+ name-of-containing-type
+
(cscomp-produce-csharp-arglist-block-from-dbrecord args)
+ (cscomp-produce-instance-member-code-fragment
member-list)
+ )))
+
+ (if (and inferred-type (string= (car inferred-type)
"type"))
+ (setq var-type (cadr inferred-type))
+ (message "%s" (prin1-to-string inferred-type))
+ )))
+
+ (cscomp-log 2 "cscomp-qualify-local-var: found %s (%s)"
+ symbol var-type)
+ (setq result (list var-type var-pos)))
+
+ ;; else - remember it. We may need it later
+ (let* ((this-var-decl
+ (buffer-substring-no-properties (elt var-pos 0) (elt
var-pos 1)))
+ (tokens (split-string this-var-decl "[ \t]" t)))
+
+ ;; include decl in prior decls if not "foreach(var foo in X)"
+ (if (or (< (length tokens) 4)
+ (not (string= (nth 3 tokens) "in")))
+ (setq prior-var-decls (concat prior-var-decls " "
this-var-decl)
+ decl-count (1+ decl-count))
+
+ ;; else - it's a foreach loop
+
+ ;; If performing completion on a var within the loop that
+ ;; depends on the loop variable, then we need to infer
+ ;; the type of the foreach loop variable here.
+ ;;
+ ;; But, I'm punting.
+
+ )))))
+ (setq locals (cdr locals)))
+
+ result))
+
+
+
+
+
+(defun cscomp-qualify-instance-var (symbol)
+ "Use the semantic lex/analysis results to classify the
+name as an instance variable. Returns a list, 1st elt is the
+variable type, and the second elt is a vector, containing
+the position of its definition.
+
+See also `cscomp-qualify-local-var'.
+
+"
+ (cscomp-start-stripped-semantic)
+ ;;(semantic-lex (point-min) (point-max) 100)
+ (let ((ivars (cscomp-instance-vars))
+ (result nil))
+ (while (and ivars (not result))
+ (let* ((x (car ivars))
+ (var-name (car x))
+ (var-type (cadr (nth 2 x)))
+ (var-pos (nth 4 x)))
+ (if (string= var-name symbol)
+ (progn
+ (cscomp-log 2 "cscomp-qualify-instance-var: found %s (%s)"
+ symbol var-type)
+ (setq result (list var-type var-pos))))
+ (setq ivars (cdr ivars))))
+ result))
+
+
+
+
+(defun cscomp-start-stripped-semantic ()
+ "Enable a stripped-down semantic for usage with csharp-completion.
+Semantic is very ambitious and tries to do many things, including
+predictive completion, modified code formatting, and other
+things. We don't want all that for this simple completion
+module. So this method starts a stripped-down version of
+semantic.
+
+"
+ (interactive)
+
+ (if (null semantic-load-system-cache-loaded)
+ (progn
+ ;;(semantic-lex (point-min) (point-max) 100)
+ (semantic-fetch-tags)
+ (global-semantic-idle-scheduler-mode 1)
+ ;;(global-semanticdb-minor-mode 1)
+ ;; This loads any created system databases which get linked into
+ ;; any searches performed.
+ (setq semantic-load-system-cache-loaded t)
+ )))
+
+
+
+
+
+;; (let (start
+;; varname
+;; (curcar (char-before))
+;; found
+;; (original-point (point))
+;; intermediate-point
+;; beg-point
+;; first-part
+;; second-part
+;; (bracket-count 0)
+;; (paren-count 0))
+;;
+;;
+;; (while (null found)
+;; (cond
+;;
+;; ;; car is a-z, A-Z 0-9 or greater than 127, or slash or underscore
+;; ((or (and (>= curcar ?a) (<= curcar ?z))
+;; (and (>= curcar ?A) (<= curcar ?Z))
+;; (and (>= curcar ?0) (<= curcar ?9))
+;; (>= curcar 127)
+;; (member curcar '(?_ ?\\ )))
+;; ;; back up!
+;; (forward-char -1))
+;;
+;; ;; curchar is a dot
+;; ((eq ?. curcar)
+;; (setq found (point)))
+;;
+;; ;; else
+;; (t
+;; (setq found t)))
+;;
+;;
+;; (setq curcar (char-before)))
+;;
+;; ;; we've backed-up to...the nearest dot or non- alphanumeric char.
+;;
+;;
+;; ;; ??
+;; (setq intermediate-point (point))
+;;
+;;
+;; (if (not (eq t found)) ;; not t means we found a dot
+;; (progn
+;;
+;; ;; get the char before
+;; (setq curcar (char-before))
+;; (while (or (and (>= curcar ?a) (<= curcar ?z))
+;; (and (>= curcar ?A) (<= curcar ?Z))
+;; (and (>= curcar ?0) (<= curcar ?9))
+;; (>= curcar 127)
+;; (and (eq curcar ? ) (or (< 0 paren-count) (< 0
bracket-count)))
+;; (member curcar '(?\. ?\_ ?\\ ?\( ?\) ?\, ?\[ ?\])))
+;; (cond
+;; ((eq curcar ?\) )
+;; (setq paren-count (1+ paren-count)))
+;; ((eq curcar ?\( )
+;; (setq paren-count (1- paren-count)))
+;; ((eq curcar ?\] )
+;; (setq paren-count (1+ bracket-count)))
+;; ((eq curcar ?\[ )
+;; (setq paren-count (1- bracket-count))))
+;; (forward-char -1)
+;;
+;; (setq curcar (char-before)))
+;;
+;;
+;; (setq beg-point (point))
+;;
+;; (set-marker cscomp-current-beginning intermediate-point)
+;;
+;; (set-marker cscomp-current-end original-point)
+;;
+;; (setq first-part (buffer-substring-no-properties beg-point (-
intermediate-point 1)))
+;;
+;; (setq first-part (cscomp-isolate-to-complete first-part))
+;;
+;; (string-match " *\\(.*\\)" first-part)
+;;
+;; (setq first-part (substring first-part (match-beginning 1)
(match-end 1)))
+;;
+;; (setq second-part (buffer-substring-no-properties
intermediate-point original-point))
+;;
+;; (list first-part second-part))
+;;
+;; nil))))
+
+
+
+(defun cscomp-build-clist-for-ns (nsinfo)
+ "Build a completion list from the NSINFO list, as returned by the
+Ionic.Cscomp.Utilities.GetCompletionsForNamespace function.
+
+If the incoming NSINFO list looks like this:
+
+ (\"System.Collections.Generic\"
+ (types (\"ArraySortHelper`1\"
+ \"ArraySortHelper`2\"
+ \"ByteEqualityComparer\"
+ \"Comparer`1\"
+ \"Dictionary`2\"
+ \"EqualityComparer`1\"))
+ (namespaces (\"Something1\"
+ \"Something2`2\")))
+
+Then the return value is like this:
+
+ (\"ArraySortHelper<T1,T2> | (type)\"
+ \"ArraySortHelper<T1> | (type)\"
+ \"ByteEqualityComparer | (type)\"
+ \"Comparer<T1> | (type)\"
+ \"Dictionary<T1,T2> | (type)\"
+ \"EqualityComparer<T1> | (type)\"
+ \"Something1 | (type)\"
+ \"Something2`2 | (type)\")
+
+"
+ (let* ((typelist (cadr (cadr nsinfo)))
+ (nslist
+ (mapcar '(lambda (item) (concat item " | (namespace)") )
+ (cadr (caddr nsinfo)))))
+ (cscomp-fix-generic-method-strings typelist)
+ (setq typelist (mapcar '(lambda (item) (concat item " | (type)") )
+ typelist))
+ ;;(reverse (sort (append typelist nslist) 'string-lessp))
+ (sort (append typelist nslist) 'string-lessp)
+ ))
+
+
+
+(defun string-replace-char (s c1 c2)
+ "Replace all occurrences of char C1 in string with char C2.
+Return the modified string."
+ (let ((string-len (length s))
+ (ix 0))
+ (while (< ix string-len)
+ (if (eq (aref s ix) c1)
+ (aset s ix c2))
+ (incf ix)))
+ s)
+
+
+
+(defun cscomp-fix-one-generic-method-string (descrip)
+ "Reformat the generic type string like System.Action`1
+into a C#-friendly System.Action<T1> . Returns the reformatted
+string. Returns nil if reformat was unnecessary.
+"
+ (cond
+
+ ;; input: System.Converter`2[T,TOutput]
+ ;; output: System.Converter<T,TOutput>
+ ((string-match "^\\(.+\\)`[1-9]\\[\\([^]]+\\)\\]$" descrip)
+ (concat
+ (match-string 1 descrip)
+ "<"
+ (match-string 2 descrip)
+ ">"))
+
+ ;; input: System.Collections.Generic.Dictionary`2
+ ;; output: System.Collections.Generic.Dictionary<T1,T2>
+ ((string-match "^\\(.+\\)`\\([1-9]\\)\\(.*\\)$" descrip)
+ (let ((z (string-to-number (match-string 2 descrip)))
+ (new-name (concat (match-string 1 descrip) "<"))
+ (i 0))
+ (while (< i z)
+ (setq i (1+ i)
+ new-name (concat new-name (format "T%d" i)))
+ (if (< i z)
+ (setq new-name (concat new-name ","))))
+ (setq new-name (concat new-name ">" (match-string 3 descrip)))))
+
+ ;; anything else
+ (t nil)))
+
+
+
+(defun cscomp-fix-generic-method-strings (clist)
+ "Fixup a list of strings, that may have generic method types
+in them, to have C#-friendly formats. This fn does the modifications
+in place. The return value is the original list, with the internal values
+modified.
+
+"
+ (let ((count (list-length clist))
+ (n 0)
+ new-name
+ cur-elt)
+ (while (< n count)
+ (setq cur-elt (nth n clist))
+ (if (setq new-name (cscomp-fix-one-generic-method-string cur-elt))
+ (setf (nth n clist) new-name))
+ ;; the following will affect the string in the list
+ (string-replace-char cur-elt ?+ ?.)
+ (setq n (1+ n))))
+ clist)
+
+
+
+(defun cscomp-fix-generics-in-descrip-line (descrip-line)
+ "Fixup a string, a description of a method or property, to
+have C#-friendly generic types.
+
+Eg, convert (Method) public (System.Converter`2[T,TOutput] converter) returns
List`1[TOutput]
+
+to (Method) public (System.Converter<T,TOutput> converter) returns
List<TOutput>
+
+"
+ (let ((tokens (split-string descrip-line "[ \t]" t)))
+ (cscomp-fix-generic-method-strings tokens)
+ (mapconcat 'identity tokens " ")))
+
+
+
+
+
+(defun cscomp-build-clist-for-type (typeinfo)
+ "Build a completion list from the TYPEINFO list, as returned by the
+Ionic.Cscomp.Utilities.GetTypeinfo function in the CscompShell. The list
+is used when performing completions on an instance of a given type.
+
+For input that looks like this:
+
+ (\"System.IO.DriveInfo\" 'type
+ ((\"Name\" 'property \"System.String\" (typemodifiers
\"readonly\" \"public\"))
+ (\"AvailableFreeSpace\" 'property \"System.Int64\" (typemodifiers
\"readonly\" \"public\"))
+ (\"TotalFreeSpace\" 'property \"System.Int64\" (typemodifiers
\"readonly\" \"public\"))
+ (\"VolumeLabel\" 'property \"System.String\" (typemodifiers
\"public\")))
+
+ ((\"Equals\" method \"System.Boolean\" (\"System.Object obj\")
(typemodifiers \"public\"))
+ (\"GetDrives\" method \"System.IO.DriveInfo[]\" nil (typemodifiers
\"public\" \"static\"))
+ (\"ToString\" method \"System.String\" nil (typemodifiers \"public\"))))
+
+
+The output looks like this:
+
+ ((\"ToString\" \"(Method) public () returns System.String\")
+ (\"GetDrives\" \"(Method) public static () returns
System.IO.DriveInfo[]\")
+ (\"Equals\" \"(Method) public (System.Object obj) returns
System.Boolean\")
+ (\"VolumeLabel\" \"(Property) public System.String\")
+ (\"TotalFreeSpace\" \"(Property) readonly public System.Int64\")
+ (\"AvailableFreeSpace\" \"(Property) readonly public System.Int64\")
+ (\"Name\" \"(Property) readonly public System.String\"))
+
+"
+
+ (let (result
+ (tname (car typeinfo))
+ (props (caddr typeinfo))
+ (methods (cadddr typeinfo))
+ (fields (caddr (cddr typeinfo)))
+ modifiers)
+
+ (cscomp-log 2 "cscomp-build-clist-for-type: typename(%s)" tname)
+
+ (while props
+ (let ((one-prop (car props) ) )
+ (setq modifiers
+ (mapconcat 'identity (cdr (nth 3 one-prop)) " "))
+ (setq result
+ (append (list
+ (list
+ (car one-prop) ;; the name of the property
+
+ ;; additional information about the property
+ (concat "(Property) "
+ modifiers ;; modifiers on this prop
+ " "
+ (nth 2 one-prop) ;; type of the prop
+ )))
+ result)))
+ (setq props (cdr props)))
+
+ (while methods
+ (let ((one-method (car methods)) params)
+ (setq modifiers
+ (mapconcat 'identity (cdr (nth 4 one-method)) " "))
+ (setq params
+ (if (nth 3 one-method)
+ (concat "("
+ (mapconcat 'identity (nth 3 one-method) ", ")
+ ")")
+ ;; else
+ "()" ))
+
+ (setq result
+ (append (list
+ (list
+ (car one-method) ;; the name of the method
+
+ ;; additional information about the method (in a
string)
+ (concat "(Method) "
+ modifiers ;; modifiers on this prop
+ " "
+ params
+ " returns "
+ (nth 2 one-method) ;; return type of the
method
+ )
+ ))
+ result)))
+
+ (setq methods (cdr methods)))
+
+
+ (while fields
+ (let ((one-field (car fields)))
+ (setq modifiers
+ (mapconcat 'identity (cdr (nth 3 one-field)) " "))
+ (setq result
+ (append (list
+ (list
+ (car one-field) ;; the name of the field
+
+ ;; additional information about the field (in a string)
+ (concat "(Field) "
+ modifiers ;; modifiers on this prop
+ " "
+ (nth 2 one-field) ;; type of the field
+ )
+ ))
+ result)))
+
+ (setq fields (cdr fields)))
+
+
+ (cscomp-log 3 "cscomp-build-clist-for-type: result: "
+ (prin1-to-string result))
+ ;;(print result (get-buffer "*Messages*"))
+ result))
+
+
+
+(defun cscomp-build-clist-for-ctors (ctor-info)
+ "Build a completion list from the CTOR-INFO list, as returned by the
+Ionic.Cscomp.Utilities.GetConstructors function in the CscompShell. The list
+is used when performing completions on a constructor for a given type.
+
+The input looks like:
+ (\"System.String\" 'type
+ (:constructors
+
+ ((:typemodifiers (\"public\")
+ :arguments ((\"value\" 'variable
+ (:type \"System.Char*\"))))
+
+ (:typemodifiers (\"public\")
+ :arguments ((\"value\" 'variable
+ (:type \"System.Char*\"))
+ (\"startIndex\" 'variable
+ (:type \"System.Int32\"))
+ (\"length\" 'variable
+ (:type \"System.Int32\"))))
+
+The output is a completion list, which is a list of (NAME DESCRIP) pairs.
+The DESCRIP of each pair should have the arguments for each constructor.
+
+That list is later transformed into a structure that is suitable
+for use in a popup menu.
+
+"
+ (let (result
+ (tname (car ctor-info))
+ (ctors (cadr (caddr ctor-info)))
+ ;;(methods (cadddr typeinfo))
+ )
+
+ (cscomp-log 2 "cscomp-build-clist-for-ctor: typename(%s)" tname)
+
+ (while ctors
+ (let* ((one-ctor (car ctors))
+ (modifiers (mapconcat 'identity (nth 1 one-ctor) " "))
+ (params
+ (if (nth 3 one-ctor)
+ (concat "("
+ (mapconcat
+ '(lambda (x)
+ (concat
+ (cadr (caddr x))
+ " "
+ (car x)))
+ (nth 3 one-ctor) ", ")
+ ")")
+ ;; else
+ "()" )))
+ (setq result
+ (append (list
+ (list
+ tname ;; name of the ctor
+
+ ;; description for this ctor
+ (concat "(Constructor) "
+ modifiers ;; modifiers on this prop
+ " "
+ params
+ )))
+ result)))
+ (setq ctors (cdr ctors)))
+
+ (cscomp-log 3 "cscomp-build-clist-for-ctors: result: "
+ (prin1-to-string result))
+ result))
+
+
+
+;; (defun cscomp-build-information-for-completion (lst)
+;; (let ((result (concat (car (cdr lst)) " " (car lst) "(")))
+;; (setq lst (cdr (cdr lst)))
+;; (while lst
+;; (setq result (concat result (car lst)))
+;; (setq lst (cdr lst))
+;; (if lst
+;; (setq result (concat result ", "))))
+;; (setq result (concat result ")"))
+;; result))
+
+
+
+
+;; (defun cscomp-popup-xemacs-completion-menu (completion-list)
+;; (let* ((items
+;; (sort
+;; ;; Change each item in the completion list from the form
+;; ;; return-value method-name(args)
+;; ;; to the form
+;; ;; method-name(args) : return-value
+;; (mapcar
+;; (lambda (completion)
+;; (let ((completion-short (nth 0 completion))
+;; (completion-long (nth 1 completion)))
+;; (if completion-long
+;; (let ((chop-pos (string-match " " completion-long)))
+;; (concat
+;; (substring completion-long (1+ chop-pos)
+;; (length completion-long))
+;; " : "
+;; (substring completion-long 0 chop-pos)))
+;; completion-short)))
+;; completion-list)
+;; 'string<))
+;; (menu
+;; (cons
+;; "Completions"
+;; (mapcar
+;; (lambda (item)
+;; (vector item (list 'cscomp-insert-completion item)))
+;; items))))
+;; (popup-menu-and-execute-in-window menu (selected-window))))
+
+
+
+
+(defun cscomp-sort-completion-list (lst)
+ (if (consp (car lst))
+ (sort lst
+ '(lambda (e1 e2)
+ (string< (car e1) (car e2))))
+ (sort lst
+ '(lambda (e1 e2)
+ (string< e1 e2)))))
+
+
+;; (defun cscomp-sort-completion-list (lst)
+;; ;; "sort LST in place."
+;; (sort lst
+;; '(lambda (e1 e2)
+;; (if (consp e1) ;; is the list elt a consp?
+;; ;; yes, we're completing on a type. Sort on the car of that
list
+;; (string< (car e1) (car e2))
+;; ;; no, we're completing on a namespace or local var.
+;; ;; The element should be a string. Sort on it directly.
+;; (string< e1 e2)
+;; ))))
+
+
+
+(defun cscomp-find-all-ns-completions (fragment lst &optional exact-match )
+ "Find all completions for FRAGMENT in LST. If EXACT-MATCH is true,
+then...? LST is a simple list of strings, as obtained from
+`cscomp-build-clist-for-ns'.
+"
+ (let ((result nil))
+ (while lst
+ (let ((candidate (car lst)))
+ (if (or (and exact-match (string= fragment candidate))
+ (and (not exact-match) (equal 0 (string-match (concat "^"
fragment) candidate))))
+ (progn
+ (message " HIT")
+ (setq result (append (list candidate) result)))))
+ (setq lst (cdr lst)))
+
+ (setq cscomp-current-fragment fragment)
+
+ ;; sort, and then transform into a list of (NAME DESCRIP) pairs:
+ (mapcar
+ '(lambda (item)
+ (if (string-match "^\\([^|]+\\) +| +\\(.+\\)$" item)
+ (list (match-string 1 item)
+ (match-string 2 item))
+ item))
+ (cscomp-sort-completion-list result))))
+
+
+
+
+(defun cscomp-find-all-type-completions (fragment lst static &optional
exact-match)
+ "Find all completions in LST for FRAGMENT. In practice,
+FRAGMENT is a string containing the characters following the last
+dot. Eg, if the user typed System.IO.Dir, then FRAGMENT would be
+\"Dir\".
+
+LST is a list obtained from `cscomp-build-clist-for-type'.
+
+If STATIC is non-nil, then match only non-static props/fields/methods.
+Otherwise, match only static fields/props/methods.
+
+If EXACT-MATCH is true, then only the completions that match the
+FRAGMENT exactly are returned. Normally, EXACT-MATCH is not
+true, and in thi case, the completions that start with the
+FRAGMENT are returned.
+
+"
+
+;; For illustration, the LST for DriveInfo looks like this:
+;; (
+;; ("Name" "(Property) readonly public System.String")
+;; ("DriveType" "(Property) readonly public System.IO.DriveType")
+;; ("DriveFormat" "(Property) readonly public System.String")
+;; ("IsReady" "(Property) readonly public System.Boolean")
+;; ("AvailableFreeSpace" "(Property) readonly public System.Int64")
+;; ("TotalFreeSpace" "(Property) readonly public System.Int64")
+;; ("TotalSize" "(Property) readonly public System.Int64")
+;; ("RootDirectory" "(Property) readonly public System.IO.DirectoryInfo")
+;; ("VolumeLabel" "(Property) public System.String")
+;; ("Equals" "(Method) public (System.Object) returns System.Boolean")
+;; ("GetDrives" "(Method) public static () returns System.IO.DriveInfo[]")
+;; ("GetHashCode" "(Method) public () returns System.Int32")
+;; ("GetType" "(Method) public () returns System.Type")
+;; ("ToString" "(Method) public () returns System.String")
+;; )
+
+
+ (let ((result nil))
+ (while lst
+ (let* ((candidate (car lst))
+ (member-name (car candidate))
+ (descrip (cadr candidate))
+ (is-static (string-match " static " descrip))
+ (is-match (and
+ (if static is-static (not is-static))
+ (if exact-match
+ (string= fragment member-name)
+ (equal 0 (string-match fragment member-name))))))
+
+ (cscomp-log 3 "cscomp-find-all-type-completions, looking at %s %s"
+ (prin1-to-string candidate)
+ (if is-match "MATCH" ""))
+
+ (if is-match
+ (setq result (append (list candidate) result))))
+
+ (setq lst (cdr lst)))
+
+ (cscomp-log 3 "cscomp-find-all-type-completions, result: %s"
+ (prin1-to-string result))
+ ;;(print result (get-buffer "*Messages*"))
+
+ (setq cscomp-current-fragment fragment)
+ (cscomp-sort-completion-list result)
+ ))
+
+
+
+
+
+
+(defvar cscomp-primitive-types
+ (list
+ '("string" "System.String")
+ '("byte" "System.Byte")
+ '("sbyte" "System.SByte")
+ '("char" "System.Char")
+ '("double" "System.Double")
+ '("float" "System.Float")
+ '("bool" "System.Boolean")
+ '("int" "System.Int32")
+ '("long" "System.Int64")
+ '("short" "System.Int16")
+ '("uint" "System.UInt32")
+ '("ulong" "System.UInt64")
+ '("ushort" "System.UInt16")
+ )
+
+ "a list that maps primitive C# types to their unboxed types.")
+
+
+
+(defun cscomp-map-primitive-type (type)
+"Maps the type to the expanded name of the type, and returns that
+expanded name. For example, bool => System.Boolean
+If type is not a primitive type, the result is nil."
+ (if (member type (mapcar 'car cscomp-primitive-types))
+ (let ((result nil)
+ (lst cscomp-primitive-types)
+ item )
+ (while lst
+ (setq item (car lst))
+ (if (string= type (car item))
+ (and
+ (setq result (cadr item))
+ (setq lst nil))
+ ;else
+ (setq lst (cdr lst))))
+ result )
+ nil ))
+
+
+
+
+
+(defun cscomp-find-completion-for-split (split opoint beginning-of-token1)
+
+ "SPLIT is a list of (TOKEN1 TOKEN2), as returned from
+`cscomp-split-by-dots'.
+
+OPOINT is the point at which the user has requested completion.
+
+If the user has typed something followed by a dot, followed by an
+optional fragment after the dot, then TOKEN1 may be a C#
+namespace or class, or a variable name, or explicitly, \"this\" .
+TOKEN2 may be the empty string, or a fragment of a name. The
+goal is to find a completion for these two things.
+
+Example: if SPLIT is (\"System\" \"Diag\") then the returned
+completion list will contain \"Diagnostics\".
+
+If the completion being requested is on a type, OPOINT is used to
+determine whether to present type names, or actual constructors.
+Do the latter if the new keyword precedes the
+thing-to-be-completed.
+
+"
+
+ (cscomp-log 2 "cscomp-find-completion-for-split: A: '%s' '%s'"
+ (car split) (cadr split))
+
+ ;;
+ ;; p1 is what precedes the final dot, p2 is what follows.
+ ;; When there is no dot, p1 is nil.
+ ;;
+ ;;
+ ;; Possibilities:
+ ;;
+ ;; 1. p1 is a namespace like System.Xml p2 is empty, or a fragment, like
XPa.
+ ;; In this case we need to find types or child namespaces in the given
+ ;; namespace that match the given fragment.
+ ;;
+ ;; 2. p1 is "this" and p2 is a fragment of a name of a class
field/prop/method.
+ ;; In this case, complete on the members of the enclosing class.
+ ;;
+ ;; 3. p1 is the name of a local variable, and p2 is empty or a fragment.
+ ;; In this case, complete with a field, prop, or method on that variable.
+ ;; Must know or learn the type of the variable in that case.
+ ;;
+ ;; 4. p1 is nil, and p2 is the partial name of a local variable.
+ ;; In this case, complete with the name of a local field, prop, or method
+ ;; that matches
+ ;;
+ ;; 5. p1 is the name of a type, like System.Console, and p2 is something.
+ ;; In this case, complete with static field, prop, and methods on
+ ;; that type.
+ ;;
+ ;; 6. p1 is the name of a primitive type, like byte or int.
+ ;; In this case, replace it with the expanded type, and complete as
+ ;; in case 5.
+ ;;
+ ;; 7. others?
+ ;;
+ ;; =============================================
+ ;;
+ ;; There are two sources for completion possibilities. For known types,
+ ;; the possibilities come from introspection, done through the CscompShell.
+ ;; So, for System.Xml.XmlDocument, we send a message to the shell and get
back
+ ;; all the known fields/props/methods of that .NET type.
+ ;;
+ ;; For unknown types - currently the only "unknown" qualifier is "this." -
+ ;; use the parse results from semantic to find fields/props/methods.
+ ;;
+
+ (let* ((p1 (car split))
+ (p2 (cadr split))
+ (is-primitive (cscomp-map-primitive-type p1))
+ (is-local-var (cscomp-qualify-local-var p1 opoint))
+ (is-instance-var (cscomp-qualify-instance-var p1))
+ (is-func (string-match "($" p2)) ;; open-paren
+ (is-ctor (and ;; constructor
+ (null p1)
+ (string-match "^\\([ \t\n\r\f\v]*new[
\t\n\r\f\v]+\\)\\(.+\\)$" p2)))
+ p1-flavor
+ p1-type
+ is-static
+ r)
+
+ (if is-ctor
+ ;; reset the beginning marker
+ (progn
+ (set-marker cscomp-current-beginning
+ (+ cscomp-current-beginning
+ (match-end 1)))
+
+ ;;(string-match "^\\([ \t]*new[ \t]+\\)\\(.+\\)$" p2)
+ ;; rely on most recent string-match context being for is-ctor
+ (setq p2 (substring p2 (match-beginning 2)))))
+
+
+
+
+ ;; figure out what we're completing on.
+
+ (cond
+ (is-primitive
+ ;; A static method/prop/field on a byte, int, char, etc.
+ (cscomp-log 3 "cscomp-find-completion-for-split: is-primitive")
+ (setq p1-flavor "type"
+ p1-type is-primitive
+ is-static t)) ;; get static methods/props/fields on System.Byte,
etc
+
+ (is-local-var
+ ;; method, property, or field on a local variable.
+ (cscomp-log 3 "cscomp-find-completion-for-split: is-local-var")
+ (setq p1-flavor "type"
+ p1-type (or (cscomp-map-primitive-type (car is-local-var))
+ (car is-local-var))))
+
+ (is-instance-var
+ ;; it's an instance variable.
+ (cscomp-log 3 "cscomp-find-completion-for-split: is-instance-var")
+ (setq p1-flavor "type"
+ p1-type (or (cscomp-map-primitive-type (car is-instance-var))
+ (car is-instance-var))))
+
+ ((string= "this" p1)
+ ;; complete on instance field/prop/method
+ (setq p1-flavor "this"))
+
+
+ ((and (null p1) ;; no prefix.
+ is-func) ;; open paren is the last char, eg "new TimeSpan(?"
+ (setq r (cscomp-qualify-name (substring p2 0 -1)))
+ (cond ((listp r)
+ (if (string= (car r) "type")
+ (setq p1-flavor "namespace"
+ p1-type (and
+ (string-match "^\\(.+\\)\\..+$" (cadr r))
+ (match-string 1 (cadr r)))
+ p1 p1-type)
+ (setq p1-flavor "local")))
+ (t
+ (setq p1-flavor "local"))))
+
+
+ (is-ctor
+ (setq p1-flavor "mixed")
+ (if (null p1)
+ (setq p1 p2)))
+
+
+;; ((and (null p1) ;; no prefix.
+;; (t
+;; ;; It's not a ctor or static method.
+;; ;; Maybe it's completion on a known typename or namespace.
+;; (setq p1-flavor "mixed"))))
+
+ (t
+ (setq r (cscomp-qualify-name p1))
+ (cond ((listp r)
+ (progn
+ (setq p1-flavor (car r))
+
+ (if (string= p1-flavor "unknown")
+ ;; assume p1 is an expression. Try to infer it's type.
+ (let ((inferred-type
+ (cscomp-get-var-type-given-decl (concat "var x = " p1
";")
+ 0
+ "NoMatter"
+ ""
+ "")))
+ (if (string= (car inferred-type) "type")
+ (setq p1-type (cadr inferred-type)
+ p1-flavor "type")))
+
+
+ ;; name qualification was successful
+ (setq p1-type p1
+ is-static t)))) ;; if it's a type, we want static f/m/p
only
+
+ (t
+ (error "qualify-name returns invalid results.")))))
+
+
+
+ (cscomp-log 2 "cscomp-find-completion-for-split: B: p1(%s,%s) p2(%s)
flav(%s)"
+ p1 (if p1 p1-type "--") (prin1-to-string p2) p1-flavor)
+
+
+ ;; now collect completion options depending on the "flavor" of p1:
+ ;; type == completing on m/f/p for a type, possibly with a fragment.
+ ;; p1 holds the typename from the buffer, could be partially
qualified.
+ ;; p1-type holds the fully qualified type name. p2 holds the
fragment.
+ ;; namespace == completing on a namespace. The result can be a child
+ ;; namespace or a type within a namespace.
+ ;; this == completing on a m/f/p of the local instance
+ ;; local == completing on a m/f/p of local variables or method args
+ ;; mixed == could be either a typename or a namespace name. This is
+ ;; the case when p1 is partial, and there is no dot.
+
+
+ (cond
+
+ ((string= p1-flavor "type")
+ (let* ((type-info (cscomp-get-typeinfo p1-type))
+ (full-list (cscomp-build-clist-for-type type-info)))
+ (cond
+
+ (is-func ;; it's a specific function on a type. Get the list of
overloads.
+ (setq cscomp-current-list
+ (cscomp-find-all-type-completions (substring p2 0 -1)
full-list is-static))
+ (cscomp-log 3 "cscomp-find-completion-for-split: [%s]"
+ (prin1-to-string cscomp-current-list)))
+ (t ;; could be anything: method, prop, field. Build the list.
+ (setq cscomp-current-list
+ (cscomp-find-all-type-completions p2 full-list is-static))
+
+ (cscomp-log 3 "cscomp-find-completion-for-split: [%s]"
+ (prin1-to-string cscomp-current-list))))))
+
+
+ ((string= p1-flavor "namespace")
+ (cscomp-log 2 "cscomp-find-completion-for-split, is-func(%s) bot(%d)"
+ is-func beginning-of-token1)
+ (cond
+ (is-func ;; it's a function on a type - see if a constructor
+ (cond
+ ((save-excursion
+ (goto-char beginning-of-token1)
+ (re-search-backward "\\<new[ \t\n\f\v]+" nil t))
+ ;; it's a constructor
+ (let* ((type-name (concat p1 "." (substring p2 0 -1)))
+ (ctor-info (cscomp-get-type-ctors type-name))
+ (full-list (cscomp-build-clist-for-ctors ctor-info)))
+
+ ;; present all constructors
+ (setq cscomp-current-list full-list)))
+
+ (t
+ ;; not a constructor. Must be some other function.
+ (error "completion on static functions is not (yet?) supported."))))
+
+ (t ;; complete on a type name, or child namespace
+ (let* ((type-list (cscomp-get-completions-for-namespace p1))
+ (full-list (cscomp-build-clist-for-ns type-list)))
+
+ (setq cscomp-current-list
+ (cscomp-find-all-ns-completions p2 full-list))
+ (cscomp-log 3 "cscomp-find-completion-for-split: [%s]"
+ (prin1-to-string cscomp-current-list))))))
+
+
+ ((string= p1-flavor "local")
+ (let ((instance-vars (cscomp-matching-instance-vars p2))
+ (locals (cscomp-matching-local-vars p2)))
+ (setq cscomp-current-list
+ (nconc instance-vars locals))))
+
+ ((string= p1-flavor "this")
+ (setq cscomp-current-list
+ (cscomp-matching-instance-members p2)))
+
+
+ ((string= p1-flavor "mixed")
+ (setq r (cscomp-get-matches p2))
+ (if (listp r)
+ (setq cscomp-current-list
+ (mapcar '(lambda (listitem)
+ (list
+ ;; the thing to insert
+ (if (and p1 (string-match (concat "^" p1)
+ (cadr listitem)))
+ (cadr listitem)
+ (string-match (concat "\\.\\(" p2 ".*\\)") (cadr
listitem))
+ (match-string 1 (cadr listitem)))
+
+ ;; the description that will be visible in the menu
+ (concat (cadr listitem) " | (" (car listitem) ")")
+ ))
+ r))
+ (error "Cannot complete.")))
+
+ (t
+ (cscomp-log 1 "cscomp-find-completion-for-split, unknown flavor (%s)"
+ p1-flavor)
+ (error "Cannot complete.")))))
+
+
+
+
+;; (defun cscomp-string-starts-with (s arg)
+;; "returns t if string S starts with ARG. Else nil."
+;; (eq 0 (string-match arg s)))
+
+
+
+(defun cscomp-yasnippet-for-arglist (method-name arglist)
+ "Produce a snippet that's usable with ya-snippet, for the method
+selected from the completion popup menu. The arguments are strings:
+METHOD-NAME is the name of the method, and ARGLIST is a description
+of the argument list for the selected method.
+
+The return value is a list, which, if selected from the menu, will be
+eval'd. The first element in the list is the literal, yas/expand-snippet,
+and subsequent elements in the return value list are simply arguments
+to that function.
+
+"
+ (let ((template "(") (str-ix 1) (arg-ix 1)
+ (next-char (char-after))
+ need-delete)
+
+ ;; Build the template for yasnippet. Each replaceable
+ ;; param is identified with ${N:foo} where N is 1,2,3...
+ ;; and foo is the placeholder shown in the buffer, which
+ ;; is then "typed over" by the user.
+ ;;
+ ;; In this case, the placeholder contains the type and the
+ ;; name of the argument, from the reflection results.
+ ;;
+ (while (string-match "\\([^ ]+\\) \\([^,)]+\\)\\([,)]\\)"
+ arglist str-ix)
+ (setq template (concat template "${"
+ (number-to-string arg-ix)
+ ":"
+ ;; type of the arg
+ (substring arglist
+ (match-beginning 1)
+ (match-end 1))
+ " "
+ ;; name of the arg
+ (substring arglist
+ (match-beginning 2)
+ (match-end 2))
+ "}"
+
+ ;; the following comma or closing paren
+ (if (string= (substring arglist
+ (match-beginning 3)
+ (match-end 3))
+ ")")
+ ;; If a close-paren is in the buffer,
+ ;; cscomp will delete it, before inserting
+ ;; the snippet.
+ (progn
+ ;; to delete the existing close paren
+ (setq need-delete (eq next-char 41))
+ ")")
+ ", "))
+
+ str-ix (match-end 0)
+ arg-ix (1+ arg-ix)))
+
+ (if (eq (length template) 1)
+ (setq template "()"))
+
+ ;; Upon selection of this item from the menu, emacs will
+ ;; call yas/expand-snippet. If necessary, emacs will
+ ;; delete a char just before doing so.
+ (if need-delete
+ (list 'progn
+ (list 'delete-char '1)
+ (list 'yas/expand-snippet '(point) '(point)
+ (concat method-name template)))
+ (list 'yas/expand-snippet '(point) '(point)
+ (concat method-name template)))))
+
+
+
+
+(defun cscomp-get-menu-item-for-clist-item (clist-item)
+ "Produce a menu item for the item on the completion list.
+
+The incoming CLIST-ITEM is either a (NAME DESCRIP) pair, or
+a simple NAME.
+
+The return value is a menu item - a thing suitable for use within
+a popup menu. It is a 2-member cons cell, the first member is a
+string to be displayed, the second is the value returned when
+that menu item is selected.
+
+This value is returned to logic in
+`cscomp-popup-completion-menu', and can be anything. That
+fn follows a convention that if the return value is a string, the
+string is inserted. If the return value is a list, as with
+ya-snippet, the list is eval'd and the result is inserted.
+
+This fn simply transforms the simple (NAME DESCRIP) pair into
+potentially something more interesting.
+
+"
+ (if (consp clist-item)
+ (let ((meth-or-prop-name (car clist-item))
+ (descrip (cscomp-fix-generics-in-descrip-line (cadr clist-item))))
+
+ (cond
+
+ ((string-match "(\\(Method\\|Constructor\\))[^(]+\\(([^)]*)\\)"
descrip)
+ ;; It's a method or constructor. The value returned when this
+ ;; menu item is selected is a cons cell that will be eval'd.
+ ;; The cons is a call to yas/expand-snippet that will insert a
+ ;; template for the (possibly empty) arglist of the selected
+ ;; method or constructor.
+ (let ((arglist (substring descrip
+ (match-beginning 2)
+ (match-end 2)))
+ ;; for ctor, insert the short name.
+ (name-to-insert (if (string= (substring descrip
+ (match-beginning 1)
+ (match-end 1))
+ "Constructor")
+ (if (string-match ".+\\."
meth-or-prop-name)
+ (substring meth-or-prop-name
+ (match-end 0))
+ meth-or-prop-name)
+ meth-or-prop-name)))
+
+ (cons (concat meth-or-prop-name " | " descrip)
+ (if (fboundp 'yas/expand-snippet)
+ (cscomp-yasnippet-for-arglist name-to-insert arglist)
+ (concat name-to-insert arglist)))))
+
+ (t
+ ;; The completion is not a method - just
+ ;; insert the name of the field/prop as a string.
+ (cons (concat meth-or-prop-name " | " descrip)
+ meth-or-prop-name))))
+
+ (cons clist-item clist-item)))
+
+
+
+(defun cscomp-popup-completion-menu (title)
+ "Popup a completion menu for the object at point. The popup
+menu displays all of the possible completions for the object it
+was invoked on.
+
+TITLE is the title presented. The contents of the menu are provided
+in `cscomp-current-list' .
+
+"
+
+ ;; cscomp-current-list holds the completion list.
+ ;;
+ ;; For a type, the clist gets methods, properties and fields.
+ ;; Each element in the list is a list, (NAME DESCRIP), where NAME
+ ;; is the name of the method/prop/field, and DESCRIP is a string
+ ;; description.
+ ;;
+ ;; For a namespace, the clist is a list of strings.
+ ;;
+ ;; In each case we want to sort the list alphabetically.
+ ;;
+
+ (let* ((menu-map (cons "ignored"
+ (mapcar 'cscomp-get-menu-item-for-clist-item
+ cscomp-current-list)))
+ (menu-result (cscomp-popup-menu
+ (list (or title "Completions...") menu-map))))
+
+ (cscomp-log 1 "menu-result: %s" (prin1-to-string menu-result))
+
+ ;; now, handle the selection
+ (cond
+
+ ((consp menu-result)
+ (delete-region cscomp-current-beginning cscomp-current-end)
+ (eval menu-result) ;; maybe a snippet expansion
+ "")
+
+ ((numberp menu-result) (number-to-string menu-result))
+
+ ((null menu-result) "")
+
+ (t menu-result)))) ;; a simple string
+
+
+
+
+
+
+;; To do thie yasnippet thing for methods/params:
+;;
+;; (defun dox ()
+;; "expand a template"
+;; (interactive)
+;; (let ((template
+;; "this is ${1:index} the expanded template ${2:foo}"
+;; ))
+;; (yas/expand-snippet (point) (point) template)))
+
+
+
+
+(defun cscomp-selected-frame ()
+ (if (fboundp 'window-edges)
+ (selected-frame)
+ (selected-window)))
+
+(defun cscomp-current-row ()
+ "Return current row number in current frame."
+ (if (fboundp 'window-edges)
+ (+ (car (cdr (window-edges))) (count-lines (window-start) (point)))
+ (count-lines (window-start) (point))))
+
+
+(defun cscomp-get-point-pixel-pos ()
+ "Return point position in pixels: (x, y)."
+ (let ((mouse-pos (mouse-position))
+ (pixel-pos nil)
+ (ret nil))
+ (if (car (cdr mouse-pos))
+ (progn
+ (set-mouse-position (cscomp-selected-frame) (current-column)
(cscomp-current-row))
+ (setq pixel-pos (mouse-pixel-position))
+ (set-mouse-position (car mouse-pos) (car (cdr mouse-pos)) (cdr (cdr
mouse-pos)))
+ (setq ret (list (car (cdr pixel-pos)) (cdr (cdr pixel-pos)))))
+ (progn
+ (setq ret '(0 0))))
+ (cscomp-log 3 "mouse pos is %s" ret)
+ ret))
+
+
+(defun cscomp-posn-at-point-as-event (&optional position window dx dy)
+ "Return pixel position of top left corner of glyph at POSITION,
+relative to top left corner of WINDOW, as a mouse-1 click
+event (identical to the event that would be triggered by clicking
+mouse button 1 at the top left corner of the glyph).
+
+POSITION and WINDOW default to the position of point in the
+selected window.
+
+DX and DY specify optional offsets from the top left of the glyph."
+ (unless window (setq window (selected-window)))
+ (unless position (setq position (window-point window)))
+ (unless dx (setq dx 0))
+ (unless dy (setq dy 0))
+
+ (let* ((pos (posn-at-point position window))
+ (x-y (posn-x-y pos))
+ (edges (window-inside-pixel-edges window))
+ (win-x-y (window-pixel-edges window)))
+ ;; adjust for window edges
+ (setcar (nthcdr 2 pos)
+ (cons (+ (car x-y) (car edges) (- (car win-x-y)) dx)
+ (+ (cdr x-y) (cadr edges) (- (cadr win-x-y)) dy)))
+ (list 'mouse-1 pos)))
+
+
+(defun cscomp-popup-menu (menu-data)
+ "Pop up the completion menu at point, using the data MENU-DATA.
+MENU-DATA is a list of error and warning messages returned by
+`cscomp-make-err-menu-data'."
+ (if (featurep 'xemacs)
+ (let* ((pos (cscomp-get-point-pixel-pos))
+ (x-pos (nth 0 pos))
+ (y-pos (nth 1 pos))
+ (fake-event-props '(button 1 x 1 y 1)))
+ (setq fake-event-props (plist-put fake-event-props 'x x-pos))
+ (setq fake-event-props (plist-put fake-event-props 'y y-pos))
+ (popup-menu (cscomp-make-xemacs-menu menu-data)
+ (make-event 'button-press fake-event-props)))
+ (x-popup-menu (if (eval-when-compile (fboundp 'posn-at-point))
+ (cscomp-posn-at-point-as-event nil nil 2 20)
+ (list (cscomp-get-point-pixel-pos) (selected-window)))
+ menu-data)))
+
+
+
+
+(defun cscomp-cycle-candidates ()
+ "Replace the previous completion by the next one in
+`cscomp-current-list'. Uses `cscomp-current-list-index'
+to track the position in the list, and markers `cscomp-current-end'
+and `cscomp-current-beginning' to track the position in the
+buffer.
+
+The elements in `cscomp-current-list' can be either lists, or
+atoms. In the case of completing on a namespace (eg, start with System.Co),
+the completion list will contain strings. (CodeDom, Console,Collections...).
+
+If completing on a type or instance, the list elems are 2-element lists -
+the car is the thing to insert, and the cadr is a description of the thing.
+is it a property, method, field? what is the return type? if a method,
+then what arguments does it require? The cadr is used only in the menu,
+not in the inserted text.
+
+On the first entry in this function, the markers record the beginning and
+end of the fragment being completed. (eg, Co if completing on System.Co).
+On exit, the markers record the beginning and end of what has been inserted.
+On subsequent entry, it retains those values. On all entries, this fn
+deletes the text between the markers, before inserting the completion.
+
+"
+ (let (elem)
+ (setq cscomp-current-list-index (1+ cscomp-current-list-index))
+ (cscomp-log 3 "list index: %d" cscomp-current-list-index)
+ (if (>= cscomp-current-list-index (length cscomp-current-list))
+ (setq cscomp-current-list-index 0)) ;; rollover
+ (setq elem (nth cscomp-current-list-index cscomp-current-list))
+
+ (cscomp-log 3 "complete-cycle-candidates: looking at %s" (prin1-to-string
elem))
+
+ (cond
+ ((listp elem)
+ (if (car elem)
+ (let ((thing-to-insert
+ (car elem))
+ ;;(substring (car elem) (length cscomp-current-fragment)))
+ )
+ (delete-region cscomp-current-beginning cscomp-current-end)
+ (insert thing-to-insert)
+ (set-marker cscomp-current-end
+ (+ (marker-position
+ cscomp-current-beginning) (length
thing-to-insert)))
+ ;; display the description of the completioni n the minibuffer
+ (message "cscomp: %s" (cadr elem)))
+ (cscomp-log 1 (format "No completion at this point!(cycle)"))))
+
+ ;; elem is an atom
+ (t
+ (let ((thing-to-insert
+ elem)
+ ;;(substring elem (length cscomp-current-fragment)))
+ )
+ (delete-region cscomp-current-beginning cscomp-current-end)
+ (insert thing-to-insert)
+ (set-marker cscomp-current-end
+ (+ (marker-position cscomp-current-beginning) (length
thing-to-insert) ))
+ ;; display the description of the completioni n the minibuffer
+ (message "cscomp: %s" elem)))
+ )))
+
+
+
+
+(defun cscomp-complete-at-point ()
+ "Completes at point. Performs completion on field, prop or
+method names if the thing being completed is a type or instance,
+or on namespace members - child namespaces or types - if the
+thing being completed is a namespace.
+
+This function is interactive.
+
+Invoking this fn multiple times in succession cycles through the
+completions.
+
+On first entry, it finds the completion, by calling out to the
+CscompShell by calling `cscomp-find-completion-for-split'.
+It then caches the resulting list of completion options into
+`cscomp-current-list'.
+
+On subsequent entry, it uses the cached results. After inserting a
+completion, this function displays the signature of a method completion
+in the minibuffer.
+
+This fn starts the Cscompshell if necessary. Hence, you may
+experience a slight delay when using this command for the first
+time in a session or when completing a field or method of an
+object that has many methods and fields.
+
+See `cscomp-complete-at-point-menu' for an alternative to this command
+that lets you select the desired completion from a popup menu.
+"
+
+ (interactive)
+
+;; This command uses the Cscompshell to run Csharp code that in turn uses
+;; .NET reflection to determine the methods, properties, and fields
+;; defined by the type of the object at point.
+
+ (if (and
+ cscomp-current-list
+ (eq last-command this-command)
+ (markerp cscomp-current-beginning)
+ (markerp cscomp-current-end)
+ (marker-position cscomp-current-beginning)
+ (marker-position cscomp-current-end)
+ (>= (point) (marker-position cscomp-current-beginning))
+ (eq (point) (marker-position cscomp-current-end)))
+
+ ;; we've got a completion list. cycle through the items.
+ (cscomp-cycle-candidates)
+ ;; no completion list available. get one.
+ (progn
+ (let* ((parse-result (cscomp-parse-csharp-expression-before-point))
+ (split (and parse-result (cadr parse-result))))
+
+ (if (not (null split))
+ (progn
+ ;; set markers
+ (cscomp-log 1 "complete-at-point: reset begin,end to (%d,%d)"
+ (- (point) (length (cadr split)))
+ (point))
+
+ ;; beginning: right after the dot (if there is one)
+ (set-marker cscomp-current-beginning
+ (- (point) (length (cadr split))))
+
+ (set-marker cscomp-current-end (point))
+ (cscomp-find-completion-for-split split (point) (car
parse-result))
+ (setq cscomp-current-list-index -1)
+ (cscomp-cycle-candidates)))))))
+
+
+
+
+(defun cscomp-complete-at-point-menu ()
+ "Pops up a menu with a list of completions for point. When completing
+on a type or instance, the menu items are the field, property and method
+names; when completing on a namespace, the menu items are child namespaces
+or types.
+
+When the user makes a selection from the menu, the selected item is
+inserted at point. In the case of a method with parameters, the inserted
+thing is a parameterized snippet, a template that can then be filled in
+by the user.
+
+See `cscomp-complete-at-point' for an alternative to this function that
+lets you cycle through the potential completions at point.
+
+"
+ (interactive)
+ (let* ((parse-result (cscomp-parse-csharp-expression-before-point))
+ (split (and parse-result (cadr parse-result))))
+
+ ;; Reset the completion list - the thing that is used
+ ;; when the "no menu" version of complete-at-pont is used.
+ (setq cscomp-current-list nil)
+
+ (if (not (null split))
+ (progn
+ ;; set markers
+ (set-marker cscomp-current-beginning (- (point) (length (cadr
split))))
+ (set-marker cscomp-current-end (point))
+
+ (cscomp-find-completion-for-split split (point) (car parse-result))
+
+ (if cscomp-current-list
+ (let* ((title (if (car split)
+ (concat (car split) "."
+ (cadr split) "...")
+ (concat (cadr split) "...")))
+ (selection (cscomp-popup-completion-menu title)))
+ (if (and selection
+ (not (string= selection "")))
+ (progn
+ (delete-region cscomp-current-beginning
cscomp-current-end)
+ (insert selection)
+ )))
+ (message "No completion at this point.")))
+ (message "No completion at this point."))))
+
+
+
+
+;; ;; =======================================================
+;; ;; Adding a new semantic tag for "namespace".
+;; ;;
+;; ;; This turned out to be much harder that it should be.
+;; ;;
+;; ;; Apparently there are special handling for tags of type 'type,
+;; ;; So, for now, punt on doing it this way.
+;; ;;
+;; ;; =======================================================
+;; ;;
+;; (defsubst semantic-tag-new-namespace (name members &rest attributes)
+;; "Create a semantic tag of class 'namespace.
+;; NAME is the name of this namespace.
+;; MEMBERS is a list of strings or semantic tags representing the
+;; elements that are contained within the namespace.
+;; ATTRIBUTES is a list of additional attributes belonging to this tag."
+;; (apply 'semantic-tag name 'namespace
+;; :members members
+;; attributes))
+;;
+;;
+;;
+;; ;; We want this:
+;; ;;
+;; ;; (defun semantic-tag-components-default (tag)
+;; ;; "Return a list of components for TAG.
+;; ;; Perform the described task in `semantic-tag-components'."
+;; ;; (cond ((semantic-tag-of-class-p tag 'type)
+;; ;; (semantic-tag-type-members tag))
+;; ;; ((semantic-tag-of-class-p tag 'namespace)
+;; ;; (semantic-tag-type-members tag))
+;; ;; ((semantic-tag-of-class-p tag 'function)
+;; ;; (semantic-tag-function-arguments tag))
+;; ;; (t nil)))
+;;
+;; ;; we'll settle for this:
+;;
+;; (defadvice semantic-tag-components-default (after
+;; cscomp-advice-1
+;; (tag)
+;; activate compile)
+;;
+;; (cond ((semantic-tag-of-class-p tag 'namespace)
+;; (semantic-tag-type-members tag))
+;; (t ad-return-value)))
+
+
+
+(provide 'csharp-completion)
+
+
+;;; end of csharp-completion.el
diff --git a/csharp-mode.el b/csharp-mode.el
index 69467f0..84b2c99 100644
--- a/csharp-mode.el
+++ b/csharp-mode.el
@@ -1,13 +1,15 @@
;;; csharp-mode.el --- C# mode derived mode
-;; Author: Dylan R. E. Moonfire (original)
-;; Maintainer: Dino Chiesa <dpchiesa@hotmail.com>
-;; Created: Feburary 2005
-;; Modified: April 2010
-;; Version: 0.7.5
-;; Keywords: c# languages oop mode
-;; X-URL: http://code.google.com/p/csharpmode/
+;; Author : Dylan R. E. Moonfire (original)
+;; Maintainer : Dino Chiesa <dpchiesa@hotmail.com>
+;; Created : Feburary 2005
+;; Modified : April 2010
+;; Version : 0.7.5
+;; Keywords : c# languages oop mode
+;; X-URL : http://code.google.com/p/csharpmode/
+;; Last-saved : <2010-May-24 15:34:26>
+;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2 of the License, or
@@ -370,30 +372,33 @@ Another option is to use `csharp-lineup-region'.
(defun csharp-insert-open-brace ()
"Intelligently insert a pair of curly braces. This fn is most
-often bound to the open-curly brace, with
+ often bound to the open-curly brace, with
- (local-set-key (kbd \"{\") 'csharp-insert-open-brace)
+ (local-set-key (kbd \"{\") 'csharp-insert-open-brace)
-The default binding for an open curly brace in cc-modes is often
-`c-electric-brace' or `skeleton-pair-insert-maybe'. The former
-can be configured to insert newlines around braces in various
-syntactic positions. The latter inserts a pair of braces and
-then does not insert a newline, and does not indent.
+ The default binding for an open curly brace in cc-modes is often
+ `c-electric-brace' or `skeleton-pair-insert-maybe'. The former
+ can be configured to insert newlines around braces in various
+ syntactic positions. The latter inserts a pair of braces and
+ then does not insert a newline, and does not indent.
-This fn provides another option, with some additional
-intelligence for csharp-mode. When you type an open curly, the
-appropriate pair of braces appears, with spacing and indent set
-in a context-sensitive manner.
+ This fn provides another option, with some additional
+ intelligence for csharp-mode. When you type an open curly, the
+ appropriate pair of braces appears, with spacing and indent set
+ in a context-sensitive manner.
-Within a string literal, you just get a pair of braces, and point
-is set between them. Following an equals sign, you get a pair of
-braces, with a semincolon appended. Otherwise, you
-get the open brace on a new line, with the closing brace on the
-line following.
+ Within a string literal, you just get a pair of braces, and
+ point is set between them. Following an equals sign, you get
+ a pair of braces, with a semincolon appended. Otherwise, you
+ get the open brace on a new line, followed by an empty line
+ and the closing brace on the line following, with point on
+ the empty line.
-There may be another way to get this to happen appropriately just within emacs,
-but I could not figure out how to do it. So I wrote this alternative.
-"
+ There may be another way to get this to happen appropriately just
+ within emacs, but I could not figure out how to do it. So I
+ wrote this alternative.
+
+ "
(interactive)
(let
(tpoint
@@ -401,7 +406,7 @@ but I could not figure out how to do it. So I wrote this
alternative.
(preceding3
(save-excursion
(and
- (skip-chars-backward " ")
+ (skip-chars-backward " \t")
(> (- (point) 2) (point-min))
(buffer-substring-no-properties (point) (- (point) 3)))))
(one-word-back
@@ -2280,11 +2285,11 @@ The return value is meaningless, and is ignored by
cc-mode.
;; c# - monkey-patching of basic parsing logic
;; ==================================================================
;;
-;; Here, the model redefines two defuns to add special cases for csharp
-;; mode. These primarily deal with indentation of instance
-;; initializers, which are somewhat unique to C#. I couldn't figure out
-;; how to get cc-mode to do what C# needs, without modifying these
-;; defuns.
+;; The following 2 defuns redefine functions from cc-mode, to add
+;; special cases for C#. These primarily deal with indentation of
+;; instance initializers, which are somewhat unique to C#. I couldn't
+;; figure out how to get cc-mode to do what C# needs, without modifying
+;; these defuns.
;;
(defun c-looking-at-inexpr-block (lim containing-sexp &optional check-at-end)
diff --git a/csharp-shell.el b/csharp-shell.el
new file mode 100644
index 0000000..aae6a26
--- /dev/null
+++ b/csharp-shell.el
@@ -0,0 +1,363 @@
+;;; csharp-shell.el --- run PowerShell to assist with c# completion.
+;;
+;; Author: Dino Chiesa <dpchiesa@hotmail.com>
+;; Created: 10 Apr 2008
+;; Modified: May 2010
+;; Version: 0.2
+;; Keywords: powershell C# shell
+;; X-URL: ??
+;;
+
+;;; Commentary:
+;;
+;; This is code that provides an emacs shell that supports C#
code-completion.
+;; It depends on Powershell.el. It is part of cscomp. (C# completion).
+;;
+;;
+;; csharp-shell.el is responsible for starting a
+;; PowerShell shell, and loading a custom utility assembly into it.
+;; The running shell can be used as a regular, interactive powershell shell,
but its
+;; primary purpose is to connect elisp to the .NET assembly that
+;; performs reflection.
+;;
+;; When the user requests completion on a variable (etc), logic within
+;; csharp-completion.el sends a command to the CscompShell, and gets the
+;; response, in the form of a lisp s-expression. The logic in
+;; csharp-completion.el then evals that sexp, and does something
+;; intelligent with the result.
+;;
+;; The two main public interfaces for this module are:
+;;
+;; `csharp-shell-exec-and-eval-result'
+;; `csharp-shell-exec-and-maybe-eval-result'
+;;
+;;
+;; Some History:
+;; idea of CSDE, the C# Development Environment, was initially
+;; conceived by Matt Bruce in 2001 (or so), and ported from JDE, the
+;; Java Development Environment.
+;;
+;; But the ambitious vision of CSDE was never completed. The latest
+;; version is aparently still available on sourceforge, but it was
+;; never updated or maintained. <URL:http://www.sourceforge.com/>.
+;;
+;; Rather than start with "everything", I thought I'd start by
+;; producing a module that did one thing well: code completion.
+;; This is known as Intellisense in Microsoft's Visual Studio.
+;;
+;; To do this, I didn't keep any of the old CSDE code. I kept only the
+;; *idea* of code completion, relying on the use of a shell.
+;;
+;; ------------
+;;
+;; Please send any comments, bugs, or upgrade requests to
+;; Dino Chiesa (dpchiesa@hotmail.com)
+;;
+
+
+(require 'powershell)
+
+(defcustom csharp-shell-location-of-util-dll nil
+ "Folder name that contains the Cscomp DLL for C# Completion.
+Set this to nil, to load the DLL named CscompUtilities.dll from
+the same directory where csharp-shell.el is located.
+
+Otherwise, set it to a fully-qualified path of a directory that contains
+the file cscompUtilities.dll. For example,
+
+ \"c:\\users\\fred\\elisp\\cscomp\"
+
+"
+ :group 'cscomp
+ :type 'string)
+
+(defcustom csharp-shell-startup-timeout 20
+ "*Length of time the CSharpShell waits for the shell process to startup.
+Increase the value of this variable if you get Lisp errors
+on Shell startup."
+ :group 'cscomp
+ :type 'integer)
+
+(defcustom csharp-shell-exec-timeout 9
+ "*Length of time in seconds to wait for the CscompShell to respond to
commands
+before giving up and signaling an error. This isn't the total timeout; it's
+the time to wait between chunks of response. "
+ :group 'cscomp
+ :type 'integer)
+
+(defcustom csharp-shell-buffer-name "*CscompShell*"
+ "Name of the Powershell buffer for C# completion support."
+ :group 'cscomp
+ :type 'string
+ )
+
+(defconst csharp-shell-prompt-string "CscompShell % "
+ "The prompt string used for csharp-shell. It is also used as a regex, so
this string must contain no regex-sensitive sequences. Best to just leave it
alone.")
+
+
+(defvar cscomp-log-level 0
+ "The current log level for C# completion operations. 0 = NONE, 1 = Info, 2
= VERBOSE, 3 = DEBUG. ")
+
+
+(defun cscomp-log (level text &rest args)
+ "Log a message at level LEVEL.
+If LEVEL is higher than `cscomp-log-level', the message is
+ignored. Otherwise, it is printed using `message'.
+TEXT is a format control string, and the remaining arguments ARGS
+are the string substitutions (see `format')."
+ (if (<= level cscomp-log-level)
+ (let* ((msg (apply 'format text args)))
+ (message "%s" msg)
+ )))
+
+
+(defun csharp-shell ()
+ "Starts CsharpShell, which is an instance of Powershell that loads
+a custom assembly dedicated to supporting C# code completion in emacs.
+"
+ (interactive)
+ (csharp-shell--internal nil)
+ )
+
+
+
+(defun csharp-shell--internal (&optional display-buffer)
+ (if (not (comint-check-proc csharp-shell-buffer-name))
+ ;; then
+ (let (version)
+ (message "Starting CscompShell...")
+ (setq version (csharp-shell--start))
+ (message "CscompShell v%s is now running..." version)
+ )
+ ;; else
+ (when display-buffer
+ (message "CscompShell is already running."))
+ )
+ )
+
+
+;; (defvar csharp-shell--reply nil
+;; "Internal use only.")
+
+
+
+(defun csharp-shell-exec-and-maybe-eval-result (expr &optional eval-return)
+ "Sends EXPR to the CscompShell. EXPR could theoretically be
+any valid Powershell command, but this fn is invoked from
+csharp-completion, the EXPR is a call to static function in the
+CscompUtilities.dll assembly.
+
+If the shell is not already running, this function starts
+it. Collects the text output from the shell. If the optional
+argument EVAL-RETURN is non-nil, this function returns the result
+of evaluating the output as a Lisp expression. Otherwise, the
+return value is the collected text.
+"
+ (let ((proc
+ (or (get-buffer-process csharp-shell-buffer-name)
+ (let (proc2)
+ (csharp-shell--internal)
+ (setq proc2 (get-buffer-process csharp-shell-buffer-name))
+ proc2))))
+
+ (if proc
+ (let (reply tmp)
+
+ (with-current-buffer csharp-shell-buffer-name
+ (cscomp-log 2 "csharp-shell-exec: Sending: %s" expr)
+ (setq reply
+ (powershell-invoke-command-silently proc expr
csharp-shell-exec-timeout)))
+
+
+ (if (or (null reply)
+ (string-match "// Error:" reply))
+ (progn
+ (cscomp-log 1
+ "csharp-shell-exec: CscompShell command error.\n
Expression: %s\n Error: %s"
+ expr reply)
+ (error "CscompShell eval error. See messages buffer for
details.")))
+
+
+ (if eval-return
+ (if (and reply (not (string= reply "")))
+ (progn
+ (cscomp-log 2 "csharp-shell-exec: evaluating reply: '%s'"
reply)
+
+ (setq tmp (read reply)) ;; get one s-exp
+ (if (not (eq tmp "CscompShell")) ;; means no response at
all
+
+ (progn
+ (setq tmp (eval tmp))
+ (cscomp-log 2 "csharp-shell-exec: eval result(%s)"
(prin1-to-string tmp)) ;; can be nil
+ tmp)
+ nil))
+
+ ;; else
+ (progn
+ (cscomp-log 1 "csharp-shell-exec: result is empty. Will not
evaluate.")
+ nil))
+
+ ;; else (no eval)
+ (progn
+ (cscomp-log 1 "csharp-shell-exec: no eval, reply: '%s'" reply)
+ reply))))))
+
+
+
+
+
+
+(defun csharp-shell-exec-and-eval-result (psh-statement)
+ "Convenience function for evaluating Powershell statements
+that return Lisp expressions as output. This function
+invokes csharp-shell-exec with the evaluate-return option set to
+t."
+ (csharp-shell-exec-and-maybe-eval-result psh-statement t))
+
+
+
+
+;; ;; dinoch - Thu, 20 May 2010 14:59
+;; ;;
+;; ;; TODO: now I cannot remember why this is here. Do I need to explicitly
+;; ;; override the shell function? Must check this.
+;;
+;; (defun shell (&optional buffer)
+;; "Run an inferior shell, with I/O through BUFFER (which defaults to
`*shell*').
+;; Interactively, a prefix arg means to prompt for BUFFER.
+;; If BUFFER exists but shell process is not running, make new shell.
+;; If BUFFER exists and shell process is running, just switch to BUFFER.
+;; Program used comes from variable `explicit-shell-file-name',
+;; or (if that is nil) from the ESHELL environment variable,
+;; or (if that is nil) from `shell-file-name'.
+;; If a file `~/.emacs_SHELLNAME' exists, or `~/.emacs.d/init_SHELLNAME.sh',
+;; it is given as initial input (but this may be lost, due to a timing
+;; error, if the shell discards input when it starts up).
+;; The buffer is put in Shell mode, giving commands for sending input
+;; and controlling the subjobs of the shell. See `shell-mode'.
+;; See also the variable `shell-prompt-pattern'.
+;;
+;; To specify a coding system for converting non-ASCII characters
+;; in the input and output to the shell, use
\\[universal-coding-system-argument]
+;; before \\[shell]. You can also specify this with
\\[set-buffer-process-coding-system]
+;; in the shell buffer, after you start the shell.
+;; The default comes from `process-coding-system-alist' and
+;; `default-process-coding-system'.
+;;
+;; The shell file name (sans directories) is used to make a symbol name
+;; such as `explicit-csh-args'. If that symbol is a variable,
+;; its value is used as a list of arguments when invoking the shell.
+;; Otherwise, one argument `-i' is passed to the shell.
+;;
+;; \(Type \\[describe-mode] in the shell buffer for a list of commands.)"
+;; (interactive
+;; (list
+;; (and current-prefix-arg
+;; (read-buffer "Shell buffer: "
+;; (generate-new-buffer-name "*shell*")))))
+;; (setq buffer (get-buffer-create (or buffer "*shell*")))
+;; ;; Pop to buffer, so that the buffer's window will be correctly set
+;; ;; when we call comint (so that comint sets the COLUMNS env var properly).
+;; (pop-to-buffer buffer)
+;; (unless (comint-check-proc buffer)
+;; (let* ((prog (or explicit-shell-file-name
+;; (getenv "ESHELL") shell-file-name))
+;; (name (file-name-nondirectory prog))
+;; (startfile (concat "~/.emacs_" name))
+;; (xargs-name (intern-soft (concat "explicit-" name "-args"))))
+;;
+;; (unless (file-exists-p startfile)
+;; (setq startfile (concat "~/.emacs.d/init_" name ".sh")))
+;; (apply 'make-comint-in-buffer "shell" buffer prog
+;; (if (file-exists-p startfile) startfile)
+;; (if (and xargs-name (boundp xargs-name))
+;; (symbol-value xargs-name)
+;; '("-i")))
+;; (shell-mode)))
+;; buffer)
+
+
+
+(defun csharp-shell--start ()
+ "Run a special instance of PowerShell in support of C# Completion, by
invoking the `powershell' function. The buffer containing the shell is named
`csharp-shell-buffer-name'.
+"
+
+ (let* ((cscompshell-buffer (powershell
+ csharp-shell-buffer-name
+ csharp-shell-prompt-string))
+ (proc (get-buffer-process cscompshell-buffer))
+ (dll-location (concat
+ (or
+ csharp-shell-location-of-util-dll
+ "idontknow" )
+ "\\CscompUtilities.dll" ))
+ result
+ version)
+
+ (if proc
+ (progn
+ ;; Don't need to call save-excursion here, because
+ ;; powershell has already called pop-to-buffer .
+ ;; The CscompShell is the current buffer, and all
+ ;; the buffer-local variables are available.
+
+ ;; load the CscompUtilities DLL .
+
+ (setq result
+ (powershell-invoke-command-silently
+ proc
+ (concat "[System.Reflection.Assembly]::LoadFrom('"
dll-location "')")
+ 6.5))
+
+ (if (string-match "^Exception .*: \"\\(.+\\)\"" result)
+ (let ((message (substring result (match-beginning 1) (match-end
1))))
+ (error (concat "error: " message))))
+
+
+ ;; get the version number
+ (setq version
+ (powershell-invoke-command-silently
+ proc
+ "[Ionic.Cscomp.Utilities]::Version()"
+ 2.9))
+
+ (if version
+ (setq version (substring version 1 -1)))
+
+ ;; If the user exits, we won't ask whether he wants to kill the
CscompShell.
+ (set-process-query-on-exit-flag proc nil)
+
+ ;;(comint-simple-send proc "prompt\n") ;; shouldn't need this
+
+ ;; Send an initial carriage-return. The effect is to make the
+ ;; prompt appear. I don't know why this is necessary here.
+ ;; It's called in the powershell function, but somehow it has
+ ;; no effect, when powershell is invoked from csharp-shell, so I
+ ;; also call it here.
+ (comint-send-input)
+ (accept-process-output proc)
+
+ ;; Remove the window for the shell.
+ ;; shell.el automatically pops to the shell buffer, but we
+ ;; don't want that in this case. For Cscomp, the shell runs unseen,
+ ;; in the background. User can pop to it, if he likes.
+ (delete-window)
+
+ )
+ )
+
+ ;; return the version of the CscompUtilities.dll
+ version))
+
+
+;; Set the default DLL location at load time,
+;; if appropriate.
+(or
+ csharp-shell-location-of-util-dll
+ (setq csharp-shell-location-of-util-dll
+ (file-name-directory load-file-name)))
+
+
+(provide 'csharp-shell)
+
+;; End of csharp-shell.el
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..c2b3d38
--- /dev/null
+++ b/makefile
@@ -0,0 +1,28 @@
+_NETHOME=c:\.net3.5
+_NETSDK20=c:\netsdk2.0\bin
+_NETSDK=c:\netsdk3.5\bin
+
+_CS_DBG_FLAGS=/debug:full /optimize-
+_CS_DLL_FLAGS=/t:library $(_CS_DBG_FLAGS)
+_CS_EXE_FLAGS=/t:exe $(_CS_DBG_FLAGS)
+
+_CSC=$(_NETHOME)\csc.exe
+
+!IFNDEF CONFIG
+CONFIG=Release
+!ENDIF
+
+
+!IF "$(CONFIG)" == "Debug"
+ADDL_DEBUG_DLL=Ionic.CopyData.dll
+ADDL_REF=/R:$(ADDL_DEBUG_DLL)
+ADDL_CSC_OPTIONS=/d:UseCopyData
+!Endif
+
+
+
+default: CscompUtilities.dll
+
+CscompUtilities.dll: makefile CscompUtilities.cs $(ADDL_DEBUG_DLL)
+ $(_CSC) $(ADDL_CSC_OPTIONS) /t:library /debug:full /optimize-
$(ADDL_REF) /out:$@ CscompUtilities.cs
+
- [elpa] branch externals/csharp-mode created (now dd30649), ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode d700d31 001/459: Initial directory structure., ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 084192f 002/459: initial import, ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 2a904cb 005/459: First checkin of TFS.el - providing the ability to perform checkin/checkout to MS TFS from within emacs., ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 497a70e 009/459: Simplify csharp-mode; eliminate flymake-for-csharp.el (unnecessary), ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode a6f4b55 012/459: fix doc to remove mention of defunct flymake-for-csharp.el, ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 6b688a6 014/459: update makefile, ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode fc60bff 019/459: Fix for breakage in Emacs 24.4, ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 13984a8 004/459: First checkin of flymake-for-csharp.el, ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 3414c63 006/459: First check in of Cscomp - C# code completion.,
ELPA Syncer <=
- [elpa] externals/csharp-mode 756f086 003/459: Few updates to csharp-mode. First checkin of aspx-mode.el, for ASPX files., ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 03ed8fb 016/459: v0.8.4 - fixes bug with yasnippet integration, ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode afe5315 022/459: Add GPL v2 license as found in the original project on Google Code., ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode e940015 023/459: Update readme-file., ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode d657aa0 025/459: Fix error in heading., ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 2a28871 026/459: clean out unused files, ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 77cbcf4 030/459: Add byte-compilation, unit-test running to makefile., ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 35d295d 008/459: Fix a problem with parsing in csharp-completion.el, ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode 9f66d3b 011/459: fix doc to remove mention of flymake-command, ELPA Syncer, 2021/08/22
- [elpa] externals/csharp-mode c47e9ea 017/459: v0.8.5 - updates to imenu, fontification, ELPA Syncer, 2021/08/22