diff options
Diffstat (limited to 'main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem')
29 files changed, 3059 insertions, 5401 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs index 65621369b2..07849bbe88 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs @@ -30,51 +30,74 @@ using System; using System.Text; using System.Collections.Generic; using MonoDevelop.Core; -using ICSharpCode.NRefactory.TypeSystem; using Mono.Cecil; using System.Linq; using MonoDevelop.Ide.CodeCompletion; +using Microsoft.CodeAnalysis; +using System.Threading; +using System.Xml; +using Mono.Addins; +using MonoDevelop.Ide.Extensions; namespace MonoDevelop.Ide.TypeSystem { - public abstract class Ambience - { - public string Name { - get; - private set; - } - - - public Ambience (string name) - { - this.Name = name; - } - - #region To implement - public abstract string GetIntrinsicTypeName (string reflectionName); - - public abstract string SingleLineComment (string text); - public abstract string GetString (string nameSpace, OutputSettings settings); - - protected abstract string GetTypeReferenceString (IType reference, OutputSettings settings); - protected abstract string GetTypeString (IType type, OutputSettings settings); - protected abstract string GetMethodString (IMethod method, OutputSettings settings); - protected abstract string GetConstructorString (IMethod constructor, OutputSettings settings); - protected abstract string GetDestructorString (IMethod destructor, OutputSettings settings); - protected abstract string GetOperatorString (IMethod op, OutputSettings settings); - - protected abstract string GetFieldString (IField field, OutputSettings settings); - protected abstract string GetEventString (IEvent evt, OutputSettings settings); - protected abstract string GetPropertyString (IProperty property, OutputSettings settings); - protected abstract string GetIndexerString (IProperty property, OutputSettings settings); - protected abstract string GetParameterString (IParameterizedMember member, IParameter parameter, OutputSettings settings); - - #endregion - - public virtual TooltipInformation GetTooltip (IEntity entity) + public static class Ambience + { + public static readonly SymbolDisplayFormat LabelFormat = + new SymbolDisplayFormat( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + propertyStyle: SymbolDisplayPropertyStyle.NameOnly, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance, + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeExplicitInterface, + parameterOptions: + SymbolDisplayParameterOptions.IncludeDefaultValue | + SymbolDisplayParameterOptions.IncludeExtensionThis | + SymbolDisplayParameterOptions.IncludeType | + SymbolDisplayParameterOptions.IncludeName | + SymbolDisplayParameterOptions.IncludeParamsRefOut, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | + SymbolDisplayMiscellaneousOptions.UseSpecialTypes + ); + /// <summary> + /// Standard format for displaying to the user. + /// </summary> + /// <remarks> + /// No return type. + /// </remarks> + public static readonly SymbolDisplayFormat NameFormat = + new SymbolDisplayFormat( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + propertyStyle: SymbolDisplayPropertyStyle.NameOnly, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance, + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeExplicitInterface, + parameterOptions: + SymbolDisplayParameterOptions.IncludeParamsRefOut | + SymbolDisplayParameterOptions.IncludeExtensionThis | + SymbolDisplayParameterOptions.IncludeType | + SymbolDisplayParameterOptions.IncludeName, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | + SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + static Ambience () { - return null; + // may not have been initialized in testing environment. + if (AddinManager.IsInitialized) { + AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/AmbienceTooltipProviders", delegate(object sender, ExtensionNodeEventArgs args) { + var node = args.ExtensionNode as MimeTypeExtensionNode; + switch (args.Change) { + case ExtensionChange.Add: + tooltipProviders.Add ((AmbienceTooltipProvider)node.CreateInstance ()); + break; + case ExtensionChange.Remove: + tooltipProviders.Remove ((AmbienceTooltipProvider)node.CreateInstance ()); + break; + } + }); + } } public static string Format (string str) @@ -86,104 +109,490 @@ namespace MonoDevelop.Ide.TypeSystem MarkupUtilities.AppendEscapedString (sb, str); return sb.ToString (); } + + #region Documentation - protected static OutputFlags GetFlags (object settings) + public class DocumentationFormatOptions { - if (settings is OutputFlags) - return (OutputFlags)settings; - return ((OutputSettings)settings).OutputFlags; + public static readonly DocumentationFormatOptions Empty = new DocumentationFormatOptions (); + public string HighlightParameter { + get; + set; + } + + public int MaxLineLength { + get; + set; + } + + public bool BigHeadings { + get; + set; + } + + public bool BoldHeadings { + get; + set; + } + + public bool SmallText { + get; + set; + } + + + public DocumentationFormatOptions () + { + BoldHeadings = true; + } + + public string FormatHeading (string heading) + { + string result = heading; + if (BigHeadings) + result = "<big>" + result + "</big>"; + if (BoldHeadings) + result = "<b>" + result + "</b>"; + return result; + } + + public string FormatBody (string body) + { + var str = body.Trim (); + if (string.IsNullOrEmpty (str)) + return ""; + return SmallText ? "<small>" + str + Environment.NewLine + "</small>" : str + Environment.NewLine; + } + } + + public static string GetSummaryMarkup (ISymbol member, CancellationToken cancellationToken = default(CancellationToken)) + { + if (member == null) + return null; + string documentation = GetDocumentation (member); + if (string.IsNullOrEmpty (documentation)) + return null; + + if (!string.IsNullOrEmpty (documentation)) { + int idx1 = documentation.IndexOf ("<summary>", StringComparison.Ordinal); + int idx2 = documentation.LastIndexOf ("</summary>", StringComparison.Ordinal); + string result; + if (idx1 >= 0 && idx2 > idx1) { + try { + var xmlText = documentation.Substring (idx1, idx2 - idx1 + "</summary>".Length); + return ParseBody (member, + new XmlTextReader (xmlText, XmlNodeType.Element, null), + "summary", + DocumentationFormatOptions.Empty + ); + } catch (Exception e) { + LoggingService.LogWarning ("Malformed documentation xml detected:" + documentation, e); + // may happen on malformed xml. + var len = idx2 - idx1 - "</summary>".Length; + result = len > 0 ? documentation.Substring (idx1 + "<summary>".Length, len) : documentation; + } + } else if (idx1 >= 0) { + result = documentation.Substring (idx1 + "<summary>".Length); + } else if (idx2 >= 0) { + result = documentation.Substring (0, idx2 - 1); + } else { + result = documentation; + } + return GetDocumentationMarkup (member, CleanEmpty (result)); + } + + return GetDocumentationMarkup (member, CleanEmpty (documentation)); } - protected static OutputSettings GetSettings (object settings) + static string CleanEmpty (string doc) + { + return IsEmptyDocumentation (doc)? null : doc; + } + + static bool IsEmptyDocumentation (string documentation) { - if (settings is OutputFlags) - return new OutputSettings ((OutputFlags)settings); - return (OutputSettings)settings; + return string.IsNullOrWhiteSpace (documentation) || documentation.StartsWith ("To be added") || documentation == "we have not entered docs yet"; } - public string GetString (string nameSpace, OutputFlags flags) + public static string GetDocumentation (ISymbol member) { - return GetString (nameSpace, new OutputSettings (flags)); + if (member == null) + return null; + var documentation = member.GetDocumentationCommentXml (); + if (string.IsNullOrEmpty (documentation)) + documentation = MonoDocDocumentationProvider.GetDocumentation (member); + if (documentation != null) + return CleanEmpty (documentation); + return null; } - public string GetString (IEntity entity, OutputSettings settings) - { - if (entity == null) { - string[] trace = Environment.StackTrace.Split (new [] { Environment.NewLine }, StringSplitOptions.None); - return "null entity: " + (trace != null && trace.Length > 2 ? trace [2] : "unknown location"); - } - string result = null; - switch (entity.SymbolKind) { - case SymbolKind.Constructor: - result = GetConstructorString ((IMethod)entity, settings); - break; - case SymbolKind.Destructor: - result = GetDestructorString ((IMethod)entity, settings); - break; - case SymbolKind.Event: - result = GetEventString ((IEvent)entity, settings); - break; - case SymbolKind.Field: - result = GetFieldString ((IField)entity, settings); - break; - case SymbolKind.Indexer: - result = GetPropertyString ((IProperty)entity, settings); - break; - case SymbolKind.Method: - result = GetMethodString ((IMethod)entity, settings); - break; - case SymbolKind.Operator: - result = GetMethodString ((IMethod)entity, settings); - break; - case SymbolKind.Property: - result = GetPropertyString ((IProperty)entity, settings); - break; - case SymbolKind.TypeDefinition: - result = GetTypeString ((ITypeDefinition)entity, settings); - break; - default: - throw new ArgumentOutOfRangeException ("SymbolKind", "Unknown entity type:" + entity.SymbolKind); - } - result = settings.PostProcess (entity, result); - return result; + static string GetCref (ICSharpCode.NRefactory.TypeSystem.ITypeResolveContext ctx, string cref) + { + if (cref == null) + return ""; + + if (cref.Length < 2) + return cref; + try { + var entity = new ICSharpCode.NRefactory.Documentation.DocumentationComment ("", ctx).ResolveCref (cref.Replace("<", "{").Replace(">", "}")); + + if (entity != null) { + var ambience = new ICSharpCode.NRefactory.CSharp.CSharpAmbience (); + ambience.ConversionFlags = ICSharpCode.NRefactory.TypeSystem.ConversionFlags.ShowParameterList | ICSharpCode.NRefactory.TypeSystem.ConversionFlags.ShowParameterNames | ICSharpCode.NRefactory.TypeSystem.ConversionFlags.ShowTypeParameterList; + return ambience.ConvertEntity (entity); + } + } catch (Exception e) { + LoggingService.LogWarning ("Invalid cref:" + cref, e); + } + + if (cref.Substring (1, 1) == ":") + return cref.Substring (2, cref.Length - 2); + + return cref; } - public string GetString (IType type, OutputSettings settings) + static bool IsSpecialChar (int charValue) { - var result = GetTypeString (type, settings); - return settings.PostProcess (type, result); + return + 0x01 <= charValue && charValue <= 0x08 || + 0x0B <= charValue && charValue <= 0x0C || + 0x0E <= charValue && charValue <= 0x1F || + 0x7F <= charValue && charValue <= 0x84 || + 0x86 <= charValue && charValue <= 0x9F; } - -/* public string GetString (ITypeReference reference, OutputSettings settings) + + public static string BreakLines (string text, int maxLineLength) { - var result = GetTypeReferenceString (reference, settings); - return settings.PostProcess (reference, result); - }*/ + if (maxLineLength <= 0) + return text; + StringBuilder result = new StringBuilder (); + int lineLength = 0; + bool inTag = false; + bool inAmp = false; + foreach (char ch in text) { + switch (ch) { + case '<': + inTag = true; + break; + case '>': + inTag = false; + break; + case '&': + inAmp = true; + break; + case ';': + inAmp = false; + break; + case '\n': + lineLength = 0; + break; + case '\r': + lineLength = 0; + break; + } + + result.Append (ch); + if (!inTag && !inAmp) + lineLength++; + if (!Char.IsLetterOrDigit (ch) && lineLength > maxLineLength) { + result.AppendLine (); + lineLength = 0; + } + } + return result.ToString (); + } - public string GetString (IEntity entity, OutputFlags flags) + public static string EscapeText (string text) { - return GetString (entity, new OutputSettings (flags)); + if (text == null) + return null; + StringBuilder result = new StringBuilder (); + foreach (char ch in text) { + switch (ch) { + case '<': + result.Append ("<"); + break; + case '>': + result.Append (">"); + break; + case '&': + result.Append ("&"); + break; + case '\'': + result.Append ("'"); + break; + case '"': + result.Append ("""); + break; + default: + int charValue = (int)ch; + if (IsSpecialChar (charValue)) { + result.AppendFormat ("&#x{0:X};", charValue); + } else { + result.Append (ch); + } + break; + } + } + return result.ToString (); } - public string GetString (IType type, OutputFlags flags) + public static string UnescapeText (string text) { - return GetString (type, new OutputSettings (flags)); + var sb = new StringBuilder (); + for (int i = 0; i < text.Length; i++) { + char ch = text[i]; + if (ch == '&') { + int end = text.IndexOf (';', i); + if (end == -1) + break; + string entity = text.Substring (i + 1, end - i - 1); + switch (entity) { + case "lt": + sb.Append ('<'); + break; + case "gt": + sb.Append ('>'); + break; + case "amp": + sb.Append ('&'); + break; + case "apos": + sb.Append ('\''); + break; + case "quot": + sb.Append ('"'); + break; + } + i = end; + } else { + sb.Append (ch); + } + } + return sb.ToString (); } - public string GetString (ITypeDefinition type, OutputFlags flags) + + public static string GetDocumentationMarkup (ISymbol member, string doc) { - return GetString ((IEntity)type, new OutputSettings (flags)); + return GetDocumentationMarkup (member, doc, DocumentationFormatOptions.Empty); } + static string ParseBody (ISymbol member, XmlTextReader xml, string endTagName, DocumentationFormatOptions options) + { + StringBuilder result = new StringBuilder (); + bool wasWhiteSpace = true; + bool appendSpace = false; + //ITypeResolveContext ctx = member.Compilation.TypeResolveContext; + while (xml.Read ()) { + switch (xml.NodeType) { + case XmlNodeType.EndElement: + if (xml.Name == endTagName) + goto end; + break; + case XmlNodeType.Element: + switch (xml.Name.ToLower ()) { + case "para": + result.AppendLine (ParseBody (member, xml, xml.Name, options)); + wasWhiteSpace = true; + break; + case "see": + if (!wasWhiteSpace) { + result.Append (' '); + wasWhiteSpace = true; + } + result.Append ("<i>"); + string name = (xml ["cref"] + xml ["langword"]).Trim (); + // if (options.Ambience != null) + // name = options.Ambience.GetIntrinsicTypeName (name); + result.Append (EscapeText (name)); + result.Append ("</i>"); + wasWhiteSpace = false; + appendSpace = true; + break; + case "paramref": + if (!wasWhiteSpace) { + result.Append (' '); + wasWhiteSpace = true; + } + result.Append ("<i>"); + result.Append (EscapeText (xml ["name"].Trim ())); + result.Append ("</i>"); + appendSpace = true; + wasWhiteSpace = false; + break; + } + break; + case XmlNodeType.Text: + if (IsEmptyDocumentation (xml.Value)) + break; + foreach (char ch in xml.Value) { + if (!Char.IsWhiteSpace (ch) && appendSpace) { + result.Append (' '); + appendSpace = false; + } + if (Char.IsWhiteSpace (ch) || ch == '\n' || ch == '\r') { + if (!wasWhiteSpace) { + result.Append (' '); + wasWhiteSpace = true; + } + continue; + } + wasWhiteSpace = false; + result.Append (EscapeText (ch.ToString ())); + } + break; + } + } + end: + return result.ToString ().Trim (); + } - public string GetString (IParameterizedMember member, IParameter parameter, OutputFlags flags) + public static string GetDocumentationMarkup (ISymbol member, string doc, DocumentationFormatOptions options) { - return GetParameterString (member, parameter, new OutputSettings (flags)); + if (string.IsNullOrEmpty (doc)) + return null; + System.IO.StringReader reader = new System.IO.StringReader ("<docroot>" + doc + "</docroot>"); + XmlTextReader xml = new XmlTextReader (reader); + StringBuilder ret = new StringBuilder (70); + StringBuilder parameterBuilder = new StringBuilder (); + StringBuilder exceptions = new StringBuilder (); + exceptions.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Exceptions:"))); + // ret.Append ("<small>"); + int paramCount = 0, exceptionCount = 0, summaryEnd = -1; + try { + xml.Read (); + do { + if (xml.NodeType == XmlNodeType.Element) { + switch (xml.Name.ToLower ()) { + case "para": + ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); + if (summaryEnd < 0) + summaryEnd = ret.Length; + break; + case "summary": + var summary = options.FormatBody (ParseBody (member, xml, xml.Name, options)); + if (!IsEmptyDocumentation (summary)) { + // ret.AppendLine (GetHeading ("Summary:", options)); + ret.Append (summary); + if (summaryEnd < 0) + summaryEnd = ret.Length; + } + break; + case "remarks": + if (string.IsNullOrEmpty (options.HighlightParameter)) { + ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Remarks:"))); + ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); + if (summaryEnd < 0) + summaryEnd = ret.Length; + } else { + options.FormatBody (ParseBody (member, xml, xml.Name, options)); + } + break; + // skip <example>-nodes + case "example": + xml.Skip (); + xml.Skip (); + break; + case "exception": + exceptionCount++; + if (options.SmallText) + exceptions.Append ("<small>"); + exceptions.Append ("<b>"); + exceptions.Append (EscapeText (xml ["cref"])); + exceptions.Append (": "); + exceptions.Append ("</b>"); + if (options.SmallText) + exceptions.Append ("</small>"); + + exceptions.AppendLine (options.FormatBody (ParseBody (member, xml, xml.Name, options))); + break; + case "returns": + if (string.IsNullOrEmpty (options.HighlightParameter)) { + ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Returns:"))); + ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); + } else { + options.FormatBody (ParseBody (member, xml, xml.Name, options)); + } + break; + case "param": + string paramName = xml.GetAttribute ("name") != null ? xml ["name"].Trim () : ""; + + var body = options.FormatBody (ParseBody (member, xml, xml.Name, options)); + if (!IsEmptyDocumentation (body)) { + paramCount++; + parameterBuilder.Append ("<i>"); + if (options.HighlightParameter == paramName) + parameterBuilder.Append ("<b>"); + if (options.SmallText) + parameterBuilder.Append ("<small>"); + parameterBuilder.Append (EscapeText (paramName)); + if (options.SmallText) + parameterBuilder.Append ("</small>"); + if (options.HighlightParameter == paramName) + parameterBuilder.Append ("</b>"); + parameterBuilder.Append (":</i> "); + parameterBuilder.Append (body); + } else { + return null; + } + break; + case "value": + ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Value:"))); + ret.AppendLine (options.FormatBody (ParseBody (member, xml, xml.Name, options))); + break; + case "seealso": + if (string.IsNullOrEmpty (options.HighlightParameter)) { + ret.Append (options.FormatHeading (GettextCatalog.GetString ("See also:"))); + ret.Append (" " + EscapeText (xml ["cref"] + xml ["langword"])); + } + break; + } + } + } while (xml.Read ()); + + } catch (Exception ex) { + MonoDevelop.Core.LoggingService.LogError (ex.ToString ()); + return EscapeText (doc); + } + + if (IsEmptyDocumentation (ret.ToString ()) && IsEmptyDocumentation (parameterBuilder.ToString ())) + return EscapeText (doc); + if (string.IsNullOrEmpty (options.HighlightParameter) && exceptionCount > 0) + ret.Append (exceptions.ToString ()); + + string result = ret.ToString (); + if (summaryEnd < 0) + summaryEnd = result.Length; + if (paramCount > 0) { + var paramSb = new StringBuilder (); + if (result.Length > 0) + paramSb.AppendLine ();/* + paramSb.Append ("<small>"); + paramSb.AppendLine (options.FormatHeading (GettextCatalog.GetPluralString ("Parameter:", "Parameters:", paramCount))); + paramSb.Append ("</small>");*/ + paramSb.Append (parameterBuilder.ToString ()); + result = result.Insert (summaryEnd, paramSb.ToString ()); + } + result = result.Trim (); + if (result.EndsWith (Environment.NewLine + "</small>")) + result = result.Substring (0, result.Length - (Environment.NewLine + "</small>").Length) + "</small>"; + return result; } - /* - public string GetString (ITypeReference reference, OutputFlags flags) + #endregion + + #region Tooltips + static List<AmbienceTooltipProvider> tooltipProviders = new List<AmbienceTooltipProvider>(); + + public static TooltipInformation GetTooltip(ISymbol symbol) { - return GetString (reference, new OutputSettings (flags)); - }*/ + foreach (var tp in tooltipProviders) { + var result = tp.GetTooltip (symbol); + if (result != null) + return result; + } + return null; + } + #endregion } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/AmbienceService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/AmbienceService.cs deleted file mode 100644 index 794144d931..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/AmbienceService.cs +++ /dev/null @@ -1,559 +0,0 @@ -// -// AmbienceService.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (C) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -using Mono.Addins; -using System.Xml; -using System.Text; -using ICSharpCode.NRefactory.TypeSystem; -using MonoDevelop.Ide; -using MonoDevelop.Projects; -using MonoDevelop.Core; -using MonoDevelop.Ide.Extensions; -using ICSharpCode.NRefactory.Documentation; - -namespace MonoDevelop.Ide.TypeSystem -{ - public static class AmbienceService - { - static Dictionary <string, Ambience> ambiences = new Dictionary <string, Ambience> (); - - static AmbienceService () - { - // may not have been initialized in testing environment. - if (AddinManager.IsInitialized) { - AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/Ambiences", delegate(object sender, ExtensionNodeEventArgs args) { - var ambience = args.ExtensionNode as MimeTypeExtensionNode; - switch (args.Change) { - case ExtensionChange.Add: - ambiences[ambience.MimeType] = (Ambience) ambience.CreateInstance (); - break; - case ExtensionChange.Remove: - ambiences.Remove (ambience.MimeType); - break; - } - }); - } - } - - public static Ambience GetAmbience (IMember member) - { - return GetAmbienceForFile (member.DeclaringTypeDefinition.Region.FileName) ?? new NetAmbience (); - } - - public static Ambience GetAmbienceForFile (string fileName) - { - if (string.IsNullOrEmpty (fileName)) - return DefaultAmbience; - return GetAmbience (DesktopService.GetMimeTypeForUri (fileName)); - } - - public static Ambience GetAmbience (string mimeType) - { - Ambience result; - if (!string.IsNullOrEmpty (mimeType) && ambiences.TryGetValue (mimeType, out result)) - return result; - return defaultAmbience; - } - - static Ambience defaultAmbience = new NetAmbience (); - - public static Ambience DefaultAmbience { get { return defaultAmbience; } } - #region Documentation - - public class DocumentationFormatOptions - { - public static readonly DocumentationFormatOptions Empty = new DocumentationFormatOptions (); - public string HighlightParameter { - get; - set; - } - - public int MaxLineLength { - get; - set; - } - - public bool BigHeadings { - get; - set; - } - - public bool BoldHeadings { - get; - set; - } - - public bool SmallText { - get; - set; - } - - public Ambience Ambience { - get; - set; - } - - - public DocumentationFormatOptions () - { - BoldHeadings = true; - } - - public string FormatHeading (string heading) - { - string result = heading; - if (BigHeadings) - result = "<big>" + result + "</big>"; - if (BoldHeadings) - result = "<b>" + result + "</b>"; - return result; - } - - public string FormatBody (string body) - { - var str = body.Trim (); - if (string.IsNullOrEmpty (str)) - return ""; - return SmallText ? "<small>" + str + Environment.NewLine + "</small>" : str + Environment.NewLine; - } - } - - public static string GetSummaryMarkup (IEntity member) - { - if (member == null || member.Documentation == null) - return null; - string documentation = member.Documentation.Xml.Text; - if (!string.IsNullOrEmpty (documentation)) { - int idx1 = documentation.IndexOf ("<summary>", StringComparison.Ordinal); - int idx2 = documentation.LastIndexOf ("</summary>", StringComparison.Ordinal); - string result; - if (idx1 >= 0 && idx2 > idx1) { - try { - var xmlText = documentation.Substring (idx1, idx2 - idx1 + "</summary>".Length); - return ParseBody (member, - new XmlTextReader (xmlText, XmlNodeType.Element, null), - "summary", - DocumentationFormatOptions.Empty - ); - } catch (Exception e) { - LoggingService.LogWarning ("Malformed documentation xml detected:" + documentation, e); - // may happen on malformed xml. - var len = idx2 - idx1 - "</summary>".Length; - result = len > 0 ? documentation.Substring (idx1 + "<summary>".Length, len) : documentation; - } - } else if (idx1 >= 0) { - result = documentation.Substring (idx1 + "<summary>".Length); - } else if (idx2 >= 0) { - result = documentation.Substring (0, idx2 - 1); - } else { - result = documentation; - } - return GetDocumentationMarkup (member, CleanEmpty (result)); - } - - return GetDocumentationMarkup (member, CleanEmpty (documentation)); - } - - static string CleanEmpty (string doc) - { - return IsEmptyDocumentation (doc)? null : doc; - } - - static bool IsEmptyDocumentation (string documentation) - { - return string.IsNullOrWhiteSpace (documentation) || documentation.StartsWith ("To be added") || documentation == "we have not entered docs yet"; - } - - public static string GetDocumentation (IEntity member) - { - if (member == null) - return null; - if (member.Documentation != null) - return CleanEmpty (member.Documentation); - return null; - } - - static string GetCref (ITypeResolveContext ctx, string cref) - { - if (cref == null) - return ""; - - if (cref.Length < 2) - return cref; - try { - var entity = new DocumentationComment ("", ctx).ResolveCref (cref.Replace("<", "{").Replace(">", "}")); - - if (entity != null) { - var ambience = new ICSharpCode.NRefactory.CSharp.CSharpAmbience (); - ambience.ConversionFlags = ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterNames | ConversionFlags.ShowTypeParameterList; - return ambience.ConvertSymbol (entity); - } - } catch (Exception e) { - LoggingService.LogWarning ("Invalid cref:" + cref, e); - } - - if (cref.Substring (1, 1) == ":") - return cref.Substring (2, cref.Length - 2); - - return cref; - } - - static bool IsSpecialChar (int charValue) - { - return - 0x01 <= charValue && charValue <= 0x08 || - 0x0B <= charValue && charValue <= 0x0C || - 0x0E <= charValue && charValue <= 0x1F || - 0x7F <= charValue && charValue <= 0x84 || - 0x86 <= charValue && charValue <= 0x9F; - } - - public static string BreakLines (string text, int maxLineLength) - { - if (maxLineLength <= 0) - return text; - StringBuilder result = new StringBuilder (); - int lineLength = 0; - bool inTag = false; - bool inAmp = false; - foreach (char ch in text) { - switch (ch) { - case '<': - inTag = true; - break; - case '>': - inTag = false; - break; - case '&': - inAmp = true; - break; - case ';': - inAmp = false; - break; - case '\n': - lineLength = 0; - break; - case '\r': - lineLength = 0; - break; - } - - result.Append (ch); - if (!inTag && !inAmp) - lineLength++; - if (!Char.IsLetterOrDigit (ch) && lineLength > maxLineLength) { - result.AppendLine (); - lineLength = 0; - } - } - return result.ToString (); - } - - public static string EscapeText (string text) - { - if (text == null) - return null; - StringBuilder result = new StringBuilder (); - foreach (char ch in text) { - switch (ch) { - case '<': - result.Append ("<"); - break; - case '>': - result.Append (">"); - break; - case '&': - result.Append ("&"); - break; - case '\'': - result.Append ("'"); - break; - case '"': - result.Append ("""); - break; - default: - int charValue = (int)ch; - if (IsSpecialChar (charValue)) { - result.AppendFormat ("&#x{0:X};", charValue); - } else { - result.Append (ch); - } - break; - } - } - return result.ToString (); - } - - public static string UnescapeText (string text) - { - var sb = new StringBuilder (); - for (int i = 0; i < text.Length; i++) { - char ch = text[i]; - if (ch == '&') { - int end = text.IndexOf (';', i); - if (end == -1) - break; - string entity = text.Substring (i + 1, end - i - 1); - switch (entity) { - case "lt": - sb.Append ('<'); - break; - case "gt": - sb.Append ('>'); - break; - case "amp": - sb.Append ('&'); - break; - case "apos": - sb.Append ('\''); - break; - case "quot": - sb.Append ('"'); - break; - } - i = end; - } else { - sb.Append (ch); - } - } - return sb.ToString (); - } - - - public static string GetDocumentationMarkup (IEntity member, string doc) - { - return GetDocumentationMarkup (member, doc, DocumentationFormatOptions.Empty); - } - - static string ParseBody (IEntity member, XmlTextReader xml, string endTagName, DocumentationFormatOptions options) - { - StringBuilder result = new StringBuilder (); - bool wasWhiteSpace = true; - bool appendSpace = false; - ITypeResolveContext ctx = member.Compilation.TypeResolveContext; - while (xml.Read ()) { - switch (xml.NodeType) { - case XmlNodeType.EndElement: - if (xml.Name == endTagName) - goto end; - break; - case XmlNodeType.Element: - switch (xml.Name.ToLower ()) { - case "para": - result.AppendLine (ParseBody (member, xml, xml.Name, options)); - wasWhiteSpace = true; - break; - case "see": - if (!wasWhiteSpace) { - result.Append (' '); - wasWhiteSpace = true; - } - result.Append ("<i>"); - string name = (GetCref (ctx, xml ["cref"]) + xml ["langword"]).Trim (); - if (options.Ambience != null) - name = options.Ambience.GetIntrinsicTypeName (name); - result.Append (EscapeText (name)); - result.Append ("</i>"); - wasWhiteSpace = false; - appendSpace = true; - break; - case "paramref": - if (!wasWhiteSpace) { - result.Append (' '); - wasWhiteSpace = true; - } - result.Append ("<i>"); - result.Append (EscapeText (xml ["name"].Trim ())); - result.Append ("</i>"); - appendSpace = true; - wasWhiteSpace = false; - break; - } - break; - case XmlNodeType.Text: - if (IsEmptyDocumentation (xml.Value)) - break; - foreach (char ch in xml.Value) { - if (!Char.IsWhiteSpace (ch) && appendSpace) { - result.Append (' '); - appendSpace = false; - } - if (Char.IsWhiteSpace (ch) || ch == '\n' || ch == '\r') { - if (!wasWhiteSpace) { - result.Append (' '); - wasWhiteSpace = true; - } - continue; - } - wasWhiteSpace = false; - result.Append (EscapeText (ch.ToString ())); - } - break; - } - } - end: - return result.ToString ().Trim (); - } - - public static string GetDocumentationMarkup (IEntity member, string doc, DocumentationFormatOptions options) - { - if (string.IsNullOrEmpty (doc)) - return null; - System.IO.StringReader reader = new System.IO.StringReader ("<docroot>" + doc + "</docroot>"); - XmlTextReader xml = new XmlTextReader (reader); - StringBuilder ret = new StringBuilder (70); - StringBuilder parameterBuilder = new StringBuilder (); - StringBuilder exceptions = new StringBuilder (); - exceptions.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Exceptions:"))); - // ret.Append ("<small>"); - int paramCount = 0, exceptionCount = 0, summaryEnd = -1; - try { - xml.Read (); - do { - if (xml.NodeType == XmlNodeType.Element) { - switch (xml.Name.ToLower ()) { - case "para": - ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); - if (summaryEnd < 0) - summaryEnd = ret.Length; - break; - case "summary": - var summary = options.FormatBody (ParseBody (member, xml, xml.Name, options)); - if (!IsEmptyDocumentation (summary)) { - // ret.AppendLine (GetHeading ("Summary:", options)); - ret.Append (summary); - if (summaryEnd < 0) - summaryEnd = ret.Length; - } - break; - case "remarks": - if (string.IsNullOrEmpty (options.HighlightParameter)) { - ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Remarks:"))); - ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); - if (summaryEnd < 0) - summaryEnd = ret.Length; - } else { - options.FormatBody (ParseBody (member, xml, xml.Name, options)); - } - break; - // skip <example>-nodes - case "example": - xml.Skip (); - xml.Skip (); - break; - case "exception": - exceptionCount++; - if (options.SmallText) - exceptions.Append ("<small>"); - exceptions.Append ("<b>"); - exceptions.Append (EscapeText (GetCref (member.Compilation.TypeResolveContext, xml ["cref"]))); - exceptions.Append (": "); - exceptions.Append ("</b>"); - if (options.SmallText) - exceptions.Append ("</small>"); - - exceptions.AppendLine (options.FormatBody (ParseBody (member, xml, xml.Name, options))); - break; - case "returns": - if (string.IsNullOrEmpty (options.HighlightParameter)) { - ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Returns:"))); - ret.Append (options.FormatBody (ParseBody (member, xml, xml.Name, options))); - } else { - options.FormatBody (ParseBody (member, xml, xml.Name, options)); - } - break; - case "param": - string paramName = xml.GetAttribute ("name") != null ? xml ["name"].Trim () : ""; - - var body = options.FormatBody (ParseBody (member, xml, xml.Name, options)); - if (!IsEmptyDocumentation (body)) { - paramCount++; - parameterBuilder.Append ("<i>"); - if (options.HighlightParameter == paramName) - parameterBuilder.Append ("<b>"); - if (options.SmallText) - parameterBuilder.Append ("<small>"); - parameterBuilder.Append (EscapeText (paramName)); - if (options.SmallText) - parameterBuilder.Append ("</small>"); - if (options.HighlightParameter == paramName) - parameterBuilder.Append ("</b>"); - parameterBuilder.Append (":</i> "); - parameterBuilder.Append (body); - } else { - return null; - } - break; - case "value": - ret.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Value:"))); - ret.AppendLine (options.FormatBody (ParseBody (member, xml, xml.Name, options))); - break; - case "seealso": - if (string.IsNullOrEmpty (options.HighlightParameter)) { - ret.Append (options.FormatHeading (GettextCatalog.GetString ("See also:"))); - ret.Append (" " + EscapeText (GetCref (member.Compilation.TypeResolveContext, xml ["cref"]) + xml ["langword"])); - } - break; - } - } - } while (xml.Read ()); - - } catch (Exception ex) { - MonoDevelop.Core.LoggingService.LogError (ex.ToString ()); - return EscapeText (doc); - } - - if (IsEmptyDocumentation (ret.ToString ()) && IsEmptyDocumentation (parameterBuilder.ToString ())) - return EscapeText (doc); - if (string.IsNullOrEmpty (options.HighlightParameter) && exceptionCount > 0) - ret.Append (exceptions.ToString ()); - - string result = ret.ToString (); - if (summaryEnd < 0) - summaryEnd = result.Length; - if (paramCount > 0) { - var paramSb = new StringBuilder (); - if (result.Length > 0) - paramSb.AppendLine ();/* - paramSb.Append ("<small>"); - paramSb.AppendLine (options.FormatHeading (GettextCatalog.GetPluralString ("Parameter:", "Parameters:", paramCount))); - paramSb.Append ("</small>");*/ - paramSb.Append (parameterBuilder.ToString ()); - result = result.Insert (summaryEnd, paramSb.ToString ()); - } - result = result.Trim (); - if (result.EndsWith (Environment.NewLine + "</small>")) - result = result.Substring (0, result.Length - (Environment.NewLine + "</small>").Length) + "</small>"; - return result; - } - #endregion - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IRefactoringContext.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/AmbienceTooltipProvider.cs index c14b46e8fc..4fd10c0a32 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IRefactoringContext.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/AmbienceTooltipProvider.cs @@ -1,10 +1,10 @@ // -// IRefactoringContext.cs +// AmbienceTooltipProvider.cs // // Author: // Mike Krüger <mkrueger@xamarin.com> // -// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// Copyright (c) 2015 Xamarin Inc. (http://xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -24,18 +24,17 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -using System; +using MonoDevelop.Ide.CodeCompletion; +using Microsoft.CodeAnalysis; namespace MonoDevelop.Ide.TypeSystem { /// <summary> - /// Base interface for refactoring contexts + /// Provides tooltips for a roslyn symbol. + /// Note: Intentionally part of internal extension API. /// </summary> - public interface IRefactoringContext + abstract class AmbienceTooltipProvider { - /// <summary> - /// Creates a refactoring script. - /// </summary> - IDisposable CreateScript (); + public abstract TooltipInformation GetTooltip (ISymbol symbol); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerationService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerationService.cs deleted file mode 100644 index 4c857af7d9..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerationService.cs +++ /dev/null @@ -1,563 +0,0 @@ -// -// CodeGenerationService.cs -// -// Author: -// mkrueger <mkrueger@novell.com> -// -// Copyright (c) 2011 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using Mono.TextEditor; -using MonoDevelop.Core; -using System.CodeDom; -using MonoDevelop.Projects; -using System.CodeDom.Compiler; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory; -using MonoDevelop.Ide.TypeSystem; -using MonoDevelop.Ide; -using ICSharpCode.NRefactory.CSharp.TypeSystem; -using System.Threading.Tasks; - -namespace MonoDevelop.Ide.TypeSystem -{ - public static class CodeGenerationService - { - public static IUnresolvedMember AddCodeDomMember (Project project, IUnresolvedTypeDefinition type, CodeTypeMember newMember) - { - bool isOpen; - var data = TextFileProvider.Instance.GetTextEditorData (type.Region.FileName, out isOpen); - var parsedDocument = TypeSystemService.ParseFile (data.Document.FileName, data.Document.MimeType, data.Text); - - var insertionPoints = GetInsertionPoints (data, parsedDocument, type); - - var suitableInsertionPoint = GetSuitableInsertionPoint (insertionPoints, type, newMember); - - var dotNetProject = project as DotNetProject; - if (dotNetProject == null) { - LoggingService.LogError ("Only .NET projects are supported."); - return null; - } - - var generator = dotNetProject.LanguageBinding.GetCodeDomProvider (); - StringWriter sw = new StringWriter (); - var options = new CodeGeneratorOptions (); - options.IndentString = data.GetLineIndent (type.Region.BeginLine) + "\t"; - if (newMember is CodeMemberMethod) - options.BracingStyle = "C"; - generator.GenerateCodeFromMember (newMember, sw, options); - - var code = sw.ToString (); - if (!string.IsNullOrEmpty (code)) - suitableInsertionPoint.Insert (data, code); - if (!isOpen) { - try { - File.WriteAllText (type.Region.FileName, data.Text); - } catch (Exception e) { - LoggingService.LogError (string.Format ("Failed to write file '{0}'.", type.Region.FileName), e); - MessageService.ShowError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName)); - } - } - var newDocument = TypeSystemService.ParseFile (data.FileName, data.MimeType, data.Text); - return newDocument.ParsedFile.GetMember (suitableInsertionPoint.Location.Line, int.MaxValue); - } - - public static void AddNewMember (ITypeDefinition type, IUnresolvedTypeDefinition part, IUnresolvedMember newMember, bool implementExplicit = false) - { - bool isOpen; - var data = TextFileProvider.Instance.GetTextEditorData (part.Region.FileName, out isOpen); - var parsedDocument = TypeSystemService.ParseFile (data.FileName, data.MimeType, data.Text); - - var insertionPoints = GetInsertionPoints (data, parsedDocument, part); - - var suitableInsertionPoint = GetSuitableInsertionPoint (insertionPoints, part, newMember); - - var generator = CreateCodeGenerator (data, type.Compilation); - - generator.IndentLevel = CalculateBodyIndentLevel (parsedDocument.GetInnermostTypeDefinition (type.Region.Begin)); - var generatedCode = generator.CreateMemberImplementation (type, part, newMember, implementExplicit); - suitableInsertionPoint.Insert (data, generatedCode.Code); - if (!isOpen) { - try { - File.WriteAllText (type.Region.FileName, data.Text); - } catch (Exception e) { - LoggingService.LogError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName), e); - MessageService.ShowError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName)); - } - } - } - - public static Task<bool> InsertMemberWithCursor ( - string operation, ITypeDefinition parentType, IUnresolvedTypeDefinition part, - IUnresolvedMember newMember, bool implementExplicit = false) - { - var tcs = new TaskCompletionSource<bool>(); - if (parentType == null) - return tcs.Task; - part = part ?? parentType.Parts.FirstOrDefault (); - if (part == null) - return tcs.Task; - var loadedDocument = IdeApp.Workbench.OpenDocument (part.Region.FileName); - loadedDocument.RunWhenLoaded (delegate { - var editor = loadedDocument.Editor; - var loc = part.Region.Begin; - var parsedDocument = loadedDocument.UpdateParseDocument (); - var declaringType = parsedDocument.GetInnermostTypeDefinition (loc); - var mode = new InsertionCursorEditMode ( - editor.Parent, - CodeGenerationService.GetInsertionPoints (loadedDocument, declaringType)); - if (mode.InsertionPoints.Count == 0) { - MessageService.ShowError ( - GettextCatalog.GetString ("No valid insertion point can be found in type '{0}'.", declaringType.Name) - ); - return; - } - var suitableInsertionPoint = GetSuitableInsertionPoint (mode.InsertionPoints, part, newMember); - if (suitableInsertionPoint != null) - mode.CurIndex = mode.InsertionPoints.IndexOf (suitableInsertionPoint); - else - mode.CurIndex = 0; - - var helpWindow = new Mono.TextEditor.PopupWindow.InsertionCursorLayoutModeHelpWindow () { - TitleText = operation - }; - mode.HelpWindow = helpWindow; - - mode.StartMode (); - mode.Exited += delegate(object s, InsertionCursorEventArgs iCArgs) { - if (!iCArgs.Success) { - tcs.SetResult (false); - return; - } - var generator = CreateCodeGenerator (editor, parentType.Compilation); - generator.IndentLevel = CalculateBodyIndentLevel (declaringType); - var generatedCode = generator.CreateMemberImplementation (parentType, part, newMember, implementExplicit); - mode.InsertionPoints[mode.CurIndex].Insert (editor, generatedCode.Code); - tcs.SetResult (true); - }; - }); - - return tcs.Task; - } - - public static Task<bool> InsertMember ( - ITypeDefinition parentType, IUnresolvedTypeDefinition part, - IUnresolvedMember newMember, bool implementExplicit = false) - { - var tcs = new TaskCompletionSource<bool>(); - if (parentType == null) - return tcs.Task; - part = part ?? parentType.Parts.FirstOrDefault (); - if (part == null) - return tcs.Task; - - var loadedDocument = IdeApp.Workbench.OpenDocument (part.Region.FileName); - loadedDocument.RunWhenLoaded (delegate { - var editor = loadedDocument.Editor; - var loc = part.Region.Begin; - var parsedDocument = loadedDocument.UpdateParseDocument (); - var declaringType = parsedDocument.GetInnermostTypeDefinition (loc); - var insertionPoints = CodeGenerationService.GetInsertionPoints (loadedDocument, declaringType); - if (insertionPoints.Count == 0) { - MessageService.ShowError ( - GettextCatalog.GetString ("No valid insertion point can be found in type '{0}'.", declaringType.Name) - ); - return; - } - var suitableInsertionPoint = GetSuitableInsertionPoint (insertionPoints, part, newMember) ?? insertionPoints.First (); - - var generator = CreateCodeGenerator (editor, parentType.Compilation); - generator.IndentLevel = CalculateBodyIndentLevel (declaringType); - var generatedCode = generator.CreateMemberImplementation (parentType, part, newMember, implementExplicit); - suitableInsertionPoint.Insert (editor, generatedCode.Code); - }); - - return tcs.Task; - } - - public static int CalculateBodyIndentLevel (IUnresolvedTypeDefinition declaringType) - { - if (declaringType == null) - return 0; - int indentLevel = 1; - while (declaringType.DeclaringTypeDefinition != null) { - indentLevel++; - declaringType = declaringType.DeclaringTypeDefinition; - } - var file = declaringType.UnresolvedFile as CSharpUnresolvedFile; - if (file == null) - return indentLevel; - var scope = file.GetUsingScope (declaringType.Region.Begin); - while (scope != null && !string.IsNullOrEmpty (scope.NamespaceName)) { - indentLevel++; - // skip virtual scopes. - while (scope.Parent != null && scope.Parent.Region == scope.Region) - scope = scope.Parent; - scope = scope.Parent; - } - return indentLevel; - } - - public static void AddNewMembers (Project project, ITypeDefinition type, IUnresolvedTypeDefinition part, IEnumerable<IUnresolvedMember> newMembers, string regionName = null, Func<IUnresolvedMember, bool> implementExplicit = null) - { - IUnresolvedMember firstNewMember = newMembers.FirstOrDefault (); - if (firstNewMember == null) - return; - bool isOpen; - var data = TextFileProvider.Instance.GetTextEditorData (part.Region.FileName, out isOpen); - var parsedDocument = TypeSystemService.ParseFile (project, data); - - var insertionPoints = GetInsertionPoints (data, parsedDocument, part); - - - var suitableInsertionPoint = GetSuitableInsertionPoint (insertionPoints, part, firstNewMember); - - var generator = CreateCodeGenerator (data, type.Compilation); - generator.IndentLevel = CalculateBodyIndentLevel (parsedDocument.GetInnermostTypeDefinition (part.Region.Begin)); - StringBuilder sb = new StringBuilder (); - foreach (var newMember in newMembers) { - if (sb.Length > 0) { - sb.AppendLine (); - sb.AppendLine (); - } - sb.Append (generator.CreateMemberImplementation (type, part, newMember, implementExplicit != null ? implementExplicit (newMember) : false).Code); - } - suitableInsertionPoint.Insert (data, string.IsNullOrEmpty (regionName) ? sb.ToString () : generator.WrapInRegions (regionName, sb.ToString ())); - if (!isOpen) { - try { - File.WriteAllText (type.Region.FileName, data.Text); - } catch (Exception e) { - LoggingService.LogError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName), e); - MessageService.ShowError (GettextCatalog.GetString ("Failed to write file '{0}'.", type.Region.FileName)); - } - } - } - - public static CodeGenerator CreateCodeGenerator (this Ide.Gui.Document doc) - { - return CodeGenerator.CreateGenerator (doc); - } - - public static CodeGenerator CreateCodeGenerator (this TextEditorData data, ICompilation compilation) - { - return CodeGenerator.CreateGenerator (data, compilation); - } - - static IUnresolvedTypeDefinition GetMainPart (IType t) - { - return t.GetDefinition ().Parts.First (); - } - - #region Insertion Points - public static List<InsertionPoint> GetInsertionPoints (MonoDevelop.Ide.Gui.Document document, IUnresolvedTypeDefinition type) - { - if (document == null) - throw new ArgumentNullException ("document"); - if (document.ParsedDocument == null) - return new List<InsertionPoint> (); - return GetInsertionPoints (document.Editor, document.ParsedDocument, type); - } - - public static List<InsertionPoint> GetInsertionPoints (TextEditorData data, ParsedDocument parsedDocument, IUnresolvedTypeDefinition type) - { - if (data == null) - throw new ArgumentNullException ("data"); - if (parsedDocument == null) - throw new ArgumentNullException ("parsedDocument"); - if (type == null) - throw new ArgumentNullException ("type"); - - // update type from parsed document, since this is always newer. - //type = parsedDocument.GetInnermostTypeDefinition (type.GetLocation ()) ?? type; - List<InsertionPoint> result = new List<InsertionPoint> (); - int offset = data.LocationToOffset (type.Region.Begin); - if (offset < 0 || type.BodyRegion.IsEmpty) - return result; - while (offset < data.Length && data.GetCharAt (offset) != '{') { - offset++; - } - var realStartLocation = data.OffsetToLocation (offset); - result.Add (GetInsertionPosition (data.Document, realStartLocation.Line, realStartLocation.Column)); - result [0].LineBefore = NewLineInsertion.None; - - foreach (var member in type.Members) { - TextLocation domLocation = member.BodyRegion.End; - if (domLocation.Line <= 0) { - DocumentLine lineSegment = data.GetLine (member.Region.BeginLine); - if (lineSegment == null) - continue; - domLocation = new TextLocation (member.Region.BeginLine, lineSegment.Length + 1); - } - result.Add (GetInsertionPosition (data.Document, domLocation.Line, domLocation.Column)); - } - - foreach (var nestedType in type.NestedTypes) { - TextLocation domLocation = nestedType.BodyRegion.End; - if (domLocation.Line <= 0) { - DocumentLine lineSegment = data.GetLine (nestedType.Region.BeginLine); - if (lineSegment == null) - continue; - domLocation = new TextLocation (nestedType.Region.BeginLine, lineSegment.Length + 1); - } - result.Add (GetInsertionPosition (data.Document, domLocation.Line, domLocation.Column)); - } - - result [result.Count - 1].LineAfter = NewLineInsertion.None; - CheckStartPoint (data.Document, result [0], result.Count == 1); - if (result.Count > 1) { - result.RemoveAt (result.Count - 1); - NewLineInsertion insertLine; - var lineBefore = data.GetLine (type.BodyRegion.EndLine - 1); - if (lineBefore != null && lineBefore.Length == lineBefore.GetIndentation (data.Document).Length) { - insertLine = NewLineInsertion.None; - } else { - insertLine = NewLineInsertion.Eol; - } - // search for line start - int col = type.BodyRegion.EndColumn - 1; - var line = data.GetLine (type.BodyRegion.EndLine); - if (line != null) { - var lineOffset = line.Offset; - col = Math.Min (line.Length, col); - while (lineOffset + col - 2 >= 0 && col > 1 && char.IsWhiteSpace (data.GetCharAt (lineOffset + col - 2))) - col--; - } - result.Add (new InsertionPoint (new DocumentLocation (type.BodyRegion.EndLine, col), insertLine, NewLineInsertion.Eol)); - CheckEndPoint (data.Document, result [result.Count - 1], result.Count == 1); - } - - foreach (var region in parsedDocument.UserRegions.Where (r => type.BodyRegion.IsInside (r.Region.Begin))) { - result.Add (new InsertionPoint (new DocumentLocation (region.Region.BeginLine + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol)); - result.Add (new InsertionPoint (new DocumentLocation (region.Region.EndLine, 1), NewLineInsertion.Eol, NewLineInsertion.Eol)); - result.Add (new InsertionPoint (new DocumentLocation (region.Region.EndLine + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol)); - } - result.Sort ((left, right) => left.Location.CompareTo (right.Location)); -// foreach (var res in result) -// Console.WriteLine (res); - return result; - } - - static void CheckEndPoint (TextDocument doc, InsertionPoint point, bool isStartPoint) - { - DocumentLine line = doc.GetLine (point.Location.Line); - if (line == null) - return; - - if (doc.GetLineIndent (line).Length + 1 < point.Location.Column) - point.LineBefore = NewLineInsertion.BlankLine; - if (point.Location.Column < line.Length + 1) - point.LineAfter = NewLineInsertion.Eol; - } - - static void CheckStartPoint (TextDocument doc, InsertionPoint point, bool isEndPoint) - { - DocumentLine line = doc.GetLine (point.Location.Line); - if (line == null) - return; - if (doc.GetLineIndent (line).Length + 1 == point.Location.Column) { - int lineNr = point.Location.Line; - while (lineNr > 1 && doc.GetLineIndent (lineNr - 1).Length == doc.GetLine (lineNr - 1).Length) { - lineNr--; - } - line = doc.GetLine (lineNr); - point.Location = new DocumentLocation (lineNr, doc.GetLineIndent (line).Length + 1); - } - - if (doc.GetLineIndent (line).Length + 1 < point.Location.Column) - point.LineBefore = NewLineInsertion.Eol; - if (point.Location.Column < line.Length + 1) - point.LineAfter = isEndPoint ? NewLineInsertion.Eol : NewLineInsertion.BlankLine; - } - - static InsertionPoint GetInsertionPosition (TextDocument doc, int line, int column) - { - int bodyEndOffset = doc.LocationToOffset (line, column) + 1; - DocumentLine curLine = doc.GetLine (line); - if (curLine != null) { - if (bodyEndOffset < curLine.Offset + curLine.Length) { - // case1: positition is somewhere inside the start line - return new InsertionPoint (new DocumentLocation (line, column + 1), NewLineInsertion.Eol, NewLineInsertion.BlankLine); - } - } - - // -> if position is at line end check next line - DocumentLine nextLine = doc.GetLine (line + 1); - if (nextLine == null) // check for 1 line case. - return new InsertionPoint (new DocumentLocation (line, column + 1), NewLineInsertion.BlankLine, NewLineInsertion.BlankLine); - - for (int i = nextLine.Offset; i < nextLine.EndOffset; i++) { - char ch = doc.GetCharAt (i); - if (!char.IsWhiteSpace (ch)) { - // case2: next line contains non ws chars. - return new InsertionPoint (new DocumentLocation (line + 1, 1), NewLineInsertion.Eol, NewLineInsertion.BlankLine); - } - } - - DocumentLine nextLine2 = doc.GetLine (line + 2); - if (nextLine2 != null) { - for (int i = nextLine2.Offset; i < nextLine2.EndOffset; i++) { - char ch = doc.GetCharAt (i); - if (!char.IsWhiteSpace (ch)) { - // case3: one blank line - return new InsertionPoint (new DocumentLocation (line + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol); - } - } - } - // case4: more than 1 blank line - return new InsertionPoint (new DocumentLocation (line + 1, 1), NewLineInsertion.Eol, NewLineInsertion.None); - } - - static InsertionPoint GetSuitableInsertionPoint (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls, IUnresolvedMember member) - { - var mainPart = cls; - switch (member.SymbolKind) { - case SymbolKind.Field: - return GetNewFieldPosition (points, mainPart); - case SymbolKind.Method: - case SymbolKind.Constructor: - case SymbolKind.Destructor: - case SymbolKind.Operator: - return GetNewMethodPosition (points, mainPart); - case SymbolKind.Event: - return GetNewEventPosition (points, mainPart); - case SymbolKind.Property: - return GetNewPropertyPosition (points, mainPart); - } - throw new InvalidOperationException ("Invalid member type: " + member.SymbolKind); - } - - static InsertionPoint GetSuitableInsertionPoint (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls, CodeTypeMember mem) - { - var mainPart = cls; - if (mem is System.CodeDom.CodeMemberEvent) - return GetNewEventPosition (points, mainPart); - if (mem is System.CodeDom.CodeMemberProperty) - return GetNewPropertyPosition (points, mainPart); - if (mem is System.CodeDom.CodeMemberField) - return GetNewFieldPosition (points, mainPart); - if (mem is System.CodeDom.CodeMemberMethod) - return GetNewMethodPosition (points, mainPart); - return GetNewFieldPosition (points, mainPart); - } - - static InsertionPoint GetNewFieldPosition (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls) - { - if (!cls.Fields.Any ()) - return points.FirstOrDefault (); - var lastField = cls.Fields.Last (); - return points.FirstOrDefault (p => p.Location.Convert () > lastField.Region.Begin); - } - - static InsertionPoint GetNewMethodPosition (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls) - { - if (!cls.Methods.Any ()) - return GetNewPropertyPosition (points, cls); - var lastMember = cls.Members.Last (); - return points.FirstOrDefault (p => p.Location.Convert () > lastMember.Region.Begin); - } - - static InsertionPoint GetNewPropertyPosition (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls) - { - if (!cls.Properties.Any ()) - return GetNewFieldPosition (points, cls); - var lastProperty = cls.Properties.Last (); - return points.FirstOrDefault (p => p.Location.Convert () > lastProperty.Region.Begin); - } - - static InsertionPoint GetNewEventPosition (IEnumerable<InsertionPoint> points, IUnresolvedTypeDefinition cls) - { - if (!cls.Events.Any ()) - return GetNewMethodPosition (points, cls); - var lastEvent = cls.Events.Last (); - return points.FirstOrDefault (p => p.Location.Convert () > lastEvent.Region.Begin); - } - #endregion - - public static void AddAttribute (ITypeDefinition cls, string name, params object[] parameters) - { - bool isOpen; - string fileName = cls.Region.FileName; - var buffer = TextFileProvider.Instance.GetTextEditorData (fileName, out isOpen); - - var attr = new CodeAttributeDeclaration (name); - foreach (var parameter in parameters) { - attr.Arguments.Add (new CodeAttributeArgument (new CodePrimitiveExpression (parameter))); - } - - var type = new CodeTypeDeclaration ("temp"); - type.CustomAttributes.Add (attr); - Project project; - if (!cls.TryGetSourceProject (out project)) { - LoggingService.LogError ("Error can't get source project for:" + cls.FullName); - } - - var provider = ((DotNetProject)project).LanguageBinding.GetCodeDomProvider (); - var sw = new StringWriter (); - provider.GenerateCodeFromType (type, sw, new CodeGeneratorOptions ()); - string code = sw.ToString (); - int start = code.IndexOf ('['); - int end = code.LastIndexOf (']'); - code = code.Substring (start, end - start + 1) + Environment.NewLine; - - int pos = buffer.LocationToOffset (cls.Region.BeginLine, cls.Region.BeginColumn); - - code = buffer.GetLineIndent (cls.Region.BeginLine) + code; - buffer.Insert (pos, code); - if (!isOpen) { - File.WriteAllText (fileName, buffer.Text); - buffer.Dispose (); - } - - } - - public static IUnresolvedTypeDefinition AddType (DotNetProject project, string folder, string namspace, CodeTypeDeclaration type) - { - - var unit = new CodeCompileUnit (); - var ns = new CodeNamespace (namspace); - ns.Types.Add (type); - unit.Namespaces.Add (ns); - - string fileName = project.LanguageBinding.GetFileName (Path.Combine (folder, type.Name)); - using (var sw = new StreamWriter (fileName)) { - var provider = project.LanguageBinding.GetCodeDomProvider (); - var options = new CodeGeneratorOptions (); - options.IndentString = "\t"; - options.BracingStyle = "C"; - - provider.GenerateCodeFromCompileUnit (unit, sw, options); - } - return TypeSystemService.ParseFile (project, fileName).TopLevelTypeDefinitions.FirstOrDefault (); - } - - } - - public static class HelperMethods - { - public static TextLocation Convert (this DocumentLocation location) - { - return new TextLocation (location.Line, location.Column); - } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerator.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerator.cs index 1b1d6e1310..760496caed 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerator.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/CodeGenerator.cs @@ -29,12 +29,12 @@ using System.Collections.Generic; using System.Linq; using Mono.Addins; using ICSharpCode.NRefactory.TypeSystem; -using Mono.TextEditor; using MonoDevelop.Core.AddIns; using MonoDevelop.Ide.TypeSystem; using ICSharpCode.NRefactory; using MonoDevelop.Projects.Policies; using MonoDevelop.Ide.Extensions; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.TypeSystem { @@ -67,36 +67,39 @@ namespace MonoDevelop.Ide.TypeSystem set; } - public ICompilation Compilation { - get; - set; - } - - public static CodeGenerator CreateGenerator (Ide.Gui.Document doc) + public static CodeGenerator CreateGenerator (TextEditor editor, DocumentContext documentContext) { MimeTypeExtensionNode node; - if (!generators.TryGetValue (doc.Editor.MimeType, out node)) + if (!generators.TryGetValue (editor.MimeType, out node)) return null; var result = (CodeGenerator)node.CreateInstance (); - result.UseSpaceIndent = doc.Editor.TabsToSpaces; - result.EolMarker = doc.Editor.EolMarker; - result.TabSize = doc.Editor.Options.TabSize; - result.Compilation = doc.Compilation; + + result.UseSpaceIndent = editor.Options.TabsToSpaces; + result.EolMarker = editor.EolMarker; + result.TabSize = editor.Options.TabSize; + return result; } - - public static CodeGenerator CreateGenerator (TextEditorData editor, ICompilation compilation) + + public static CodeGenerator CreateGenerator (Ide.Gui.Document doc) + { + return CreateGenerator (doc.Editor, doc); + } + + public static CodeGenerator CreateGenerator (ITextDocument editor, ICompilation compilation) { MimeTypeExtensionNode node; if (!generators.TryGetValue (editor.MimeType, out node)) return null; var result = (CodeGenerator)node.CreateInstance (); - result.UseSpaceIndent = editor.TabsToSpaces; - result.EolMarker = editor.EolMarker; - result.TabSize = editor.Options.TabSize; - result.Compilation = compilation; + + //result.UseSpaceIndent = editor.Options.TabsToSpaces; + result.EolMarker = editor.GetEolMarker (); + //result.TabSize = editor.Options.TabSize; + //result.Compilation = compilation; + return result; } @@ -154,12 +157,6 @@ namespace MonoDevelop.Ide.TypeSystem generators.Remove (node.MimeType); } - protected void SetIndentTo (IUnresolvedTypeDefinition implementingType) - { - if (IndentLevel < 0) - IndentLevel = AutoIndent ? CodeGenerationService.CalculateBodyIndentLevel (implementingType) : 0; - } - static bool CompareMethods (IMethod interfaceMethod, IMethod typeMethod) { if (typeMethod.IsExplicitInterfaceImplementation) @@ -168,15 +165,26 @@ namespace MonoDevelop.Ide.TypeSystem } public abstract string WrapInRegions (string regionName, string text); - public abstract CodeGeneratorMemberResult CreateMemberImplementation (ITypeDefinition implementingType, IUnresolvedTypeDefinition part, IUnresolvedMember member, bool explicitDeclaration); - public abstract CodeGeneratorMemberResult CreateMemberImplementation (ITypeDefinition implementingType, IUnresolvedTypeDefinition part, IMember member, bool explicitDeclaration); - public abstract string CreateFieldEncapsulation (IUnresolvedTypeDefinition implementingType, IField field, string propertyName, Accessibility modifiers, bool readOnly); + public abstract void AddGlobalNamespaceImport (TextEditor editor, DocumentContext context, string nsName); + public abstract void AddLocalNamespaceImport (TextEditor editor, DocumentContext context, string nsName, TextLocation caretLocation); + + public void AddGlobalNamespaceImport (MonoDevelop.Ide.Gui.Document doc, string nsName) + { + if (doc == null) + throw new ArgumentNullException ("doc"); + AddGlobalNamespaceImport (doc.Editor, doc, nsName); + } + + public void AddLocalNamespaceImport (MonoDevelop.Ide.Gui.Document doc, string nsName, TextLocation caretLocation) + { + if (doc == null) + throw new ArgumentNullException ("doc"); + AddLocalNamespaceImport (doc.Editor, doc, nsName, caretLocation); + } - public abstract void AddGlobalNamespaceImport (MonoDevelop.Ide.Gui.Document doc, string nsName); - public abstract void AddLocalNamespaceImport (MonoDevelop.Ide.Gui.Document doc, string nsName, TextLocation caretLocation); - public abstract string GetShortTypeString (MonoDevelop.Ide.Gui.Document doc, IType type); + //public abstract string GetShortTypeString (TextEditor editor, DocumentContext context, IType type); public abstract void CompleteStatement (MonoDevelop.Ide.Gui.Document doc); } @@ -230,4 +238,4 @@ namespace MonoDevelop.Ide.TypeSystem } } } -}
\ No newline at end of file +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Comment.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Comment.cs index 6f615285ba..01eceed24a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Comment.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Comment.cs @@ -26,7 +26,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; -using ICSharpCode.NRefactory.TypeSystem; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.TypeSystem { @@ -55,7 +56,13 @@ namespace MonoDevelop.Ide.TypeSystem set; } - public DomRegion Region { + public bool HasRegion { + get { + return !Region.IsEmpty; + } + } + + public DocumentRegion Region { get; set; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ConditionalRegion.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ConditionalRegion.cs deleted file mode 100644 index f0d1bb4c98..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ConditionalRegion.cs +++ /dev/null @@ -1,88 +0,0 @@ -// ConditionalRegion.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (c) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -using System; -using System.Collections.Generic; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory; - -namespace MonoDevelop.Ide.TypeSystem -{ - [Serializable] - public class ConditionBlock - { - public string Flag { - get; - set; - } - - public DomRegion Region { - get; - set; - } - - public TextLocation Start { - get; - set; - } - - public TextLocation End { - get; - set; - } - - public ConditionBlock (string flag) : this (flag, TextLocation.Empty) - { - } - - public ConditionBlock (string flag, TextLocation start) - { - this.Flag = flag; - this.Start = start; - this.Region = DomRegion.Empty; - } - } - - [Serializable] - public class ConditionalRegion : ConditionBlock - { - public DomRegion ElseBlock { - get; - set; - } - - List<ConditionBlock> conditionBlocks = new List<ConditionBlock> (); - - public List<ConditionBlock> ConditionBlocks { - get { - return conditionBlocks; - } - } - - public ConditionalRegion (string flag) : base (flag) - { - } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Error.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Error.cs new file mode 100644 index 0000000000..ca6a00634b --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Error.cs @@ -0,0 +1,216 @@ +// +// Error.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2015 mkrueger +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using MonoDevelop.Core.Text; +using MonoDevelop.Ide.Editor; + +namespace MonoDevelop.Ide.TypeSystem +{ + /// <summary> + /// Enum that describes the type of an error. + /// </summary> + public enum ErrorType + { + Unknown, + Error, + Warning + } + + /// <summary> + /// Descibes an error during parsing. + /// </summary> + [Serializable] + public class Error + { + readonly ErrorType errorType; + readonly string message; + readonly DocumentRegion region; + + /// <summary> + /// The type of the error. + /// </summary> + public ErrorType ErrorType { get { return errorType; } } + + /// <summary> + /// The error description. + /// </summary> + public string Message { get { return message; } } + + /// <summary> + /// The id of the error. + /// </summary> + public string Id { get; private set; } + + /// <summary> + /// The region of the error. + /// </summary> + public DocumentRegion Region { get { return region; } } + + /// <summary> + /// Gets or sets the tag. + /// </summary> + public object Tag { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + /// <param name='region'> + /// The region of the error. + /// </param> + public Error (ErrorType errorType, string message, DocumentRegion region) + { + this.errorType = errorType; + this.message = message; + this.region = region; + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + /// <param name='location'> + /// The location of the error. + /// </param> + public Error (ErrorType errorType, string message, DocumentLocation location) + { + this.errorType = errorType; + this.message = message; + this.region = new DocumentRegion (location, location); + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + public Error (ErrorType errorType, string message, int line, int column) : this (errorType, message, new DocumentLocation (line, column)) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + public Error (ErrorType errorType, string message) + { + this.errorType = errorType; + this.message = message; + this.region = DocumentRegion.Empty; + } + + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + /// <param name='region'> + /// The region of the error. + /// </param> + public Error (ErrorType errorType, string id, string message, DocumentRegion region) + { + this.errorType = errorType; + this.Id = id; + this.message = message; + this.region = region; + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + /// <param name='location'> + /// The location of the error. + /// </param> + public Error (ErrorType errorType, string id, string message, DocumentLocation location) + { + this.errorType = errorType; + this.Id = id; + this.message = message; + this.region = new DocumentRegion (location, location); + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + public Error (ErrorType errorType, string id, string message, int line, int column) : this (errorType, id, message, new DocumentLocation (line, column)) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.Error"/> class. + /// </summary> + /// <param name='errorType'> + /// The error type. + /// </param> + /// <param name='message'> + /// The description of the error. + /// </param> + public Error (ErrorType errorType, string id, string message) + { + this.errorType = errorType; + this.Id = id; + this.message = message; + this.region = DocumentRegion.Empty; + } + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs index 46f0ffcfea..91f5b99b93 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/FoldingRegion.cs @@ -24,7 +24,7 @@ // THE SOFTWARE. // using System; -using ICSharpCode.NRefactory.TypeSystem; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.TypeSystem { @@ -38,37 +38,37 @@ namespace MonoDevelop.Ide.TypeSystem //NOTE: thsi is only respected if the FoldType is set to "Undefined" public bool IsFoldedByDefault { get; set; } - public DomRegion Region { get; set; } + public DocumentRegion Region { get; set; } public FoldType Type { get; set; } - public FoldingRegion (DomRegion region) : this (null, region) + public FoldingRegion (DocumentRegion region) : this (null, region) { } - public FoldingRegion (string name, DomRegion region) + public FoldingRegion (string name, DocumentRegion region) { this.Name = name ?? defaultName; this.Region = region; } - public FoldingRegion (string name, DomRegion region, bool isFoldedByDefault) : this (name, region) + public FoldingRegion (string name, DocumentRegion region, bool isFoldedByDefault) : this (name, region) { this.IsFoldedByDefault = isFoldedByDefault; } - public FoldingRegion (string name, DomRegion region, FoldType type) : this (name, region) + public FoldingRegion (string name, DocumentRegion region, FoldType type) : this (name, region) { this.Type = type; } - public FoldingRegion (string name, DomRegion region, FoldType type, bool isFoldedByDefault) : this (name, region) + public FoldingRegion (string name, DocumentRegion region, FoldType type, bool isFoldedByDefault) : this (name, region) { this.Type = type; this.IsFoldedByDefault = isFoldedByDefault; } - public FoldingRegion (DomRegion region, FoldType type) : this (null, region, type) + public FoldingRegion (DocumentRegion region, FoldType type) : this (null, region, type) { } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/PreProcessorDefine.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IFoldingParser.cs index 45f3b48a2d..66ea540bed 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/PreProcessorDefine.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IFoldingParser.cs @@ -1,9 +1,10 @@ -// PreProcessorDefine.cs +// +// IFoldingParser.cs // // Author: -// Mike Krüger <mkrueger@novell.com> +// Mike Krüger <mkrueger@xamarin.com> // -// Copyright (c) 2008 Novell, Inc (http://www.novell.com) +// Copyright (c) 2015 Xamarin Inc. (http://xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -22,37 +23,19 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -// -using System; -using ICSharpCode.NRefactory; + namespace MonoDevelop.Ide.TypeSystem { - public class PreProcessorDefine + /// <summary> + /// The folding parser is used for generating a preliminary parsed document that does not + /// contain a full dom - only some basic lexical constructs like comments or pre processor directives. + /// + /// This is useful for opening a document the first time to have some folding regions as start that are folded by default. + /// Otherwise an irritating screen update will occur. + /// </summary> + public interface IFoldingParser { - public string Define { - get; - set; - } - - public TextLocation Location { - get; - set; - } - - public PreProcessorDefine () - { - } - - public PreProcessorDefine (string define, TextLocation location) - { - this.Define = define; - this.Location = location; - } - - public override string ToString () - { - return string.Format ("[PreProcessorDefine: Define={0}, Location={1}]", Define, Location); - } + ParsedDocument Parse (string fileName, string content); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MetadataReferenceCache.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MetadataReferenceCache.cs new file mode 100644 index 0000000000..77d8e818eb --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MetadataReferenceCache.cs @@ -0,0 +1,166 @@ +// +// MetadataReferenceCache.cs +// +// Author: +// David Karlaš <david.karlas@xamarin.com> +// +// Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.IO; +using MonoDevelop.Core; +using System.Threading; + +namespace MonoDevelop.Ide.TypeSystem +{ + static class MetadataReferenceCache + { + static Dictionary<string, MetadataReferenceCacheItem> cache = new Dictionary<string, MetadataReferenceCacheItem> (); + + public static MetadataReference LoadReference (ProjectId projectId, string path) + { + lock (cache) { + MetadataReferenceCacheItem result; + if (!cache.TryGetValue (path, out result)) { + result = new MetadataReferenceCacheItem (path); + cache.Add (path, result); + } + result.InUseBy.Add (projectId); + return result.Reference; + } + } + + //TODO: This should be called when reference is actually removed and not on + //project reload because if this is only project that has this reference... Cache will be + //invalidated and when reload comes back in few miliseconds it will need to reload reference again + public static void RemoveReference (ProjectId projectId, string path) + { + lock (cache) { + MetadataReferenceCacheItem result; + if (cache.TryGetValue (path, out result)) { + result.InUseBy.Remove (projectId); + if (result.InUseBy.Count == 0) { + cache.Remove (path); + } + } + } + } + + public static void RemoveReferences (ProjectId id) + { + lock (cache) { + var toRemove = new List<string> (); + foreach (var val in cache) { + val.Value.InUseBy.Remove (id); + if (val.Value.InUseBy.Count == 0) { + toRemove.Add (val.Key); + } + } + toRemove.ForEach ((k) => cache.Remove (k)); + } + } + + static Timer timer; + + static MetadataReferenceCache () + { + timer = new Timer ((o) => CheckForChanges (), null, 10000, 10000); + } + + //TODO: Call this method when focus returns to MD or even better use FileSystemWatcher + public static void CheckForChanges () + { + lock (cache) { + foreach (var value in cache.Values) { + value.CheckForChange (); + } + } + } + + class MetadataReferenceCacheItem + { + public HashSet<ProjectId> InUseBy { get; private set; } + + public MetadataReference Reference { get; private set; } + + readonly string path; + + DateTime timeStamp; + + public MetadataReferenceCacheItem (string path) + { + this.path = path; + CreateNewReference (); + InUseBy = new HashSet<ProjectId> (); + } + + public void CheckForChange () + { + if (timeStamp != File.GetLastWriteTimeUtc (path)) { + if (Reference != null) { + foreach (var solution in IdeApp.Workspace.GetAllSolutions ()) { + var workspace = TypeSystemService.GetWorkspace (solution); + foreach (var projId in InUseBy) { + while (true) { + var project = workspace.CurrentSolution.GetProject (projId); + if (project == null) + break; + if (workspace.TryApplyChanges (project.RemoveMetadataReference (Reference).Solution)) + break; + } + } + } + } + CreateNewReference (); + if (Reference != null) { + foreach (var solution in IdeApp.Workspace.GetAllSolutions ()) { + var workspace = TypeSystemService.GetWorkspace (solution); + foreach (var projId in InUseBy) { + while (true) { + var project = workspace.CurrentSolution.GetProject (projId); + if (project == null) + break; + if (workspace.TryApplyChanges (project.AddMetadataReference (Reference).Solution)) + break; + } + } + } + } + } + } + + readonly static DateTime NonExistentFile = new DateTime (1601, 1, 1); + + void CreateNewReference () + { + timeStamp = File.GetLastWriteTimeUtc (path); + if (timeStamp == NonExistentFile) { + Reference = null; + } else { + Reference = MetadataReference.CreateFromFile (path, MetadataReferenceProperties.Assembly); + } + } + } + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopProjectContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopProjectContent.cs deleted file mode 100644 index bc77935929..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopProjectContent.cs +++ /dev/null @@ -1,109 +0,0 @@ -// -// MonoDevelopProjectContent.cs -// -// Author: -// Mike Krüger <mkrueger@xamarin.com> -// -// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -using System; -using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; -using System.Collections.Generic; -using MonoDevelop.Projects; - -namespace MonoDevelop.Ide.TypeSystem -{ - [Serializable] - class MonoDevelopProjectContent : CSharpProjectContent - { - [NonSerialized] - Project project; - - public Project Project { - get { - return project; - } - internal set { - project = value; - } - } - - public MonoDevelopProjectContent (Project project) - { - this.project = project; - } - - MonoDevelopProjectContent (MonoDevelopProjectContent pc) : base (pc) - { - this.project = pc.project; - } - - public override ICompilation CreateCompilation() - { - var solutionSnapshot = new DefaultSolutionSnapshot(); - ICompilation compilation = new MonoDevelopCompilation(solutionSnapshot, this, AssemblyReferences); - solutionSnapshot.AddCompilation(this, compilation); - return compilation; - } - - protected override CSharpProjectContent Clone() - { - return new MonoDevelopProjectContent(this); - } - - public override ICompilation CreateCompilation(ISolutionSnapshot solutionSnapshot) - { - return new MonoDevelopCompilation(solutionSnapshot, this, AssemblyReferences); - } - } - - class MonoDevelopCompilation : SimpleCompilation - { - readonly MonoDevelopProjectContent content; - - public MonoDevelopCompilation (ISolutionSnapshot solutionSnapshot, MonoDevelopProjectContent content, IEnumerable<IAssemblyReference> assemblyReferences) : base (solutionSnapshot, content, assemblyReferences) - { - this.content = content; - } - - public override INamespace GetNamespaceForExternAlias (string alias) - { - var netProject = content.Project as DotNetProject; - if (netProject == null) - return null; - foreach (var r in netProject.References) { - if (r.Aliases == alias) { - foreach (var refAsm in ReferencedAssemblies) { - if (refAsm.FullAssemblyName == r.StoredReference) { - return refAsm.RootNamespace; - } - - } - } - } - - - return null; - } - } -} - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopSourceTextContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopSourceTextContainer.cs new file mode 100644 index 0000000000..fa3c9b7192 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopSourceTextContainer.cs @@ -0,0 +1,123 @@ +// +// RoslynTypeSystemService.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Microsoft.CodeAnalysis; +using System.Linq; +using System.IO; +using MonoDevelop.Core; +using System.Collections.Generic; +using System.Threading; +using System.Reflection; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; +using Microsoft.CodeAnalysis.Text; + +namespace MonoDevelop.Ide.TypeSystem +{ + class MonoDevelopSourceText : SourceText + { + readonly ITextSource doc; + + public override System.Text.Encoding Encoding { + get { + return doc.Encoding; + } + } + + public MonoDevelopSourceText (ITextSource doc) + { + if (doc == null) + throw new ArgumentNullException ("doc"); + this.doc = doc; + } + + #region implemented abstract members of SourceText + public override void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count) + { + while (count --> 0) { + destination[destinationIndex++] = doc.GetCharAt (sourceIndex++); + } + } + + public override int Length { + get { + return doc.Length; + } + } + + public override char this [int index] { + get { + return doc.GetCharAt (index); + } + } + #endregion + } + + class MonoDevelopSourceTextContainer : SourceTextContainer + { + readonly ITextDocument document; + public DocumentId Id { + get; + private set; + } + + public MonoDevelopSourceTextContainer (DocumentId documentId, ITextDocument document) : this (document) + { + this.Id = documentId; + } + + public MonoDevelopSourceTextContainer (ITextDocument document) + { + this.document = document; + this.document.TextChanging += HandleTextReplacing; + } + + ~MonoDevelopSourceTextContainer () + { + document.TextChanging -= HandleTextReplacing; + } + + void HandleTextReplacing (object sender, MonoDevelop.Core.Text.TextChangeEventArgs e) + { + var handler = TextChanged; + if (handler != null) { + var oldText = CurrentText; + var newText = oldText.Replace (e.Offset, e.RemovalLength, e.InsertedText.Text); + handler (this, new Microsoft.CodeAnalysis.Text.TextChangeEventArgs (oldText, newText, new TextChangeRange(TextSpan.FromBounds (e.Offset, e.Offset + e.RemovalLength), e.InsertionLength))); + } + + } + #region implemented abstract members of SourceTextContainer + public override SourceText CurrentText { + get { + return new MonoDevelopSourceText (document.CreateSnapshot ()); + } + } + + public override event EventHandler<Microsoft.CodeAnalysis.Text.TextChangeEventArgs> TextChanged; + #endregion + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopTextLoader.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopTextLoader.cs new file mode 100644 index 0000000000..d81a4d16a7 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopTextLoader.cs @@ -0,0 +1,69 @@ +// +// MonoDevelopTextLoader.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using Microsoft.CodeAnalysis; +using System.Threading.Tasks; +using System.Threading; +using Microsoft.CodeAnalysis.Text; +using MonoDevelop.Core.Text; + +namespace MonoDevelop.Ide.TypeSystem +{ + class MonoDevelopTextLoader : TextLoader + { + readonly string fileName; + + public MonoDevelopTextLoader (string fileName) + { + this.fileName = fileName; + } + + #region implemented abstract members of TextLoader + TextAndVersion GetTextAndVersion (Workspace workspace, DocumentId documentId) + { + SourceText text; + if (workspace.IsDocumentOpen (documentId)) { + text = SourceText.From (TextFileProvider.Instance.GetTextEditorData (fileName).Text); + } + else { + text = SourceText.From (MonoDevelop.Core.Text.TextFileUtility.GetText (fileName)); + } + return TextAndVersion.Create (text, VersionStamp.Create ()); + } + + public override Task<TextAndVersion> LoadTextAndVersionAsync (Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + { + return Task.FromResult (GetTextAndVersion (workspace, documentId)); + } + #endregion + + public static TextLoader CreateFromText (string text) + { + if (text == null) + throw new System.ArgumentNullException ("text"); + return TextLoader.From (TextAndVersion.Create (SourceText.From (text), VersionStamp.Create ())); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs new file mode 100644 index 0000000000..e7ce3ef053 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs @@ -0,0 +1,848 @@ +// +// MonoDevelopWorkspace.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using Microsoft.CodeAnalysis; +using System.Linq; +using System.IO; +using MonoDevelop.Core; +using System.Collections.Generic; +using System.Threading; +using Microsoft.CodeAnalysis.Text; +using System.Threading.Tasks; +using MonoDevelop.Ide.Editor; +using Microsoft.CodeAnalysis.Host; +using MonoDevelop.Core.Text; +using System.Collections.Concurrent; +using MonoDevelop.Ide.CodeFormatting; +using MonoDevelop.Core.ProgressMonitoring; +using Gtk; + +namespace MonoDevelop.Ide.TypeSystem +{ + class MonoDevelopWorkspace : Workspace + { + readonly static HostServices services = Microsoft.CodeAnalysis.Host.Mef.MefHostServices.DefaultHost; + CancellationTokenSource src = new CancellationTokenSource (); + MonoDevelop.Projects.Solution currentMonoDevelopSolution; + object addLock = new object(); + bool added; + bool internalChanges; + + public MonoDevelop.Projects.Solution MonoDevelopSolution { + get { + return currentMonoDevelopSolution; + } + } + + public MonoDevelopWorkspace () : base (services, "MonoDevelopWorkspace") + { + if (IdeApp.Workspace != null) { + IdeApp.Workspace.ActiveConfigurationChanged += HandleActiveConfigurationChanged; + } + } + + protected override void Dispose (bool finalize) + { + base.Dispose (finalize); + if (IdeApp.Workspace != null) { + IdeApp.Workspace.ActiveConfigurationChanged -= HandleActiveConfigurationChanged; + } + + } + + internal void InformDocumentTextChange (DocumentId id, SourceText text) + { + base.ApplyDocumentTextChanged (id, text); + } + + void CancelLoad () + { + src.Cancel (); + src = new CancellationTokenSource (); + } + + static StatusBarIcon statusIcon = null; + static int workspacesLoading = 0; + + internal static event EventHandler LoadingFinished; + + static void OnLoadingFinished (EventArgs e) + { + var handler = LoadingFinished; + if (handler != null) + handler (null, e); + } + + internal void HideStatusIcon () + { + Gtk.Application.Invoke (delegate { + workspacesLoading--; + if (workspacesLoading == 0 && statusIcon != null) { + statusIcon.Dispose (); + statusIcon = null; + OnLoadingFinished (EventArgs.Empty); + } + }); + } + + internal void ShowStatusIcon () + { + Gtk.Application.Invoke (delegate { + workspacesLoading++; + if (statusIcon != null) + return; + statusIcon = IdeApp.Workbench?.StatusBar.ShowStatusIcon (ImageService.GetIcon ("md-parser")); + }); + } + + void HandleActiveConfigurationChanged (object sender, EventArgs e) + { + if (currentMonoDevelopSolution == null) + return; + ShowStatusIcon (); + CancelLoad (); + var token = src.Token; + Task.Run (() => { + try { + return CreateSolutionInfo (currentMonoDevelopSolution, token); + } catch (Exception ex) { + LoggingService.LogError ("Error while reloading solution.", ex); + return null; + } + }).ContinueWith ((Task<SolutionInfo> t) => { + try { + if (t.Status == TaskStatus.RanToCompletion) { + if (t.Result == null) + return; + OnSolutionReloaded (t.Result); + } + } finally { + HideStatusIcon (); + } + }); + } + + SolutionInfo CreateSolutionInfo (MonoDevelop.Projects.Solution solution, CancellationToken token) + { + var projects = new ConcurrentBag<ProjectInfo> (); + var mdProjects = solution.GetAllProjects (); + Parallel.ForEach (mdProjects, proj => { + if (token.IsCancellationRequested) + return; + projects.Add (LoadProject (proj, token)); + }); + if (token.IsCancellationRequested) + return null; + var solutionInfo = SolutionInfo.Create (GetSolutionId (solution), VersionStamp.Create (), solution.FileName, projects); + lock (addLock) { + if (!added) { + added = true; + OnSolutionAdded (solutionInfo); + } + } + return solutionInfo; + } + + public void TryLoadSolution (MonoDevelop.Projects.Solution solution/*, IProgressMonitor progressMonitor*/) + { + this.currentMonoDevelopSolution = solution; + CancelLoad (); + CreateSolutionInfo (solution, src.Token); + } + + public void UnloadSolution () + { + OnSolutionRemoved (); + } + + Dictionary<MonoDevelop.Projects.Solution, SolutionId> solutionIdMap = new Dictionary<MonoDevelop.Projects.Solution, SolutionId> (); + + internal SolutionId GetSolutionId (MonoDevelop.Projects.Solution solution) + { + if (solution == null) + throw new ArgumentNullException ("solution"); + lock (solutionIdMap) { + SolutionId result; + if (!solutionIdMap.TryGetValue (solution, out result)) { + result = SolutionId.CreateNewId (solution.Name); + solutionIdMap [solution] = result; + } + return result; + } + } + + ConcurrentDictionary<MonoDevelop.Projects.Project, ProjectId> projectIdMap = new ConcurrentDictionary<MonoDevelop.Projects.Project, ProjectId> (); + ConcurrentDictionary<ProjectId, ProjectData> projectDataMap = new ConcurrentDictionary<ProjectId, ProjectData> (); + + internal MonoDevelop.Projects.Project GetMonoProject (Project project) + { + return GetMonoProject (project.Id); + } + + internal MonoDevelop.Projects.Project GetMonoProject (ProjectId projectId) + { + foreach (var kv in projectIdMap) { + if (kv.Value == projectId) + return kv.Key; + } + return null; + } + + public bool Contains (ProjectId projectId) + { + return projectDataMap.ContainsKey (projectId); + } + + internal ProjectId GetProjectId (MonoDevelop.Projects.Project p) + { + lock (projectIdMap) { + ProjectId result; + if (projectIdMap.TryGetValue (p, out result)) + return result; + return null; + } + } + + internal ProjectId GetOrCreateProjectId (MonoDevelop.Projects.Project p) + { + lock (projectIdMap) { + ProjectId result; + if (!projectIdMap.TryGetValue (p, out result)) { + result = ProjectId.CreateNewId (p.Name); + projectIdMap [p] = result; + } + return result; + } + } + + ProjectData GetProjectData (ProjectId id) + { + lock (projectIdMap) { + ProjectData result; + if (projectDataMap.TryGetValue (id, out result)) { + return result; + } + return null; + } + } + + ProjectData GetOrCreateProjectData (ProjectId id) + { + lock (projectIdMap) { + ProjectData result; + if (!projectDataMap.TryGetValue (id, out result)) { + result = new ProjectData (id); + projectDataMap [id] = result; + } + return result; + } + } + + class ProjectData + { + readonly ProjectId projectId; + readonly Dictionary<string, DocumentId> documentIdMap; + + public ProjectInfo Info { + get; + set; + } + + public ProjectData (ProjectId projectId) + { + this.projectId = projectId; + documentIdMap = new Dictionary<string, DocumentId> (FilePath.PathComparer); + } + + internal DocumentId GetOrCreateDocumentId (string name) + { + lock (documentIdMap) { + DocumentId result; + if (!documentIdMap.TryGetValue (name, out result)) { + result = DocumentId.CreateNewId (projectId, name); + documentIdMap [name] = result; + } + return result; + } + } + + public DocumentId GetDocumentId (string name) + { + DocumentId result; + if (!documentIdMap.TryGetValue (name, out result)) + return null; + return result; + } + + internal void RemoveDocument (string name) + { + documentIdMap.Remove (name); + } + } + + internal DocumentId GetDocumentId (ProjectId projectId, string name) + { + var data = GetProjectData (projectId); + if (data == null) + return null; + return data.GetDocumentId (name); + } + + public override bool CanApplyChange (ApplyChangesKind feature) + { + return true; + } + + ProjectInfo LoadProject (MonoDevelop.Projects.Project p, CancellationToken token) + { + if (!projectIdMap.ContainsKey (p)) { + p.FileAddedToProject += OnFileAdded; + p.FileRemovedFromProject += OnFileRemoved; + p.FileRenamedInProject += OnFileRenamed; + p.Modified += OnProjectModified; + } + + var projectId = GetOrCreateProjectId (p); + var projectData = GetOrCreateProjectData (projectId); + var config = IdeApp.Workspace != null ? p.GetConfiguration (IdeApp.Workspace.ActiveConfiguration) as MonoDevelop.Projects.DotNetProjectConfiguration : null; + MonoDevelop.Projects.DotNetConfigurationParameters cp = null; + if (config != null) + cp = config.CompilationParameters as MonoDevelop.Projects.DotNetConfigurationParameters; + FilePath fileName = IdeApp.Workspace != null ? p.GetOutputFileName (IdeApp.Workspace.ActiveConfiguration) : new FilePath (p.Name + ".dll"); + var info = ProjectInfo.Create ( + projectId, + VersionStamp.Create (), + p.Name, + fileName.FileNameWithoutExtension, + LanguageNames.CSharp, + p.FileName, + fileName, + cp != null ? cp.CreateCompilationOptions () : null, + cp != null ? cp.CreateParseOptions () : null, + CreateDocuments (projectData, p, token), + CreateProjectReferences (p, token), + CreateMetadataReferences (p, projectId, token) + ); + + projectData.Info = info; + return info; + } + + internal static Func<string, TextLoader> CreateTextLoader = fileName => new MonoDevelopTextLoader (fileName); + + static DocumentInfo CreateDocumentInfo (string projectName, ProjectData id, MonoDevelop.Projects.ProjectFile f) + { + var sourceCodeKind = f.FilePath.Extension == ".sketchcs" ? SourceCodeKind.Interactive : SourceCodeKind.Regular; + return DocumentInfo.Create ( + id.GetOrCreateDocumentId (f.Name), + f.FilePath, + new [] { projectName }.Concat (f.ProjectVirtualPath.ParentDirectory.ToString ().Split (Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)), + sourceCodeKind, + CreateTextLoader (f.Name), + f.Name, + false + ); + } + + IEnumerable<DocumentInfo> CreateDocuments (ProjectData id, MonoDevelop.Projects.Project p, CancellationToken token) + { + var duplicates = new HashSet<DocumentId> (); + foreach (var f in p.Files) { + if (token.IsCancellationRequested) + yield break; + if (TypeSystemParserNode.IsCompileBuildAction (f.BuildAction)) { + if (!duplicates.Add (id.GetOrCreateDocumentId (f.Name))) + continue; + yield return CreateDocumentInfo (p.Name, id, f); + continue; + } + var mimeType = DesktopService.GetMimeTypeForUri (f.FilePath); + var node = TypeSystemService.GetTypeSystemParserNode (mimeType, f.BuildAction); + if (node == null || !node.Parser.CanGenerateProjection (mimeType, f.BuildAction, p.SupportedLanguages)) + continue; + if (!duplicates.Add (id.GetOrCreateDocumentId (f.Name))) + continue; + var options = new ParseOptions { + FileName = f.FilePath, + Project = p, + Content = StringTextSource.ReadFrom (f.FilePath), + }; + var projections = node.Parser.GenerateProjections (options); + foreach (var projection in projections.Result) { + yield return DocumentInfo.Create ( + id.GetOrCreateDocumentId (projection.Document.FileName), + projection.Document.FileName, + null, + SourceCodeKind.Regular, + TextLoader.From (TextAndVersion.Create (new MonoDevelopSourceText (projection.Document), VersionStamp.Create (), projection.Document.FileName)), + f.Name, + false + ); + } + } + } + + static IEnumerable<MetadataReference> CreateMetadataReferences (MonoDevelop.Projects.Project p, ProjectId projectId, CancellationToken token) + { + var netProject = p as MonoDevelop.Projects.DotNetProject; + if (netProject == null) + yield break; + var configurationSelector = IdeApp.Workspace?.ActiveConfiguration ?? MonoDevelop.Projects.ConfigurationSelector.Default; + var hashSet = new HashSet<string> (FilePath.PathComparer); + + bool addFacadeAssemblies = false; + + foreach (string file in netProject.GetReferencedAssemblies (configurationSelector, false)) { + if (token.IsCancellationRequested) + yield break; + string fileName; + if (!Path.IsPathRooted (file)) { + fileName = Path.Combine (Path.GetDirectoryName (netProject.FileName), file); + } else { + fileName = Path.GetFullPath (file); + } + if (hashSet.Contains (fileName)) + continue; + hashSet.Add (fileName); + yield return MetadataReferenceCache.LoadReference (projectId, fileName); + addFacadeAssemblies |= MonoDevelop.Core.Assemblies.SystemAssemblyService.ContainsReferenceToSystemRuntime (fileName); + } + + // HACK: Facade assemblies should be added by the project system. Remove that when the project system can do that. + if (addFacadeAssemblies) { + if (netProject != null) { + var runtime = netProject.TargetRuntime ?? MonoDevelop.Core.Runtime.SystemAssemblyService.DefaultRuntime; + var facades = runtime.FindFacadeAssembliesForPCL (netProject.TargetFramework); + foreach (var facade in facades) + yield return MetadataReferenceCache.LoadReference (projectId, facade); + } + } + + foreach (var pr in p.GetReferencedItems (configurationSelector)) { + if (token.IsCancellationRequested) + yield break; + var referencedProject = pr as MonoDevelop.Projects.DotNetProject; + if (referencedProject == null) + continue; + if (TypeSystemService.IsOutputTrackedProject (referencedProject)) { + var fileName = referencedProject.GetOutputFileName (configurationSelector); + yield return MetadataReferenceCache.LoadReference (projectId, fileName); + } + } + } + + IEnumerable<ProjectReference> CreateProjectReferences (MonoDevelop.Projects.Project p, CancellationToken token) + { + foreach (var pr in p.GetReferencedItems (MonoDevelop.Projects.ConfigurationSelector.Default)) { + if (token.IsCancellationRequested) + yield break; + var referencedProject = pr as MonoDevelop.Projects.DotNetProject; + if (referencedProject == null) + continue; + if (TypeSystemService.IsOutputTrackedProject (referencedProject)) + continue; + yield return new ProjectReference (GetOrCreateProjectId (referencedProject)); + } + } + + #region Open documents + public override bool CanOpenDocuments { + get { + return true; + } + } + + public override void OpenDocument (DocumentId documentId, bool activate = true) + { + var document = GetDocument (documentId); + if (document == null) + return; + MonoDevelop.Projects.Project prj = null; + foreach (var curPrj in IdeApp.Workspace.GetAllProjects ()) { + if (GetProjectId (curPrj) == documentId.ProjectId) { + prj = curPrj; + break; + } + } + IdeApp.Workbench.OpenDocument (new MonoDevelop.Ide.Gui.FileOpenInformation ( + DetermineFilePath(document.Id, document.Name, document.FilePath, document.Folders), + prj, + activate + )); + } + + List<MonoDevelopSourceTextContainer> openDocuments = new List<MonoDevelopSourceTextContainer>(); + internal void InformDocumentOpen (DocumentId documentId, ITextDocument editor) + { + var document = this.GetDocument (documentId); + if (document == null) { + return; + } + if (IsDocumentOpen (documentId)) + InformDocumentClose (documentId, document.FilePath); + var monoDevelopSourceTextContainer = new MonoDevelopSourceTextContainer (documentId, editor); + lock (openDocuments) { + openDocuments.Add (monoDevelopSourceTextContainer); + } + OnDocumentOpened (documentId, monoDevelopSourceTextContainer); + } + + Solution newSolution; + public override bool TryApplyChanges (Solution newSolution) + { + this.newSolution = newSolution; + return base.TryApplyChanges (newSolution); + } + + protected override void ApplyProjectChanges (ProjectChanges projectChanges) + { + try { + internalChanges = true; + base.ApplyProjectChanges (projectChanges); + var data = GetMonoProject (projectChanges.NewProject); + if (data != null) { + Application.Invoke (delegate { + data.Save (new NullProgressMonitor ()); + }); + } + } finally { + internalChanges = false; + } + } + + protected override void ApplyAdditionalDocumentAdded (DocumentInfo info, SourceText text) + { + base.ApplyAdditionalDocumentAdded (info, text); + } + + protected override void OnDocumentTextChanged (Document document) + { + base.OnDocumentTextChanged (document); + } + + protected override void OnDocumentClosing (DocumentId documentId) + { + base.OnDocumentClosing (documentId); + lock (openDocuments) { + var openDoc = openDocuments.FirstOrDefault (d => d.Id == documentId); + if (openDoc != null) { +// openDoc.TextChanged -= HandleTextChanged; + openDocuments.Remove (openDoc); + } + } + } + +// internal override bool CanChangeActiveContextDocument { +// get { +// return true; +// } +// } + + public void InformDocumentClose (DocumentId analysisDocument, string filePath) + { + try { + OnDocumentClosed (analysisDocument, new MonoDevelopTextLoader (filePath)); + } catch (Exception e) { + LoggingService.LogError ("Exception while closing document.", e); + } + } + + public override void CloseDocument (DocumentId documentId) + { + } + + protected override void ApplyDocumentTextChanged (DocumentId id, SourceText text) + { + var document = GetDocument (id); + + if (document == null) + return; + bool isOpen; + var data = TextFileProvider.Instance.GetTextEditorData (document.FilePath, out isOpen); + var changes = text.GetTextChanges (document.GetTextAsync ().Result).OrderByDescending (c => c.Span.Start).ToList (); + + int delta = 0; + foreach (var change in changes) { + data.ReplaceText (change.Span.Start, change.Span.Length, change.NewText); + delta += change.Span.Length - change.NewText.Length; + } + + if (!isOpen) { + var formatter = CodeFormatterService.GetFormatter (data.MimeType); + var mp = GetMonoProject (CurrentSolution.GetProject (id.ProjectId)); + string currentText = data.Text; + foreach (var change in changes) { + delta -= change.Span.Length - change.NewText.Length; + var startOffset = change.Span.Start - delta; + var str = formatter.FormatText (mp.Policies, currentText, startOffset, startOffset + change.NewText.Length); + data.ReplaceText (startOffset, change.NewText.Length, str); + } + data.Save (); + FileService.NotifyFileChanged (document.FilePath); + } else { + var formatter = CodeFormatterService.GetFormatter (data.MimeType); + var documentContext = IdeApp.Workbench.Documents.FirstOrDefault (d => FilePath.PathComparer.Compare (d.FileName, document.FilePath) == 0); + if (documentContext != null) { + foreach (var change in changes) { + delta -= change.Span.Length - change.NewText.Length; + var startOffset = change.Span.Start - delta; + formatter.OnTheFlyFormat ((TextEditor)data, documentContext, startOffset, startOffset + change.NewText.Length); + } + } + } + OnDocumentTextChanged (id, new MonoDevelopSourceText(data), PreservationMode.PreserveValue); + } + + protected override void ApplyDocumentAdded (DocumentInfo info, SourceText text) + { + var id = info.Id; + var path = DetermineFilePath (info.Id, info.Name, info.FilePath, info.Folders, true); + + MonoDevelop.Projects.Project mdProject = null; + + if (id.ProjectId != null) { + var project = CurrentSolution.GetProject (id.ProjectId); + mdProject = GetMonoProject (project); + if (mdProject == null) + LoggingService.LogWarning ("Couldn't find project for newly generated file {0} (Project {1}).", path, info.Id.ProjectId); + } + + string formattedText; + var formatter = CodeFormatterService.GetFormatter (DesktopService.GetMimeTypeForUri (path)); + if (formatter != null && mdProject != null) { + formattedText = formatter.FormatText (mdProject.Policies, text.ToString ()); + } else { + formattedText = text.ToString (); + } + + var textSource = new StringTextSource (formattedText, text.Encoding ?? System.Text.Encoding.UTF8); + try { + textSource.WriteTextTo (path); + } catch (Exception e) { + LoggingService.LogError ("Exception while saving file to " + path, e); + } + + if (mdProject != null) { + var file = new MonoDevelop.Projects.ProjectFile (path); + Application.Invoke (delegate { + mdProject.Files.Add (file); + IdeApp.ProjectOperations.Save (mdProject); + }); + } + + OnDocumentAdded (info.WithTextLoader (new MonoDevelopTextLoader (path))); + } + + string DetermineFilePath (DocumentId id, string name, string filePath, IReadOnlyList<string> docFolders, bool createDirectory = false) + { + var path = filePath; + + if (string.IsNullOrEmpty (path)) { + var monoProject = GetMonoProject (id.ProjectId); + + // If the first namespace name matches the name of the project, then we don't want to + // generate a folder for that. The project is implicitly a folder with that name. + IEnumerable<string> folders; + if (docFolders.FirstOrDefault () == monoProject.Name) { + folders = docFolders.Skip (1); + } else { + folders = docFolders; + } + + if (folders.Any ()) { + string baseDirectory = Path.Combine (monoProject.BaseDirectory, Path.Combine (folders.ToArray ())); + try { + if (createDirectory && !Directory.Exists (baseDirectory)) + Directory.CreateDirectory (baseDirectory); + } catch (Exception e) { + LoggingService.LogError ("Error while creating directory for a new file : " + baseDirectory, e); + } + path = Path.Combine (baseDirectory, name); + } + } + return path; + } + #endregion + + public Document GetDocument (DocumentId documentId, CancellationToken cancellationToken = default(CancellationToken)) + { + var project = CurrentSolution.GetProject (documentId.ProjectId); + if (project == null) + return null; + return project.GetDocument (documentId); + } + + public void UpdateFileContent (string fileName, string text) + { + SourceText newText = SourceText.From (text); + foreach (var project in this.projectDataMap.Keys) { + var docId = this.GetDocumentId (project, fileName); + if (docId == null) + continue; + base.OnDocumentTextChanged (docId, newText, PreservationMode.PreserveIdentity); + } + } + + public void AddProject (MonoDevelop.Projects.Project project) + { + var info = LoadProject (project, default(CancellationToken)); + OnProjectAdded (info); + } + + public void RemoveProject (MonoDevelop.Projects.Project project) + { + var id = GetProjectId (project); + if (id != null) { + foreach (var docId in GetOpenDocumentIds (id).ToList ()) { + ClearOpenDocument (docId); + } + OnProjectRemoved (id); + ProjectId val; + projectIdMap.TryRemove (project, out val); + ProjectData val2; + projectDataMap.TryRemove (id, out val2); + MetadataReferenceCache.RemoveReferences (id); + + project.FileAddedToProject -= OnFileAdded; + project.FileRemovedFromProject -= OnFileRemoved; + project.FileRenamedInProject -= OnFileRenamed; + project.Modified -= OnProjectModified; + } + } + + #region Project modification handlers + + void OnFileAdded (object sender, MonoDevelop.Projects.ProjectFileEventArgs args) + { + if (internalChanges) + return; + var project = (MonoDevelop.Projects.Project)sender; + foreach (MonoDevelop.Projects.ProjectFileEventInfo fargs in args) { + if (!TypeSystemParserNode.IsCompileBuildAction (fargs.ProjectFile.BuildAction)) + continue; + var projectId = GetProjectId (project); + var newDocument = CreateDocumentInfo (project.Name, GetProjectData (projectId), fargs.ProjectFile); + OnDocumentAdded (newDocument); + } + } + + void OnFileRemoved (object sender, MonoDevelop.Projects.ProjectFileEventArgs args) + { + if (internalChanges) + return; + var project = (MonoDevelop.Projects.Project)sender; + foreach (MonoDevelop.Projects.ProjectFileEventInfo fargs in args) { + var projectId = GetProjectId (project); + var data = GetProjectData (projectId); + var id = data.GetDocumentId (fargs.ProjectFile.FilePath); + if (id != null) { + ClearDocumentData (id); + OnDocumentRemoved (id); + data.RemoveDocument (fargs.ProjectFile.FilePath); + } + } + } + + void OnFileRenamed (object sender, MonoDevelop.Projects.ProjectFileRenamedEventArgs args) + { + if (internalChanges) + return; + var project = (MonoDevelop.Projects.Project)sender; + foreach (MonoDevelop.Projects.ProjectFileRenamedEventInfo fargs in args) { + if (!TypeSystemParserNode.IsCompileBuildAction (fargs.ProjectFile.BuildAction)) + continue; + + var projectId = GetProjectId (project); + var data = GetProjectData (projectId); + + var id = data.GetDocumentId (fargs.OldName); + if (id != null) { + if (this.IsDocumentOpen (id)) { + this.InformDocumentClose (id, fargs.OldName); + } + OnDocumentRemoved (id); + data.RemoveDocument (fargs.OldName); + } + + var newDocument = CreateDocumentInfo (project.Name, GetProjectData (projectId), fargs.ProjectFile); + OnDocumentAdded (newDocument); + } + } + + void OnProjectModified (object sender, MonoDevelop.Projects.SolutionItemModifiedEventArgs args) + { + if (internalChanges) + return; + if (!args.Any (x => x.Hint == "TargetFramework" || x.Hint == "References")) + return; + var project = (MonoDevelop.Projects.Project)sender; + var projectId = GetProjectId (project); + OnProjectReloaded (LoadProject (project, default(CancellationToken))); + } + + #endregion + + } + +// static class MonoDevelopWorkspaceFeatures +// { +// static FeaturePack pack; +// +// public static FeaturePack Features { +// get { +// if (pack == null) +// Interlocked.CompareExchange (ref pack, ComputePack (), null); +// return pack; +// } +// } +// +// static FeaturePack ComputePack () +// { +// var assemblies = new List<Assembly> (); +// var workspaceCoreAssembly = typeof(Workspace).Assembly; +// assemblies.Add (workspaceCoreAssembly); +// +// LoadAssembly (assemblies, "Microsoft.CodeAnalysis.CSharp.Workspaces"); +// //LoadAssembly (assemblies, "Microsoft.CodeAnalysis.VisualBasic.Workspaces"); +// +// var catalogs = assemblies.Select (a => new System.ComponentModel.Composition.Hosting.AssemblyCatalog (a)); +// +// return new MefExportPack (catalogs); +// } +// +// static void LoadAssembly (List<Assembly> assemblies, string assemblyName) +// { +// try { +// var loadedAssembly = Assembly.Load (assemblyName); +// assemblies.Add (loadedAssembly); +// } catch (Exception e) { +// LoggingService.LogWarning ("Couldn't load assembly:" + assemblyName, e); +// } +// } +// } + +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDocDocumentationProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDocDocumentationProvider.cs index 67e8fd2bf2..9d98c1beb5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDocDocumentationProvider.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDocDocumentationProvider.cs @@ -26,29 +26,18 @@ using System; using System.Collections.Generic; using System.Xml; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; using MonoDevelop.Core; -using ICSharpCode.NRefactory.Documentation; using System.Text; +using Microsoft.CodeAnalysis; namespace MonoDevelop.Ide.TypeSystem { - [Serializable] - public class MonoDocDocumentationProvider : IDocumentationProvider + static class MonoDocDocumentationProvider { - [NonSerialized] - bool hadError; - - public MonoDocDocumentationProvider () - { - } + static bool hadError; + static Dictionary<string, string> commentCache = new Dictionary<string, string> (); - #region IDocumentationProvider implementation - [NonSerialized] - readonly Dictionary<string, DocumentationComment> commentCache = new Dictionary<string, DocumentationComment> (); - - public DocumentationComment GetDocumentation (IEntity entity) + public static string GetDocumentation (ISymbol entity) { if (entity == null) throw new System.ArgumentNullException ("entity"); @@ -57,8 +46,10 @@ namespace MonoDevelop.Ide.TypeSystem // shouldn't try it again. A corrupt .zip file could cause long tooltip delays otherwise. if (hadError) return null; - var idString = entity.GetIdString (); - DocumentationComment result; + var idString = entity.GetDocumentationCommentId (); + if (string.IsNullOrEmpty (idString)) + return null; + string result; if (commentCache.TryGetValue (idString, out result)) return result; XmlDocument doc = null; @@ -66,21 +57,20 @@ namespace MonoDevelop.Ide.TypeSystem var helpTree = MonoDevelop.Projects.HelpService.HelpTree; if (helpTree == null) return null; - if (entity.SymbolKind == SymbolKind.TypeDefinition) { + if (entity.Kind == SymbolKind.NamedType) { doc = helpTree.GetHelpXml (idString); } else { - var parentId = entity.DeclaringTypeDefinition.GetIdString (); - + var containingType = entity.ContainingType; + if (containingType == null) + return null; + var parentId = containingType.GetDocumentationCommentId (); doc = helpTree.GetHelpXml (parentId); if (doc == null) return null; XmlNode node = SelectNode (doc, entity); - if (node != null) - return commentCache [idString] = new DocumentationComment (node.OuterXml, new SimpleTypeResolveContext (entity)); + return commentCache [idString] = node.OuterXml; return null; -// var node = doc.SelectSingleNode ("/Type/Members/Member") -// return new DocumentationComment (doc.OuterXml, new SimpleTypeResolveContext (entity)); } } catch (Exception e) { hadError = true; @@ -90,33 +80,34 @@ namespace MonoDevelop.Ide.TypeSystem commentCache [idString] = null; return null; } - return commentCache [idString] = new DocumentationComment (doc.OuterXml, new SimpleTypeResolveContext (entity)); + return commentCache [idString] = doc.OuterXml; } - public XmlNode SelectNode (XmlDocument doc, IEntity entity) + internal static void ClearCommentCache () { - switch (entity.SymbolKind) { - case SymbolKind.None: - case SymbolKind.TypeDefinition: + commentCache = new Dictionary<string, string> (); + } + + static XmlNode SelectNode (XmlDocument doc, ISymbol entity) + { + switch (entity.Kind) { + case SymbolKind.NamedType: case SymbolKind.Field: case SymbolKind.Property: - case SymbolKind.Indexer: case SymbolKind.Event: return doc.SelectSingleNode ("/Type/Members/Member[@MemberName='" + entity.Name + "']"); case SymbolKind.Method: - case SymbolKind.Operator: - case SymbolKind.Destructor: - return SelectOverload (doc.SelectNodes ("/Type/Members/Member[@MemberName='" + entity.Name + "']"), (IParameterizedMember)entity); - case SymbolKind.Constructor: - return SelectOverload (doc.SelectNodes ("/Type/Members/Member[@MemberName='.ctor']"), (IParameterizedMember)entity); - + var method = (IMethodSymbol)entity; + if (method.MethodKind == MethodKind.Constructor) + return SelectOverload (doc.SelectNodes ("/Type/Members/Member[@MemberName='.ctor']"), method); + return SelectOverload (doc.SelectNodes ("/Type/Members/Member[@MemberName='" + entity.Name + "']"), method); default: throw new ArgumentOutOfRangeException (); } - } - public XmlNode SelectOverload (XmlNodeList nodes, IParameterizedMember entity) + + static XmlNode SelectOverload (XmlNodeList nodes, IMethodSymbol entity) { XmlNode node = null; if (nodes.Count == 1) { @@ -125,12 +116,12 @@ namespace MonoDevelop.Ide.TypeSystem var p = entity.Parameters; foreach (XmlNode curNode in nodes) { var paramList = curNode.SelectNodes ("Parameters/*"); - if (p.Count == 0 && paramList.Count == 0) + if (p.Length == 0 && paramList.Count == 0) return curNode; - if (p.Count != paramList.Count) + if (p.Length != paramList.Count) continue; bool matched = true; - for (int i = 0; i < p.Count; i++) { + for (int i = 0; i < p.Length; i++) { var idString = GetTypeString (p [i].Type); if (idString != paramList [i].Attributes ["Type"].Value) { matched = false; @@ -148,69 +139,20 @@ namespace MonoDevelop.Ide.TypeSystem } return null; } - - static string GetTypeString (IType t) + static string GetTypeString (ITypeSymbol t) { - if (t.Kind == TypeKind.Unknown) - return t.Name; - - if (t.Kind == TypeKind.TypeParameter) - return t.FullName; - - var typeWithElementType = t as TypeWithElementType; - if (typeWithElementType != null) { - var sb = new StringBuilder (); - - if (typeWithElementType is PointerType) { - sb.Append ("*"); - } - sb.Append (GetTypeString (typeWithElementType.ElementType)); - - if (typeWithElementType is ArrayType) { - sb.Append ("["); - sb.Append (new string (',', ((ArrayType)t).Dimensions - 1)); - sb.Append ("]"); - } - return sb.ToString (); - } - - ITypeDefinition typeDef = t.GetDefinition (); - if (typeDef == null) - return ""; - - var result = new StringBuilder (); - - result.Append (typeDef.Namespace + "."); - - if (typeDef.DeclaringTypeDefinition != null) { - string typeString = GetTypeString (typeDef.DeclaringTypeDefinition); - result.Append (typeString); - result.Append ("."); - } - - result.Append (typeDef.Name); - - if (typeDef.TypeParameterCount > 0) { - result.Append ("<"); - for (int i = 0; i < typeDef.TypeParameterCount; i++) { - if (i > 0) - result.Append (","); - if (t.TypeArguments.Count > 0) { - result.Append (GetTypeString (t.TypeArguments [i])); - } else { - result.Append (typeDef.TypeParameters [i].FullName); - } - } - result.Append (">"); + switch (t.TypeKind) { + case TypeKind.Array: + var arr = (IArrayTypeSymbol)t; + return GetTypeString (arr.ElementType) + "[" + new string (',', arr.Rank - 1) + "]"; + case TypeKind.Pointer: + var ptr = (IPointerTypeSymbol)t; + return "*" + GetTypeString (ptr.PointedAtType); + default: + var docComment = t.GetDocumentationCommentId (); + return docComment != null && docComment.Length > 2 ? docComment.Substring (2) : t.Name; } - - return result.ToString (); } - - #endregion - - } } - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NetAmbience.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NetAmbience.cs deleted file mode 100755 index 28b35bc9bb..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NetAmbience.cs +++ /dev/null @@ -1,367 +0,0 @@ -// -// NetAmbience.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (C) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Text; -using ICSharpCode.NRefactory.TypeSystem; -using System.Collections.Generic; - -namespace MonoDevelop.Ide.TypeSystem -{ - public class NetAmbience : Ambience - { - public NetAmbience () : base ("NET") - { - classTypes [TypeKind.Class] = "Class"; - classTypes [TypeKind.Enum] = "Enumeration"; - classTypes [TypeKind.Interface] = "Interface"; - classTypes [TypeKind.Struct] = "Structure"; - classTypes [TypeKind.Delegate] = "Delegate"; - } - - public override string SingleLineComment (string text) - { - return "// " + text; - } - - #region Type system output - public override string GetIntrinsicTypeName (string reflectionName) - { - return reflectionName; - } - - protected override string GetTypeReferenceString (IType reference, OutputSettings settings) - { - return reference.ToString (); - } - - protected override string GetTypeString (IType t, OutputSettings settings) - { - ITypeDefinition type = t.GetDefinition (); - var result = new StringBuilder (); - if (settings.IncludeModifiers) - AppendModifiers (result, settings, type); - if (settings.IncludeKeywords) - result.Append (settings.EmitKeyword (GetString (type.Kind))); - - result.Append (settings.EmitName (type, settings.UseFullName ? type.FullName : type.Name)); - - int parameterCount = type.TypeParameters.Count; - - if (settings.IncludeGenerics && parameterCount > 0) { - result.Append (settings.Markup ("<")); - if (!settings.HideGenericParameterNames) { - for (int i = 0; i < parameterCount; i++) { - if (i > 0) - result.Append (settings.Markup (", ")); - result.Append (type.TypeParameters [i].Name); - } - } - result.Append (settings.Markup (">")); - } - - if (settings.IncludeBaseTypes && type.DirectBaseTypes.Any ()) { - result.Append (settings.Markup (" : ")); - bool first = true; - foreach (var baseType in type.DirectBaseTypes) { - if (baseType.Equals (type.Compilation.FindType (KnownTypeCode.Object))) - continue; - if (!first) - result.Append (settings.Markup (", ")); - first = false; - result.Append (GetTypeReferenceString (baseType, settings)); - } - - } - return result.ToString (); - } - - protected override string GetMethodString (IMethod method, OutputSettings settings) - { - var result = new StringBuilder (); - - if (settings.IncludeModifiers) - AppendModifiers (result, settings, method); - - result.Append (settings.EmitKeyword ("Method")); - result.Append (settings.EmitName (method, settings.UseFullName ? method.FullName : method.Name)); - - if (settings.IncludeParameters) { - result.Append (settings.Markup ("(")); - bool first = true; - foreach (var parameter in method.Parameters) { - if (!first) - result.Append (settings.Markup (", ")); - result.Append (GetParameterString (method, parameter, settings)); - first = false; - } - result.Append (settings.Markup (")")); - } - - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (method.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetConstructorString (IMethod method, OutputSettings settings) - { - var result = new StringBuilder (); - - if (settings.IncludeModifiers) - AppendModifiers (result, settings, method); - - result.Append (settings.EmitKeyword ("Constructor")); - result.Append (settings.EmitName (method, method.DeclaringType.Name)); - - if (settings.IncludeParameters) { - result.Append (settings.Markup ("(")); - bool first = true; - foreach (var parameter in method.Parameters) { - if (!first) - result.Append (settings.Markup (", ")); - result.Append (GetParameterString (method, parameter, settings)); - first = false; - } - result.Append (settings.Markup (")")); - } - return result.ToString (); - } - - protected override string GetDestructorString (IMethod method, OutputSettings settings) - { - var result = new StringBuilder (); - result.Append (settings.EmitKeyword ("Destructor")); - result.Append (settings.EmitName (method, method.DeclaringType.Name)); - return result.ToString (); - } - - protected override string GetOperatorString (IMethod method, OutputSettings settings) - { - var result = new StringBuilder (); - - if (settings.IncludeModifiers) - AppendModifiers (result, settings, method); - - result.Append (settings.EmitKeyword ("Operator")); - result.Append (settings.EmitName (method, settings.UseFullName ? method.FullName : method.Name)); - - if (settings.IncludeParameters) { - result.Append (settings.Markup ("(")); - bool first = true; - foreach (var parameter in method.Parameters) { - if (!first) - result.Append (settings.Markup (", ")); - result.Append (GetParameterString (method, parameter, settings)); - first = false; - } - result.Append (settings.Markup (")")); - } - - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (method.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetFieldString (IField field, OutputSettings settings) - { - var result = new StringBuilder (); - - if (settings.IncludeModifiers) - AppendModifiers (result, settings, field); - - result.Append (settings.EmitKeyword ("Field")); - result.Append (settings.EmitName (field, field.Name)); - - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (field.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetEventString (IEvent evt, OutputSettings settings) - { - var result = new StringBuilder (); - - if (settings.IncludeModifiers) - AppendModifiers (result, settings, evt); - - result.Append (settings.EmitKeyword ("Event")); - result.Append (settings.EmitName (evt, evt.Name)); - - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (evt.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetPropertyString (IProperty property, OutputSettings settings) - { - var result = new StringBuilder (); - if (settings.IncludeModifiers) - AppendModifiers (result, settings, property); - result.Append (settings.EmitKeyword ("Property")); - result.Append (settings.EmitName (property, property.Name)); - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (property.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetIndexerString (IProperty property, OutputSettings settings) - { - var result = new StringBuilder (); - if (settings.IncludeModifiers) - AppendModifiers (result, settings, property); - result.Append (settings.EmitKeyword ("Indexer")); - result.Append (settings.EmitName (property, property.Name)); - - if (settings.IncludeParameters && property.Parameters.Count > 0) { - result.Append (settings.Markup ("(")); - bool first = true; - foreach (var parameter in property.Parameters) { - if (!first) - result.Append (settings.Markup (", ")); - result.Append (GetParameterString (property, parameter, settings)); - first = false; - } - result.Append (settings.Markup (")")); - } - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (property.ReturnType, settings)); - } - return result.ToString (); - } - - protected override string GetParameterString (IParameterizedMember member, IParameter parameter, OutputSettings settings) - { - var result = new StringBuilder (); - if (settings.IncludeParameterName) { - result.Append (Format (parameter.Name)); - if (settings.IncludeReturnType) { - result.Append (settings.Markup (" : ")); - result.Append (GetTypeReferenceString (parameter.Type, settings)); - } - } else { - result.Append (GetTypeReferenceString (parameter.Type, settings)); - } - if (parameter.IsRef || parameter.IsOut) - result.Append (settings.Markup ("&")); - return result.ToString (); - } - #endregion - - void AppendModifiers (StringBuilder result, OutputSettings settings, IEntity entity) - { - if (entity.IsStatic) - result.Append (settings.EmitModifiers ("Static")); - if (entity.IsSealed) - result.Append (settings.EmitModifiers ("Sealed")); - if (entity.IsAbstract) - result.Append (settings.EmitModifiers ("Abstract")); - if (entity.IsShadowing) - result.Append (settings.EmitModifiers ("Shadows")); - if (entity.IsSynthetic) - result.Append (settings.EmitModifiers ("Synthetic")); - - switch (entity.Accessibility) { - case Accessibility.Internal: - result.Append (settings.EmitModifiers ("Internal")); - break; - case Accessibility.ProtectedAndInternal: - result.Append (settings.EmitModifiers ("Protected And Internal")); - break; - case Accessibility.ProtectedOrInternal: - result.Append (settings.EmitModifiers ("Protected Or Internal")); - break; - case Accessibility.Protected: - result.Append (settings.EmitModifiers ("Protected")); - break; - case Accessibility.Private: - result.Append (settings.EmitModifiers ("Private")); - break; - case Accessibility.Public: - result.Append (settings.EmitModifiers ("Public")); - break; - } - } - - public override string GetString (string nameSpace, OutputSettings settings) - { - var result = new StringBuilder (); - result.Append (settings.EmitKeyword ("Namespace")); - result.Append (Format (nameSpace)); - return result.ToString (); - } - - Dictionary<TypeKind, string> classTypes = new Dictionary<TypeKind, string> (); - - string GetString (TypeKind classType) - { - string res; - if (classTypes.TryGetValue (classType, out res)) - return res; - return string.Empty; - } -// public string Visit (IAttribute attribute, OutputSettings settings) -// { -// StringBuilder result = new StringBuilder (); -// result.Append (settings.Markup ("[")); -// result.Append (GetString (attribute.AttributeType, settings)); -// result.Append (settings.Markup ("(")); -// bool first = true; -// if (attribute.PositionalArguments != null) { -// foreach (object o in attribute.PositionalArguments) { -// if (!first) -// result.Append (settings.Markup (", ")); -// first = false; -// if (o is string) { -// result.Append (settings.Markup ("\"")); -// result.Append (o); -// result.Append (settings.Markup ("\"")); -// } else if (o is char) { -// result.Append (settings.Markup ("\"")); -// result.Append (o); -// result.Append (settings.Markup ("\"")); -// } else -// result.Append (o); -// } -// } -// result.Append (settings.Markup (")]")); -// return result.ToString (); -// } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputFlags.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputFlags.cs deleted file mode 100644 index 6ef2920d6d..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputFlags.cs +++ /dev/null @@ -1,65 +0,0 @@ -// -// OutputFlags.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (C) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; - -namespace MonoDevelop.Ide.TypeSystem -{ - [Flags] - public enum OutputFlags { - None = 0, - - // Flags - UseFullName = 0x0001, - IncludeReturnType = 0x0002, - IncludeParameters = 0x0004, - IncludeParameterName = 0x0008, - IncludeMarkup = 0x0010, - IncludeKeywords = 0x0020, - IncludeModifiers = 0x0040, - IncludeBaseTypes = 0x0080, - IncludeGenerics = 0x0100, - UseIntrinsicTypeNames = 0x0200, - HighlightName = 0x0400, - HideExtensionsParameter = 0x0800, - HideGenericParameterNames= 0x1000, - HideArrayBrackets = 0x2000, - UseNETTypeNames = 0x4000, // print 'System.Int32' intead of 'int' - UseFullInnerTypeName = 0x8000, - ReformatDelegates = 0x10000, - GeneralizeGenerics = 0x20000, - StaticUsage = 0x40000, // to distinguish static usage for extension methods. - IncludeConstraints = 0x80000, - ReturnTypesLast = 0x100000, - CompletionListFomat = 0x200000, // print "Name : type" instead of "type : Name" - IncludeAccessor = 0x400000, // print {get;set;} after property name. - - ClassBrowserEntries = IncludeReturnType | IncludeParameters | IncludeGenerics, - AssemblyBrowserDescription = IncludeGenerics | IncludeBaseTypes | IncludeReturnType | IncludeParameters | IncludeParameterName | IncludeMarkup | IncludeKeywords | IncludeModifiers - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputSettings.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputSettings.cs deleted file mode 100644 index 0071f2b4ac..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/OutputSettings.cs +++ /dev/null @@ -1,268 +0,0 @@ -// OutputSettings.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (c) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using MonoDevelop.Projects.Policies; -using System.Collections.Generic; -using ICSharpCode.NRefactory.TypeSystem; - -namespace MonoDevelop.Ide.TypeSystem -{ - public class OutputSettings - { - public OutputFlags OutputFlags { - get; - set; - } - - public PolicyContainer PolicyParent { - get; - set; - } - - public OutputSettings (OutputFlags outputFlags) - { - this.OutputFlags = outputFlags; - } - - public string Markup (string text) - { - if (MarkupCallback != null) - return MarkupCallback (text); - return IncludeMarkup ? PangoFormat (text) : text; - } - - public string EmitName (object domVisitable, string text) - { - if (EmitNameCallback != null) - return EmitNameCallback (domVisitable, text); - return text; - } - - public string EmitModifiers (string text) - { - if (!IncludeModifiers) - return string.Empty; - if (EmitModifiersCallback != null) - return EmitModifiersCallback (text) + " "; - if (IncludeMarkup) - return "<b>" + PangoFormat (text) + "</b> "; - return text + " "; - } - - public string EmitKeyword (string text) - { - if (EmitKeywordCallback != null) - return EmitKeywordCallback (text) + " "; - if (!IncludeKeywords) - return ""; - if (IncludeMarkup) - return "<b>" + PangoFormat (text) + "</b> "; - return text + " "; - } - - public string Highlight (string text) - { - if (HighlightCallback != null) - return HighlightCallback (text); - if (IncludeMarkup) - return "<b>" + PangoFormat (text) + "</b>"; - return text; - } - - public string PostProcess (object domVisitable, string outString) - { - if (PostProcessCallback != null) - return PostProcessCallback (domVisitable, outString); - return outString; - } - - static string PangoFormat (string input) - { - StringBuilder result = new StringBuilder (); - foreach (char ch in input) { - switch (ch) { - case '<': - result.Append ("<"); - break; - case '>': - result.Append (">"); - break; - case '&': - result.Append ("&"); - break; - default: - result.Append (ch); - break; - } - } - return result.ToString (); - } - - public bool IncludeMarkup { - get { - return (OutputFlags & OutputFlags.IncludeMarkup) == OutputFlags.IncludeMarkup; - } - } - - public bool IncludeKeywords { - get { - return (OutputFlags & OutputFlags.IncludeKeywords) == OutputFlags.IncludeKeywords; - } - } - - public bool IncludeModifiers { - get { - return (OutputFlags & OutputFlags.IncludeModifiers) == OutputFlags.IncludeModifiers; - } - } - - public bool UseFullName { - get { - return (OutputFlags & OutputFlags.UseFullName) == OutputFlags.UseFullName; - } - } - - public bool UseFullInnerTypeName { - get { - return (OutputFlags & OutputFlags.UseFullInnerTypeName) == OutputFlags.UseFullInnerTypeName; - } - } - - public bool IncludeParameters { - get { - return (OutputFlags & OutputFlags.IncludeParameters) == OutputFlags.IncludeParameters; - } - } - - public bool IncludeReturnType { - get { - return (OutputFlags & OutputFlags.IncludeReturnType) == OutputFlags.IncludeReturnType; - } - } - - public bool IncludeParameterName { - get { - return (OutputFlags & OutputFlags.IncludeParameterName) == OutputFlags.IncludeParameterName; - } - } - - public bool IncludeBaseTypes { - get { - return (OutputFlags & OutputFlags.IncludeBaseTypes) == OutputFlags.IncludeBaseTypes; - } - } - - public bool IncludeGenerics { - get { - return (OutputFlags & OutputFlags.IncludeGenerics) == OutputFlags.IncludeGenerics; - } - } - - public bool HideArrayBrackets { - get { - return (OutputFlags & OutputFlags.HideArrayBrackets) == OutputFlags.HideArrayBrackets; - } - } - - public bool HighlightName { - get { - return (OutputFlags & OutputFlags.HighlightName) == OutputFlags.HighlightName; - } - } - - public bool HideExtensionsParameter { - get { - return (OutputFlags & OutputFlags.HideExtensionsParameter) == OutputFlags.HideExtensionsParameter; - } - } - - public bool HideGenericParameterNames { - get { - return (OutputFlags & OutputFlags.HideGenericParameterNames) != 0; - } - } - - public bool GeneralizeGenerics { - get { - return (OutputFlags & OutputFlags.GeneralizeGenerics) != 0; - } - } - - public bool UseNETTypeNames { - get { - return (OutputFlags & OutputFlags.UseNETTypeNames) != 0; - } - } - - public bool ReformatDelegates { - get { - return (OutputFlags & OutputFlags.ReformatDelegates) != 0; - } - } - - public bool StaticUsage { - get { - return (OutputFlags & OutputFlags.StaticUsage) != 0; - } - } - - public bool IncludeConstraints { - get { - return (OutputFlags & OutputFlags.IncludeConstraints) != 0; - } - } - - public bool CompletionListFomat { - get { - return (OutputFlags & OutputFlags.CompletionListFomat) != 0; - } - } - - public bool ReturnTypesLast { - get { - return (OutputFlags & OutputFlags.ReturnTypesLast) != 0; - } - } - - public bool IncludeAccessor { - get { - return (OutputFlags & OutputFlags.IncludeAccessor) == OutputFlags.IncludeAccessor; - } - } - - public MarkupText EmitModifiersCallback; - public MarkupText EmitKeywordCallback; - public MarkupText MarkupCallback; - public MarkupText HighlightCallback; - public Func<object, string, string> EmitNameCallback; - - public delegate string MarkupText (string text); - - public Func<object, string, string> PostProcessCallback; - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedDocument.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedDocument.cs index 0fdb4a13cd..dddd7641f3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedDocument.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedDocument.cs @@ -29,10 +29,8 @@ using System.Collections.Generic; using System.Text; using System.Linq; using System.Threading; -using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.Semantics; -using Mono.TextEditor; - +using MonoDevelop.Ide.Editor; +using System.Threading.Tasks; namespace MonoDevelop.Ide.TypeSystem { @@ -43,7 +41,6 @@ namespace MonoDevelop.Ide.TypeSystem NonSerializable = 1 } - public abstract class ParsedDocument { DateTime lastWriteTimeUtc = DateTime.UtcNow; @@ -51,63 +48,8 @@ namespace MonoDevelop.Ide.TypeSystem get { return lastWriteTimeUtc; } set { lastWriteTimeUtc = value; } } - - [NonSerialized] - List<Comment> comments = new List<Comment> (); - - public virtual IUnresolvedFile ParsedFile { - get { return null; } - set { throw new InvalidOperationException (); } - } - public IList<Comment> Comments { - get { - return comments; - } - } - /// <summary> - /// Gets or sets a value indicating whether this instance is invalid and needs to be reparsed. - /// </summary> - public bool IsInvalid { - get; - set; - } - - List<Tag> tagComments = new List<Tag> (); - public IList<Tag> TagComments { - get { - return tagComments; - } - } - - IEnumerable<FoldingRegion> foldings = null; - public virtual IEnumerable<FoldingRegion> Foldings { - get { - return foldings ?? Enumerable.Empty<FoldingRegion> (); - } - } - - public IEnumerable<FoldingRegion> UserRegions { - get { - return Foldings.Where (f => f.Type == FoldType.UserRegion); - } - } - - List<PreProcessorDefine> defines = new List<PreProcessorDefine> (); - public IList<PreProcessorDefine> Defines { - get { - return defines; - } - } - - List<ConditionalRegion> conditionalRegions = new List<ConditionalRegion> (); - public IList<ConditionalRegion> ConditionalRegions { - get { - return conditionalRegions; - } - } - [NonSerialized] ParsedDocumentFlags flags; public ParsedDocumentFlags Flags { @@ -128,16 +70,30 @@ namespace MonoDevelop.Ide.TypeSystem fileName = value; } } - - public virtual IList<Error> Errors { - get { - return new Error[0]; - } + + /// <summary> + /// Gets or sets a value indicating whether this instance is invalid and needs to be reparsed. + /// </summary> + public bool IsInvalid { + get; + set; } - + + public abstract Task<IReadOnlyList<Comment>> GetCommentsAsync (CancellationToken cancellationToken = default(CancellationToken)); + public abstract Task<IReadOnlyList<Tag>> GetTagCommentsAsync (CancellationToken cancellationToken = default(CancellationToken)); + public abstract Task<IReadOnlyList<FoldingRegion>> GetFoldingsAsync (CancellationToken cancellationToken = default(CancellationToken)); + + public async Task<IEnumerable<FoldingRegion>> GetUserRegionsAsync (CancellationToken cancellationToken = default(CancellationToken)) + { + var foldings = await GetFoldingsAsync (cancellationToken).ConfigureAwait (false); + return foldings.Where (f => f.Type == FoldType.UserRegion); + } + + public abstract Task<IReadOnlyList<Error>> GetErrorsAsync (CancellationToken cancellationToken = default(CancellationToken)); + public bool HasErrors { get { - return Errors.Any (e => e.ErrorType == ErrorType.Error); + return GetErrorsAsync ().Result.Any (e => e.ErrorType == ErrorType.Error); } } @@ -162,236 +118,84 @@ namespace MonoDevelop.Ide.TypeSystem { this.fileName = fileName; } - - - public void Add (Comment comment) - { - comments.Add (comment); - } - - public void Add (Tag tagComment) - { - tagComments.Add (tagComment); - } - - public void Add (PreProcessorDefine define) - { - defines.Add (define); - } - - public void Add (ConditionalRegion region) + } + + public class DefaultParsedDocument : ParsedDocument + { + public DefaultParsedDocument (string fileName) : base (fileName) { - conditionalRegions.Add (region); + Flags |= ParsedDocumentFlags.NonSerializable; } - List<FoldingRegion> EnsureFoldingList () - { - if (this.foldings == null || !(foldings is List<FoldingRegion>)) - this.foldings = new List<FoldingRegion> (); - return (List<FoldingRegion>)foldings; - } - - public void Add (FoldingRegion region) + List<Comment> comments = new List<Comment> (); + + public void Add (Comment comment) { - EnsureFoldingList ().Add (region); + comments.Add (comment); } - - public void Add (IEnumerable<Comment> comments) + + public void AddRange (IEnumerable<Comment> comments) { this.comments.AddRange (comments); } - - public void Add (IEnumerable<Tag> tagComments) - { - this.tagComments.AddRange (tagComments); - } - - public void Add (IEnumerable<PreProcessorDefine> defines) - { - this.defines.AddRange (defines); - } - - public void Add (IEnumerable<FoldingRegion> folds) - { - if (foldings == null) { - this.foldings = folds; - return; - } - if (foldings != null && !(foldings is List<FoldingRegion>)) - EnsureFoldingList ().AddRange (foldings); - EnsureFoldingList ().AddRange (folds); - } - - public void Add (IEnumerable<ConditionalRegion> conditionalRegions) - { - this.conditionalRegions.AddRange (conditionalRegions); - } - - #region IUnresolvedFile delegation - public virtual IUnresolvedTypeDefinition GetTopLevelTypeDefinition (TextLocation location) + + public override Task<IReadOnlyList<Comment>> GetCommentsAsync (CancellationToken cancellationToken = default(CancellationToken)) { - return null; + return Task.FromResult<IReadOnlyList<Comment>> (comments); } - - public virtual IUnresolvedTypeDefinition GetInnermostTypeDefinition (TextLocation location) + + List<Tag> tagComments = new List<Tag> (); + + public void Add (Tag tagComment) { - return null; + tagComments.Add (tagComment); } - public virtual IUnresolvedMember GetMember (TextLocation location) + public void AddRange (IEnumerable<Tag> tagComments) { - return null; + this.tagComments.AddRange (tagComments); } - public virtual IList<IUnresolvedTypeDefinition> TopLevelTypeDefinitions { - get { - return new List<IUnresolvedTypeDefinition> (); - } + public override Task<IReadOnlyList<Tag>> GetTagCommentsAsync (CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult<IReadOnlyList<Tag>> (tagComments); } - #endregion + List<FoldingRegion> foldingRegions = new List<FoldingRegion> (); - public Func<MonoDevelop.Ide.Gui.Document, CancellationToken, IRefactoringContext> CreateRefactoringContext; - public Func<TextEditorData, object, CancellationToken, IRefactoringContext> CreateRefactoringContextWithEditor; - } - - public class DefaultParsedDocument : ParsedDocument, IUnresolvedFile - { - - public override IUnresolvedFile ParsedFile { - get { return this; } - } - - List<Error> errors = new List<Error> (); - - public override IList<Error> Errors { - get { - return errors; - } - } - - public DefaultParsedDocument (string fileName) : base (fileName) + public void Add (FoldingRegion foldingRegion) { - Flags |= ParsedDocumentFlags.NonSerializable; - } - - #region IUnresolvedFile implementation - public override IUnresolvedTypeDefinition GetTopLevelTypeDefinition(TextLocation location) - { - return TopLevelTypeDefinitions.FirstOrDefault (t => t.Region.IsInside (location)); + foldingRegions.Add (foldingRegion); } - - public override IUnresolvedTypeDefinition GetInnermostTypeDefinition(TextLocation location) + + public void AddRange (IEnumerable<FoldingRegion> foldingRegions) { - IUnresolvedTypeDefinition parent = null; - var type = GetTopLevelTypeDefinition(location); - while (type != null) { - parent = type; - type = parent.NestedTypes.FirstOrDefault (t => t.Region.IsInside (location)); - } - return parent; + this.foldingRegions.AddRange (foldingRegions); } - - public override IUnresolvedMember GetMember(TextLocation location) + + public override Task<IReadOnlyList<FoldingRegion>> GetFoldingsAsync (CancellationToken cancellationToken = default(CancellationToken)) { - var type = GetInnermostTypeDefinition(location); - if (type == null) - return null; - return type.Members.FirstOrDefault (e => e.Region.IsInside(location)); - } - - List<IUnresolvedTypeDefinition> types = new List<IUnresolvedTypeDefinition> (); - public override IList<IUnresolvedTypeDefinition> TopLevelTypeDefinitions { - get { - return types; - } - } - - List<IUnresolvedAttribute> attributes = new List<IUnresolvedAttribute> (); - public IList<IUnresolvedAttribute> AssemblyAttributes { - get { - return attributes; - } + return Task.FromResult<IReadOnlyList<FoldingRegion>> (foldingRegions); } - public IList<IUnresolvedAttribute> ModuleAttributes { - get { - return new List<IUnresolvedAttribute> (); - } - } - #endregion - + List<Error> errors = new List<Error> (); + public void Add (Error error) { errors.Add (error); } - - public void Add (IEnumerable<Error> errors) - { - this.errors.AddRange (errors); - } - - #region IUnresolvedFile implementation - DateTime? IUnresolvedFile.LastWriteTime { - get { - return LastWriteTimeUtc; - } - set { - LastWriteTimeUtc = value.HasValue ? value.Value : DateTime.UtcNow; - } - } - #endregion - } - - [Serializable] - public class ParsedDocumentDecorator : ParsedDocument - { - IUnresolvedFile parsedFile; - - public override IUnresolvedFile ParsedFile { - get { return parsedFile; } - set { parsedFile = value; FileName = parsedFile.FileName; } - } - - public override IList<Error> Errors { - get { - return parsedFile.Errors; - } - } - - public ParsedDocumentDecorator (IUnresolvedFile parsedFile) : base (parsedFile.FileName) - { - this.parsedFile = parsedFile; - } - - public ParsedDocumentDecorator () : base ("") - { - } - - #region IUnresolvedFile implementation - public override IUnresolvedTypeDefinition GetTopLevelTypeDefinition (TextLocation location) - { - return parsedFile.GetTopLevelTypeDefinition (location); - } - public override IUnresolvedTypeDefinition GetInnermostTypeDefinition (TextLocation location) + public void AddRange (IEnumerable<Error> errors) { - return parsedFile.GetInnermostTypeDefinition (location); + this.errors.AddRange (errors); } - public override IUnresolvedMember GetMember (TextLocation location) + public override Task<IReadOnlyList<Error>> GetErrorsAsync (CancellationToken cancellationToken = default(CancellationToken)) { - return parsedFile.GetMember (location); - } - - public override IList<IUnresolvedTypeDefinition> TopLevelTypeDefinitions { - get { - return parsedFile.TopLevelTypeDefinitions; - } + return Task.FromResult<IReadOnlyList<Error>> (errors); } - #endregion } - + public static class FoldingUtilities { static bool IncompleteOrSingleLine (DomRegion region) @@ -399,7 +203,7 @@ namespace MonoDevelop.Ide.TypeSystem return region.BeginLine <= 0 || region.EndLine <= region.BeginLine; } - public static IEnumerable<FoldingRegion> ToFolds (this IList<Comment> comments) + public static IEnumerable<FoldingRegion> ToFolds (this IReadOnlyList<Comment> comments) { for (int i = 0; i < comments.Count; i++) { Comment comment = comments [i]; @@ -449,7 +253,7 @@ namespace MonoDevelop.Ide.TypeSystem curLine = curComment.Region.BeginLine; } - if (j - i > 1) { + if (j - i > 1 || (comment.IsDocumentation && comment.Region.BeginLine < comment.Region.EndLine)) { string txt; if (comment.IsDocumentation) { txt = "/// ..."; @@ -483,7 +287,7 @@ namespace MonoDevelop.Ide.TypeSystem } yield return new FoldingRegion (txt, - new DomRegion (comment.Region.Begin, end), + new DocumentRegion (comment.Region.Begin, end), FoldType.Comment); i = j - 1; } @@ -550,6 +354,23 @@ namespace MonoDevelop.Ide.TypeSystem } return false; } + + static bool IsInsideMember (this DocumentRegion region, IUnresolvedTypeDefinition cl) + { + if (region.IsEmpty || cl == null || !cl.BodyRegion.IsInside (region.Begin.Line, region.Begin.Column)) + return false; + foreach (var member in cl.Members) { + if (member.BodyRegion.IsEmpty) + continue; + if (member.BodyRegion.IsInside (region.Begin.Line, region.Begin.Column) && member.BodyRegion.IsInside (region.End.Line, region.End.Column)) + return true; + } + foreach (var inner in cl.NestedTypes) { + if (region.IsInsideMember (inner)) + return true; + } + return false; + } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedFileEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedFileEventArgs.cs deleted file mode 100644 index 50300be800..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ParsedFileEventArgs.cs +++ /dev/null @@ -1,44 +0,0 @@ -// -// ParsedFileEventArgs.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (c) 2011 Mike Krüger <mkrueger@novell.com> -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using ICSharpCode.NRefactory.TypeSystem; - -namespace MonoDevelop.Ide.TypeSystem -{ - public class ParsedFileEventArgs : EventArgs - { - public IUnresolvedFile File { - get; - private set; - } - - public ParsedFileEventArgs (IUnresolvedFile file) - { - this.File = file; - } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ProjectCommentTags.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ProjectCommentTags.cs deleted file mode 100644 index c2b33ee0b0..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ProjectCommentTags.cs +++ /dev/null @@ -1,80 +0,0 @@ -// -// ProjectCommentTags.cs -// -// Author: -// Mike Krüger <mkrueger@xamarin.com> -// -// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Collections.Concurrent; -using MonoDevelop.Projects; -using MonoDevelop.Ide.Tasks; - -namespace MonoDevelop.Ide.TypeSystem -{ - [Serializable] - public class ProjectCommentTags - { - readonly Dictionary<string, List<Tag>> tags = new Dictionary<string, List<Tag>> (); - - public IDictionary<string, List<Tag>> Tags { - get { - return tags; - } - } - - public void UpdateTags (Project project, string fileName, IList<Tag> tagComments) - { - var list = tagComments == null || tagComments.Count == 0 ? null : new List<Tag> (tagComments); - lock (tags) { - List<Tag> oldList; - tags.TryGetValue (fileName, out oldList); - if (list == null && oldList == null) - return; - tags[fileName] = list; - TaskService.InformCommentTasks (new CommentTasksChangedEventArgs (fileName, tagComments, project)); - } - } - - public void RemoveFile (Project project, string fileName) - { - lock (tags) { - if (!tags.ContainsKey (fileName)) - return; - tags[fileName] = null; - } - - TaskService.InformCommentTasks (new CommentTasksChangedEventArgs (fileName, null, project)); - } - - internal void Update (Project project) - { - foreach (var file in project.Files) { - if (file.BuildAction == BuildAction.None) - continue; - TypeSystemService.ParseFile (project, file.FilePath); - } - } - } -} - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ProjectContentEventArgs.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ProjectContentEventArgs.cs deleted file mode 100644 index 54d31aa138..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/ProjectContentEventArgs.cs +++ /dev/null @@ -1,52 +0,0 @@ -// -// ProjectContentEventArgs.cs -// -// Author: -// Mike Krüger <mkrueger@novell.com> -// -// Copyright (c) 2011 Mike Krüger <mkrueger@novell.com> -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -using System; -using ICSharpCode.NRefactory.TypeSystem; -using MonoDevelop.Projects; - -namespace MonoDevelop.Ide.TypeSystem -{ - [Serializable] - public sealed class ProjectContentEventArgs : EventArgs - { - public Project Project { - get; - private set; - } - - public IProjectContent Content { - get; - private set; - } - - public ProjectContentEventArgs (Project project, IProjectContent content) - { - this.Project = project; - this.Content = content; - } - } -} - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/StockIcons.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/StockIcons.cs index bd7eea162b..d52111e65f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/StockIcons.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/StockIcons.cs @@ -25,8 +25,8 @@ // THE SOFTWARE. using System; using MonoDevelop.Core; -using ICSharpCode.NRefactory.TypeSystem; using Mono.Cecil; +using ICSharpCode.NRefactory6.CSharp; namespace MonoDevelop.Ide.TypeSystem { @@ -122,149 +122,175 @@ namespace MonoDevelop.Ide.TypeSystem Stock.Event, Stock.PrivateEvent, Stock.Event, Stock.ProtectedEvent, Stock.InternalEvent, Stock.ProtectedOrInternalEvent, Stock.InternalAndProtectedEvent }; - public static string GetStockIcon (this INamedElement element) - { - if (element is IType) - return ((IType)element).GetStockIcon (); - if (element is ITypeParameter) - return ((ITypeParameter)element).GetStockIcon (); - if (element is IUnresolvedEntity) - return ((IUnresolvedEntity)element).GetStockIcon (); - return ((IEntity)element).GetStockIcon (); - } - - public static string GetStockIcon (this ITypeDefinition entity) - { - return GetStockIcon ((IType)entity); - } - - public static string GetStockIcon (this IType entity) +// public static IconId GetStockIcon (this INamedElement element) +// { +// if (element is IType) +// return ((IType)element).GetStockIcon (); +// if (element is ITypeParameter) +// return ((ITypeParameter)element).GetStockIcon (); +// if (element is IUnresolvedEntity) +// return ((IUnresolvedEntity)element).GetStockIcon (); +// return ((IEntity)element).GetStockIcon (); +// } +// +// public static IconId GetStockIcon (this ITypeDefinition entity) +// { +// return GetStockIcon ((IType)entity); +// } +// +// public static IconId GetStockIcon (this IType entity) +// { +// var def = entity.GetDefinition (); +// if (def == null) +// return Class; +// switch (def.Kind) { +// case TypeKind.Class: +// return typeIconTable [0, (int)def.Accessibility]; +// case TypeKind.Enum: +// return typeIconTable [1, (int)def.Accessibility]; +// case TypeKind.Interface: +// return typeIconTable [2, (int)def.Accessibility]; +// case TypeKind.Struct: +// return typeIconTable [3, (int)def.Accessibility]; +// case TypeKind.Delegate: +// return typeIconTable [4, (int)def.Accessibility]; +// default: +// return typeIconTable [0, (int)def.Accessibility]; +// } +// } + + static int GetIndex (Microsoft.CodeAnalysis.Accessibility accessibility) { - var def = entity.GetDefinition (); - if (def == null) - return Class; - switch (def.Kind) { - case TypeKind.Class: - return typeIconTable [0, (int)def.Accessibility]; - case TypeKind.Enum: - return typeIconTable [1, (int)def.Accessibility]; - case TypeKind.Interface: - return typeIconTable [2, (int)def.Accessibility]; - case TypeKind.Struct: - return typeIconTable [3, (int)def.Accessibility]; - case TypeKind.Delegate: - return typeIconTable [4, (int)def.Accessibility]; + switch (accessibility) { + case Microsoft.CodeAnalysis.Accessibility.NotApplicable: + return 0; + case Microsoft.CodeAnalysis.Accessibility.Private: + return 1; + case Microsoft.CodeAnalysis.Accessibility.ProtectedAndInternal: + return 5; + case Microsoft.CodeAnalysis.Accessibility.Protected: + return 3; + case Microsoft.CodeAnalysis.Accessibility.Internal: + return 4; + case Microsoft.CodeAnalysis.Accessibility.ProtectedOrInternal: + return 5; + case Microsoft.CodeAnalysis.Accessibility.Public: + return 2; default: - return typeIconTable [0, (int)def.Accessibility]; + throw new ArgumentOutOfRangeException (); } } - - public static string GetStockIcon (this IUnresolvedTypeDefinition def) + + public static IconId GetStockIcon (this Microsoft.CodeAnalysis.ISymbol symbol) { - switch (def.Kind) { - case TypeKind.Class: - return typeIconTable [0, (int)def.Accessibility]; - case TypeKind.Enum: - return typeIconTable [1, (int)def.Accessibility]; - case TypeKind.Interface: - return typeIconTable [2, (int)def.Accessibility]; - case TypeKind.Struct: - return typeIconTable [3, (int)def.Accessibility]; - case TypeKind.Delegate: - return typeIconTable [4, (int)def.Accessibility]; + switch (symbol.Kind) { + case Microsoft.CodeAnalysis.SymbolKind.Alias: + case Microsoft.CodeAnalysis.SymbolKind.ArrayType: + case Microsoft.CodeAnalysis.SymbolKind.Assembly: + case Microsoft.CodeAnalysis.SymbolKind.DynamicType: + case Microsoft.CodeAnalysis.SymbolKind.ErrorType: + case Microsoft.CodeAnalysis.SymbolKind.Label: + case Microsoft.CodeAnalysis.SymbolKind.Local: + case Microsoft.CodeAnalysis.SymbolKind.NetModule: + case Microsoft.CodeAnalysis.SymbolKind.PointerType: + return Field; + case Microsoft.CodeAnalysis.SymbolKind.NamedType: + var namedTypeSymbol = (Microsoft.CodeAnalysis.INamedTypeSymbol)symbol; + return typeIconTable [GetTypeIndex(namedTypeSymbol.TypeKind ), GetIndex (namedTypeSymbol.DeclaredAccessibility)]; + case Microsoft.CodeAnalysis.SymbolKind.Event: + var evtSymbol = (Microsoft.CodeAnalysis.IEventSymbol)symbol; + return eventIconTable [GetIndex (evtSymbol.DeclaredAccessibility)]; + case Microsoft.CodeAnalysis.SymbolKind.Field: + var fieldSymbol = (Microsoft.CodeAnalysis.IFieldSymbol)symbol; + return fieldIconTable [GetIndex (fieldSymbol.DeclaredAccessibility)]; + case Microsoft.CodeAnalysis.SymbolKind.Method: + var methodSymbol = (Microsoft.CodeAnalysis.IMethodSymbol)symbol; + return methodIconTable [GetIndex (methodSymbol.DeclaredAccessibility)]; + case Microsoft.CodeAnalysis.SymbolKind.Namespace: + return Namespace; + case Microsoft.CodeAnalysis.SymbolKind.Parameter: + return Field; + case Microsoft.CodeAnalysis.SymbolKind.Property: + var propertySymbol = (Microsoft.CodeAnalysis.IPropertySymbol)symbol; + return propertyIconTable [GetIndex (propertySymbol.DeclaredAccessibility)]; + case Microsoft.CodeAnalysis.SymbolKind.RangeVariable: + return Field; + case Microsoft.CodeAnalysis.SymbolKind.TypeParameter: + return Stock.typeIconTable [0, 0]; + case Microsoft.CodeAnalysis.SymbolKind.Preprocessing: + return Field; default: - return typeIconTable [0, (int)def.Accessibility]; + throw new ArgumentOutOfRangeException (); } } - - public static string GetStockIcon (this IField field) - { - return GetStockIcon ((IEntity)field); - } - - public static string GetStockIcon (this IVariable variable) - { - return Field; - } - - public static string GetStockIcon (this IParameter parameter) - { - return Field; - } - - public static string GetStockIcon (this IUnresolvedTypeParameter parameter) - { - return Field; - } - - public static string GetStockIcon (this IEntity entity, bool showAccessibility = true) + + static int GetTypeIndex (Microsoft.CodeAnalysis.TypeKind typeKind) { - switch (entity.SymbolKind) { - case SymbolKind.TypeDefinition: - return GetStockIcon ((IType)entity); - case SymbolKind.Field: - if (showAccessibility) - return fieldIconTable [(int)entity.Accessibility]; - else - return fieldIconTable [0]; - case SymbolKind.Method: - case SymbolKind.Constructor: - case SymbolKind.Destructor: - case SymbolKind.Operator: - if (showAccessibility) { - if (((IMethod)entity).IsExtensionMethod) - return extensionMethodIconTable [(int)entity.Accessibility]; - return methodIconTable [(int)entity.Accessibility]; - } else { - if (((IMethod)entity).IsExtensionMethod) - return extensionMethodIconTable [0]; - return methodIconTable [0]; - } - case SymbolKind.Property: - case SymbolKind.Indexer: - if (showAccessibility) - return propertyIconTable [(int)entity.Accessibility]; - else - return propertyIconTable [0]; - case SymbolKind.Event: - if (showAccessibility) - return eventIconTable [(int)entity.Accessibility]; - else - return eventIconTable [0]; + switch (typeKind) { + case Microsoft.CodeAnalysis.TypeKind.Unknown: + case Microsoft.CodeAnalysis.TypeKind.Array: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Class: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Delegate: + return 4; + case Microsoft.CodeAnalysis.TypeKind.Dynamic: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Enum: + return 1; + case Microsoft.CodeAnalysis.TypeKind.Error: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Interface: + return 2; + case Microsoft.CodeAnalysis.TypeKind.Module: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Pointer: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Struct: + return 3; + case Microsoft.CodeAnalysis.TypeKind.TypeParameter: + return 0; + case Microsoft.CodeAnalysis.TypeKind.Submission: + return 0; + default: + throw new ArgumentOutOfRangeException (); } - return ""; } - public static string GetStockIcon (this IUnresolvedEntity entity, bool showAccessibility = true) + + internal static IconId GetStockIconForSymbolInfo (this DeclaredSymbolInfo symbol) { - switch (entity.SymbolKind) { - case SymbolKind.TypeDefinition: - return GetStockIcon ((IUnresolvedTypeDefinition)entity); - case SymbolKind.Field: - if (showAccessibility) - return fieldIconTable [(int)entity.Accessibility]; - else - return fieldIconTable [0]; - case SymbolKind.Method: - case SymbolKind.Constructor: - case SymbolKind.Destructor: - case SymbolKind.Operator: - if (showAccessibility) - return methodIconTable [(int)entity.Accessibility]; - else - return methodIconTable [0]; - case SymbolKind.Property: - case SymbolKind.Indexer: - if (showAccessibility) - return propertyIconTable [(int)entity.Accessibility]; - else - return propertyIconTable [0]; - case SymbolKind.Event: - if (showAccessibility) - return eventIconTable [(int)entity.Accessibility]; - else - return eventIconTable [0]; + switch (symbol.Kind) { + case DeclaredSymbolInfoKind.Class: + return Stock.Class; + case DeclaredSymbolInfoKind.Constant: + return Stock.Field; + case DeclaredSymbolInfoKind.Constructor: + return Stock.Method; + case DeclaredSymbolInfoKind.Delegate: + return Stock.Delegate; + case DeclaredSymbolInfoKind.Enum: + return Stock.Enum; + case DeclaredSymbolInfoKind.EnumMember: + return Stock.Field; + case DeclaredSymbolInfoKind.Event: + return Stock.Event; + case DeclaredSymbolInfoKind.Field: + return Stock.Field; + case DeclaredSymbolInfoKind.Indexer: + return Stock.Method; + case DeclaredSymbolInfoKind.Interface: + return Stock.Interface; + case DeclaredSymbolInfoKind.Method: + return Stock.Method; + case DeclaredSymbolInfoKind.Module: + return Stock.Method; + case DeclaredSymbolInfoKind.Property: + return Stock.Property; + case DeclaredSymbolInfoKind.Struct: + return Stock.Struct; + default: + throw new ArgumentOutOfRangeException (); } - return ""; } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Tag.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Tag.cs index f45231144b..9f6924a2d0 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Tag.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Tag.cs @@ -51,7 +51,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; -using ICSharpCode.NRefactory.TypeSystem; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.TypeSystem { @@ -66,16 +67,16 @@ namespace MonoDevelop.Ide.TypeSystem } } - public Tag (string key, DomRegion region) + public Tag (string key, DocumentRegion region) { this.key = key; base.Region = region; } - - public Tag (string key, string comment, DomRegion region) : base (comment) + + public Tag (string key, string comment, DocumentRegion region) : base (comment) { this.key = key; base.Region = region; } } -} +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParser.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParser.cs index 7470e8c358..62ca819303 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParser.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParser.cs @@ -27,9 +27,53 @@ using System; using System.IO; using ICSharpCode.NRefactory.TypeSystem; using MonoDevelop.Projects; +using MonoDevelop.Core.Text; +using Microsoft.CodeAnalysis; +using System.Threading.Tasks; +using System.Threading; +using MonoDevelop.Ide.Editor.Projection; +using MonoDevelop.Ide.Editor; +using System.Collections.Generic; namespace MonoDevelop.Ide.TypeSystem { + public sealed class ParseOptions + { + public string FileName { get; set; } + + public ITextSource Content { get; set; } + + public MonoDevelop.Projects.Project Project { get; set; } + + public Document RoslynDocument { get; set; } + } + + [Flags] + public enum DisabledProjectionFeatures { + None = 0, + Completion = 1 << 0, + SemanticHighlighting = 1 << 1, + Tooltips = 1 << 2, + + All = Completion | SemanticHighlighting | Tooltips + } + + public class ParsedDocumentProjection + { + public ParsedDocument ParsedDocument { get; private set; } + + public IReadOnlyList<Projection> Projections { get; private set;} + + public DisabledProjectionFeatures DisabledProjectionFeatures { get; private set;} + + public ParsedDocumentProjection (ParsedDocument parsedDocument, IReadOnlyList<Projection> projections, DisabledProjectionFeatures disabledProjectionFeatures = DisabledProjectionFeatures.None) + { + this.ParsedDocument = parsedDocument; + this.Projections = projections; + this.DisabledProjectionFeatures = disabledProjectionFeatures; + } + } + /// <summary> /// A type system parser provides a ParsedDocument (which just adds some more information to a IUnresolvedFile) for /// a given file. This is required for adding information to the type system service to make the file contents available @@ -52,24 +96,51 @@ namespace MonoDevelop.Ide.TypeSystem /// <param name='project'> /// The project the file belongs to. /// </param> - public abstract ParsedDocument Parse (bool storeAst, string fileName, TextReader content, Project project = null); + public abstract Task<ParsedDocument> Parse (ParseOptions options, CancellationToken cancellationToken = default(CancellationToken)); /// <summary> - /// Parse the specified file. The file content should be read by the type system parser. + /// If true projections are possible. A projection transforms a source to a target language and maps certain parts of the file to parts in the projected file. + /// That's used for embedded languages for example. /// </summary> - /// <param name='storeAst'> - /// If set to <c>true</c> the ast should be stored in the parsed document. - /// </param> - /// <param name='fileName'> - /// The name of the file. - /// </param> - /// <param name='project'> - /// The project the file belongs to. - /// </param> - public virtual ParsedDocument Parse (bool storeAst, string fileName, Project project = null) + /// <returns><c>true</c> if this instance can generate projection the specified mimeType buildAction supportedLanguages; + /// otherwise, <c>false</c>.</returns> + /// <param name="mimeType">MIME type.</param> + /// <param name="buildAction">Build action.</param> + /// <param name="supportedLanguages">Supported languages.</param> + public virtual bool CanGenerateProjection (string mimeType, string buildAction, string[] supportedLanguages) + { + return false; + } + + /// <summary> + /// Generates the plain projection. This is used for type system services. + /// </summary> + /// <returns>The projection.</returns> + /// <param name="options">Options.</param> + /// <param name="cancellationToken">Cancellation token.</param> + public virtual Task<IReadOnlyList<Projection>> GenerateProjections (ParseOptions options, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotSupportedException (); + } + + /// <summary> + /// Generates the parsed document projection. That contains the parsed document and the projection. This is used inside the IDE for the editor. + /// That's usually more efficient than calling Parse/GenerateProjection separately. + /// </summary> + /// <returns>The parsed document projection.</returns> + /// <param name="options">Options.</param> + /// <param name="cancellationToken">Cancellation token.</param> + public virtual Task<ParsedDocumentProjection> GenerateParsedDocumentProjection (ParseOptions options, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotSupportedException (); + } + + /// <summary> + /// Gets an up to date partial projection used by code completion. + /// </summary> + public virtual Task<IReadOnlyList<Projection>> GetPartialProjectionsAsync (DocumentContext ctx, TextEditor editor, ParsedDocument currentParsedDocument, CancellationToken cancellationToken = default(CancellationToken)) { - using (var stream = Mono.TextEditor.Utils.TextFileUtility.OpenStream (fileName)) - return Parse (storeAst, fileName, stream, project); + throw new NotSupportedException (); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs index 1cb27e418a..1a58e50598 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs @@ -113,6 +113,4 @@ namespace MonoDevelop.Ide.TypeSystem } } } - } - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs index e18c2c603c..8a5bf71552 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs @@ -28,138 +28,24 @@ using System.Collections.Generic; using System.Linq; using System.IO; using MonoDevelop.Projects; -using ICSharpCode.NRefactory.TypeSystem.Implementation; -using ICSharpCode.NRefactory.TypeSystem; using Mono.Addins; using MonoDevelop.Core; using MonoDevelop.Ide; -using Mono.TextEditor; using System.Threading; -using MonoDevelop.Core.ProgressMonitoring; using System.Xml; using ICSharpCode.NRefactory.Utils; -using ICSharpCode.NRefactory; using System.Threading.Tasks; -using ICSharpCode.NRefactory.Documentation; -using ICSharpCode.NRefactory.CSharp; using MonoDevelop.Ide.Extensions; using MonoDevelop.Core.Assemblies; using System.Text; -using ICSharpCode.NRefactory.Completion; -using System.Diagnostics; -using MonoDevelop.Projects.SharedAssetsProjects; -using Mono.CSharp.Nullable; -using MonoDevelop.Ide.Templates; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core.Text; +using Microsoft.CodeAnalysis.Text; +using Mono.Posix; namespace MonoDevelop.Ide.TypeSystem { - public static class TypeSystemServiceExt - { - /// <summary> - /// Tries to the get source project for a given type definition. This operation may fall if it was called on an outdated - /// compilation unit or the correspondening project was unloaded. - /// </summary> - /// <returns><c>true</c>, if get source project was found, <c>false</c> otherwise.</returns> - /// <param name="type">The type definition.</param> - /// <param name="project">The project or null if it wasn't found.</param> - public static bool TryGetSourceProject (this ITypeDefinition type, out Project project) - { - var location = type.Compilation.MainAssembly.UnresolvedAssembly.Location; - if (string.IsNullOrEmpty (location)) { - project = null; - return false; - } - project = TypeSystemService.GetProject (location); - return project != null; - } - - /// <summary> - /// Tries to the get source project for a given type. This operation may fall if it was called on an outdated - /// compilation unit or the correspondening project was unloaded. - /// </summary> - /// <returns><c>true</c>, if get source project was found, <c>false</c> otherwise.</returns> - /// <param name="type">The type.</param> - /// <param name="project">The project or null if it wasn't found.</param> - public static bool TryGetSourceProject (this IType type, out Project project) - { - var def = type.GetDefinition (); - if (def == null) { - project = null; - return false; - } - return def.TryGetSourceProject (out project); - } - - - internal static Project GetProjectWhereTypeIsDefined (this ITypeDefinition type) - { - var location = type.ParentAssembly.UnresolvedAssembly.Location; - if (string.IsNullOrEmpty (location)) - return null; - return TypeSystemService.GetProject (location); - } - - internal static Project GetProjectWhereTypeIsDefined (this IType type) - { - Project project; - TryGetSourceProject (type, out project); - return project; - } - - public static TextLocation GetLocation (this IType type) - { - return type.GetDefinition ().Region.Begin; - } - - public static bool IsBaseType (this IType type, IType potentialBase) - { - return type.GetAllBaseTypes ().Any (t => t.Equals (potentialBase)); - } - - public static bool IsObsolete (this IEntity member) - { - return member != null && member.Attributes.Any (a => a.AttributeType.FullName == "System.ObsoleteAttribute"); - } - - public static bool IsObsolete (this IEntity member, out string reason) - { - if (member == null) { - reason = null; - return false; - } - var attr = member.Attributes.FirstOrDefault (a => a.AttributeType.FullName == "System.ObsoleteAttribute"); - if (attr == null) { - reason = null; - return false; - } - reason = attr.PositionalArguments.Count > 0 ? attr.PositionalArguments [0].ConstantValue.ToString () : null; - return true; - } - - public static IType Resolve (this IUnresolvedTypeDefinition def, Project project) - { - if (project == null) - throw new ArgumentNullException ("project"); - var compilation = TypeSystemService.GetCompilation (project); - var ctx = new SimpleTypeResolveContext (compilation.MainAssembly); - var resolvedType = def.Resolve (ctx); - return resolvedType; - } - } - - /// <summary> - /// The folding parser is used for generating a preliminary parsed document that does not - /// contain a full dom - only some basic lexical constructs like comments or pre processor directives. - /// - /// This is useful for opening a document the first time to have some folding regions as start that are folded by default. - /// Otherwise an irritating screen update will occur. - /// </summary> - public interface IFoldingParser - { - ParsedDocument Parse (string fileName, string content); - } - - public static class TypeSystemService + public static partial class TypeSystemService { const string CurrentVersion = "1.1.8"; static readonly List<TypeSystemParserNode> parsers; @@ -171,6 +57,11 @@ namespace MonoDevelop.Ide.TypeSystem } } + public static bool TrackFileChanges { + get; + set; + } + public static void RemoveSkippedfile (FilePath fileName) { filesSkippedInParseThread = filesSkippedInParseThread.Where (f => f != fileName).ToArray (); @@ -196,7 +87,33 @@ namespace MonoDevelop.Ide.TypeSystem break; } }); + try { + emptyWorkspace = new MonoDevelopWorkspace (); + } catch (Exception e) { + LoggingService.LogFatalError ("Can't create roslyn workspace", e); + } + FileService.FileChanged += delegate(object sender, FileEventArgs e) { + // if (!TrackFileChanges) + // return; + foreach (var file in e) { + // Open documents are handled by the Document class itself. + if (IdeApp.Workbench != null && IdeApp.Workbench.GetDocument (file.FileName) != null) + continue; + try { + var text = MonoDevelop.Core.Text.StringTextSource.ReadFrom (file.FileName).Text; + foreach (var w in Workspaces) + w.UpdateFileContent (file.FileName, text); + } catch (FileNotFoundException) {} + } + if (IdeApp.Workbench != null) + foreach (var w in IdeApp.Workbench.Documents) + w.StartReparseThread (); + }; + + IntitializeTrackedProjectHandling (); + } +/* AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/OutputTracking", delegate (object sender, ExtensionNodeEventArgs args) { var node = (TypeSystemOutputTrackingNode)args.ExtensionNode; switch (args.Change) { @@ -209,63 +126,11 @@ namespace MonoDevelop.Ide.TypeSystem } }); - FileService.FileChanged += delegate(object sender, FileEventArgs e) { - if (!TrackFileChanges) - return; - foreach (var file in e) { - // Open documents are handled by the Document class itself. - if (IdeApp.Workbench != null && IdeApp.Workbench.GetDocument (file.FileName) != null) - continue; - // - lock (projectWrapperUpdateLock) { - foreach (var wrapper in projectContents.Values) { - var projectFile = wrapper.Project.Files.GetFile (file.FileName); - if (projectFile != null) - QueueParseJob (wrapper, new [] { projectFile }); - } - UnresolvedAssemblyProxy ctx; - if (cachedAssemblyContents.TryGetValue (file.FileName, out ctx)) - CheckModifiedFile (ctx); - } - } - - foreach (var content in projectContents.Values.ToArray ()) { - var files = new List<ProjectFile> (); - foreach (var file in e) { - var f = content.Project.GetProjectFile (file.FileName); - if (f == null || f.BuildAction == BuildAction.None) - continue; - files.Add (f); - } - if (files.Count > 0) - QueueParseJob (content, files); - } - - }; - if (IdeApp.ProjectOperations != null) { - IdeApp.ProjectOperations.EndBuild += HandleEndBuild; - } - if (IdeApp.Workspace != null) { - IdeApp.Workspace.ActiveConfigurationChanged += HandleActiveConfigurationChanged; - } - } - - static void HandleActiveConfigurationChanged (object sender, EventArgs e) - { - foreach (var pr in projectContents.ToArray ()) { - var project = pr.Key as DotNetProject; - if (project != null) - CheckProjectOutput (project, true); - - pr.Value.referencesConnected = false; - } - } - static readonly List<TypeSystemOutputTrackingNode> outputTrackedProjects = new List<TypeSystemOutputTrackingNode> (); static bool IsOutputTracked (DotNetProject project) { - foreach (var projectType in project.GetTypeTags ()) { + foreach (var projectType in project.GetProjectTypes ()) { if (outputTrackedProjects.Any (otp => otp.ProjectType != null && string.Equals (otp.ProjectType, projectType, StringComparison.OrdinalIgnoreCase))) { return true; } @@ -273,35 +138,7 @@ namespace MonoDevelop.Ide.TypeSystem return outputTrackedProjects.Any (otp => otp.LanguageName != null && string.Equals (otp.LanguageName, project.LanguageName, StringComparison.OrdinalIgnoreCase)); } - static void CheckProjectOutput (DotNetProject project, bool autoUpdate) - { - if (project == null) - throw new ArgumentNullException ("project"); - if (IsOutputTracked (project)) { - var fileName = project.GetOutputFileName (IdeApp.Workspace.ActiveConfiguration); - - var wrapper = GetProjectContentWrapper (project); - if (wrapper == null) - return; - bool update = wrapper.UpdateTrackedOutputAssembly (fileName); - if (autoUpdate && update) { - wrapper.ReconnectAssemblyReferences (); - - // update documents - foreach (var openDocument in IdeApp.Workbench.Documents) { - openDocument.ReparseDocument (); - } - } - } - } - - static void HandleEndBuild (object sender, BuildEventArgs args) - { - var project = args.SolutionItem as DotNetProject; - if (project == null) - return; - CheckProjectOutput (project, true); - } +*/ public static TypeSystemParser GetParser (string mimeType, string buildAction = BuildAction.Compile) { @@ -309,7 +146,7 @@ namespace MonoDevelop.Ide.TypeSystem return n != null ? n.Parser : null; } - static TypeSystemParserNode GetTypeSystemParserNode (string mimeType, string buildAction) + internal static TypeSystemParserNode GetTypeSystemParserNode (string mimeType, string buildAction) { foreach (var mt in DesktopService.GetMimeTypeInheritanceChain (mimeType)) { var provider = Parsers.FirstOrDefault (p => p.CanParse (mt, buildAction)); @@ -319,172 +156,157 @@ namespace MonoDevelop.Ide.TypeSystem return null; } - static List<MimeTypeExtensionNode> foldingParsers; - - static IEnumerable<MimeTypeExtensionNode> FoldingParsers { - get { - if (foldingParsers == null) { - foldingParsers = new List<MimeTypeExtensionNode> (); - AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/FoldingParser", delegate (object sender, ExtensionNodeEventArgs args) { - switch (args.Change) { - case ExtensionChange.Add: - foldingParsers.Add ((MimeTypeExtensionNode)args.ExtensionNode); - break; - case ExtensionChange.Remove: - foldingParsers.Remove ((MimeTypeExtensionNode)args.ExtensionNode); - break; - } - }); - } - return foldingParsers; - } - } - - public static IFoldingParser GetFoldingParser (string mimeType) + public static Task<ParsedDocument> ParseFile (Project project, string fileName, CancellationToken cancellationToken = default(CancellationToken)) { - foreach (var mt in DesktopService.GetMimeTypeInheritanceChain (mimeType)) { - var node = FoldingParsers.FirstOrDefault (n => n.MimeType == mt); - if (node != null) - return node.CreateInstance () as IFoldingParser; - } - return null; - } + StringTextSource text; - public static ParsedDocument ParseFile (Project project, string fileName) - { - string text; - try { if (!File.Exists (fileName)) return null; - text = Mono.TextEditor.Utils.TextFileUtility.ReadAllText (fileName); + text = StringTextSource.ReadFrom (fileName); } catch (Exception) { return null; } - - return ParseFile (project, fileName, DesktopService.GetMimeTypeForUri (fileName), text); + + return ParseFile (project, fileName, DesktopService.GetMimeTypeForUri (fileName), text, cancellationToken); } - static readonly object projectWrapperUpdateLock = new object (); - - public static ParsedDocument ParseFile (Project project, string fileName, string mimeType, string content) + public static Task<ParsedDocument> ParseFile (ParseOptions options, string mimeType, CancellationToken cancellationToken = default(CancellationToken)) { - if (fileName == null) - throw new ArgumentNullException ("fileName"); + if (options == null) + throw new ArgumentNullException ("options"); + if (options.FileName == null) + throw new ArgumentNullException ("options.FileName"); + var parser = GetParser (mimeType); if (parser == null) - return null; + return Task.FromResult ((ParsedDocument)null); - var t = Counters.ParserService.FileParsed.BeginTiming (fileName); + var t = Counters.ParserService.FileParsed.BeginTiming (options.FileName); try { - var result = parser.Parse (true, fileName, new StringReader (content), project); - lock (projectWrapperUpdateLock) { - ProjectContentWrapper wrapper; - if (project != null) { - projectContents.TryGetValue (project, out wrapper); - } else { - wrapper = null; - } - if (wrapper != null && (result.Flags & ParsedDocumentFlags.NonSerializable) != ParsedDocumentFlags.NonSerializable) { - var oldFile = wrapper._content.GetFile (fileName); - wrapper.UpdateContent (c => c.AddOrUpdateFiles (result.ParsedFile)); - UpdateProjectCommentTasks (wrapper, result); - if (oldFile != null) - wrapper.InformFileRemoved (new ParsedFileEventArgs (oldFile)); - wrapper.InformFileAdded (new ParsedFileEventArgs (result.ParsedFile)); - } - - // The parsed file could be included in other projects as well, therefore - // they need to be updated. - foreach (var cnt in projectContents.ToArray ()) { - if (cnt.Key == project) - continue; - // Use the project context because file lookup is faster there than in the project class. - var pcnt = cnt.Value; - var file = pcnt._content.GetFile (fileName); - if (file != null) { - var newResult = parser.Parse (false, fileName, new StringReader (content), pcnt.Project); - if ((newResult.Flags & ParsedDocumentFlags.NonSerializable) != ParsedDocumentFlags.NonSerializable) { - pcnt.UpdateContent (c => c.AddOrUpdateFiles (newResult.ParsedFile)); - pcnt.InformFileRemoved (new ParsedFileEventArgs (file)); - pcnt.InformFileAdded (new ParsedFileEventArgs (newResult.ParsedFile)); - } - } - } - } + var result = parser.Parse (options, cancellationToken); return result; + } catch (OperationCanceledException) { + return Task.FromResult ((ParsedDocument)null); } catch (Exception e) { LoggingService.LogError ("Exception while parsing: " + e); - return null; + return Task.FromResult ((ParsedDocument)null); } finally { t.Dispose (); } } - public static ParsedDocument ParseFile (Project project, string fileName, string mimeType, TextReader content) + internal static bool CanParseProjections (Project project, string mimeType, string fileName) + { + var parser = GetParser (mimeType); + if (parser == null) + return false; + var projectFile = project.GetProjectFile (fileName); + if (projectFile == null) + return false; + + return parser.CanGenerateProjection (mimeType, projectFile.BuildAction, project.SupportedLanguages); + } + + public static Task<ParsedDocument> ParseFile (Project project, string fileName, string mimeType, ITextSource content, CancellationToken cancellationToken = default(CancellationToken)) { - return ParseFile (project, fileName, mimeType, content.ReadToEnd ()); + return ParseFile (new ParseOptions { FileName = fileName, Project = project, Content = content }, mimeType, cancellationToken); } - public static ParsedDocument ParseFile (Project project, TextEditorData data) + public static Task<ParsedDocument> ParseFile (Project project, string fileName, string mimeType, TextReader content, CancellationToken cancellationToken = default(CancellationToken)) { - return ParseFile (project, data.FileName, data.MimeType, data.Text); + return ParseFile (project, fileName, mimeType, new StringTextSource (content.ReadToEnd ()), cancellationToken); } - public static ParsedDocument ParseFile (string fileName, string mimeType, string text, ProjectContentWrapper wrapper = null) + public static Task<ParsedDocument> ParseFile (Project project, IReadonlyTextDocument data, CancellationToken cancellationToken = default(CancellationToken)) { - using (var reader = new StringReader (text)) - return ParseFile (fileName, mimeType, reader, wrapper); + return ParseFile (project, data.FileName, data.MimeType, data, cancellationToken); } - public static ParsedDocument ParseFile (string fileName, string mimeType, TextReader content, ProjectContentWrapper wrapper = null) + internal static Task<ParsedDocumentProjection> ParseProjection (ParseOptions options, string mimeType, CancellationToken cancellationToken = default(CancellationToken)) { + if (options == null) + throw new ArgumentNullException ("options"); + if (options.FileName == null) + throw new ArgumentNullException ("fileName"); + var parser = GetParser (mimeType); if (parser == null) - return null; - var t = Counters.ParserService.FileParsed.BeginTiming (fileName); + return Task.FromResult ((ParsedDocumentProjection)null); + + var t = Counters.ParserService.FileParsed.BeginTiming (options.FileName); try { - var result = parser.Parse (true, fileName, content); - lock (projectWrapperUpdateLock) { - if (wrapper != null && (result.Flags & ParsedDocumentFlags.NonSerializable) != ParsedDocumentFlags.NonSerializable) { - var oldFile = wrapper._content.GetFile (fileName); - wrapper.UpdateContent (c => c.AddOrUpdateFiles (result.ParsedFile)); - UpdateProjectCommentTasks (wrapper, result); - if (oldFile != null) - wrapper.InformFileRemoved (new ParsedFileEventArgs (oldFile)); - wrapper.InformFileAdded (new ParsedFileEventArgs (result.ParsedFile)); + var result = parser.GenerateParsedDocumentProjection (options, cancellationToken); + if (options.Project != null) { + var Workspace = Workspaces.First () ; + var projectId = Workspace.GetProjectId (options.Project); + if (projectId != null) { + foreach (var projection in result.Result.Projections) { + var docId = Workspace.GetDocumentId (projectId, projection.Document.FileName); + if (docId != null) + Workspace.InformDocumentTextChange (docId, new MonoDevelopSourceText (projection.Document)); + } } } return result; + } catch (OperationCanceledException) { + return Task.FromResult ((ParsedDocumentProjection)null); } catch (Exception e) { - LoggingService.LogError ("Exception while parsing :" + e); - return null; + LoggingService.LogError ("Exception while parsing: " + e); + return Task.FromResult ((ParsedDocumentProjection)null); } finally { t.Dispose (); } } - public static event EventHandler ParseOperationStarted; + internal static Task<ParsedDocumentProjection> ParseProjection (Project project, string fileName, string mimeType, ITextSource content, CancellationToken cancellationToken = default(CancellationToken)) + { + return ParseProjection (new ParseOptions { FileName = fileName, Project = project, Content = content }, mimeType, cancellationToken); + } + + internal static Task<ParsedDocumentProjection> ParseProjection (Project project, string fileName, string mimeType, TextReader content, CancellationToken cancellationToken = default(CancellationToken)) + { + return ParseProjection (project, fileName, mimeType, new StringTextSource (content.ReadToEnd ()), cancellationToken); + } - internal static void StartParseOperation () + internal static Task<ParsedDocumentProjection> ParseProjection (Project project, IReadonlyTextDocument data, CancellationToken cancellationToken = default(CancellationToken)) { - if ((parseStatus++) == 0) { - if (ParseOperationStarted != null) - ParseOperationStarted (null, EventArgs.Empty); - } + return ParseProjection (project, data.FileName, data.MimeType, data, cancellationToken); } - public static event EventHandler ParseOperationFinished; + + #region Folding parsers + static List<MimeTypeExtensionNode> foldingParsers; - internal static void EndParseOperation () + static IEnumerable<MimeTypeExtensionNode> FoldingParsers { + get { + if (foldingParsers == null) { + foldingParsers = new List<MimeTypeExtensionNode> (); + AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/FoldingParser", delegate (object sender, ExtensionNodeEventArgs args) { + switch (args.Change) { + case ExtensionChange.Add: + foldingParsers.Add ((MimeTypeExtensionNode)args.ExtensionNode); + break; + case ExtensionChange.Remove: + foldingParsers.Remove ((MimeTypeExtensionNode)args.ExtensionNode); + break; + } + }); + } + return foldingParsers; + } + } + + public static IFoldingParser GetFoldingParser (string mimeType) { - if (parseStatus == 0) - return; - if (--parseStatus == 0) { - if (ParseOperationFinished != null) - ParseOperationFinished (null, EventArgs.Empty); + foreach (var mt in DesktopService.GetMimeTypeInheritanceChain (mimeType)) { + var node = FoldingParsers.FirstOrDefault (n => n.MimeType == mt); + if (node != null) + return node.CreateInstance () as IFoldingParser; } + return null; } + #endregion #region Parser Database Handling @@ -702,7 +524,7 @@ namespace MonoDevelop.Ide.TypeSystem { var t = Counters.ParserService.ObjectDeserialized.BeginTiming (path); try { - using (var fs = new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) { + using (var fs = new FileStream (path, System.IO.FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) { using (var reader = new BinaryReaderWith7BitEncodedInts (fs)) { lock (sharedSerializer) { return (T)sharedSerializer.Deserialize (reader); @@ -724,7 +546,7 @@ namespace MonoDevelop.Ide.TypeSystem var t = Counters.ParserService.ObjectSerialized.BeginTiming (path); try { - using (var fs = new FileStream (path, FileMode.Create, FileAccess.Write)) { + using (var fs = new FileStream (path, System.IO.FileMode.Create, FileAccess.Write)) { using (var writer = new BinaryWriterWith7BitEncodedInts (fs)) { lock (sharedSerializer) { sharedSerializer.Serialize (writer, obj); @@ -805,2230 +627,86 @@ namespace MonoDevelop.Ide.TypeSystem } } - static void StoreProjectCache (Project project, ProjectContentWrapper wrapper) - { - if (!wrapper.WasChanged) - return; - string cacheDir = GetCacheDirectory (project, true); - string fileName = Path.GetTempFileName (); - - SerializeObject (fileName, wrapper.Content.RemoveAssemblyReferences (wrapper.Content.AssemblyReferences)); - - string cacheFile = Path.Combine (cacheDir, "completion.cache"); - - try { - if (File.Exists (cacheFile)) - File.Delete (cacheFile); - File.Move (fileName, cacheFile); - } catch (Exception e) { - LoggingService.LogError ("Error whil saving cache " + cacheFile, e); - } - - foreach (var extensionObject in wrapper.ExtensionObjects) { - StoreExtensionObject (cacheDir, extensionObject); - } - } - #endregion - - #region Project loading - - public static void Load (WorkspaceItem item) - { - using (Counters.ParserService.WorkspaceItemLoaded.BeginTiming ()) { - InternalLoad (item); - CleanupCache (); - } - } - static CancellationTokenSource loadCancellationSource = new CancellationTokenSource (); - static bool loadWs = false; - static void InternalLoad (WorkspaceItem item) - { - var ws = item as Workspace; - if (ws != null) { - loadWs = true; - loadCancellationSource.Cancel (); - loadCancellationSource = new CancellationTokenSource (); - foreach (WorkspaceItem it in ws.Items) - InternalLoad (it); - ws.ItemAdded += OnWorkspaceItemAdded; - ws.ItemRemoved += OnWorkspaceItemRemoved; - } else { - if (!loadWs) { - loadCancellationSource.Cancel (); - loadCancellationSource = new CancellationTokenSource (); - } - var solution = item as Solution; - if (solution != null) { - Parallel.ForEach (solution.GetAllProjects (), project => LoadProject (project)); - var list = projectContents.Values.ToList (); - Task.Run (delegate { - foreach (var wrapper in list) { - CheckModifiedFiles (wrapper.Project, wrapper.Project.Files.ToArray (), wrapper, loadCancellationSource.Token); - } - }); - - solution.SolutionItemAdded += OnSolutionItemAdded; - solution.SolutionItemRemoved += OnSolutionItemRemoved; - OnSolutionLoaded (new SolutionEventArgs (solution)); - } - } - } - - public static event EventHandler<SolutionEventArgs> SolutionLoaded; - - static void OnSolutionLoaded (SolutionEventArgs e) - { - var handler = SolutionLoaded; - if (handler != null) - handler (null, e); - } - - [Serializable] - public class UnresolvedAssemblyDecorator : IUnresolvedAssembly - { - readonly ProjectContentWrapper wrapper; - - IUnresolvedAssembly assembly { - get { - if (wrapper.OutputAssembly != null) - return wrapper.OutputAssembly; - return wrapper.Compilation.MainAssembly.UnresolvedAssembly; - } - } - - public UnresolvedAssemblyDecorator (ProjectContentWrapper wrapper) - { - this.wrapper = wrapper; - } - - #region IUnresolvedAssembly implementation - - public string AssemblyName { - get { - return assembly.AssemblyName; - } - } - - public string FullAssemblyName { - get { - return assembly.FullAssemblyName; - } - } - - public string Location { - get { - return assembly.Location; - } - } - - public IEnumerable<IUnresolvedAttribute> AssemblyAttributes { - get { - return assembly.AssemblyAttributes; - } - } - - public IEnumerable<IUnresolvedAttribute> ModuleAttributes { - get { - return assembly.ModuleAttributes; - } - } - - public IEnumerable<IUnresolvedTypeDefinition> TopLevelTypeDefinitions { - get { - return assembly.TopLevelTypeDefinitions; - } - } - - #endregion - - #region IAssemblyReference implementation - - public IAssembly Resolve (ITypeResolveContext context) - { - return assembly.Resolve (context); - } - - #endregion - - } - - [Serializable] - public class ProjectContentWrapper - { - readonly Dictionary<Type, object> extensionObjects = new Dictionary<Type, object> (); - List<ProjectContentWrapper> referencedWrappers = new List<ProjectContentWrapper>(); - List<UnresolvedAssemblyProxy> referencedAssemblies = new List<UnresolvedAssemblyProxy>(); - internal IProjectContent _content; - internal bool referencesConnected; - - /* - static bool GetReferencesConnected (ProjectContentWrapper pcw, HashSet<ProjectContentWrapper> wrapper) - { - if (wrapper.Contains (pcw)) - return true; - wrapper.Add (pcw); - return pcw.referencesConnected && pcw.referencedWrappers.All (w => GetReferencesConnected (w, wrapper)); - }*/ - - public IProjectContent Content { - get { - if (!referencesConnected) { - EnsureReferencesAreLoaded (); - } - return _content; - } - private set { - if (value == null) - throw new InvalidOperationException ("Project content can't be null"); - _content = value; - } - } - - /// <summary> - /// Gets the extension objects attached to the content wrapper. - /// </summary> - public IEnumerable<object> ExtensionObjects { - get { - return extensionObjects.Values; - } - } - - /// <summary> - /// Updates an extension object for the wrapper. Note that only one extension object of a certain - /// type may be stored inside the project content wrapper. - /// - /// The extension objects need to be serializable and are stored in the project cache on project unload. - /// </summary> - public void UpdateExtensionObject (object ext) - { - if (ext == null) - throw new ArgumentNullException ("ext"); - extensionObjects [ext.GetType ()] = ext; - } - - /// <summary> - /// Gets a specific extension object. This may lazy load an existing extension object from disk, - /// if called the first time and a serialized extension object exists. - /// </summary> - /// <returns> - /// The extension object. Or null, if no extension object of the specified type was registered. - /// </returns> - /// <typeparam name='T'> - /// The type of the extension object. - /// </typeparam> - public T GetExtensionObject<T> () where T : class - { - object result; - if (extensionObjects.TryGetValue (typeof(T), out result)) - return (T)result; - - string cacheDir = GetCacheDirectory (Project); - if (cacheDir == null) - return default(T); - - try { - string fileName = Path.Combine (cacheDir, typeof(T).FullName + ".cache"); - if (File.Exists (fileName)) { - var deserialized = DeserializeObject<T> (fileName); - extensionObjects [typeof(T)] = deserialized; - return deserialized; - } - } catch (Exception) { - Console.WriteLine ("Can't deserialize :" + typeof(T).FullName); - } - - return default (T); - } - - List<Action<IProjectContent>> loadActions = new List<Action<IProjectContent>> (); - - public void RunWhenLoaded (Action<IProjectContent> act) - { - lock (updateContentLock) { - var lazyProjectLoader = _content as LazyProjectLoader; - if (loadActions != null) { - lock (loadActions) { - if (lazyProjectLoader != null && !lazyProjectLoader.ContextTask.IsCompleted) { - loadActions.Add (act); - return; - } - } - } - } - act (Content); - } - - void ClearCachedCompilations () - { - // Need to clear this compilation & all compilations that reference this directly or indirectly - var stack = new Stack<ProjectContentWrapper> (); - stack.Push (this); - var cleared = new HashSet<ProjectContentWrapper> (); - while (stack.Count > 0) { - var cur = stack.Pop (); - if (cleared.Contains (cur)) - continue; - cleared.Add (cur); - cur.compilation = null; - foreach (var project in cur.ReferencedProjects) { - var projectContentWrapper = GetProjectContentWrapper (project); - if (projectContentWrapper != null) - stack.Push (projectContentWrapper); - } - } - } - - readonly object updateContentLock = new object (); - - void RunLoadActions () - { - if (loadActions == null) - return; - Action<IProjectContent>[] actions; - lock (loadActions) { - actions = loadActions.ToArray (); - loadActions = null; - } - foreach (var action in actions) - action (Content); - } - - public void UpdateContent (Func<IProjectContent, IProjectContent> updateFunc) - { - LazyProjectLoader lazyProjectLoader; - lock (updateContentLock) { - lazyProjectLoader = _content as LazyProjectLoader; - if (lazyProjectLoader != null) { - lazyProjectLoader.ContextTask.Wait (); - } - _content = updateFunc (_content); - ClearCachedCompilations (); - WasChanged = true; - if (lazyProjectLoader != null && !(_content is LazyProjectLoader)) { - RunLoadActions (); - } - } - } - - public void InformFileRemoved (ParsedFileEventArgs e) - { - var handler = FileRemoved; - if (handler != null) - handler (this, e); - } - - public void InformFileAdded (ParsedFileEventArgs e) - { - var handler = FileAdded; - if (handler != null) - handler (this, e); - } - - public EventHandler<ParsedFileEventArgs> FileAdded; - public EventHandler<ParsedFileEventArgs> FileRemoved; - public bool WasChanged; - [NonSerialized] - ICompilation compilation; - - public ICompilation Compilation { - get { - lock (updateContentLock) { - if (compilation == null) { - compilation = Content.CreateCompilation (); - } - return compilation; - } - } - } - - public Project Project { - get; - private set; - } - - [NonSerialized] - int loadOperationDepth = 0; - [NonSerialized] - readonly object loadOperationLocker = new object (); - - internal void BeginLoadOperation () - { - lock (loadOperationLocker) { - loadOperationDepth++; - if (loadOperationDepth == 1) - UpdateLoadState (); - } - } - - internal void EndLoadOperation () - { - lock (loadOperationLocker) { - if (loadOperationDepth > 0) { - loadOperationDepth--; - } - if (loadOperationDepth == 0) - UpdateLoadState (); - } - } - - bool isLoaded; - public bool IsLoaded { - get { - return isLoaded; - } - } - - [NonSerialized] - CancellationTokenSource src; - - internal void CancelLoad () - { - if (src != null) - src.Cancel (); - } - - void UpdateLoadState () - { - bool wasLoaded = isLoaded; - isLoaded = loadOperationDepth == 0 && referencesConnected && !referencedAssemblies.Any (a => a.InLoad); - if (isLoaded && !wasLoaded) - OnLoad (EventArgs.Empty); - } - - internal void RequestLoad () - { - BeginLoadOperation (); - EnsureReferencesAreLoaded (); - CancelLoad (); - src = new CancellationTokenSource (); - var token = src.Token; - //Task.Factory.StartNew (delegate { - try { - foreach (var asm in referencedAssemblies.ToArray ()) { - if (token.IsCancellationRequested) - break; - var ctxLoader = asm.CtxLoader; - if (ctxLoader != null) - ctxLoader.EnsureAssemblyLoaded (); - } - } finally { - EndLoadOperation (); - } - //}); - } - - public event EventHandler Loaded; - - protected virtual void OnLoad (EventArgs e) - { - var handler = Loaded; - if (handler != null) - handler (this, e); - } - - [NonSerialized] - internal LazyAssemblyLoader OutputAssembly; - - internal bool UpdateTrackedOutputAssembly (FilePath fileName) - { - if (File.Exists (fileName)) { - OutputAssembly = new LazyAssemblyLoader (fileName, null); - // a clean operation could remove the assembly, thefore we need to load it. - OutputAssembly.EnsureAssemblyLoaded (); - return true; - } - return false; - } - - public ProjectContentWrapper (Project project) - { - if (project == null) - throw new ArgumentNullException ("project"); - this.Project = project; - var lazyProjectLoader = new LazyProjectLoader (this); - this.Content = lazyProjectLoader; - } - - public IEnumerable<Project> ReferencedProjects { - get { - return Project.GetReferencedItems (ConfigurationSelector.Default).OfType<DotNetProject> (); - } - } - - class LazyProjectLoader : IProjectContent - { - readonly ProjectContentWrapper wrapper; - readonly Task<IProjectContent> contextTask; - - public Task<IProjectContent> ContextTask { - get { - return contextTask; - } - } - object contentLock = new object (); - IProjectContent contentWithReferences; - public IProjectContent Content { - get { - lock (contentLock) { - if (References != null) { - return contentWithReferences ?? (contentWithReferences = contextTask.Result.AddAssemblyReferences (References)); - } - return contextTask.Result; - } - } - } - - List<IAssemblyReference> references; - public List<IAssemblyReference> References { - get { - return references; - } - set { - lock (contentLock) { - references = value; - } - } - } - - public LazyProjectLoader (ProjectContentWrapper wrapper) - { - this.wrapper = wrapper; - contextTask = Task.Factory.StartNew (delegate { - try { - this.wrapper.BeginLoadOperation (); - var p = this.wrapper.Project; - var context = LoadProjectCache (p); - - var assemblyName = p.ParentSolution != null ? p.GetOutputFileName (p.ParentSolution.DefaultConfigurationSelector).FileNameWithoutExtension : p.Name; - if (string.IsNullOrEmpty (assemblyName)) - assemblyName = p.Name; - - if (context != null) { - return context.SetAssemblyName (assemblyName) ?? context; - } - - context = new MonoDevelopProjectContent (p); - context = context.SetLocation (p.FileName); - context = context.SetAssemblyName (assemblyName); - - QueueParseJob (this.wrapper); - return context; - } finally { - this.wrapper.EndLoadOperation (); - } - }); - } - - static IProjectContent LoadProjectCache (Project project) - { - string cacheDir = GetCacheDirectory (project); - if (cacheDir == null) - return null; - - var cacheFile = Path.Combine (cacheDir, "completion.cache"); - if (!File.Exists (cacheFile)) - return null; - try { - var cache = DeserializeObject<IProjectContent> (cacheFile); - var monoDevelopProjectContent = cache as MonoDevelopProjectContent; - if (monoDevelopProjectContent != null) - monoDevelopProjectContent.Project = project; - return cache; - } catch (Exception e) { - LoggingService.LogWarning ("Error while reading completion cache, regenerating", e); - Directory.Delete (cacheDir, true); - return null; - } - } - - #region IAssemblyReference implementation - - IAssembly IAssemblyReference.Resolve (ITypeResolveContext context) - { - return Content.Resolve (context); - } - - #endregion - - #region IUnresolvedAssembly implementation - - string IUnresolvedAssembly.AssemblyName { - get { - return Content.AssemblyName; - } - } - - string IUnresolvedAssembly.FullAssemblyName { - get { - return Content.FullAssemblyName; - } - } - - string IUnresolvedAssembly.Location { - get { - return Content.Location; - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.AssemblyAttributes { - get { - return Content.AssemblyAttributes; - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.ModuleAttributes { - get { - return Content.ModuleAttributes; - } - } - - IEnumerable<IUnresolvedTypeDefinition> IUnresolvedAssembly.TopLevelTypeDefinitions { - get { - return Content.TopLevelTypeDefinitions; - } - } - - #endregion - - #region IProjectContent implementation - - string IProjectContent.ProjectFileName { get { return Content.ProjectFileName; } } - - IUnresolvedFile IProjectContent.GetFile (string fileName) - { - return Content.GetFile (fileName); - } - - ICompilation IProjectContent.CreateCompilation () - { - return Content.CreateCompilation (); - } - - public ICompilation CreateCompilation (ISolutionSnapshot solutionSnapshot) - { - return Content.CreateCompilation (solutionSnapshot); - } - - IProjectContent IProjectContent.SetAssemblyName (string newAssemblyName) - { - return Content.SetAssemblyName (newAssemblyName); - } - - IProjectContent IProjectContent.SetLocation (string newLocation) - { - return Content.SetLocation (newLocation); - } - - IProjectContent IProjectContent.AddAssemblyReferences (IEnumerable<IAssemblyReference> references) - { - return Content.AddAssemblyReferences (references); - } - - IProjectContent IProjectContent.AddAssemblyReferences (params IAssemblyReference[] references) - { - return Content.AddAssemblyReferences (references); - } - - IProjectContent IProjectContent.RemoveAssemblyReferences (IEnumerable<IAssemblyReference> references) - { - return Content.RemoveAssemblyReferences (references); - } - - IProjectContent IProjectContent.RemoveAssemblyReferences (params IAssemblyReference[] references) - { - return Content.RemoveAssemblyReferences (references); - } - #pragma warning disable 618 - IProjectContent IProjectContent.UpdateProjectContent (IUnresolvedFile oldFile, IUnresolvedFile newFile) - { - return Content.UpdateProjectContent (oldFile, newFile); - } - - public IProjectContent UpdateProjectContent (IEnumerable<IUnresolvedFile> oldFiles, IEnumerable<IUnresolvedFile> newFiles) - { - return Content.UpdateProjectContent (oldFiles, newFiles); - } - #pragma warning restore 618 - - public IProjectContent AddOrUpdateFiles (IEnumerable<IUnresolvedFile> newFiles) - { - return Content.AddOrUpdateFiles (newFiles); - } - - public IProjectContent AddOrUpdateFiles (params IUnresolvedFile[] newFiles) - { - return Content.AddOrUpdateFiles (newFiles); - } - - IEnumerable<IUnresolvedFile> IProjectContent.Files { - get { - return Content.Files; - } - } - - IEnumerable<IAssemblyReference> IProjectContent.AssemblyReferences { - get { - return Content.AssemblyReferences; - } - } - - IProjectContent IProjectContent.SetProjectFileName (string newProjectFileName) - { - return Content.SetProjectFileName (newProjectFileName); - } - - IProjectContent IProjectContent.RemoveFiles (IEnumerable<string> fileNames) - { - return Content.RemoveFiles (fileNames); - } - - IProjectContent IProjectContent.RemoveFiles (params string[] fileNames) - { - return Content.RemoveFiles (fileNames); - } - - #endregion - - object compilerSettings; - - public IProjectContent SetCompilerSettings (object compilerSettings) - { - this.compilerSettings = compilerSettings; - return this; - } - - public object CompilerSettings { - get { - return compilerSettings; - } - } - } - - bool HasCyclicRefs (ProjectContentWrapper wrapper, HashSet<Project> nonCyclicCache) - { - if (nonCyclicCache.Contains (wrapper.Project)) - return false; - nonCyclicCache.Add (wrapper.Project); - foreach (var referencedProject in wrapper.ReferencedProjects) { - ProjectContentWrapper w; - if (referencedProject == Project || referencedProject == wrapper.Project || projectContents.TryGetValue (referencedProject, out w) && HasCyclicRefs (w, nonCyclicCache)) { - return true; - } - } - return false; - } - - List<UnresolvedAssemblyProxy> LoadReferencedAssemblies (DotNetProject netProject) - { - var newReferencedAssemblies = new List<UnresolvedAssemblyProxy> (); - try { - foreach (string file in netProject.GetReferencedAssemblies (IdeApp.IsInitialized ? IdeApp.Workspace.ActiveConfiguration : ConfigurationSelector.Default, false)) { - string fileName; - if (!Path.IsPathRooted (file)) { - fileName = Path.Combine (Path.GetDirectoryName (netProject.FileName), file); - } - else { - fileName = Path.GetFullPath (file); - } - var ctx = LoadAssemblyContext (fileName); - if (ctx != null) { - newReferencedAssemblies.Add (ctx); - ctx.Loaded += HandleReferencedProjectInLoadChange; - } - else { - LoggingService.LogWarning ("TypeSystemService: Can't load assembly context for:" + file); - } - } - } - catch (Exception e) { - LoggingService.LogError ("Error while getting assembly references", e); - } - return newReferencedAssemblies; - } - - public void EnsureReferencesAreLoaded () - { - lock (projectContentLock) { - if (referencesConnected) - return; - compilation = null; - referencesConnected = true; - var netProject = Project as DotNetProject; - if (netProject == null) - return; - try { - var contexts = new List<IAssemblyReference> (); - var nonCyclicCache = new HashSet<Project> (); - foreach (var referencedWrapper in referencedWrappers) { - referencedWrapper.Loaded -= HandleReferencedProjectInLoadChange; - } - var newReferencedWrappers = new List<ProjectContentWrapper> (); - foreach (var referencedProject in ReferencedProjects) { - ProjectContentWrapper wrapper; - if (projectContents.TryGetValue (referencedProject, out wrapper)) { - if (HasCyclicRefs (wrapper, nonCyclicCache)) - continue; - wrapper.Loaded += HandleReferencedProjectInLoadChange; - newReferencedWrappers.Add (wrapper); - contexts.Add (new UnresolvedAssemblyDecorator (wrapper)); - } - } - this.referencedWrappers = newReferencedWrappers; - UnresolvedAssemblyProxy ctx; - // Add mscorlib reference - var config = IdeApp.Workspace != null ? netProject.GetConfiguration (IdeApp.Workspace.ActiveConfiguration) as DotNetProjectConfiguration : null; - bool noStdLib = false; - if (config != null) { - var parameters = config.CompilationParameters as DotNetCompilerParameters; - if (parameters != null) { - noStdLib = parameters.NoStdLib; - } - } - if (!noStdLib && netProject.TargetRuntime != null && netProject.TargetRuntime.AssemblyContext != null) { - var corLibRef = netProject.TargetRuntime.AssemblyContext.GetAssemblyForVersion (typeof(object).Assembly.FullName, null, netProject.TargetFramework); - if (corLibRef != null) { - ctx = LoadAssemblyContext (corLibRef.Location); - if (ctx != null) - contexts.Add (ctx); - } - } - // Get the assembly references throught the project, since it may have custom references - foreach (var asm in referencedAssemblies) { - asm.Loaded += HandleReferencedProjectInLoadChange; - } - var newReferencedAssemblies = LoadReferencedAssemblies (netProject); - contexts.AddRange (newReferencedAssemblies); - referencedAssemblies = newReferencedAssemblies; - bool changed = WasChanged; - var lazyProjectLoader = Content as LazyProjectLoader; - if (lazyProjectLoader != null) { - lazyProjectLoader.References = contexts; - } - else { - UpdateContent (c => c.RemoveAssemblyReferences (Content.AssemblyReferences).AddAssemblyReferences (contexts)); - } - WasChanged = changed; - } catch (Exception e) { - if (netProject.TargetRuntime == null) { - LoggingService.LogError ("Target runtime was null: " + Project.Name); - } else if (netProject.TargetRuntime.AssemblyContext == null) { - LoggingService.LogError ("Target runtime assembly context was null: " + Project.Name); - } - LoggingService.LogError ("Error while reloading all references of project: " + Project.Name, e); - } finally { - UpdateLoadState (); - } - } - } - static readonly object reconnectLock = new object(); - public void ReconnectAssemblyReferences () - { - var netProject = Project as DotNetProject; - if (netProject == null) - return; - lock (reconnectLock) { - CancelLoad (); - this.referencesConnected = false; - RequestLoad (); - } - } - - void HandleReferencedProjectInLoadChange (object sender, EventArgs e) - { - UpdateLoadState (); - } - - internal void Unload () - { - CancelLoad (); - foreach (var asm in referencedAssemblies) { - asm.Loaded -= HandleReferencedProjectInLoadChange; - } - foreach (var wrapper in referencedWrappers) { - wrapper.Loaded -= HandleReferencedProjectInLoadChange; - } - loadActions = null; - foreach (var wrapper in referencedWrappers) { - wrapper.Loaded -= HandleReferencedProjectInLoadChange; - } - referencedWrappers.Clear (); - referencedAssemblies.Clear (); - Loaded = null; - Content = new CSharpProjectContent (); - } - } - - static readonly object projectContentLock = new object (); - static readonly Dictionary<Project, ProjectContentWrapper> projectContents = new Dictionary<Project, ProjectContentWrapper> (); - - public static ProjectContentWrapper LoadProject (Project project) + + internal static Microsoft.CodeAnalysis.Document GetCodeAnalysisDocument (Microsoft.CodeAnalysis.DocumentId analysisDocument, CancellationToken cancellationToken = default (CancellationToken)) { - if (IncLoadCount (project) != 1) - return null; - lock (projectContentLock) { - if (projectContents.ContainsKey (project)) - return null; - try { - Counters.ParserService.ProjectsLoaded++; - ProjectContentWrapper wrapper; - projectContents [project] = wrapper = new ProjectContentWrapper (project); - var dotNetProject = project as DotNetProject; - if (dotNetProject != null) - CheckProjectOutput (dotNetProject, false); - - project.FileAddedToProject += OnFileAdded; - project.FileRemovedFromProject += OnFileRemoved; - project.FileRenamedInProject += OnFileRenamed; - project.Modified += OnProjectModified; - - if (dotNetProject != null) { - StartFrameworkLookup (dotNetProject); - } - return wrapper; - } catch (Exception ex) { - LoggingService.LogError ("Parser database for project '" + project.Name + " could not be loaded", ex); - } - return null; + foreach (var w in Workspaces) { + var doc = w.GetDocument (analysisDocument, cancellationToken); + if (doc != null) + return doc; } - } - - public static Project GetProject (IEntity entity) - { - if (entity == null) - throw new ArgumentNullException ("entity"); - return GetProject (entity.ParentAssembly.UnresolvedAssembly.Location); - } - - public static Project GetProject (string location) - { - foreach (var wrapper in projectContents) - if (wrapper.Value.Compilation.MainAssembly.UnresolvedAssembly.Location == location) - return wrapper.Key; return null; } - #region Project modification handlers - - static void OnFileAdded (object sender, ProjectFileEventArgs args) - { - var project = (Project)sender; - foreach (ProjectFileEventInfo fargs in args) { - QueueParseJob (projectContents [project], new [] { fargs.ProjectFile }); - } - } - - static void OnFileRemoved (object sender, ProjectFileEventArgs args) - { - var project = (Project)sender; - foreach (ProjectFileEventInfo fargs in args) { - var wrapper = projectContents [project]; - var fileName = fargs.ProjectFile.Name; - var file = wrapper._content.GetFile (fileName); - if (file == null) - continue; - wrapper.UpdateContent (c => c.RemoveFiles (fileName)); - wrapper.InformFileRemoved (new ParsedFileEventArgs (file)); - - var tags = wrapper.GetExtensionObject <ProjectCommentTags> (); - if (tags != null) - tags.RemoveFile (wrapper.Project, fileName); - } - } - - static void OnFileRenamed (object sender, ProjectFileRenamedEventArgs args) - { - var project = (Project)sender; - foreach (ProjectFileRenamedEventInfo fargs in args) { - var content = projectContents [project]; - var file = content._content.GetFile (fargs.OldName); - if (file == null) - continue; - content.UpdateContent (c => c.RemoveFiles (fargs.OldName)); - content.InformFileRemoved (new ParsedFileEventArgs (file)); - - var tags = content.GetExtensionObject <ProjectCommentTags> (); - if (tags != null) - tags.RemoveFile (project, fargs.OldName); - - QueueParseJob (content, new [] { fargs.ProjectFile }); - } - } - - static void OnProjectModified (object sender, SolutionItemModifiedEventArgs args) + internal static void InformDocumentClose (Microsoft.CodeAnalysis.DocumentId analysisDocument, FilePath fileName) { - if (!args.Any (x => x.Hint == "TargetFramework" || x.Hint == "References")) - return; - var project = (Project)sender; - - ProjectContentWrapper wrapper; - projectContents.TryGetValue (project, out wrapper); - if (wrapper == null) - return; - wrapper.ReconnectAssemblyReferences (); - } - - #endregion - - internal static void Unload (WorkspaceItem item) - { - var ws = item as Workspace; - TrackFileChanges = false; - loadCancellationSource.Cancel (); - if (ws != null) { - foreach (WorkspaceItem it in ws.Items) - Unload (it); - ws.ItemAdded -= OnWorkspaceItemAdded; - ws.ItemRemoved -= OnWorkspaceItemRemoved; - projectContents.Clear (); - loadWs = false; - } else { - var solution = item as Solution; - if (solution != null) { - foreach (var project in solution.GetAllProjects ()) { - UnloadProject (project); - } - solution.SolutionItemAdded -= OnSolutionItemAdded; - solution.SolutionItemRemoved -= OnSolutionItemRemoved; - } - } + foreach (var w in Workspaces) { + if (w.GetOpenDocumentIds ().Contains (analysisDocument) ) + w.InformDocumentClose (analysisDocument, fileName); - cachedAssemblyContents.Clear (); - lock (parseQueueLock) { - parseQueueIndex.Clear (); - parseQueue.Clear (); } - TrackFileChanges = true; } - internal static void UnloadProject (Project project, bool skipProjectSerialization = false) + internal static void InformDocumentOpen (Microsoft.CodeAnalysis.DocumentId analysisDocument, TextEditor editor) { - if (DecLoadCount (project) != 0) - return; - Counters.ParserService.ProjectsLoaded--; - project.FileAddedToProject -= OnFileAdded; - project.FileRemovedFromProject -= OnFileRemoved; - project.FileRenamedInProject -= OnFileRenamed; - project.Modified -= OnProjectModified; - - ProjectContentWrapper wrapper; - lock (projectWrapperUpdateLock) { - if (!projectContents.TryGetValue (project, out wrapper)) + foreach (var w in Workspaces) { + if (w.Contains (analysisDocument.ProjectId)) { + w.InformDocumentOpen (analysisDocument, editor); return; - projectContents.Remove (project); - } - if (!skipProjectSerialization) - StoreProjectCache (project, wrapper); - OnProjectUnloaded (new ProjectUnloadEventArgs (project, wrapper)); - wrapper.Unload (); - } - - public static event EventHandler<ProjectUnloadEventArgs> ProjectUnloaded; - - static void OnProjectUnloaded (ProjectUnloadEventArgs e) - { - var handler = ProjectUnloaded; - if (handler != null) - handler (null, e); - } - - static void OnWorkspaceItemAdded (object s, WorkspaceItemEventArgs args) - { - Load (args.Item); - } - - static void OnWorkspaceItemRemoved (object s, WorkspaceItemEventArgs args) - { - Unload (args.Item); - } - - static void OnSolutionItemAdded (object sender, SolutionItemChangeEventArgs args) - { - var project = args.SolutionItem as Project; - if (project != null) { - var wrapper = LoadProject (project); - if (wrapper != null) { - var files = wrapper.Project.Files.ToArray (); - Task.Run (delegate { - CheckModifiedFiles (wrapper.Project, files, wrapper); - wrapper.RequestLoad (); - }); } } - } - - static void OnSolutionItemRemoved (object sender, SolutionItemChangeEventArgs args) - { - var project = args.SolutionItem as Project; - if (project != null) - UnloadProject (project); - } - - #endregion - - #region Reference Counting - - static readonly Dictionary<Project,int> loadCount = new Dictionary<Project,int> (); - static readonly object rwLock = new object (); - - static int DecLoadCount (Project ob) - { - lock (rwLock) { - int c; - if (loadCount.TryGetValue (ob, out c)) { - c--; - if (c == 0) - loadCount.Remove (ob); - else - loadCount [ob] = c; - return c; - } - LoggingService.LogError ("DecLoadCount: Object not registered."); - return 0; - } - } - - static int IncLoadCount (Project ob) - { - lock (rwLock) { - int c; - if (loadCount.TryGetValue (ob, out c)) { - c++; - loadCount [ob] = c; - return c; - } - loadCount [ob] = 1; - return 1; - } - } - - #endregion - - static bool GetXml (string baseName, TargetRuntime runtime, out FilePath xmlFileName) - { - try { - xmlFileName = LookupLocalizedXmlDoc (baseName); - if (!xmlFileName.IsNull) - return true; - } catch (Exception e) { - LoggingService.LogError ("Error while looking up XML docs.", e); - } - - if (MonoDevelop.Core.Platform.IsWindows) { - string windowsFileName = FindWindowsXmlDocumentation (baseName, runtime); - if (File.Exists (windowsFileName)) { - xmlFileName = windowsFileName; - return true; - } - } - - xmlFileName = ""; - return false; - } - - #region Lookup XML documentation - - // ProgramFilesX86 is broken on 32-bit WinXP, this is a workaround - static string GetProgramFilesX86 () - { - return Environment.GetFolderPath (IntPtr.Size == 8 ? - Environment.SpecialFolder.ProgramFilesX86 : Environment.SpecialFolder.ProgramFiles); - } - - static readonly string referenceAssembliesPath = Path.Combine (GetProgramFilesX86 (), @"Reference Assemblies\Microsoft\\Framework"); - static readonly string frameworkPath = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Windows), @"Microsoft.NET\Framework"); - - static string FindWindowsXmlDocumentation (string assemblyFileName, TargetRuntime runtime) - { - string fileName; - ClrVersion version = runtime != null && runtime.CustomFrameworks.Any () ? runtime.CustomFrameworks.First ().ClrVersion : ClrVersion.Default; - switch (version) { -// case "1.0": -// fileName = LookupLocalizedXmlDoc (Path.Combine (frameworkPath, "v1.0.3705", assemblyFileName)); -// break; - case ClrVersion.Net_1_1: - fileName = LookupLocalizedXmlDoc (Path.Combine (frameworkPath, "v1.1.4322", assemblyFileName)); - break; - case ClrVersion.Net_2_0: - case ClrVersion.Clr_2_1: - fileName = LookupLocalizedXmlDoc (Path.Combine (frameworkPath, "v2.0.50727", assemblyFileName)) - ?? LookupLocalizedXmlDoc (Path.Combine (referenceAssembliesPath, "v3.5")) - ?? LookupLocalizedXmlDoc (Path.Combine (referenceAssembliesPath, "v3.0")) - ?? LookupLocalizedXmlDoc (Path.Combine (referenceAssembliesPath, @".NETFramework\v3.5\Profile\Client")); - break; - default: - fileName = LookupLocalizedXmlDoc (Path.Combine (referenceAssembliesPath, @".NETFramework\v4.0", assemblyFileName)) - ?? LookupLocalizedXmlDoc (Path.Combine (frameworkPath, "v4.0.30319", assemblyFileName)); - break; - } - return fileName; - } - - static string LookupLocalizedXmlDoc (string fileName) - { - return XmlDocumentationProvider.LookupLocalizedXmlDoc (fileName); - } - - #endregion - - class UnresolvedAssemblyProxy : IUnresolvedAssembly - { - public readonly string FileName; - internal LazyAssemblyLoader CtxLoader; - - public IUnresolvedAssembly Ctx { - get { - return CtxLoader; - } - } - - public bool InLoad { - get { - return CtxLoader == null || CtxLoader.InLoad; - } - } - - public event EventHandler Loaded { - add { - var ctxLoader = CtxLoader; - if (ctxLoader != null) - ctxLoader.Loaded += value; - } - remove { - var ctxLoader = CtxLoader; - if (ctxLoader != null) - ctxLoader.Loaded -= value; - } - } - - public UnresolvedAssemblyProxy (string fileName) - { - if (fileName == null) - throw new ArgumentNullException ("fileName"); - this.FileName = fileName; - } - - #region IUnresolvedAssembly implementation - - string IUnresolvedAssembly.AssemblyName { - get { - return Ctx.AssemblyName; - } - } - - string IUnresolvedAssembly.FullAssemblyName { - get { - return Ctx.FullAssemblyName; - } - } - - string IUnresolvedAssembly.Location { - get { - return Ctx.Location; - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.AssemblyAttributes { - get { - return Ctx.AssemblyAttributes; - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.ModuleAttributes { - get { - return Ctx.ModuleAttributes; - } - } - - IEnumerable<IUnresolvedTypeDefinition> IUnresolvedAssembly.TopLevelTypeDefinitions { - get { - return Ctx.TopLevelTypeDefinitions; - } - } - - #endregion - - #region IAssemblyReference implementation - - IAssembly IAssemblyReference.Resolve (ITypeResolveContext context) - { - var ctx = Ctx; - if (ctx == null) - return null; - return ctx.Resolve (context); - } - - #endregion - - public override string ToString () - { - return string.Format ("[UnresolvedAssemblyProxy: FileName={0}]", FileName); - } - } - - internal class LazyAssemblyLoader : IUnresolvedAssembly - { - class LazyAssembly : IAssembly - { - readonly LazyAssemblyLoader loader; - readonly ITypeResolveContext context; - IAssembly assembly; - - IAssembly Assembly { - get { - lock (loader) { - if (assembly == null) { - loader.EnsureAssemblyLoaded (); - assembly = loader.assembly.Resolve (context); - } - return assembly; - } - } - } - - - public LazyAssembly (LazyAssemblyLoader loader, ITypeResolveContext context) - { - this.loader = loader; - this.context = context; - } - - #region IAssembly implementation - - bool IAssembly.InternalsVisibleTo (IAssembly assembly) - { - return Assembly.InternalsVisibleTo (assembly); - } - - ITypeDefinition IAssembly.GetTypeDefinition (TopLevelTypeName typeName) - { - return Assembly.GetTypeDefinition (typeName); - } - - IUnresolvedAssembly IAssembly.UnresolvedAssembly { - get { - return Assembly.UnresolvedAssembly; - } - } - - bool IAssembly.IsMainAssembly { - get { - return Assembly.IsMainAssembly; - } - } - - string IAssembly.AssemblyName { - get { - return Assembly.AssemblyName; - } - } - - string IAssembly.FullAssemblyName { - get { - return Assembly.FullAssemblyName; - } - } - - IList<IAttribute> IAssembly.AssemblyAttributes { - get { - return Assembly.AssemblyAttributes; - } - } - - IList<IAttribute> IAssembly.ModuleAttributes { - get { - return Assembly.ModuleAttributes; - } - } - - INamespace IAssembly.RootNamespace { - get { - return Assembly.RootNamespace; - } - } - - IEnumerable<ITypeDefinition> IAssembly.TopLevelTypeDefinitions { - get { - return Assembly.TopLevelTypeDefinitions; - } - } - - #endregion - - #region ICompilationProvider implementation - - ICompilation ICompilationProvider.Compilation { - get { - return Assembly.Compilation; - } - } - - #endregion - - } - - #region IAssemblyReference implementation - - IAssembly IAssemblyReference.Resolve (ITypeResolveContext context) - { - if (assembly != null) - return assembly.Resolve (context); - return new LazyAssembly (this, context); - } - - #endregion - - #region IUnresolvedAssembly implementation - - readonly object assemblyLock = new object (); - - string IUnresolvedAssembly.AssemblyName { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.AssemblyName; - } - } - } - - string IUnresolvedAssembly.FullAssemblyName { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.FullAssemblyName; - } - } - } - - string IUnresolvedAssembly.Location { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.Location; - } - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.AssemblyAttributes { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.AssemblyAttributes; - } - } - } - - IEnumerable<IUnresolvedAttribute> IUnresolvedAssembly.ModuleAttributes { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.ModuleAttributes; - } - } - } - - IEnumerable<IUnresolvedTypeDefinition> IUnresolvedAssembly.TopLevelTypeDefinitions { - get { - lock (assemblyLock) { - EnsureAssemblyLoaded (); - return assembly.TopLevelTypeDefinitions; - } - } - } - - #endregion - - readonly string fileName; - readonly string cache; - IUnresolvedAssembly assembly; - - readonly object asmLocker = new object (); - internal void EnsureAssemblyLoaded () - { - lock (asmLocker) { - if (assembly != null) - return; - var loadedAssembly = LoadAssembly (); - if (loadedAssembly == null) { - LoggingService.LogWarning ("Assembly " + fileName + " could not be loaded cleanly."); - assembly = new DefaultUnresolvedAssembly (fileName); - } else { - assembly = loadedAssembly; - } - - OnLoad (EventArgs.Empty); - } - } - - public override string ToString () - { - return string.Format ("[LazyAssemblyLoader: fileName={0}, assembly={1}]", fileName, assembly); - } - - public bool InLoad { - get { - return assembly == null; - } - } - - public event EventHandler Loaded; - - protected virtual void OnLoad (EventArgs e) - { - var handler = Loaded; - if (handler != null) - handler (this, e); - } - - public LazyAssemblyLoader (string fileName, string cache) - { - this.fileName = fileName; - this.cache = cache; - } - - - IUnresolvedAssembly LoadAssembly () - { - var assemblyPath = cache != null ? Path.Combine (cache, "assembly.data") : null; - var assemblyTag = cache != null ? Path.Combine (cache, "assembly.tag") : null; - try { - if (assemblyPath != null && assemblyTag != null && File.Exists (assemblyPath) && File.Exists (assemblyTag)) { - var deserializedAssembly = DeserializeObject <IUnresolvedAssembly> (assemblyPath); - if (deserializedAssembly != null) { - return deserializedAssembly; - } - } - } catch (Exception) { - } - IUnresolvedAssembly result; - try { - var loader = new IkvmLoader (); - loader.IncludeInternalMembers = true; - loader.DocumentationProvider = new CombinedDocumentationProvider (fileName); - result = loader.LoadAssemblyFile (fileName); - } catch (Exception e) { - LoggingService.LogError ("Can't convert assembly: " + fileName, e); - return null; - } - - if (cache != null) { - var writeTime = File.GetLastWriteTimeUtc (fileName); - SerializeObject (assemblyPath, result); - SerializeObject (assemblyTag, new AssemblyTag (writeTime)); - } - return result; - } - } - - [Serializable] - class CombinedDocumentationProvider : IDocumentationProvider - { - readonly string fileName; - [NonSerialized] - IDocumentationProvider baseProvider; - - public IDocumentationProvider BaseProvider { - get { - if (baseProvider == null) { - FilePath xmlDocFile; - if (GetXml (fileName, null, out xmlDocFile)) { - try { - baseProvider = new XmlDocumentationProvider (xmlDocFile); - } catch (Exception ex) { - LoggingService.LogWarning ("Ignoring error while reading xml doc from " + xmlDocFile, ex); - } - } - if (baseProvider == null) - baseProvider = new MonoDocDocumentationProvider (); - } - return baseProvider; - } - } - - public CombinedDocumentationProvider (string fileName) - { - this.fileName = fileName; - } - - #region IDocumentationProvider implementation - - public DocumentationComment GetDocumentation (IEntity entity) - { - var provider = BaseProvider; - return provider != null ? provider.GetDocumentation (entity) : null; - } - - #endregion - - } - - static readonly object assemblyContextLock = new object (); - - static UnresolvedAssemblyProxy LoadAssemblyContext (FilePath fileName) - { - CanonicalizePath (ref fileName); - - UnresolvedAssemblyProxy loadedContext; - if (cachedAssemblyContents.TryGetValue (fileName, out loadedContext)) { - return loadedContext; - } - if (!File.Exists (fileName)) - return null; - lock (assemblyContextLock) { - if (cachedAssemblyContents.TryGetValue (fileName, out loadedContext)) { - CheckModifiedFile (loadedContext); - return loadedContext; - } - - string cache = GetCacheDirectory (fileName, true); - - try { - var result = new UnresolvedAssemblyProxy (fileName); - result.CtxLoader = new LazyAssemblyLoader (fileName, cache); - CheckModifiedFile (result); - var newcachedAssemblyContents = new Dictionary<string, UnresolvedAssemblyProxy> (cachedAssemblyContents); - newcachedAssemblyContents [fileName] = result; - cachedAssemblyContents = newcachedAssemblyContents; - OnAssemblyLoaded (new AssemblyLoadedEventArgs (result.CtxLoader)); - return result; - } catch (Exception ex) { - LoggingService.LogError ("Error loading assembly " + fileName, ex); - return null; - } + if (!gotDocumentRequestError) { + gotDocumentRequestError = true; + LoggingService.LogWarning ("Can't open requested document : " + analysisDocument + ":" + editor.FileName); } } - internal static event EventHandler<AssemblyLoadedEventArgs> AssemblyLoaded; - - static void OnAssemblyLoaded (AssemblyLoadedEventArgs e) - { - var handler = AssemblyLoaded; - if (handler != null) - handler (null, e); - } - - public static IUnresolvedAssembly LoadAssemblyContext (TargetRuntime runtime, TargetFramework fx, string fileName) + internal static void InformDocumentOpen (Microsoft.CodeAnalysis.Workspace ws, Microsoft.CodeAnalysis.DocumentId analysisDocument, TextEditor editor) { - if (File.Exists (fileName)) - return LoadAssemblyContext (fileName); - var corLibRef = runtime.AssemblyContext.GetAssemblyForVersion (fileName, null, fx); - return corLibRef == null ? null : LoadAssemblyContext (corLibRef.Location); + ((MonoDevelopWorkspace)ws).InformDocumentOpen (analysisDocument, editor); } - public static IProjectContent GetProjectContext (Project project) - { - if (project == null) - throw new ArgumentNullException ("project"); - var content = GetProjectContentWrapper (project); - if (content == null) - return null; - return content.Content; - } + static bool gotDocumentRequestError = false; - public static ICompilation GetCompilation (Project project) + public static Microsoft.CodeAnalysis.ProjectId GetProjectId (MonoDevelop.Projects.Project project) { if (project == null) throw new ArgumentNullException ("project"); - var content = GetProjectContentWrapper (project); - if (content == null) - return null; - return content.Compilation; - } - - public static ICompilation GetCompilation (SystemAssembly assembly, ICompilation compilation) - { - var ctx = LoadAssemblyContext (assembly.Location); - var list = compilation.ReferencedAssemblies.Select (r => r.UnresolvedAssembly).ToList (); - list.Add (compilation.MainAssembly.UnresolvedAssembly); - var result = new SimpleCompilation (ctx, list); - return result; - } - - static IEnumerable<SystemAssembly> GetFrameworkAssemblies (DotNetProject netProject) - { - var assemblies = new Dictionary<string, SystemAssembly> (); - foreach (var assembly in netProject.AssemblyContext.GetAssemblies ()) { - SystemAssembly existing; - if (assemblies.TryGetValue (assembly.Name, out existing)) { - Version v1, v2; - if (!Version.TryParse (existing.Version, out v1)) - continue; - if (!Version.TryParse (assembly.Version, out v2)) - continue; - if (v1 > v2) - continue; + foreach (var w in Workspaces) { + var projectId = w.GetProjectId (project); + if (projectId != null) { + return projectId; } - assemblies [assembly.Name] = assembly; - } - return assemblies.Values; - } - - class FrameworkTask - { - public int RetryCount { get; set; } - - public Task<FrameworkLookup> Task { get; set; } - } - - readonly static Dictionary<string, FrameworkTask> frameworkLookup = new Dictionary<string, FrameworkTask> (); - - static void StartFrameworkLookup (DotNetProject netProject) - { - if (netProject == null) - throw new ArgumentNullException ("netProject"); - lock (frameworkLookup) { - FrameworkTask result; - if (netProject.TargetFramework == null) - return; - var frameworkName = netProject.TargetFramework.Name; - if (!frameworkLookup.TryGetValue (frameworkName, out result)) - frameworkLookup [frameworkName] = result = new FrameworkTask (); - if (result.Task != null) - return; - result.Task = Task.Factory.StartNew (delegate { - return GetFrameworkLookup (netProject); - }); - } - } - - public static bool TryGetFrameworkLookup (DotNetProject project, out FrameworkLookup lookup) - { - lock (frameworkLookup) { - FrameworkTask result; - if (frameworkLookup.TryGetValue (project.TargetFramework.Name, out result)) { - if (!result.Task.IsCompleted) { - lookup = null; - return false; - } - lookup = result.Task.Result; - return true; - } - } - lookup = null; - return false; - } - - public static bool RecreateFrameworkLookup (DotNetProject netProject) - { - lock (frameworkLookup) { - FrameworkTask result; - var frameworkName = netProject.TargetFramework.Name; - if (!frameworkLookup.TryGetValue (frameworkName, out result)) - return false; - if (result.RetryCount > 5) { - LoggingService.LogError ("Can't create framework lookup for:" + frameworkName); - return false; - } - result.RetryCount++; - LoggingService.LogInfo ("Trying to recreate framework lookup for {0}, try {1}.", frameworkName, result.RetryCount); - result.Task = null; - StartFrameworkLookup (netProject); - return true; } + return null; } - static FrameworkLookup GetFrameworkLookup (DotNetProject netProject) + public static Microsoft.CodeAnalysis.Document GetCodeAnysisDocument (Microsoft.CodeAnalysis.DocumentId docId, CancellationToken cancellationToken = default (CancellationToken)) { - FrameworkLookup result; - string fileName; - var cache = GetCacheDirectory (netProject.TargetFramework); - fileName = Path.Combine (cache, "FrameworkLookup_" + FrameworkLookup.CurrentVersion + ".dat"); - try { - if (File.Exists (fileName)) { - result = FrameworkLookup.Load (fileName); - if (result != null) { - return result; - } + if (docId == null) + throw new ArgumentNullException ("docId"); + foreach (var w in Workspaces) { + var documentId = w.GetDocument (docId, cancellationToken); + if (documentId != null) { + return documentId; } - } catch (Exception e) { - LoggingService.LogWarning ("Can't read framework cache - recreating...", e); - } - - try { - using (var creator = FrameworkLookup.Create (fileName)) { - foreach (var assembly in GetFrameworkAssemblies (netProject)) { - var ctx = LoadAssemblyContext (assembly.Location); - foreach (var type in ctx.Ctx.GetAllTypeDefinitions ()) { - if (!type.IsPublic) - continue; - creator.AddLookup (assembly.Package.Name, assembly.FullName, type); - } - } - } - } catch (Exception e) { - LoggingService.LogError ("Error while storing framework lookup", e); - return FrameworkLookup.Empty; - } - - try { - result = FrameworkLookup.Load (fileName); - return result; - } catch (Exception e) { - LoggingService.LogError ("Error loading framework lookup", e); - return FrameworkLookup.Empty; } + return null; } - public static ProjectContentWrapper GetProjectContentWrapper (Project project) + public static MonoDevelop.Projects.Project GetMonoProject (Microsoft.CodeAnalysis.Project project) { if (project == null) throw new ArgumentNullException ("project"); - ProjectContentWrapper content; - if (projectContents.TryGetValue (project, out content)) - return content; - // in case of outdated projects try to get the most recent project wrapper. - foreach (var cnt in projectContents) { - if (cnt.Key.FileName == project.FileName) - return cnt.Value; - } - return null; - } - - public static IProjectContent GetContext (FilePath file, string mimeType, string text) - { - using (var reader = new StringReader (text)) { - var parsedDocument = ParseFile (file, mimeType, reader); - - var content = new CSharpProjectContent (); - return content.AddOrUpdateFiles (parsedDocument.ParsedFile); - } - } - - static Dictionary<string, UnresolvedAssemblyProxy> cachedAssemblyContents = new Dictionary<string, UnresolvedAssemblyProxy> (); - - /// <summary> - /// Force the update of a project context. Note: This method blocks the thread. - /// It was just implemented for use inside unit tests. - /// </summary> - public static void ForceUpdate (ProjectContentWrapper context) - { - CheckModifiedFiles (); - while (!context.IsLoaded) { - Thread.Sleep (10); - } - } - - #region Parser queue - - static bool threadRunning; - - public static IProgressMonitorFactory ParseProgressMonitorFactory { - get; - set; - } - - class InternalProgressMonitor - : ProgressMonitor - { - public InternalProgressMonitor () - { - StartParseOperation (); - } - - public override void Dispose () - { - EndParseOperation (); - } - } - - internal static ProgressMonitor GetParseProgressMonitor () - { - var mon = ParseProgressMonitorFactory != null ? ParseProgressMonitorFactory.CreateProgressMonitor () : new ProgressMonitor (); - - return new AggregatedProgressMonitor (mon, new InternalProgressMonitor ()); - } - - static readonly Queue<ParsingJob> parseQueue = new Queue<ParsingJob> (); - - class ParsingJob - { - public ProjectContentWrapper Context; - public IEnumerable<ProjectFile> FileList; - // public Action<string, IProgressMonitor> ParseCallback; - public void Run (ProgressMonitor monitor, CancellationToken token) - { - TypeSystemParserNode node = null; - TypeSystemParser parser = null; - var tags = Context.GetExtensionObject <ProjectCommentTags> (); - try { - Context.BeginLoadOperation (); - var parsedFiles = new List<Tuple<ParsedDocument, IUnresolvedFile>> (); - foreach (var file in (FileList ?? Context.Project.Files)) { - if (token.IsCancellationRequested) - return; - var fileName = file.FilePath; - if (filesSkippedInParseThread.Any (f => f == fileName)) { - continue; - } - if (node == null || !node.CanParse (fileName, file.BuildAction)) { - var newNode = GetTypeSystemParserNode (DesktopService.GetMimeTypeForUri (fileName), file.BuildAction); - var newParser = newNode != null ? newNode.Parser : null; - if (newParser == null) - continue; - node = newNode; - parser = newParser; - } - - if (parser == null || !File.Exists (fileName)) - continue; - ParsedDocument parsedDocument; - try { - parsedDocument = parser.Parse (false, fileName, Context.Project); - } catch (Exception e) { - LoggingService.LogError ("Error while parsing " + fileName, e); - continue; - } - if (token.IsCancellationRequested) - return; - if (tags != null) - tags.UpdateTags (Context.Project, parsedDocument.FileName, parsedDocument.TagComments); - if (token.IsCancellationRequested) - return; - parsedFiles.Add (Tuple.Create (parsedDocument, Context._content.GetFile (fileName))); - } - Context.UpdateContent (c => c.AddOrUpdateFiles (parsedFiles.Where (f => (f.Item1.Flags & ParsedDocumentFlags.NonSerializable) != ParsedDocumentFlags.NonSerializable).Select (p => p.Item1.ParsedFile))); - foreach (var file in parsedFiles) { - if (token.IsCancellationRequested) - return; - if (file.Item2 != null) - Context.InformFileRemoved (new ParsedFileEventArgs (file.Item2)); - var parsedDocument = file.Item1; - if ((parsedDocument.Flags & ParsedDocumentFlags.NonSerializable) != ParsedDocumentFlags.NonSerializable) - Context.InformFileAdded (new ParsedFileEventArgs (parsedDocument.ParsedFile)); - } - } finally { - Context.EndLoadOperation (); + foreach (var w in Workspaces) { + var documentId = w.GetMonoProject (project); + if (documentId != null) { + return documentId; } } + return null; } - static void UpdateProjectCommentTasks (ProjectContentWrapper context, ParsedDocument parsedDocument) - { - var tags = context.GetExtensionObject <ProjectCommentTags> (); - if (tags != null) // When tags are not there they're updated first time the tasks are requested. - tags.UpdateTags (context.Project, parsedDocument.FileName, parsedDocument.TagComments); - } - // public static event EventHandler<ProjectFileEventArgs> FileParsed; - static readonly object parseQueueLock = new object (); - static readonly AutoResetEvent parseEvent = new AutoResetEvent (false); - static readonly ManualResetEvent queueEmptied = new ManualResetEvent (true); - static bool trackingFileChanges; - - public static bool TrackFileChanges { - get { - return trackingFileChanges; - } - set { - lock (parseQueueLock) { - if (value != trackingFileChanges) { - trackingFileChanges = value; - if (value) - StartParserThread (); - } - } - } - } - - static int parseStatus; - - public static bool IsParsing { - get { return parseStatus > 0; } - } - - static readonly Dictionary<ProjectContentWrapper, ParsingJob> parseQueueIndex = new Dictionary<ProjectContentWrapper, ParsingJob> (); - - internal static int PendingJobCount { - get { - lock (parseQueueLock) { - return parseQueueIndex.Count; - } - } - } - - static void QueueParseJob (ProjectContentWrapper context, IEnumerable<ProjectFile> fileList = null) - { - var job = new ParsingJob { - Context = context, - FileList = fileList - }; - lock (parseQueueLock) { - RemoveParseJob (context); - context.BeginLoadOperation (); - parseQueueIndex [context] = job; - parseQueue.Enqueue (job); - parseEvent.Set (); - - if (parseQueueIndex.Count == 1) - queueEmptied.Reset (); - } - } - - static bool WaitForParseJob (int timeout = 5000) - { - return parseEvent.WaitOne (timeout, true); - } - - static ParsingJob DequeueParseJob () - { - lock (parseQueueLock) { - if (parseQueue.Count > 0) { - var job = parseQueue.Dequeue (); - parseQueueIndex.Remove (job.Context); - return job; - } - return null; - } - } - - internal static void WaitForParseQueue () - { - queueEmptied.WaitOne (); - } - - static void RemoveParseJob (ProjectContentWrapper project) - { - lock (parseQueueLock) { - ParsingJob job; - if (parseQueueIndex.TryGetValue (project, out job)) { - parseQueueIndex.Remove (project); - project.EndLoadOperation (); - } - } - } - - static void StartParserThread () - { - lock (parseQueueLock) { - if (!threadRunning) { - threadRunning = true; - var t = new Thread (new ThreadStart (ParserUpdateThread)); - t.Name = "Background parser"; - t.IsBackground = true; - t.Priority = ThreadPriority.AboveNormal; - t.Start (); - } - } - } - - static void ParserUpdateThread () - { - try { - while (trackingFileChanges) { - WaitForParseJob (); -// CheckModifiedFiles (); - if (trackingFileChanges) - ConsumeParsingQueue (); - } - } catch (Exception ex) { - LoggingService.LogError ("Unhandled error in parsing thread", ex); - } - lock (parseQueueLock) { - threadRunning = false; - if (trackingFileChanges) - StartParserThread (); - } - } - - static bool IsFileModified (ProjectFile file, IUnresolvedFile parsedFile) - { - if (parsedFile == null || !parsedFile.LastWriteTime.HasValue) - return true; - try { - return File.GetLastWriteTimeUtc (file.FilePath) != parsedFile.LastWriteTime; - } catch (Exception) { - return true; - } - } - - static void CheckModifiedFiles (Project project, ProjectFile[] projectFiles, ProjectContentWrapper content, CancellationToken token = default (CancellationToken)) - { - if (token.IsCancellationRequested) { - return; - } -// Console.WriteLine ("add modified file check for :" + project.Name); - content.RunWhenLoaded (delegate(IProjectContent cnt) { - try { -// Console.WriteLine ("check for " + project.Name); - content.BeginLoadOperation (); - var modifiedFiles = new List<ProjectFile> (); - var oldFileNewFile = new List<Tuple<ProjectFile, IUnresolvedFile>> (); - foreach (var file in projectFiles) { - if (token.IsCancellationRequested) { - return; - } - if (file.BuildAction == null) - continue; - // if the file is already inside the content a parser exists for it, if not check if it can be parsed. - var oldFile = cnt.GetFile (file.Name); - oldFileNewFile.Add (Tuple.Create (file, oldFile)); - } - - // This is disk intensive and slow - oldFileNewFile.RemoveAll (t => !IsFileModified (t.Item1, t.Item2)); - - foreach (var v in oldFileNewFile) { - var file = v.Item1; - var oldFile = v.Item2; - if (oldFile == null) { - var parser = TypeSystemService.GetParser (DesktopService.GetMimeTypeForUri (file.Name), file.BuildAction); - if (parser == null) - continue; - } - modifiedFiles.Add (file); - } - var tags = content.GetExtensionObject <ProjectCommentTags> (); - - // check if file needs to be removed from project content - foreach (var file in cnt.Files) { - if (token.IsCancellationRequested) { - return; - } - if (project.GetProjectFile (file.FileName) == null) { - content.UpdateContent (c => c.RemoveFiles (file.FileName)); - content.InformFileRemoved (new ParsedFileEventArgs (file)); - if (tags != null) - tags.RemoveFile (project, file.FileName); - } - } - if (token.IsCancellationRequested) { - return; - } - if (modifiedFiles.Count > 0) { - QueueParseJob (content, modifiedFiles); - WaitForParseJob (); - } - } catch (Exception e) { - LoggingService.LogError ("Exception in check modified files.", e); - } finally { - content.EndLoadOperation (); - } - }); - } - - /// <summary> - /// Used to store meta data information about the assembly. - /// </summary> - [Serializable] - class AssemblyTag - { - public DateTime LastWriteTimeUTC { get; set; } - - public AssemblyTag (DateTime lastWriteTimeUTC) - { - this.LastWriteTimeUTC = lastWriteTimeUTC; - } - } - - static void CheckModifiedFile (UnresolvedAssemblyProxy context) - { - try { - string cache = GetCacheDirectory (context.FileName); - if (cache == null) - return; - var assemblyDataDirectory = Path.Combine (cache, "assembly.tag"); - var writeTime = File.GetLastWriteTimeUtc (context.FileName); - var cacheTime = File.Exists (assemblyDataDirectory) ? DeserializeObject<AssemblyTag> (assemblyDataDirectory) : new AssemblyTag (writeTime); - if (writeTime != cacheTime.LastWriteTimeUTC) { - cache = GetCacheDirectory (context.FileName); - if (cache != null) { - try { - // Files will be reloaded by the lazy loader - File.Delete (assemblyDataDirectory); - File.Delete (Path.Combine (cache, "assembly.data")); - } catch { - } - context.CtxLoader = new LazyAssemblyLoader (context.FileName, cache); - } - } - } catch (Exception e) { - LoggingService.LogError ("Error while updating assembly " + context.FileName, e); - } - } - - static void CheckModifiedFiles () - { - Queue<KeyValuePair<Project, ProjectContentWrapper>> list; - - lock (projectContentLock) { - list = new Queue<KeyValuePair<Project, ProjectContentWrapper>> (projectContents); - } - - while (list.Count > 0) { - var readydb = list.Dequeue (); - var files = readydb.Key.Files.ToArray (); - CheckModifiedFiles (readydb.Key, files, readydb.Value); - } - - var assemblyList = new Queue<KeyValuePair<string, UnresolvedAssemblyProxy>> (cachedAssemblyContents); - - while (assemblyList.Count > 0) { - var readydb = assemblyList.Dequeue (); - CheckModifiedFile (readydb.Value); - } - } - - static void ConsumeParsingQueue () - { - int pending = 0; - ProgressMonitor monitor = null; - var token = loadCancellationSource.Token; - StartParseOperation (); - try { - do { - if (pending > 5 && monitor == null) { - monitor = GetParseProgressMonitor (); - monitor.BeginTask (GettextCatalog.GetString ("Generating database"), 0); - } - var job = DequeueParseJob (); - if (job != null) { - try { - job.Run (monitor, token); - } catch (Exception ex) { - if (monitor == null) - monitor = GetParseProgressMonitor (); - monitor.ReportError (null, ex); - } finally { - job.Context.EndLoadOperation (); - } - } - - if (token.IsCancellationRequested) - break; - pending = PendingJobCount; - } while (pending > 0); - queueEmptied.Set (); - } finally { - if (monitor != null) - monitor.Dispose (); - EndParseOperation (); - } - } - - #endregion - - } - - sealed class AssemblyLoadedEventArgs : EventArgs - { - public readonly TypeSystemService.LazyAssemblyLoader Assembly; - - public AssemblyLoadedEventArgs (TypeSystemService.LazyAssemblyLoader assembly) - { - this.Assembly = assembly; - } - } - - public sealed class ProjectUnloadEventArgs : EventArgs - { - public readonly Project Project; - public readonly TypeSystemService.ProjectContentWrapper Wrapper; - - public ProjectUnloadEventArgs (Project project, TypeSystemService.ProjectContentWrapper wrapper) - { - this.Project = project; - this.Wrapper = wrapper; - } } } - - diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs new file mode 100644 index 0000000000..e623dbf213 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs @@ -0,0 +1,488 @@ +// +// TypeSystemService.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Microsoft.CodeAnalysis; +using MonoDevelop.Core; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Mono.Addins; +using MonoDevelop.Projects; +using System.IO; +using System.Linq; +using System.Collections.Immutable; +using System.Collections.Concurrent; + +namespace MonoDevelop.Ide.TypeSystem +{ + +// static class MonoDevelopWorkspaceFeatures +// { +// static FeaturePack pack; +// +// public static FeaturePack Features { +// get { +// if (pack == null) +// Interlocked.CompareExchange (ref pack, ComputePack (), null); +// return pack; +// } +// } +// +// static FeaturePack ComputePack () +// { +// var assemblies = new List<Assembly> (); +// var workspaceCoreAssembly = typeof(Workspace).Assembly; +// assemblies.Add (workspaceCoreAssembly); +// +// LoadAssembly (assemblies, "Microsoft.CodeAnalysis.CSharp.Workspaces"); +// //LoadAssembly (assemblies, "Microsoft.CodeAnalysis.VisualBasic.Workspaces"); +// +// var catalogs = assemblies.Select (a => new System.ComponentModel.Composition.Hosting.AssemblyCatalog (a)); +// +// return new MefExportPack (catalogs); +// } +// +// static void LoadAssembly (List<Assembly> assemblies, string assemblyName) +// { +// try { +// var loadedAssembly = Assembly.Load (assemblyName); +// assemblies.Add (loadedAssembly); +// } catch (Exception e) { +// LoggingService.LogWarning ("Couldn't load assembly:" + assemblyName, e); +// } +// } +// } + + public static partial class TypeSystemService + { + static readonly MonoDevelopWorkspace emptyWorkspace; + + static ConcurrentBag<MonoDevelopWorkspace> workspaces = new ConcurrentBag<MonoDevelopWorkspace>(); + + static ImmutableArray<MonoDevelopWorkspace> Workspaces { + get { + return workspaces.ToImmutableArray (); + } + } + public static ImmutableArray<Microsoft.CodeAnalysis.Workspace> AllWorkspaces { + get { + return workspaces.ToImmutableArray<Microsoft.CodeAnalysis.Workspace> (); + } + } + + + internal static MonoDevelopWorkspace GetWorkspace (MonoDevelop.Projects.Solution solution) + { + if (solution == null) + throw new ArgumentNullException ("solution"); + foreach (var ws in Workspaces) { + if (ws.MonoDevelopSolution == solution) + return ws; + } + return emptyWorkspace; + } + + public static Microsoft.CodeAnalysis.Workspace Workspace { + get { + var solution = IdeApp.ProjectOperations?.CurrentSelectedSolution; + if (solution == null) + return emptyWorkspace; + return GetWorkspace (solution); + } + } + + + public static void NotifyFileChange (string fileName, string text) + { + foreach (var ws in Workspaces) + ws.UpdateFileContent (fileName, text); + } + + internal static Microsoft.CodeAnalysis.Workspace Load (WorkspaceItem item, IProgressMonitor progressMonitor, bool loadInBackground = true) + { + using (Counters.ParserService.WorkspaceItemLoaded.BeginTiming ()) { + var workspace = new MonoDevelopWorkspace (); + if (!(item is MonoDevelop.Projects.Workspace)) + workspaces.Add (workspace); + workspace.ShowStatusIcon (); + InternalLoad (item, progressMonitor, workspace, loadInBackground).ContinueWith (t => { + workspace.HideStatusIcon (); + }); + return workspace; + } + } + + static Task InternalLoad (MonoDevelop.Projects.WorkspaceItem item, IProgressMonitor progressMonitor, MonoDevelopWorkspace workspace, bool loadInBackground) + { + var ws = item as MonoDevelop.Projects.Workspace; + if (ws != null) { + Action loadAction = () => { + var newWorkspace = new MonoDevelopWorkspace (); + foreach (var it in ws.Items) + InternalLoad (it, progressMonitor, newWorkspace, false); + workspaces.Add (workspace); + ws.ItemAdded += OnWorkspaceItemAdded; + ws.ItemRemoved += OnWorkspaceItemRemoved; + }; + if (loadInBackground) { + return Task.Run (loadAction); + } else { + loadAction (); + } + } else { + var solution = item as MonoDevelop.Projects.Solution; + if (solution != null) { + Action loadAction = () => { + workspace.TryLoadSolution (solution/*, progressMonitor*/); + solution.SolutionItemAdded += OnSolutionItemAdded; + solution.SolutionItemRemoved += OnSolutionItemRemoved; + }; + if (loadInBackground) { + return Task.Run (loadAction); + } else { + loadAction (); + } + } + } + return Task.FromResult(false); + } + + internal static void Unload (MonoDevelop.Projects.WorkspaceItem item) + { + var ws = item as MonoDevelop.Projects.Workspace; + if (ws != null) { + foreach (var it in ws.Items) + Unload (it); + ws.ItemAdded -= OnWorkspaceItemAdded; + ws.ItemRemoved -= OnWorkspaceItemRemoved; + MonoDocDocumentationProvider.ClearCommentCache (); + } else { + var solution = item as MonoDevelop.Projects.Solution; + if (solution != null) { + MonoDevelopWorkspace result = GetWorkspace (solution); + if (result != emptyWorkspace) { + workspaces = new ConcurrentBag<MonoDevelopWorkspace> (Workspaces.Where (w => w != result)); + result.Dispose (); + } + solution.SolutionItemAdded -= OnSolutionItemAdded; + solution.SolutionItemRemoved -= OnSolutionItemRemoved; + if (solution.ParentWorkspace == null) + MonoDocDocumentationProvider.ClearCommentCache (); + } + } + } + + public static DocumentId GetDocumentId (MonoDevelop.Projects.Project project, string fileName) + { + if (project == null) + throw new ArgumentNullException ("project"); + if (fileName == null) + throw new ArgumentNullException ("fileName"); + fileName = FileService.GetFullPath (fileName); + foreach (var w in Workspaces) { + var projectId = w.GetProjectId (project); + if (projectId != null) + return w.GetDocumentId (projectId, fileName); + } + return null; + } + + public static DocumentId GetDocumentId (Microsoft.CodeAnalysis.Workspace workspace, MonoDevelop.Projects.Project project, string fileName) + { + if (project == null) + throw new ArgumentNullException ("project"); + if (fileName == null) + throw new ArgumentNullException ("fileName"); + fileName = FileService.GetFullPath (fileName); + var projectId = ((MonoDevelopWorkspace)workspace).GetProjectId (project); + if (projectId != null) + return ((MonoDevelopWorkspace)workspace).GetDocumentId (projectId, fileName); + return null; + } + + + public static DocumentId GetDocumentId (ProjectId projectId, string fileName) + { + if (projectId == null) + throw new ArgumentNullException ("projectId"); + if (fileName == null) + throw new ArgumentNullException ("fileName"); + foreach (var w in Workspaces) { + if (w.Contains (projectId)) + return w.GetDocumentId (projectId, fileName); + } + return null; + } + + public static IEnumerable<DocumentId> GetDocuments (string fileName) + { + if (fileName == null) + throw new ArgumentNullException ("fileName"); + fileName = FileService.GetFullPath (fileName); + foreach (var w in Workspaces) { + foreach (var projectId in w.CurrentSolution.ProjectIds) { + var docId = w.GetDocumentId (projectId, fileName); + if (docId != null) + yield return docId; + } + } + } + + public static Microsoft.CodeAnalysis.Project GetCodeAnalysisProject (MonoDevelop.Projects.Project project) + { + if (project == null) + throw new ArgumentNullException ("project"); + foreach (var w in Workspaces) { + var projectId = w.GetProjectId (project); + if (projectId != null) + return w.CurrentSolution.GetProject (projectId); + } + return null; + } + + public static async Task<Compilation> GetCompilationAsync (MonoDevelop.Projects.Project project, CancellationToken cancellationToken = default(CancellationToken)) + { + if (project == null) + throw new ArgumentNullException ("project"); + foreach (var w in Workspaces) { + var projectId = w.GetProjectId (project); + if (projectId == null) + continue; + var roslynProject = w.CurrentSolution.GetProject (projectId); + if (roslynProject == null) + continue; + return await roslynProject.GetCompilationAsync (cancellationToken).ConfigureAwait (false); + } + return null; + } + + static void OnWorkspaceItemAdded (object s, MonoDevelop.Projects.WorkspaceItemEventArgs args) + { + Task.Run (() => TypeSystemService.Load (args.Item, null)); + } + + static void OnWorkspaceItemRemoved (object s, MonoDevelop.Projects.WorkspaceItemEventArgs args) + { + Unload (args.Item); + } + + static void OnSolutionItemAdded (object sender, MonoDevelop.Projects.SolutionItemChangeEventArgs args) + { + var project = args.SolutionItem as MonoDevelop.Projects.Project; + if (project != null) { + var ws = GetWorkspace (project.ParentSolution); + ws.AddProject (project); + } + } + + static void OnSolutionItemRemoved (object sender, MonoDevelop.Projects.SolutionItemChangeEventArgs args) + { + var project = args.SolutionItem as MonoDevelop.Projects.Project; + var solution = sender as MonoDevelop.Projects.Solution; + if (project != null) { + var ws = GetWorkspace (solution); + ws.RemoveProject (project); + } + } + + #region Tracked project handling + static readonly List<string> outputTrackedProjects = new List<string> (); + + static void IntitializeTrackedProjectHandling () + { + AddinManager.AddExtensionNodeHandler ("/MonoDevelop/TypeSystem/OutputTracking", delegate (object sender, ExtensionNodeEventArgs args) { + var projectType = ((TypeSystemOutputTrackingNode)args.ExtensionNode).ProjectType; + switch (args.Change) { + case ExtensionChange.Add: + outputTrackedProjects.Add (projectType); + break; + case ExtensionChange.Remove: + outputTrackedProjects.Remove (projectType); + break; + } + }); + if (IdeApp.ProjectOperations != null) + IdeApp.ProjectOperations.EndBuild += HandleEndBuild; + if (IdeApp.Workspace != null) + IdeApp.Workspace.ActiveConfigurationChanged += HandleActiveConfigurationChanged; + + + } + + static void HandleEndBuild (object sender, BuildEventArgs args) + { + var project = args.SolutionItem as DotNetProject; + if (project == null) + return; + CheckProjectOutput (project, true); + } + + static void HandleActiveConfigurationChanged (object sender, EventArgs e) + { + foreach (var pr in IdeApp.ProjectOperations.CurrentSelectedSolution.GetAllProjects ()) { + var project = pr as DotNetProject; + if (project != null) + CheckProjectOutput (project, true); + } + } + + internal static bool IsOutputTrackedProject (DotNetProject project) + { + if (project == null) + throw new ArgumentNullException ("project"); + return project.GetProjectTypes ().Any (p => outputTrackedProjects.Contains (p, StringComparer.OrdinalIgnoreCase)); + } + + static void CheckProjectOutput (DotNetProject project, bool autoUpdate) + { + if (project == null) + throw new ArgumentNullException ("project"); + if (IsOutputTrackedProject (project)) { + var fileName = project.GetOutputFileName (IdeApp.Workspace.ActiveConfiguration); + if (!File.Exists (fileName)) + return; + FileService.NotifyFileChanged (fileName); + if (autoUpdate) { + // update documents + foreach (var openDocument in IdeApp.Workbench.Documents) { + openDocument.ReparseDocument (); + } + } + } + } + #endregion + +// TODO: Port framework lookup to NR6 +// #region FrameworkLookup +// class FrameworkTask +// { +// public int RetryCount { get; set; } +// +// public Task<FrameworkLookup> Task { get; set; } +// } +// +// readonly static Dictionary<string, FrameworkTask> frameworkLookup = new Dictionary<string, FrameworkTask> (); +// +// static void StartFrameworkLookup (DotNetProject netProject) +// { +// if (netProject == null) +// throw new ArgumentNullException ("netProject"); +// lock (frameworkLookup) { +// FrameworkTask result; +// if (netProject.TargetFramework == null) +// return; +// var frameworkName = netProject.TargetFramework.Name; +// if (!frameworkLookup.TryGetValue (frameworkName, out result)) +// frameworkLookup [frameworkName] = result = new FrameworkTask (); +// if (result.Task != null) +// return; +// result.Task = Task.Factory.StartNew (delegate { +// return GetFrameworkLookup (netProject); +// }); +// } +// } +// +// public static bool TryGetFrameworkLookup (DotNetProject project, out FrameworkLookup lookup) +// { +// lock (frameworkLookup) { +// FrameworkTask result; +// if (frameworkLookup.TryGetValue (project.TargetFramework.Name, out result)) { +// if (!result.Task.IsCompleted) { +// lookup = null; +// return false; +// } +// lookup = result.Task.Result; +// return true; +// } +// } +// lookup = null; +// return false; +// } +// +// public static bool RecreateFrameworkLookup (DotNetProject netProject) +// { +// lock (frameworkLookup) { +// FrameworkTask result; +// var frameworkName = netProject.TargetFramework.Name; +// if (!frameworkLookup.TryGetValue (frameworkName, out result)) +// return false; +// if (result.RetryCount > 5) { +// LoggingService.LogError ("Can't create framework lookup for:" + frameworkName); +// return false; +// } +// result.RetryCount++; +// LoggingService.LogInfo ("Trying to recreate framework lookup for {0}, try {1}.", frameworkName, result.RetryCount); +// result.Task = null; +// StartFrameworkLookup (netProject); +// return true; +// } +// } +// +// static FrameworkLookup GetFrameworkLookup (DotNetProject netProject) +// { +// FrameworkLookup result; +// string fileName; +// var cache = GetCacheDirectory (netProject.TargetFramework); +// fileName = Path.Combine (cache, "FrameworkLookup_" + FrameworkLookup.CurrentVersion + ".dat"); +// try { +// if (File.Exists (fileName)) { +// result = FrameworkLookup.Load (fileName); +// if (result != null) { +// return result; +// } +// } +// } catch (Exception e) { +// LoggingService.LogWarning ("Can't read framework cache - recreating...", e); +// } +// +// try { +// using (var creator = FrameworkLookup.Create (fileName)) { +// foreach (var assembly in GetFrameworkAssemblies (netProject)) { +// var ctx = LoadAssemblyContext (assembly.Location); +// foreach (var type in ctx.Ctx.GetAllTypeDefinitions ()) { +// if (!type.IsPublic) +// continue; +// creator.AddLookup (assembly.Package.Name, assembly.FullName, type); +// } +// } +// } +// } catch (Exception e) { +// LoggingService.LogError ("Error while storing framework lookup", e); +// return FrameworkLookup.Empty; +// } +// +// try { +// result = FrameworkLookup.Load (fileName); +// return result; +// } catch (Exception e) { +// LoggingService.LogError ("Error loading framework lookup", e); +// return FrameworkLookup.Empty; +// } +// } +// #endregion + } + +}
\ No newline at end of file |