From ab95b18a3f600253648ee45551f5469c090bbac4 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 28 Mar 2012 18:44:12 +0200 Subject: [PATCH] Refactored parser service. --- SharpDevelop.sln | 2 +- .../Project/Src/CSharpSemanticHighlighter.cs | 13 +- .../Src/Completion/CSharpCompletionBinding.cs | 8 +- .../CSharpFormattingStrategy.cs | 3 +- .../Project/Src/Parser/CSharpSymbolSearch.cs | 6 +- .../Project/Src/Parser/Parser.cs | 19 +- .../Src/Project/CSharpMyNamespaceBuilder.cs | 2 +- .../Project/Src/Refactoring/IssueManager.cs | 7 +- .../Project/Src/Refactoring/SDScript.cs | 3 +- .../CSharpBinding/Tests/MockTextEditor.cs | 7 +- .../CppBinding/CppBinding/CppBinding.csproj | 1 - .../CppBinding/Project/DependencyRelation.cs | 44 +- .../CppBinding/Project/MultiDictionary.cs | 160 --- .../XamlOutlineContentHost.xaml.cs | 5 +- .../XamlBinding/XamlBinding/XamlParser.cs | 15 +- .../XamlBinding/XamlSymbolSearch.cs | 5 +- .../Src/AvalonEditViewContent.cs | 4 +- .../AvalonEdit.AddIn/Src/CodeEditor.cs | 10 +- .../AvalonEdit.AddIn/Src/CodeEditorView.cs | 2 +- .../ContextActions/EditorActionsProvider.cs | 4 +- .../Src/DefaultChangeWatcher.cs | 4 +- .../AvalonEdit.AddIn/Src/QuickClassBrowser.cs | 2 +- .../Src/Snippets/CodeSnippet.cs | 2 +- .../GitAddIn/Src/OverlayIconManager.cs | 4 +- .../OverlayIconManager.cs | 4 +- .../Project/ICSharpCode.SharpDevelop.addin | 30 +- .../Project/ICSharpCode.SharpDevelop.csproj | 13 +- .../Commands/SymbolUnderCaretMenuCommand.cs | 2 +- .../Editor/IDocumentBaseVersionProvider.cs | 3 +- .../Src/Editor/ToolTipRequestEventArgs.cs | 2 +- .../Project/Src/Gui/Dialogs/GotoDialog.cs | 6 +- .../Project/Src/Gui/Dialogs/NewFileDialog.cs | 3 +- .../IDEOptions/TaskListOptionsl.xaml.cs | 5 +- .../ReferenceDialog/GacReferencePanel.cs | 2 +- .../Src/Gui/Dialogs/WordCountDialog.cs | 4 +- src/Main/Base/Project/Src/Gui/IWorkbench.cs | 5 - .../Project/Src/Gui/Pads/DefinitionViewPad.cs | 7 +- .../Commands/ReferenceFolderNodeCommands.cs | 4 +- .../Src/Gui/Pads/PropertyPad/PropertyPad.cs | 2 +- .../Src/Gui/Pads/TaskList/TaskListPad.cs | 6 +- .../Gui/Pads/TaskList/TaskListPadCommands.cs | 5 +- .../Project/Src/Gui/WorkbenchSingleton.cs | 72 +- .../Templates/Project/ProjectDescriptor.cs | 2 +- .../Project/Src/Project/AbstractProject.cs | 11 + .../Project/Src/Project/CompilableProject.cs | 13 + .../Base/Project/Src/Project/CustomTool.cs | 2 +- src/Main/Base/Project/Src/Project/IProject.cs | 12 + .../Src/Project/ProjectChangeWatcher.cs | 3 +- .../Refactoring/EditorRefactoringContext.cs | 10 +- .../Src/Services/File/FileChangeWatcher.cs | 3 +- .../Project/Src/Services/File/FileService.cs | 6 +- .../Project/Src/Services/File/IFileService.cs | 55 + .../Project/Src/Services/File/OpenedFile.cs | 4 +- .../Base/Project/Src/Services/IMessageLoop.cs | 93 ++ .../NavigationService/NavigationService.cs | 2 +- .../Services/ParserService/DomAssemblyName.cs | 11 +- .../IGlobalAssemblyCacheService.cs | 30 + .../Src/Services/ParserService/IParser.cs | 23 +- .../Services/ParserService/IParserService.cs | 291 +++++ .../ParseInformationEventArgs.cs | 13 +- .../ParserService/ParseProjectContent.cs | 44 +- .../ParserService/ParsedFileListener.cs | 18 - .../Services/ParserService/ParserService.cs | 1026 ----------------- .../ParseableFileContentFinder.cs | 6 +- .../Services/ProjectService/ProjectService.cs | 4 +- .../FindReferenceService.cs | 35 +- .../FindReferencesAndRenameHelper.cs | 19 +- src/Main/Base/Project/Src/Services/SD.cs | 18 + .../Project/Src/Services/Tasks/TaskService.cs | 6 +- .../Base/Project/Src/Util/ExtensionMethods.cs | 2 +- .../Base/Project/Src/Util/MultiDictionary.cs | 115 ++ .../Project/Src/Util/ReactiveExtensions.cs | 16 +- .../Base/Project/Src/Util/WorkerThread.cs | 112 -- .../Project/Src/Util/WpfSynchronizeInvoke.cs | 120 -- .../Parser}/Fusion.cs | 0 .../Parser/GlobalAssemblyCacheService.cs} | 28 +- .../Parser}/ParserDescriptor.cs | 2 +- .../Parser}/ParserDoozer.cs | 2 +- src/Main/SharpDevelop/Parser/ParserService.cs | 359 ++++++ .../SharpDevelop/Parser/ParserServiceEntry.cs | 330 ++++++ src/Main/SharpDevelop/Sda/CallHelper.cs | 2 +- src/Main/SharpDevelop/SharpDevelop.csproj | 45 +- .../SharpDevelop/{Gui => Startup}/App.xaml | 2 +- .../SharpDevelop/{Gui => Startup}/App.xaml.cs | 2 +- .../{ => Startup}/SharpDevelopMain.cs | 4 +- .../{Gui => Startup}/SplashScreen.cs | 2 +- .../{Gui => }/Workbench/AvalonDockLayout.cs | 3 +- .../{Gui => }/Workbench/AvalonPadContent.cs | 3 +- .../Workbench/AvalonWorkbenchWindow.cs | 3 +- .../Workbench/ChooseLayoutCommand.cs | 7 +- .../Workbench/DispatcherMessageLoop.cs | 146 +++ .../SharpDevelop/Workbench/FileService.cs | 61 + .../Workbench/FullScreenEnabledWindow.cs | 2 +- .../Workbench/LayoutConfiguration.cs | 9 +- .../{Gui => }/Workbench/SDStatusBar.cs | 3 +- .../Workbench/SingleInstanceHelper.cs | 15 +- .../{Gui => }/Workbench/StatusBarService.cs | 5 +- .../{Gui => }/Workbench/WorkbenchStartup.cs | 20 +- .../{Gui => }/Workbench/WpfWorkbench.cs | 29 +- .../{Gui => }/Workbench/WpfWorkbench.xaml | 8 +- 100 files changed, 1892 insertions(+), 1832 deletions(-) delete mode 100644 src/AddIns/BackendBindings/CppBinding/CppBinding/Project/MultiDictionary.cs create mode 100644 src/Main/Base/Project/Src/Services/File/IFileService.cs create mode 100644 src/Main/Base/Project/Src/Services/IMessageLoop.cs create mode 100644 src/Main/Base/Project/Src/Services/ParserService/IGlobalAssemblyCacheService.cs create mode 100644 src/Main/Base/Project/Src/Services/ParserService/IParserService.cs delete mode 100644 src/Main/Base/Project/Src/Services/ParserService/ParsedFileListener.cs delete mode 100644 src/Main/Base/Project/Src/Services/ParserService/ParserService.cs create mode 100644 src/Main/Base/Project/Src/Util/MultiDictionary.cs delete mode 100644 src/Main/Base/Project/Src/Util/WorkerThread.cs delete mode 100644 src/Main/Base/Project/Src/Util/WpfSynchronizeInvoke.cs rename src/Main/{Base/Project/Src/Services/ParserService => SharpDevelop/Parser}/Fusion.cs (100%) rename src/Main/{Base/Project/Src/Services/ParserService/GacInterop.cs => SharpDevelop/Parser/GlobalAssemblyCacheService.cs} (82%) rename src/Main/{Base/Project/Src/Services/ParserService/Doozer => SharpDevelop/Parser}/ParserDescriptor.cs (97%) rename src/Main/{Base/Project/Src/Services/ParserService/Doozer => SharpDevelop/Parser}/ParserDoozer.cs (96%) create mode 100644 src/Main/SharpDevelop/Parser/ParserService.cs create mode 100644 src/Main/SharpDevelop/Parser/ParserServiceEntry.cs rename src/Main/SharpDevelop/{Gui => Startup}/App.xaml (87%) rename src/Main/SharpDevelop/{Gui => Startup}/App.xaml.cs (90%) rename src/Main/SharpDevelop/{ => Startup}/SharpDevelopMain.cs (98%) rename src/Main/SharpDevelop/{Gui => Startup}/SplashScreen.cs (98%) rename src/Main/SharpDevelop/{Gui => }/Workbench/AvalonDockLayout.cs (99%) rename src/Main/SharpDevelop/{Gui => }/Workbench/AvalonPadContent.cs (97%) rename src/Main/SharpDevelop/{Gui => }/Workbench/AvalonWorkbenchWindow.cs (99%) rename src/Main/SharpDevelop/{Gui => }/Workbench/ChooseLayoutCommand.cs (97%) create mode 100644 src/Main/SharpDevelop/Workbench/DispatcherMessageLoop.cs create mode 100644 src/Main/SharpDevelop/Workbench/FileService.cs rename src/Main/SharpDevelop/{Gui => }/Workbench/FullScreenEnabledWindow.cs (97%) rename src/Main/SharpDevelop/{Gui => }/Workbench/LayoutConfiguration.cs (94%) rename src/Main/SharpDevelop/{Gui => }/Workbench/SDStatusBar.cs (98%) rename src/Main/SharpDevelop/{Gui => }/Workbench/SingleInstanceHelper.cs (88%) rename src/Main/SharpDevelop/{Gui => }/Workbench/StatusBarService.cs (94%) rename src/Main/SharpDevelop/{Gui => }/Workbench/WorkbenchStartup.cs (79%) rename src/Main/SharpDevelop/{Gui => }/Workbench/WpfWorkbench.cs (96%) rename src/Main/SharpDevelop/{Gui => }/Workbench/WpfWorkbench.xaml (80%) diff --git a/SharpDevelop.sln b/SharpDevelop.sln index b1b629d9f3..f6e49fda76 100644 --- a/SharpDevelop.sln +++ b/SharpDevelop.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 -# SharpDevelop 4.2.0.8695-Beta 2 +# SharpDevelop 4.2.0.8707-Beta 2 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Main", "Main", "{256F5C28-532C-44C0-8AB8-D8EC5E492E01}" ProjectSection(SolutionItems) = postProject EndProjectSection diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs index 66d05cfeec..7b78c01098 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs @@ -14,6 +14,7 @@ using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Parser; @@ -60,15 +61,15 @@ namespace CSharpBinding this.valueKeywordColor = highlightingDefinition.GetNamedColor("NullOrValueKeywords"); this.parameterModifierColor = highlightingDefinition.GetNamedColor("ParameterModifiers"); - ParserService.ParseInformationUpdated += ParserService_ParseInformationUpdated; - ParserService.LoadSolutionProjectsThreadEnded += ParserService_LoadSolutionProjectsThreadEnded; + SD.ParserService.ParseInformationUpdated += ParserService_ParseInformationUpdated; + SD.ParserService.LoadSolutionProjectsThreadEnded += ParserService_LoadSolutionProjectsThreadEnded; syntaxHighlighter.VisibleDocumentLinesChanged += syntaxHighlighter_VisibleDocumentLinesChanged; } public void Dispose() { - ParserService.ParseInformationUpdated -= ParserService_ParseInformationUpdated; - ParserService.LoadSolutionProjectsThreadEnded -= ParserService_LoadSolutionProjectsThreadEnded; + SD.ParserService.ParseInformationUpdated -= ParserService_ParseInformationUpdated; + SD.ParserService.LoadSolutionProjectsThreadEnded -= ParserService_LoadSolutionProjectsThreadEnded; syntaxHighlighter.VisibleDocumentLinesChanged -= syntaxHighlighter_VisibleDocumentLinesChanged; } #endregion @@ -202,7 +203,7 @@ namespace CSharpBinding return cachedLine.HighlightedLine; } - var parseInfo = ParserService.GetCachedParseInformation(textEditor.FileName, textEditor.Document.Version) as CSharpFullParseInformation; + var parseInfo = SD.ParserService.GetCachedParseInformation(textEditor.FileName, textEditor.Document.Version) as CSharpFullParseInformation; if (parseInfo == null) { if (!invalidLines.Contains(documentLine)) invalidLines.Add(documentLine); @@ -218,7 +219,7 @@ namespace CSharpBinding } } - var compilation = ParserService.GetCompilationForFile(parseInfo.FileName); + var compilation = SD.ParserService.GetCompilationForFile(parseInfo.FileName); this.resolver = parseInfo.GetResolver(compilation); HighlightedLine line = new HighlightedLine(textEditor.Document, documentLine); diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs index 9c2ebe0566..9cda1c9234 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionBinding.cs @@ -4,11 +4,11 @@ using System; using System.Collections.Generic; using System.Linq; - using CSharpBinding.Parser; using ICSharpCode.NRefactory.Completion; using ICSharpCode.NRefactory.CSharp.Completion; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor.CodeCompletion; using ICSharpCode.SharpDevelop.Parser; @@ -38,13 +38,13 @@ namespace CSharpBinding.Completion bool ShowCompletion(ITextEditor editor, char completionChar, bool ctrlSpace) { // Don't require the very latest parse information, an older cached version is OK. - var parseInfo = ParserService.GetCachedParseInformation(editor.FileName) as CSharpFullParseInformation; + var parseInfo = SD.ParserService.GetCachedParseInformation(editor.FileName) as CSharpFullParseInformation; if (parseInfo == null) { - parseInfo = ParserService.Parse(editor.FileName, editor.Document) as CSharpFullParseInformation; + parseInfo = SD.ParserService.Parse(editor.FileName, editor.Document) as CSharpFullParseInformation; if (parseInfo == null) return false; } - ICompilation compilation = ParserService.GetCompilationForFile(editor.FileName); + ICompilation compilation = SD.ParserService.GetCompilationForFile(editor.FileName); var pc = compilation.MainAssembly.UnresolvedAssembly as IProjectContent; if (pc == null) return false; diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/CSharpFormattingStrategy.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/CSharpFormattingStrategy.cs index 46590e3f4c..70867635b9 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/CSharpFormattingStrategy.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/CSharpFormattingStrategy.cs @@ -9,6 +9,7 @@ using ICSharpCode.AvalonEdit.Indentation.CSharp; using ICSharpCode.Core; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Parser; @@ -180,7 +181,7 @@ namespace CSharpBinding.FormattingStrategy FileName fileName = editor.FileName; IUnresolvedEntity nextElement = null; if (fileName != null) { - IParsedFile parsedFile = ParserService.ParseFile(fileName, editor.Document.CreateSnapshot()); + IParsedFile parsedFile = SD.ParserService.ParseFile(fileName, editor.Document); if (parsedFile != null) { var currentClass = parsedFile.GetInnermostTypeDefinition(caretLine, 0); int nextElementLine = int.MaxValue; diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/CSharpSymbolSearch.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/CSharpSymbolSearch.cs index 051040ffa6..4de7192ffd 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/CSharpSymbolSearch.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/CSharpSymbolSearch.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; - using CSharpBinding.Parser; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Core; @@ -19,6 +18,7 @@ using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.Utils; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor.Search; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Parser; @@ -44,7 +44,7 @@ namespace CSharpBinding { this.project = project; searchScopes = fr.GetSearchScopes(entity); - compilation = ParserService.GetCompilation(project); + compilation = SD.ParserService.GetCompilation(project); interestingFileNames = new IList[searchScopes.Count]; for (int i = 0; i < searchScopes.Count; i++) { interestingFileNames[i] = fr.GetInterestingFiles(searchScopes[i], compilation).Select(f => f.FileName).ToList(); @@ -93,7 +93,7 @@ namespace CSharpBinding return; } - var parseInfo = ParserService.Parse(fileName, textSource) as CSharpFullParseInformation; + var parseInfo = SD.ParserService.Parse(fileName, textSource) as CSharpFullParseInformation; if (parseInfo == null) return; ReadOnlyDocument document = null; diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/Parser.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/Parser.cs index 096b7f2032..db45491d63 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/Parser.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/Parser.cs @@ -28,17 +28,7 @@ namespace CSharpBinding.Parser { public class TParser : IParser { - ///IParser Interface - string[] lexerTags; - - public string[] LexerTags { - get { - return lexerTags; - } - set { - lexerTags = value; - } - } + public IReadOnlyList TaskListTokens { get; set; } public bool CanParse(string fileName) { @@ -77,7 +67,8 @@ namespace CSharpBinding.Parser } */ - public ParseInformation Parse(FileName fileName, ITextSource fileContent, bool fullParseInformationRequested) + public ParseInformation Parse(FileName fileName, ITextSource fileContent, bool fullParseInformationRequested, + IProject parentProject, CancellationToken cancellationToken) { CSharpParser parser = new CSharpParser(); parser.GenerateTypeSystemMode = !fullParseInformationRequested; @@ -102,7 +93,7 @@ namespace CSharpBinding.Parser ReadOnlyDocument document = null; foreach (var comment in cu.Descendants.OfType().Where(c => c.CommentType != CommentType.InactiveCode)) { int matchLength; - int index = comment.Content.IndexOfAny(lexerTags, 0, out matchLength); + int index = comment.Content.IndexOfAny(TaskListTokens, 0, out matchLength); if (index > -1) { if (document == null) document = new ReadOnlyDocument(fileContent); @@ -115,7 +106,7 @@ namespace CSharpBinding.Parser int endOffset = Math.Min(document.GetLineByNumber(startLocation.Line).EndOffset, document.GetOffset(comment.EndLocation) - commentEndSignLength); string content = document.GetText(absoluteOffset, endOffset - absoluteOffset); tagComments.Add(new TagComment(content.Substring(0, matchLength), new DomRegion(cu.FileName, startLocation.Line, startLocation.Column), content.Substring(matchLength))); - index = comment.Content.IndexOfAny(lexerTags, endOffset - startOffset - commentSignLength, out matchLength); + index = comment.Content.IndexOfAny(TaskListTokens, endOffset - startOffset - commentSignLength, out matchLength); } while (index > -1); } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Project/CSharpMyNamespaceBuilder.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Project/CSharpMyNamespaceBuilder.cs index fd787be718..c48786d54c 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Project/CSharpMyNamespaceBuilder.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Project/CSharpMyNamespaceBuilder.cs @@ -63,7 +63,7 @@ namespace CSharpBinding continue; } if (trimmedLine == "/*LIST OF FORMS*/") { - var compilation = ParserService.GetCompilation(vbProject); + var compilation = SD.ParserService.GetCompilation(vbProject); ITypeDefinition myFormsClass = FindMyFormsClass(compilation.MainAssembly, ns); if (myFormsClass != null) { string indentation = line.Substring(0, line.Length - trimmedLine.Length); diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs index 1177b7a7f2..185995e54e 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs @@ -15,6 +15,7 @@ using ICSharpCode.NRefactory; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.Editor; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Parser; @@ -90,14 +91,14 @@ namespace CSharpBinding.Refactoring { this.editor = editor; this.markerService = editor.GetService(typeof(ITextMarkerService)) as ITextMarkerService; - ParserService.ParserUpdateStepFinished += ParserService_ParserUpdateStepFinished; + //SD.ParserService.ParserUpdateStepFinished += ParserService_ParserUpdateStepFinished; editor.ContextActionProviders.Add(this); } public void Dispose() { editor.ContextActionProviders.Remove(this); - ParserService.ParserUpdateStepFinished -= ParserService_ParserUpdateStepFinished; + //SD.ParserService.ParserUpdateStepFinished -= ParserService_ParserUpdateStepFinished; if (cancellationTokenSource != null) cancellationTokenSource.Cancel(); Clear(); @@ -243,7 +244,7 @@ namespace CSharpBinding.Refactoring try { await Task.Run( delegate { - var compilation = ParserService.GetCompilationForFile(parseInfo.FileName); + var compilation = SD.ParserService.GetCompilationForFile(parseInfo.FileName); var resolver = parseInfo.GetResolver(compilation); var context = new SDRefactoringContext(textSource, resolver, new TextLocation(0, 0), 0, 0, cancellationToken); foreach (var issueProvider in issueProviders.Value) { diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDScript.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDScript.cs index d85840cda6..4fe9418b95 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDScript.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDScript.cs @@ -10,6 +10,7 @@ using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Parser; @@ -41,7 +42,7 @@ namespace CSharpBinding.Refactoring public override void FormatText(AstNode node) { - var parseInfo = ParserService.Parse(editor.FileName, editor.Document) as CSharpFullParseInformation; + var parseInfo = SD.ParserService.Parse(editor.FileName, editor.Document) as CSharpFullParseInformation; if (parseInfo != null) { //var startLocation = editor.Document.GetLocation(offset); //var endLocation = editor.Document.GetLocation(offset + length); diff --git a/src/AddIns/BackendBindings/CSharpBinding/Tests/MockTextEditor.cs b/src/AddIns/BackendBindings/CSharpBinding/Tests/MockTextEditor.cs index 264337f571..4695488afe 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Tests/MockTextEditor.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Tests/MockTextEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +using System.Threading; using ICSharpCode.AvalonEdit.Highlighting; using System; using System.Collections.Generic; @@ -27,18 +28,16 @@ namespace CSharpBinding.Tests public MockTextEditor() : base(new TextEditor()) { - PropertyService.InitializeServiceForUnitTests(); } public override FileName FileName { get { return new FileName("mockFileName.cs"); } } - public void CreateParseInformation() + public ParseInformation CreateParseInformation() { var parser = new CSharpBinding.Parser.TParser(); - var parseInfo = parser.Parse(this.FileName, this.Document, true); - ParserService.RegisterParseInformation(parseInfo.FileName, parseInfo, parser); + return parser.Parse(this.FileName, this.Document, true, null, CancellationToken.None); } ICompletionItemList lastCompletionItemList; diff --git a/src/AddIns/BackendBindings/CppBinding/CppBinding/CppBinding.csproj b/src/AddIns/BackendBindings/CppBinding/CppBinding/CppBinding.csproj index 6b736db153..55e9254b95 100644 --- a/src/AddIns/BackendBindings/CppBinding/CppBinding/CppBinding.csproj +++ b/src/AddIns/BackendBindings/CppBinding/CppBinding/CppBinding.csproj @@ -71,7 +71,6 @@ - UserControl diff --git a/src/AddIns/BackendBindings/CppBinding/CppBinding/Project/DependencyRelation.cs b/src/AddIns/BackendBindings/CppBinding/CppBinding/Project/DependencyRelation.cs index ebebfbf309..ad7ec4fa80 100644 --- a/src/AddIns/BackendBindings/CppBinding/CppBinding/Project/DependencyRelation.cs +++ b/src/AddIns/BackendBindings/CppBinding/CppBinding/Project/DependencyRelation.cs @@ -3,9 +3,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; -using System.Diagnostics; + +using ICSharpCode.SharpDevelop; namespace ICSharpCode.CppBinding.Project { @@ -18,13 +20,14 @@ namespace ICSharpCode.CppBinding.Project : this(new MultiDictionary()) { } - public DependencyRelation(IMultiDictionary innerDep) + public DependencyRelation(MultiDictionary innerDep) { dep = innerDep; revDep = new MultiDictionary(); - foreach (KeyValuePair item in dep) - revDep.Add(item.Value, item.Key); + foreach (var group in dep) + foreach (var value in group) + revDep.Add(value, group.Key); } /// @@ -53,11 +56,9 @@ namespace ICSharpCode.CppBinding.Project /// Returns a list of items given element directly depend on. /// If there is no element in the dependency relation, returns an empty list. /// - public IList DependOn(T1 element) + public IReadOnlyList DependOn(T1 element) { - if (dep.ContainsKey(element)) - return dep[element]; - return new List(); + return dep[element]; } /// @@ -83,11 +84,9 @@ namespace ICSharpCode.CppBinding.Project /// If the is no item that depend on given element, returns an empty list. /// /// - public IList DependingOn(T2 element) + public IReadOnlyList DependingOn(T2 element) { - if (revDep.ContainsKey(element)) - return revDep[element]; - return new List(); + return revDep[element]; } /// @@ -108,7 +107,7 @@ namespace ICSharpCode.CppBinding.Project return DependencyTree(revDep, element).Cast(); } - IEnumerable DependencyTree(IMultiDictionary rel, T startItem) + IEnumerable DependencyTree(MultiDictionary rel, T startItem) { HashSet visited = new HashSet(); Stack pendingItems = new Stack(); @@ -117,21 +116,20 @@ namespace ICSharpCode.CppBinding.Project while (pendingItems.Count > 0) { T currentItem = pendingItems.Pop(); - if (rel.ContainsKey(currentItem)) - foreach (T depend in rel[currentItem].Cast()) - if (!visited.Contains(depend)) - { - visited.Add(depend); - pendingItems.Push(depend); - yield return depend; - } + foreach (T depend in rel[currentItem].Cast()) + if (!visited.Contains(depend)) + { + visited.Add(depend); + pendingItems.Push(depend); + yield return depend; + } } } //dependency relation - IMultiDictionary dep; + MultiDictionary dep; //reverse dependency relation - IMultiDictionary revDep; + MultiDictionary revDep; } } diff --git a/src/AddIns/BackendBindings/CppBinding/CppBinding/Project/MultiDictionary.cs b/src/AddIns/BackendBindings/CppBinding/CppBinding/Project/MultiDictionary.cs deleted file mode 100644 index c2984e7547..0000000000 --- a/src/AddIns/BackendBindings/CppBinding/CppBinding/Project/MultiDictionary.cs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Collections.ObjectModel; - -namespace ICSharpCode.CppBinding.Project -{ - public interface IMultiDictionary : ICollection> - { - void Add(TKey key, TValue value); - bool Contains(TKey key, TValue value); - bool ContainsKey(TKey key); - bool Remove(TKey key, TValue value); - IList this[TKey key] { get; } - } - - /// - /// A dictionary that allows multiple pairs with the same key. - /// - public class MultiDictionary : IMultiDictionary - { - public MultiDictionary() - : this(new Dictionary>()) - { } - - public MultiDictionary(IDictionary> innerDictionary) - { - if (innerDictionary == null) - throw new ArgumentNullException("innerDictionary"); - dict = innerDictionary; - count = CountElements(dict); - } - IDictionary> dict; - int count; - - public void Add(TKey key, TValue value) - { - IList valueList; - if (!dict.TryGetValue(key, out valueList)) - { - valueList = new List(); - dict.Add(key, valueList); - } - valueList.Add(value); - count++; - } - - public bool Contains(TKey key, TValue value) - { - IList valueList; - if (!dict.TryGetValue(key, out valueList)) - return false; - return valueList.Contains(value); - } - - public bool ContainsKey(TKey key) - { - return dict.ContainsKey(key); - } - - public bool Remove(TKey key, TValue value) - { - IList valueList; - if (!dict.TryGetValue(key, out valueList)) - return false; - return valueList.Remove(value); - } - - public IList this[TKey key] - { - get - { - return new ReadOnlyCollection(dict[key]); - } - } - - #region ICollection> Members - - public void Add(KeyValuePair item) - { - Add(item.Key, item.Value); - } - - public void Clear() - { - dict.Clear(); - count = 0; - } - - public bool Contains(KeyValuePair item) - { - return Contains(item.Key, item.Value); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - if (array == null) - throw new ArgumentNullException("array"); - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException("arrayIndex"); - if (array.Rank != 1) - throw new ArgumentException("Array is multidimensional", "array"); - if (arrayIndex + count >= array.Length) - throw new ArgumentException("Array is to small", "array"); - - foreach (KeyValuePair> item in dict) - foreach (TValue value in item.Value) - array[arrayIndex++] = new KeyValuePair(item.Key, value); - } - - public int Count - { - get { return count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - public bool Remove(KeyValuePair item) - { - return Remove(item.Key, item.Value); - } - - #endregion - - #region IEnumerable> Members - - public IEnumerator> GetEnumerator() - { - foreach (KeyValuePair> item in dict) - foreach (TValue value in item.Value) - yield return new KeyValuePair(item.Key, value); - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - - static int CountElements(IDictionary> dict) - { - int count = 0; - foreach (KeyValuePair> item in dict) - count += item.Value.Count; - return count; - } - } -} diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlOutlineContentHost.xaml.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlOutlineContentHost.xaml.cs index 41853517d8..a3d6683197 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlOutlineContentHost.xaml.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlOutlineContentHost.xaml.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.XamlBinding InitializeComponent(); - ParserService.ParseInformationUpdated += ParseInfoUpdated; + SD.ParserService.ParseInformationUpdated += ParseInfoUpdated; } void ParseInfoUpdated(object sender, ParseInformationEventArgs e) @@ -35,6 +35,7 @@ namespace ICSharpCode.XamlBinding if (this.editor == null || !FileUtility.IsEqualFileName(this.editor.FileName, e.FileName)) return; + #warning Reimplement XAML outline // var cu = e.NewCompilationUnit as XamlCompilationUnit; // // if (cu != null && cu.TreeRootNode != null) @@ -103,7 +104,7 @@ namespace ICSharpCode.XamlBinding public void Dispose() { - ParserService.ParseInformationUpdated -= ParseInfoUpdated; + SD.ParserService.ParseInformationUpdated -= ParseInfoUpdated; } } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs index af36669183..da8a98eaeb 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlParser.cs @@ -27,13 +27,7 @@ namespace ICSharpCode.XamlBinding /// public class XamlParser : IParser { - string[] lexerTags; - - public string[] LexerTags - { - get { return lexerTags; } - set { lexerTags = value; } - } + public IReadOnlyList TaskListTokens { get; set; } // public LanguageProperties Language // { @@ -132,7 +126,8 @@ namespace ICSharpCode.XamlBinding volatile IncrementalParserState parserState; - public ParseInformation Parse(FileName fileName, ITextSource fileContent, bool fullParseInformationRequested) + public ParseInformation Parse(FileName fileName, ITextSource fileContent, bool fullParseInformationRequested, + IProject parentProject, CancellationToken cancellationToken) { AXmlParser parser = new AXmlParser(); AXmlDocument document; @@ -155,7 +150,7 @@ namespace ICSharpCode.XamlBinding foreach (var tag in TreeTraversal.PreOrder(xmlDocument, node => node.Children).OfType().Where(t => t.IsComment)) { int matchLength; AXmlText comment = tag.Children.OfType().First(); - int index = comment.Value.IndexOfAny(lexerTags, 0, out matchLength); + int index = comment.Value.IndexOfAny(TaskListTokens, 0, out matchLength); if (index > -1) { if (document == null) document = fileContent as IDocument ?? new ReadOnlyDocument(fileContent); @@ -165,7 +160,7 @@ namespace ICSharpCode.XamlBinding int endOffset = Math.Min(document.GetLineByOffset(startOffset).EndOffset, comment.EndOffset); string content = document.GetText(startOffset, endOffset - startOffset); parseInfo.TagComments.Add(new TagComment(content.Substring(0, matchLength), new DomRegion(parseInfo.FileName, startLocation.Line, startLocation.Column), content.Substring(matchLength))); - index = comment.Value.IndexOfAny(lexerTags, endOffset - comment.StartOffset, out matchLength); + index = comment.Value.IndexOfAny(TaskListTokens, endOffset - comment.StartOffset, out matchLength); } while (index > -1); } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlSymbolSearch.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlSymbolSearch.cs index 191fe55dee..e2ad3803d3 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlSymbolSearch.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlSymbolSearch.cs @@ -13,6 +13,7 @@ using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor.Search; using ICSharpCode.SharpDevelop.Parser; using ICSharpCode.SharpDevelop.Project; @@ -35,7 +36,7 @@ namespace ICSharpCode.XamlBinding public XamlSymbolSearch(IProject project, IEntity entity) { this.entity = entity; - compilation = ParserService.GetCompilation(project); + compilation = SD.ParserService.GetCompilation(project); interestingFileNames = new List(); foreach (var item in project.ParentSolution.SolutionFolderContainers.Select(f => f.SolutionItems).SelectMany(si => si.Items).Where(i => ".xaml".Equals(Path.GetExtension(i.Location), StringComparison.OrdinalIgnoreCase))) interestingFileNames.Add(new FileName(Path.Combine(project.ParentSolution.Directory, item.Location))); @@ -81,7 +82,7 @@ namespace ICSharpCode.XamlBinding if (offset < 0) return; - var parseInfo = ParserService.Parse(fileName, textSource) as XamlFullParseInformation; + var parseInfo = SD.ParserService.Parse(fileName, textSource) as XamlFullParseInformation; if (parseInfo == null) return; ReadOnlyDocument document = null; diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs index 7304e16478..7ed3925a5c 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs @@ -171,7 +171,7 @@ namespace ICSharpCode.AvalonEdit.AddIn FileName newFileName = file.FileName; if (!string.IsNullOrEmpty(oldFileName)) - ParserService.ClearParseInformation(oldFileName); + SD.ParserService.ClearParseInformation(oldFileName); BookmarksNotifyNameChange(oldFileName, newFileName); @@ -182,7 +182,7 @@ namespace ICSharpCode.AvalonEdit.AddIn codeEditor.FileName = newFileName; UpdateSyntaxHighlighting(newFileName); - ParserService.ParseAsync(file.FileName, codeEditor.Document).FireAndForget(); + SD.ParserService.ParseAsync(file.FileName, codeEditor.Document).FireAndForget(); } } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs index cbb134bd4d..b5b32df266 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs @@ -141,7 +141,7 @@ namespace ICSharpCode.AvalonEdit.AddIn { CodeEditorOptions.Instance.PropertyChanged += CodeEditorOptions_Instance_PropertyChanged; CustomizedHighlightingColor.ActiveColorsChanged += CustomizedHighlightingColor_ActiveColorsChanged; - ParserService.ParseInformationUpdated += ParserServiceParseInformationUpdated; + SD.ParserService.ParseInformationUpdated += ParserServiceParseInformationUpdated; this.FlowDirection = FlowDirection.LeftToRight; // code editing is always left-to-right this.CommandBindings.Add(new CommandBinding(SharpDevelopRoutedCommands.SplitView, OnSplitView)); @@ -476,7 +476,7 @@ namespace ICSharpCode.AvalonEdit.AddIn // Immediately parse on enter. // This ensures we have up-to-date CC info about the method boundary when a user // types near the end of a method. - ParserService.ParseAsync(this.FileName, this.Document.CreateSnapshot()).FireAndForget(); + SD.ParserService.ParseAsync(this.FileName, this.Document.CreateSnapshot()).FireAndForget(); } else { if (e.Text.Length == 1) { foreach (ICodeCompletionBinding cc in CodeCompletionBindings) { @@ -539,10 +539,10 @@ namespace ICSharpCode.AvalonEdit.AddIn void FetchParseInformation() { - ParseInformation parseInfo = ParserService.GetCachedParseInformation(this.FileName); + ParseInformation parseInfo = SD.ParserService.GetCachedParseInformation(this.FileName); if (parseInfo == null) { // if parse info is not yet available, start parsing on background - ParserService.ParseAsync(this.FileName, primaryTextEditorAdapter.Document).FireAndForget(); + SD.ParserService.ParseAsync(this.FileName, primaryTextEditorAdapter.Document).FireAndForget(); // we'll receive the result using the ParseInformationUpdated event } ParseInformationUpdated(parseInfo); @@ -600,7 +600,7 @@ namespace ICSharpCode.AvalonEdit.AddIn { CodeEditorOptions.Instance.PropertyChanged -= CodeEditorOptions_Instance_PropertyChanged; CustomizedHighlightingColor.ActiveColorsChanged -= CustomizedHighlightingColor_ActiveColorsChanged; - ParserService.ParseInformationUpdated -= ParserServiceParseInformationUpdated; + SD.ParserService.ParseInformationUpdated -= ParserServiceParseInformationUpdated; if (primaryTextEditorAdapter.Language != null) primaryTextEditorAdapter.Language.Detach(); diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs index c4ab795790..5a371b1553 100755 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs @@ -550,7 +550,7 @@ namespace ICSharpCode.AvalonEdit.AddIn parseInfo = null; } else { if (parseInfo == null || !parseInfo.IsFullParseInformation) - parseInfo = ParserService.Parse(this.FileName, this.Document); + parseInfo = SD.ParserService.Parse(this.FileName, this.Document); } IServiceContainer container = this.Adapter.GetService(typeof(IServiceContainer)) as IServiceContainer; diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ContextActions/EditorActionsProvider.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ContextActions/EditorActionsProvider.cs index 5bafd707ad..d79f6db158 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ContextActions/EditorActionsProvider.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ContextActions/EditorActionsProvider.cs @@ -6,9 +6,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; - using ICSharpCode.Core; using ICSharpCode.NRefactory; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Parser; using ICSharpCode.SharpDevelop.Refactoring; @@ -83,7 +83,7 @@ namespace ICSharpCode.AvalonEdit.AddIn.ContextActions /// async Task> GetActionsAsync(IEnumerable providers, CancellationToken cancellationToken) { - if (ParserService.LoadSolutionProjectsThreadRunning) + if (SD.ParserService.LoadSolutionProjectsThreadRunning) return EmptyList.Instance; var providerList = providers.ToList(); var actions = await Task.WhenAll(providerList.Select(p => p.GetAvailableActionsAsync(this.EditorContext, cancellationToken))); diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs index 8b43968662..f98d5556b1 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DefaultChangeWatcher.cs @@ -4,11 +4,11 @@ using System; using System.Collections.Generic; using System.IO; - using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.Core; using ICSharpCode.NRefactory.Editor; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Parser; using ICSharpCode.SharpDevelop.Widgets.MyersDiff; @@ -137,7 +137,7 @@ namespace ICSharpCode.AvalonEdit.AddIn stream.CopyTo(memory); stream.Close(); memory.Position = 0; - return FileReader.ReadFileContent(memory, ParserService.DefaultFileEncoding); + return FileReader.ReadFileContent(memory, SD.FileService.DefaultFileEncoding); } Stream GetBaseVersion(FileName fileName) diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/QuickClassBrowser.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/QuickClassBrowser.cs index cefb326933..63132b02d1 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/QuickClassBrowser.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/QuickClassBrowser.cs @@ -242,7 +242,7 @@ namespace ICSharpCode.AvalonEdit.AddIn IUnresolvedTypeDefinition selectedClass = item != null ? item.Entity as IUnresolvedTypeDefinition : null; memberItems = new List(); if (selectedClass != null) { - ICompilation compilation = ParserService.GetCompilationForFile(FileName.Create(selectedClass.ParsedFile.FileName)); + ICompilation compilation = SD.ParserService.GetCompilationForFile(FileName.Create(selectedClass.ParsedFile.FileName)); var context = new SimpleTypeResolveContext(compilation.MainAssembly); ITypeDefinition compoundClass = selectedClass.Resolve(context).GetDefinition(); if (compoundClass != null) { diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/CodeSnippet.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/CodeSnippet.cs index 7f68066371..5cfbe14f7c 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/CodeSnippet.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Snippets/CodeSnippet.cs @@ -194,7 +194,7 @@ namespace ICSharpCode.AvalonEdit.AddIn.Snippets static IUnresolvedTypeDefinition GetCurrentClass(ITextEditor editor) { - var parseInfo = ParserService.GetExistingParsedFile(editor.FileName); + var parseInfo = SD.ParserService.GetExistingParsedFile(editor.FileName); if (parseInfo != null) { return parseInfo.GetInnermostTypeDefinition(editor.Caret.Location); } diff --git a/src/AddIns/VersionControl/GitAddIn/Src/OverlayIconManager.cs b/src/AddIns/VersionControl/GitAddIn/Src/OverlayIconManager.cs index e3661c402c..86015748bc 100644 --- a/src/AddIns/VersionControl/GitAddIn/Src/OverlayIconManager.cs +++ b/src/AddIns/VersionControl/GitAddIn/Src/OverlayIconManager.cs @@ -7,9 +7,9 @@ using System.Diagnostics; using System.Drawing; using System.Threading; using System.Windows.Forms; - using ICSharpCode.Core; using ICSharpCode.Core.WinForms; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Project; @@ -83,7 +83,7 @@ namespace ICSharpCode.GitAddIn // sleep a tiny bit to give main thread time to add more jobs to the queue Thread.Sleep(100); while (true) { - if (ICSharpCode.SharpDevelop.Parser.ParserService.LoadSolutionProjectsThreadRunning) { + if (SD.ParserService.LoadSolutionProjectsThreadRunning) { // Run OverlayIconManager much more slowly while solution is being loaded. // This prevents the disk from seeking too much Thread.Sleep(100); diff --git a/src/AddIns/VersionControl/SubversionAddIn/Src/Gui/ProjectBrowserVisitor/OverlayIconManager.cs b/src/AddIns/VersionControl/SubversionAddIn/Src/Gui/ProjectBrowserVisitor/OverlayIconManager.cs index 2836754ec7..427b4c363d 100644 --- a/src/AddIns/VersionControl/SubversionAddIn/Src/Gui/ProjectBrowserVisitor/OverlayIconManager.cs +++ b/src/AddIns/VersionControl/SubversionAddIn/Src/Gui/ProjectBrowserVisitor/OverlayIconManager.cs @@ -6,9 +6,9 @@ using System.Collections.Generic; using System.Drawing; using System.Threading; using System.Windows.Forms; - using ICSharpCode.Core; using ICSharpCode.Core.WinForms; +using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Project; namespace ICSharpCode.Svn @@ -139,7 +139,7 @@ namespace ICSharpCode.Svn // sleep a tiny bit to give main thread time to add more jobs to the queue Thread.Sleep(2); while (true) { - if (ICSharpCode.SharpDevelop.Parser.ParserService.LoadSolutionProjectsThreadRunning) { + if (SD.ParserService.LoadSolutionProjectsThreadRunning) { // Run OverlayIconManager much more slowly while solution is being loaded. // This prevents the disk from seeking too much Thread.Sleep(100); diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.addin b/src/Main/Base/Project/ICSharpCode.SharpDevelop.addin index 84f5abcbf3..42c4ffc310 100755 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.addin +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.addin @@ -41,7 +41,6 @@ - @@ -49,9 +48,20 @@ - + + + + + + + + + @@ -1066,7 +1076,7 @@ + class = "ICSharpCode.SharpDevelop.Workbench.ChooseLayoutCommand" /> @@ -2243,18 +2253,6 @@ class = "ICSharpCode.SharpDevelop.Gui.ToggleConsoleWordWrapCommand"/> - - - - - - - - - - - - @@ -2286,6 +2284,6 @@ - + diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 3cb9c882ea..858780bc67 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -360,8 +360,10 @@ + + @@ -377,13 +379,11 @@ - - - + + - @@ -578,8 +578,6 @@ - - @@ -734,6 +732,7 @@ + @@ -784,8 +783,6 @@ - - ExtractInterfaceDialog.cs diff --git a/src/Main/Base/Project/Src/Editor/Commands/SymbolUnderCaretMenuCommand.cs b/src/Main/Base/Project/Src/Editor/Commands/SymbolUnderCaretMenuCommand.cs index 70c8e96730..72f002849a 100644 --- a/src/Main/Base/Project/Src/Editor/Commands/SymbolUnderCaretMenuCommand.cs +++ b/src/Main/Base/Project/Src/Editor/Commands/SymbolUnderCaretMenuCommand.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.SharpDevelop.Editor.Commands public void Run(ITextEditor editor, int caretOffset) { var location = editor.Document.GetLocation(caretOffset); - var resolveResult = ParserService.Resolve(editor.FileName, location, editor.Document); + var resolveResult = SD.ParserService.Resolve(editor, location); RunImpl(editor, editor.Caret.Offset, resolveResult); } diff --git a/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs b/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs index 031e1fc2ce..5cfc77788e 100644 --- a/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs +++ b/src/Main/Base/Project/Src/Editor/IDocumentBaseVersionProvider.cs @@ -49,8 +49,7 @@ namespace ICSharpCode.SharpDevelop.Editor { this.watcher = new FileSystemWatcher(repositoryRoot); - if (WorkbenchSingleton.Workbench != null) - watcher.SynchronizingObject = WorkbenchSingleton.Workbench.SynchronizingObject; + watcher.SynchronizingObject = SD.MainThread.SynchronizingObject; WorkbenchSingleton.MainWindow.Activated += MainWindowActivated; diff --git a/src/Main/Base/Project/Src/Editor/ToolTipRequestEventArgs.cs b/src/Main/Base/Project/Src/Editor/ToolTipRequestEventArgs.cs index af5824b79a..692696a362 100644 --- a/src/Main/Base/Project/Src/Editor/ToolTipRequestEventArgs.cs +++ b/src/Main/Base/Project/Src/Editor/ToolTipRequestEventArgs.cs @@ -45,7 +45,7 @@ namespace ICSharpCode.SharpDevelop.Editor get { if (!resolveResultInitialized) { if (InDocument) - resolveResult = ParserService.Resolve(this.Editor.FileName, this.LogicalPosition, this.Editor.Document); + resolveResult = SD.ParserService.Resolve(this.Editor, this.LogicalPosition); resolveResultInitialized = true; } return resolveResult; diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/GotoDialog.cs b/src/Main/Base/Project/Src/Gui/Dialogs/GotoDialog.cs index e8b7d7e3e0..6b6f438beb 100644 --- a/src/Main/Base/Project/Src/Gui/Dialogs/GotoDialog.cs +++ b/src/Main/Base/Project/Src/Gui/Dialogs/GotoDialog.cs @@ -38,14 +38,14 @@ namespace ICSharpCode.SharpDevelop.Gui { InitializeComponent(); FormLocationHelper.ApplyWindow(this, "ICSharpCode.SharpDevelop.Gui.GotoDialog.Bounds", true); - ParserService.LoadSolutionProjectsThreadEnded += ParserService_LoadSolutionProjectsThreadEnded; + SD.ParserService.LoadSolutionProjectsThreadEnded += ParserService_LoadSolutionProjectsThreadEnded; textBox.Focus(); } protected override void OnClosed(EventArgs e) { Instance = null; - ParserService.LoadSolutionProjectsThreadEnded -= ParserService_LoadSolutionProjectsThreadEnded; + SD.ParserService.LoadSolutionProjectsThreadEnded -= ParserService_LoadSolutionProjectsThreadEnded; base.OnClosed(e); } @@ -163,7 +163,7 @@ namespace ICSharpCode.SharpDevelop.Gui { ITextEditor editor = GetEditor(); if (editor != null) { - IParsedFile parseInfo = ParserService.GetExistingParsedFile(editor.FileName); + IParsedFile parseInfo = SD.ParserService.GetExistingParsedFile(editor.FileName); if (parseInfo != null) { foreach (IUnresolvedTypeDefinition c in parseInfo.TopLevelTypeDefinitions) { AddAllMembersMatchingText(c, text); diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs b/src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs index d1db825437..c636588e0a 100644 --- a/src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs +++ b/src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs @@ -393,8 +393,7 @@ namespace ICSharpCode.SharpDevelop.Gui if (!String.IsNullOrEmpty(binaryFileName)) File.Copy(binaryFileName, parsedFileName); else - File.WriteAllText(parsedFileName, parsedContent, ParserService.DefaultFileEncoding); - ParserService.ParseFileAsync(FileName.Create(parsedFileName), new StringTextSource(parsedContent)).FireAndForget(); + File.WriteAllText(parsedFileName, parsedContent, SD.FileService.DefaultFileEncoding); } else { if (!String.IsNullOrEmpty(binaryFileName)) { LoggingService.Warn("binary file was skipped"); diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/IDEOptions/TaskListOptionsl.xaml.cs b/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/IDEOptions/TaskListOptionsl.xaml.cs index e0c9815e25..51ea96627d 100644 --- a/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/IDEOptions/TaskListOptionsl.xaml.cs +++ b/src/Main/Base/Project/Src/Gui/Dialogs/OptionPanels/IDEOptions/TaskListOptionsl.xaml.cs @@ -23,8 +23,7 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels public TaskListXaml() { InitializeComponent(); - string[] tokens = ParserService.TaskListTokens; - foreach (var token in tokens) { + foreach (var token in SD.ParserService.TaskListTokens) { listView.Items.Add(token); }; } @@ -80,7 +79,7 @@ namespace ICSharpCode.SharpDevelop.Gui.OptionPanels tokens.Add(text); } } - ParserService.TaskListTokens = tokens.ToArray(); + SD.ParserService.TaskListTokens = tokens.ToArray(); return true; } } diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/ReferenceDialog/GacReferencePanel.cs b/src/Main/Base/Project/Src/Gui/Dialogs/ReferenceDialog/GacReferencePanel.cs index ea26520a71..dcef64688b 100644 --- a/src/Main/Base/Project/Src/Gui/Dialogs/ReferenceDialog/GacReferencePanel.cs +++ b/src/Main/Base/Project/Src/Gui/Dialogs/ReferenceDialog/GacReferencePanel.cs @@ -442,7 +442,7 @@ namespace ICSharpCode.SharpDevelop.Gui protected virtual IList GetCacheContent() { - return GacInterop.GetGacAssemblyFullNames() + return SD.GlobalAssemblyCache.GetGacAssemblyFullNames() .Where(name => !name.ShortName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) .ToList(); } diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/WordCountDialog.cs b/src/Main/Base/Project/Src/Gui/Dialogs/WordCountDialog.cs index 52a1f08792..d315f1421a 100644 --- a/src/Main/Base/Project/Src/Gui/Dialogs/WordCountDialog.cs +++ b/src/Main/Base/Project/Src/Gui/Dialogs/WordCountDialog.cs @@ -53,7 +53,9 @@ namespace ICSharpCode.SharpDevelop.Gui { if (!File.Exists(filename)) return null; - return GetReport(filename, ParserService.GetParseableFileContent(filename).CreateReader()); + using (var reader = SD.FileService.GetFileContent(filename).CreateReader()) { + return GetReport(filename, reader); + } } Report GetReport(IViewContent content, TextReader reader) diff --git a/src/Main/Base/Project/Src/Gui/IWorkbench.cs b/src/Main/Base/Project/Src/Gui/IWorkbench.cs index ae8a189cfe..ad3fde8d74 100644 --- a/src/Main/Base/Project/Src/Gui/IWorkbench.cs +++ b/src/Main/Base/Project/Src/Gui/IWorkbench.cs @@ -21,11 +21,6 @@ namespace ICSharpCode.SharpDevelop.Gui /// IWin32Window MainWin32Window { get; } - /// - /// Object for executing methods on the main thread. - /// - ISynchronizeInvoke SynchronizingObject { get; } - /// /// The main window. /// diff --git a/src/Main/Base/Project/Src/Gui/Pads/DefinitionViewPad.cs b/src/Main/Base/Project/Src/Gui/Pads/DefinitionViewPad.cs index 4d25aedb11..c910349c37 100755 --- a/src/Main/Base/Project/Src/Gui/Pads/DefinitionViewPad.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/DefinitionViewPad.cs @@ -40,7 +40,8 @@ namespace ICSharpCode.SharpDevelop.Gui ctl = Editor.AvalonEdit.AvalonEditTextEditorAdapter.CreateAvalonEditInstance(); ctl.IsReadOnly = true; ctl.MouseDoubleClick += OnDoubleClick; - ParserService.ParserUpdateStepFinished += OnParserUpdateStep; + throw new NotImplementedException(); + //ParserService.ParserUpdateStepFinished += OnParserUpdateStep; ctl.IsVisibleChanged += delegate { UpdateTick(null); }; } @@ -49,7 +50,7 @@ namespace ICSharpCode.SharpDevelop.Gui /// public override void Dispose() { - ParserService.ParserUpdateStepFinished -= OnParserUpdateStep; + //ParserService.ParserUpdateStepFinished -= OnParserUpdateStep; ctl.Document = null; base.Dispose(); } @@ -95,7 +96,7 @@ namespace ICSharpCode.SharpDevelop.Gui // don't resolve when an unrelated file was changed if (e != null && editor.FileName != e.FileName) return null; - return ParserService.ResolveAsync(editor.FileName, editor.Caret.Location, editor.Document); + return SD.ParserService.ResolveAsync(editor.FileName, editor.Caret.Location, editor.Document); } DomRegion oldPosition; diff --git a/src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/Commands/ReferenceFolderNodeCommands.cs b/src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/Commands/ReferenceFolderNodeCommands.cs index 30c588487f..2ecb4a6e6b 100644 --- a/src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/Commands/ReferenceFolderNodeCommands.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/Commands/ReferenceFolderNodeCommands.cs @@ -68,7 +68,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands } // Update code completion. - ParserService.ParseFileAsync(FileName.Create(webReference.WebProxyFileName)).FireAndForget(); + SD.ParserService.ParseFileAsync(FileName.Create(webReference.WebProxyFileName), parentProject: url.Project).FireAndForget(); } } catch (WebException ex) { MessageService.ShowException(ex, String.Format(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.ProjectBrowser.RefreshWebReference.ReadServiceDescriptionError}"), url.UpdateFromURL)); @@ -125,7 +125,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands AddWebReferenceToProjectBrowser(node, refDialog.WebReference); // Add proxy to code completion. - ParserService.ParseFileAsync(FileName.Create(refDialog.WebReference.WebProxyFileName)).FireAndForget(); + SD.ParserService.ParseFileAsync(FileName.Create(refDialog.WebReference.WebProxyFileName)).FireAndForget(); node.Project.Save(); } diff --git a/src/Main/Base/Project/Src/Gui/Pads/PropertyPad/PropertyPad.cs b/src/Main/Base/Project/Src/Gui/Pads/PropertyPad/PropertyPad.cs index 887f024ba8..5c77998b8c 100644 --- a/src/Main/Base/Project/Src/Gui/Pads/PropertyPad/PropertyPad.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/PropertyPad/PropertyPad.cs @@ -413,7 +413,7 @@ namespace ICSharpCode.SharpDevelop.Gui if (gridItem != null) { Type component = gridItem.PropertyDescriptor.ComponentType; if (component != null) { - ICompilation compilation = ParserService.GetCompilation(ProjectService.CurrentProject); + ICompilation compilation = SD.ParserService.GetCompilation(ProjectService.CurrentProject); IType componentType = compilation.FindType(component); IProperty property = componentType.GetProperties(p => p.Name == gridItem.PropertyDescriptor.Name).FirstOrDefault(); if (property != null) diff --git a/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPad.cs b/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPad.cs index 8f3bce8248..eea8e3d67a 100644 --- a/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPad.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPad.cs @@ -129,7 +129,7 @@ namespace ICSharpCode.SharpDevelop.Gui contentPanel.Controls.Add(taskView); - string[] tokens = ParserService.TaskListTokens; + IReadOnlyList tokens = SD.ParserService.TaskListTokens; foreach (string token in tokens) { @@ -219,7 +219,7 @@ namespace ICSharpCode.SharpDevelop.Gui if (WorkbenchSingleton.Workbench.ActiveViewContent == null) return null; - IParsedFile parseInfo = ParserService.GetExistingParsedFile(WorkbenchSingleton.Workbench.ActiveViewContent.PrimaryFileName); + IParsedFile parseInfo = SD.ParserService.GetExistingParsedFile(WorkbenchSingleton.Workbench.ActiveViewContent.PrimaryFileName); if (parseInfo != null) { IPositionable positionable = WorkbenchSingleton.Workbench.ActiveViewContent as IPositionable; if (positionable != null) { @@ -236,7 +236,7 @@ namespace ICSharpCode.SharpDevelop.Gui // Tasks are created by parsing, so the parse information for item.FileName should already be present. // If they aren't, that's because the file might have been deleted/renamed in the meantime. // We use GetExistingParseInformation to avoid trying to parse a file that might have been deleted/renamed. - IParsedFile parseInfo = ParserService.GetExistingParsedFile(item.FileName); + IParsedFile parseInfo = SD.ParserService.GetExistingParsedFile(item.FileName); if (parseInfo != null) { var c = parseInfo.GetInnermostTypeDefinition(item.Line, item.Column); if (c != null) return c; diff --git a/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPadCommands.cs b/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPadCommands.cs index 39a537f5bd..b0a6e26e08 100644 --- a/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPadCommands.cs +++ b/src/Main/Base/Project/Src/Gui/Pads/TaskList/TaskListPadCommands.cs @@ -2,6 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.Collections.Generic; using System.Windows.Forms; using ICSharpCode.Core; using ICSharpCode.Core.WinForms; @@ -76,9 +77,9 @@ namespace ICSharpCode.SharpDevelop.Gui public class TaskListTokensBuilder { - public static ShowTaskListTokenButton[] BuildItems(string[] tokens) + public static ShowTaskListTokenButton[] BuildItems(IReadOnlyList tokens) { - ShowTaskListTokenButton[] buttons = new ShowTaskListTokenButton[tokens.Length]; + ShowTaskListTokenButton[] buttons = new ShowTaskListTokenButton[tokens.Count]; for (int i = 0; i < buttons.Length; i++) { diff --git a/src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs b/src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs index d980fcd941..94a471066a 100644 --- a/src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs +++ b/src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel; using System.Diagnostics; +using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; using ICSharpCode.Core; @@ -70,7 +71,6 @@ namespace ICSharpCode.SharpDevelop.Gui DisplayBindingService.InitializeService(); FileService.InitializeService(); - ParserService.InitializeParserService(); TaskService.Initialize(); Bookmarks.BookmarkManager.Initialize(); Project.CustomToolsService.Initialize(); @@ -80,7 +80,7 @@ namespace ICSharpCode.SharpDevelop.Gui if (messageService != null) { messageService.DialogOwner = workbench.MainWin32Window; Debug.Assert(messageService.DialogOwner != null); - messageService.DialogSynchronizeInvoke = workbench.SynchronizingObject; + messageService.DialogSynchronizeInvoke = SD.MainThread.SynchronizingObject; } workbench.Initialize(); @@ -133,7 +133,7 @@ namespace ICSharpCode.SharpDevelop.Gui if (workbench == null) return false; // unit test mode, don't crash else - return workbench.SynchronizingObject.InvokeRequired; + return SD.MainThread.InvokeRequired; } } @@ -166,13 +166,7 @@ namespace ICSharpCode.SharpDevelop.Gui /// public static R SafeThreadFunction(Func method) { - // InvokeRequired test is necessary so that we don't run other actions in the message queue - // when we're already running on the main thread (unexpected reentrancy) - ISynchronizeInvoke si = workbench.SynchronizingObject; - if (si.InvokeRequired) - return (R)workbench.SynchronizingObject.Invoke(method, emptyObjectArray); - else - return method(); + return SD.MainThread.InvokeIfRequired(method); } /// @@ -182,11 +176,7 @@ namespace ICSharpCode.SharpDevelop.Gui /// public static R SafeThreadFunction(Func method, A arg1) { - ISynchronizeInvoke si = workbench.SynchronizingObject; - if (si.InvokeRequired) - return (R)si.Invoke(method, new object[] { arg1 }); - else - return method(arg1); + return SD.MainThread.InvokeIfRequired(() => method(arg1)); } /// @@ -196,11 +186,7 @@ namespace ICSharpCode.SharpDevelop.Gui /// public static void SafeThreadCall(Action method) { - ISynchronizeInvoke si = workbench.SynchronizingObject; - if (si.InvokeRequired) - si.Invoke(method, emptyObjectArray); - else - method(); + SD.MainThread.InvokeIfRequired(method); } /// @@ -210,11 +196,7 @@ namespace ICSharpCode.SharpDevelop.Gui /// public static void SafeThreadCall(Action method, A arg1) { - ISynchronizeInvoke si = workbench.SynchronizingObject; - if (si.InvokeRequired) - si.Invoke(method, new object[] { arg1 }); - else - method(arg1); + SD.MainThread.InvokeIfRequired(() => method(arg1)); } /// @@ -224,11 +206,7 @@ namespace ICSharpCode.SharpDevelop.Gui /// public static void SafeThreadCall(Action method, A arg1, B arg2) { - ISynchronizeInvoke si = workbench.SynchronizingObject; - if (si.InvokeRequired) - si.Invoke(method, new object[] { arg1, arg2 }); - else - method(arg1, arg2); + SD.MainThread.InvokeIfRequired(() => method(arg1, arg2)); } /// @@ -238,11 +216,7 @@ namespace ICSharpCode.SharpDevelop.Gui /// public static void SafeThreadCall(Action method, A arg1, B arg2, C arg3) { - ISynchronizeInvoke si = workbench.SynchronizingObject; - if (si.InvokeRequired) - si.Invoke(method, new object[] { arg1, arg2, arg3 }); - else - method(arg1, arg2, arg3); + SD.MainThread.InvokeIfRequired(() => method(arg1, arg2, arg3)); } /// @@ -250,7 +224,7 @@ namespace ICSharpCode.SharpDevelop.Gui /// public static void SafeThreadAsyncCall(Action method) { - workbench.SynchronizingObject.BeginInvoke(method, emptyObjectArray); + SD.MainThread.InvokeAsync(method).FireAndForget(); } /// @@ -258,7 +232,7 @@ namespace ICSharpCode.SharpDevelop.Gui /// public static void SafeThreadAsyncCall(Action method, A arg1) { - workbench.SynchronizingObject.BeginInvoke(method, new object[] { arg1 }); + SD.MainThread.InvokeAsync(() => method(arg1)).FireAndForget(); } /// @@ -266,7 +240,7 @@ namespace ICSharpCode.SharpDevelop.Gui /// public static void SafeThreadAsyncCall(Action method, A arg1, B arg2) { - workbench.SynchronizingObject.BeginInvoke(method, new object[] { arg1, arg2 }); + SD.MainThread.InvokeAsync(() => method(arg1, arg2)).FireAndForget(); } /// @@ -274,30 +248,16 @@ namespace ICSharpCode.SharpDevelop.Gui /// public static void SafeThreadAsyncCall(Action method, A arg1, B arg2, C arg3) { - workbench.SynchronizingObject.BeginInvoke(method, new object[] { arg1, arg2, arg3 }); + SD.MainThread.InvokeAsync(() => method(arg1, arg2, arg3)).FireAndForget(); } /// /// Calls a method on the GUI thread, but delays the call a bit. /// - public static void CallLater(TimeSpan delay, Action method) + public static async void CallLater(TimeSpan delay, Action method) { - int delayMilliseconds = (int)delay.TotalMilliseconds; - if (delayMilliseconds < 0) - throw new ArgumentOutOfRangeException("delay", delay, "Value must be positive"); - if (method == null) - throw new ArgumentNullException("method"); - SafeThreadAsyncCall( - delegate { - Timer t = new Timer(); - t.Interval = Math.Max(1, delayMilliseconds); - t.Tick += delegate { - t.Stop(); - t.Dispose(); - method(); - }; - t.Start(); - }); + await Task.Delay(delay).ConfigureAwait(false); + SD.MainThread.InvokeAsync(method).FireAndForget(); } #endregion diff --git a/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectDescriptor.cs b/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectDescriptor.cs index b4706c79e9..9f47709710 100644 --- a/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectDescriptor.cs +++ b/src/Main/Base/Project/Src/Internal/Templates/Project/ProjectDescriptor.cs @@ -372,7 +372,7 @@ namespace ICSharpCode.SharpDevelop.Internal.Templates File.Copy(file.BinaryFileName,fileName); } else { // Textual content - StreamWriter sr = new StreamWriter(File.Create(fileName), ParserService.DefaultFileEncoding); + StreamWriter sr = new StreamWriter(File.Create(fileName), SD.FileService.DefaultFileEncoding); string fileContent = StringParser.Parse(file.Content, new StringTagPair("ProjectName", projectCreateInformation.ProjectName), new StringTagPair("FileName", fileName)); fileContent = StringParser.Parse(fileContent); if (SD.EditorControlService.GlobalOptions.IndentationString != "\t") { diff --git a/src/Main/Base/Project/Src/Project/AbstractProject.cs b/src/Main/Base/Project/Src/Project/AbstractProject.cs index b46a76ec4b..029ca9f3fa 100644 --- a/src/Main/Base/Project/Src/Project/AbstractProject.cs +++ b/src/Main/Base/Project/Src/Project/AbstractProject.cs @@ -15,6 +15,7 @@ using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui.OptionPanels; +using ICSharpCode.SharpDevelop.Parser; namespace ICSharpCode.SharpDevelop.Project { @@ -409,6 +410,16 @@ namespace ICSharpCode.SharpDevelop.Project } } + public virtual event EventHandler ParseInformationUpdated { + add {} + remove {} + } + + public virtual void OnParseInformationUpdated(ParseInformationEventArgs args) + { + throw new NotSupportedException(); + } + /// /// Creates a new projectItem for the passed itemType /// diff --git a/src/Main/Base/Project/Src/Project/CompilableProject.cs b/src/Main/Base/Project/Src/Project/CompilableProject.cs index 358dea5ac1..3b807bac74 100644 --- a/src/Main/Base/Project/Src/Project/CompilableProject.cs +++ b/src/Main/Base/Project/Src/Project/CompilableProject.cs @@ -379,6 +379,19 @@ namespace ICSharpCode.SharpDevelop.Project return GetParseProjectContentContainer().ProjectContent; } } + + public override void OnParseInformationUpdated(ParseInformationEventArgs args) + { + GetParseProjectContentContainer().ParseInformationUpdated(args.OldParsedFile, args.NewParsedFile); + // OnParseInformationUpdated is called inside a lock, but we don't want to raise the event inside that lock. + // To ensure events are raised in the same order, we always invoke on the main thread. + SD.MainThread.InvokeAsync( + delegate { + ParseInformationUpdated(null, args); + }); + } + + public override event EventHandler ParseInformationUpdated = delegate {}; #endregion } } diff --git a/src/Main/Base/Project/Src/Project/CustomTool.cs b/src/Main/Base/Project/Src/Project/CustomTool.cs index 7c8c338530..3d92f211ce 100644 --- a/src/Main/Base/Project/Src/Project/CustomTool.cs +++ b/src/Main/Base/Project/Src/Project/CustomTool.cs @@ -201,7 +201,7 @@ namespace ICSharpCode.SharpDevelop.Project }, outputFileName, FileErrorPolicy.Inform); EnsureOutputFileIsInProject(baseItem, outputFileName); - ParserService.ParseAsync(FileName.Create(outputFileName), new StringTextSource(codeOutput)).FireAndForget(); + SD.ParserService.ParseAsync(FileName.Create(outputFileName), new StringTextSource(codeOutput)).FireAndForget(); } public void GenerateCodeDomAsync(FileProjectItem baseItem, string outputFileName, Func func) diff --git a/src/Main/Base/Project/Src/Project/IProject.cs b/src/Main/Base/Project/Src/Project/IProject.cs index 496ef876ae..7ee9b823b2 100644 --- a/src/Main/Base/Project/Src/Project/IProject.cs +++ b/src/Main/Base/Project/Src/Project/IProject.cs @@ -12,6 +12,7 @@ using System.Xml.Linq; using ICSharpCode.Core; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Parser; namespace ICSharpCode.SharpDevelop.Project { @@ -304,6 +305,17 @@ namespace ICSharpCode.SharpDevelop.Project /// An object that can be used to perform the search; or null if this project does not support symbol searches. /// Refactoring.ISymbolSearch PrepareSymbolSearch(IEntity entity); + + /// + /// Occurs whenever parse information for this project was updated. This event is raised on the main thread. + /// + event EventHandler ParseInformationUpdated; + + /// + /// Notifies the project that the parse information was updated. + /// This method is called by the parser service within a per-file lock. + /// + void OnParseInformationUpdated(ParseInformationEventArgs args); } /// diff --git a/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs b/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs index 849ec019eb..168f2fbb67 100644 --- a/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs +++ b/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs @@ -72,8 +72,7 @@ namespace ICSharpCode.SharpDevelop.Project try { if (watcher == null) { watcher = new FileSystemWatcher(); - if (WorkbenchSingleton.Workbench != null) - watcher.SynchronizingObject = WorkbenchSingleton.Workbench.SynchronizingObject; + watcher.SynchronizingObject = SD.MainThread.SynchronizingObject; watcher.Changed += OnFileChangedEvent; watcher.Created += OnFileChangedEvent; watcher.Renamed += OnFileChangedEvent; diff --git a/src/Main/Base/Project/Src/Refactoring/EditorRefactoringContext.cs b/src/Main/Base/Project/Src/Refactoring/EditorRefactoringContext.cs index 513f26356b..994ca83bdb 100644 --- a/src/Main/Base/Project/Src/Refactoring/EditorRefactoringContext.cs +++ b/src/Main/Base/Project/Src/Refactoring/EditorRefactoringContext.cs @@ -76,7 +76,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring { lock (syncRoot) { if (parseInformation == null) - parseInformation = ParserService.ParseAsync(this.FileName, this.TextSource); + parseInformation = SD.ParserService.ParseAsync(this.FileName, this.TextSource); return parseInformation; } } @@ -86,6 +86,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring /// public ParseInformation GetParseInformation() { + // waiting for the task is safe because we specified the text source in the ParseAsync call return GetParseInformationAsync().Result; } @@ -96,7 +97,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring { lock (syncRoot) { if (compilation == null) - compilation = Task.FromResult(ParserService.GetCompilationForFile(this.FileName)); + compilation = Task.FromResult(SD.ParserService.GetCompilationForFile(this.FileName)); return compilation; } } @@ -145,14 +146,11 @@ namespace ICSharpCode.SharpDevelop.Refactoring async Task ResolveCurrentSymbolAsync() { - var parser = ParserService.GetParser(this.FileName); - if (parser == null) - return null; var parseInfo = await GetParseInformationAsync().ConfigureAwait(false); if (parseInfo == null) return null; var compilation = await GetCompilationAsync().ConfigureAwait(false); - return await Task.Run(() => ParserService.ResolveAsync(this.FileName, caretLocation, this.TextSource, CancellationToken.None)).ConfigureAwait(false); + return await Task.Run(() => SD.ParserService.ResolveAsync(this.FileName, caretLocation, this.TextSource, compilation)).ConfigureAwait(false); } /// diff --git a/src/Main/Base/Project/Src/Services/File/FileChangeWatcher.cs b/src/Main/Base/Project/Src/Services/File/FileChangeWatcher.cs index dcfe810a6e..0aa3c2cda5 100644 --- a/src/Main/Base/Project/Src/Services/File/FileChangeWatcher.cs +++ b/src/Main/Base/Project/Src/Services/File/FileChangeWatcher.cs @@ -134,8 +134,7 @@ namespace ICSharpCode.SharpDevelop try { if (watcher == null) { watcher = new FileSystemWatcher(); - if (WorkbenchSingleton.Workbench != null) - watcher.SynchronizingObject = WorkbenchSingleton.Workbench.SynchronizingObject; + watcher.SynchronizingObject = SD.MainThread.SynchronizingObject; watcher.Changed += OnFileChangedEvent; watcher.Created += OnFileChangedEvent; watcher.Renamed += OnFileChangedEvent; diff --git a/src/Main/Base/Project/Src/Services/File/FileService.cs b/src/Main/Base/Project/Src/Services/File/FileService.cs index 2d7515d201..2c6e0090e4 100644 --- a/src/Main/Base/Project/Src/Services/File/FileService.cs +++ b/src/Main/Base/Project/Src/Services/File/FileService.cs @@ -44,7 +44,7 @@ namespace ICSharpCode.SharpDevelop PropertyService.SetNestedProperties("RecentOpen", recentOpen.ToProperties()); } ProjectService.SolutionLoaded -= ProjectServiceSolutionLoaded; - ParserService.LoadSolutionProjectsThreadEnded -= ParserServiceLoadSolutionProjectsThreadEnded; + SD.ParserService.LoadSolutionProjectsThreadEnded -= ParserServiceLoadSolutionProjectsThreadEnded; serviceInitialized = false; } @@ -52,7 +52,7 @@ namespace ICSharpCode.SharpDevelop { if (!serviceInitialized) { ProjectService.SolutionLoaded += ProjectServiceSolutionLoaded; - ParserService.LoadSolutionProjectsThreadEnded += ParserServiceLoadSolutionProjectsThreadEnded; + SD.ParserService.LoadSolutionProjectsThreadEnded += ParserServiceLoadSolutionProjectsThreadEnded; serviceInitialized = true; } } @@ -278,7 +278,7 @@ namespace ICSharpCode.SharpDevelop /// Content of the file to create public static IViewContent NewFile(string defaultName, string content) { - return NewFile(defaultName, ParserService.DefaultFileEncoding.GetBytesWithPreamble(content)); + return NewFile(defaultName, SD.FileService.DefaultFileEncoding.GetBytesWithPreamble(content)); } /// diff --git a/src/Main/Base/Project/Src/Services/File/IFileService.cs b/src/Main/Base/Project/Src/Services/File/IFileService.cs new file mode 100644 index 0000000000..4ac87d0e99 --- /dev/null +++ b/src/Main/Base/Project/Src/Services/File/IFileService.cs @@ -0,0 +1,55 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Text; +using System.Threading; +using ICSharpCode.Core; +using ICSharpCode.NRefactory.Editor; + +namespace ICSharpCode.SharpDevelop +{ + /// + /// File service. + /// + public interface IFileService + { + /// + /// Gets the default file encoding. + /// This property is thread-safe. + /// + Encoding DefaultFileEncoding { get; } + + /// + /// Gets the content of the specified file. + /// If the file is currently open in SharpDevelop, retrieves a snapshot + /// of the editor content. + /// + /// + /// This method is thread-safe. This method involves waiting for the main thread, so using it while + /// holding a lock can lead to deadlocks. + /// + ITextSource GetFileContent(FileName fileName); + + /// + ITextSource GetFileContent(string fileName); + + /// + /// Gets the file content for a file that is currently open. + /// Returns null if the file is not open. + /// + /// + /// This method is thread-safe. This method involves waiting for the main thread, so using it while + /// holding a lock can lead to deadlocks. + /// + ITextSource GetFileContentForOpenFile(FileName fileName); + + /// + /// Gets the file content from disk, ignoring open files. + /// + /// + /// This method is thread-safe. + /// + ITextSource GetFileContentFromDisk(FileName fileName, CancellationToken cancellationToken = default(CancellationToken)); + } +} diff --git a/src/Main/Base/Project/Src/Services/File/OpenedFile.cs b/src/Main/Base/Project/Src/Services/File/OpenedFile.cs index c0dbed0573..c6f3d1f8a8 100644 --- a/src/Main/Base/Project/Src/Services/File/OpenedFile.cs +++ b/src/Main/Base/Project/Src/Services/File/OpenedFile.cs @@ -461,9 +461,9 @@ namespace ICSharpCode.SharpDevelop // We discarded some information when closing the file, // so we need to re-parse it. if (File.Exists(this.FileName)) - ParserService.ParseAsync(this.FileName).FireAndForget(); + SD.ParserService.ParseAsync(this.FileName).FireAndForget(); else - ParserService.ClearParseInformation(this.FileName); + SD.ParserService.ClearParseInformation(this.FileName); } } } diff --git a/src/Main/Base/Project/Src/Services/IMessageLoop.cs b/src/Main/Base/Project/Src/Services/IMessageLoop.cs new file mode 100644 index 0000000000..728e4fce25 --- /dev/null +++ b/src/Main/Base/Project/Src/Services/IMessageLoop.cs @@ -0,0 +1,93 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace ICSharpCode.SharpDevelop +{ + /// + /// Represents a thread running a message loop. + /// + public interface IMessageLoop + { + /// + /// Gets the thread corresponding to this message loop. + /// + Thread Thread { get; } + + /// + /// Gets the dispatcher for this message loop. + /// + Dispatcher Dispatcher { get; } + + /// + /// Gets the synchronization context corresponding to this message loop. + /// + SynchronizationContext SynchronizationContext { get; } + + /// + /// Gets the implementation corresponding to this message loop. + /// + ISynchronizeInvoke SynchronizingObject { get; } + + /// + /// Gets whether the current thread is different from the thread running this message loop. + /// + /// InvokeRequired = !CheckAcess() + bool InvokeRequired { get; } + + /// + /// Gets whether the current thread is the same as the thread running this message loop. + /// + /// CheckAccess() = !InvokeRequired + bool CheckAccess(); + + /// + /// Throws an exception if the current thread is different from the thread running this message loop. + /// + void VerifyAccess(); + + /// + /// Invokes the specified callback on the message loop and waits for its completion. + /// If the current thread is the thread running the message loop, executes the callback + /// directly without pumping the message loop. + /// + void InvokeIfRequired(Action callback); + /// + void InvokeIfRequired(Action callback, DispatcherPriority priority); + /// + void InvokeIfRequired(Action callback, DispatcherPriority priority, CancellationToken cancellationToken); + + /// + /// Invokes the specified callback, waits for its completion, and returns the result. + /// If the current thread is the thread running the message loop, executes the callback + /// directly without pumping the message loop. + /// + T InvokeIfRequired(Func callback); + /// + T InvokeIfRequired(Func callback, DispatcherPriority priority); + /// + T InvokeIfRequired(Func callback, DispatcherPriority priority, CancellationToken cancellationToken); + + /// + /// Invokes the specified callback. + /// + /// Returns a task that is signalled when the execution of the callback is completed. + Task InvokeAsync(Action callback); + /// + Task InvokeAsync(Action callback, DispatcherPriority priority); + /// + Task InvokeAsync(Action callback, DispatcherPriority priority, CancellationToken cancellationToken); + + /// + Task InvokeAsync(Func callback); + /// + Task InvokeAsync(Func callback, DispatcherPriority priority); + /// + Task InvokeAsync(Func callback, DispatcherPriority priority, CancellationToken cancellationToken); + } +} diff --git a/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs b/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs index b967e9b217..b2eeabd56b 100644 --- a/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs +++ b/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs @@ -70,7 +70,7 @@ namespace ICSharpCode.SharpDevelop // ignore files opened as part of loading a solution. ProjectService.SolutionLoading += ProjectService_SolutionLoading; - ParserService.LoadSolutionProjectsThreadEnded += LoadSolutionProjectsThreadEnded; + SD.ParserService.LoadSolutionProjectsThreadEnded += LoadSolutionProjectsThreadEnded; FileService.FileRenamed += FileService_FileRenamed; ProjectService.SolutionClosed += ProjectService_SolutionClosed; diff --git a/src/Main/Base/Project/Src/Services/ParserService/DomAssemblyName.cs b/src/Main/Base/Project/Src/Services/ParserService/DomAssemblyName.cs index cee0edd61d..99471741c7 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/DomAssemblyName.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/DomAssemblyName.cs @@ -9,6 +9,7 @@ namespace ICSharpCode.SharpDevelop.Parser /// Similar to System.Reflection.AssemblyName, but does not raise an exception /// on invalid assembly names. (See SD-1307) /// + [Serializable] public sealed class DomAssemblyName : IEquatable { readonly string fullAssemblyName; @@ -79,15 +80,5 @@ namespace ICSharpCode.SharpDevelop.Parser { return other != null && fullAssemblyName == other.fullAssemblyName; } - - internal static DomAssemblyName[] Convert(System.Reflection.AssemblyName[] names) - { - if (names == null) return null; - DomAssemblyName[] n = new DomAssemblyName[names.Length]; - for (int i = 0; i < names.Length; i++) { - n[i] = new DomAssemblyName(names[i].FullName); - } - return n; - } } } diff --git a/src/Main/Base/Project/Src/Services/ParserService/IGlobalAssemblyCacheService.cs b/src/Main/Base/Project/Src/Services/ParserService/IGlobalAssemblyCacheService.cs new file mode 100644 index 0000000000..26feb2c6b3 --- /dev/null +++ b/src/Main/Base/Project/Src/Services/ParserService/IGlobalAssemblyCacheService.cs @@ -0,0 +1,30 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Parser +{ + /// + /// Interface for global assembly cache service. + /// + public interface IGlobalAssemblyCacheService + { + /// + /// Gets whether the file name is within the GAC. + /// + bool IsGACAssembly(string fileName); + + /// + /// Gets the names of all assemblies in the GAC. + /// + IEnumerable GetGacAssemblyFullNames(); + + /// + /// Gets the file name for an assembly stored in the GAC. + /// Returns null if the assembly cannot be found. + /// + string FindAssemblyInNetGac(DomAssemblyName reference); + } +} diff --git a/src/Main/Base/Project/Src/Services/ParserService/IParser.cs b/src/Main/Base/Project/Src/Services/ParserService/IParser.cs index 04e8ab9029..870a8dea69 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/IParser.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/IParser.cs @@ -2,17 +2,18 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.Collections.Generic; using System.Threading; + using ICSharpCode.Core; using ICSharpCode.NRefactory; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.SharpDevelop.Editor; -using ICSharpCode.SharpDevelop.Parser; +using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Refactoring; -namespace ICSharpCode.SharpDevelop.Project +namespace ICSharpCode.SharpDevelop.Parser { /// /// Represents a language parser that produces ParseInformation @@ -23,10 +24,7 @@ namespace ICSharpCode.SharpDevelop.Project /// /// Gets/Sets the tags used to identify tasks. /// - string[] LexerTags { - get; - set; - } + IReadOnlyList TaskListTokens { get; set; } /// /// Gets if the parser can parse the specified file. @@ -45,15 +43,20 @@ namespace ICSharpCode.SharpDevelop.Project /// Specifies whether full parse information were requested for this file. /// If this parameter is false, only the ParsedFile and TagComments on the parse information need to be set. /// + /// The parent project for this parse run. + /// Cancellation Token. /// The parse information representing the parse results. /// - /// SharpDevelop may call IParser.Parse in parallel; even on the same IParser instance - /// if there are two parallel parse requests for the same file. Parser implementations must be thread-safe. + /// The SharpDevelop parser service may call IParser.Parse in parallel; + /// even on the same IParser instance if there are two parallel parse requests for the same file. + /// Parser implementations must be thread-safe. + /// /// The SharpDevelop main thread is allowed to wait for a parse operation to finish; thus IParser /// implementations must not invoke methods on the main thread and wait for their results, /// as that would deadlock. /// - ParseInformation Parse(FileName fileName, ITextSource fileContent, bool fullParseInformationRequested); + ParseInformation Parse(FileName fileName, ITextSource fileContent, bool fullParseInformationRequested, + IProject parentProject, CancellationToken cancellationToken); ResolveResult Resolve(ParseInformation parseInfo, TextLocation location, ICompilation compilation, CancellationToken cancellationToken); diff --git a/src/Main/Base/Project/Src/Services/ParserService/IParserService.cs b/src/Main/Base/Project/Src/Services/ParserService/IParserService.cs new file mode 100644 index 0000000000..c25afafbaf --- /dev/null +++ b/src/Main/Base/Project/Src/Services/ParserService/IParserService.cs @@ -0,0 +1,291 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Project; +using ICSharpCode.SharpDevelop.Refactoring; + +namespace ICSharpCode.SharpDevelop.Parser +{ + /// + /// Manages parse runs and caches ParseInformation. + /// + public interface IParserService + { + /// + /// Gets/Sets the task list tokens. + /// The getter of this property is thread-safe; + /// the setter must only be called on the main thread. + /// + IReadOnlyList TaskListTokens { get; set; } + + #region Load Solution Projects Thread + /// + /// Gets whether the solution is being loaded, or a major re-parse is happening + /// (e.g. after adding a project). + /// + /// This property is only changed by the main thread. + bool LoadSolutionProjectsThreadRunning { get; } + + /// + /// This event is raised when the LoadSolutionProjectsThreadRunning property changes to true. + /// This always happens on the main thread. + /// + event EventHandler LoadSolutionProjectsThreadStarted; + + /// + /// This event is raised when the LoadSolutionProjectsThreadRunning property changes to false. + /// This always happens on the main thread. + /// + event EventHandler LoadSolutionProjectsThreadEnded; // TODO: rename to finished + #endregion + + #region GetCompilation + /// + /// Gets or creates a compilation for the specified project. + /// + /// + /// This method is thread-safe. + /// This method never returns null - in case of errors, a dummy compilation is created. + /// + ICompilation GetCompilation(IProject project); + + /// + /// Gets or creates a compilation for the project that contains the specified file. + /// + /// + /// This method is thread-safe. + /// This method never returns null - in case of errors, a dummy compilation is created. + /// + ICompilation GetCompilationForFile(FileName fileName); + + /// + /// Gets a snapshot of the current compilations + /// This method is useful when a consistent snapshot across multiple compilations is needed. + /// + /// + /// This method is thread-safe. + /// + SharpDevelopSolutionSnapshot GetCurrentSolutionSnapshot(); + + /// + /// Invalidates the current solution snapshot, causing + /// the next call to create a new one. + /// This method needs to be called whenever IProject.ProjectContent changes. + /// + /// + /// This method is thread-safe. + /// + void InvalidateCurrentSolutionSnapshot(); + #endregion + + #region GetExistingParsedFile + /// + /// Gets the unresolved type system for the specified file. + /// + /// Name of the file. + /// + /// Optional: requested version of the file. + /// If this parameter is specified and the existing parsed file belongs to a different version, + /// this method will return null. + /// + /// + /// Optional: If the file is part of multiple projects, specifies + /// which parsed version of the file to return (for example, different project settings + /// can cause the file to be parsed differently). + /// + /// + /// Returns the IParsedFile for the specified file, + /// or null if the file has not been parsed yet. + /// + /// This method is thread-safe. + IParsedFile GetExistingParsedFile(FileName fileName, ITextSourceVersion version = null, IProject parentProject = null); + + /// + /// Gets full parse information for the specified file, if it is available. + /// + /// Name of the file. + /// + /// Optional: requested version of the file. + /// If this parameter is specified and the existing parsed file belongs to a different version, + /// this method will return null. + /// + /// + /// Optional: If the file is part of multiple projects, specifies + /// which parsed version of the file to return (for example, different project settings + /// can cause the file to be parsed differently). + /// + /// + /// If only the IParsedFile is available (non-full parse information), this method returns null. + /// + ParseInformation GetCachedParseInformation(FileName fileName, ITextSourceVersion version = null, IProject parentProject = null); + #endregion + + #region Parse + /// + /// Parses the specified file. + /// Produces full parse information. + /// + /// Name of the file to parse + /// Optional: Content of the file to parse. + /// + /// Optional: If the file is part of multiple projects, specifies + /// which parsed version of the file to return (for example, different project settings + /// can cause the file to be parsed differently). + /// + /// + /// Returns the ParseInformation for the specified file, or null if the file cannot be parsed. + /// For files currently open in an editor, this method does not necessary reparse, but may return + /// an existing cached parse information (but only if it's still up-to-date). + /// + /// + /// This method is thread-safe. + /// + /// If is null, this method will block and wait for the main thread + /// to retrieve the latest file content. This can cause deadlocks if this method is called within a lock. + /// + /// + /// If not null, the exact file version specified will be parsed. + /// This method will not wait for the main thread in that case. + /// If the specified version is older than the latest version, the old version will be parsed + /// and returned, but the old parse information will not be registered. + /// + /// + ParseInformation Parse(FileName fileName, ITextSource fileContent = null, IProject parentProject = null, + CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Parses the specified file. + /// This method does not request full parse information. + /// + /// Name of the file to parse + /// Optional: Content of the file to parse. + /// + /// Optional: If the file is part of multiple projects, specifies + /// which parsed version of the file to return (for example, different project settings + /// can cause the file to be parsed differently). + /// + /// + /// Returns the IParsedFile for the specified file, or null if the file cannot be parsed. + /// For files currently open in an editor, this method does not necessarily reparse, but may return + /// the existing IParsedFile (but only if it's still up-to-date). + /// + /// + IParsedFile ParseFile(FileName fileName, ITextSource fileContent = null, IProject parentProject = null, + CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Parses the specified file on a background thread. + /// Produces full parse information. + /// + /// Name of the file to parse + /// Optional: Content of the file to parse. + /// + /// Optional: If the file is part of multiple projects, specifies + /// which parsed version of the file to return (for example, different project settings + /// can cause the file to be parsed differently). + /// + /// + /// + /// This method is thread-safe. + /// + /// If is null, the task wait for the main thread + /// to retrieve the latest file content. + /// This means that waiting for the task can cause deadlocks. (however, using C# 5 await is safe) + /// + /// + /// If not null, the exact file version specified will be parsed. + /// This method will not wait for the main thread in that case. + /// If the specified version is older than the latest version, the old version will be parsed + /// and returned, but the old parse information will not be registered. + /// + /// + Task ParseAsync(FileName fileName, ITextSource fileContent = null, IProject parentProject = null, + CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Parses the specified file on a background thread. + /// This method does not request full parse information. + /// + /// Name of the file to parse + /// Optional: Content of the file to parse. + /// + /// Optional: If the file is part of multiple projects, specifies + /// which parsed version of the file to return (for example, different project settings + /// can cause the file to be parsed differently). + /// + /// + /// + Task ParseFileAsync(FileName fileName, ITextSource fileContent = null, IProject parentProject = null, + CancellationToken cancellationToken = default(CancellationToken)); + #endregion + + #region Resolve + ResolveResult Resolve(ITextEditor editor, TextLocation location, + ICompilation compilation = null, + CancellationToken cancellationToken = default(CancellationToken)); + + ResolveResult Resolve(FileName fileName, TextLocation location, + ITextSource fileContent = null, ICompilation compilation = null, + CancellationToken cancellationToken = default(CancellationToken)); + + Task ResolveAsync(FileName fileName, TextLocation location, + ITextSource fileContent = null, ICompilation compilation = null, + CancellationToken cancellationToken = default(CancellationToken)); + + Task FindLocalReferencesAsync(FileName fileName, IVariable variable, Action callback, + ITextSource fileContent = null, ICompilation compilation = null, + CancellationToken cancellationToken = default(CancellationToken)); + #endregion + + #region Parsed File Listeners + /// + /// Gets whether a parser is registered for the specified file name. + /// + bool HasParser(FileName fileName); + + /// + /// Clears the cached parse information. + /// If the file does not belong to any project, this also clears the cached type system. + /// + void ClearParseInformation(FileName fileName); + + /// + /// Adds a project that owns the file and wishes to receive parse information. + /// + /// Name of the file contained in the project. + /// The parent project of the file. + /// + /// Whether to start an asynchronous parse operation for the specified file. + /// + /// + /// Specified whether the file is linked within the project, i.e. likely also belongs to another project. + /// The parser services tries to use the project that contains the file directly (non-linked) + /// as the primary parent project. + /// + void AddOwnerProject(FileName fileName, IProject project, bool startAsyncParse, bool isLinkedFile); + + /// + /// Removes a project from the owners of the file. + /// This method invokes project.UpdateParseInformation(existingParsedFile, null);. + /// (unless existingParsedFile==null) + /// + void RemoveOwnerProject(FileName fileName, IProject project); + + /// + /// Occurs whenever parse information was updated. This event is raised on the main thread. + /// + event EventHandler ParseInformationUpdated; + #endregion + } +} diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParseInformationEventArgs.cs b/src/Main/Base/Project/Src/Services/ParserService/ParseInformationEventArgs.cs index 89642ff9bb..d77b5df8ec 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/ParseInformationEventArgs.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/ParseInformationEventArgs.cs @@ -6,11 +6,13 @@ using System.Diagnostics; using ICSharpCode.Core; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop.Project; namespace ICSharpCode.SharpDevelop.Parser { public class ParseInformationEventArgs : EventArgs { + IProject parentProject; IParsedFile oldParsedFile; ParseInformation newParseInformation; @@ -23,6 +25,14 @@ namespace ICSharpCode.SharpDevelop.Parser } } + /// + /// Gets the parent project for this parse information. + /// Returns null if the parse information is not associated with any project. + /// + public IProject ParentProject { + get { return parentProject; } + } + /// /// The old parsed file. /// Returns null if no old parse information exists (first parse run). @@ -57,13 +67,14 @@ namespace ICSharpCode.SharpDevelop.Parser get { return this.NewParsedFile; } } - public ParseInformationEventArgs(IParsedFile oldParsedFile, ParseInformation newParseInformation) + public ParseInformationEventArgs(IProject parentProject, IParsedFile oldParsedFile, ParseInformation newParseInformation) { if (oldParsedFile == null && newParseInformation == null) throw new ArgumentNullException(); if (oldParsedFile != null && newParseInformation != null) { Debug.Assert(FileUtility.IsEqualFileName(oldParsedFile.FileName, newParseInformation.FileName)); } + this.parentProject = parentProject; this.oldParsedFile = oldParsedFile; this.newParseInformation = newParseInformation; } diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs b/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs index c89c43b6f9..693e80d075 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs +++ b/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs @@ -26,7 +26,6 @@ namespace ICSharpCode.SharpDevelop.Parser /// readonly object lockObj = new object(); - readonly ParsedFileListener myListener; IProjectContent projectContent; IAssemblyReference[] references = { MinimalCorlib.Instance }; bool initializing; @@ -38,19 +37,18 @@ namespace ICSharpCode.SharpDevelop.Parser throw new ArgumentNullException("project"); this.project = project; this.projectContent = initialProjectContent.SetAssemblyName(project.AssemblyName); - this.myListener = new ParsedFileListener(OnParsedFileUpdated); this.initializing = true; LoadSolutionProjects.AddJob(Initialize, "Loading " + project.Name + "...", GetInitializationWorkAmount()); } - - void OnParsedFileUpdated(IParsedFile oldFile, IParsedFile newFile) + + public void ParseInformationUpdated(IParsedFile oldFile, IParsedFile newFile) { // This method is called by the parser service within the parser service lock. lock (lockObj) { if (!disposed) projectContent = projectContent.UpdateProjectContent(oldFile, newFile); - ParserService.InvalidateCurrentSolutionSnapshot(); + SD.ParserService.InvalidateCurrentSolutionSnapshot(); } } @@ -64,7 +62,7 @@ namespace ICSharpCode.SharpDevelop.Parser disposed = true; } foreach (var fileName in GetFilesToParse(project.Items)) { - ParserService.RemoveParsedFileListener(fileName, myListener); + SD.ParserService.RemoveOwnerProject(fileName.Item1, project); } initializing = false; } @@ -107,12 +105,14 @@ namespace ICSharpCode.SharpDevelop.Parser initializing = false; } - IEnumerable GetFilesToParse(IEnumerable projectItems) + static readonly ItemType[] compilableItemTypes = { ItemType.Compile, ItemType.Page }; + + IEnumerable> GetFilesToParse(IEnumerable projectItems) { - return ( - from p in projectItems - where (p.ItemType == ItemType.Compile || p.ItemType == ItemType.Page) && !String.IsNullOrEmpty(p.FileName) - select FileName.Create(p.FileName)); + return + from p in projectItems.OfType() + where compilableItemTypes.Contains(p.ItemType) && !String.IsNullOrEmpty(p.FileName) + select Tuple.Create(FileName.Create(p.FileName), p.IsLink); } void ParseFiles(ICollection projectItems, IProgressMonitor progressMonitor) @@ -128,16 +128,17 @@ namespace ICSharpCode.SharpDevelop.Parser MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = progressMonitor.CancellationToken }, - fileName => { + tuple => { + var fileName = tuple.Item1; // Don't read files we don't have a parser for. // This avoids loading huge files (e.g. sdps) when we have no intention of parsing them. - if (ParserService.HasParser(fileName)) { - ParserService.AddParsedFileListener(fileName, myListener, startAsyncParse: false); + if (SD.ParserService.HasParser(fileName)) { + // We don't start an asynchronous parse operation since we want to + // parse on this thread. + SD.ParserService.AddOwnerProject(fileName, project, startAsyncParse: false, isLinkedFile: tuple.Item2); ITextSource content = finder.Create(fileName); if (content != null) { - // Parse the file on this thread so that AddParsedFileListener() does not - // start an asynchronous parse operation. - ParserService.ParseFile(fileName, content); + SD.ParserService.ParseFile(fileName, content, project); } } lock (progressLock) { @@ -181,7 +182,7 @@ namespace ICSharpCode.SharpDevelop.Parser lock (lockObj) { projectContent = projectContent.RemoveAssemblyReferences(this.references).AddAssemblyReferences(newReferences); this.references = newReferences.ToArray(); - ParserService.InvalidateCurrentSolutionSnapshot(); + SD.ParserService.InvalidateCurrentSolutionSnapshot(); } }, progressMonitor.CancellationToken); } @@ -230,9 +231,10 @@ namespace ICSharpCode.SharpDevelop.Parser ReparseReferences(); } } - if (e.ProjectItem.ItemType == ItemType.Compile) { + FileProjectItem fileProjectItem = e.ProjectItem as FileProjectItem; + if (fileProjectItem != null && compilableItemTypes.Contains(fileProjectItem.ItemType)) { var fileName = FileName.Create(e.ProjectItem.FileName); - ParserService.AddParsedFileListener(fileName, myListener, startAsyncParse: true); + SD.ParserService.AddOwnerProject(fileName, project, startAsyncParse: true, isLinkedFile: fileProjectItem.IsLink); } } @@ -255,7 +257,7 @@ namespace ICSharpCode.SharpDevelop.Parser } if (e.ProjectItem.ItemType == ItemType.Compile) { - ParserService.RemoveParsedFileListener(FileName.Create(e.ProjectItem.FileName), myListener); + SD.ParserService.RemoveOwnerProject(FileName.Create(e.ProjectItem.FileName), project); } } } diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParsedFileListener.cs b/src/Main/Base/Project/Src/Services/ParserService/ParsedFileListener.cs deleted file mode 100644 index b70a037ae8..0000000000 --- a/src/Main/Base/Project/Src/Services/ParserService/ParsedFileListener.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using ICSharpCode.NRefactory.TypeSystem; - -namespace ICSharpCode.SharpDevelop.Parser -{ - /// - /// Listener for parse info changes. - /// Caution: The callback is invoked within the parser service's lock. Beware of deadlocks! - /// The method is called on the thread that performed the parse operation, which might be the main thread - /// or a background thread. - /// If possible, use the event instead, which is called on the main thread - /// and after the parser service released its lock. - /// - public delegate void ParsedFileListener(IParsedFile oldFile, IParsedFile newFile); -} diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs b/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs deleted file mode 100644 index 0a3946eec8..0000000000 --- a/src/Main/Base/Project/Src/Services/ParserService/ParserService.cs +++ /dev/null @@ -1,1026 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Threading; - -using ICSharpCode.Core; -using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.Editor; -using ICSharpCode.NRefactory.Semantics; -using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; -using ICSharpCode.SharpDevelop.Editor; -using ICSharpCode.SharpDevelop.Gui; -using ICSharpCode.SharpDevelop.Project; - -namespace ICSharpCode.SharpDevelop.Parser -{ - /// - /// Stores the compilation units for files. - /// - public static class ParserService - { - static readonly object syncLock = new object(); - static IList parserDescriptors; - //static Dictionary projectContents = new Dictionary(); - static Dictionary fileEntryDict = new Dictionary(); - - #region Manage Project Contents - /// - /// Gets or creates a compilation for the specified project. - /// - public static ICompilation GetCompilation(IProject project) - { - return GetCurrentSolutionSnapshot().GetCompilation(project); - } - - /// - /// Gets or creates a compilation for the project that contains the specified file. - /// Returns a dummy compilation if there is no project for that file. - /// - public static ICompilation GetCompilationForFile(FileName fileName) - { - Solution solution = ProjectService.OpenSolution; - IProject project = solution != null ? solution.FindProjectContainingFile(fileName) : null; - if (project != null) - return GetCompilation(project); - - var entry = GetFileEntry(fileName, false); - if (entry != null && entry.parser != null) { - var parsedFile = entry.GetExistingParsedFile(); - if (parsedFile != null) { - ICompilation compilation = entry.parser.CreateCompilationForSingleFile(fileName, parsedFile); - if (compilation != null) - return compilation; - } - } - return MinimalCorlib.Instance.CreateCompilation(); - } - - // Use a WeakReference for caching the solution snapshot - it can require - // lots of memory and may not be invalidated soon enough if the user - // is only browsing code. - volatile static WeakReference currentSolutionSnapshot; - - /// - /// Gets a snapshot of the current compilations - /// This method is useful when a consistent snapshot across multiple compilations is needed. - /// - public static SharpDevelopSolutionSnapshot GetCurrentSolutionSnapshot() - { - var weakRef = currentSolutionSnapshot; - SharpDevelopSolutionSnapshot result; - if (weakRef == null || !weakRef.TryGetTarget(out result)) { - // create new snapshot if we don't have one cached - result = new SharpDevelopSolutionSnapshot(ProjectService.OpenSolution); - currentSolutionSnapshot = new WeakReference(result); - } - return result; - } - - /// - /// Invalidates the current solution snapshot, causing - /// the next call to create a new one. - /// This method needs to be called whenever IProject.ProjectContent changes. - /// - public static void InvalidateCurrentSolutionSnapshot() - { - currentSolutionSnapshot = null; - } - - [Obsolete("Use project.ProjectContent instead")] - public static IProjectContent GetProjectContent(IProject project) - { - return project.ProjectContent; - } - #endregion - - #region Initialization + ParserThread - internal static void InitializeParserService() - { - if (parserDescriptors == null) { - parserDescriptors = AddInTree.BuildItems("/Workspace/Parser", null, false); - } - } - - static DispatcherTimer timer; - - internal static void StartParserThread() - { - WorkbenchSingleton.DebugAssertMainThread(); - timer = new DispatcherTimer(DispatcherPriority.Background); - timer.Interval = TimeSpan.FromSeconds(1.5); - timer.Tick += new EventHandler(timer_Tick); - timer.Start(); - } - - internal static void StopParserThread() - { - timer.Stop(); - } - - static Task lastParseRun; - - static void timer_Tick(object sender, EventArgs e) - { - if (lastParseRun != null) { - // don't start another parse run if the last one is still running - if (!lastParseRun.IsCompleted) - return; - lastParseRun = null; - } - - IViewContent viewContent = WorkbenchSingleton.Workbench.ActiveViewContent; - if (viewContent == null) - return; - FileName fileName = viewContent.PrimaryFileName; - if (fileName == null) - return; - if (!HasParser(fileName)) - return; - - ITextSource snapshot; - IEditable editable = viewContent as IEditable; - if (editable != null) - snapshot = editable.CreateSnapshot(); - else - snapshot = GetParseableFileContent(viewContent.PrimaryFileName); - - lastParseRun = ParseAsync(fileName, snapshot).ContinueWith( - delegate(Task backgroundTask) { - ParseInformation parseInfo = backgroundTask.Result; - RaiseParserUpdateStepFinished(new ParserUpdateStepEventArgs(fileName, snapshot, parseInfo)); - }); - } - #endregion - - #region CreateParser / TaskListTokens - static readonly string[] DefaultTaskListTokens = {"HACK", "TODO", "UNDONE", "FIXME"}; - - /// - /// Gets/Sets the task list tokens. - /// This property is thread-safe. - /// - public static string[] TaskListTokens { - get { - if (PropertyService.Contains("SharpDevelop.TaskListTokens")) - return PropertyService.GetList("SharpDevelop.TaskListTokens").ToArray(); - else - return DefaultTaskListTokens; - } - set { PropertyService.SetList("SharpDevelop.TaskListTokens", value); } - } - - /// - /// Creates a new IParser instance that can parse the specified file. - /// This method is thread-safe. - /// - public static IParser CreateParser(FileName fileName) - { - if (fileName == null) - throw new ArgumentNullException("fileName"); - if (parserDescriptors == null) - return null; - foreach (ParserDescriptor descriptor in parserDescriptors) { - if (descriptor.CanParse(fileName)) { - IParser p = descriptor.CreateParser(); - if (p != null) { - p.LexerTags = TaskListTokens; - return p; - } - } - } - return null; - } - - /// - /// Gets whether a parser is registered for the specified file name. - /// - /// GetParser()/CreateParser() can still return null when HasParser() returns true - /// if there is an error during parser creation (e.g. incorrectly registered AddIn) - public static bool HasParser(FileName fileName) - { - if (fileName == null) - throw new ArgumentNullException("fileName"); - if (parserDescriptors == null) - return false; - foreach (ParserDescriptor descriptor in parserDescriptors) { - if (descriptor.CanParse(fileName)) { - return true; - } - } - return false; - } - #endregion - - #region GetParseableFileContent - /// - /// Gets the default file encoding. - /// This property is thread-safe. - /// - public static Encoding DefaultFileEncoding { - get { - return Encoding.GetEncoding(FileService.DefaultFileEncodingCodePage); - } - } - - /// - /// Gets the content of the specified file. - /// This method is thread-safe. This method involves waiting for the main thread, so using it while - /// holding a lock can lead to deadlocks. - /// - public static ITextSource GetParseableFileContent(FileName fileName) - { - ITextSource fileContent = Gui.WorkbenchSingleton.SafeThreadFunction(GetParseableFileContentForOpenFile, fileName); - if (fileContent != null) - return fileContent; - else - return new StringTextSource(ICSharpCode.AvalonEdit.Utils.FileReader.ReadFileContent(fileName, DefaultFileEncoding)); - } - - public static ITextSource GetParseableFileContent(string fileName) - { - return GetParseableFileContent(FileName.Create(fileName)); - } - - /// - /// Gets the file content for a file that is currently open. - /// Returns null if the file is not open. - /// - static ITextSource GetParseableFileContentForOpenFile(FileName fileName) - { - OpenedFile file = FileService.GetOpenedFile(fileName); - if (file != null) { - IFileDocumentProvider p = file.CurrentView as IFileDocumentProvider; - if (p != null) { - IDocument document = p.GetDocumentForFile(file); - if (document != null) { - return document.CreateSnapshot(); - } - } - - using(Stream s = file.OpenRead()) { - // load file - return new StringTextSource(ICSharpCode.AvalonEdit.Utils.FileReader.ReadFileContent(s, DefaultFileEncoding)); - } - } - return null; - } - #endregion - - #region Parse Information Management - sealed class FileEntry - { - readonly FileName fileName; - internal readonly IParser parser; - volatile IParsedFile mainParsedFile; - volatile ParseInformation cachedParseInformation; - ITextSourceVersion bufferVersion; - bool disposed; - ParsedFileListener activeListeners; - - public FileEntry(FileName fileName) - { - this.fileName = fileName; - this.parser = CreateParser(fileName); - } - - public FileEntry(FileName fileName, ParseInformation parseInfo, IParser parser) - { - this.fileName = fileName; - this.parser = parser; - this.mainParsedFile = parseInfo != null ? parseInfo.ParsedFile : null; - if (parseInfo != null && parseInfo.IsFullParseInformation) - this.cachedParseInformation = parseInfo; - } - - public void AddParsedFileListener(ParsedFileListener listener, bool startAsyncParse) - { - bool isNewEntry; - lock (this) { - isNewEntry = (this.activeListeners == null); - this.activeListeners += listener; - if (mainParsedFile != null) { - // We already have parse info: tell the new listener - listener(null, mainParsedFile); - // No need to start async parse. - isNewEntry = false; - } - } - if (isNewEntry && startAsyncParse) - ParseAsync(null).FireAndForget(); - } - - public void RemoveParsedFileListener(ParsedFileListener listener) - { - lock (this) { - this.activeListeners += listener; - } - } - - public bool HasParseListeners { - get { - lock (this) { - return this.activeListeners != null; - } - } - } - - public ParseInformation GetCachedParseInformation() - { - return cachedParseInformation; // read volatile - } - - public ParseInformation GetCachedParseInformation(ITextSourceVersion version) - { - if (version == null) - return GetCachedParseInformation(); - lock (this) { - if (bufferVersion != null && bufferVersion.BelongsToSameDocumentAs(version)) { - if (bufferVersion.CompareAge(version) >= 0) - return cachedParseInformation; - } - } - return null; - } - - public IParsedFile GetExistingParsedFile() - { - return this.mainParsedFile; // read volatile - } - - public ParseInformation Parse(ITextSource fileContent) - { - if (fileContent == null) { - fileContent = WorkbenchSingleton.SafeThreadFunction(GetParseableFileContentForOpenFile, fileName); - } - - ParseInformation parseInfo; - IParsedFile parsedFile; - DoParse(fileContent, true, out parseInfo, out parsedFile); - return parseInfo; - } - - public IParsedFile ParseFile(ITextSource fileContent) - { - if (fileContent == null) { - fileContent = WorkbenchSingleton.SafeThreadFunction(GetParseableFileContentForOpenFile, fileName); - } - - ParseInformation parseInfo; - IParsedFile parsedFile; - DoParse(fileContent, false, out parseInfo, out parsedFile); - return parsedFile; - } - - void DoParse(ITextSource fileContent, - bool fullParseInformationRequested, out ParseInformation resultParseInfo, out IParsedFile resultUnit) - { - resultParseInfo = null; - resultUnit = null; - - if (parser == null) - return; - - if (fileContent == null) { - // No file content was specified. Because the callers of this method already check for currently open files, - // we can assume that the file isn't open and simply read it from disk. - lock (this) { - // Don't bother reading the file if this FileEntry was already disposed. - if (this.disposed) - return; - } - string fileAsString; - try { - fileAsString = ICSharpCode.AvalonEdit.Utils.FileReader.ReadFileContent(fileName, DefaultFileEncoding); - } catch (IOException) { - // It is possible that the file gets deleted/becomes inaccessible while a background parse - // operation is enqueued, so we have to handle IO exceptions. - return; - } catch (UnauthorizedAccessException) { - return; - } - fileContent = new StringTextSource(fileAsString); - } - - ITextSourceVersion fileContentVersion = fileContent.Version; - lock (this) { - if (this.disposed) - return; - - if (fileContentVersion != null && this.bufferVersion != null && this.bufferVersion.BelongsToSameDocumentAs(fileContentVersion)) { - if (this.bufferVersion.CompareAge(fileContentVersion) >= 0) { - // If full parse info is requested, ensure we have full parse info. - if (!(fullParseInformationRequested && this.cachedParseInformation == null)) { - resultParseInfo = this.cachedParseInformation; - resultUnit = this.mainParsedFile; - return; - } - } - } - } // exit lock - - // We now leave the lock to do the actual parsing. - // This is done to allow IParser implementations to invoke methods on the main thread without - // risking deadlocks. - - try { - resultParseInfo = parser.Parse(fileName, fileContent, fullParseInformationRequested); - } catch (Exception ex) { - throw new ApplicationException("Error parsing " + fileName, ex); - } - if (resultParseInfo == null) - throw new NullReferenceException(parser.GetType().Name + ".Parse() returned null"); - if (fullParseInformationRequested && !resultParseInfo.IsFullParseInformation) - throw new InvalidOperationException(parser.GetType().Name + ".Parse() did not return full parse info as requested."); - - resultUnit = resultParseInfo.ParsedFile; - // ensure the new unit is frozen before we make it visible to the outside world - FreezableHelper.Freeze(resultUnit); - - lock (this) { - if (this.disposed) { - resultParseInfo = null; - resultUnit = null; - return; - } - - // ensure we never go backwards in time (we need to repeat this check after we've reacquired the lock) - if (fileContentVersion != null && this.bufferVersion != null && this.bufferVersion.BelongsToSameDocumentAs(fileContentVersion)) { - if (this.bufferVersion.CompareAge(fileContentVersion) >= 0) { - if (fullParseInformationRequested && this.cachedParseInformation == null) { - // We must not go backwards in time, but the newer version that we have parsed - // does not have full parse information. - // Thus, we return the outdated (but "new enough") parse information that we found above, - // but we won't register it. - return; - } else { - resultParseInfo = this.cachedParseInformation; - resultUnit = this.mainParsedFile; - return; - } - } - } - - if (this.activeListeners != null) - this.activeListeners(mainParsedFile, resultParseInfo.ParsedFile); - RaiseParseInformationUpdated(new ParseInformationEventArgs(mainParsedFile, resultParseInfo)); - - this.bufferVersion = fileContentVersion; - this.mainParsedFile = resultUnit; - // Cached the new parse information around if it was requested, or if we had already cached parse information previously. - if (resultParseInfo.IsFullParseInformation && (fullParseInformationRequested || this.cachedParseInformation != null)) { - this.cachedParseInformation = resultParseInfo; - } else { - this.cachedParseInformation = null; - } - } // exit lock - } - - public void Clear() - { - IParsedFile parseInfo; - lock (this) { - // by setting the disposed flag, we'll cause all running ParseFile() calls to return null and not - // call into the parser anymore - this.disposed = true; - parseInfo = this.mainParsedFile; - this.bufferVersion = null; - this.mainParsedFile = null; - if (parseInfo != null && this.activeListeners != null) - this.activeListeners(parseInfo, null); - } - if (parseInfo != null) - RaiseParseInformationUpdated(new ParseInformationEventArgs(parseInfo, null)); - } - - void SnapshotFileContentForAsyncOperation(ref ITextSource fileContent, out bool lookupOpenFileOnTargetThread) - { - if (fileContent != null) { - lookupOpenFileOnTargetThread = false; - // File content was explicitly specified: - // Let's make a snapshot in case the text source is mutable. - fileContent = fileContent.CreateSnapshot(); - } else if (WorkbenchSingleton.InvokeRequired) { - // fileContent == null && not on the main thread: - // Don't fetch the file content right now; if we need to SafeThreadCall() anyways, - // it's better to do so from the background task. - lookupOpenFileOnTargetThread = true; - } else { - // fileContent == null && we are on the main thread: - // Let's look up the file in the list of open files right now - // so that we don't need to SafeThreadCall() later on. - lookupOpenFileOnTargetThread = false; - fileContent = GetParseableFileContentForOpenFile(fileName); - } - } - - Task runningAsyncParseTask; - ITextSourceVersion runningAsyncParseFileContentVersion; - - public Task ParseAsync(ITextSource fileContent) - { - bool lookupOpenFileOnTargetThread; - SnapshotFileContentForAsyncOperation(ref fileContent, out lookupOpenFileOnTargetThread); - - ITextSourceVersion fileContentVersion = fileContent != null ? fileContent.Version : null; - if (fileContentVersion != null) { - // Optimization: - // don't start a background task if fileContent was specified and up-to-date parse info is available - lock (this) { - if (cachedParseInformation != null && bufferVersion != null && bufferVersion.BelongsToSameDocumentAs(fileContentVersion)) { - if (bufferVersion.CompareAge(fileContentVersion) >= 0) { - return Task.FromResult(cachedParseInformation); - } - } - } - } - - var task = new Task( - delegate { - try { - if (lookupOpenFileOnTargetThread) { - fileContent = WorkbenchSingleton.SafeThreadFunction(GetParseableFileContentForOpenFile, fileName); - } - ParseInformation parseInfo; - IParsedFile parsedFile; - DoParse(fileContent, true, out parseInfo, out parsedFile); - return parseInfo; - } catch (Exception ex) { - MessageService.ShowException(ex, "Error during async parse"); - return null; - } finally { - lock (this) { - this.runningAsyncParseTask = null; - this.runningAsyncParseFileContentVersion = null; - } - } - } - ); - if (fileContentVersion != null) { - // Optimization: when additional async parse runs are requested while the parser is already - // running for that file content, return the task that's already running - // instead of starting additional copies. - lock (this) { - if (runningAsyncParseTask != null && runningAsyncParseFileContentVersion.BelongsToSameDocumentAs(fileContentVersion)) { - if (runningAsyncParseFileContentVersion.CompareAge(fileContentVersion) >= 0) - return runningAsyncParseTask; - } - this.runningAsyncParseTask = task; - this.runningAsyncParseFileContentVersion = fileContentVersion; - } - } - task.Start(); - return task; - } - - public Task ParseFileAsync(ITextSource fileContent) - { - bool lookupOpenFileOnTargetThread; - SnapshotFileContentForAsyncOperation(ref fileContent, out lookupOpenFileOnTargetThread); - - // TODO: don't use background task if fileContent was specified and up-to-date parse info is available - return Task.Run( - delegate { - try { - if (lookupOpenFileOnTargetThread) { - fileContent = WorkbenchSingleton.SafeThreadFunction(GetParseableFileContentForOpenFile, fileName); - } - ParseInformation parseInfo; - IParsedFile parsedFile; - DoParse(fileContent, false, out parseInfo, out parsedFile); - return parsedFile; - } catch (Exception ex) { - MessageService.ShowException(ex, "Error during async parse"); - return null; - } - } - ); - } - } - - static FileEntry GetFileEntry(FileName fileName, bool createOnDemand) - { - if (fileName == null) - throw new ArgumentNullException("fileName"); - - FileEntry entry; - lock (syncLock) { - if (!fileEntryDict.TryGetValue(fileName, out entry)) { - if (!createOnDemand) - return null; - entry = new FileEntry(fileName); - fileEntryDict.Add(fileName, entry); - } - } - return entry; - } - - /// - /// Removes all parse information (both IParsedFile and ParseInformation) for the specified file. - /// This method is thread-safe. - /// - /// Whether to remove the parse information even if there are listeners - /// registered for it. - public static void ClearParseInformation(FileName fileName, bool force = false) - { - if (fileName == null) - throw new ArgumentNullException("fileName"); - - LoggingService.Info("ClearParseInformation: " + fileName); - - FileEntry entry; - lock (syncLock) { - if (fileEntryDict.TryGetValue(fileName, out entry)) { - // avoid concurrent deregistration of parse listener between entry.HasParseListeners and entry.Clear() - lock (entry) { - if (force || !entry.HasParseListeners) { - fileEntryDict.Remove(fileName); - // Call entry.Clear() within the lock so that we don't have a race condition - // when the entry is immediately recreated by another thread. - entry.Clear(); - } - } - } - } - } - - /// - /// This is the old method returning potentially-stale parse information. - /// Use Parse()/ParseFile() instead if you need fresh parse info; otherwise use GetExistingParsedFile(). - /// - [Obsolete("Use Parse()/ParseFile() instead if you need fresh parse info; otherwise use GetExistingParsedFile().")] - public static ParseInformation GetParseInformation(string fileName) - { - if (string.IsNullOrEmpty(fileName)) - return null; - return Parse(FileName.Create(fileName)); - } - - /// - /// Gets full parse information for the specified file, if it is available. - /// - /// - /// Returns the ParseInformation for the specified file, - /// or null if it is not in the parse information cache. - /// - /// If only the IParsedFile is available (non-full parse information), this method - /// returns null. - /// - /// - /// This method is thread-safe. - /// - /// The ParserService may drop elements from the cache at any moment, - /// only IParsedFile will be stored for a longer time. - /// - public static ParseInformation GetCachedParseInformation(FileName fileName) - { - if (string.IsNullOrEmpty(fileName)) - return null; - FileEntry entry = GetFileEntry(fileName, false); - if (entry != null) - return entry.GetCachedParseInformation(); - else - return null; - } - - /// - /// Gets full parse information for the specified file, if it is available and at least as recent as the specified version. - /// - /// - /// If only the IParsedFile is available (non-full parse information), this method - /// returns null. - /// If parse information is avaiable but potentially outdated (older than , - /// or belonging to a different document), this method returns null. - /// - public static ParseInformation GetCachedParseInformation(FileName fileName, ITextSourceVersion version) - { - if (string.IsNullOrEmpty(fileName)) - return null; - FileEntry entry = GetFileEntry(fileName, false); - if (entry != null) - return entry.GetCachedParseInformation(version); - else - return null; - } - - /// - /// Gets parse information for the specified file. - /// - /// Name of the file. - /// - /// Returns the IParsedFile for the specified file, - /// or null if the file has not been parsed yet. - /// - /// This method is thread-safe. - public static IParsedFile GetExistingParsedFile(FileName fileName) - { - if (string.IsNullOrEmpty(fileName)) - return null; - FileEntry entry = GetFileEntry(fileName, false); - if (entry != null) - return entry.GetExistingParsedFile(); - else - return null; - } - - /// - /// Parses the specified file. - /// Produces full parse information. - /// - /// Name of the file to parse - /// Optional: Content of the file to parse. - /// The fileContent is taken as a hint - if a newer version than it is already available, that will be used instead. - /// - /// - /// Returns the ParseInformation for the specified file, or null if the file cannot be parsed. - /// For files currently open in an editor, this method does not necessary reparse, but may return - /// an existing cached parse information (but only if it's still up-to-date). - /// - /// - /// This method is thread-safe. - /// If is null, this method will block and wait for the main thread - /// to retrieve the latest file content. This can cause deadlocks if this method is called within a lock. - /// - public static ParseInformation Parse(FileName fileName, ITextSource fileContent = null) - { - return GetFileEntry(fileName, true).Parse(fileContent); - } - - /// - /// Asynchronous version of . - /// - /// Name of the file to parse - /// Optional: Content of the file to parse. - /// The fileContent is taken as a hint - if a newer version than it is already available, that will be used instead. - /// - /// - /// - /// This method is thread-safe. - /// If is null, the task may wait for the main thread. - /// This means that waiting for the task can cause deadlocks. (however, using C# 5 'await' is safe) - /// - public static Task ParseAsync(FileName fileName, ITextSource fileContent = null) - { - return GetFileEntry(fileName, true).ParseAsync(fileContent); - } - - /// - /// Parses the specified file. - /// This method does not request full parse information. - /// - /// Name of the file to parse - /// Optional: Content of the file to parse. - /// The fileContent is taken as a hint - if a newer version than it is already available, that will be used instead. - /// - /// - /// Returns the IParsedFile for the specified file, or null if the file cannot be parsed. - /// For files currently open in an editor, this method does not necessarily reparse, but may return - /// the existing IParsedFile (but only if it's still up-to-date). - /// - /// - public static IParsedFile ParseFile(FileName fileName, ITextSource fileContent = null) - { - return GetFileEntry(fileName, true).ParseFile(fileContent); - } - - /// - /// Asynchronous version of . - /// - /// - /// - public static Task ParseFileAsync(FileName fileName, ITextSource fileContent = null) - { - return GetFileEntry(fileName, true).ParseFileAsync(fileContent); - } - - /// - /// Parses the current view content. - /// This method can only be called from the main thread. - /// - public static ParseInformation ParseCurrentViewContent() - { - WorkbenchSingleton.AssertMainThread(); - IViewContent viewContent = WorkbenchSingleton.Workbench.ActiveViewContent; - if (viewContent != null) - return ParseViewContent(viewContent); - else - return null; - } - - /// - /// Parses the specified view content. - /// This method can only be called from the main thread. - /// - public static ParseInformation ParseViewContent(IViewContent viewContent) - { - if (viewContent == null) - throw new ArgumentNullException("viewContent"); - WorkbenchSingleton.AssertMainThread(); - if (string.IsNullOrEmpty(viewContent.PrimaryFileName)) - return null; - IEditable editable = viewContent as IEditable; - if (editable != null) - return Parse(viewContent.PrimaryFileName, editable.CreateSnapshot()); - else - return Parse(viewContent.PrimaryFileName); - } - - /// - /// Gets the parser instance that is responsible for the specified file. - /// Will create a new IParser instance on demand. - /// This method is thread-safe. - /// - public static IParser GetParser(FileName fileName) - { - return GetFileEntry(fileName, true).parser; - } - - /// - /// Registers a compilation unit in the parser service. - /// Does not fire the OnParseInformationUpdated event, please use this for unit tests only! - /// - public static void RegisterParseInformation(FileName fileName, ParseInformation parseInfo, IParser parser = null) - { - lock (syncLock) { - fileEntryDict[fileName] = new FileEntry(fileName, parseInfo, parser); - } - } - - /// - /// Replaces the list of available parsers. - /// Please use this for unit tests only! - /// - public static void RegisterAvailableParsers(params ParserDescriptor[] descriptors) - { - lock (syncLock) { - parserDescriptors = new List(); - parserDescriptors.AddRange(descriptors); - } - } - - #endregion - - #region ParseInformationUpdated / ParserUpdateStepFinished events - /// - /// Occurs whenever parse information was updated. This event is raised on the main thread. - /// - public static event EventHandler ParseInformationUpdated = delegate {}; - - static void RaiseParseInformationUpdated(ParseInformationEventArgs e) - { - // RaiseParseInformationUpdated is called inside a lock, but we don't want to raise the event inside that lock. - // To ensure events are raised in the same order, we always invoke on the main thread. - WorkbenchSingleton.SafeThreadAsyncCall( - delegate { - string addition; - if (e.OldParsedFile == null) - addition = " (new)"; - else if (e.NewParsedFile == null) - addition = " (removed)"; - else - addition = " (updated)"; - LoggingService.Debug("ParseInformationUpdated " + e.FileName + addition); - ParseInformationUpdated(null, e); - }); - } - - /// - /// Occurs when the parse step started by a timer finishes. - /// This event is raised on the main thread. - /// - public static event EventHandler ParserUpdateStepFinished = delegate {}; - - static void RaiseParserUpdateStepFinished(ParserUpdateStepEventArgs e) - { - WorkbenchSingleton.SafeThreadAsyncCall( - delegate { - ParserUpdateStepFinished(null, e); - }); - } - - #endregion - - #region LoadSolutionProjects - - /// - /// Gets whether the LoadSolutionProjects thread is currently running. - /// - public static bool LoadSolutionProjectsThreadRunning { - get { return LoadSolutionProjects.IsThreadRunning; } - } - - /// - /// Occurs when the 'load solution projects' thread has finished. - /// This event is not raised when the 'load solution projects' is aborted because the solution was closed. - /// This event is raised on the main thread. - /// - public static event EventHandler LoadSolutionProjectsThreadEnded { - add { LoadSolutionProjects.ThreadEnded += value; } - remove { LoadSolutionProjects.ThreadEnded -= value; } - } - - internal static void OnSolutionLoaded() - { - //foreach (IProject project in ProjectService.OpenSolution.Projects) { - // RegisterProjectContentForAddedProject(project); - //} - InvalidateCurrentSolutionSnapshot(); - } - - internal static void OnSolutionClosed() - { - LoadSolutionProjects.CancelAllJobs(); - lock (syncLock) { - //projectContents.Clear(); - } - ClearAllFileEntries(); - InvalidateCurrentSolutionSnapshot(); - } - - static void ClearAllFileEntries() - { - FileEntry[] entries; - lock (syncLock) { - entries = fileEntryDict.Values.ToArray(); - fileEntryDict.Clear(); - } - foreach (FileEntry entry in entries) - entry.Clear(); - } - #endregion - - public static ResolveResult Resolve(FileName fileName, TextLocation location, ITextSource fileContent = null, - CancellationToken cancellationToken = default(CancellationToken)) - { - var entry = GetFileEntry(fileName, true); - if (entry.parser == null) - return null; - var parseInfo = entry.Parse(fileContent); - if (parseInfo == null) - return null; - var compilation = GetCompilationForFile(fileName); - ResolveResult rr = entry.parser.Resolve(parseInfo, location, compilation, cancellationToken); - LoggingService.Debug("Resolved " + location + " to " + rr); - return rr; - } - - public static Task ResolveAsync(FileName fileName, TextLocation location, ITextSource fileContent = null, - CancellationToken cancellationToken = default(CancellationToken)) - { - var entry = GetFileEntry(fileName, true); - if (entry.parser == null) - return Task.FromResult(null); - return entry.ParseAsync(fileContent).ContinueWith( - delegate (Task parseInfoTask) { - var parseInfo = parseInfoTask.Result; - if (parseInfo == null) - return null; - var compilation = GetCompilationForFile(fileName); - ResolveResult rr = entry.parser.Resolve(parseInfo, location, compilation, cancellationToken); - LoggingService.Debug("Resolved " + location + " to " + rr); - return rr; - }, cancellationToken); - } - - /// - /// Adds a parsed file listener for the specified file. - /// If parse information for the file is already known, this method immediately invokes - /// listener(null, existingParsedFile);. - /// - /// If no parse information is already known, this parameter - /// controls whether an asynchronous parse operation is started. - public static void AddParsedFileListener(FileName fileName, ParsedFileListener listener, bool startAsyncParse) - { - if (listener == null) - throw new ArgumentNullException("listener"); - GetFileEntry(fileName, true).AddParsedFileListener(listener, startAsyncParse); - } - - /// - /// Removes a parsed file listener for the specified file. - /// This method invokes listener(existingParsedFile, null);. (unless existingParsedFile==null) - /// - /// - /// When the last file listener is removed, the stored parse information is cleared. - /// - public static void RemoveParsedFileListener(FileName fileName, ParsedFileListener listener) - { - if (listener == null) - throw new ArgumentNullException("listener"); - FileEntry entry = GetFileEntry(fileName, false); - if (entry == null) - return; - entry.RemoveParsedFileListener(listener); - ClearParseInformation(fileName, force: false); - } - } -} diff --git a/src/Main/Base/Project/Src/Services/ProjectService/ParseableFileContentFinder.cs b/src/Main/Base/Project/Src/Services/ProjectService/ParseableFileContentFinder.cs index 978185aa35..7e93d6a11c 100644 --- a/src/Main/Base/Project/Src/Services/ProjectService/ParseableFileContentFinder.cs +++ b/src/Main/Base/Project/Src/Services/ProjectService/ParseableFileContentFinder.cs @@ -17,7 +17,7 @@ namespace ICSharpCode.SharpDevelop.Project /// public class ParseableFileContentFinder { - FileName[] viewContentFileNamesCollection = WorkbenchSingleton.SafeThreadFunction(() => FileService.OpenedFiles.Select(f => f.FileName).ToArray()); + FileName[] viewContentFileNamesCollection = SD.MainThread.InvokeIfRequired(() => FileService.OpenedFiles.Select(f => f.FileName).ToArray()); /// /// Retrieves the file contents for the specified project items. @@ -26,10 +26,10 @@ namespace ICSharpCode.SharpDevelop.Project { foreach (FileName name in viewContentFileNamesCollection) { if (FileUtility.IsEqualFileName(name, fileName)) - return WorkbenchSingleton.SafeThreadFunction(ParserService.GetParseableFileContent, fileName.ToString()); + return SD.FileService.GetFileContent(fileName); } try { - return new StringTextSource(ICSharpCode.AvalonEdit.Utils.FileReader.ReadFileContent(fileName, ParserService.DefaultFileEncoding)); + return new StringTextSource(ICSharpCode.AvalonEdit.Utils.FileReader.ReadFileContent(fileName, SD.FileService.DefaultFileEncoding)); } catch (IOException) { return null; } catch (UnauthorizedAccessException) { diff --git a/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs b/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs index 85b15e4b14..665cc85ea1 100644 --- a/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs +++ b/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs @@ -300,8 +300,7 @@ namespace ICSharpCode.SharpDevelop.Project } catch (Exception ex) { MessageService.ShowException(ex); } - // Create project contents for solution - ParserService.OnSolutionLoaded(); + SD.ParserService.InvalidateCurrentSolutionSnapshot(); Project.Converter.UpgradeViewContent.ShowIfRequired(openSolution); @@ -549,7 +548,6 @@ namespace ICSharpCode.SharpDevelop.Project openSolution.Dispose(); openSolution = null; - ParserService.OnSolutionClosed(); OnSolutionClosed(EventArgs.Empty); CommandManager.InvalidateRequerySuggested(); diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs index d46904cfc7..61550539e1 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs @@ -67,7 +67,8 @@ namespace ICSharpCode.SharpDevelop.Refactoring throw new ArgumentNullException("progressMonitor"); if (callback == null) throw new ArgumentNullException("callback"); - if (ParserService.LoadSolutionProjectsThreadRunning) { + SD.MainThread.VerifyAccess(); + if (SD.ParserService.LoadSolutionProjectsThreadRunning) { progressMonitor.ShowingDialog = true; MessageService.ShowMessage("${res:SharpDevelop.Refactoring.LoadSolutionProjectsThreadRunning}"); progressMonitor.ShowingDialog = false; @@ -90,26 +91,34 @@ namespace ICSharpCode.SharpDevelop.Refactoring public static IObservable FindReferences(IEntity entity, IProgressMonitor progressMonitor) { - return ReactiveExtensions.CreateObservable((monitor, callback) => FindReferencesAsync(entity, monitor, callback), progressMonitor); + return ReactiveExtensions.CreateObservable( + (monitor, callback) => FindReferencesAsync(entity, monitor, callback), + progressMonitor); } /// /// Finds references to a local variable. /// - public static void FindReferences(IVariable variable, Action callback) + public static async Task FindLocalReferencesAsync(IVariable variable, IProgressMonitor progressMonitor) { if (variable == null) throw new ArgumentNullException("variable"); - if (callback == null) - throw new ArgumentNullException("callback"); + if (progressMonitor == null) + throw new ArgumentNullException("progressMonitor"); var fileName = FileName.Create(variable.Region.FileName); - IParser parser = ParserService.GetParser(fileName); - var fileContent = ParserService.GetParseableFileContent(fileName); - ParseInformation pi = ParserService.Parse(fileName, fileContent); - if (pi == null || parser == null) - return; - var compilation = ParserService.GetCompilationForFile(fileName); - parser.FindLocalReferences(pi, fileContent, variable, compilation, callback, CancellationToken.None); + List references = new List(); + await SD.ParserService.FindLocalReferencesAsync( + fileName, variable, + r => { lock (references) references.Add(r); }, + cancellationToken: progressMonitor.CancellationToken); + return new SearchedFile(fileName, references); + } + + public static IObservable FindLocalReferences(IVariable variable, IProgressMonitor progressMonitor) + { + return ReactiveExtensions.CreateObservable( + (monitor, callback) => FindLocalReferencesAsync(variable, monitor).ContinueWith(t => callback(t.Result)), + progressMonitor); } #endregion @@ -148,7 +157,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring static SharpDevelopSolutionSnapshot GetSolutionSnapshot(ICompilation compilation) { var snapshot = compilation.SolutionSnapshot as SharpDevelopSolutionSnapshot; - return snapshot ?? ParserService.GetCurrentSolutionSnapshot(); + return snapshot ?? SD.ParserService.GetCurrentSolutionSnapshot(); } diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs index 2e41e95600..656de6edc0 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferencesAndRenameHelper.cs @@ -234,6 +234,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring */ #region Common helper functions + [Obsolete] public static ProvidedDocumentInformation GetDocumentInformation(string fileName) { OpenedFile file = FileService.GetOpenedFile(fileName); @@ -246,7 +247,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring } } } - ITextSource fileContent = ParserService.GetParseableFileContent(fileName); + ITextSource fileContent = SD.FileService.GetFileContent(fileName); return new ProvidedDocumentInformation(fileContent, fileName, 0); } @@ -352,6 +353,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring } */ + [Obsolete] public static void ShowAsSearchResults(string title, List list) { if (list == null) return; @@ -361,7 +363,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring IHighlighter highlighter = null; foreach (Reference r in list) { if (document == null || fileName != r.FileName) { - document = new TextDocument(ParserService.GetParseableFileContent(r.FileName)); + document = new TextDocument(SD.FileService.GetFileContent(r.FileName)); fileName = r.FileName; var def = HighlightingManager.Instance.GetDefinitionByExtension(Path.GetExtension(r.FileName)); if (def != null) @@ -530,7 +532,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring public static void RunFindReferences(IEntity entity) { string entityName = (entity.DeclaringTypeDefinition != null ? entity.DeclaringTypeDefinition.Name + "." + entity.Name : entity.Name); - var monitor = WorkbenchSingleton.StatusBar.CreateProgressMonitor(); + var monitor = SD.StatusBar.CreateProgressMonitor(); var results = FindReferenceService.FindReferences(entity, monitor); SearchResultsPad.Instance.ShowSearchResults(StringParser.Parse("${res:SharpDevelop.Refactoring.ReferencesTo}", new StringTagPair("Name", entityName)), results); SearchResultsPad.Instance.BringToFront(); @@ -538,13 +540,10 @@ namespace ICSharpCode.SharpDevelop.Refactoring public static void RunFindReferences(LocalResolveResult local) { - var references = new List(); - FindReferenceService.FindReferences(local.Variable, r => { lock (references) references.Add(r); }); - FindReferencesAndRenameHelper.ShowAsSearchResults( - StringParser.Parse("${res:SharpDevelop.Refactoring.ReferencesTo}", - new StringTagPair("Name", local.Variable.Name)), - references - ); + var monitor = SD.StatusBar.CreateProgressMonitor(); + var results = FindReferenceService.FindLocalReferences(local.Variable, monitor); + SearchResultsPad.Instance.ShowSearchResults(StringParser.Parse("${res:SharpDevelop.Refactoring.ReferencesTo}", new StringTagPair("Name", local.Variable.Name)), results); + SearchResultsPad.Instance.BringToFront(); } // public static ICSharpCode.Core.WinForms.MenuCommand MakeFindReferencesMenuCommand(EventHandler handler) diff --git a/src/Main/Base/Project/Src/Services/SD.cs b/src/Main/Base/Project/Src/Services/SD.cs index f6febc9804..5a24175d31 100644 --- a/src/Main/Base/Project/Src/Services/SD.cs +++ b/src/Main/Base/Project/Src/Services/SD.cs @@ -2,11 +2,13 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.ComponentModel; using System.ComponentModel.Design; using ICSharpCode.Core; using ICSharpCode.Core.Implementation; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Parser; namespace ICSharpCode.SharpDevelop { @@ -60,6 +62,10 @@ namespace ICSharpCode.SharpDevelop get { return GetRequiredService(); } } + public static IMessageLoop MainThread { + get { return GetRequiredService(); } + } + /// /// Gets the status bar. /// @@ -82,5 +88,17 @@ namespace ICSharpCode.SharpDevelop public static IAnalyticsMonitor AnalyticsMonitor { get { return GetRequiredService(); } } + + public static IParserService ParserService { + get { return GetRequiredService(); } + } + + public static IFileService FileService { + get { return GetRequiredService(); } + } + + public static IGlobalAssemblyCacheService GlobalAssemblyCache { + get { return GetRequiredService(); } + } } } diff --git a/src/Main/Base/Project/Src/Services/Tasks/TaskService.cs b/src/Main/Base/Project/Src/Services/Tasks/TaskService.cs index 1bc14a6e88..f88de90baa 100644 --- a/src/Main/Base/Project/Src/Services/Tasks/TaskService.cs +++ b/src/Main/Base/Project/Src/Services/Tasks/TaskService.cs @@ -77,15 +77,15 @@ namespace ICSharpCode.SharpDevelop internal static void Initialize() { // avoid trouble with double initialization - ParserService.ParseInformationUpdated -= ParserService_ParseInformationUpdated; - ParserService.ParseInformationUpdated += ParserService_ParseInformationUpdated; + SD.ParserService.ParseInformationUpdated -= ParserService_ParseInformationUpdated; + SD.ParserService.ParseInformationUpdated += ParserService_ParseInformationUpdated; ProjectService.SolutionClosed -= ProjectServiceSolutionClosed; ProjectService.SolutionClosed += ProjectServiceSolutionClosed; } static void ParserService_ParseInformationUpdated(object sender, ParseInformationEventArgs e) { - if (e.NewParsedFile == ParserService.GetExistingParsedFile(e.FileName)) { + if (e.NewParsedFile == SD.ParserService.GetExistingParsedFile(e.FileName)) { // Call UpdateCommentTags only for the main parse information (if a file is in multiple projects), // and only if the results haven't already been replaced with a more recent ParseInformation. if (e.NewParseInformation != null) { diff --git a/src/Main/Base/Project/Src/Util/ExtensionMethods.cs b/src/Main/Base/Project/Src/Util/ExtensionMethods.cs index 725bb20b2d..a9ae133b98 100644 --- a/src/Main/Base/Project/Src/Util/ExtensionMethods.cs +++ b/src/Main/Base/Project/Src/Util/ExtensionMethods.cs @@ -583,7 +583,7 @@ namespace ICSharpCode.SharpDevelop return newContent; } - public static int IndexOfAny(this string haystack, string[] needles, int startIndex, out int matchLength) + public static int IndexOfAny(this string haystack, IEnumerable needles, int startIndex, out int matchLength) { if (haystack == null) throw new ArgumentNullException("haystack"); diff --git a/src/Main/Base/Project/Src/Util/MultiDictionary.cs b/src/Main/Base/Project/Src/Util/MultiDictionary.cs new file mode 100644 index 0000000000..c984d3a7d0 --- /dev/null +++ b/src/Main/Base/Project/Src/Util/MultiDictionary.cs @@ -0,0 +1,115 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory; + +namespace ICSharpCode.SharpDevelop +{ + /// + /// A dictionary that allows multiple pairs with the same key. + /// + public class MultiDictionary : ILookup + { + Dictionary> dict; + + public MultiDictionary() + { + } + + public MultiDictionary(IEqualityComparer comparer) + { + dict = new Dictionary>(comparer); + } + + public void Add(TKey key, TValue value) + { + List valueList; + if (!dict.TryGetValue(key, out valueList)) { + valueList = new List(); + dict.Add(key, valueList); + } + valueList.Add(value); + } + + public bool Remove(TKey key, TValue value) + { + List valueList; + if (dict.TryGetValue(key, out valueList)) { + if (valueList.Remove(value)) { + if (valueList.Count == 0) + dict.Remove(key); + return true; + } + } + return false; + } + + public void Clear() + { + dict.Clear(); + } + + public IReadOnlyList this[TKey key] { + get { + List list; + if (dict.TryGetValue(key, out list)) + return list; + else + return new TValue[0]; + } + } + + public int Count { + get { return dict.Count; } + } + + IEnumerable ILookup.this[TKey key] { + get { return this[key]; } + } + + bool ILookup.Contains(TKey key) + { + return dict.ContainsKey(key); + } + + public IEnumerator> GetEnumerator() + { + foreach (var pair in dict) + yield return new Grouping(pair.Key, pair.Value); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + sealed class Grouping : IGrouping + { + readonly TKey key; + readonly List values; + + public Grouping(TKey key, List values) + { + this.key = key; + this.values = values; + } + + public TKey Key { + get { return key; } + } + + public IEnumerator GetEnumerator() + { + return values.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return values.GetEnumerator(); + } + } + } +} diff --git a/src/Main/Base/Project/Src/Util/ReactiveExtensions.cs b/src/Main/Base/Project/Src/Util/ReactiveExtensions.cs index a44469aa02..64a636b6e2 100644 --- a/src/Main/Base/Project/Src/Util/ReactiveExtensions.cs +++ b/src/Main/Base/Project/Src/Util/ReactiveExtensions.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; -using System.Reflection; +using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; -using System.Windows.Threading; + using ICSharpCode.SharpDevelop.Gui; namespace ICSharpCode.SharpDevelop @@ -91,15 +91,17 @@ namespace ICSharpCode.SharpDevelop return source.Subscribe(new AnonymousObserver(onNext, onError, onCompleted)); } - public static List ToList(this IObservable source, CancellationToken cancellation) + public static List ToList(this IObservable source, CancellationToken cancellationToken) { List results = new List(); ManualResetEventSlim ev = new ManualResetEventSlim(); - Exception error = null; - using (source.Subscribe(item => results.Add(item), exception => { error = exception; ev.Set(); }, () => ev.Set())) - ev.Wait(cancellation); + ExceptionDispatchInfo error = null; + using (source.Subscribe(item => results.Add(item), + exception => { error = ExceptionDispatchInfo.Capture(exception); ev.Set(); }, + () => ev.Set())) + ev.Wait(cancellationToken); if (error != null) - throw new TargetInvocationException(error); + error.Throw(); return results; } diff --git a/src/Main/Base/Project/Src/Util/WorkerThread.cs b/src/Main/Base/Project/Src/Util/WorkerThread.cs deleted file mode 100644 index 6ac4f6f57a..0000000000 --- a/src/Main/Base/Project/Src/Util/WorkerThread.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Collections.Generic; -using System.Threading; - -namespace ICSharpCode.SharpDevelop.Util -{ - /// - /// A worker thread that normally sleeps, but can run a queue of commands. - /// - /// This class does not create a worker thread on its own, it merely manages tasks for - /// the worker thread that calls . - /// - public class WorkerThread - { - sealed class AsyncTask : IAsyncResult - { - internal readonly ManualResetEventSlim manualResetEvent = new ManualResetEventSlim(false); - internal readonly Action method; - volatile bool isCompleted; - - internal AsyncTask(Action method) - { - this.method = method; - } - - internal void SetCompleted() - { - isCompleted = true; - manualResetEvent.Set(); - } - - public bool IsCompleted { - get { return isCompleted; } - } - - public WaitHandle AsyncWaitHandle { - get { return manualResetEvent.WaitHandle; } - } - - public object AsyncState { get; set; } - public bool CompletedSynchronously { get { return false; } } - } - - /// - /// Runs on the worker thread. - /// - /// The method to run. - /// IAsyncResult that gets completed when the action has executed. - public IAsyncResult Enqueue(Action method) - { - if (method == null) - throw new ArgumentNullException("method"); - AsyncTask task = new AsyncTask(method); - lock (lockObject) { - taskQueue.Enqueue(task); - Monitor.Pulse(lockObject); - } - return task; - } - - readonly object lockObject = new object(); - - // access needs lock using 'lockObject' - Queue taskQueue = new Queue(); - // access needs lock using 'lockObject' - bool workerRunning; - - // not a shared variable: accessed only within worker thread - bool exitWorker; - - /// - /// Runs the worker thread loop on the current thread. - /// - public void RunLoop() - { - lock (lockObject) { - if (workerRunning) - throw new InvalidOperationException("There already is a worker running"); - workerRunning = true; - } - try { - exitWorker = false; - while (!exitWorker) { - AsyncTask task; - lock (lockObject) { - while (taskQueue.Count == 0) - Monitor.Wait(lockObject); - task = taskQueue.Dequeue(); - } - task.method(); - task.SetCompleted(); - } - } finally { - lock (lockObject) { - workerRunning = false; - } - } - } - - /// - /// Exits running the worker thread after executing all currently enqueued methods. - /// - /// IAsyncResult that gets completed when the worker thread has shut down. - public IAsyncResult ExitWorkerThread() - { - return Enqueue(delegate { exitWorker = true; }); - } - } -} diff --git a/src/Main/Base/Project/Src/Util/WpfSynchronizeInvoke.cs b/src/Main/Base/Project/Src/Util/WpfSynchronizeInvoke.cs deleted file mode 100644 index bc23f3c5e7..0000000000 --- a/src/Main/Base/Project/Src/Util/WpfSynchronizeInvoke.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.ComponentModel; -using System.Reflection; -using System.Threading; -using System.Windows.Threading; - -namespace ICSharpCode.SharpDevelop -{ - /// - /// Implements the ISynchronizeInvoke interface by using a WPF dispatcher - /// to perform the cross-thread call. - /// - sealed class WpfSynchronizeInvoke : ISynchronizeInvoke - { - readonly Dispatcher dispatcher; - - public WpfSynchronizeInvoke(Dispatcher dispatcher) - { - if (dispatcher == null) - throw new ArgumentNullException("dispatcher"); - this.dispatcher = dispatcher; - } - - public bool InvokeRequired { - get { - return !dispatcher.CheckAccess(); - } - } - - public IAsyncResult BeginInvoke(Delegate method, object[] args) - { - DispatcherOperation op; - if (args == null || args.Length == 0) - op = dispatcher.BeginInvoke(DispatcherPriority.Normal, method); - else if (args.Length == 1) - op = dispatcher.BeginInvoke(DispatcherPriority.Normal, method, args[0]); - else - op = dispatcher.BeginInvoke(DispatcherPriority.Normal, method, args[0], args.Splice(1)); - return new AsyncResult(op); - } - - sealed class AsyncResult : IAsyncResult - { - internal readonly DispatcherOperation op; - readonly object lockObj = new object(); - ManualResetEvent resetEvent; - - public AsyncResult(DispatcherOperation op) - { - this.op = op; - } - - public bool IsCompleted { - get { - return op.Status == DispatcherOperationStatus.Completed; - } - } - - public WaitHandle AsyncWaitHandle { - get { - lock (lockObj) { - if (resetEvent == null) { - op.Completed += op_Completed; - resetEvent = new ManualResetEvent(false); - if (IsCompleted) - resetEvent.Set(); - } - return resetEvent; - } - } - } - - void op_Completed(object sender, EventArgs e) - { - lock (lockObj) { - resetEvent.Set(); - } - } - - public object AsyncState { - get { return null; } - } - - public bool CompletedSynchronously { - get { return false; } - } - } - - public object EndInvoke(IAsyncResult result) - { - AsyncResult r = result as AsyncResult; - if (r == null) - throw new ArgumentException("result must be the return value of a WpfSynchronizeInvoke.BeginInvoke call!"); - r.op.Wait(); - return r.op.Result; - } - - public object Invoke(Delegate method, object[] args) - { - object result = null; - Exception exception = null; - dispatcher.Invoke( - DispatcherPriority.Normal, - (Action)delegate { - try { - result = method.DynamicInvoke(args); - } catch (TargetInvocationException ex) { - exception = ex.InnerException; - } - }); - // if an exception occurred, re-throw it on the calling thread - if (exception != null) - throw new TargetInvocationException(exception); - return result; - } - } -} diff --git a/src/Main/Base/Project/Src/Services/ParserService/Fusion.cs b/src/Main/SharpDevelop/Parser/Fusion.cs similarity index 100% rename from src/Main/Base/Project/Src/Services/ParserService/Fusion.cs rename to src/Main/SharpDevelop/Parser/Fusion.cs diff --git a/src/Main/Base/Project/Src/Services/ParserService/GacInterop.cs b/src/Main/SharpDevelop/Parser/GlobalAssemblyCacheService.cs similarity index 82% rename from src/Main/Base/Project/Src/Services/ParserService/GacInterop.cs rename to src/Main/SharpDevelop/Parser/GlobalAssemblyCacheService.cs index 875771032c..55a252fe40 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/GacInterop.cs +++ b/src/Main/SharpDevelop/Parser/GlobalAssemblyCacheService.cs @@ -1,25 +1,21 @@ // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) -// Based on the MIT-licensed GacInterop.cs from ILSpy. - using System; using System.Collections.Generic; using System.IO; using System.Text; -using Mono.Cecil; namespace ICSharpCode.SharpDevelop.Parser { - /// - /// Interop with the .NET GAC. - /// - public static class GacInterop + sealed class GlobalAssemblyCacheService : IGlobalAssemblyCacheService { - /// - /// Gets the names of all assemblies in the GAC. - /// - public static IEnumerable GetGacAssemblyFullNames() + public bool IsGACAssembly(string fileName) + { + throw new NotImplementedException(); + } + + public IEnumerable GetGacAssemblyFullNames() { IApplicationContext applicationContext = null; IAssemblyEnum assemblyEnum = null; @@ -37,7 +33,7 @@ namespace ICSharpCode.SharpDevelop.Parser } } - #region FindAssemblyInGac + #region FindAssemblyInNetGac // This region is based on code from Mono.Cecil: // Author: @@ -65,14 +61,14 @@ namespace ICSharpCode.SharpDevelop.Parser // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // - static readonly string[] gac_paths = { Fusion.GetGacPath(false), Fusion.GetGacPath(true) }; - static readonly string[] gacs = { "GAC_MSIL", "GAC_32", "GAC" }; - static readonly string[] prefixes = { string.Empty, "v4.0_" }; + readonly string[] gac_paths = { Fusion.GetGacPath(false), Fusion.GetGacPath(true) }; + readonly string[] gacs = { "GAC_MSIL", "GAC_32", "GAC" }; + readonly string[] prefixes = { string.Empty, "v4.0_" }; /// /// Gets the file name for an assembly stored in the GAC. /// - public static string FindAssemblyInNetGac (DomAssemblyName reference) + public string FindAssemblyInNetGac (DomAssemblyName reference) { // without public key, it can't be in the GAC if (reference.PublicKeyToken == null) diff --git a/src/Main/Base/Project/Src/Services/ParserService/Doozer/ParserDescriptor.cs b/src/Main/SharpDevelop/Parser/ParserDescriptor.cs similarity index 97% rename from src/Main/Base/Project/Src/Services/ParserService/Doozer/ParserDescriptor.cs rename to src/Main/SharpDevelop/Parser/ParserDescriptor.cs index 961ccc7b52..318ff7bfb5 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/Doozer/ParserDescriptor.cs +++ b/src/Main/SharpDevelop/Parser/ParserDescriptor.cs @@ -8,7 +8,7 @@ using ICSharpCode.SharpDevelop.Project; namespace ICSharpCode.SharpDevelop.Parser { - public sealed class ParserDescriptor + sealed class ParserDescriptor { Codon codon; Type parserType; diff --git a/src/Main/Base/Project/Src/Services/ParserService/Doozer/ParserDoozer.cs b/src/Main/SharpDevelop/Parser/ParserDoozer.cs similarity index 96% rename from src/Main/Base/Project/Src/Services/ParserService/Doozer/ParserDoozer.cs rename to src/Main/SharpDevelop/Parser/ParserDoozer.cs index 67410797a4..2da0ee5e61 100644 --- a/src/Main/Base/Project/Src/Services/ParserService/Doozer/ParserDoozer.cs +++ b/src/Main/SharpDevelop/Parser/ParserDoozer.cs @@ -20,7 +20,7 @@ namespace ICSharpCode.SharpDevelop.Parser /// /// An ParserDescriptor object that wraps the IParser object. /// - public sealed class ParserDoozer : IDoozer + sealed class ParserDoozer : IDoozer { /// /// Gets if the doozer handles codon conditions on its own. diff --git a/src/Main/SharpDevelop/Parser/ParserService.cs b/src/Main/SharpDevelop/Parser/ParserService.cs new file mode 100644 index 0000000000..cb9c7ce9c6 --- /dev/null +++ b/src/Main/SharpDevelop/Parser/ParserService.cs @@ -0,0 +1,359 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Project; +using ICSharpCode.SharpDevelop.Refactoring; + +namespace ICSharpCode.SharpDevelop.Parser +{ + sealed class ParserService : IParserService + { + IList parserDescriptors; + + public ParserService() + { + parserDescriptors = AddInTree.BuildItems("/Workspace/Parser", null, false); + } + + #region ParseInformationUpdated + public event EventHandler ParseInformationUpdated = delegate {}; + + internal void RaiseParseInformationUpdated(ParseInformationEventArgs e) + { + // RaiseParseInformationUpdated is called inside a lock, but we don't want to raise the event inside that lock. + // To ensure events are raised in the same order, we always invoke on the main thread. + WorkbenchSingleton.SafeThreadAsyncCall( + delegate { + string addition; + if (e.OldParsedFile == null) + addition = " (new)"; + else if (e.NewParsedFile == null) + addition = " (removed)"; + else + addition = " (updated)"; + LoggingService.Debug("ParseInformationUpdated " + e.FileName + addition); + ParseInformationUpdated(null, e); + }); + } + #endregion + + #region TaskListTokens + IReadOnlyList taskListTokens = LoadTaskListTokens(); + + public IReadOnlyList TaskListTokens { + get { return taskListTokens; } + set { + SD.MainThread.VerifyAccess(); + if (!value.SequenceEqual(taskListTokens)) { + taskListTokens = value.ToArray(); + PropertyService.SetList("SharpDevelop.TaskListTokens", taskListTokens); + // TODO: trigger reparse? + } + } + } + + static IReadOnlyList LoadTaskListTokens() + { + if (PropertyService.Contains("SharpDevelop.TaskListTokens")) + return PropertyService.GetList("SharpDevelop.TaskListTokens").ToArray(); + else + return new string[] { "HACK", "TODO", "UNDONE", "FIXME" }; + } + #endregion + + #region Load Solution Projects Thread + public bool LoadSolutionProjectsThreadRunning { + get { return false; } + } + + public event EventHandler LoadSolutionProjectsThreadStarted; + public event EventHandler LoadSolutionProjectsThreadEnded; + #endregion + + #region Compilation + public ICompilation GetCompilation(IProject project) + { + return GetCurrentSolutionSnapshot().GetCompilation(project); + } + + public ICompilation GetCompilationForFile(FileName fileName) + { + Solution solution = ProjectService.OpenSolution; + IProject project = solution != null ? solution.FindProjectContainingFile(fileName) : null; + if (project != null) + return GetCompilation(project); + + var entry = GetFileEntry(fileName, false); + if (entry != null && entry.parser != null) { + var parsedFile = entry.GetExistingParsedFile(null, null); + if (parsedFile != null) { + ICompilation compilation = entry.parser.CreateCompilationForSingleFile(fileName, parsedFile); + if (compilation != null) + return compilation; + } + } + return MinimalCorlib.Instance.CreateCompilation(); + } + + // Use a WeakReference for caching the solution snapshot - it can require + // lots of memory and may not be invalidated soon enough if the user + // is only browsing code. + volatile WeakReference currentSolutionSnapshot; + + public SharpDevelopSolutionSnapshot GetCurrentSolutionSnapshot() + { + var weakRef = currentSolutionSnapshot; + SharpDevelopSolutionSnapshot result; + if (weakRef == null || !weakRef.TryGetTarget(out result)) { + // create new snapshot if we don't have one cached + result = new SharpDevelopSolutionSnapshot(ProjectService.OpenSolution); + currentSolutionSnapshot = new WeakReference(result); + } + return result; + } + + public void InvalidateCurrentSolutionSnapshot() + { + currentSolutionSnapshot = null; + } + #endregion + + #region Entry management + const int cachedEntryCount = 5; + Dictionary fileEntryDict = new Dictionary(); + Queue cacheExpiryQueue = new Queue(); + + ParserServiceEntry GetFileEntry(FileName fileName, bool createIfMissing) + { + if (fileName == null) + throw new ArgumentNullException("fileName"); + ParserServiceEntry entry; + lock (fileEntryDict) { + if (!fileEntryDict.TryGetValue(fileName, out entry)) { + if (!createIfMissing) + return null; + entry = new ParserServiceEntry(this, fileName); + fileEntryDict.Add(fileName, entry); + } + } + return entry; + } + + public void ClearParseInformation(FileName fileName) + { + ParserServiceEntry entry = GetFileEntry(fileName, false); + if (entry != null) { + entry.ExpireCache(); + } + } + + internal void RemoveEntry(ParserServiceEntry entry) + { + Debug.Assert(Monitor.IsEntered(entry)); + lock (fileEntryDict) { + ParserServiceEntry entryAtKey; + if (fileEntryDict.TryGetValue(entry.fileName, out entryAtKey)) { + if (entry == entryAtKey) + fileEntryDict.Remove(entry.fileName); + } + } + } + + internal void RegisterForCacheExpiry(ParserServiceEntry entry) + { + // This method should not be called within any locks + Debug.Assert(!Monitor.IsEntered(entry)); + ParserServiceEntry expiredItem = null; + lock (cacheExpiryQueue) { + if (cacheExpiryQueue.Count >= cachedEntryCount) { + expiredItem = cacheExpiryQueue.Dequeue(); + } + cacheExpiryQueue.Enqueue(entry); + } + if (expiredItem != null) + expiredItem.ExpireCache(); + } + + public void AddOwnerProject(FileName fileName, IProject project, bool startAsyncParse, bool isLinkedFile) + { + if (project == null) + throw new ArgumentNullException("project"); + GetFileEntry(fileName, true).AddOwnerProject(project, isLinkedFile); + } + + public void RemoveOwnerProject(FileName fileName, IProject project) + { + if (project == null) + throw new ArgumentNullException("project"); + var entry = GetFileEntry(fileName, false); + if (entry != null) + entry.RemoveOwnerProject(project); + } + #endregion + + #region Forward Parse() calls to entry + public IParsedFile GetExistingParsedFile(FileName fileName, ITextSourceVersion version, IProject parentProject) + { + var entry = GetFileEntry(fileName, false); + if (entry != null) + return entry.GetExistingParsedFile(version, parentProject); + else + return null; + } + + public ParseInformation GetCachedParseInformation(FileName fileName, ITextSourceVersion version, IProject parentProject) + { + var entry = GetFileEntry(fileName, false); + if (entry != null) + return entry.GetCachedParseInformation(version, parentProject); + else + return null; + } + + public ParseInformation Parse(FileName fileName, ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) + { + return GetFileEntry(fileName, true).Parse(fileContent, parentProject, cancellationToken); + } + + public IParsedFile ParseFile(FileName fileName, ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) + { + return GetFileEntry(fileName, true).ParseFile(fileContent, parentProject, cancellationToken); + } + + public Task ParseAsync(FileName fileName, ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) + { + return GetFileEntry(fileName, true).ParseAsync(fileContent, parentProject, cancellationToken); + } + + public Task ParseFileAsync(FileName fileName, ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) + { + return GetFileEntry(fileName, true).ParseFileAsync(fileContent, parentProject, cancellationToken); + } + #endregion + + #region Resolve + public ResolveResult Resolve(ITextEditor editor, TextLocation location, ICompilation compilation, CancellationToken cancellationToken) + { + if (editor == null) + throw new ArgumentNullException("editor"); + return Resolve(editor.FileName, location, editor.Document, compilation, cancellationToken); + } + + public ResolveResult Resolve(FileName fileName, TextLocation location, ITextSource fileContent, ICompilation compilation, CancellationToken cancellationToken) + { + var entry = GetFileEntry(fileName, true); + if (entry.parser == null) + return ErrorResolveResult.UnknownError; + IProject project = compilation != null ? compilation.GetProject() : null; + var parseInfo = entry.Parse(fileContent, project, cancellationToken); + if (parseInfo == null) + return ErrorResolveResult.UnknownError; + if (compilation == null) + compilation = GetCompilationForFile(fileName); + ResolveResult rr = entry.parser.Resolve(parseInfo, location, compilation, cancellationToken); + LoggingService.Debug("Resolved " + location + " to " + rr); + return rr; + } + + public Task ResolveAsync(FileName fileName, TextLocation location, ITextSource fileContent, ICompilation compilation, CancellationToken cancellationToken) + { + var entry = GetFileEntry(fileName, true); + if (entry.parser == null) + return Task.FromResult(ErrorResolveResult.UnknownError); + IProject project = compilation != null ? compilation.GetProject() : null; + return entry.ParseAsync(fileContent, project, cancellationToken).ContinueWith( + delegate (Task parseInfoTask) { + var parseInfo = parseInfoTask.Result; + if (parseInfo == null) + return ErrorResolveResult.UnknownError; + if (compilation == null) + compilation = GetCompilationForFile(fileName); + ResolveResult rr = entry.parser.Resolve(parseInfo, location, compilation, cancellationToken); + LoggingService.Debug("Resolved " + location + " to " + rr); + return rr; + }, cancellationToken); + } + + public async Task FindLocalReferencesAsync(FileName fileName, IVariable variable, Action callback, ITextSource fileContent, ICompilation compilation, CancellationToken cancellationToken) + { + var entry = GetFileEntry(fileName, true); + if (entry.parser == null) + return; + if (fileContent == null) + fileContent = SD.FileService.GetFileContent(fileName); + if (compilation == null) + compilation = GetCompilationForFile(fileName); + var parseInfo = await entry.ParseAsync(fileContent, compilation.GetProject(), cancellationToken).ConfigureAwait(false); + await Task.Run( + () => entry.parser.FindLocalReferences(parseInfo, fileContent, variable, compilation, callback, cancellationToken) + ); + } + #endregion + + #region HasParser / CreateParser + public bool HasParser(FileName fileName) + { + if (fileName == null) + throw new ArgumentNullException("fileName"); + if (parserDescriptors == null) + return false; + foreach (ParserDescriptor descriptor in parserDescriptors) { + if (descriptor.CanParse(fileName)) { + return true; + } + } + return false; + } + + /// + /// Creates a new IParser instance that can parse the specified file. + /// This method is thread-safe. + /// + internal IParser CreateParser(FileName fileName) + { + if (fileName == null) + throw new ArgumentNullException("fileName"); + if (parserDescriptors == null) + return null; + foreach (ParserDescriptor descriptor in parserDescriptors) { + if (descriptor.CanParse(fileName)) { + IParser p = descriptor.CreateParser(); + if (p != null) { + p.TaskListTokens = TaskListTokens; + return p; + } + } + } + return null; + } + #endregion + + internal void StartParserThread() + { + // TODO + } + + internal void StopParserThread() + { + // TODO + } + } +} diff --git a/src/Main/SharpDevelop/Parser/ParserServiceEntry.cs b/src/Main/SharpDevelop/Parser/ParserServiceEntry.cs new file mode 100644 index 0000000000..de1c7f94d6 --- /dev/null +++ b/src/Main/SharpDevelop/Parser/ParserServiceEntry.cs @@ -0,0 +1,330 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using ICSharpCode.Core; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; +using ICSharpCode.SharpDevelop.Project; + +namespace ICSharpCode.SharpDevelop.Parser +{ + sealed class ParserServiceEntry + { + struct ProjectEntry + { + public readonly IProject Project; + public readonly IParsedFile ParsedFile; + public readonly ParseInformation CachedParseInformation; + + public ProjectEntry(IProject project, IParsedFile parsedFile, ParseInformation cachedParseInformation) + { + this.Project = project; + this.ParsedFile = parsedFile; + this.CachedParseInformation = cachedParseInformation; + } + } + + readonly ParserService parserService; + internal readonly FileName fileName; + internal readonly IParser parser; + List entries = new List { default(ProjectEntry) }; + ITextSourceVersion currentVersion; + + public ParserServiceEntry(ParserService parserService, FileName fileName) + { + this.parserService = parserService; + this.fileName = fileName; + this.parser = parserService.CreateParser(fileName); + } + + #region Owner Projects + IProject PrimaryProject { + get { return entries[0].Project; } + } + + int FindIndexForProject(IProject parentProject) + { + if (parentProject == null) + return 0; + for (int i = 0; i < entries.Count; i++) { + if (entries[i].Project == parentProject) + return i; + } + // project not found + return -1; + } + + public void AddOwnerProject(IProject project, bool isLinkedFile) + { + Debug.Assert(project != null); + lock (this) { + if (FindIndexForProject(project) >= 0) + throw new InvalidOperationException("The project alreadys owns the file"); + ProjectEntry newEntry = new ProjectEntry(project, null, null); + if (entries[0].Project == null) { + entries[0] = newEntry; + } else if (isLinkedFile) { + entries.Add(newEntry); + } else { + entries.Insert(0, newEntry); + } + } + } + + public void RemoveOwnerProject(IProject project) + { + Debug.Assert(project != null); + lock (this) { + int index = FindIndexForProject(project); + if (index < 0) + throw new InvalidOperationException("The project does not own the file"); + if (entries.Count == 1) { + entries[0] = default(ProjectEntry); + } else { + entries.RemoveAt(index); + } + } + } + #endregion + + /// + /// Compares currentVersion with version. + /// -1 = currentVersion is older; 0 = same version; 1 = newVersion is older + /// + int CompareVersions(ITextSourceVersion newVersion) + { + if (currentVersion != null && newVersion != null && currentVersion.BelongsToSameDocumentAs(newVersion)) + return currentVersion.CompareAge(newVersion); + else + return -1; + } + + #region Expire Cache + GetExistingParsedFile + GetCachedParseInformation + public void ExpireCache() + { + lock (this) { + if (PrimaryProject == null) { + parserService.RemoveEntry(this); + } else { + for (int i = 0; i < entries.Count; i++) { + var oldEntry = entries[i]; + entries[i] = new ProjectEntry(oldEntry.Project, oldEntry.ParsedFile, null); + } + } + } + } + + public IParsedFile GetExistingParsedFile(ITextSourceVersion version, IProject parentProject) + { + lock (this) { + if (version != null && CompareVersions(version) != 0) { + return null; + } + int index = FindIndexForProject(parentProject); + if (index < 0) + return null; + return entries[index].ParsedFile; + } + } + + public ParseInformation GetCachedParseInformation(ITextSourceVersion version, IProject parentProject) + { + lock (this) { + if (version != null && CompareVersions(version) != 0) { + return null; + } + int index = FindIndexForProject(parentProject); + if (index < 0) + return null; + return entries[index].CachedParseInformation; + } + } + #endregion + + #region Parse + public ParseInformation Parse(ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) + { + if (fileContent == null) { + fileContent = SD.FileService.GetFileContent(fileName); + } + + return DoParse(fileContent, parentProject, false, cancellationToken).CachedParseInformation; + } + + public IParsedFile ParseFile(ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) + { + if (fileContent == null) { + fileContent = SD.FileService.GetFileContent(fileName); + } + + return DoParse(fileContent, parentProject, false, cancellationToken).ParsedFile; + } + + ProjectEntry DoParse(ITextSource fileContent, IProject parentProject, bool fullParseInformationRequested, + CancellationToken cancellationToken) + { + if (parser == null) + return default(ProjectEntry); + + if (fileContent == null) { + // No file content was specified. Because the callers of this method already check for currently open files, + // we can assume that the file isn't open and simply read it from disk. + try { + fileContent = SD.FileService.GetFileContentFromDisk(fileName, cancellationToken); + } catch (IOException) { + // It is possible that the file gets deleted/becomes inaccessible while a background parse + // operation is enqueued, so we have to handle IO exceptions. + return default(ProjectEntry); + } catch (UnauthorizedAccessException) { + return default(ProjectEntry); + } + } + + ProjectEntry result; + lock (this) { + int index = FindIndexForProject(parentProject); + int versionComparison = CompareVersions(fileContent.Version); + if (versionComparison > 0 || index < 0) { + // We're going backwards in time, or are requesting a project that is not an owner + // for this entry. + var parseInfo = parser.Parse(fileName, fileContent, fullParseInformationRequested, parentProject, cancellationToken); + FreezableHelper.Freeze(parseInfo.ParsedFile); + return new ProjectEntry(parentProject, parseInfo.ParsedFile, parseInfo); + } else { + if (versionComparison == 0 && index >= 0) { + // If full parse info is requested, ensure we have full parse info. + if (!(fullParseInformationRequested && entries[index].CachedParseInformation == null)) { + // We already have the requested version parsed, just return it: + return entries[index]; + } + } + } + + ParseInformationEventArgs[] results = new ParseInformationEventArgs[entries.Count]; + for (int i = 0; i < entries.Count; i++) { + ParseInformation parseInfo; + try { + parseInfo = parser.Parse(fileName, fileContent, fullParseInformationRequested, entries[i].Project, cancellationToken); + } catch (Exception ex) { + SD.LoggingService.Error("Got " + ex.GetType().Name + " while parsing " + fileName); + throw; + } + if (parseInfo == null) + throw new NullReferenceException(parser.GetType().Name + ".Parse() returned null"); + if (fullParseInformationRequested && !parseInfo.IsFullParseInformation) + throw new InvalidOperationException(parser.GetType().Name + ".Parse() did not return full parse info as requested."); + FreezableHelper.Freeze(parseInfo.ParsedFile); + results[i] = new ParseInformationEventArgs(entries[i].Project, entries[i].ParsedFile, parseInfo); + } + + // Only if all parse runs succeeded, register the parse information. + currentVersion = fileContent.Version; + for (int i = 0; i < entries.Count; i++) { + if (fullParseInformationRequested || entries[i].CachedParseInformation != null) + entries[i] = new ProjectEntry(entries[i].Project, entries[i].ParsedFile, results[i].NewParseInformation); + else + entries[i] = new ProjectEntry(entries[i].Project, entries[i].ParsedFile, null); + if (entries[i].Project != null) + entries[i].Project.OnParseInformationUpdated(results[i]); + parserService.RaiseParseInformationUpdated(results[i]); + } + result = entries[index]; + } // exit lock + parserService.RegisterForCacheExpiry(this); + return result; + } + #endregion + + #region ParseAsync + Task runningAsyncParseTask; + ITextSourceVersion runningAsyncParseFileContentVersion; + bool runningAsyncParseFullInfoRequested; + + public async Task ParseAsync(ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) + { + return (await DoParseAsync(fileContent, parentProject, true, cancellationToken)).CachedParseInformation; + } + + public async Task ParseFileAsync(ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) + { + return (await DoParseAsync(fileContent, parentProject, false, cancellationToken)).ParsedFile; + } + + Task DoParseAsync(ITextSource fileContent, IProject parentProject, bool requestFullParseInformation, CancellationToken cancellationToken) + { + // Create snapshot of file content, if required + bool lookupOpenFileOnTargetThread; + if (fileContent != null) { + lookupOpenFileOnTargetThread = false; + // File content was explicitly specified: + // Let's make a snapshot in case the text source is mutable. + fileContent = fileContent.CreateSnapshot(); + } else if (SD.MainThread.InvokeRequired) { + // fileContent == null && not on the main thread: + // Don't fetch the file content right now; if we need to SafeThreadCall() anyways, + // it's better to do so from the background task. + lookupOpenFileOnTargetThread = true; + } else { + // fileContent == null && we are on the main thread: + // Let's look up the file in the list of open files right now + // so that we don't need to SafeThreadCall() later on. + lookupOpenFileOnTargetThread = false; + fileContent = SD.FileService.GetFileContentForOpenFile(fileName); + } + Task task; + lock (this) { + if (fileContent != null) { + // Optimization: + // don't start a background task if fileContent was specified and up-to-date parse info is available + int index = FindIndexForProject(parentProject); + int versionComparison = CompareVersions(fileContent.Version); + if (versionComparison == 0 && index >= 0) { + // If full parse info is requested, ensure we have full parse info. + if (!(requestFullParseInformation && entries[index].CachedParseInformation == null)) { + // We already have the requested version parsed, just return it: + return Task.FromResult(entries[index]); + } + } + // Optimization: + // if an equivalent task is already running, return that one instead + if (runningAsyncParseTask != null && (!requestFullParseInformation || runningAsyncParseFullInfoRequested) + && runningAsyncParseFileContentVersion.BelongsToSameDocumentAs(fileContent.Version) + && runningAsyncParseFileContentVersion.CompareAge(fileContent.Version) == 0) + { + return runningAsyncParseTask; + } + } + task = new Task( + delegate { + try { + if (lookupOpenFileOnTargetThread) { + fileContent = SD.FileService.GetFileContentForOpenFile(fileName); + } + return DoParse(fileContent, parentProject, requestFullParseInformation, cancellationToken); + } finally { + lock (this) { + runningAsyncParseTask = null; + runningAsyncParseFileContentVersion = null; + } + } + }, cancellationToken); + if (fileContent != null && fileContent.Version != null && !cancellationToken.CanBeCanceled) { + runningAsyncParseTask = task; + runningAsyncParseFileContentVersion = fileContent.Version; + runningAsyncParseFullInfoRequested = requestFullParseInformation; + } + } + task.Start(); + return task; + } + #endregion + } +} diff --git a/src/Main/SharpDevelop/Sda/CallHelper.cs b/src/Main/SharpDevelop/Sda/CallHelper.cs index ca452c4f9b..316b63be67 100644 --- a/src/Main/SharpDevelop/Sda/CallHelper.cs +++ b/src/Main/SharpDevelop/Sda/CallHelper.cs @@ -17,7 +17,7 @@ using ICSharpCode.Core; using ICSharpCode.Core.WinForms; using ICSharpCode.SharpDevelop.Commands; using ICSharpCode.SharpDevelop.Gui; -using ICSharpCode.SharpDevelop.Gui.Workbench; +using ICSharpCode.SharpDevelop.Workbench; using ICSharpCode.SharpDevelop.Logging; using ICSharpCode.SharpDevelop.Parser; diff --git a/src/Main/SharpDevelop/SharpDevelop.csproj b/src/Main/SharpDevelop/SharpDevelop.csproj index 1dc3390907..47e48b57a6 100644 --- a/src/Main/SharpDevelop/SharpDevelop.csproj +++ b/src/Main/SharpDevelop/SharpDevelop.csproj @@ -77,23 +77,32 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + @@ -101,7 +110,6 @@ - Resources\StringResources.resx @@ -174,14 +182,15 @@ - - + + + - - + + \ No newline at end of file diff --git a/src/Main/SharpDevelop/Gui/App.xaml b/src/Main/SharpDevelop/Startup/App.xaml similarity index 87% rename from src/Main/SharpDevelop/Gui/App.xaml rename to src/Main/SharpDevelop/Startup/App.xaml index 33de4d01da..e0f754059f 100644 --- a/src/Main/SharpDevelop/Gui/App.xaml +++ b/src/Main/SharpDevelop/Startup/App.xaml @@ -1,4 +1,4 @@ - /// Interaction logic for App.xaml diff --git a/src/Main/SharpDevelop/SharpDevelopMain.cs b/src/Main/SharpDevelop/Startup/SharpDevelopMain.cs similarity index 98% rename from src/Main/SharpDevelop/SharpDevelopMain.cs rename to src/Main/SharpDevelop/Startup/SharpDevelopMain.cs index c0ad0564a1..3768b04763 100644 --- a/src/Main/SharpDevelop/SharpDevelopMain.cs +++ b/src/Main/SharpDevelop/Startup/SharpDevelopMain.cs @@ -10,12 +10,12 @@ using System.Windows.Forms; using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Gui; -using ICSharpCode.SharpDevelop.Gui.Workbench; +using ICSharpCode.SharpDevelop.Workbench; using ICSharpCode.SharpDevelop.Logging; using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Sda; -namespace ICSharpCode.SharpDevelop +namespace ICSharpCode.SharpDevelop.Startup { /// /// This Class is the Core main class, it starts the program. diff --git a/src/Main/SharpDevelop/Gui/SplashScreen.cs b/src/Main/SharpDevelop/Startup/SplashScreen.cs similarity index 98% rename from src/Main/SharpDevelop/Gui/SplashScreen.cs rename to src/Main/SharpDevelop/Startup/SplashScreen.cs index f95bc1bfae..c91b714fdf 100644 --- a/src/Main/SharpDevelop/Gui/SplashScreen.cs +++ b/src/Main/SharpDevelop/Startup/SplashScreen.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -namespace ICSharpCode.SharpDevelop.Gui +namespace ICSharpCode.SharpDevelop.Startup { sealed class SplashScreenForm : Form { diff --git a/src/Main/SharpDevelop/Gui/Workbench/AvalonDockLayout.cs b/src/Main/SharpDevelop/Workbench/AvalonDockLayout.cs similarity index 99% rename from src/Main/SharpDevelop/Gui/Workbench/AvalonDockLayout.cs rename to src/Main/SharpDevelop/Workbench/AvalonDockLayout.cs index 8b9e4c7bf0..3b988033f4 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/AvalonDockLayout.cs +++ b/src/Main/SharpDevelop/Workbench/AvalonDockLayout.cs @@ -12,8 +12,9 @@ using System.Windows.Media.Imaging; using AvalonDock; using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { /// /// Workbench layout using the AvalonDock library. diff --git a/src/Main/SharpDevelop/Gui/Workbench/AvalonPadContent.cs b/src/Main/SharpDevelop/Workbench/AvalonPadContent.cs similarity index 97% rename from src/Main/SharpDevelop/Gui/Workbench/AvalonPadContent.cs rename to src/Main/SharpDevelop/Workbench/AvalonPadContent.cs index cd5c8f186c..d04472d0e1 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/AvalonPadContent.cs +++ b/src/Main/SharpDevelop/Workbench/AvalonPadContent.cs @@ -9,8 +9,9 @@ using System.Windows.Threading; using AvalonDock; using ICSharpCode.Core.Presentation; +using ICSharpCode.SharpDevelop.Gui; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { sealed class AvalonPadContent : DockableContent, IDisposable { diff --git a/src/Main/SharpDevelop/Gui/Workbench/AvalonWorkbenchWindow.cs b/src/Main/SharpDevelop/Workbench/AvalonWorkbenchWindow.cs similarity index 99% rename from src/Main/SharpDevelop/Gui/Workbench/AvalonWorkbenchWindow.cs rename to src/Main/SharpDevelop/Workbench/AvalonWorkbenchWindow.cs index 14b9f8f31e..c62033887c 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/AvalonWorkbenchWindow.cs +++ b/src/Main/SharpDevelop/Workbench/AvalonWorkbenchWindow.cs @@ -14,8 +14,9 @@ using System.Windows.Threading; using AvalonDock; using ICSharpCode.Core; using ICSharpCode.Core.Presentation; +using ICSharpCode.SharpDevelop.Gui; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { sealed class AvalonWorkbenchWindow : DocumentContent, IWorkbenchWindow, IOwnerState { diff --git a/src/Main/SharpDevelop/Gui/Workbench/ChooseLayoutCommand.cs b/src/Main/SharpDevelop/Workbench/ChooseLayoutCommand.cs similarity index 97% rename from src/Main/SharpDevelop/Gui/Workbench/ChooseLayoutCommand.cs rename to src/Main/SharpDevelop/Workbench/ChooseLayoutCommand.cs index 03834a86a7..f23adbdcc3 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/ChooseLayoutCommand.cs +++ b/src/Main/SharpDevelop/Workbench/ChooseLayoutCommand.cs @@ -4,11 +4,12 @@ using System; using System.Collections.Generic; using System.IO; +using System.Windows.Forms; using ICSharpCode.Core; -using System.Windows.Forms; +using ICSharpCode.SharpDevelop.Gui; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { /// /// Command for layout combobox in toolbar. @@ -45,7 +46,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench } if (oldItem != editIndex && oldItem != resetIndex) { - WorkbenchSingleton.Workbench.WorkbenchLayout.StoreConfiguration(); + SD.Workbench.WorkbenchLayout.StoreConfiguration(); } if (comboBox.SelectedIndex == editIndex) { diff --git a/src/Main/SharpDevelop/Workbench/DispatcherMessageLoop.cs b/src/Main/SharpDevelop/Workbench/DispatcherMessageLoop.cs new file mode 100644 index 0000000000..7091bf8d01 --- /dev/null +++ b/src/Main/SharpDevelop/Workbench/DispatcherMessageLoop.cs @@ -0,0 +1,146 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace ICSharpCode.SharpDevelop.Workbench +{ + sealed class DispatcherMessageLoop : IMessageLoop, ISynchronizeInvoke + { + readonly Dispatcher dispatcher; + readonly SynchronizationContext synchronizationContext; + + public DispatcherMessageLoop(Dispatcher dispatcher, SynchronizationContext synchronizationContext) + { + this.dispatcher = dispatcher; + this.synchronizationContext = synchronizationContext; + } + + public Thread Thread { + get { return dispatcher.Thread; } + } + + public Dispatcher Dispatcher { + get { return dispatcher; } + } + + public SynchronizationContext SynchronizationContext { + get { return synchronizationContext; } + } + + public ISynchronizeInvoke SynchronizingObject { + get { return this; } + } + + public bool InvokeRequired { + get { return !dispatcher.CheckAccess(); } + } + + public bool CheckAccess() + { + return dispatcher.CheckAccess(); + } + + public void VerifyAccess() + { + dispatcher.VerifyAccess(); + } + + public void InvokeIfRequired(Action callback) + { + if (dispatcher.CheckAccess()) + callback(); + else + dispatcher.Invoke(callback); + } + + public void InvokeIfRequired(Action callback, DispatcherPriority priority) + { + if (dispatcher.CheckAccess()) + callback(); + else + dispatcher.Invoke(callback, priority); + } + + public void InvokeIfRequired(Action callback, DispatcherPriority priority, CancellationToken cancellationToken) + { + if (dispatcher.CheckAccess()) + callback(); + else + dispatcher.Invoke(callback, priority, cancellationToken); + } + + public T InvokeIfRequired(Func callback) + { + if (dispatcher.CheckAccess()) + return callback(); + else + return dispatcher.Invoke(callback); + } + + public T InvokeIfRequired(Func callback, DispatcherPriority priority) + { + if (dispatcher.CheckAccess()) + return callback(); + else + return dispatcher.Invoke(callback, priority); + } + + public T InvokeIfRequired(Func callback, DispatcherPriority priority, CancellationToken cancellationToken) + { + if (dispatcher.CheckAccess()) + return callback(); + else + return dispatcher.Invoke(callback, priority, cancellationToken); + } + + public Task InvokeAsync(Action callback) + { + return dispatcher.InvokeAsync(callback).Task; + } + + public Task InvokeAsync(Action callback, DispatcherPriority priority) + { + return dispatcher.InvokeAsync(callback, priority).Task; + } + + public Task InvokeAsync(Action callback, DispatcherPriority priority, CancellationToken cancellationToken) + { + return dispatcher.InvokeAsync(callback, priority, cancellationToken).Task; + } + + public Task InvokeAsync(Func callback) + { + return dispatcher.InvokeAsync(callback).Task; + } + + public Task InvokeAsync(Func callback, DispatcherPriority priority) + { + return dispatcher.InvokeAsync(callback, priority).Task; + } + + public Task InvokeAsync(Func callback, DispatcherPriority priority, CancellationToken cancellationToken) + { + return dispatcher.InvokeAsync(callback, priority, cancellationToken).Task; + } + + IAsyncResult ISynchronizeInvoke.BeginInvoke(Delegate method, object[] args) + { + return dispatcher.InvokeAsync(() => method.DynamicInvoke(args)).Task; + } + + object ISynchronizeInvoke.EndInvoke(IAsyncResult result) + { + return ((Task)result).Result; + } + + object ISynchronizeInvoke.Invoke(Delegate method, object[] args) + { + return dispatcher.Invoke(() => method.DynamicInvoke(args)); + } + } +} diff --git a/src/Main/SharpDevelop/Workbench/FileService.cs b/src/Main/SharpDevelop/Workbench/FileService.cs new file mode 100644 index 0000000000..4edf719828 --- /dev/null +++ b/src/Main/SharpDevelop/Workbench/FileService.cs @@ -0,0 +1,61 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.IO; +using System.Text; +using System.Threading; +using ICSharpCode.AvalonEdit.Utils; +using ICSharpCode.Core; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.SharpDevelop.Editor; + +namespace ICSharpCode.SharpDevelop.Workbench +{ + sealed class FileService : IFileService + { + public Encoding DefaultFileEncoding { + get { + return Encoding.GetEncoding(SharpDevelop.FileService.DefaultFileEncodingCodePage); + } + } + + public ITextSource GetFileContent(FileName fileName) + { + return GetFileContentForOpenFile(fileName) ?? GetFileContentFromDisk(fileName, CancellationToken.None); + } + + public ITextSource GetFileContent(string fileName) + { + return GetFileContent(FileName.Create(fileName)); + } + + public ITextSource GetFileContentForOpenFile(FileName fileName) + { + return SD.MainThread.InvokeIfRequired( + delegate { + OpenedFile file = SharpDevelop.FileService.GetOpenedFile(fileName); + if (file != null) { + IFileDocumentProvider p = file.CurrentView as IFileDocumentProvider; + if (p != null) { + IDocument document = p.GetDocumentForFile(file); + if (document != null) { + return document.CreateSnapshot(); + } + } + + using (Stream s = file.OpenRead()) { + // load file + return new StringTextSource(FileReader.ReadFileContent(s, DefaultFileEncoding)); + } + } + return null; + }); + } + + public ITextSource GetFileContentFromDisk(FileName fileName, CancellationToken cancellationToken) + { + return new StringTextSource(FileReader.ReadFileContent(fileName, DefaultFileEncoding)); + } + } +} diff --git a/src/Main/SharpDevelop/Gui/Workbench/FullScreenEnabledWindow.cs b/src/Main/SharpDevelop/Workbench/FullScreenEnabledWindow.cs similarity index 97% rename from src/Main/SharpDevelop/Gui/Workbench/FullScreenEnabledWindow.cs rename to src/Main/SharpDevelop/Workbench/FullScreenEnabledWindow.cs index c8ecc69867..c189bbb245 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/FullScreenEnabledWindow.cs +++ b/src/Main/SharpDevelop/Workbench/FullScreenEnabledWindow.cs @@ -6,7 +6,7 @@ using System.Windows; using System.Windows.Forms; using System.Windows.Interop; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { using WindowState = System.Windows.WindowState; diff --git a/src/Main/SharpDevelop/Gui/Workbench/LayoutConfiguration.cs b/src/Main/SharpDevelop/Workbench/LayoutConfiguration.cs similarity index 94% rename from src/Main/SharpDevelop/Gui/Workbench/LayoutConfiguration.cs rename to src/Main/SharpDevelop/Workbench/LayoutConfiguration.cs index 7bfb335304..325aa38855 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/LayoutConfiguration.cs +++ b/src/Main/SharpDevelop/Workbench/LayoutConfiguration.cs @@ -8,7 +8,7 @@ using System.Xml; using ICSharpCode.Core; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { class LayoutConfiguration { @@ -122,11 +122,10 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench return currentLayoutName; } set { - if (WorkbenchSingleton.InvokeRequired) - throw new InvalidOperationException("Invoke required"); + SD.MainThread.VerifyAccess(); if (value != CurrentLayoutName) { currentLayoutName = value; - WorkbenchSingleton.Workbench.WorkbenchLayout.LoadConfiguration(); + SD.Workbench.WorkbenchLayout.LoadConfiguration(); OnLayoutChanged(EventArgs.Empty); } } @@ -135,7 +134,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench public static void ReloadDefaultLayout() { currentLayoutName = DefaultLayoutName; - WorkbenchSingleton.Workbench.WorkbenchLayout.LoadConfiguration(); + SD.Workbench.WorkbenchLayout.LoadConfiguration(); OnLayoutChanged(EventArgs.Empty); } diff --git a/src/Main/SharpDevelop/Gui/Workbench/SDStatusBar.cs b/src/Main/SharpDevelop/Workbench/SDStatusBar.cs similarity index 98% rename from src/Main/SharpDevelop/Gui/Workbench/SDStatusBar.cs rename to src/Main/SharpDevelop/Workbench/SDStatusBar.cs index db327d505c..d327a56cb8 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/SDStatusBar.cs +++ b/src/Main/SharpDevelop/Workbench/SDStatusBar.cs @@ -11,8 +11,9 @@ using System.Windows.Media.Imaging; using System.Windows.Shell; using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { class SDStatusBar : StatusBar { diff --git a/src/Main/SharpDevelop/Gui/Workbench/SingleInstanceHelper.cs b/src/Main/SharpDevelop/Workbench/SingleInstanceHelper.cs similarity index 88% rename from src/Main/SharpDevelop/Gui/Workbench/SingleInstanceHelper.cs rename to src/Main/SharpDevelop/Workbench/SingleInstanceHelper.cs index 1ba5be8535..16c476da36 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/SingleInstanceHelper.cs +++ b/src/Main/SharpDevelop/Workbench/SingleInstanceHelper.cs @@ -11,7 +11,7 @@ using System.Windows.Forms; using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Project; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { static class SingleInstanceHelper { @@ -68,15 +68,14 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench return new IntPtr(RESULT_PROJECT_IS_OPEN); } else { try { - WorkbenchSingleton.SafeThreadAsyncCall( - delegate { NativeMethods.SetForegroundWindow(WorkbenchSingleton.MainWin32Window.Handle) ; } - ); + SD.MainThread.InvokeAsync( + delegate { NativeMethods.SetForegroundWindow(SD.Workbench.MainWin32Window.Handle); } + ).FireAndForget(); string tempFileName = Path.Combine(Path.GetTempPath(), "sd" + fileNumber + ".tmp"); foreach (string file in File.ReadAllLines(tempFileName)) { - WorkbenchSingleton.SafeThreadAsyncCall( - delegate(string openFileName) { FileService.OpenFile(openFileName); } - , file - ); + SD.MainThread.InvokeAsync( + delegate { SharpDevelop.FileService.OpenFile(file); } + ).FireAndForget(); } } catch (Exception ex) { LoggingService.Warn(ex); diff --git a/src/Main/SharpDevelop/Gui/Workbench/StatusBarService.cs b/src/Main/SharpDevelop/Workbench/StatusBarService.cs similarity index 94% rename from src/Main/SharpDevelop/Gui/Workbench/StatusBarService.cs rename to src/Main/SharpDevelop/Workbench/StatusBarService.cs index cd5edda002..2737fe15b9 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/StatusBarService.cs +++ b/src/Main/SharpDevelop/Workbench/StatusBarService.cs @@ -8,8 +8,9 @@ using System.Threading; using System.Windows; using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { sealed class StatusBarService : IStatusBarService { @@ -57,7 +58,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench public IProgressMonitor CreateProgressMonitor(CancellationToken cancellationToken = default(CancellationToken)) { - ProgressCollector progress = new ProgressCollector(WorkbenchSingleton.Workbench.SynchronizingObject, cancellationToken); + ProgressCollector progress = new ProgressCollector(SD.MainThread.SynchronizingObject, cancellationToken); AddProgress(progress); return progress.ProgressMonitor; } diff --git a/src/Main/SharpDevelop/Gui/Workbench/WorkbenchStartup.cs b/src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs similarity index 79% rename from src/Main/SharpDevelop/Gui/Workbench/WorkbenchStartup.cs rename to src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs index 3b43869014..99daa05b40 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/WorkbenchStartup.cs +++ b/src/Main/SharpDevelop/Workbench/WorkbenchStartup.cs @@ -4,15 +4,16 @@ using System; using System.Collections.Generic; using System.IO; -using System.Windows.Forms; +using System.Threading; using System.Windows.Interop; using ICSharpCode.Core; -using ICSharpCode.Core.WinForms; using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Parser; using ICSharpCode.SharpDevelop.Project; +using ICSharpCode.SharpDevelop.Startup; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { /// /// Runs workbench initialization. @@ -30,6 +31,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench ComponentDispatcher.ThreadIdle -= ComponentDispatcher_ThreadIdle; // ensure we don't register twice ComponentDispatcher.ThreadIdle += ComponentDispatcher_ThreadIdle; LayoutConfiguration.LoadLayoutConfiguration(); + SD.Services.AddService(typeof(IMessageLoop), new DispatcherMessageLoop(app.Dispatcher, SynchronizationContext.Current)); WorkbenchSingleton.InitializeWorkbench(new WpfWorkbench(), new AvalonDockLayout()); } @@ -54,7 +56,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench if (loader != null) { loader.Load(fullFileName); } else { - FileService.OpenFile(fullFileName); + SharpDevelop.FileService.OpenFile(fullFileName); } } catch (Exception e) { MessageService.ShowException(e, "unable to open file " + file); @@ -63,8 +65,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench // load previous solution if (!didLoadSolutionOrFile && PropertyService.Get("SharpDevelop.LoadPrevProjectOnStartup", false)) { - if (FileService.RecentOpen.RecentProject.Count > 0) { - ProjectService.LoadSolution(FileService.RecentOpen.RecentProject[0]); + if (SharpDevelop.FileService.RecentOpen.RecentProject.Count > 0) { + ProjectService.LoadSolution(SharpDevelop.FileService.RecentOpen.RecentProject[0]); didLoadSolutionOrFile = true; } } @@ -81,14 +83,14 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench NavigationService.ResumeLogging(); - Parser.ParserService.StartParserThread(); + ((ParserService)SD.ParserService).StartParserThread(); // finally run the workbench window ... - app.Run(WorkbenchSingleton.MainWindow); + app.Run(SD.Workbench.MainWindow); // save the workbench memento in the ide properties try { - PropertyService.SetNestedProperties(workbenchMemento, WorkbenchSingleton.Workbench.CreateMemento()); + PropertyService.SetNestedProperties(workbenchMemento, SD.Workbench.CreateMemento()); } catch (Exception e) { MessageService.ShowException(e, "Exception while saving workbench state."); } diff --git a/src/Main/SharpDevelop/Gui/Workbench/WpfWorkbench.cs b/src/Main/SharpDevelop/Workbench/WpfWorkbench.cs similarity index 96% rename from src/Main/SharpDevelop/Gui/Workbench/WpfWorkbench.cs rename to src/Main/SharpDevelop/Workbench/WpfWorkbench.cs index 6a42a58335..af94c4f16f 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/WpfWorkbench.cs +++ b/src/Main/SharpDevelop/Workbench/WpfWorkbench.cs @@ -16,12 +16,15 @@ using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Navigation; + using ICSharpCode.Core; using ICSharpCode.Core.Presentation; +using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Parser; using ICSharpCode.SharpDevelop.Project; +using ICSharpCode.SharpDevelop.Startup; -namespace ICSharpCode.SharpDevelop.Gui.Workbench +namespace ICSharpCode.SharpDevelop.Workbench { /// /// Workbench implementation using WPF and AvalonDock. @@ -53,7 +56,6 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench } public System.Windows.Forms.IWin32Window MainWin32Window { get { return this; } } - public ISynchronizeInvoke SynchronizingObject { get; private set; } public Window MainWindow { get { return this; } } IntPtr System.Windows.Forms.IWin32Window.Handle { @@ -72,7 +74,6 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench public WpfWorkbench() { - this.SynchronizingObject = new WpfSynchronizeInvoke(this.Dispatcher); SD.Services.AddService(typeof(IStatusBarService), new StatusBarService(statusBar)); InitializeComponent(); InitFocusTrackingEvents(); @@ -123,12 +124,12 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench AddHandler(Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler(OnRequestNavigate)); Project.ProjectService.CurrentProjectChanged += SetProjectTitle; - FileService.FileRemoved += CheckRemovedOrReplacedFile; - FileService.FileReplaced += CheckRemovedOrReplacedFile; - FileService.FileRenamed += CheckRenamedFile; + SharpDevelop.FileService.FileRemoved += CheckRemovedOrReplacedFile; + SharpDevelop.FileService.FileReplaced += CheckRemovedOrReplacedFile; + SharpDevelop.FileService.FileRenamed += CheckRenamedFile; - FileService.FileRemoved += FileService.RecentOpen.FileRemoved; - FileService.FileRenamed += FileService.RecentOpen.FileRenamed; + SharpDevelop.FileService.FileRemoved += SharpDevelop.FileService.RecentOpen.FileRemoved; + SharpDevelop.FileService.FileRenamed += SharpDevelop.FileService.RecentOpen.FileRenamed; requerySuggestedEventHandler = new EventHandler(CommandManager_RequerySuggested); CommandManager.RequerySuggested += requerySuggestedEventHandler; @@ -156,7 +157,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench // catch exceptions - e.g. incorrectly installed mail client } } else { - FileService.OpenFile(e.Uri.ToString()); + SharpDevelop.FileService.OpenFile(e.Uri.ToString()); } } @@ -171,7 +172,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench void CheckRemovedOrReplacedFile(object sender, FileEventArgs e) { - foreach (OpenedFile file in FileService.OpenedFiles) { + foreach (OpenedFile file in SharpDevelop.FileService.OpenedFiles) { if (FileUtility.IsBaseDirectory(e.FileName, file.FileName)) { foreach (IViewContent content in file.RegisteredViewContents.ToArray()) { // content.WorkbenchWindow can be null if multiple view contents @@ -189,13 +190,13 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench void CheckRenamedFile(object sender, FileRenameEventArgs e) { if (e.IsDirectory) { - foreach (OpenedFile file in FileService.OpenedFiles) { + foreach (OpenedFile file in SharpDevelop.FileService.OpenedFiles) { if (file.FileName != null && FileUtility.IsBaseDirectory(e.SourceFile, file.FileName)) { file.FileName = new FileName(FileUtility.RenameBaseDirectory(file.FileName, e.SourceFile, e.TargetFile)); } } } else { - OpenedFile file = FileService.GetOpenedFile(e.SourceFile); + OpenedFile file = SharpDevelop.FileService.GetOpenedFile(e.SourceFile); if (file != null) { file.FileName = new FileName(e.TargetFile); } @@ -593,7 +594,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench } Project.ProjectService.CloseSolution(); - ParserService.StopParserThread(); + ((ParserService)SD.ParserService).StopParserThread(); restoreBoundsBeforeClosing = this.RestoreBounds; @@ -668,7 +669,7 @@ namespace ICSharpCode.SharpDevelop.Gui.Workbench if (loader != null) { FileUtility.ObservedLoad(new NamedFileOperationDelegate(loader.Load), file); } else { - FileService.OpenFile(file); + SharpDevelop.FileService.OpenFile(file); } } } diff --git a/src/Main/SharpDevelop/Gui/Workbench/WpfWorkbench.xaml b/src/Main/SharpDevelop/Workbench/WpfWorkbench.xaml similarity index 80% rename from src/Main/SharpDevelop/Gui/Workbench/WpfWorkbench.xaml rename to src/Main/SharpDevelop/Workbench/WpfWorkbench.xaml index e4d2f414ba..b35cf23958 100644 --- a/src/Main/SharpDevelop/Gui/Workbench/WpfWorkbench.xaml +++ b/src/Main/SharpDevelop/Workbench/WpfWorkbench.xaml @@ -1,10 +1,10 @@ - - \ No newline at end of file + \ No newline at end of file