diff options
Diffstat (limited to 'main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs')
-rw-r--r-- | main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs | 2680 |
1 files changed, 179 insertions, 2501 deletions
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; - } } } - - |