├── src ├── packages │ ├── NUnit.2.6.3 │ │ ├── license.txt │ │ ├── NUnit.2.6.3.nupkg │ │ └── lib │ │ │ └── nunit.framework.dll │ ├── libs │ │ ├── Microsoft.CodeAnalysis.dll │ │ ├── System.Collections.Immutable.dll │ │ ├── System.Reflection.Metadata.dll │ │ ├── Microsoft.CodeAnalysis.CSharp.dll │ │ ├── Microsoft.CodeAnalysis.Desktop.dll │ │ └── Microsoft.CodeAnalysis.VisualBasic.dll │ └── repositories.config ├── DotNetFiddle.IntelligentCompletion.Tests │ ├── packages.config │ ├── Samples │ │ ├── VBNet │ │ │ ├── DogMain.vb │ │ │ ├── DogOneProperty.vb │ │ │ └── DogTwoProperties.vb │ │ └── CSharp │ │ │ ├── DogOneProperty.cs │ │ │ ├── DogMain.cs │ │ │ └── DogTwoProperties.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Project │ │ ├── VBNet │ │ │ ├── VBNetTests.cs │ │ │ ├── VBNetProjectFileTests.cs │ │ │ └── VBNetProjectTextTests.cs │ │ └── CSharp │ │ │ ├── CSharpTests.cs │ │ │ ├── CSharpProjectFileTests.cs │ │ │ └── CSharpProjectTextTests.cs │ ├── DotNetFiddle.IntelligentCompletion.Tests.csproj │ ├── CSharpTests.cs │ └── VBNetTests.cs ├── DotNetFiddle.IntelligentCompletion │ ├── Language.cs │ ├── TokenTypeResult.cs │ ├── Languages │ │ ├── LanguageServiceOptions.cs │ │ ├── CSharpLanguageService.cs │ │ ├── VBNetLanguageService.cs │ │ └── LanguageService.cs │ ├── RoslynSource │ │ ├── SpecializedCollections.Empty.Array.cs │ │ ├── SpecializedCollections.cs │ │ ├── DocumentationCommentXmlNames.cs │ │ ├── DocumentationComment.cs │ │ ├── GlobalAssemblyCache.cs │ │ └── FusionAssemblyIdentity.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── DocumentationProviderFactory.cs │ ├── NamespaceToDllMap.cs │ ├── AssemblyHelper.cs │ ├── AutoCompleteItem.cs │ ├── Project.cs │ └── DotNetFiddle.IntelligentCompletion.csproj └── DotNetFiddle.IntelligentCompletion.sln ├── LICENSE ├── .gitignore └── Readme.md /src/packages/NUnit.2.6.3/license.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericpopivker/.NET-Fiddle-Intelligent-Completion/HEAD/src/packages/NUnit.2.6.3/license.txt -------------------------------------------------------------------------------- /src/packages/NUnit.2.6.3/NUnit.2.6.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericpopivker/.NET-Fiddle-Intelligent-Completion/HEAD/src/packages/NUnit.2.6.3/NUnit.2.6.3.nupkg -------------------------------------------------------------------------------- /src/packages/libs/Microsoft.CodeAnalysis.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericpopivker/.NET-Fiddle-Intelligent-Completion/HEAD/src/packages/libs/Microsoft.CodeAnalysis.dll -------------------------------------------------------------------------------- /src/packages/NUnit.2.6.3/lib/nunit.framework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericpopivker/.NET-Fiddle-Intelligent-Completion/HEAD/src/packages/NUnit.2.6.3/lib/nunit.framework.dll -------------------------------------------------------------------------------- /src/packages/libs/System.Collections.Immutable.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericpopivker/.NET-Fiddle-Intelligent-Completion/HEAD/src/packages/libs/System.Collections.Immutable.dll -------------------------------------------------------------------------------- /src/packages/libs/System.Reflection.Metadata.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericpopivker/.NET-Fiddle-Intelligent-Completion/HEAD/src/packages/libs/System.Reflection.Metadata.dll -------------------------------------------------------------------------------- /src/packages/libs/Microsoft.CodeAnalysis.CSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericpopivker/.NET-Fiddle-Intelligent-Completion/HEAD/src/packages/libs/Microsoft.CodeAnalysis.CSharp.dll -------------------------------------------------------------------------------- /src/packages/libs/Microsoft.CodeAnalysis.Desktop.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericpopivker/.NET-Fiddle-Intelligent-Completion/HEAD/src/packages/libs/Microsoft.CodeAnalysis.Desktop.dll -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/packages/libs/Microsoft.CodeAnalysis.VisualBasic.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericpopivker/.NET-Fiddle-Intelligent-Completion/HEAD/src/packages/libs/Microsoft.CodeAnalysis.VisualBasic.dll -------------------------------------------------------------------------------- /src/packages/repositories.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Samples/VBNet/DogMain.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | 3 | Public Class Program 4 | 5 | Public Shared Sub Main() 6 | { 7 | Dim dog As Dog = New Dog() 8 | dog. 9 | End Sub 10 | End Class -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Samples/VBNet/DogOneProperty.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | 3 | Public Class Dog 4 | 5 | ''' 6 | ''' Dog name 7 | ''' 8 | Public Property Name As String 9 | 10 | End Class -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Samples/CSharp/DogOneProperty.cs: -------------------------------------------------------------------------------- 1 | namespace Sample 2 | { 3 | public class Dog 4 | { 5 | /// 6 | /// Dog name 7 | /// 8 | public string Name { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Samples/CSharp/DogMain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Sample 4 | { 5 | class Program 6 | { 7 | public static void Main() 8 | { 9 | var dog = new Dog(); 10 | dog. 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Samples/VBNet/DogTwoProperties.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | 3 | Public Class Dog 4 | 5 | ''' 6 | ''' Dog name 7 | ''' 8 | Public Property Name As String 9 | 10 | Public Property Birthdate As DateTime 11 | End Class -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/Language.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace DotNetFiddle.IntelligentCompletion 5 | { 6 | [Serializable] 7 | public enum Language 8 | { 9 | [Description("C#")] 10 | CSharp = 1, 11 | [Description("VB.NET")] 12 | VbNet 13 | } 14 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Samples/CSharp/DogTwoProperties.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Sample 4 | { 5 | public class Dog 6 | { 7 | /// 8 | /// Dog name 9 | /// 10 | public string Name { get; set; } 11 | 12 | public DateTime Birthdate { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/TokenTypeResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DotNetFiddle.IntelligentCompletion 4 | { 5 | [Serializable] 6 | public class TokenTypeResult 7 | { 8 | public string Type { get; set; } 9 | 10 | public bool IsInsideArgumentList { get; set; } 11 | 12 | public int? ParentLine { get; set; } 13 | 14 | public int? ParentChar { get; set; } 15 | 16 | public string[] PreviousArgumentListTokenTypes { get; set; } 17 | 18 | public string RawArgumentsList { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/Languages/LanguageServiceOptions.cs: -------------------------------------------------------------------------------- 1 | namespace DotNetFiddle.IntelligentCompletion 2 | { 3 | /// 4 | /// Options for language service 5 | /// 6 | public class LanguageServiceOptions 7 | { 8 | public LanguageServiceOptions() 9 | { 10 | ParseDocumenation = true; 11 | } 12 | 13 | /// 14 | /// Do we need to parse xml documentation. It may affect perfomance a bit 15 | /// 16 | public bool ParseDocumenation { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/RoslynSource/SpecializedCollections.Empty.Array.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 2 | 3 | namespace Roslyn.Utilities 4 | { 5 | internal partial class SpecializedCollections 6 | { 7 | private partial class Empty 8 | { 9 | internal class Array 10 | { 11 | public static readonly T[] Instance = new T[0]; 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 ENTech Solutions 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/RoslynSource/SpecializedCollections.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 2 | 3 | using System.Collections.Generic; 4 | 5 | namespace Roslyn.Utilities 6 | { 7 | internal static partial class SpecializedCollections 8 | { 9 | public static readonly byte[] EmptyBytes = EmptyArray(); 10 | public static readonly object[] EmptyObjects = EmptyArray(); 11 | 12 | public static T[] EmptyArray() 13 | { 14 | return Empty.Array.Instance; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("IntelligentCompletion")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("IntelligentCompletion")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("48a2392e-3cbc-4e38-99da-e5bf29a41a62")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | 9 | [assembly: AssemblyTitle("IntelligentCompletion.Tests")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("Microsoft")] 13 | [assembly: AssemblyProduct("IntelligentCompletion.Tests")] 14 | [assembly: AssemblyCopyright("Copyright © 2014")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // Setting ComVisible to false makes the types in this assembly not visible 19 | // to COM components. If you need to access a type in this assembly from 20 | // COM, set the ComVisible attribute to true on that type. 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | [assembly: Guid("d4131baf-106a-4583-a7b2-d0204c2698b1")] 25 | 26 | // Version information for an assembly consists of the following four values: 27 | // 28 | // Major Version 29 | // Minor Version 30 | // Build Number 31 | // Revision 32 | // 33 | // You can specify all the values or you can default the Build and Revision Numbers 34 | // by using the '*' as shown below: 35 | // [assembly: AssemblyVersion("1.0.*")] 36 | [assembly: AssemblyVersion("1.0.0.0")] 37 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/DocumentationProviderFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace DotNetFiddle.IntelligentCompletion 8 | { 9 | /// 10 | /// We need DocumenationProvider to be able parse XML documenation for existing assemblies 11 | /// 12 | public class DocumentationProviderFactory 13 | { 14 | private static readonly Func _creator; 15 | 16 | static DocumentationProviderFactory() 17 | { 18 | try 19 | { 20 | // It's a bit hackery, as this class is internal and we can't access it, so we use Reflaction and Expression tree to get access 21 | var type = Type.GetType("Microsoft.CodeAnalysis.XmlDocumentationProvider, Microsoft.CodeAnalysis.Workspaces"); 22 | 23 | var method = type.GetMethod( 24 | "Create", 25 | BindingFlags.Static | BindingFlags.Public, 26 | null, 27 | new Type[1] { typeof(string) }, 28 | new ParameterModifier[0]); 29 | 30 | var pathParameter = Expression.Parameter(typeof(string)); 31 | var callExpression = Expression.Call(method, pathParameter); 32 | 33 | var lambdaExpr = Expression.Lambda>(callExpression, new[] { pathParameter }); 34 | 35 | _creator = lambdaExpr.Compile(); 36 | } 37 | catch 38 | { 39 | // it might can't find it if assembly changed, so in that case we just won't return anything 40 | } 41 | } 42 | 43 | public static DocumentationProvider Create(string xmlPath) 44 | { 45 | return _creator != null ? _creator(xmlPath) : null; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30723.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetFiddle.IntelligentCompletion", "DotNetFiddle.IntelligentCompletion\DotNetFiddle.IntelligentCompletion.csproj", "{C74B894F-8082-4636-BCCC-D54AB8CA04E0}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetFiddle.IntelligentCompletion.Tests", "DotNetFiddle.IntelligentCompletion.Tests\DotNetFiddle.IntelligentCompletion.Tests.csproj", "{16B5B391-4BDD-4FF4-B954-EB5C8E817179}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {C74B894F-8082-4636-BCCC-D54AB8CA04E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {C74B894F-8082-4636-BCCC-D54AB8CA04E0}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {C74B894F-8082-4636-BCCC-D54AB8CA04E0}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {C74B894F-8082-4636-BCCC-D54AB8CA04E0}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {16B5B391-4BDD-4FF4-B954-EB5C8E817179}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {16B5B391-4BDD-4FF4-B954-EB5C8E817179}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {16B5B391-4BDD-4FF4-B954-EB5C8E817179}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {16B5B391-4BDD-4FF4-B954-EB5C8E817179}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Project/VBNet/VBNetTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | 5 | namespace DotNetFiddle.IntelligentCompletion.Tests.Project 6 | { 7 | [TestFixture] 8 | public class VBNetTests 9 | { 10 | [Test] 11 | public void AddReferenceWorks() 12 | { 13 | var sourceMain = @"Imports System 14 | Imports System.Configuration 15 | 16 | Public Class Program 17 | 18 | Public Shared Sub() 19 | ConfigurationManager.AppSettings. 20 | End Sub 21 | End Class"; 22 | 23 | var service = new VBNetLanguageService(); 24 | 25 | var files = new Dictionary() 26 | { 27 | {"main.vb", sourceMain} 28 | }; 29 | 30 | var project = service.GetProject(files); 31 | Assert.IsNotNull(project); 32 | var configIdentity = project.ReferencedAssemblies.FirstOrDefault(i => i.Name == "System.Configuration"); 33 | 34 | Assert.IsNull(configIdentity); 35 | 36 | project.AddReference("System.Configuration"); 37 | 38 | configIdentity = project.ReferencedAssemblies.FirstOrDefault(i => i.Name == "System.Configuration"); 39 | Assert.IsNotNull(configIdentity); 40 | 41 | int dotIndex = sourceMain.LastIndexOf(".") + 1; 42 | var autoCompleteItems = service.GetAutoCompleteItems(project, "main.vb", dotIndex); 43 | Assert.IsTrue(autoCompleteItems.Any()); 44 | 45 | project.RemoveReference("System.Configuration"); 46 | configIdentity = project.ReferencedAssemblies.FirstOrDefault(i => i.Name == "System.Configuration"); 47 | 48 | Assert.IsNull(configIdentity); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Project/CSharp/CSharpTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | 5 | namespace DotNetFiddle.IntelligentCompletion.Tests.Project 6 | { 7 | [TestFixture] 8 | public class CSharpTests 9 | { 10 | [Test] 11 | public void AddReferenceWorks() 12 | { 13 | var sourceMain = @"using System; 14 | using System.Configuration; 15 | 16 | public class Program 17 | { 18 | public static void Main() 19 | { 20 | ConfigurationManager.AppSettings. 21 | } 22 | }"; 23 | 24 | var service = new CSharpLanguageService(); 25 | 26 | var files = new Dictionary() 27 | { 28 | {"main.cs", sourceMain} 29 | }; 30 | 31 | var project = service.GetProject(files); 32 | Assert.IsNotNull(project); 33 | var configIdentity = project.ReferencedAssemblies.FirstOrDefault(i => i.Name == "System.Configuration"); 34 | 35 | Assert.IsNull(configIdentity); 36 | 37 | project.AddReference("System.Configuration"); 38 | 39 | configIdentity = project.ReferencedAssemblies.FirstOrDefault(i => i.Name == "System.Configuration"); 40 | Assert.IsNotNull(configIdentity); 41 | 42 | int dotIndex = sourceMain.LastIndexOf(".") + 1; 43 | var autoCompleteItems = service.GetAutoCompleteItems(project, "main.cs", dotIndex); 44 | Assert.IsTrue(autoCompleteItems.Any()); 45 | 46 | project.RemoveReference("System.Configuration"); 47 | configIdentity = project.ReferencedAssemblies.FirstOrDefault(i => i.Name == "System.Configuration"); 48 | 49 | Assert.IsNull(configIdentity); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/NamespaceToDllMap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DotNetFiddle.IntelligentCompletion 5 | { 6 | public class NamespaceToDllMap 7 | { 8 | protected internal static readonly Dictionary _map; 9 | 10 | static NamespaceToDllMap() 11 | { 12 | var namespaces = new List 13 | { 14 | "System", 15 | "System.ComponentModel.DataAnnotations", 16 | "System.Core", 17 | "System.Data", 18 | "System.Data.DataSetExtensions", 19 | "System.Data.Entity", 20 | "System.Data.Linq", 21 | "System.Drawing", 22 | "System.EnterpriseServices", 23 | "System.Net", 24 | "System.Numerics", 25 | "System.Web", 26 | "System.Xml", 27 | "System.Xml.Linq" 28 | }; 29 | 30 | _map = new Dictionary(); 31 | foreach (string ns in namespaces) 32 | _map[ns] = ns; 33 | 34 | _map["System.Linq"] = "System.Core"; 35 | _map.Remove("System.Core"); 36 | } 37 | 38 | 39 | public static List GetDlls(List namespaces) 40 | { 41 | var dlls = new List(); 42 | foreach (string ns in namespaces) 43 | { 44 | string dll = GetDllFromNamespace(ns); 45 | if (dll != null && !dlls.Contains(dll)) 46 | dlls.Add(dll); 47 | } 48 | 49 | return dlls; 50 | } 51 | 52 | private static string GetDllFromNamespace(string ns) 53 | { 54 | //Keep removing last namespace till there is a match 55 | while (true) 56 | { 57 | if (String.IsNullOrEmpty(ns)) 58 | return null; 59 | 60 | if (_map.ContainsKey(ns)) 61 | return _map[ns] + ".dll"; 62 | 63 | int index = ns.LastIndexOf("."); 64 | if (index == -1) 65 | return null; 66 | 67 | if (index == 0) 68 | return null; 69 | 70 | ns = ns.Substring(0, index); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/RoslynSource/DocumentationCommentXmlNames.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 2 | 3 | using System; 4 | 5 | namespace Microsoft.CodeAnalysis.Shared.Utilities 6 | { 7 | internal static class DocumentationCommentXmlNames 8 | { 9 | public const string CElementName = "c"; 10 | public const string CodeElementName = "code"; 11 | public const string ExampleElementName = "example"; 12 | public const string ExceptionElementName = "exception"; 13 | public const string IncludeElementName = "include"; 14 | public const string ListElementName = "list"; 15 | public const string ParaElementName = "para"; 16 | public const string ParameterElementName = "param"; 17 | public const string ParameterReferenceElementName = "paramref"; 18 | public const string PermissionElementName = "permission"; 19 | public const string RemarksElementName = "remarks"; 20 | public const string ReturnsElementName = "returns"; 21 | public const string SeeElementName = "see"; 22 | public const string SeeAlsoElementName = "seealso"; 23 | public const string SummaryElementName = "summary"; 24 | public const string TypeParameterElementName = "typeparam"; 25 | public const string TypeParameterReferenceElementName = "typeparamref"; 26 | public const string ValueElementName = "value"; 27 | public const string CrefAttributeName = "cref"; 28 | public const string NameAttributeName = "name"; 29 | public const string FileAttributeName = "file"; 30 | public const string PathAttributeName = "path"; 31 | public const string TypeAttributeName = "type"; 32 | 33 | public static bool ElementEquals(string name1, string name2, bool fromVb = false) 34 | { 35 | return string.Equals(name1, name2, fromVb ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); 36 | } 37 | 38 | public static bool AttributeEquals(string name1, string name2) 39 | { 40 | return string.Equals(name1, name2, StringComparison.Ordinal); 41 | } 42 | 43 | public new static bool Equals(object left, object right) 44 | { 45 | return object.Equals(left, right); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/AssemblyHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.IO; 4 | using System.Reflection; 5 | 6 | using Microsoft.CodeAnalysis; 7 | 8 | namespace DotNetFiddle.IntelligentCompletion 9 | { 10 | public class AssemblyHelper 11 | { 12 | private static readonly ConcurrentDictionary _assemblyLocations = 13 | new ConcurrentDictionary(); 14 | 15 | //From http://stackoverflow.com/questions/52797/how-do-i-get-the-path-of-the-assembly-the-code-is-in 16 | public static string GetAssemblyLocation(Assembly assembly) 17 | { 18 | if (assembly.IsDynamic) 19 | { 20 | return null; 21 | } 22 | 23 | string codeBase = assembly.CodeBase; 24 | UriBuilder uri = new UriBuilder(codeBase); 25 | 26 | string path = Uri.UnescapeDataString(uri.Path); 27 | path = Path.GetDirectoryName(path); 28 | path = Path.Combine(path, Path.GetFileName(assembly.Location)); 29 | 30 | return path; 31 | } 32 | 33 | public static string GetAssemblyDirectory(Assembly assembly) 34 | { 35 | return Path.GetDirectoryName(GetAssemblyLocation(assembly)); 36 | } 37 | 38 | public static string GetGACAssemblyLocation(string assemblyDisplayName) 39 | { 40 | return _assemblyLocations.GetOrAdd( 41 | assemblyDisplayName, 42 | (name) => 43 | { 44 | string path; 45 | GlobalAssemblyCache.ResolvePartialName(assemblyDisplayName, out path); 46 | return path; 47 | }); 48 | } 49 | 50 | public static string GetAssemblyLocation(string assemblyDisplayName) 51 | { 52 | // search in .Net framework folder 53 | var systemPath = GetAssemblyDirectory(typeof(object).Assembly); 54 | string assemblyFullPath = Path.Combine(systemPath, assemblyDisplayName + ".dll"); 55 | 56 | if (!File.Exists(assemblyFullPath)) 57 | { 58 | if (Environment.OSVersion.Platform == PlatformID.Win32NT) 59 | { 60 | // due to changes in Roslyn, ResolveAssemblyName won't search in GAC, because it's platform specific logic 61 | // http://roslyn.codeplex.com/discussions/541557 62 | assemblyFullPath = GetGACAssemblyLocation(assemblyDisplayName); 63 | } 64 | // so the last step would be check application BIN folder for it 65 | if (string.IsNullOrWhiteSpace(assemblyFullPath)) 66 | { 67 | assemblyFullPath = AssemblyHelper.GetAssemblyLocation(typeof(LanguageService).Assembly); 68 | assemblyFullPath = Path.GetDirectoryName(assemblyFullPath); 69 | AssemblyName assemblyName = new AssemblyName(assemblyDisplayName); 70 | assemblyFullPath = Path.Combine(assemblyFullPath, assemblyName.Name + ".dll"); 71 | } 72 | } 73 | return assemblyFullPath; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/AutoCompleteItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | 6 | namespace DotNetFiddle.IntelligentCompletion 7 | { 8 | 9 | public enum AutoCompleteItemType 10 | { 11 | Variable = 0, 12 | Property = 1, 13 | Method = 2, 14 | Class = 3, 15 | Namespace = 4 16 | } 17 | 18 | [Serializable] 19 | [DebuggerDisplay("{ItemType}: {Name}. Returns {Type}")] 20 | public class AutoCompleteItem : ICloneable 21 | { 22 | public string Name { get; set; } 23 | 24 | public string Type { get; set; } 25 | 26 | public bool IsStatic { get; set; } 27 | 28 | public bool IsGeneric { get; set; } 29 | 30 | public bool IsExtension { get; set; } 31 | 32 | public string Description { get; set; } 33 | 34 | public AutoCompleteItemType ItemType { get; set; } 35 | 36 | //Args - method parameters, constructor parameters for classes 37 | public AutoCompleteItemParameter[] Params { get; set; } 38 | 39 | public object Clone() 40 | { 41 | var newItem = (AutoCompleteItem)this.MemberwiseClone(); 42 | newItem.Params = null; 43 | return newItem; 44 | } 45 | } 46 | 47 | 48 | public class AutoCompleteItemEqualityComparer : IEqualityComparer 49 | { 50 | public bool Equals(AutoCompleteItem x, AutoCompleteItem y) 51 | { 52 | if (x.ItemType != y.ItemType) return false; 53 | if (x.Name != y.Name) return false; 54 | if (x.Type != y.Type) return false; 55 | if (x.Params != null && y.Params != null && x.Params.Count() != y.Params.Count()) return false; 56 | if (x.Params != null && y.Params != null && 57 | !x.Params.Except(y.Params, new AutoCompleteItemParameterEqualityComparer()).Any() && 58 | !y.Params.Except(x.Params, new AutoCompleteItemParameterEqualityComparer()).Any()) return false; 59 | return true; 60 | } 61 | 62 | public int GetHashCode(AutoCompleteItem obj) 63 | { 64 | return 65 | String.Format("{0}-{1}-{2}-{3}", obj.Type, obj.ItemType, obj.Name, 66 | obj.Params != null ? String.Join("-", obj.Params.Select(p => p.Name + p.Type)) : null) 67 | .GetHashCode(); 68 | } 69 | } 70 | 71 | 72 | [Serializable] 73 | public class AutoCompleteItemParameter 74 | { 75 | public string Name { get; set; } 76 | 77 | public string Type { get; set; } 78 | 79 | public string Description { get; set; } 80 | 81 | public bool IsParams { get; set; } 82 | 83 | public bool IsOptional { get; set; } 84 | } 85 | 86 | 87 | public class AutoCompleteItemParameterEqualityComparer : IEqualityComparer 88 | { 89 | public bool Equals(AutoCompleteItemParameter x, AutoCompleteItemParameter y) 90 | { 91 | if (x.Name != y.Name) return false; 92 | if (x.Type != y.Type) return false; 93 | return true; 94 | } 95 | 96 | public int GetHashCode(AutoCompleteItemParameter obj) 97 | { 98 | return String.Format("{0}-{1}", obj.Type, obj.Name).GetHashCode(); 99 | } 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | UnitTestResults.html 25 | 26 | *_i.c 27 | *_p.c 28 | *.ilk 29 | *.meta 30 | *.obj 31 | *.pch 32 | *.pdb 33 | *.pgc 34 | *.pgd 35 | *.rsp 36 | *.sbr 37 | *.tlb 38 | *.tli 39 | *.tlh 40 | *.tmp 41 | *.tmp_proj 42 | *.log 43 | *.vspscc 44 | *.vssscc 45 | .builds 46 | *.pidb 47 | *.log 48 | *.scc 49 | 50 | # Visual Studio cache files 51 | *.sln.ide/ 52 | 53 | # Visual C++ cache files 54 | ipch/ 55 | *.aps 56 | *.ncb 57 | *.opensdf 58 | *.sdf 59 | *.cachefile 60 | 61 | # Visual Studio profiler 62 | *.psess 63 | *.vsp 64 | *.vspx 65 | 66 | # Guidance Automation Toolkit 67 | *.gpState 68 | 69 | # ReSharper is a .NET coding add-in 70 | _ReSharper*/ 71 | *.[Rr]e[Ss]harper 72 | 73 | # TeamCity is a build add-in 74 | _TeamCity* 75 | 76 | # DotCover is a Code Coverage Tool 77 | *.dotCover 78 | 79 | # NCrunch 80 | *.ncrunch* 81 | .*crunch*.local.xml 82 | 83 | # Installshield output folder 84 | [Ee]xpress/ 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish/ 98 | 99 | # Publish Web Output 100 | *.Publish.xml 101 | 102 | # NuGet Packages Directory 103 | #packages/ 104 | 105 | # Windows Azure Build Output 106 | csx 107 | *.build.csdef 108 | 109 | # Windows Store app package directory 110 | AppPackages/ 111 | 112 | # Others 113 | sql/ 114 | *.Cache 115 | ClientBin/ 116 | [Ss]tyle[Cc]op.* 117 | ~$* 118 | *~ 119 | *.dbmdl 120 | *.[Pp]ublish.xml 121 | *.pfx 122 | *.publishsettings 123 | 124 | # RIA/Silverlight projects 125 | Generated_Code/ 126 | 127 | # Backup & report files from converting an old project file to a newer 128 | # Visual Studio version. Backup files are not needed, because we have git ;-) 129 | _UpgradeReport_Files/ 130 | Backup*/ 131 | UpgradeLog*.XML 132 | UpgradeLog*.htm 133 | 134 | # SQL Server files 135 | App_Data/*.mdf 136 | App_Data/*.ldf 137 | 138 | 139 | #LightSwitch generated files 140 | GeneratedArtifacts/ 141 | _Pvt_Extensions/ 142 | ModelManifest.xml 143 | 144 | # ========================= 145 | # Windows detritus 146 | # ========================= 147 | 148 | # Windows image file caches 149 | Thumbs.db 150 | ehthumbs.db 151 | 152 | # Folder config file 153 | Desktop.ini 154 | 155 | # Recycle Bin used on file shares 156 | $RECYCLE.BIN/ 157 | 158 | # Mac desktop service store files 159 | .DS_Store 160 | -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/Languages/CSharpLanguageService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.CSharp; 7 | 8 | namespace DotNetFiddle.IntelligentCompletion 9 | { 10 | /// 11 | /// CSharp related parsing logic 12 | /// 13 | public class CSharpLanguageService : LanguageService 14 | { 15 | private readonly CSharpParseOptions _options; 16 | 17 | private static readonly CSharpParseOptions DefaultOptions = 18 | new CSharpParseOptions(LanguageVersion.CSharp6).WithDocumentationMode(DocumentationMode.Parse); 19 | 20 | public CSharpLanguageService() 21 | { 22 | _options = DefaultOptions; 23 | } 24 | 25 | public CSharpLanguageService(CSharpParseOptions options) 26 | : this(options, null) 27 | { 28 | } 29 | 30 | public CSharpLanguageService(CSharpParseOptions options, LanguageServiceOptions serviceOptions) 31 | : base(serviceOptions) 32 | { 33 | _options = options ?? DefaultOptions; 34 | } 35 | 36 | public override SyntaxTree ParseSyntaxTreeText(string code, string path = "") 37 | { 38 | var tree = ParseSyntaxTree(code, _options, path); 39 | return tree; 40 | } 41 | 42 | public override Language Language 43 | { 44 | get { return Language.CSharp; } 45 | } 46 | 47 | internal override string GetUsingKeyword() 48 | { 49 | return "using"; 50 | } 51 | 52 | protected override int GetNewKeywordCode() 53 | { 54 | return (int) SyntaxKind.ObjectCreationExpression; 55 | } 56 | 57 | protected override int GetUsingDirectiveCode() 58 | { 59 | return (int) SyntaxKind.UsingDirective; 60 | } 61 | 62 | protected override int GetArgumentCode() 63 | { 64 | return (int) SyntaxKind.Argument; 65 | } 66 | 67 | protected override int GetArgumentListCode() 68 | { 69 | return (int) SyntaxKind.ArgumentList; 70 | } 71 | 72 | protected override int GetIdentifierCode() 73 | { 74 | return (int) SyntaxKind.IdentifierToken; 75 | } 76 | 77 | protected override int GetQualifiedNameCode() 78 | { 79 | return (int) SyntaxKind.QualifiedName; 80 | } 81 | 82 | public override Type GetSyntaxTreeType() 83 | { 84 | return typeof (SyntaxTree); 85 | } 86 | 87 | public override string GetUsingNamespaceLinePattern() 88 | { 89 | return "^\\s*using\\s*(\\S*)\\s*;"; 90 | } 91 | 92 | protected SyntaxTree ParseSyntaxTree(string code, CSharpParseOptions parseOptions, string path = "") 93 | { 94 | var tree = CSharpSyntaxTree.ParseText(code, parseOptions, path); 95 | return tree; 96 | } 97 | 98 | public override Compilation CreateCompilation(string compilatioName, SyntaxTree[] syntaxTrees, 99 | List metadataReferences) 100 | { 101 | Compilation compilation = CSharpCompilation.Create(compilatioName, syntaxTrees.ToArray(), metadataReferences, 102 | new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); 103 | 104 | return compilation; 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Project/VBNet/VBNetProjectFileTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using NUnit.Framework; 6 | 7 | namespace DotNetFiddle.IntelligentCompletion.Tests.Project 8 | { 9 | [TestFixture] 10 | public class VBNetProjectFileTests 11 | { 12 | [Test] 13 | public void Project_Works() 14 | { 15 | var service = new VBNetLanguageService(); 16 | 17 | var currentPath = AssemblyHelper.GetAssemblyDirectory(typeof (CSharpProjectFileTests).Assembly); 18 | currentPath = Path.Combine(currentPath, "Samples", "VBNet"); 19 | 20 | string dogMainPath = Path.Combine(currentPath, "DogMain.vb"); 21 | string dogPath = Path.Combine(currentPath, "DogOneProperty.vb"); 22 | 23 | var files = new List() 24 | { 25 | dogMainPath, dogPath 26 | }; 27 | 28 | var project = service.GetProject(files); 29 | Assert.IsNotNull(project); 30 | 31 | var sourceMain = project.Sources[dogMainPath]; 32 | int dotIndex = sourceMain.IndexOf(".") + 1; 33 | var autoCompleteItems = service.GetAutoCompleteItems(project, dogMainPath, dotIndex); 34 | Assert.IsTrue(autoCompleteItems.Any()); 35 | 36 | var propertyName = 37 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 38 | 39 | Assert.IsNotNull(propertyName, "property should be returned from language service"); 40 | Assert.AreEqual("Name", propertyName.Name); 41 | Assert.AreEqual("String", propertyName.Type); 42 | Assert.IsFalse(propertyName.IsStatic); 43 | Assert.IsFalse(propertyName.IsGeneric); 44 | Assert.IsFalse(propertyName.IsExtension); 45 | Assert.AreEqual(AutoCompleteItemType.Property, propertyName.ItemType); 46 | Assert.AreEqual("Dog name", propertyName.Description); 47 | } 48 | 49 | [Test] 50 | public void Project_Changed_Works() 51 | { 52 | var service = new VBNetLanguageService(); 53 | 54 | var currentPath = AssemblyHelper.GetAssemblyDirectory(typeof (CSharpProjectFileTests).Assembly); 55 | currentPath = Path.Combine(currentPath, "Samples", "VBNet"); 56 | 57 | string dogMainPath = Path.Combine(currentPath, "DogMain.vb"); 58 | string dogPath = Path.Combine(currentPath, "DogOneProperty.vb"); 59 | string dogNewPath = Path.Combine(currentPath, "DogTwoProperties.vb"); 60 | 61 | var files = new List() 62 | { 63 | dogMainPath, 64 | dogPath 65 | }; 66 | 67 | var project = service.GetProject(files); 68 | Assert.IsNotNull(project); 69 | 70 | var sourceMain = project.Sources[dogMainPath]; 71 | int dotIndex = sourceMain.IndexOf(".") + 1; 72 | var autoCompleteItems = service.GetAutoCompleteItems(project, dogMainPath, dotIndex); 73 | Assert.IsTrue(autoCompleteItems.Any()); 74 | 75 | var property = 76 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 77 | Assert.IsNotNull(property, "property Name should be returned from language service"); 78 | 79 | var newDogContent = File.ReadAllText(dogNewPath); 80 | project.ReplaceSourceFile(dogPath, newDogContent); 81 | 82 | autoCompleteItems = service.GetAutoCompleteItems(project, dogMainPath, dotIndex); 83 | property = 84 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 85 | 86 | Assert.IsNotNull(property, "property Name should be returned from language service"); 87 | 88 | property = 89 | autoCompleteItems.FirstOrDefault( 90 | p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Birthdate"); 91 | 92 | Assert.IsNotNull(property, "property Birthdate should be returned from language service"); 93 | } 94 | 95 | } 96 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Project/CSharp/CSharpProjectFileTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using NUnit.Framework; 6 | 7 | namespace DotNetFiddle.IntelligentCompletion.Tests.Project 8 | { 9 | [TestFixture] 10 | public class CSharpProjectFileTests 11 | { 12 | [Test] 13 | public void Project_Works() 14 | { 15 | var service = new CSharpLanguageService(); 16 | 17 | var currentPath = AssemblyHelper.GetAssemblyDirectory(typeof (CSharpProjectFileTests).Assembly); 18 | currentPath = Path.Combine(currentPath, "Samples", "CSharp"); 19 | 20 | string dogMainPath = Path.Combine(currentPath, "DogMain.cs"); 21 | string dogPath = Path.Combine(currentPath, "DogOneProperty.cs"); 22 | 23 | var files = new List() 24 | { 25 | dogMainPath, dogPath 26 | }; 27 | 28 | var project = service.GetProject(files); 29 | Assert.IsNotNull(project); 30 | 31 | var sourceMain = project.Sources[dogMainPath]; 32 | int dotIndex = sourceMain.IndexOf(".") + 1; 33 | var autoCompleteItems = service.GetAutoCompleteItems(project, dogMainPath, dotIndex); 34 | Assert.IsTrue(autoCompleteItems.Any()); 35 | 36 | var propertyName = 37 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 38 | 39 | Assert.IsNotNull(propertyName, "property should be returned from language service"); 40 | Assert.AreEqual("Name", propertyName.Name); 41 | Assert.AreEqual("String", propertyName.Type); 42 | Assert.IsFalse(propertyName.IsStatic); 43 | Assert.IsFalse(propertyName.IsGeneric); 44 | Assert.IsFalse(propertyName.IsExtension); 45 | Assert.AreEqual(AutoCompleteItemType.Property, propertyName.ItemType); 46 | Assert.AreEqual("Dog name", propertyName.Description); 47 | } 48 | 49 | [Test] 50 | public void Project_Changed_Works() 51 | { 52 | var service = new CSharpLanguageService(); 53 | 54 | var currentPath = AssemblyHelper.GetAssemblyDirectory(typeof (CSharpProjectFileTests).Assembly); 55 | currentPath = Path.Combine(currentPath, "Samples", "CSharp"); 56 | 57 | string dogMainPath = Path.Combine(currentPath, "DogMain.cs"); 58 | string dogPath = Path.Combine(currentPath, "DogOneProperty.cs"); 59 | string dogNewPath = Path.Combine(currentPath, "DogTwoProperties.cs"); 60 | 61 | var files = new List() 62 | { 63 | dogMainPath, 64 | dogPath 65 | }; 66 | 67 | var project = service.GetProject(files); 68 | Assert.IsNotNull(project); 69 | 70 | var sourceMain = project.Sources[dogMainPath]; 71 | int dotIndex = sourceMain.IndexOf(".") + 1; 72 | var autoCompleteItems = service.GetAutoCompleteItems(project, dogMainPath, dotIndex); 73 | Assert.IsTrue(autoCompleteItems.Any()); 74 | 75 | var property = 76 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 77 | Assert.IsNotNull(property, "property Name should be returned from language service"); 78 | 79 | var newDogContent = File.ReadAllText(dogNewPath); 80 | project.ReplaceSourceFile(dogPath, newDogContent); 81 | 82 | autoCompleteItems = service.GetAutoCompleteItems(project, dogMainPath, dotIndex); 83 | property = 84 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 85 | 86 | Assert.IsNotNull(property, "property Name should be returned from language service"); 87 | 88 | property = 89 | autoCompleteItems.FirstOrDefault( 90 | p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Birthdate"); 91 | 92 | Assert.IsNotNull(property, "property Birthdate should be returned from language service"); 93 | } 94 | 95 | } 96 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/Project.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace DotNetFiddle.IntelligentCompletion 6 | { 7 | public class Project 8 | { 9 | public Project(LanguageService languageService, Compilation compilation) 10 | { 11 | LanguageService = languageService; 12 | Compilation = compilation; 13 | } 14 | 15 | /// 16 | /// Roslyn complication 17 | /// 18 | public Compilation Compilation { get; private set; } 19 | 20 | /// 21 | /// Language service that used for project creation 22 | /// 23 | public LanguageService LanguageService { get; private set; } 24 | 25 | /// 26 | /// Mapping between File IDs and Roslyn's SyntaxTrees 27 | /// 28 | public Dictionary Trees { get; internal set; } 29 | 30 | /// 31 | /// Mapping between File IDs and sources. File ID can be path to the file or some unique string that will identify this source 32 | /// 33 | public Dictionary Sources { get; internal set; } 34 | 35 | #region References 36 | 37 | /// 38 | /// List of already referenced assemblies 39 | /// 40 | public IEnumerable ReferencedAssemblies 41 | { 42 | get { return Compilation.ReferencedAssemblyNames; } 43 | } 44 | 45 | /// 46 | /// Add reference to the project 47 | /// 48 | /// Assembly name like System.Xml which can be used for search or full path assembly 49 | public void AddReference(string displayNameOrPath) 50 | { 51 | var reference = LanguageService.CreateMetadataReference(displayNameOrPath); 52 | Compilation = Compilation.AddReferences(reference); 53 | } 54 | 55 | /// 56 | /// Remove reference from project 57 | /// 58 | /// Assembly name like System.Xml which can be used for search or full path assembly 59 | public void RemoveReference(string displayNameOrPath) 60 | { 61 | // we need to find metadata reference to get Display property with assembly full path, and then we can remove it 62 | var newReference = LanguageService.CreateMetadataReference(displayNameOrPath); 63 | var existingReference = Compilation.References.FirstOrDefault(r => r.Display == newReference.Display); 64 | Compilation = Compilation.RemoveReferences(existingReference); 65 | } 66 | 67 | #endregion 68 | 69 | /// 70 | /// Replace loaded source file by new one if it's changed 71 | /// 72 | /// File ID or path 73 | /// source code 74 | public void ReplaceSourceFile(string fileId, string source) 75 | { 76 | var oldSourceTree = Trees[fileId]; 77 | var newSourceTree = LanguageService.ParseSyntaxTreeText(source, fileId); 78 | 79 | Trees[fileId] = newSourceTree; 80 | Sources[fileId] = source; 81 | 82 | // replacing new tree 83 | Compilation = Compilation.ReplaceSyntaxTree(oldSourceTree, newSourceTree); 84 | } 85 | 86 | 87 | /// 88 | /// Reload changed file from file system 89 | /// 90 | /// File path 91 | public void ReloadSourceFile(string fileId) 92 | { 93 | var oldSourceTree = Trees[fileId]; 94 | var newSourceTree = LanguageService.ParseSyntaxTreeFile(fileId); 95 | 96 | Trees[fileId] = newSourceTree; 97 | Sources[fileId] = newSourceTree.ToString(); 98 | 99 | // replacing new tree 100 | Compilation = Compilation.ReplaceSyntaxTree(oldSourceTree, newSourceTree); 101 | } 102 | 103 | 104 | } 105 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Project/CSharp/CSharpProjectTextTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | 5 | namespace DotNetFiddle.IntelligentCompletion.Tests.Project 6 | { 7 | [TestFixture] 8 | public class CSharpProjectTextTests 9 | { 10 | [Test] 11 | public void Project_Works() 12 | { 13 | var source = @"using System; 14 | 15 | public class Dog 16 | { 17 | /// 18 | /// Dog name 19 | /// 20 | public string Name {get;set;} 21 | }"; 22 | 23 | var sourceMain = @"using System; 24 | 25 | public class Program 26 | { 27 | public static void Main() 28 | { 29 | var dog = new Dog(); 30 | dog. 31 | } 32 | }"; 33 | 34 | var service = new CSharpLanguageService(); 35 | 36 | var files = new Dictionary() 37 | { 38 | {"dog.cs", source}, 39 | {"main.cs", sourceMain} 40 | }; 41 | 42 | var project = service.GetProject(files); 43 | Assert.IsNotNull(project); 44 | 45 | int dotIndex = sourceMain.IndexOf(".") + 1; 46 | var autoCompleteItems = service.GetAutoCompleteItems(project, "main.cs", dotIndex); 47 | Assert.IsTrue(autoCompleteItems.Any()); 48 | 49 | var propertyName = 50 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 51 | 52 | Assert.IsNotNull(propertyName, "property should be returned from language service"); 53 | Assert.AreEqual("Name", propertyName.Name); 54 | Assert.AreEqual("String", propertyName.Type); 55 | Assert.IsFalse(propertyName.IsStatic); 56 | Assert.IsFalse(propertyName.IsGeneric); 57 | Assert.IsFalse(propertyName.IsExtension); 58 | Assert.AreEqual(AutoCompleteItemType.Property, propertyName.ItemType); 59 | Assert.AreEqual("Dog name", propertyName.Description); 60 | } 61 | 62 | [Test] 63 | public void Project_Changed_Works() 64 | { 65 | var source = @"using System; 66 | 67 | public class Dog 68 | { 69 | /// 70 | /// Dog name 71 | /// 72 | public string Name {get;set;} 73 | }"; 74 | 75 | var sourceMain = @"using System; 76 | 77 | public class Program 78 | { 79 | public static void Main() 80 | { 81 | var dog = new Dog(); 82 | dog. 83 | } 84 | }"; 85 | 86 | var service = new CSharpLanguageService(); 87 | 88 | var files = new Dictionary() 89 | { 90 | {"dog.cs", source}, 91 | {"main.cs", sourceMain} 92 | }; 93 | 94 | var project = service.GetProject(files); 95 | Assert.IsNotNull(project); 96 | 97 | int dotIndex = sourceMain.IndexOf(".") + 1; 98 | var autoCompleteItems = service.GetAutoCompleteItems(project, "main.cs", dotIndex); 99 | Assert.IsTrue(autoCompleteItems.Any()); 100 | 101 | var property = 102 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 103 | 104 | Assert.IsNotNull(property, "property should be returned from language service"); 105 | 106 | source = @"using System; 107 | 108 | public class Dog 109 | { 110 | /// 111 | /// Dog name 112 | /// 113 | public string Name {get;set;} 114 | 115 | public DateTime Birthdate {get;set;} 116 | }"; 117 | 118 | project.ReplaceSourceFile("dog.cs", source); 119 | 120 | autoCompleteItems = service.GetAutoCompleteItems(project, "main.cs", dotIndex); 121 | Assert.IsTrue(autoCompleteItems.Any()); 122 | 123 | property = 124 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 125 | 126 | Assert.IsNotNull(property, "property Name should be returned from language service"); 127 | 128 | property = 129 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Birthdate"); 130 | 131 | Assert.IsNotNull(property, "property Birthdate should be returned from language service"); 132 | 133 | } 134 | 135 | } 136 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/Project/VBNet/VBNetProjectTextTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | 5 | namespace DotNetFiddle.IntelligentCompletion.Tests.Project 6 | { 7 | [TestFixture] 8 | public class VBNetProjectTextTests 9 | { 10 | [Test] 11 | public void Project_Works() 12 | { 13 | var source = @"Imports System 14 | 15 | Public Class Dog 16 | 17 | ''' 18 | ''' Dog name 19 | ''' 20 | Public property Name As String 21 | 22 | End Class"; 23 | 24 | var sourceMain = @"Imports System 25 | 26 | Public Class Program 27 | 28 | Public Shared Sub Main() 29 | { 30 | Dim dog as Dog = New Dog() 31 | dog. 32 | End Sub 33 | End Class"; 34 | 35 | var service = new VBNetLanguageService(); 36 | 37 | var files = new Dictionary() 38 | { 39 | {"dog.vb", source}, 40 | {"main.vb", sourceMain} 41 | }; 42 | 43 | var project = service.GetProject(files); 44 | Assert.IsNotNull(project); 45 | 46 | int dotIndex = sourceMain.IndexOf(".") + 1; 47 | var autoCompleteItems = service.GetAutoCompleteItems(project, "main.vb", dotIndex); 48 | Assert.IsTrue(autoCompleteItems.Any()); 49 | 50 | var propertyName = 51 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 52 | 53 | Assert.IsNotNull(propertyName, "property should be returned from language service"); 54 | Assert.AreEqual("Name", propertyName.Name); 55 | Assert.AreEqual("String", propertyName.Type); 56 | Assert.IsFalse(propertyName.IsStatic); 57 | Assert.IsFalse(propertyName.IsGeneric); 58 | Assert.IsFalse(propertyName.IsExtension); 59 | Assert.AreEqual(AutoCompleteItemType.Property, propertyName.ItemType); 60 | Assert.AreEqual("Dog name", propertyName.Description); 61 | } 62 | 63 | [Test] 64 | public void Project_Changed_Works() 65 | { 66 | var source = @"Imports System 67 | 68 | Public Class Dog 69 | 70 | ''' 71 | ''' Dog name 72 | ''' 73 | Public property Name As String 74 | 75 | End Class"; 76 | 77 | var sourceMain = @"Imports System 78 | 79 | Public Class Program 80 | 81 | Public Shared Sub Main() 82 | { 83 | Dim dog as Dog = New Dog() 84 | dog. 85 | End Sub 86 | End Class"; 87 | 88 | 89 | var service = new VBNetLanguageService(); 90 | 91 | var files = new Dictionary() 92 | { 93 | {"dog.vb", source}, 94 | {"main.vb", sourceMain} 95 | }; 96 | 97 | var project = service.GetProject(files); 98 | Assert.IsNotNull(project); 99 | 100 | int dotIndex = sourceMain.IndexOf(".") + 1; 101 | var autoCompleteItems = service.GetAutoCompleteItems(project, "main.vb", dotIndex); 102 | Assert.IsTrue(autoCompleteItems.Any()); 103 | 104 | var property = 105 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 106 | 107 | Assert.IsNotNull(property, "property should be returned from language service"); 108 | 109 | source = @"Imports System 110 | 111 | Public Class Dog 112 | 113 | ''' 114 | ''' Dog name 115 | ''' 116 | Public Property Name as String 117 | 118 | Public Property Birthdate As DateTime 119 | End Class"; 120 | 121 | project.ReplaceSourceFile("dog.vb", source); 122 | 123 | autoCompleteItems = service.GetAutoCompleteItems(project, "main.vb", dotIndex); 124 | Assert.IsTrue(autoCompleteItems.Any()); 125 | 126 | property = 127 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Name"); 128 | 129 | Assert.IsNotNull(property, "property Name should be returned from language service"); 130 | 131 | property = 132 | autoCompleteItems.FirstOrDefault(p => p.ItemType == AutoCompleteItemType.Property && p.Name == "Birthdate"); 133 | 134 | Assert.IsNotNull(property, "property Birthdate should be returned from language service"); 135 | 136 | } 137 | 138 | } 139 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/Languages/VBNetLanguageService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.VisualBasic; 6 | 7 | namespace DotNetFiddle.IntelligentCompletion 8 | { 9 | /// 10 | /// VBNet related parsing logic 11 | /// 12 | public class VBNetLanguageService : LanguageService 13 | { 14 | private readonly VisualBasicParseOptions _options; 15 | 16 | private static readonly VisualBasicParseOptions DefaultOptions = 17 | new VisualBasicParseOptions().WithDocumentationMode(DocumentationMode.Parse); 18 | 19 | public VBNetLanguageService() 20 | { 21 | _options = DefaultOptions; 22 | } 23 | 24 | public VBNetLanguageService(VisualBasicParseOptions options) 25 | : this(options, null) 26 | { 27 | } 28 | 29 | public VBNetLanguageService(VisualBasicParseOptions options, LanguageServiceOptions serviceOptions) 30 | : base(serviceOptions) 31 | { 32 | _options = options ?? DefaultOptions; 33 | } 34 | 35 | public override Language Language 36 | { 37 | get { return Language.VbNet; } 38 | } 39 | 40 | public override Type GetSyntaxTreeType() 41 | { 42 | return typeof (SyntaxTree); 43 | } 44 | 45 | internal override string GetUsingKeyword() 46 | { 47 | return "imports"; 48 | } 49 | 50 | 51 | public override string GetUsingNamespaceLinePattern() 52 | { 53 | return "^\\s*imports\\s*(\\S*)\\s*$"; 54 | } 55 | 56 | protected override void AppendDefaultMetadataReferences(List references, 57 | bool includeDocumentation) 58 | { 59 | //Need to add vb or getting error 60 | //Requested operation is not available because the runtime library function 'Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute..ctor' is not defined. 61 | MetadataReference vb = CreateMetadataReference("Microsoft.VisualBasic", includeDocumentation); 62 | references.Add(vb); 63 | } 64 | 65 | 66 | // http://stackoverflow.com/questions/13601412/compilation-errors-when-dealing-with-c-sharp-script-using-roslyn 67 | protected SyntaxTree ParseSyntaxTree(string code, VisualBasicParseOptions parseOptions, string path = "") 68 | { 69 | var tree = VisualBasicSyntaxTree.ParseText(code, parseOptions, path); 70 | return tree; 71 | } 72 | 73 | public override SyntaxTree ParseSyntaxTreeText(string code, string path = "") 74 | { 75 | var tree = ParseSyntaxTree(code, _options, path); 76 | return tree; 77 | } 78 | 79 | public override Compilation CreateCompilation(string compilatioName, SyntaxTree[] syntaxTrees, 80 | List metadataReferences) 81 | { 82 | var vbSyntaxTrees = new List(); 83 | foreach (var syntaxTree in syntaxTrees) 84 | vbSyntaxTrees.Add((SyntaxTree) syntaxTree); 85 | 86 | var vbOptions = new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary); 87 | 88 | Compilation compilation = VisualBasicCompilation.Create( 89 | compilatioName, 90 | syntaxTrees: vbSyntaxTrees.ToArray(), 91 | references: metadataReferences, 92 | options: vbOptions); 93 | 94 | return compilation; 95 | } 96 | 97 | protected override int GetNewKeywordCode() 98 | { 99 | return (int) SyntaxKind.ObjectCreationExpression; 100 | } 101 | 102 | protected override int GetUsingDirectiveCode() 103 | { 104 | return (int) SyntaxKind.ImportsStatement; 105 | } 106 | 107 | protected override int GetArgumentCode() 108 | { 109 | return (int) SyntaxKind.SimpleArgument; 110 | } 111 | 112 | protected override int GetArgumentListCode() 113 | { 114 | return (int) SyntaxKind.ArgumentList; 115 | } 116 | 117 | protected override int GetIdentifierCode() 118 | { 119 | return (int) SyntaxKind.IdentifierToken; 120 | } 121 | 122 | protected override int GetQualifiedNameCode() 123 | { 124 | return (int) SyntaxKind.QualifiedName; 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/DotNetFiddle.IntelligentCompletion.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C74B894F-8082-4636-BCCC-D54AB8CA04E0} 8 | Library 9 | Properties 10 | DotNetFiddle.IntelligentCompletion 11 | DotNetFiddle.IntelligentCompletion 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | true 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | False 36 | ..\packages\libs\Microsoft.CodeAnalysis.dll 37 | 38 | 39 | False 40 | ..\packages\libs\Microsoft.CodeAnalysis.CSharp.dll 41 | 42 | 43 | False 44 | ..\packages\libs\Microsoft.CodeAnalysis.Desktop.dll 45 | 46 | 47 | False 48 | ..\packages\libs\Microsoft.CodeAnalysis.VisualBasic.dll 49 | 50 | 51 | 52 | False 53 | ..\packages\libs\System.Collections.Immutable.dll 54 | 55 | 56 | 57 | False 58 | ..\packages\libs\System.Reflection.Metadata.dll 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | PreserveNewest 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 96 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | .NET Fiddle Intelligent Completion 2 | =================== 3 | 4 | 5 | This is part of code that used in [DotNetFiddle](https://dotnetfiddle.net) for intellisense purposes. 6 | It based on [Roslyn](https://roslyn.codeplex.com/) and at the moment supports two languages: 7 | - C# 8 | - VBNet 9 | 10 | ---------- 11 | 12 | Linux\Mac 13 | ------------- 14 | Currently Roslyn doesn't support Linux\Mac fully, but Mono has [own forked project](https://github.com/mono/roslyn) that updates from Roslyn repository with Mono's fixes and Mono's team is trying make it up to date. We built Mono's Roslyn version and saved it as libraries in `packages\libs` folder. Thath's why we don't use NuGet for now. 15 | 16 | Current version of `.NET Fiddle Intelligent Completion` is tested under Windows\Ubuntu and all tests are passed fine. 17 | 18 | 19 | Samples 20 | ------------- 21 | ### One file example 22 | 23 | Lets assume that you have such user code: 24 | 25 | using System; 26 | using System.Text; 27 | 28 | class Program 29 | { 30 | public void Main() 31 | { 32 | var sb= new StringBuilder(); 33 | sb. 34 | 35 | Then using LanguageService classes you can get list of suggested items in the following way by passing this user code to `GetAutoCompleteItems` method: 36 | 37 | var service = new CSharpLanguageService(); 38 | List autoCompleteItems = service.GetAutoCompleteItems(SourceCodeString); 39 | 40 | Method `GetAutoCompleteItems` also has `int` parameter that can be used to specify where is user cursor are. If parameter is omitted, then it will use the latest symbol in the source code. 41 | 42 | ### Project examples 43 | For use cases when there are a lot of files and external references Intelligent Completion supports simple Project system. It has `Project` class with following signature: 44 | 45 | public class Project 46 | { 47 | public Project(LanguageService languageService, Compilation compilation){} 48 | 49 | /// 50 | /// Roslyn complication 51 | /// 52 | public Compilation Compilation { get; private set; } 53 | 54 | /// 55 | /// Language service that used for project creation 56 | /// 57 | public LanguageService LanguageService { get; private set; } 58 | 59 | /// 60 | /// Mapping between File IDs and Roslyn's SyntaxTrees 61 | /// 62 | public Dictionary Trees { get; internal set; } 63 | 64 | /// 65 | /// Mapping between File IDs and sources. File ID can be path to the file or some unique string that will identify this source 66 | /// 67 | public Dictionary Sources { get; internal set; } 68 | 69 | #region References 70 | 71 | /// 72 | /// List of already referenced assemblies 73 | /// 74 | public IEnumerable ReferencedAssemblies 75 | { 76 | get { return Compilation.ReferencedAssemblyNames; } 77 | } 78 | 79 | /// 80 | /// Add reference to the project 81 | /// 82 | /// Assembly name like System.Xml which can be used for search or full path assembly 83 | public void AddReference(string displayNameOrPath){} 84 | /// 85 | /// Remove reference from project 86 | /// 87 | /// Assembly name like System.Xml which can be used for search or full path assembly 88 | public void RemoveReference(string displayNameOrPath){} 89 | #endregion 90 | 91 | /// 92 | /// Replace loaded source file by new one if it's changed 93 | /// 94 | /// File ID or path 95 | /// source code 96 | public void ReplaceSourceFile(string fileId, string source){} 97 | 98 | 99 | /// 100 | /// Reload changed file from file system 101 | /// 102 | /// File path 103 | public void ReloadSourceFile(string fileId){} 104 | 105 | LanguageService can return `Project` instance for list of files passed to it, so this project and be used by adding\removing assembly reference and updating changed file. Main purpose of project is to cache already parsed source files, so we don't need to parse all files each time and we can parse just changed files by using `ReloadSourceFile`,`ReplaceSourceFile` methods. It should increase performance for typical use cases. 106 | 107 | Usage sample: 108 | 109 | Lets assume that we have two files: 110 | 111 | using System; 112 | 113 | public class Dog 114 | { 115 | public string Name {get;set;} 116 | } 117 | And 118 | 119 | using System; 120 | 121 | public class Program 122 | { 123 | public static void Main() 124 | { 125 | var dog = new Dog(); 126 | dog. 127 | } 128 | } 129 | 130 | Then we can use Intelligent Completionin such way: 131 | 132 | var service = new CSharpLanguageService(); 133 | 134 | var files = new Dictionary() 135 | { 136 | {"dog.cs", dogSource}, 137 | {"main.cs", mainSource} 138 | }; 139 | 140 | var project = service.GetProject(files); 141 | 142 | // here we need to know where user cursor is. But for sample we just search for the last . symbol 143 | int dotIndex = sourceMain.IndexOf(".") + 1; 144 | var autoCompleteItems = service.GetAutoCompleteItems(project, "main.cs", dotIndex); 145 | // autoCompleteItems will contain Name property 146 | 147 | In case if Dog class is changed, then we can reload it with new source in following way: 148 | 149 | project.ReplaceSourceFile("dog.cs", newSource); 150 | 151 | `"dog.cs"` is unique identifier of that source file that will be used during auto completion. We can also specify full path to files on file system, and then identifier will be full path to the file. 152 | 153 | 154 | There are a couple of nUnit tests that can be also checked for use cases. 155 | -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/DotNetFiddle.IntelligentCompletion.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {16B5B391-4BDD-4FF4-B954-EB5C8E817179} 8 | Library 9 | Properties 10 | DotNetFiddle.IntelligentCompletion.Tests 11 | DotNetFiddle.IntelligentCompletion.Tests 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | False 35 | ..\packages\libs\Microsoft.CodeAnalysis.dll 36 | 37 | 38 | False 39 | ..\packages\libs\Microsoft.CodeAnalysis.CSharp.dll 40 | 41 | 42 | False 43 | ..\packages\libs\Microsoft.CodeAnalysis.Desktop.dll 44 | 45 | 46 | ..\packages\libs\Microsoft.CodeAnalysis.VisualBasic.dll 47 | 48 | 49 | False 50 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll 51 | 52 | 53 | 54 | False 55 | ..\packages\libs\System.Collections.Immutable.dll 56 | 57 | 58 | 59 | False 60 | ..\packages\libs\System.Reflection.Metadata.dll 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | PreserveNewest 74 | 75 | 76 | PreserveNewest 77 | 78 | 79 | PreserveNewest 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | PreserveNewest 89 | 90 | 91 | PreserveNewest 92 | 93 | 94 | PreserveNewest 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | {C74B894F-8082-4636-BCCC-D54AB8CA04E0} 103 | DotNetFiddle.IntelligentCompletion 104 | 105 | 106 | 107 | 108 | 109 | 110 | 117 | -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/RoslynSource/DocumentationComment.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 2 | 3 | using Microsoft.CodeAnalysis; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Collections.Immutable; 7 | using System.IO; 8 | using System.Xml; 9 | 10 | namespace Microsoft.CodeAnalysis.Shared.Utilities 11 | { 12 | internal sealed class DocumentationComment 13 | { 14 | private readonly Dictionary parameterTexts = new Dictionary(); 15 | private readonly Dictionary typeParameterTexts = new Dictionary(); 16 | private readonly Dictionary> exceptionTexts = new Dictionary>(); 17 | public static readonly DocumentationComment Empty = new DocumentationComment(); 18 | 19 | public bool HadXmlParseError { get; private set; } 20 | 21 | public string FullXmlFragment { get; private set; } 22 | 23 | public string ExampleText { get; private set; } 24 | 25 | public string SummaryText { get; private set; } 26 | 27 | public string ReturnsText { get; private set; } 28 | 29 | public string RemarksText { get; private set; } 30 | 31 | public ImmutableArray ParameterNames { get; private set; } 32 | 33 | public ImmutableArray TypeParameterNames { get; private set; } 34 | 35 | public ImmutableArray ExceptionTypes { get; private set; } 36 | 37 | static DocumentationComment() 38 | { 39 | } 40 | 41 | private DocumentationComment() 42 | { 43 | this.ParameterNames = ImmutableArray.Create(); 44 | this.TypeParameterNames = ImmutableArray.Create(); 45 | this.ExceptionTypes = ImmutableArray.Create(); 46 | } 47 | 48 | public static DocumentationComment FromXmlFragment(string xml) 49 | { 50 | try 51 | { 52 | StringReader stringReader = new StringReader(xml); 53 | XmlReaderSettings settings = new XmlReaderSettings(); 54 | int num = 1; 55 | settings.ConformanceLevel = (ConformanceLevel)num; 56 | XmlReader xmlReader = XmlReader.Create((TextReader)stringReader, settings); 57 | DocumentationComment documentationComment = new DocumentationComment(); 58 | documentationComment.FullXmlFragment = xml; 59 | List list1 = new List(); 60 | List list2 = new List(); 61 | List list3 = new List(); 62 | try 63 | { 64 | Dictionary> dictionary = new Dictionary>(); 65 | while (!xmlReader.EOF) 66 | { 67 | if (xmlReader.IsStartElement()) 68 | { 69 | string localName = xmlReader.LocalName; 70 | if (DocumentationCommentXmlNames.ElementEquals(localName, "example", false) && documentationComment.ExampleText == null) 71 | documentationComment.ExampleText = xmlReader.ReadInnerXml().Trim(); 72 | else if (DocumentationCommentXmlNames.ElementEquals(localName, "summary", false) && documentationComment.SummaryText == null) 73 | documentationComment.SummaryText = xmlReader.ReadInnerXml().Trim(); 74 | else if (DocumentationCommentXmlNames.ElementEquals(localName, "returns", false) && documentationComment.ReturnsText == null) 75 | documentationComment.ReturnsText = xmlReader.ReadInnerXml().Trim(); 76 | else if (DocumentationCommentXmlNames.ElementEquals(localName, "remarks", false) && documentationComment.RemarksText == null) 77 | documentationComment.RemarksText = xmlReader.ReadInnerXml().Trim(); 78 | else if (DocumentationCommentXmlNames.ElementEquals(localName, "param", false)) 79 | { 80 | string attribute = xmlReader.GetAttribute("name"); 81 | string str = xmlReader.ReadInnerXml(); 82 | if (!string.IsNullOrWhiteSpace(attribute) && !documentationComment.parameterTexts.ContainsKey(attribute)) 83 | { 84 | list1.Add(attribute); 85 | documentationComment.parameterTexts.Add(attribute, str.Trim()); 86 | } 87 | } 88 | else if (DocumentationCommentXmlNames.ElementEquals(localName, "typeparam", false)) 89 | { 90 | string attribute = xmlReader.GetAttribute("name"); 91 | string str = xmlReader.ReadInnerXml(); 92 | if (!string.IsNullOrWhiteSpace(attribute) && !documentationComment.typeParameterTexts.ContainsKey(attribute)) 93 | { 94 | list2.Add(attribute); 95 | documentationComment.typeParameterTexts.Add(attribute, str.Trim()); 96 | } 97 | } 98 | else if (DocumentationCommentXmlNames.ElementEquals(localName, "exception", false)) 99 | { 100 | string attribute = xmlReader.GetAttribute("cref"); 101 | string str = xmlReader.ReadInnerXml(); 102 | if (!string.IsNullOrWhiteSpace(attribute)) 103 | { 104 | if (!dictionary.ContainsKey(attribute)) 105 | { 106 | list3.Add(attribute); 107 | dictionary.Add(attribute, new List()); 108 | } 109 | dictionary[attribute].Add(str); 110 | } 111 | } 112 | else 113 | xmlReader.Read(); 114 | } 115 | else 116 | xmlReader.Read(); 117 | } 118 | foreach (KeyValuePair> keyValuePair in dictionary) 119 | documentationComment.exceptionTexts.Add(keyValuePair.Key, ImmutableArrayExtensions.AsImmutable((IEnumerable)keyValuePair.Value)); 120 | } 121 | finally 122 | { 123 | documentationComment.ParameterNames = ImmutableArrayExtensions.AsImmutable((IEnumerable)list1); 124 | documentationComment.TypeParameterNames = ImmutableArrayExtensions.AsImmutable((IEnumerable)list2); 125 | documentationComment.ExceptionTypes = ImmutableArrayExtensions.AsImmutable((IEnumerable)list3); 126 | } 127 | return documentationComment; 128 | } 129 | catch //(Exception ex) 130 | { 131 | DocumentationComment documentationComment = new DocumentationComment(); 132 | string str = xml; 133 | documentationComment.FullXmlFragment = str; 134 | int num = 1; 135 | documentationComment.HadXmlParseError = num != 0; 136 | return documentationComment; 137 | } 138 | } 139 | 140 | public string GetParameterText(string parameterName) 141 | { 142 | string str; 143 | this.parameterTexts.TryGetValue(parameterName, out str); 144 | return str; 145 | } 146 | 147 | public string GetTypeParameterText(string typeParameterName) 148 | { 149 | string str; 150 | this.typeParameterTexts.TryGetValue(typeParameterName, out str); 151 | return str; 152 | } 153 | 154 | public ImmutableArray GetExceptionTexts(string exceptionName) 155 | { 156 | ImmutableArray immutableArray; 157 | this.exceptionTexts.TryGetValue(exceptionName, out immutableArray); 158 | if (immutableArray.IsDefault) 159 | immutableArray = ImmutableArray.Create(); 160 | return immutableArray; 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/CSharpTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | using NUnit.Framework; 4 | 5 | namespace DotNetFiddle.IntelligentCompletion.Tests 6 | { 7 | [TestFixture] 8 | public class CSharpTests 9 | { 10 | [Test] 11 | public void WhenStringBuilder_Works() 12 | { 13 | string codeInFile = @"using System; using System.Text; 14 | class Program 15 | { 16 | public void Main() 17 | { 18 | var sb= new StringBuilder(); 19 | sb."; 20 | 21 | var service = new CSharpLanguageService(); 22 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 23 | Assert.IsNotEmpty(autoCompleteItems); 24 | } 25 | 26 | [Test] 27 | public void WhenStringBuilder_FullCode_Works() 28 | { 29 | string codeInFile = @"using System; using System.Text; 30 | 31 | class Program 32 | { 33 | public void Main() 34 | { 35 | var sb= new StringBuilder(); 36 | sb. 37 | } 38 | 39 | }"; 40 | 41 | var service = new CSharpLanguageService(); 42 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile, codeInFile.Length - 3); 43 | Assert.IsNotEmpty(autoCompleteItems); 44 | Assert.IsNotEmpty(autoCompleteItems.Where(a => a.Name == "Append")); 45 | Assert.IsNotEmpty(autoCompleteItems.Where(a => a.Name == "AppendLine")); 46 | Assert.IsNotEmpty(autoCompleteItems.Where(a => a.Name == "Clear")); 47 | } 48 | 49 | [Test] 50 | public void WhenExtensionStringBuilder_Works() 51 | { 52 | string codeInFile = @"using System; using System.Text; 53 | 54 | static class Extension 55 | { 56 | public static void Test(this StringBuilder builder) 57 | { 58 | } 59 | } 60 | 61 | class Program 62 | { 63 | public void Main() 64 | { 65 | var sb= new StringBuilder(); 66 | sb. 67 | } 68 | 69 | }"; 70 | 71 | var service = new CSharpLanguageService(); 72 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile, codeInFile.Length - 3); 73 | Assert.IsNotEmpty(autoCompleteItems); 74 | Assert.IsNotNull(autoCompleteItems.FirstOrDefault(a => a.Name == "Test" && a.Type == "void")); 75 | } 76 | 77 | 78 | [Test] 79 | public void Constructor_Works() 80 | { 81 | string codeInFile = @"using System; 82 | class Program 83 | { 84 | 85 | /// 86 | /// Program constructor 87 | /// 88 | /// Test parameter 89 | public Program(string test) 90 | { 91 | } 92 | 93 | public void Main() 94 | { 95 | var sb= new Program("; 96 | 97 | var service = new CSharpLanguageService(); 98 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 99 | Assert.IsNotEmpty(autoCompleteItems); 100 | 101 | var programConstructor = autoCompleteItems.FirstOrDefault(a => a.Name == "Program"); 102 | Assert.IsNotNull(programConstructor); 103 | Assert.AreEqual(programConstructor.Description, "Program constructor"); 104 | } 105 | 106 | [Test] 107 | public void DisabledDocumentationDoesntDisplay() 108 | { 109 | string codeInFile = @"using System; using System.Text; 110 | 111 | class Program 112 | { 113 | public void Main() 114 | { 115 | var sb= new StringBuilder(); 116 | sb."; 117 | 118 | 119 | var service = new CSharpLanguageService(null, new LanguageServiceOptions() 120 | { 121 | ParseDocumenation = false 122 | }); 123 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 124 | Assert.IsNotEmpty(autoCompleteItems); 125 | 126 | var appendMethod = autoCompleteItems.FirstOrDefault(a => a.Name == "Append"); 127 | Assert.IsNotNull(appendMethod); 128 | Assert.IsNullOrEmpty(appendMethod.Description); 129 | } 130 | 131 | [Test] 132 | public void Namespace_Works() 133 | { 134 | string codeInFile = @"using System; 135 | using S"; 136 | 137 | var service = new CSharpLanguageService(); 138 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 139 | Assert.IsNotEmpty(autoCompleteItems); 140 | } 141 | 142 | [Test] 143 | public void NamespaceSystem_Works() 144 | { 145 | string codeInFile = @"using System; 146 | using System."; 147 | 148 | var service = new CSharpLanguageService(); 149 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 150 | Assert.IsNotEmpty(autoCompleteItems); 151 | } 152 | 153 | 154 | [Test] 155 | public void NewMethod_Works() 156 | { 157 | string codeInFile = @"using System; using System.Text; 158 | class Program 159 | { 160 | public void TestMethod(){} 161 | 162 | public void Main() 163 | { 164 | this."; 165 | 166 | var service = new CSharpLanguageService(); 167 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 168 | Assert.IsNotEmpty(autoCompleteItems); 169 | 170 | var method = 171 | autoCompleteItems.FirstOrDefault( 172 | p => p.ItemType == AutoCompleteItemType.Method && p.Name == "TestMethod"); 173 | 174 | Assert.IsNotNull(method, "Method TestMethod should be present"); 175 | Assert.AreEqual("void", method.Type); 176 | } 177 | 178 | [Test] 179 | public void NewStaticMethod_Works() 180 | { 181 | string codeInFile = @"using System; using System.Text; 182 | class Program 183 | { 184 | public static void TestMethod(){} 185 | 186 | public void Main() 187 | { 188 | Program."; 189 | 190 | var service = new CSharpLanguageService(); 191 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 192 | Assert.IsNotEmpty(autoCompleteItems); 193 | 194 | var method = 195 | autoCompleteItems.FirstOrDefault( 196 | p => p.ItemType == AutoCompleteItemType.Method && p.Name == "TestMethod"); 197 | 198 | Assert.IsNotNull(method, "Method TestMethod should be present"); 199 | Assert.AreEqual("void", method.Type); 200 | } 201 | 202 | [Test] 203 | public void NewStaticMethod_InNonStatic_Ignored() 204 | { 205 | string codeInFile = @"using System; using System.Text; 206 | class Program 207 | { 208 | public void TestMethod(){} 209 | 210 | public static void Main() 211 | { 212 | Program."; 213 | 214 | var service = new CSharpLanguageService(); 215 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 216 | Assert.IsNotEmpty(autoCompleteItems); 217 | 218 | var method = 219 | autoCompleteItems.FirstOrDefault( 220 | p => p.ItemType == AutoCompleteItemType.Method && p.Name == "TestMethod"); 221 | 222 | Assert.IsNull(method, "Method TestMethod shouldn't be present"); 223 | } 224 | 225 | } 226 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion.Tests/VBNetTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | using NUnit.Framework; 4 | 5 | namespace DotNetFiddle.IntelligentCompletion.Tests 6 | { 7 | [TestFixture] 8 | public class VBNetTests 9 | { 10 | // TODO: remove copypaste and make common inheritor for the same tests 11 | 12 | [Test] 13 | public void WhenStringBuilder_Works() 14 | { 15 | string codeInFile = @"Imports System 16 | Imports System.Text 17 | 18 | Public Module Module1 19 | Public Sub Main() 20 | Dim sb As StringBuilder = New StringBuilder 21 | sb."; 22 | 23 | var service = new VBNetLanguageService(); 24 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 25 | Assert.IsNotEmpty(autoCompleteItems); 26 | } 27 | 28 | [Test] 29 | public void WhenStringBuilder_FullCode_Works() 30 | { 31 | string codeInFile = @"Imports System 32 | Imports System.Text 33 | 34 | Public Module Module1 35 | Public Sub Main() 36 | Dim sb As StringBuilder = New StringBuilder 37 | sb. 38 | End Sub 39 | End Module"; 40 | 41 | var service = new VBNetLanguageService(); 42 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile, codeInFile.LastIndexOf('.') + 1); 43 | Assert.IsNotEmpty(autoCompleteItems); 44 | Assert.IsNotEmpty(autoCompleteItems.Where(a => a.Name == "Append")); 45 | Assert.IsNotEmpty(autoCompleteItems.Where(a => a.Name == "AppendLine")); 46 | Assert.IsNotEmpty(autoCompleteItems.Where(a => a.Name == "Clear")); 47 | } 48 | 49 | [Test] 50 | public void WhenExtensionStringBuilder_Works() 51 | { 52 | string codeInFile = @"Imports System 53 | Imports System.Text 54 | Imports System.Runtime.CompilerServices 55 | 56 | Module StringBuilderExtensions 57 | 58 | 59 | Public Sub Test(ByVal builder as StringBuilder) 60 | End Sub 61 | End Module 62 | 63 | Public Module Module1 64 | Public Sub Main() 65 | Dim sb As StringBuilder = New StringBuilder 66 | sb. 67 | End Sub 68 | End Module"; 69 | 70 | var service = new VBNetLanguageService(); 71 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile, codeInFile.LastIndexOf('.') + 1); 72 | Assert.IsNotEmpty(autoCompleteItems); 73 | Assert.IsNotNull(autoCompleteItems.FirstOrDefault(a => a.Name == "Test" && a.Type == "void")); 74 | } 75 | 76 | 77 | [Test] 78 | public void Constructor_Works() 79 | { 80 | string codeInFile = @"Imports System 81 | 82 | Public Class Program 83 | 84 | ''' 85 | ''' Program constructor 86 | ''' 87 | ''' Test parameter 88 | Public Sub New(ByVal test as String) 89 | 90 | End Sub 91 | 92 | Public Sub Main() 93 | Dim sb as Program = New Program("; 94 | 95 | var service = new VBNetLanguageService(); 96 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 97 | Assert.IsNotEmpty(autoCompleteItems); 98 | 99 | var programConstructor = autoCompleteItems.FirstOrDefault(a => a.Name == "Program"); 100 | Assert.IsNotNull(programConstructor); 101 | Assert.AreEqual(programConstructor.Description, "Program constructor"); 102 | } 103 | 104 | [Test] 105 | public void DisabledDocumentationDoesntDisplay() 106 | { 107 | string codeInFile = @"Imports System 108 | Imports System.Text 109 | 110 | Public Module Module1 111 | Public Sub Main() 112 | Dim sb As StringBuilder = New StringBuilder 113 | sb."; 114 | 115 | var service = new VBNetLanguageService(null, new LanguageServiceOptions() 116 | { 117 | ParseDocumenation = false 118 | }); 119 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 120 | Assert.IsNotEmpty(autoCompleteItems); 121 | 122 | var appendMethod = autoCompleteItems.FirstOrDefault(a => a.Name == "Append"); 123 | Assert.IsNotNull(appendMethod); 124 | Assert.IsNullOrEmpty(appendMethod.Description); 125 | } 126 | 127 | [Test] 128 | public void Namespace_Works() 129 | { 130 | string codeInFile = @"Imports System 131 | Imports S"; 132 | 133 | var service = new VBNetLanguageService(); 134 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 135 | Assert.IsNotEmpty(autoCompleteItems); 136 | } 137 | 138 | [Test] 139 | public void NamespaceSystem_Works() 140 | { 141 | string codeInFile = @"Imports System 142 | Imports System."; 143 | 144 | var service = new VBNetLanguageService(); 145 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 146 | Assert.IsNotEmpty(autoCompleteItems); 147 | } 148 | 149 | 150 | [Test] 151 | public void NewMethod_Works() 152 | { 153 | string codeInFile = @"Imports System 154 | 155 | Public Class Program 156 | 157 | Public Sub TestMethod() 158 | End Sub 159 | 160 | Public Sub Main() 161 | Me."; 162 | 163 | var service = new VBNetLanguageService(); 164 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 165 | Assert.IsNotEmpty(autoCompleteItems); 166 | 167 | var method = 168 | autoCompleteItems.FirstOrDefault( 169 | p => p.ItemType == AutoCompleteItemType.Method && p.Name == "TestMethod"); 170 | 171 | Assert.IsNotNull(method, "Method TestMethod should be present"); 172 | Assert.AreEqual("void", method.Type); 173 | } 174 | 175 | [Test] 176 | public void NewStaticMethod_Works() 177 | { 178 | string codeInFile = @"Imports System 179 | 180 | Public Class Program 181 | 182 | Public Shared Sub TestMethod() 183 | End Sub 184 | 185 | Public Sub Main() 186 | Program."; 187 | 188 | var service = new VBNetLanguageService(); 189 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 190 | Assert.IsNotEmpty(autoCompleteItems); 191 | 192 | var method = 193 | autoCompleteItems.FirstOrDefault( 194 | p => p.ItemType == AutoCompleteItemType.Method && p.Name == "TestMethod"); 195 | 196 | Assert.IsNotNull(method, "Method TestMethod should be present"); 197 | Assert.AreEqual("void", method.Type); 198 | } 199 | 200 | [Test] 201 | public void NewStaticMethod_InNonStatic_Ignored() 202 | { 203 | string codeInFile = @"Imports System 204 | 205 | Public Class Program 206 | 207 | Public Sub TestMethod() 208 | End Sub 209 | 210 | Public Shared Sub Main() 211 | Program."; 212 | 213 | var service = new VBNetLanguageService(); 214 | var autoCompleteItems = service.GetAutoCompleteItems(codeInFile); 215 | Assert.IsNotEmpty(autoCompleteItems); 216 | 217 | var method = 218 | autoCompleteItems.FirstOrDefault( 219 | p => p.ItemType == AutoCompleteItemType.Method && p.Name == "TestMethod"); 220 | 221 | Assert.IsNull(method, "Method TestMethod shouldn't be present"); 222 | } 223 | 224 | } 225 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/RoslynSource/GlobalAssemblyCache.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Collections.Immutable; 6 | using System.Diagnostics; 7 | using System.Globalization; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Reflection; 11 | using System.Runtime.InteropServices; 12 | using Roslyn.Utilities; 13 | using Debug = System.Diagnostics.Debug; 14 | 15 | namespace Microsoft.CodeAnalysis 16 | { 17 | /// 18 | /// Provides APIs to enumerate and look up assemblies stored in the Global Assembly Cache. 19 | /// 20 | [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter")] 21 | [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation")] 22 | [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore")] 23 | internal static class GlobalAssemblyCache 24 | { 25 | /// 26 | /// Represents the current Processor architecture 27 | /// 28 | public static readonly Func CurrentArchitectureFilter = (IntPtr.Size == 4) ? 29 | new Func(a => a == ProcessorArchitecture.None || a == ProcessorArchitecture.MSIL || a == ProcessorArchitecture.X86) : 30 | new Func(a => a == ProcessorArchitecture.None || a == ProcessorArchitecture.MSIL || a == ProcessorArchitecture.Amd64); 31 | 32 | #region Interop 33 | 34 | private const int MAX_PATH = 260; 35 | 36 | [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("21b8916c-f28e-11d2-a473-00c04f8ef448")] 37 | private interface IAssemblyEnum 38 | { 39 | [PreserveSig] 40 | int GetNextAssembly(out FusionAssemblyIdentity.IApplicationContext ppAppCtx, out FusionAssemblyIdentity.IAssemblyName ppName, uint dwFlags); 41 | 42 | [PreserveSig] 43 | int Reset(); 44 | 45 | [PreserveSig] 46 | int Clone(out IAssemblyEnum ppEnum); 47 | } 48 | 49 | [ComImport, Guid("e707dcde-d1cd-11d2-bab9-00c04f8eceae"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 50 | private interface IAssemblyCache 51 | { 52 | void UninstallAssembly(); 53 | 54 | void QueryAssemblyInfo(uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName, ref ASSEMBLY_INFO pAsmInfo); 55 | 56 | void CreateAssemblyCacheItem(); 57 | void CreateAssemblyScavenger(); 58 | void InstallAssembly(); 59 | } 60 | 61 | [StructLayout(LayoutKind.Sequential)] 62 | private unsafe struct ASSEMBLY_INFO 63 | { 64 | public uint cbAssemblyInfo; 65 | public uint dwAssemblyFlags; 66 | public ulong uliAssemblySizeInKB; 67 | public char* pszCurrentAssemblyPathBuf; 68 | public uint cchBuf; 69 | } 70 | 71 | private enum ASM_CACHE 72 | { 73 | ZAP = 0x1, 74 | GAC = 0x2, // C:\Windows\Assembly\GAC 75 | DOWNLOAD = 0x4, 76 | ROOT = 0x8, // C:\Windows\Assembly 77 | GAC_MSIL = 0x10, 78 | GAC_32 = 0x20, // C:\Windows\Assembly\GAC_32 79 | GAC_64 = 0x40, // C:\Windows\Assembly\GAC_64 80 | ROOT_EX = 0x80, // C:\Windows\Microsoft.NET\assembly 81 | } 82 | 83 | [DllImport("clr", CharSet = CharSet.Auto, PreserveSig = true)] 84 | private static extern int CreateAssemblyEnum(out IAssemblyEnum ppEnum, FusionAssemblyIdentity.IApplicationContext pAppCtx, FusionAssemblyIdentity.IAssemblyName pName, ASM_CACHE dwFlags, IntPtr pvReserved); 85 | 86 | [DllImport("clr", CharSet = CharSet.Auto, PreserveSig = true)] 87 | private static unsafe extern int GetCachePath(ASM_CACHE id, byte* path, ref int length); 88 | 89 | [DllImport("clr", CharSet = CharSet.Auto, PreserveSig = false)] 90 | private static extern void CreateAssemblyCache(out IAssemblyCache ppAsmCache, uint dwReserved); 91 | 92 | private const int ERROR_INSUFFICIENT_BUFFER = unchecked((int)0x8007007A); 93 | 94 | public static readonly ImmutableArray RootLocations; 95 | 96 | static GlobalAssemblyCache() 97 | { 98 | RootLocations = ImmutableArray.Create( 99 | GetLocation(ASM_CACHE.ROOT), 100 | GetLocation(ASM_CACHE.ROOT_EX)); 101 | } 102 | 103 | private static unsafe string GetLocation(ASM_CACHE gacId) 104 | { 105 | int characterCount = 0; 106 | int hr = GetCachePath(gacId, null, ref characterCount); 107 | if (hr != ERROR_INSUFFICIENT_BUFFER) 108 | { 109 | throw Marshal.GetExceptionForHR(hr); 110 | } 111 | 112 | byte[] data = new byte[((int)characterCount + 1) * 2]; 113 | fixed (byte* p = data) 114 | { 115 | hr = GetCachePath(gacId, p, ref characterCount); 116 | if (hr != 0) 117 | { 118 | throw Marshal.GetExceptionForHR(hr); 119 | } 120 | 121 | return Marshal.PtrToStringUni((IntPtr)p); 122 | } 123 | } 124 | 125 | #endregion 126 | 127 | /// 128 | /// Enumerates assemblies in the GAC returning those that match given partial name and 129 | /// architecture. 130 | /// 131 | /// Optional partial name. 132 | /// Optional architecture filter. 133 | public static IEnumerable GetAssemblyIdentities(AssemblyName partialName, Func architectureFilter = null) 134 | { 135 | return GetAssemblyIdentities(FusionAssemblyIdentity.ToAssemblyNameObject(partialName), architectureFilter); 136 | } 137 | 138 | /// 139 | /// Enumerates assemblies in the GAC returning those that match given partial name and 140 | /// architecture. 141 | /// 142 | /// The optional partial name. 143 | /// The optional architecture filter. 144 | public static IEnumerable GetAssemblyIdentities(string partialName = null, Func architectureFilter = null) 145 | { 146 | FusionAssemblyIdentity.IAssemblyName nameObj; 147 | if (partialName != null) 148 | { 149 | nameObj = FusionAssemblyIdentity.ToAssemblyNameObject(partialName); 150 | if (nameObj == null) 151 | { 152 | return Enumerable.Empty(); 153 | } 154 | } 155 | else 156 | { 157 | nameObj = null; 158 | } 159 | 160 | return GetAssemblyIdentities(nameObj, architectureFilter); 161 | } 162 | 163 | /// 164 | /// Enumerates assemblies in the GAC returning their simple names. 165 | /// 166 | /// Optional architecture filter. 167 | /// Unique simple names of GAC assemblies. 168 | public static IEnumerable GetAssemblySimpleNames(Func architectureFilter = null) 169 | { 170 | var q = from nameObject in GetAssemblyObjects(partialNameFilter: null, architectureFilter: architectureFilter) 171 | select FusionAssemblyIdentity.GetName(nameObject); 172 | return q.Distinct(); 173 | } 174 | 175 | private static IEnumerable GetAssemblyIdentities( 176 | FusionAssemblyIdentity.IAssemblyName partialName, 177 | Func architectureFilter) 178 | { 179 | return from nameObject in GetAssemblyObjects(partialName, architectureFilter) 180 | select FusionAssemblyIdentity.ToAssemblyIdentity(nameObject); 181 | } 182 | 183 | private const int S_OK = 0; 184 | private const int S_FALSE = 1; 185 | 186 | // Internal for testing. 187 | internal static IEnumerable GetAssemblyObjects( 188 | FusionAssemblyIdentity.IAssemblyName partialNameFilter, 189 | Func architectureFilter) 190 | { 191 | IAssemblyEnum enumerator; 192 | FusionAssemblyIdentity.IApplicationContext applicationContext = null; 193 | 194 | int hr = CreateAssemblyEnum(out enumerator, applicationContext, partialNameFilter, ASM_CACHE.GAC, IntPtr.Zero); 195 | if (hr == S_FALSE) 196 | { 197 | // no assembly found 198 | yield break; 199 | } 200 | else if (hr != S_OK) 201 | { 202 | Exception e = Marshal.GetExceptionForHR(hr); 203 | if (e is FileNotFoundException) 204 | { 205 | // invalid assembly name: 206 | yield break; 207 | } 208 | else if (e != null) 209 | { 210 | throw e; 211 | } 212 | else 213 | { 214 | // for some reason it might happen that CreateAssemblyEnum returns non-zero HR that doesn't correspond to any exception: 215 | throw new ArgumentException("Invalid assembly name"); 216 | } 217 | } 218 | 219 | while (true) 220 | { 221 | FusionAssemblyIdentity.IAssemblyName nameObject; 222 | 223 | hr = enumerator.GetNextAssembly(out applicationContext, out nameObject, 0); 224 | if (hr != 0) 225 | { 226 | if (hr < 0) 227 | { 228 | Marshal.ThrowExceptionForHR(hr); 229 | } 230 | 231 | break; 232 | } 233 | 234 | if (architectureFilter != null) 235 | { 236 | var assemblyArchitecture = FusionAssemblyIdentity.GetProcessorArchitecture(nameObject); 237 | if (!architectureFilter(assemblyArchitecture)) 238 | { 239 | continue; 240 | } 241 | } 242 | 243 | yield return nameObject; 244 | } 245 | } 246 | 247 | /// 248 | /// Looks up specified partial assembly name in the GAC and returns the best matching . 249 | /// 250 | /// The display name of an assembly 251 | /// The optional processor architecture 252 | /// The optional preferred culture information 253 | /// An assembly identity or null, if can't be resolved. 254 | /// is null. 255 | public static AssemblyIdentity ResolvePartialName( 256 | string displayName, 257 | Func architectureFilter = null, 258 | CultureInfo preferredCulture = null) 259 | { 260 | string location; 261 | return ResolvePartialName(displayName, architectureFilter, preferredCulture, out location, resolveLocation: false); 262 | } 263 | 264 | /// 265 | /// Looks up specified partial assembly name in the GAC and returns the best matching . 266 | /// 267 | /// The display name of an assembly 268 | /// Full path name of the resolved assembly 269 | /// The optional processor architecture 270 | /// The optional preferred culture information 271 | /// An assembly identity or null, if can't be resolved. 272 | /// is null. 273 | public static AssemblyIdentity ResolvePartialName( 274 | string displayName, 275 | out string location, 276 | Func architectureFilter = null, 277 | CultureInfo preferredCulture = null) 278 | { 279 | return ResolvePartialName(displayName, architectureFilter, preferredCulture, out location, resolveLocation: true); 280 | } 281 | 282 | private static AssemblyIdentity ResolvePartialName( 283 | string displayName, 284 | Func architectureFilter, 285 | CultureInfo preferredCulture, 286 | out string location, 287 | bool resolveLocation) 288 | { 289 | if (displayName == null) 290 | { 291 | throw new ArgumentNullException("displayName"); 292 | } 293 | 294 | location = null; 295 | FusionAssemblyIdentity.IAssemblyName nameObject = FusionAssemblyIdentity.ToAssemblyNameObject(displayName); 296 | if (nameObject == null) 297 | { 298 | return null; 299 | } 300 | 301 | var candidates = GetAssemblyObjects(nameObject, architectureFilter); 302 | string cultureName = (preferredCulture != null && !preferredCulture.IsNeutralCulture) ? preferredCulture.Name : null; 303 | 304 | var bestMatch = FusionAssemblyIdentity.GetBestMatch(candidates, cultureName); 305 | if (bestMatch == null) 306 | { 307 | return null; 308 | } 309 | 310 | if (resolveLocation) 311 | { 312 | location = GetAssemblyLocation(bestMatch); 313 | } 314 | 315 | return null; 316 | //return FusionAssemblyIdentity.ToAssemblyIdentity(bestMatch); 317 | } 318 | 319 | internal static unsafe string GetAssemblyLocation(FusionAssemblyIdentity.IAssemblyName nameObject) 320 | { 321 | // NAME | VERSION | CULTURE | PUBLIC_KEY_TOKEN | RETARGET | PROCESSORARCHITECTURE 322 | string fullName = FusionAssemblyIdentity.GetDisplayName(nameObject, FusionAssemblyIdentity.ASM_DISPLAYF.FULL); 323 | 324 | fixed (char* p = new char[MAX_PATH]) 325 | { 326 | ASSEMBLY_INFO info = new ASSEMBLY_INFO 327 | { 328 | cbAssemblyInfo = (uint)Marshal.SizeOf(typeof(ASSEMBLY_INFO)), 329 | pszCurrentAssemblyPathBuf = p, 330 | cchBuf = (uint)MAX_PATH 331 | }; 332 | 333 | IAssemblyCache assemblyCacheObject; 334 | CreateAssemblyCache(out assemblyCacheObject, 0); 335 | assemblyCacheObject.QueryAssemblyInfo(0, fullName, ref info); 336 | System.Diagnostics.Debug.Assert(info.pszCurrentAssemblyPathBuf != null); 337 | System.Diagnostics.Debug.Assert(info.pszCurrentAssemblyPathBuf[info.cchBuf - 1] == '\0'); 338 | 339 | var result = Marshal.PtrToStringUni((IntPtr)info.pszCurrentAssemblyPathBuf, (int)info.cchBuf - 1); 340 | System.Diagnostics.Debug.Assert(result.IndexOf('\0') == -1); 341 | return result; 342 | } 343 | } 344 | } 345 | } -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/RoslynSource/FusionAssemblyIdentity.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Globalization; 7 | using System.Reflection; 8 | using System.Runtime.InteropServices; 9 | using Microsoft.CodeAnalysis; 10 | using Roslyn.Utilities; 11 | 12 | namespace Microsoft.CodeAnalysis 13 | { 14 | internal sealed class FusionAssemblyIdentity 15 | { 16 | [Flags] 17 | internal enum ASM_DISPLAYF 18 | { 19 | VERSION = 0x01, 20 | CULTURE = 0x02, 21 | PUBLIC_KEY_TOKEN = 0x04, 22 | PUBLIC_KEY = 0x08, 23 | CUSTOM = 0x10, 24 | PROCESSORARCHITECTURE = 0x20, 25 | LANGUAGEID = 0x40, 26 | RETARGET = 0x80, 27 | CONFIG_MASK = 0x100, 28 | MVID = 0x200, 29 | CONTENT_TYPE = 0x400, 30 | FULL = VERSION | CULTURE | PUBLIC_KEY_TOKEN | RETARGET | PROCESSORARCHITECTURE | CONTENT_TYPE 31 | } 32 | 33 | internal enum PropertyId 34 | { 35 | PUBLIC_KEY = 0, // 0 36 | PUBLIC_KEY_TOKEN, // 1 37 | HASH_VALUE, // 2 38 | NAME, // 3 39 | MAJOR_VERSION, // 4 40 | MINOR_VERSION, // 5 41 | BUILD_NUMBER, // 6 42 | REVISION_NUMBER, // 7 43 | CULTURE, // 8 44 | PROCESSOR_ID_ARRAY, // 9 45 | OSINFO_ARRAY, // 10 46 | HASH_ALGID, // 11 47 | ALIAS, // 12 48 | CODEBASE_URL, // 13 49 | CODEBASE_LASTMOD, // 14 50 | NULL_PUBLIC_KEY, // 15 51 | NULL_PUBLIC_KEY_TOKEN, // 16 52 | CUSTOM, // 17 53 | NULL_CUSTOM, // 18 54 | MVID, // 19 55 | FILE_MAJOR_VERSION, // 20 56 | FILE_MINOR_VERSION, // 21 57 | FILE_BUILD_NUMBER, // 22 58 | FILE_REVISION_NUMBER, // 23 59 | RETARGET, // 24 60 | SIGNATURE_BLOB, // 25 61 | CONFIG_MASK, // 26 62 | ARCHITECTURE, // 27 63 | CONTENT_TYPE, // 28 64 | MAX_PARAMS // 29 65 | } 66 | 67 | private static class CANOF 68 | { 69 | public const uint PARSE_DISPLAY_NAME = 0x1; 70 | public const uint SET_DEFAULT_VALUES = 0x2; 71 | } 72 | 73 | [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("CD193BC0-B4BC-11d2-9833-00C04FC31D2E")] 74 | internal unsafe interface IAssemblyName 75 | { 76 | void SetProperty(PropertyId id, void* data, uint size); 77 | 78 | [PreserveSig] 79 | int GetProperty(PropertyId id, void* data, ref uint size); 80 | 81 | [PreserveSig] 82 | int Finalize(); 83 | 84 | [PreserveSig] 85 | int GetDisplayName(byte* buffer, ref uint characterCount, ASM_DISPLAYF dwDisplayFlags); 86 | 87 | [PreserveSig] 88 | int __BindToObject(/*...*/); 89 | 90 | [PreserveSig] 91 | int __GetName(/*...*/); 92 | 93 | [PreserveSig] 94 | int GetVersion(out uint versionHi, out uint versionLow); 95 | 96 | [PreserveSig] 97 | int IsEqual(IAssemblyName pName, uint dwCmpFlags); 98 | 99 | [PreserveSig] 100 | int Clone(out IAssemblyName pName); 101 | } 102 | 103 | [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("7c23ff90-33af-11d3-95da-00a024a85b51")] 104 | internal interface IApplicationContext 105 | { 106 | } 107 | 108 | // NOTE: The CLR caches assembly identities, but doesn't do so in a threadsafe manner. 109 | // Wrap all calls to this with a lock. 110 | private static object assemblyIdentityGate = new object(); 111 | private static int CreateAssemblyNameObject(out IAssemblyName ppEnum, string szAssemblyName, uint dwFlags, IntPtr pvReserved) 112 | { 113 | lock (assemblyIdentityGate) 114 | { 115 | return RealCreateAssemblyNameObject(out ppEnum, szAssemblyName, dwFlags, pvReserved); 116 | } 117 | } 118 | 119 | [DllImport("clr", EntryPoint = "CreateAssemblyNameObject", CharSet = CharSet.Unicode, PreserveSig = true)] 120 | private static extern int RealCreateAssemblyNameObject(out IAssemblyName ppEnum, [MarshalAs(UnmanagedType.LPWStr)]string szAssemblyName, uint dwFlags, IntPtr pvReserved); 121 | 122 | private const int ERROR_INSUFFICIENT_BUFFER = unchecked((int)0x8007007A); 123 | private const int FUSION_E_INVALID_NAME = unchecked((int)0x80131047); 124 | 125 | internal static unsafe string GetDisplayName(IAssemblyName nameObject, ASM_DISPLAYF displayFlags) 126 | { 127 | int hr; 128 | uint characterCountIncludingTerminator = 0; 129 | 130 | hr = nameObject.GetDisplayName(null, ref characterCountIncludingTerminator, displayFlags); 131 | if (hr == 0) 132 | { 133 | return String.Empty; 134 | } 135 | 136 | if (hr != ERROR_INSUFFICIENT_BUFFER) 137 | { 138 | throw Marshal.GetExceptionForHR(hr); 139 | } 140 | 141 | byte[] data = new byte[(int)characterCountIncludingTerminator * 2]; 142 | fixed (byte* p = data) 143 | { 144 | hr = nameObject.GetDisplayName(p, ref characterCountIncludingTerminator, displayFlags); 145 | if (hr != 0) 146 | { 147 | throw Marshal.GetExceptionForHR(hr); 148 | } 149 | 150 | return Marshal.PtrToStringUni((IntPtr)p, (int)characterCountIncludingTerminator - 1); 151 | } 152 | } 153 | 154 | internal static unsafe byte[] GetPropertyBytes(IAssemblyName nameObject, PropertyId propertyId) 155 | { 156 | int hr; 157 | uint size = 0; 158 | 159 | hr = nameObject.GetProperty(propertyId, null, ref size); 160 | if (hr == 0) 161 | { 162 | return null; 163 | } 164 | 165 | if (hr != ERROR_INSUFFICIENT_BUFFER) 166 | { 167 | throw Marshal.GetExceptionForHR(hr); 168 | } 169 | 170 | byte[] data = new byte[(int)size]; 171 | fixed (byte* p = data) 172 | { 173 | hr = nameObject.GetProperty(propertyId, p, ref size); 174 | if (hr != 0) 175 | { 176 | throw Marshal.GetExceptionForHR(hr); 177 | } 178 | } 179 | 180 | return data; 181 | } 182 | 183 | internal static unsafe string GetPropertyString(IAssemblyName nameObject, PropertyId propertyId) 184 | { 185 | byte[] data = GetPropertyBytes(nameObject, propertyId); 186 | if (data == null) 187 | { 188 | return null; 189 | } 190 | 191 | fixed (byte* p = data) 192 | { 193 | return Marshal.PtrToStringUni((IntPtr)p, (data.Length / 2) - 1); 194 | } 195 | } 196 | 197 | internal static unsafe bool IsKeyOrTokenEmpty(IAssemblyName nameObject, PropertyId propertyId) 198 | { 199 | System.Diagnostics.Debug.Assert(propertyId == PropertyId.NULL_PUBLIC_KEY_TOKEN || propertyId == PropertyId.NULL_PUBLIC_KEY); 200 | uint size = 0; 201 | int hr = nameObject.GetProperty(propertyId, null, ref size); 202 | return hr == 0; 203 | } 204 | 205 | internal static unsafe Version GetVersion(IAssemblyName nameObject) 206 | { 207 | uint hi, lo; 208 | int hr = nameObject.GetVersion(out hi, out lo); 209 | if (hr != 0) 210 | { 211 | System.Diagnostics.Debug.Assert(hr == FUSION_E_INVALID_NAME); 212 | return null; 213 | } 214 | 215 | return new Version((int)(hi >> 16), (int)(hi & 0xffff), (int)(lo >> 16), (int)(lo & 0xffff)); 216 | } 217 | 218 | internal static Version GetVersion(IAssemblyName name, out AssemblyIdentityParts parts) 219 | { 220 | uint? major = GetPropertyWord(name, PropertyId.MAJOR_VERSION); 221 | uint? minor = GetPropertyWord(name, PropertyId.MINOR_VERSION); 222 | uint? build = GetPropertyWord(name, PropertyId.BUILD_NUMBER); 223 | uint? revision = GetPropertyWord(name, PropertyId.REVISION_NUMBER); 224 | 225 | parts = 0; 226 | 227 | if (major != null) 228 | { 229 | parts |= AssemblyIdentityParts.VersionMajor; 230 | } 231 | 232 | if (minor != null) 233 | { 234 | parts |= AssemblyIdentityParts.VersionMinor; 235 | } 236 | 237 | if (build != null) 238 | { 239 | parts |= AssemblyIdentityParts.VersionBuild; 240 | } 241 | 242 | if (revision != null) 243 | { 244 | parts |= AssemblyIdentityParts.VersionRevision; 245 | } 246 | 247 | return new Version((int)(major ?? 0), (int)(minor ?? 0), (int)(build ?? 0), (int)(revision ?? 0)); 248 | } 249 | 250 | internal static byte[] GetPublicKeyToken(IAssemblyName nameObject) 251 | { 252 | byte[] result = GetPropertyBytes(nameObject, PropertyId.PUBLIC_KEY_TOKEN); 253 | if (result != null) 254 | { 255 | return result; 256 | } 257 | 258 | if (IsKeyOrTokenEmpty(nameObject, PropertyId.NULL_PUBLIC_KEY_TOKEN)) 259 | { 260 | return SpecializedCollections.EmptyArray(); 261 | } 262 | 263 | return null; 264 | } 265 | 266 | internal static byte[] GetPublicKey(IAssemblyName nameObject) 267 | { 268 | byte[] result = GetPropertyBytes(nameObject, PropertyId.PUBLIC_KEY); 269 | if (result != null) 270 | { 271 | return result; 272 | } 273 | 274 | if (IsKeyOrTokenEmpty(nameObject, PropertyId.NULL_PUBLIC_KEY)) 275 | { 276 | return SpecializedCollections.EmptyArray(); 277 | } 278 | 279 | return null; 280 | } 281 | 282 | internal static unsafe uint? GetPropertyWord(IAssemblyName nameObject, PropertyId propertyId) 283 | { 284 | uint result; 285 | uint size = sizeof(uint); 286 | int hr = nameObject.GetProperty(propertyId, &result, ref size); 287 | if (hr != 0) 288 | { 289 | throw Marshal.GetExceptionForHR(hr); 290 | } 291 | 292 | if (size == 0) 293 | { 294 | return null; 295 | } 296 | 297 | return result; 298 | } 299 | 300 | internal static string GetName(IAssemblyName nameObject) 301 | { 302 | return GetPropertyString(nameObject, PropertyId.NAME); 303 | } 304 | 305 | internal static string GetCulture(IAssemblyName nameObject) 306 | { 307 | return GetPropertyString(nameObject, PropertyId.CULTURE); 308 | } 309 | 310 | internal static AssemblyContentType GetContentType(IAssemblyName nameObject) 311 | { 312 | return (AssemblyContentType)(GetPropertyWord(nameObject, PropertyId.CONTENT_TYPE) ?? 0); 313 | } 314 | 315 | internal static CultureInfo GetCultureInfo(IAssemblyName nameObject) 316 | { 317 | var cultureName = GetCulture(nameObject); 318 | return (cultureName != null) ? new CultureInfo(cultureName) : null; 319 | } 320 | 321 | internal static ProcessorArchitecture GetProcessorArchitecture(IAssemblyName nameObject) 322 | { 323 | return (ProcessorArchitecture)(GetPropertyWord(nameObject, PropertyId.ARCHITECTURE) ?? 0); 324 | } 325 | 326 | internal static unsafe AssemblyNameFlags GetFlags(IAssemblyName nameObject) 327 | { 328 | AssemblyNameFlags result = 0; 329 | 330 | uint retarget = GetPropertyWord(nameObject, PropertyId.RETARGET) ?? 0; 331 | if (retarget != 0) 332 | { 333 | result |= AssemblyNameFlags.Retargetable; 334 | } 335 | 336 | return result; 337 | } 338 | 339 | private static unsafe void SetProperty(IAssemblyName nameObject, PropertyId propertyId, string data) 340 | { 341 | if (data == null) 342 | { 343 | nameObject.SetProperty(propertyId, null, 0); 344 | } 345 | else 346 | { 347 | System.Diagnostics.Debug.Assert(data.IndexOf('\0') == -1); 348 | 349 | fixed (char* p = data) 350 | { 351 | System.Diagnostics.Debug.Assert(p[data.Length] == '\0'); 352 | 353 | // size is in bytes, include trailing \0 character: 354 | nameObject.SetProperty(propertyId, p, (uint)(data.Length + 1) * 2); 355 | } 356 | } 357 | } 358 | 359 | private static unsafe void SetProperty(IAssemblyName nameObject, PropertyId propertyId, byte[] data) 360 | { 361 | if (data == null) 362 | { 363 | nameObject.SetProperty(propertyId, null, 0); 364 | } 365 | else 366 | { 367 | fixed (byte* p = data) 368 | { 369 | nameObject.SetProperty(propertyId, p, (uint)data.Length); 370 | } 371 | } 372 | } 373 | 374 | private static unsafe void SetProperty(IAssemblyName nameObject, PropertyId propertyId, ushort data) 375 | { 376 | nameObject.SetProperty(propertyId, &data, sizeof(ushort)); 377 | } 378 | 379 | private static unsafe void SetProperty(IAssemblyName nameObject, PropertyId propertyId, uint data) 380 | { 381 | nameObject.SetProperty(propertyId, &data, sizeof(uint)); 382 | } 383 | 384 | private static unsafe void SetPublicKeyToken(IAssemblyName nameObject, byte[] value) 385 | { 386 | // An empty public key token is set via NULL_PUBLIC_KEY_TOKEN property. 387 | if (value != null && value.Length == 0) 388 | { 389 | nameObject.SetProperty(PropertyId.NULL_PUBLIC_KEY_TOKEN, null, 0); 390 | } 391 | else 392 | { 393 | SetProperty(nameObject, PropertyId.PUBLIC_KEY_TOKEN, value); 394 | } 395 | } 396 | 397 | /// 398 | /// Converts to with possibly missing name components. 399 | /// 400 | /// 401 | /// An whose fields are be null if not present in . 402 | /// 403 | internal static AssemblyName ToAssemblyName(IAssemblyName nameObject) 404 | { 405 | var result = new AssemblyName(); 406 | result.Name = GetName(nameObject); 407 | result.Version = GetVersion(nameObject); 408 | result.CultureInfo = GetCultureInfo(nameObject); 409 | 410 | byte[] publicKey = GetPublicKey(nameObject); 411 | if (publicKey != null && publicKey.Length != 0) 412 | { 413 | result.SetPublicKey(publicKey); 414 | } 415 | else 416 | { 417 | result.SetPublicKeyToken(GetPublicKeyToken(nameObject)); 418 | } 419 | 420 | result.Flags = GetFlags(nameObject); 421 | result.ContentType = GetContentType(nameObject); 422 | return result; 423 | } 424 | 425 | /// 426 | /// Converts to with all metadata fields filled. 427 | /// 428 | /// 429 | /// Assembly name with Version, Culture and PublicKeyToken components filled in: 430 | /// "SimpleName, Version=#.#.#.#, Culture=XXX, PublicKeyToken=XXXXXXXXXXXXXXXX". 431 | /// In addition Retargetable flag and ContentType are set. 432 | /// 433 | internal static AssemblyIdentity ToAssemblyIdentity(IAssemblyName nameObject) 434 | { 435 | if (nameObject == null) 436 | { 437 | return null; 438 | } 439 | 440 | AssemblyNameFlags flags = GetFlags(nameObject); 441 | 442 | byte[] publicKey = GetPublicKey(nameObject); 443 | bool hasPublicKey = publicKey != null && publicKey.Length != 0; 444 | 445 | AssemblyIdentityParts versionParts; 446 | return new AssemblyIdentity( 447 | GetName(nameObject), 448 | GetVersion(nameObject, out versionParts), 449 | GetCulture(nameObject) ?? "", 450 | (hasPublicKey ? publicKey : GetPublicKeyToken(nameObject)).AsImmutableOrNull(), 451 | hasPublicKey: hasPublicKey, 452 | isRetargetable: (flags & AssemblyNameFlags.Retargetable) != 0, 453 | contentType: GetContentType(nameObject)); 454 | } 455 | 456 | /// 457 | /// Converts to an equivalent . 458 | /// 459 | internal static IAssemblyName ToAssemblyNameObject(AssemblyName name) 460 | { 461 | if (name == null) 462 | { 463 | return null; 464 | } 465 | 466 | IAssemblyName result; 467 | Marshal.ThrowExceptionForHR(CreateAssemblyNameObject(out result, null, 0, IntPtr.Zero)); 468 | 469 | string assemblyName = name.Name; 470 | if (assemblyName != null) 471 | { 472 | if (assemblyName.IndexOf('\0') >= 0) 473 | { 474 | throw new ArgumentException("Invalid characters in assemblyName", "name"); 475 | } 476 | 477 | SetProperty(result, PropertyId.NAME, assemblyName); 478 | } 479 | 480 | if (name.Version != null) 481 | { 482 | SetProperty(result, PropertyId.MAJOR_VERSION, unchecked((ushort)name.Version.Major)); 483 | SetProperty(result, PropertyId.MINOR_VERSION, unchecked((ushort)name.Version.Minor)); 484 | SetProperty(result, PropertyId.BUILD_NUMBER, unchecked((ushort)name.Version.Build)); 485 | SetProperty(result, PropertyId.REVISION_NUMBER, unchecked((ushort)name.Version.Revision)); 486 | } 487 | 488 | CultureInfo info = name.CultureInfo; 489 | if (info != null) 490 | { 491 | string cultureName = info.Name; 492 | if (cultureName.IndexOf('\0') >= 0) 493 | { 494 | throw new ArgumentException("Invalid characters in assembly name", "name"); 495 | } 496 | 497 | SetProperty(result, PropertyId.CULTURE, cultureName); 498 | } 499 | 500 | if (name.Flags == AssemblyNameFlags.Retargetable) 501 | { 502 | SetProperty(result, PropertyId.RETARGET, 1U); 503 | } 504 | 505 | if (name.ContentType != AssemblyContentType.Default) 506 | { 507 | SetProperty(result, PropertyId.CONTENT_TYPE, (uint)name.ContentType); 508 | } 509 | 510 | byte[] token = name.GetPublicKeyToken(); 511 | SetPublicKeyToken(result, token); 512 | return result; 513 | } 514 | 515 | /// 516 | /// Creates object by parsing given display name. 517 | /// 518 | internal static IAssemblyName ToAssemblyNameObject(string displayName) 519 | { 520 | // CLR doesn't handle \0 in the display name well: 521 | if (displayName.IndexOf('\0') >= 0) 522 | { 523 | return null; 524 | } 525 | 526 | System.Diagnostics.Debug.Assert(displayName != null); 527 | IAssemblyName result; 528 | int hr = CreateAssemblyNameObject(out result, displayName, CANOF.PARSE_DISPLAY_NAME, IntPtr.Zero); 529 | if (hr != 0) 530 | { 531 | return null; 532 | } 533 | 534 | System.Diagnostics.Debug.Assert(result != null); 535 | return result; 536 | } 537 | 538 | /// 539 | /// Selects the candidate assembly with the largest version number. Uses culture as a tie-breaker if it is provided. 540 | /// All candidates are assumed to have the same name and must include versions and cultures. 541 | /// 542 | internal static IAssemblyName GetBestMatch(IEnumerable candidates, string preferredCultureOpt) 543 | { 544 | IAssemblyName bestCandidate = null; 545 | Version bestVersion = null; 546 | string bestCulture = null; 547 | foreach (var candidate in candidates) 548 | { 549 | if (bestCandidate != null) 550 | { 551 | Version candidateVersion = GetVersion(candidate); 552 | System.Diagnostics.Debug.Assert(candidateVersion != null); 553 | 554 | if (bestVersion == null) 555 | { 556 | bestVersion = GetVersion(bestCandidate); 557 | System.Diagnostics.Debug.Assert(bestVersion != null); 558 | } 559 | 560 | int cmp = bestVersion.CompareTo(candidateVersion); 561 | if (cmp == 0) 562 | { 563 | if (preferredCultureOpt != null) 564 | { 565 | string candidateCulture = GetCulture(candidate); 566 | System.Diagnostics.Debug.Assert(candidateCulture != null); 567 | 568 | if (bestCulture == null) 569 | { 570 | bestCulture = GetCulture(candidate); 571 | System.Diagnostics.Debug.Assert(bestCulture != null); 572 | } 573 | 574 | // we have exactly the preferred culture or 575 | // we have neutral culture and the best candidate's culture isn't the preferred one: 576 | if (StringComparer.OrdinalIgnoreCase.Equals(candidateCulture, preferredCultureOpt) || 577 | candidateCulture.Length == 0 && !StringComparer.OrdinalIgnoreCase.Equals(bestCulture, preferredCultureOpt)) 578 | { 579 | bestCandidate = candidate; 580 | bestVersion = candidateVersion; 581 | bestCulture = candidateCulture; 582 | } 583 | } 584 | } 585 | else if (cmp < 0) 586 | { 587 | bestCandidate = candidate; 588 | bestVersion = candidateVersion; 589 | } 590 | } 591 | else 592 | { 593 | bestCandidate = candidate; 594 | } 595 | } 596 | 597 | return bestCandidate; 598 | } 599 | } 600 | } 601 | -------------------------------------------------------------------------------- /src/DotNetFiddle.IntelligentCompletion/Languages/LanguageService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Immutable; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using System.Text.RegularExpressions; 8 | using Microsoft.CodeAnalysis; 9 | using Microsoft.CodeAnalysis.Shared.Utilities; 10 | 11 | namespace DotNetFiddle.IntelligentCompletion 12 | { 13 | /// 14 | /// Class with common parsing logic 15 | /// 16 | public abstract class LanguageService 17 | { 18 | private readonly LanguageServiceOptions _options; 19 | 20 | protected LanguageService() 21 | : this(new LanguageServiceOptions()) 22 | { 23 | 24 | } 25 | 26 | protected LanguageService(LanguageServiceOptions options) 27 | { 28 | _options = options ?? new LanguageServiceOptions(); 29 | _systemXmlFilesDir = GetSystemXmlFilesDir(); 30 | } 31 | 32 | private string _systemXmlFilesDir; 33 | 34 | public abstract Language Language { get; } 35 | 36 | internal abstract string GetUsingKeyword(); 37 | 38 | public abstract Type GetSyntaxTreeType(); 39 | 40 | public abstract SyntaxTree ParseSyntaxTreeText(string code, string path = ""); 41 | 42 | public abstract Compilation CreateCompilation( 43 | string compilatioName, 44 | SyntaxTree[] syntaxTrees, 45 | List matadataReferences); 46 | 47 | private static Regex _crefRegex = new Regex(@"]+"); 48 | 49 | //Used for AutoComplete items 50 | protected abstract int GetNewKeywordCode(); 51 | 52 | protected abstract int GetUsingDirectiveCode(); 53 | 54 | protected abstract int GetArgumentCode(); 55 | 56 | protected abstract int GetArgumentListCode(); 57 | 58 | protected abstract int GetIdentifierCode(); 59 | 60 | protected abstract int GetQualifiedNameCode(); 61 | 62 | public abstract string GetUsingNamespaceLinePattern(); 63 | 64 | protected virtual void AppendDefaultMetadataReferences(List references, 65 | bool includeDocumentation) 66 | { 67 | } 68 | 69 | public Project GetProject(List filePaths) 70 | { 71 | var files = new Dictionary(); 72 | foreach (var filePath in filePaths) 73 | { 74 | var syntaxTree = ParseSyntaxTreeFile(filePath); 75 | files.Add(filePath, syntaxTree); 76 | } 77 | 78 | var sources = files.ToDictionary(tree => tree.Key, tree => tree.Value.ToString()); 79 | 80 | var compilation = GetCompilationFromSyntaxTree(files.Values.ToList(), sources.Values.ToList()); 81 | return new Project(this, compilation) 82 | { 83 | Trees = files, 84 | Sources = sources 85 | }; 86 | } 87 | 88 | /// 89 | /// Parse all sources and returns project object 90 | /// 91 | /// Key should be unuque identifier of source like filepath. Key is source code 92 | /// Projectfile 93 | public Project GetProject(Dictionary sources) 94 | { 95 | var files = new Dictionary(); 96 | foreach (var fileId in sources.Keys) 97 | { 98 | var syntaxTree = ParseSyntaxTreeText(sources[fileId], fileId); 99 | files.Add(fileId, syntaxTree); 100 | } 101 | 102 | var compilation = GetCompilationFromSyntaxTree(files.Values.ToList(), sources.Values.ToList()); 103 | return new Project(this, compilation) 104 | { 105 | Trees = files, 106 | Sources = sources 107 | }; 108 | } 109 | 110 | /// 111 | /// Get autocomplete suggestions based on the code 112 | /// 113 | /// Source code 114 | /// Cursor position. If null then the last symbol will be used 115 | /// List of suggested items 116 | public List GetAutoCompleteItems(string code, int? pos = null) 117 | { 118 | var syntaxTree = this.ParseSyntaxTreeText(code); 119 | var semanticModel = this.GetSemanticModelFromSyntaxTree(syntaxTree, code); 120 | return GetAutoCompleteItems(code, syntaxTree, semanticModel, pos); 121 | } 122 | 123 | /// 124 | /// Get autocomplete suggestions based on the code 125 | /// 126 | /// Source code 127 | /// Cursor position. If null then the last symbol will be used 128 | /// List of suggested items 129 | public List GetAutoCompleteItems(Project project, string fileId, int? pos = null) 130 | { 131 | var syntaxTree = project.Trees[fileId]; 132 | var semanticModel = project.Compilation.GetSemanticModel(syntaxTree); 133 | return GetAutoCompleteItems(project.Sources[fileId], project.Trees[fileId], semanticModel, pos); 134 | } 135 | 136 | /// 137 | /// Get autocomplete suggestions based on the code 138 | /// 139 | /// Source code 140 | /// Cursor position. If null then the last symbol will be used 141 | /// List of suggested items 142 | internal List GetAutoCompleteItems(string code, SyntaxTree syntaxTree, 143 | SemanticModel semanticModel, int? pos = null) 144 | { 145 | bool isStaticContext = false; 146 | bool appendDefaultNamespaces = true; 147 | 148 | var position = GetCursorPosition(code, pos); 149 | 150 | var token = syntaxTree.GetRoot().FindToken(position); 151 | 152 | var synNode = token.Parent; 153 | 154 | var forUsing = GetParentByKind(synNode, GetUsingDirectiveCode()); 155 | var forNew = GetParentByKind(synNode, GetNewKeywordCode()); 156 | var errorType = false; 157 | 158 | if (token.Value.Equals(")") && token.Parent.RawKind == GetArgumentListCode()) 159 | { 160 | synNode = token.Parent.Parent; 161 | 162 | //handle inline declaration 163 | forNew = false; 164 | } 165 | 166 | ITypeSymbol classType = semanticModel.GetTypeInfo(synNode).Type; 167 | 168 | if (classType != null && classType.TypeKind == TypeKind.Error) 169 | { 170 | if (!forNew) 171 | { 172 | errorType = true; 173 | } 174 | classType = null; 175 | } 176 | else if (classType != null) 177 | { 178 | var symbol = semanticModel.GetSymbolInfo(synNode).Symbol; 179 | if (symbol is INamedTypeSymbol) 180 | { 181 | isStaticContext = true; 182 | appendDefaultNamespaces = false; 183 | } 184 | 185 | if (symbol is ILocalSymbol) 186 | { 187 | appendDefaultNamespaces = false; 188 | } 189 | } 190 | 191 | var autoCompleteItems = new List(); 192 | 193 | if (errorType && !forUsing) 194 | { 195 | //error type means that token under cursor has unknown type we should not show any items in this case 196 | //unclude static symbols 197 | //var symbols = semanticModel.LookupSymbols(position, 198 | // container: null); 199 | //autoCompleteItems.AddRange(GetAutoCompleteItemsFromSymbols(symbols, false, true, this.Language)); 200 | 201 | return autoCompleteItems; 202 | } 203 | 204 | var isNamespace = synNode.Parent.RawKind == GetQualifiedNameCode(); 205 | 206 | //check the namespace 207 | var namespaceSymbolInfo = semanticModel.GetSymbolInfo(synNode); 208 | 209 | var namespaces = GetNamespaces(appendDefaultNamespaces, namespaceSymbolInfo); 210 | 211 | if (classType == null && (forUsing || isNamespace)) 212 | { 213 | var usingNamespace = isNamespace ? synNode.Parent.ToFullString().Split(new[] {'\r', '\n'}).First() : ""; 214 | if (namespaces.Count(n => n.StartsWith(usingNamespace)) > 1) 215 | { 216 | foreach (var ns in namespaces) 217 | { 218 | string innerNameSpace = null; 219 | if (namespaceSymbolInfo.Symbol != null) 220 | { 221 | innerNameSpace = 222 | ns.Replace(namespaceSymbolInfo.Symbol.ToDisplayString(), "") 223 | .TrimStart('.') 224 | .Split('.') 225 | .First(); 226 | } 227 | else 228 | { 229 | innerNameSpace = 230 | ns.Replace(synNode.Parent.ToFullString(), "").TrimStart('.').Split('.').First(); 231 | } 232 | 233 | if (innerNameSpace.Length > 0) 234 | { 235 | autoCompleteItems.Add( 236 | new AutoCompleteItem() 237 | { 238 | IsStatic = false, 239 | Name = innerNameSpace, 240 | ItemType = AutoCompleteItemType.Namespace 241 | }); 242 | } 243 | } 244 | } 245 | } 246 | 247 | if (!forUsing) 248 | { 249 | var symbols = semanticModel.LookupSymbols( 250 | position, 251 | container: classType ?? (INamespaceOrTypeSymbol) namespaceSymbolInfo.Symbol, 252 | includeReducedExtensionMethods: !forNew); 253 | 254 | autoCompleteItems.AddRange(GetAutoCompleteItemsFromSymbols(symbols, forNew, isStaticContext, 255 | this.Language)); 256 | 257 | if (!forNew && classType == null && !isNamespace && !isStaticContext) 258 | { 259 | //add static items 260 | autoCompleteItems.AddRange(GetAutoCompleteItemsFromSymbols(symbols, false, true, this.Language)); 261 | } 262 | } 263 | else 264 | { 265 | //include static classes in usings/imports 266 | 267 | var symbols = 268 | semanticModel.LookupSymbols(position, container: (INamespaceOrTypeSymbol) namespaceSymbolInfo.Symbol) 269 | .Where(s => s.Kind != SymbolKind.Namespace && s.Kind != SymbolKind.Method) 270 | .ToImmutableArray(); 271 | autoCompleteItems.AddRange(GetAutoCompleteItemsFromSymbols(symbols, false, true, this.Language)); 272 | } 273 | 274 | return autoCompleteItems.Distinct(new AutoCompleteItemEqualityComparer()).OrderBy(i => i.Name).ToList(); 275 | } 276 | 277 | public TokenTypeResult GetTokenType(string code, int? pos = null) 278 | { 279 | var result = new TokenTypeResult(); 280 | 281 | var syntaxTree = this.ParseSyntaxTreeText(code); 282 | var semanticModel = this.GetSemanticModelFromSyntaxTree(syntaxTree, code); 283 | 284 | var position = GetCursorPosition(code, pos); 285 | 286 | var token = (SyntaxToken) syntaxTree.GetRoot().FindToken(position); 287 | 288 | var synNode = token.Parent; 289 | 290 | var forUsing = GetParentByKind(synNode, GetUsingDirectiveCode()); 291 | 292 | if (!forUsing) 293 | { 294 | var argumentList = FindArgumentListRecursive(token.Parent); 295 | if (argumentList != null) 296 | { 297 | result.IsInsideArgumentList = true; 298 | result.PreviousArgumentListTokenTypes = 299 | argumentList.ChildNodesAndTokens() 300 | .Where(t => t.RawKind == GetArgumentCode()) 301 | .Select( 302 | t => 303 | semanticModel.GetTypeInfo( 304 | syntaxTree.GetRoot().FindToken(t.AsNode().Span.Start).Parent).Type) 305 | .Select(t => t != null ? t.ToDisplayString() : null) 306 | .ToArray(); 307 | result.RawArgumentsList = argumentList.ToString(); 308 | result.ParentLine = argumentList.GetLocation().GetLineSpan().StartLinePosition.Line; 309 | result.ParentChar = argumentList.GetLocation().GetLineSpan().StartLinePosition.Character; 310 | } 311 | 312 | ITypeSymbol classType = semanticModel.GetTypeInfo(synNode).Type; 313 | 314 | if (classType != null) 315 | { 316 | result.Type = classType.ToDisplayString(); 317 | } 318 | } 319 | 320 | return result; 321 | } 322 | 323 | private SyntaxNode FindArgumentListRecursive(SyntaxNode node) 324 | { 325 | if (node == null) 326 | { 327 | return null; 328 | } 329 | 330 | if (node.RawKind == GetArgumentListCode()) 331 | { 332 | return node; 333 | } 334 | 335 | return FindArgumentListRecursive(node.Parent); 336 | } 337 | 338 | private bool GetParentByKind(SyntaxNode node, int kind) 339 | { 340 | if (node.RawKind == kind) 341 | { 342 | return true; 343 | } 344 | if (node.Parent != null) 345 | { 346 | return GetParentByKind(node.Parent, kind); 347 | } 348 | 349 | return false; 350 | } 351 | 352 | private List GetNamespaces(bool appendDefaultNamespaces, SymbolInfo namespaceSymbolInfo) 353 | { 354 | var namespaces = new List(); 355 | 356 | if (appendDefaultNamespaces) 357 | { 358 | namespaces.AddRange(NamespaceToDllMap._map.Keys); 359 | } 360 | 361 | // here we can add custom namespaces 362 | 363 | if (namespaceSymbolInfo.Symbol != null && namespaceSymbolInfo.Symbol.Kind == SymbolKind.Namespace) 364 | { 365 | var symbolNameSpaces = new List(); 366 | 367 | // TODO: new Roslyn compatibility issue. new roslyn doesn't have Location property. Should be fixed with next Roslyn or reimplemented somehow 368 | symbolNameSpaces.AddRange( 369 | ((INamespaceSymbol) namespaceSymbolInfo.Symbol).ConstituentNamespaces.OfType() 370 | .SelectMany(n => n.GetNamespaceMembers().Select(s => s.ToDisplayString())) 371 | .Distinct()); 372 | 373 | symbolNameSpaces.AddRange(namespaces); 374 | 375 | namespaces = 376 | symbolNameSpaces.Distinct() 377 | .Where(n => n != null && n.StartsWith(namespaceSymbolInfo.Symbol.ToDisplayString())) 378 | .ToList(); 379 | } 380 | 381 | return namespaces; 382 | } 383 | 384 | private int GetCursorPosition(string code, int? pos) 385 | { 386 | if (pos == null) 387 | { 388 | pos = code.Length; 389 | } 390 | 391 | var codeFragment = pos < code.Length ? code.Substring(0, pos.Value) : code; 392 | 393 | pos = (pos < code.Length ? pos : code.Length) - 1; 394 | 395 | var dotPos = codeFragment.LastIndexOf('.') - 1; 396 | var spacePos = codeFragment.LastIndexOf(' ') - 1; 397 | var openBracketPos = codeFragment.LastIndexOf('('); 398 | var closeBracketPos = codeFragment.LastIndexOf(')'); 399 | 400 | if (openBracketPos > closeBracketPos && openBracketPos > dotPos && openBracketPos > spacePos) 401 | { 402 | return pos.Value; 403 | } 404 | 405 | if (dotPos > spacePos) 406 | { 407 | pos = dotPos; 408 | } 409 | 410 | return pos.Value; 411 | } 412 | 413 | internal MetadataReference CreateMetadataReference(string assemblyDisplayNameOrPath, 414 | bool? includeDocumentation = null) 415 | { 416 | if (!includeDocumentation.HasValue) 417 | includeDocumentation = _options.ParseDocumenation; 418 | 419 | string assemblyFullPath = File.Exists(assemblyDisplayNameOrPath) 420 | ? assemblyDisplayNameOrPath 421 | : AssemblyHelper.GetAssemblyLocation(assemblyDisplayNameOrPath); 422 | 423 | DocumentationProvider documentationProvider = null; 424 | if (!string.IsNullOrWhiteSpace(_systemXmlFilesDir)) 425 | { 426 | string xmlFile = Path.Combine(_systemXmlFilesDir, assemblyDisplayNameOrPath + ".xml"); 427 | 428 | if (includeDocumentation.Value && File.Exists(xmlFile)) 429 | { 430 | documentationProvider = DocumentationProviderFactory.Create(xmlFile); 431 | } 432 | } 433 | return new MetadataFileReference(assemblyFullPath, MetadataReferenceProperties.Assembly, 434 | documentationProvider); 435 | } 436 | 437 | private SemanticModel GetSemanticModelFromSyntaxTree( 438 | SyntaxTree syntaxTree, 439 | string code) 440 | { 441 | var compilation = this.GetCompilationFromSyntaxTree(syntaxTree, code); 442 | SemanticModel semanticModel = compilation.GetSemanticModel(syntaxTree); 443 | return semanticModel; 444 | } 445 | 446 | public Compilation GetCompilationFromSyntaxTree(SyntaxTree syntaxTree, string code) 447 | { 448 | return GetCompilationFromSyntaxTree( 449 | new List() {syntaxTree}, 450 | new List() {code}); 451 | } 452 | 453 | public Compilation GetCompilationFromSyntaxTree( 454 | List syntaxTrees, 455 | List codes) 456 | { 457 | var includeDocumentation = _options.ParseDocumenation; 458 | 459 | MetadataReference mscorlib = CreateMetadataReference("mscorlib", includeDocumentation); 460 | var metaDllReferences = new List {mscorlib}; 461 | 462 | AppendDefaultMetadataReferences(metaDllReferences, includeDocumentation); 463 | 464 | List dllNames = new List(); 465 | foreach (var code in codes) 466 | { 467 | List gacDlls = GetGacDlls(code); 468 | foreach (var dllName in gacDlls) 469 | { 470 | string dllNameWithoutExtension = dllName.Substring(0, dllName.Length - 4); //remove .dll 471 | 472 | if (dllNames.IndexOf(dllNameWithoutExtension) == -1) 473 | { 474 | dllNames.Add(dllNameWithoutExtension); 475 | } 476 | } 477 | } 478 | 479 | foreach (string dllName in dllNames) 480 | { 481 | MetadataReference metaRef = CreateMetadataReference(dllName, includeDocumentation); 482 | metaDllReferences.Add(metaRef); 483 | } 484 | 485 | //http://msdn.microsoft.com/en-us/vstudio/hh500769.aspx#Toc306015688 486 | 487 | Compilation compilation = this.CreateCompilation( 488 | Guid.NewGuid().ToString(), 489 | syntaxTrees: syntaxTrees.ToArray(), 490 | matadataReferences: metaDllReferences); 491 | 492 | return compilation; 493 | } 494 | 495 | private string GetSystemXmlFilesDir() 496 | { 497 | // TODO: update base path for XML documenation for Mono 498 | if (Environment.OSVersion.Platform != PlatformID.Win32NT) 499 | return null; 500 | 501 | string programFilesDir; 502 | //From http://stackoverflow.com/questions/194157/c-sharp-how-to-get-program-files-x86-on-windows-vista-64-bit 503 | if (8 == IntPtr.Size || 504 | (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432")))) 505 | { 506 | programFilesDir = Environment.GetEnvironmentVariable("ProgramFiles(x86)"); 507 | } 508 | else 509 | { 510 | programFilesDir = Environment.GetEnvironmentVariable("ProgramFiles"); 511 | } 512 | 513 | //Ex C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5 514 | 515 | string dir = Path.Combine(programFilesDir, "Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.5"); 516 | return dir; 517 | } 518 | 519 | private static IEnumerable GetAutoCompleteItemsFromSymbols( 520 | ImmutableArray symbols, 521 | bool forNewKeyword, 522 | bool isStaticContext, 523 | Language language) 524 | { 525 | var autoCompleteItemsQuery = symbols.ToList() as IEnumerable;//.AsParallel(); Mono has issue with AsParallel, so for now we do it in simple way 526 | 527 | //if for new keyowrd - get only named types or namespaces 528 | //if in staticcontext - get static members only 529 | if (forNewKeyword) 530 | { 531 | autoCompleteItemsQuery = 532 | autoCompleteItemsQuery.Where( 533 | s => 534 | !s.IsStatic && !s.IsVirtual && !s.IsAbstract 535 | && (s.Kind == SymbolKind.NamedType || s.Kind == SymbolKind.Namespace)); 536 | } 537 | else 538 | { 539 | if (language == Language.CSharp) 540 | { 541 | autoCompleteItemsQuery = autoCompleteItemsQuery.Where(s => s.IsStatic == isStaticContext); 542 | } 543 | 544 | else if (language == Language.VbNet) 545 | { 546 | autoCompleteItemsQuery = 547 | autoCompleteItemsQuery.Where( 548 | s => 549 | (s.Kind != SymbolKind.NamedType && s.IsStatic == isStaticContext) 550 | || (s.Kind == SymbolKind.NamedType && isStaticContext 551 | && ((INamedTypeSymbol) s).GetMembers() 552 | .Any( 553 | m => 554 | m.Kind == SymbolKind.Method && m.IsStatic && m.CanBeReferencedByName 555 | && m.DeclaredAccessibility == Accessibility.Public))); 556 | } 557 | } 558 | 559 | var autoCompleteItems = autoCompleteItemsQuery.SelectMany(i => GetAutoCompleteItem(i, !forNewKeyword)); 560 | 561 | return autoCompleteItems.Distinct(new AutoCompleteItemEqualityComparer()); 562 | } 563 | 564 | private static IEnumerable GetAutoCompleteItem(ISymbol symbol, bool showClassesAsStatic) 565 | { 566 | var result = new List(); 567 | 568 | var item = new AutoCompleteItem {Name = symbol.Name}; 569 | 570 | var itemDoc = symbol.GetDocumentationCommentXml(CultureInfo.GetCultureInfo("en-US")); 571 | 572 | DocumentationComment comment = null; 573 | if (!string.IsNullOrWhiteSpace(itemDoc)) 574 | { 575 | comment = DocumentationComment.FromXmlFragment(itemDoc); 576 | 577 | item.Description = comment.SummaryText; 578 | } 579 | 580 | switch (symbol.Kind) 581 | { 582 | case SymbolKind.Method: 583 | item.ItemType = AutoCompleteItemType.Method; 584 | var methodSymbol = (IMethodSymbol) symbol; 585 | item.IsExtension = methodSymbol.IsExtensionMethod; 586 | item.IsStatic = methodSymbol.IsStatic; 587 | item.Type = methodSymbol.ReturnsVoid ? "void" : methodSymbol.ReturnType.Name; 588 | 589 | // formatting complicated types name like arrays 590 | if (string.IsNullOrWhiteSpace(item.Type)) 591 | item.Type = GetParameterTypeName(methodSymbol.ReturnType); 592 | 593 | //args 594 | item.Params = GetSymbolParameters(methodSymbol.Parameters, comment); 595 | item.IsGeneric = methodSymbol.IsGenericMethod; 596 | break; 597 | case SymbolKind.Local: 598 | item.ItemType = AutoCompleteItemType.Variable; 599 | var localSymbol = (ILocalSymbol) symbol; 600 | item.Type = localSymbol.Type.Name; 601 | break; 602 | case SymbolKind.Field: 603 | item.ItemType = AutoCompleteItemType.Variable; 604 | var fieldSymbol = (IFieldSymbol) symbol; 605 | item.Type = fieldSymbol.Type.Name; 606 | break; 607 | case SymbolKind.Property: 608 | item.ItemType = AutoCompleteItemType.Property; 609 | var propertySymbol = (IPropertySymbol) symbol; 610 | item.Type = propertySymbol.Type.Name; 611 | break; 612 | case SymbolKind.Namespace: 613 | item.ItemType = AutoCompleteItemType.Namespace; 614 | var namespaceSymbol = (INamespaceSymbol) symbol; 615 | item.Name = namespaceSymbol.Name; 616 | break; 617 | case SymbolKind.NamedType: 618 | item.ItemType = AutoCompleteItemType.Class; 619 | var classSymbol = (INamedTypeSymbol) symbol; 620 | item.Name = classSymbol.Name; 621 | item.IsStatic = showClassesAsStatic || classSymbol.IsStatic; 622 | item.IsGeneric = classSymbol.IsGenericType; 623 | 624 | if (!showClassesAsStatic) 625 | { 626 | var constructors = classSymbol.Constructors; 627 | foreach (var constructor in constructors) 628 | { 629 | itemDoc = constructor.GetDocumentationCommentXml(CultureInfo.GetCultureInfo("en-US")); 630 | 631 | DocumentationComment doc = null; 632 | if (!string.IsNullOrWhiteSpace(itemDoc)) 633 | { 634 | doc = DocumentationComment.FromXmlFragment(itemDoc); 635 | } 636 | 637 | var consItem = (AutoCompleteItem) item.Clone(); 638 | if (doc != null && doc.SummaryText != null) 639 | { 640 | consItem.Description = GetItemDescription(doc.SummaryText); 641 | } 642 | consItem.Params = GetSymbolParameters(constructor.Parameters, doc); 643 | result.Add(consItem); 644 | } 645 | } 646 | break; 647 | } 648 | 649 | if (result.Count == 0) 650 | { 651 | result.Add(item); 652 | } 653 | 654 | return result; 655 | } 656 | 657 | private static string GetItemDescription(string rawDesc) 658 | { 659 | var result = rawDesc; 660 | var match = _crefRegex.Match(result); 661 | while (match.Success) 662 | { 663 | result = result.Replace(match.Value.Trim(), "" + match.Groups[1].Value.Split('.').Last() + ""); 664 | match = _crefRegex.Match(result); 665 | } 666 | 667 | return result; 668 | } 669 | 670 | private static AutoCompleteItemParameter[] GetSymbolParameters( 671 | ImmutableArray paramsArray, 672 | DocumentationComment comment, 673 | bool includeThis = false) 674 | { 675 | var result = 676 | paramsArray.Where(p => !includeThis || !p.IsThis) 677 | .Select( 678 | p => 679 | new AutoCompleteItemParameter() 680 | { 681 | Name = p.Name, 682 | Type = GetParameterTypeName(p.Type), 683 | Description = comment != null ? comment.GetParameterText(p.Name) : null, 684 | IsParams = p.IsParams, 685 | IsOptional = p.IsOptional 686 | }) 687 | .ToArray(); 688 | return result.Length == 0 ? null : result; 689 | } 690 | 691 | private static string GetParameterTypeName(ITypeSymbol type) 692 | { 693 | var symbol = type as IPointerTypeSymbol; 694 | if (symbol != null) 695 | { 696 | return symbol.PointedAtType.ToDisplayString() + "*"; 697 | } 698 | var symbol2 = type as IArrayTypeSymbol; 699 | if (symbol2 != null) 700 | { 701 | return symbol2.ElementType.ToDisplayString() + "[]"; 702 | } 703 | 704 | var symbol3 = type as INamedTypeSymbol; 705 | if (symbol3 != null && symbol3.TypeArguments.Length > 0) 706 | { 707 | return symbol3.ConstructedFrom.ToDisplayString() + "<" 708 | + String.Join(", ", symbol3.TypeArguments.Select(t => t.ToDisplayString())) + ">"; 709 | } 710 | 711 | return type.ToDisplayString(); 712 | } 713 | 714 | protected virtual List GetGacDlls(string code) 715 | { 716 | List referencedDlls = NamespaceToDllMap.GetDlls(this.GetUsedNamespaces(code)); 717 | 718 | // expressions require System.Core assemblies, so we can't understand when to add it, so we will add it for every run 719 | if (!referencedDlls.Contains("System.Core.dll")) 720 | { 721 | referencedDlls.Add("System.Core.dll"); 722 | } 723 | 724 | // used by dynamic keyword 725 | if (!referencedDlls.Contains("Microsoft.CSharp.dll")) 726 | { 727 | referencedDlls.Add("Microsoft.CSharp.dll"); 728 | } 729 | 730 | return referencedDlls; 731 | } 732 | 733 | /// 734 | /// Get list of namespaces that used in code block 735 | /// 736 | /// Source code 737 | /// List of namespaces 738 | public List GetUsedNamespaces(string code) 739 | { 740 | string pattern = GetUsingNamespaceLinePattern(); 741 | 742 | var regexOpts = RegexOptions.Multiline | RegexOptions.IgnoreCase; 743 | Regex regex = new Regex(pattern, regexOpts); 744 | 745 | var usingNamespaces = new List(); 746 | 747 | if (!String.IsNullOrEmpty(code)) 748 | { 749 | var matches = regex.Matches(code); 750 | foreach (Match match in matches) 751 | { 752 | usingNamespaces.Add(match.Groups[1].Value); 753 | } 754 | } 755 | 756 | return usingNamespaces; 757 | } 758 | 759 | public SyntaxTree ParseSyntaxTreeFile(string filePath) 760 | { 761 | // since Roslyn is Portable lib now, they removed method for ParseFIle 762 | var content = File.ReadAllText(filePath); 763 | var tree = this.ParseSyntaxTreeText(content, filePath); 764 | return tree; 765 | } 766 | 767 | } 768 | } --------------------------------------------------------------------------------