public override Task ResolveReferencesAsync() { if (UnresolvedReferences != null) { foreach (var unresolvedReference in UnresolvedReferences) { var fullReferencePath = this.ResolveReferencePath(unresolvedReference); if (RoslynWorkspace.GetWorkspace(Solution).ResolveReference(this, unresolvedReference)) { var currentProject = Solution.FindProjectByPath(fullReferencePath); if (currentProject == null) { throw new Exception("Error loading msbuild project, out of sync"); } AddReference(currentProject); } else { AddReference(new UnresolvedReference(Solution, Path.Combine(Solution.CurrentDirectory, Path.GetFileNameWithoutExtension(fullReferencePath)))); } } } return(Task.CompletedTask); }
public OmniSharpProject(string location) : base(true) { Location = location; ExcludedFiles = new List <string>(); Items = new ObservableCollection <IProjectItem>(); References = new ObservableCollection <IProject>(); ToolchainSettings = new ExpandoObject(); DebugSettings = new ExpandoObject(); Settings = new ExpandoObject(); Project = this; var fileWatcher = new FileSystemWatcher(CurrentDirectory, Path.GetFileName(Location)) { EnableRaisingEvents = true, IncludeSubdirectories = false, NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite }; fileWatcher.Changed += async(sender, e) => { // todo restore packages and re-evaluate. RoslynWorkspace.GetWorkspace(Solution).ReevaluateProject(this); }; FileAdded += (sender, e) => { switch (e.Extension) { case ".cs": RoslynWorkspace.GetWorkspace(Solution).AddDocument(RoslynProject, e); break; } }; }
public override void ResolveReferences() { if (UnresolvedReferences != null) { foreach (var unresolvedReference in UnresolvedReferences) { RoslynWorkspace.GetWorkspace(Solution).ResolveReference(this, unresolvedReference); } } }
public async Task <IEnumerable <CodeFix> > GetCodeFixes(IEditor editor, int offset, int length, CancellationToken cancellationToken) { var textSpan = new TextSpan(offset, length); var workspace = RoslynWorkspace.GetWorkspace(editor.SourceFile.Project.Solution); var document = workspace.GetDocument(editor.SourceFile); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); if (textSpan.End >= text.Length) { return(Array.Empty <CodeFix>()); } var codeFixService = IoC.Get <ICodeFixService>(); var fixes = (await codeFixService.GetFixesAsync(document, textSpan, true, cancellationToken)) .SelectMany(f => f.Fixes) .GroupBy(f => f.Action.EquivalenceKey) .Select(group => group.First()).Select(fix => new CodeFix { Action = new RoslynCodeAction(fix.Action) }); try { var codeRefactorings = (await workspace.GetService <ICodeRefactoringService>().GetRefactoringsAsync( document, textSpan, cancellationToken).ConfigureAwait(false)) .Where(x => ExcludedRefactoringProviders.All(p => !x.Provider.GetType().Name.Contains(p))) .SelectMany(refactoring => refactoring.Actions) .GroupBy(f => f.EquivalenceKey) .Select(group => group.First()) .Select(refactoring => new CodeFix { Action = new RoslynCodeAction(refactoring) }); return(fixes.Concat(codeRefactorings)); } catch (TaskCanceledException) { return(fixes); } //var actions = new List<CodeAction>(); //var refactoringContext = new CodeRefactoringContext(document, textSpan, action => actions.Add(action), cancellationToken); //foreach (var action in actions) //{ // result.Add(new CodeFix { Action = new RoslynCodeAction(action) }); //} //return result; }
public IEnumerable <IContextActionProvider> GetContextActionProviders(IEditor editor) { var dataAssociation = GetAssociatedData(editor); var workspace = RoslynWorkspace.GetWorkspace(dataAssociation.Solution); return(new List <IContextActionProvider> { new RoslynContextActionProvider(workspace) }); }
public void RegisterEditor(ITextEditor editor) { _editor = editor; if (dataAssociations.TryGetValue(editor, out CSharpDataAssociation association)) { throw new Exception("Source file already registered with language service."); } association = new CSharpDataAssociation { Solution = editor.SourceFile.Project.Solution }; dataAssociations.Add(editor, association); if (!(editor.SourceFile is MetaDataFile)) { var avaloniaEditTextContainer = new AvalonEditTextContainer(editor.Document) { Editor = editor }; RoslynWorkspace.GetWorkspace(association.Solution).OpenDocument(editor.SourceFile, avaloniaEditTextContainer, (diagnostics) => { var dataAssociation = GetAssociatedData(editor); var results = new List <Diagnostic>(); var fadedCode = new SyntaxHighlightDataList(); foreach (var diagnostic in diagnostics.Diagnostics) { if (diagnostic.CustomTags.Contains("Unnecessary")) { fadedCode.Add(new OffsetSyntaxHighlightingData { Start = diagnostic.TextSpan.Start, Length = diagnostic.TextSpan.Length, Type = HighlightType.Unnecessary }); } else { results.Add(FromRoslynDiagnostic(diagnostic, editor.SourceFile.Location, editor.SourceFile.Project)); } } var errorList = IoC.Get <IErrorList>(); errorList.Remove((diagnostics.Id, editor.SourceFile)); errorList.Create((diagnostics.Id, editor.SourceFile), editor.SourceFile.FilePath, DiagnosticSourceKind.Analysis, results.ToImmutableArray(), fadedCode); });
public void UnregisterSourceFile(IEditor editor) { var association = GetAssociatedData(editor); editor.TextEntered -= association.TextInputHandler; editor.TextEntering -= association.BeforeTextInputHandler; if (!(editor.SourceFile is MetaDataFile)) { RoslynWorkspace.GetWorkspace(association.Solution).CloseDocument(editor.SourceFile); } association.Solution = null; dataAssociations.Remove(editor); }
public static async Task <OmniSharpProject> Create(ISolution solution, string path) { var(project, projectReferences, targetPath) = await RoslynWorkspace.GetWorkspace(solution).AddProject(solution.CurrentDirectory, path); var roslynProject = project; var references = projectReferences; OmniSharpProject result = new OmniSharpProject(path) { Solution = solution, RoslynProject = roslynProject, UnresolvedReferences = references, detectedTargetPath = targetPath }; return(result); }
private Microsoft.CodeAnalysis.Document GetDocument(CSharpDataAssociation dataAssociation, ISourceFile file, RoslynWorkspace workspace = null) { if (file is MetaDataFile metaDataFile) { return(metaDataFile.Document); } else { if (workspace == null) { workspace = RoslynWorkspace.GetWorkspace(dataAssociation.Solution); } return(workspace.GetDocument(file)); } }
public int Format(uint offset, uint length, int cursor) { if (_editor.SourceFile is MetaDataFile) { return(cursor); } var dataAssociation = GetAssociatedData(_editor); var document = RoslynWorkspace.GetWorkspace(dataAssociation.Solution).GetDocument(_editor.SourceFile); var formattedDocument = Formatter.FormatAsync(document).GetAwaiter().GetResult(); RoslynWorkspace.GetWorkspace(dataAssociation.Solution).TryApplyChanges(formattedDocument.Project.Solution); return(-1); }
public async Task <SignatureHelp> SignatureHelp(IEditor editor, List <UnsavedFile> unsavedFiles, int offset, string methodName) { var dataAssociation = GetAssociatedData(editor); var workspace = RoslynWorkspace.GetWorkspace(dataAssociation.Solution); var document = workspace.GetDocument(editor.SourceFile); var invocation = await GetInvocation(document, offset); if (invocation != null) { return(invocation.BuildSignatureHelp()); } return(null); }
public override async Task UnloadAsync() { await base.UnloadAsync(); fileWatcher?.Dispose(); lock (s_unloadLock) { RoslynProject = null; var workspace = RoslynWorkspace.GetWorkspace(Solution, false); if (workspace != null) { RoslynWorkspace.DisposeWorkspace(Solution); } } }
public async Task <Symbol> GetSymbolAsync(IEditor editor, List <UnsavedFile> unsavedFiles, int offset) { var dataAssociation = GetAssociatedData(editor); var workspace = RoslynWorkspace.GetWorkspace(dataAssociation.Solution); var document = GetDocument(dataAssociation, editor.SourceFile, workspace); var semanticModel = await document.GetSemanticModelAsync(); var symbol = await SymbolFinder.FindSymbolAtPositionAsync(semanticModel, offset, workspace); Symbol result = null; if (symbol != null) { result = SymbolFromRoslynSymbol(offset, semanticModel, symbol); } return(result); }
public void RegisterSourceFile(IEditor editor) { if (dataAssociations.TryGetValue(editor, out CSharpDataAssociation association)) { throw new Exception("Source file already registered with language service."); } IndentationStrategy = new CSharpIndentationStrategy(new AvaloniaEdit.TextEditorOptions { ConvertTabsToSpaces = true }); association = new CSharpDataAssociation { Solution = editor.SourceFile.Project.Solution }; dataAssociations.Add(editor, association); if (!(editor.SourceFile is MetaDataFile)) { var avaloniaEditTextContainer = new AvalonEditTextContainer(editor.Document) { Editor = editor }; RoslynWorkspace.GetWorkspace(association.Solution).OpenDocument(editor.SourceFile, avaloniaEditTextContainer, (diagnostics) => { var dataAssociation = GetAssociatedData(editor); var results = new List <Diagnostic>(); var fadedCode = new SyntaxHighlightDataList(); foreach (var diagnostic in diagnostics.Diagnostics) { if (diagnostic.CustomTags.Contains("Unnecessary")) { fadedCode.Add(new OffsetSyntaxHighlightingData { Start = diagnostic.TextSpan.Start, Length = diagnostic.TextSpan.Length, Type = HighlightType.Unnecessary }); } else { results.Add(FromRoslynDiagnostic(diagnostic, editor.SourceFile.Location, editor.SourceFile.Project)); } } DiagnosticsUpdated?.Invoke(this, new DiagnosticsUpdatedEventArgs(diagnostics.Id, editor.SourceFile, (DiagnosticsUpdatedKind)diagnostics.Kind, results.ToImmutableArray(), fadedCode)); }); association.TextInputHandler = (sender, e) => { switch (e.Text) { case "}": case ";": editor.IndentLine(editor.Line); break; case "{": if (IndentationStrategy != null) { editor.IndentLine(editor.Line); } break; } OpenBracket(editor, editor.Document, e.Text); CloseBracket(editor, editor.Document, e.Text); }; association.BeforeTextInputHandler = (sender, e) => { switch (e.Text) { case "\n": case "\r\n": var nextChar = ' '; if (editor.CaretOffset != editor.Document.TextLength) { nextChar = editor.Document.GetCharAt(editor.CaretOffset); } if (nextChar == '}') { var newline = "\r\n"; // TextUtilities.GetNewLineFromDocument(editor.Document, editor.TextArea.Caret.Line); editor.Document.Insert(editor.CaretOffset, newline); editor.Document.TrimTrailingWhiteSpace(editor.Line - 1); editor.IndentLine(editor.Line); editor.CaretOffset -= newline.Length; } break; } }; editor.TextEntered += association.TextInputHandler; editor.TextEntering += association.BeforeTextInputHandler; } }
public async Task <QuickInfoResult> QuickInfo(IEnumerable <UnsavedFile> unsavedFiles, int offset) { var dataAssociation = GetAssociatedData(_editor); var workspace = RoslynWorkspace.GetWorkspace(dataAssociation.Solution); var document = GetDocument(dataAssociation, _editor.SourceFile, workspace); var semanticModel = await document.GetSemanticModelAsync(); var descriptionService = workspace.Services.GetLanguageServices(semanticModel.Language).GetService <ISymbolDisplayService>(); var root = semanticModel.SyntaxTree.GetRoot(CancellationToken.None); SyntaxToken syntaxToken; try { syntaxToken = root.FindToken(offset); } catch (ArgumentOutOfRangeException) { return(null); } if (!syntaxToken.Span.IntersectsWith(offset)) { return(null); } var node = GetBestFitResolveableNode(syntaxToken.Parent); var symbolInfo = semanticModel.GetSymbolInfo(node, CancellationToken.None); var symbol = symbolInfo.Symbol ?? semanticModel.GetDeclaredSymbol(node, CancellationToken.None); if (symbol != null) { var sections = await descriptionService.ToDescriptionGroupsAsync(workspace, semanticModel, offset, new[] { symbol }.AsImmutable(), default(CancellationToken)).ConfigureAwait(false); ImmutableArray <TaggedText> parts; var styledText = StyledText.Create(); var theme = ColorScheme.CurrentColorScheme; if (sections.TryGetValue(SymbolDescriptionGroups.MainDescription, out parts)) { TaggedTextUtil.AppendTaggedText(styledText, theme, parts); } // if generating quick info for an attribute, bind to the class instead of the constructor if (symbol.ContainingType?.IsAttribute() == true) { symbol = symbol.ContainingType; } var formatter = workspace.Services.GetLanguageServices(semanticModel.Language).GetService <IDocumentationCommentFormattingService>(); var documentation = symbol.GetDocumentationParts(semanticModel, offset, formatter, CancellationToken.None); if (documentation != null && documentation.Any()) { styledText.AppendLine(); TaggedTextUtil.AppendTaggedText(styledText, theme, documentation); } if (sections.TryGetValue(SymbolDescriptionGroups.AnonymousTypes, out parts)) { if (!parts.IsDefaultOrEmpty) { styledText.AppendLine(); TaggedTextUtil.AppendTaggedText(styledText, theme, parts); } } if (sections.TryGetValue(SymbolDescriptionGroups.AwaitableUsageText, out parts)) { if (!parts.IsDefaultOrEmpty) { styledText.AppendLine(); TaggedTextUtil.AppendTaggedText(styledText, theme, parts); } } if (sections.TryGetValue(SymbolDescriptionGroups.Exceptions, out parts)) { if (!parts.IsDefaultOrEmpty) { styledText.AppendLine(); TaggedTextUtil.AppendTaggedText(styledText, theme, parts); } } if (sections.TryGetValue(SymbolDescriptionGroups.Captures, out parts)) { if (!parts.IsDefaultOrEmpty) { styledText.AppendLine(); TaggedTextUtil.AppendTaggedText(styledText, theme, parts); } } return(new QuickInfoResult(styledText)); } return(null); }
public async Task <GotoDefinitionInfo> GotoDefinition(int offset) { var dataAssociation = GetAssociatedData(_editor); var document = GetDocument(dataAssociation, _editor.SourceFile); var semanticModel = await document.GetSemanticModelAsync(); var symbol = await SymbolFinder.FindSymbolAtPositionAsync(semanticModel, _editor.Offset, RoslynWorkspace.GetWorkspace(dataAssociation.Solution)); if (symbol != null && !(symbol is Microsoft.CodeAnalysis.INamespaceSymbol)) { // for partial methods, pick the one with body if (symbol is Microsoft.CodeAnalysis.IMethodSymbol method) { symbol = method.PartialImplementationPart ?? symbol; } var location = symbol.Locations.First(); if (location.IsInSource) { var lineSpan = symbol.Locations.First().GetMappedLineSpan(); return(new GotoDefinitionInfo { FileName = lineSpan.Path, Line = lineSpan.StartLinePosition.Line + 1, Column = lineSpan.StartLinePosition.Character + 1 }); } else if (location.IsInMetadata) { var timeout = 5000; var cancellationSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeout)); var(metadataDocument, _) = await _metadataHelper.GetAndAddDocumentFromMetadata(document.Project, symbol, cancellationSource.Token); if (metadataDocument != null) { cancellationSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeout)); var metadataLocation = await _metadataHelper.GetSymbolLocationFromMetadata(symbol, metadataDocument, cancellationSource.Token); var lineSpan = metadataLocation.GetMappedLineSpan(); var metaDataFile = new MetaDataFile(_editor.SourceFile.Project, metadataDocument, metadataDocument.Name, _metadataHelper.GetSymbolName(symbol)); return(new GotoDefinitionInfo { FileName = lineSpan.Path, Line = lineSpan.StartLinePosition.Line + 1, Column = lineSpan.StartLinePosition.Character + 1, MetaDataFile = metaDataFile }); } } } return(null); }
public async Task <CodeCompletionResults> CodeCompleteAtAsync(int index, int line, int column, IEnumerable <UnsavedFile> unsavedFiles, char previousChar, string filter) { if (_editor.SourceFile is MetaDataFile) { return(null); } var result = new CodeCompletionResults(); var dataAssociation = GetAssociatedData(_editor); var workspace = RoslynWorkspace.GetWorkspace(dataAssociation.Solution); var document = workspace.GetDocument(_editor.SourceFile); var semanticModel = await document.GetSemanticModelAsync(); var completionService = CompletionService.GetService(document); var data = await completionService.GetCompletionsAsync(document, index); if (data != null) { var recommendedSymbols = await Microsoft.CodeAnalysis.Recommendations.Recommender.GetRecommendedSymbolsAtPositionAsync(semanticModel, index, workspace); foreach (var completion in data.Items) { var insertionText = completion.DisplayText; if (completion.Properties.ContainsKey("InsertionText")) { insertionText = completion.Properties["InsertionText"]; } var selectionBehavior = Languages.CompletionItemSelectionBehavior.Default; int priority = 0; if (completion.Rules.SelectionBehavior != Microsoft.CodeAnalysis.Completion.CompletionItemSelectionBehavior.Default) { selectionBehavior = (Languages.CompletionItemSelectionBehavior)completion.Rules.SelectionBehavior; priority = completion.Rules.MatchPriority; } if (completion.Properties.ContainsKey("Provider") && completion.Properties["Provider"] == "Microsoft.CodeAnalysis.CSharp.Completion.Providers.SymbolCompletionProvider") { var symbols = recommendedSymbols.Where(x => x.Name == completion.Properties["SymbolName"] && (int)x.Kind == int.Parse(completion.Properties["SymbolKind"])).Distinct(); if (symbols != null && symbols.Any()) { foreach (var symbol in symbols) { if (symbol != null) { var newCompletion = new CodeCompletionData(symbol.Name, completion.FilterText, insertionText, null, selectionBehavior, priority); if (completion.Properties.ContainsKey("SymbolKind")) { newCompletion.Kind = FromOmniSharpKind(completion.Properties["SymbolKind"]); } var xmlDocumentation = symbol.GetDocumentationCommentXml(); if (xmlDocumentation != string.Empty) { var docComment = DocumentationComment.From(xmlDocumentation, Environment.NewLine); newCompletion.BriefComment = docComment.SummaryText; } result.Completions.Add(newCompletion); } } } } else { var newCompletion = new CodeCompletionData(completion.DisplayText, completion.FilterText, insertionText, null, selectionBehavior, priority); if (completion.Properties.ContainsKey("SymbolKind")) { newCompletion.Kind = FromOmniSharpKind(completion.Properties["SymbolKind"]); } result.Completions.Add(newCompletion); } } result.Contexts = Languages.CompletionContext.AnyType; } return(result); }
public OmniSharpProject(string location) : base(true) { Location = location; ExcludedFiles = new List <string>(); Items = new ObservableCollection <IProjectItem>(); References = new ObservableCollection <IProject>(); ToolchainSettings = new ExpandoObject(); DebugSettings = new ExpandoObject(); Settings = new ExpandoObject(); Project = this; Items.InsertSorted(new ReferenceFolder(this)); ExcludedFiles.Add("bin"); ExcludedFiles.Add("obj"); try { fileWatcher = new FileSystemWatcher(CurrentDirectory, Path.GetFileName(Location)) { EnableRaisingEvents = true, IncludeSubdirectories = false, NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite }; fileWatcher.Changed += async(sender, e) => { var lastWriteTime = File.GetLastWriteTime(e.FullPath); if (lastWriteTime != lastProjectFileRead) { lastProjectFileRead = lastWriteTime; RestoreRequired = true; var statusBar = IoC.Get <IStatusBar>(); statusBar.SetText($"Project: {Name} has changed, running restore..."); await Restore(null, statusBar); RestoreRequired = false; statusBar.SetText($"Project: {Name} has changed, re-evaluating project..."); // todo restore packages and re-evaluate. await RoslynWorkspace.GetWorkspace(Solution).ReevaluateProject(this); statusBar.ClearText(); } }; } catch (System.IO.IOException e) { var console = IoC.Get <IConsole>(); console.WriteLine("Reached Max INotify Limit, to use AvalonStudio on Unix increase the INotify Limit"); console.WriteLine("often it is set here: '/proc/sys/fs/inotify/max_user_watches'"); console.WriteLine(e.Message); } FileAdded += (sender, e) => { switch (e.Extension) { case ".cs": RoslynWorkspace.GetWorkspace(Solution).AddDocument(RoslynProject, e); break; } }; }
public void RegisterSourceFile(IEditor editor) { if (dataAssociations.TryGetValue(editor, out CSharpDataAssociation association)) { throw new Exception("Source file already registered with language service."); } IndentationStrategy = new CSharpIndentationStrategy(new AvaloniaEdit.TextEditorOptions { ConvertTabsToSpaces = true }); association = new CSharpDataAssociation { Solution = editor.SourceFile.Project.Solution }; dataAssociations.Add(editor, association); if (!(editor.SourceFile is MetaDataFile)) { var avaloniaEditTextContainer = new AvalonEditTextContainer(editor.Document) { Editor = editor }; RoslynWorkspace.GetWorkspace(association.Solution).OpenDocument(editor.SourceFile, avaloniaEditTextContainer, (diagnostics) => { var dataAssociation = GetAssociatedData(editor); //var results = new TextSegmentCollection<Diagnostic>(); //foreach (var diagnostic in diagnostics.Diagnostics) //{ // results.Add(FromRoslynDiagnostic(diagnostic, editor.SourceFile.Location, editor.SourceFile.Project)); //} //(Diagnostics as Subject<TextSegmentCollection<Diagnostic>>).OnNext(results); }); association.TextInputHandler = (sender, e) => { switch (e.Text) { case "}": case ";": editor.IndentLine(editor.Line); break; case "{": if (IndentationStrategy != null) { editor.IndentLine(editor.Line); } break; } OpenBracket(editor, editor.Document, e.Text); CloseBracket(editor, editor.Document, e.Text); }; association.BeforeTextInputHandler = (sender, e) => { switch (e.Text) { case "\n": case "\r\n": var nextChar = ' '; if (editor.CaretOffset != editor.Document.TextLength) { nextChar = editor.Document.GetCharAt(editor.CaretOffset); } if (nextChar == '}') { var newline = "\r\n"; // TextUtilities.GetNewLineFromDocument(editor.Document, editor.TextArea.Caret.Line); editor.Document.Insert(editor.CaretOffset, newline); editor.Document.TrimTrailingWhiteSpace(editor.Line - 1); editor.IndentLine(editor.Line); editor.CaretOffset -= newline.Length; } break; } }; editor.TextEntered += association.TextInputHandler; editor.TextEntering += association.BeforeTextInputHandler; } }
public async Task <IEnumerable <SymbolRenameInfo> > RenameSymbol(IEditor editor, string renameTo = "") { if (editor.SourceFile is MetaDataFile) { return(null); } var dataAssociation = GetAssociatedData(editor); var workspace = RoslynWorkspace.GetWorkspace(dataAssociation.Solution); var document = GetDocument(dataAssociation, editor.SourceFile, workspace); if (document != null) { var sourceText = await document.GetTextAsync(); var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, editor.CaretOffset); var solution = workspace.CurrentSolution; if (symbol != null) { if (renameTo == string.Empty) { renameTo = "Test" + symbol.Name + "1"; } try { solution = await Renamer.RenameSymbolAsync(solution, symbol, renameTo, workspace.Options); } catch (ArgumentException e) { return(null); } } var changes = new Dictionary <string, SymbolRenameInfo>(); var solutionChanges = solution.GetChanges(workspace.CurrentSolution); foreach (var projectChange in solutionChanges.GetProjectChanges()) { foreach (var changedDocumentId in projectChange.GetChangedDocuments()) { var changedDocument = solution.GetDocument(changedDocumentId); if (!changes.TryGetValue(changedDocument.FilePath, out var modifiedFileResponse)) { modifiedFileResponse = new SymbolRenameInfo(changedDocument.FilePath); changes[changedDocument.FilePath] = modifiedFileResponse; } var originalDocument = workspace.CurrentSolution.GetDocument(changedDocumentId); var linePositionSpanTextChanges = await TextChanges.GetAsync(changedDocument, originalDocument); modifiedFileResponse.Changes = modifiedFileResponse.Changes != null ? modifiedFileResponse.Changes.Union(linePositionSpanTextChanges) : linePositionSpanTextChanges; } } return(changes.Values); } return(null); }