mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
Bookmarks now capture and restore the requested decompiler view state, support rendered-line fallback anchors, expose hover and pane location affordances, route bookmark context-menu actions through the clicked offset, and update the JSON sidecar under a mutex so concurrent instances do not overwrite unrelated bookmark edits. The plan file records the completed checklist and the remaining manual smoke-test gap. Assisted-by: CodeAlta:gpt-5.5:CodeAltapull/3839/head
23 changed files with 1146 additions and 219 deletions
@ -0,0 +1,138 @@
@@ -0,0 +1,138 @@
|
||||
// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System.Linq; |
||||
using System.Reflection; |
||||
using System.Threading.Tasks; |
||||
|
||||
using Avalonia.Headless.NUnit; |
||||
using Avalonia.Threading; |
||||
|
||||
using AvaloniaEdit.Document; |
||||
|
||||
using AwesomeAssertions; |
||||
|
||||
using ICSharpCode.ILSpy; |
||||
using ICSharpCode.ILSpy.AppEnv; |
||||
using ICSharpCode.ILSpy.Bookmarks; |
||||
using ICSharpCode.ILSpy.Properties; |
||||
using ICSharpCode.ILSpy.TextView; |
||||
using ICSharpCode.ILSpy.TreeNodes; |
||||
|
||||
using NUnit.Framework; |
||||
|
||||
namespace ICSharpCode.ILSpy.Tests.Bookmarks; |
||||
|
||||
[TestFixture] |
||||
public class BookmarkContextMenuTests |
||||
{ |
||||
[AvaloniaTest] |
||||
public async Task Toggle_Bookmark_Entry_Acts_On_The_Right_Clicked_Line_Not_The_Caret() |
||||
{ |
||||
var (window, vm) = await TestHarness.BootAsync(3); |
||||
var manager = AppComposition.Current.GetExport<BookmarkManager>(); |
||||
manager.Clear(); |
||||
|
||||
var coreLibName = typeof(object).Assembly.GetName().Name!; |
||||
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(coreLibName, "System", "System.Object"); |
||||
vm.AssemblyTreeModel.SelectNode(typeNode); |
||||
await vm.DockWorkspace.WaitForDecompiledTextAsync(); |
||||
|
||||
var view = await window.WaitForComponent<DecompilerTextView>(); |
||||
for (int i = 0; i < 8; i++) |
||||
{ |
||||
Dispatcher.UIThread.RunJobs(); |
||||
await Task.Delay(25); |
||||
} |
||||
|
||||
var bookmarkableLines = Enumerable.Range(1, view.Editor.Document.LineCount) |
||||
.Where(view.CanToggleBookmarkAtLine) |
||||
.Take(2) |
||||
.ToArray(); |
||||
bookmarkableLines.Should().HaveCount(2, "two distinct bookmarkable lines are needed to tell the caret from the click"); |
||||
|
||||
int caretLine = bookmarkableLines[0]; |
||||
int clickedLine = bookmarkableLines[1]; |
||||
view.Editor.TextArea.Caret.Offset = view.Editor.Document.GetLineByNumber(caretLine).Offset; |
||||
int clickedOffset = view.Editor.Document.GetLineByNumber(clickedLine).Offset; |
||||
|
||||
var toggle = AppComposition.Current.GetExport<ContextMenuEntryRegistry>() |
||||
.GetEntry(nameof(Resources.BookmarkToggle)); |
||||
toggle.Execute(new TextViewContext { TextView = view, TextLocation = clickedOffset }); |
||||
Dispatcher.UIThread.RunJobs(); |
||||
|
||||
manager.Bookmarks.Should().ContainSingle(); |
||||
view.GetLineForBookmark(manager.Bookmarks[0]).Should().Be(clickedLine); |
||||
manager.Bookmarks[0].LocationNodeName.Should().Be("System.Object"); |
||||
} |
||||
|
||||
[AvaloniaTest] |
||||
public async Task Navigation_Does_Not_Fall_Back_To_Entity_When_Saved_Tree_Path_Is_Missing() |
||||
{ |
||||
var (window, vm) = await TestHarness.BootAsync(3); |
||||
var manager = AppComposition.Current.GetExport<BookmarkManager>(); |
||||
manager.Clear(); |
||||
|
||||
var coreLibName = typeof(object).Assembly.GetName().Name!; |
||||
var objectNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(coreLibName, "System", "System.Object"); |
||||
vm.AssemblyTreeModel.SelectNode(objectNode); |
||||
await vm.DockWorkspace.WaitForDecompiledTextAsync(); |
||||
|
||||
var view = await window.WaitForComponent<DecompilerTextView>(); |
||||
for (int i = 0; i < 8; i++) |
||||
{ |
||||
Dispatcher.UIThread.RunJobs(); |
||||
await Task.Delay(25); |
||||
} |
||||
|
||||
int line = Enumerable.Range(1, view.Editor.Document.LineCount) |
||||
.First(view.CanToggleBookmarkAtLine); |
||||
int offset = view.Editor.Document.GetLineByNumber(line).Offset; |
||||
AppComposition.Current.GetExport<ContextMenuEntryRegistry>() |
||||
.GetEntry(nameof(Resources.BookmarkToggle)) |
||||
.Execute(new TextViewContext { TextView = view, TextLocation = offset }); |
||||
Dispatcher.UIThread.RunJobs(); |
||||
|
||||
manager.Bookmarks.Should().ContainSingle(); |
||||
var bookmark = manager.Bookmarks[0]; |
||||
bookmark.ViewState.Should().NotBeNull(); |
||||
bookmark.ViewState = bookmark.ViewState! with { SelectedTreeNodePath = new[] { "Missing assembly", "Missing type" } }; |
||||
|
||||
var stringNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(coreLibName, "System", "System.String"); |
||||
vm.AssemblyTreeModel.SelectNode(stringNode); |
||||
|
||||
await AppComposition.Current.GetExport<BookmarkNavigator>().NavigateToAsync(bookmark); |
||||
|
||||
((object?)vm.AssemblyTreeModel.SelectedItem).Should().BeSameAs(stringNode); |
||||
} |
||||
|
||||
[AvaloniaTest] |
||||
public async Task Queued_Bookmark_Scroll_Ignores_A_Replaced_Document() |
||||
{ |
||||
var (window, _) = await TestHarness.BootAsync(3); |
||||
var view = await window.WaitForComponent<DecompilerTextView>(); |
||||
view.Editor.Document = new TextDocument(string.Join("\n", Enumerable.Range(1, 30).Select(i => $"line {i}"))); |
||||
|
||||
var scrollToLine = typeof(DecompilerTextView).GetMethod("ScrollToLine", BindingFlags.Instance | BindingFlags.NonPublic); |
||||
scrollToLine.Should().NotBeNull(); |
||||
scrollToLine!.Invoke(view, new object?[] { 20, null }); |
||||
|
||||
view.Editor.Document = new TextDocument("replacement"); |
||||
Dispatcher.UIThread.RunJobs(); |
||||
} |
||||
} |
||||
@ -0,0 +1,137 @@
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
|
||||
using Avalonia; |
||||
using Avalonia.Headless; |
||||
using Avalonia.Headless.NUnit; |
||||
using Avalonia.Input; |
||||
using Avalonia.Interactivity; |
||||
using Avalonia.Threading; |
||||
|
||||
using AvaloniaEdit.Rendering; |
||||
|
||||
using AwesomeAssertions; |
||||
|
||||
using ICSharpCode.ILSpy.AppEnv; |
||||
using ICSharpCode.ILSpy.Bookmarks; |
||||
using ICSharpCode.ILSpy.TextView; |
||||
using ICSharpCode.ILSpy.TreeNodes; |
||||
|
||||
using NUnit.Framework; |
||||
|
||||
namespace ICSharpCode.ILSpy.Tests.Bookmarks; |
||||
|
||||
[TestFixture] |
||||
public class BookmarkGutterTests |
||||
{ |
||||
[AvaloniaTest] |
||||
public async Task Clicking_Bookmark_Gutter_Toggles_The_Visible_Line() |
||||
{ |
||||
var (window, vm) = await TestHarness.BootAsync(3); |
||||
var manager = AppComposition.Current.GetExport<BookmarkManager>(); |
||||
manager.Clear(); |
||||
|
||||
var coreLibName = typeof(object).Assembly.GetName().Name!; |
||||
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(coreLibName, "System", "System.Object"); |
||||
vm.AssemblyTreeModel.SelectNode(typeNode); |
||||
await vm.DockWorkspace.WaitForDecompiledTextAsync(); |
||||
|
||||
var view = await window.WaitForComponent<DecompilerTextView>(); |
||||
for (int i = 0; i < 8; i++) |
||||
{ |
||||
Dispatcher.UIThread.RunJobs(); |
||||
await Task.Delay(25); |
||||
} |
||||
window.UpdateLayout(); |
||||
|
||||
var margin = view.Editor.TextArea.LeftMargins.OfType<BookmarkMargin>().Single(); |
||||
margin.Bounds.Width.Should().BeGreaterThan(0, "the bookmark gutter must reserve hit-testable space"); |
||||
|
||||
var textView = view.Editor.TextArea.TextView; |
||||
textView.EnsureVisualLines(); |
||||
var visualLine = textView.VisualLines.First(line => view.CanToggleBookmarkAtLine(line.FirstDocumentLine.LineNumber)); |
||||
int documentLine = visualLine.FirstDocumentLine.LineNumber; |
||||
double y = visualLine.GetTextLineVisualYPosition(visualLine.TextLines[0], VisualYPosition.LineMiddle) - textView.VerticalOffset; |
||||
y.Should().BeInRange(0, margin.Bounds.Height, "the selected visual line must be inside the gutter bounds"); |
||||
var point = margin.TranslatePoint(new Point(margin.Bounds.Width / 2, y), window); |
||||
point.Should().NotBeNull("the gutter line coordinate must map into the test window"); |
||||
|
||||
int pressed = 0; |
||||
margin.AddHandler(InputElement.PointerPressedEvent, (_, _) => pressed++, RoutingStrategies.Tunnel | RoutingStrategies.Bubble); |
||||
|
||||
window.MouseDown(point!.Value, MouseButton.Left); |
||||
window.MouseUp(point.Value, MouseButton.Left); |
||||
Dispatcher.UIThread.RunJobs(); |
||||
|
||||
pressed.Should().BeGreaterThan(0, "the real pointer event must hit the bookmark gutter"); |
||||
manager.Bookmarks.Should().ContainSingle(); |
||||
view.GetLineForBookmark(manager.Bookmarks[0]).Should().Be(documentLine); |
||||
|
||||
window.MouseDown(point.Value, MouseButton.Left); |
||||
window.MouseUp(point.Value, MouseButton.Left); |
||||
Dispatcher.UIThread.RunJobs(); |
||||
|
||||
manager.Bookmarks.Should().BeEmpty(); |
||||
} |
||||
|
||||
[AvaloniaTest] |
||||
public async Task RightClicking_Bookmark_Gutter_Does_Not_Toggle() |
||||
{ |
||||
var (window, vm) = await TestHarness.BootAsync(3); |
||||
var manager = AppComposition.Current.GetExport<BookmarkManager>(); |
||||
manager.Clear(); |
||||
|
||||
var coreLibName = typeof(object).Assembly.GetName().Name!; |
||||
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(coreLibName, "System", "System.Object"); |
||||
vm.AssemblyTreeModel.SelectNode(typeNode); |
||||
await vm.DockWorkspace.WaitForDecompiledTextAsync(); |
||||
|
||||
var view = await window.WaitForComponent<DecompilerTextView>(); |
||||
for (int i = 0; i < 8; i++) |
||||
{ |
||||
Dispatcher.UIThread.RunJobs(); |
||||
await Task.Delay(25); |
||||
} |
||||
window.UpdateLayout(); |
||||
|
||||
var margin = view.Editor.TextArea.LeftMargins.OfType<BookmarkMargin>().Single(); |
||||
var textView = view.Editor.TextArea.TextView; |
||||
textView.EnsureVisualLines(); |
||||
var visualLine = textView.VisualLines.First(line => view.CanToggleBookmarkAtLine(line.FirstDocumentLine.LineNumber)); |
||||
double y = visualLine.GetTextLineVisualYPosition(visualLine.TextLines[0], VisualYPosition.LineMiddle) - textView.VerticalOffset; |
||||
var point = margin.TranslatePoint(new Point(margin.Bounds.Width / 2, y), window); |
||||
point.Should().NotBeNull("the gutter line coordinate must map into the test window"); |
||||
|
||||
window.MouseDown(point!.Value, MouseButton.Right); |
||||
window.MouseUp(point.Value, MouseButton.Right); |
||||
Dispatcher.UIThread.RunJobs(); |
||||
|
||||
manager.Bookmarks.Should().BeEmpty("a right-click in the gutter must not toggle a bookmark"); |
||||
|
||||
// The same coordinate still toggles on a left-click, proving the gesture is button-gated
|
||||
// rather than disabled at that line.
|
||||
window.MouseDown(point.Value, MouseButton.Left); |
||||
window.MouseUp(point.Value, MouseButton.Left); |
||||
Dispatcher.UIThread.RunJobs(); |
||||
|
||||
manager.Bookmarks.Should().ContainSingle("a left-click at the same coordinate still toggles"); |
||||
} |
||||
} |
||||
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System.Linq; |
||||
|
||||
using Avalonia.Controls; |
||||
using Avalonia.Headless.NUnit; |
||||
using Avalonia.Threading; |
||||
|
||||
using AwesomeAssertions; |
||||
|
||||
using ICSharpCode.ILSpy.Bookmarks; |
||||
using ICSharpCode.ILSpy.Properties; |
||||
using ICSharpCode.ILSpy.Views.Controls; |
||||
|
||||
using NUnit.Framework; |
||||
|
||||
namespace ICSharpCode.ILSpy.Tests.Bookmarks; |
||||
|
||||
[TestFixture] |
||||
public class BookmarksPaneStructureTests |
||||
{ |
||||
[AvaloniaTest] |
||||
public void Toolbar_uses_main_toolbar_chrome_and_button_content() |
||||
{ |
||||
var pane = new BookmarksPane(); |
||||
var toolbarBorder = pane.FindControl<Border>("ToolbarBorder")!; |
||||
var toolbarRoot = pane.FindControl<StackPanel>("ToolbarRoot")!; |
||||
|
||||
toolbarBorder.BorderThickness.Should().Be(new Avalonia.Thickness(0, 0, 0, 1)); |
||||
toolbarBorder.MinHeight.Should().Be(29); |
||||
toolbarBorder.Padding.Should().Be(new Avalonia.Thickness(3)); |
||||
toolbarRoot.Children.OfType<Separator>().Should().HaveCount(2); |
||||
toolbarRoot.Children.OfType<Button>().Should().AllSatisfy(button => { |
||||
button.Content.Should().BeOfType<GrayscaleAwareImage>(); |
||||
}); |
||||
} |
||||
|
||||
[AvaloniaTest] |
||||
public void Module_column_shows_module_name_with_full_path_tooltip() |
||||
{ |
||||
var pane = new BookmarksPane(); |
||||
var grid = pane.FindControl<DataGrid>("BookmarkGrid")!; |
||||
var column = grid.Columns.OfType<DataGridTemplateColumn>() |
||||
.Single(c => Equals(c.Header, Resources.BookmarkModule)); |
||||
var bookmark = new Bookmark { |
||||
Name = "Bookmark0", |
||||
FileName = @"C:\assemblies\Sample.dll", |
||||
AssemblyFullName = "Sample, Version=1.0.0.0", |
||||
ModuleName = "Sample.dll", |
||||
Token = 0x02000001, |
||||
Kind = BookmarkKind.Token, |
||||
LineNumber = 1, |
||||
MemberName = "Sample.Type", |
||||
}; |
||||
|
||||
var textBlock = column.CellTemplate!.Build(bookmark).Should().BeOfType<TextBlock>().Subject; |
||||
textBlock.DataContext = bookmark; |
||||
Dispatcher.UIThread.RunJobs(); |
||||
|
||||
textBlock.Text.Should().Be("Sample.dll"); |
||||
ToolTip.GetTip(textBlock).Should().Be(@"C:\assemblies\Sample.dll"); |
||||
} |
||||
} |
||||
|
Before Width: | Height: | Size: 912 B |
|
Before Width: | Height: | Size: 1008 B |
|
Before Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,100 @@
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
|
||||
using ICSharpCode.ILSpy.Options; |
||||
using ICSharpCode.ILSpy.TextView; |
||||
|
||||
namespace ICSharpCode.ILSpy.Bookmarks |
||||
{ |
||||
/// <summary>A serializable folding range stored inside a bookmark view-state payload.</summary>
|
||||
public sealed record BookmarkFoldingRange(int Start, int End); |
||||
|
||||
/// <summary>Display settings that affect the rendered C# text layout.</summary>
|
||||
public sealed record BookmarkRenderedLayoutSettings( |
||||
bool FoldBraces, |
||||
bool ExpandMemberDefinitions, |
||||
bool ExpandUsingDeclarations, |
||||
bool ShowDebugInfo, |
||||
bool IndentationUseTabs, |
||||
int IndentationSize, |
||||
int IndentationTabSize) |
||||
{ |
||||
public static BookmarkRenderedLayoutSettings From(DisplaySettings display) |
||||
=> new( |
||||
display.FoldBraces, |
||||
display.ExpandMemberDefinitions, |
||||
display.ExpandUsingDeclarations, |
||||
display.ShowDebugInfo, |
||||
display.IndentationUseTabs, |
||||
display.IndentationSize, |
||||
display.IndentationTabSize); |
||||
|
||||
public void ApplyTo(DisplaySettings display) |
||||
{ |
||||
display.FoldBraces = FoldBraces; |
||||
display.ExpandMemberDefinitions = ExpandMemberDefinitions; |
||||
display.ExpandUsingDeclarations = ExpandUsingDeclarations; |
||||
display.ShowDebugInfo = ShowDebugInfo; |
||||
display.IndentationUseTabs = IndentationUseTabs; |
||||
display.IndentationSize = IndentationSize; |
||||
display.IndentationTabSize = IndentationTabSize; |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Serializable editor view state captured with a bookmark. The version is part of the payload so
|
||||
/// future schema changes can be handled without changing the bookmark file version.
|
||||
/// </summary>
|
||||
public sealed record BookmarkViewState( |
||||
int Version, |
||||
int CaretOffset, |
||||
double VerticalOffset, |
||||
double HorizontalOffset, |
||||
int? FoldingChecksum, |
||||
IReadOnlyList<BookmarkFoldingRange>? ExpandedFoldings, |
||||
BookmarkRenderedLayoutSettings? RenderedLayoutSettings = null, |
||||
IReadOnlyList<string>? SelectedTreeNodePath = null) |
||||
{ |
||||
public static BookmarkViewState From(DecompilerTextViewState state, DisplaySettings? displaySettings = null, |
||||
IReadOnlyList<string>? selectedTreeNodePath = null) |
||||
=> new( |
||||
1, |
||||
state.CaretOffset, |
||||
state.VerticalOffset, |
||||
state.HorizontalOffset, |
||||
state.Foldings?.Checksum, |
||||
state.Foldings?.Expanded.Select(r => new BookmarkFoldingRange(r.Start, r.End)).ToArray(), |
||||
displaySettings != null ? BookmarkRenderedLayoutSettings.From(displaySettings) : null, |
||||
selectedTreeNodePath); |
||||
|
||||
public DecompilerTextViewState ToDecompilerTextViewState() |
||||
{ |
||||
FoldingsViewState.Snapshot? foldings = null; |
||||
if (FoldingChecksum is { } checksum) |
||||
{ |
||||
var expanded = ExpandedFoldings?.Select(r => (r.Start, r.End)).ToArray() |
||||
?? System.Array.Empty<(int Start, int End)>(); |
||||
foldings = new FoldingsViewState.Snapshot(expanded, checksum); |
||||
} |
||||
return new DecompilerTextViewState(CaretOffset, VerticalOffset, HorizontalOffset, foldings); |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue