Skip to content

Commit 6b356d4

Browse files
authored
Batch movement (#36)
* Batch movement
1 parent 9686b51 commit 6b356d4

File tree

10 files changed

+340
-209
lines changed

10 files changed

+340
-209
lines changed

CSharpCodeAnalyst/App.xaml.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System.IO;
22
using System.Windows;
3-
using System.Windows.Controls;
43
using CodeParser.Parser;
54
using CSharpCodeAnalyst.Analyzers;
65
using CSharpCodeAnalyst.Areas.AdvancedSearchArea;
@@ -36,10 +35,9 @@ protected override async void OnStartup(StartupEventArgs e)
3635

3736
// Run in UI mode
3837
StartUi();
39-
38+
4039
// Faster debugging
4140
await LoadProjectFileFromCommandLineAsync(e);
42-
4341
}
4442

4543
private async Task LoadProjectFileFromCommandLineAsync(StartupEventArgs e)
@@ -52,7 +50,7 @@ private async Task LoadProjectFileFromCommandLineAsync(StartupEventArgs e)
5250
{
5351
return;
5452
}
55-
53+
5654
// Allow loading a project file (json) via command line for faster debugging
5755
if (MainWindow?.DataContext is MainViewModel dc)
5856
{
@@ -70,8 +68,8 @@ private void StartUi()
7068
// ToolTipService.BetweenShowDelayProperty.OverrideMetadata(
7169
// typeof(DependencyObject),
7270
// new FrameworkPropertyMetadata(delayMs));
73-
74-
71+
72+
7573
try
7674
{
7775
Initializer.InitializeMsBuildLocator();
@@ -113,7 +111,7 @@ private void StartUi()
113111
var viewModel = new MainViewModel(messaging, applicationSettings, userSettings, analyzerManager, refactoringService);
114112
var graphViewModel = new GraphViewModel(explorationGraphViewer, explorer, messaging, applicationSettings, refactoringService);
115113
var treeViewModel = new TreeViewModel(messaging, refactoringService);
116-
var searchViewModel = new AdvancedSearchViewModel(messaging);
114+
var searchViewModel = new AdvancedSearchViewModel(messaging, refactoringService);
117115
var infoPanelViewModel = new InfoPanelViewModel();
118116

119117
viewModel.InfoPanelViewModel = infoPanelViewModel;
@@ -130,13 +128,13 @@ private void StartUi()
130128
messaging.Subscribe<ShowPartitionsRequest>(viewModel.HandleShowPartitionsRequest);
131129
messaging.Subscribe<ShowCycleGroupRequest>(viewModel.HandleShowCycleGroupRequest);
132130

133-
131+
134132
// Refactorings are forwarded to all other view models
135133
messaging.Subscribe<CodeGraphRefactored>(viewModel.HandleCodeGraphRefactored);
136134
// messaging.Subscribe<CodeElementsMoved>(viewModel.HandleCodeGraphRefactored);
137135
// messaging.Subscribe<CodeElementsDeleted>(viewModel.HandleCodeGraphRefactored);
138136
// messaging.Subscribe<CodeElementCreated>(viewModel.HandleCodeGraphRefactored);
139-
137+
140138

141139
mainWindow.DataContext = viewModel;
142140
MainWindow = mainWindow;

CSharpCodeAnalyst/Areas/AdvancedSearchArea/AdvancedSearchControl.xaml

Lines changed: 160 additions & 147 deletions
Large diffs are not rendered by default.

CSharpCodeAnalyst/Areas/AdvancedSearchArea/AdvancedSearchControl.xaml.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Windows.Controls;
33
using System.Windows.Controls.Primitives;
44
using System.Windows.Input;
5+
using CSharpCodeAnalyst.Resources;
56

67
namespace CSharpCodeAnalyst.Areas.AdvancedSearchArea;
78

@@ -11,7 +12,7 @@ public AdvancedSearchControl()
1112
{
1213
InitializeComponent();
1314
}
14-
15+
1516
private void DropdownButton_Click(object sender, RoutedEventArgs e)
1617
{
1718
if (sender is Button { ContextMenu: not null } button)
@@ -26,11 +27,21 @@ private void SearchDataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
2627
{
2728
if (e.Key == Key.A && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
2829
{
29-
if (sender is DataGrid {DataContext: AdvancedSearchViewModel viewModel})
30+
if (sender is DataGrid { DataContext: AdvancedSearchViewModel viewModel })
3031
{
3132
e.Handled = true;
3233
viewModel.SelectAllCommand.Execute(null);
3334
}
3435
}
3536
}
37+
38+
private void SearchDataGrid_OnContextMenuOpening(object sender, ContextMenuEventArgs e)
39+
{
40+
// Update the refactoring movement parent
41+
if (SearchDataGrid.DataContext is AdvancedSearchViewModel vm)
42+
{
43+
var parent = vm.GetRefactoringNewMoveParent();
44+
MenuRefactoringMove.Header = string.IsNullOrEmpty(parent) ? Strings.Refactor_MoveSelectedCodeElements : string.Format(Strings.Refactor_MoveSelectedCodeElementTo, parent);
45+
}
46+
}
3647
}

CSharpCodeAnalyst/Areas/AdvancedSearchArea/AdvancedSearchViewModel.cs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System.Collections;
21
using System.Collections.ObjectModel;
32
using System.ComponentModel;
43
using System.Windows;
@@ -7,22 +6,25 @@
76
using Contracts.Graph;
87
using CSharpCodeAnalyst.Common;
98
using CSharpCodeAnalyst.Messages;
9+
using CSharpCodeAnalyst.Refactoring;
1010
using CSharpCodeAnalyst.Wpf;
1111

1212
namespace CSharpCodeAnalyst.Areas.AdvancedSearchArea;
1313

1414
public sealed class AdvancedSearchViewModel : INotifyPropertyChanged
1515
{
1616
private readonly MessageBus _messaging;
17+
private readonly RefactoringService _refactoringService;
1718
private readonly DispatcherTimer _searchTimer;
1819
private ObservableCollection<SearchItemViewModel> _allItems;
1920
private CodeGraph? _codeGraph;
2021
private ObservableCollection<SearchItemViewModel> _filteredItems;
2122
private string _searchText;
2223

23-
public AdvancedSearchViewModel(MessageBus messaging)
24+
public AdvancedSearchViewModel(MessageBus messaging, RefactoringService refactoringService)
2425
{
2526
_messaging = messaging;
27+
_refactoringService = refactoringService;
2628
_searchText = string.Empty;
2729
_allItems = [];
2830
_filteredItems = [];
@@ -45,8 +47,12 @@ public AdvancedSearchViewModel(MessageBus messaging)
4547
CopyToClipboardCommand = new WpfCommand<SearchItemViewModel>(OnCopyToClipboard);
4648
SelectAllCommand = new WpfCommand(SelectAll);
4749
DeselectAllCommand = new WpfCommand(DeselectAll);
50+
51+
SetMovementTargetCommand = new WpfCommand<SearchItemViewModel>(RefactoringSetMovementTarget, RefactoringCanSetMovementTarget);
52+
MoveSelectedCommand = new WpfCommand(RefactoringMoveCodeElement, RefactoringCanMoveCodeElement);
4853
}
4954

55+
5056
public ObservableCollection<SearchItemViewModel> AllItems
5157
{
5258
get => _allItems;
@@ -87,9 +93,43 @@ public string SearchText
8793
public ICommand CopyToClipboardCommand { get; }
8894
public ICommand SelectAllCommand { get; }
8995
public ICommand DeselectAllCommand { get; }
96+
public ICommand SetMovementTargetCommand { get; }
97+
public ICommand MoveSelectedCommand { get; }
9098

9199
public event PropertyChangedEventHandler? PropertyChanged;
92100

101+
private bool RefactoringCanSetMovementTarget(SearchItemViewModel vm)
102+
{
103+
return _refactoringService.CanSetMovementTarget(vm.CodeElement?.Id);
104+
}
105+
106+
private bool RefactoringCanMoveCodeElement()
107+
{
108+
var ids = GetSelectedCodeElements().Select(e => e.Id).ToHashSet();
109+
return _refactoringService.CanMoveCodeElements(ids);
110+
}
111+
112+
private void RefactoringMoveCodeElement()
113+
{
114+
var elementIds = GetSelectedCodeElements().Select(e => e.Id).ToHashSet();
115+
if (elementIds.Any())
116+
{
117+
_refactoringService.MoveCodeElements(elementIds);
118+
}
119+
}
120+
121+
public string GetRefactoringNewMoveParent()
122+
{
123+
var target = _refactoringService.GetMovementTarget();
124+
return target?.Name != null ? target.Name : string.Empty;
125+
}
126+
127+
128+
private void RefactoringSetMovementTarget(SearchItemViewModel vm)
129+
{
130+
_refactoringService.SetMovementTarget(vm.CodeElement?.Id);
131+
}
132+
93133
private void OnCopyToClipboard(SearchItemViewModel item)
94134
{
95135
var text = item.CodeElement?.FullName;
@@ -214,8 +254,8 @@ private void AddSelectedToGraphInternal(bool addCollapsed)
214254
}
215255

216256
/// <summary>
217-
/// Gets selected code elements from all items, including the
218-
/// currently non-visible.
257+
/// Gets selected code elements from all items, including the
258+
/// currently non-visible.
219259
/// </summary>
220260
private List<CodeElement> GetSelectedCodeElements()
221261
{

CSharpCodeAnalyst/Areas/GraphArea/GraphViewModel.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ internal sealed class GraphViewModel : INotifyPropertyChanged
3131
private readonly ApplicationSettings _settings;
3232
private readonly LinkedList<GraphSession> _undoStack;
3333
private readonly IGraphViewer _viewer;
34-
private readonly GraphDropHandler _dropHandler;
3534

3635
private HighlightOption _selectedHighlightOption;
3736
private RenderOption _selectedRenderOption;
@@ -47,7 +46,7 @@ internal GraphViewModel(IGraphViewer viewer, ICodeGraphExplorer explorer, IPubli
4746
_publisher = publisher;
4847
_settings = settings;
4948
_refactoringService = refactoringService;
50-
_dropHandler = new GraphDropHandler(publisher);
49+
DropHandler = new GraphDropHandler(publisher);
5150

5251
// Initialize RenderOptions
5352
RenderOptions =
@@ -277,7 +276,7 @@ public HighlightOption SelectedHighlightOption
277276

278277
public ICommand AddParentsCommand { get; }
279278

280-
public GraphDropHandler DropHandler => _dropHandler;
279+
public GraphDropHandler DropHandler { get; }
281280

282281
public event PropertyChangedEventHandler? PropertyChanged;
283282

@@ -474,7 +473,7 @@ private void OnAddParents()
474473
{
475474
elementIds = _viewer.GetGraph().GetRoots().Select(r => r.Id).ToList();
476475
}
477-
476+
478477
if (elementIds.Any())
479478
{
480479
AddParents(elementIds);
@@ -591,7 +590,7 @@ private void AddToGraph(IEnumerable<CodeElement> originalCodeElements, IEnumerab
591590
// Don't trigger undo
592591
return;
593592
}
594-
593+
595594
PushUndo();
596595

597596
// Apply "Automatically add containing type" setting
@@ -828,6 +827,7 @@ public void HandleCodeGraphRefactored(CodeGraphRefactored message)
828827
}
829828
else if (message is CodeElementsMoved moved)
830829
{
830+
// TODO May be sufficient to just check the source ids and not their children.
831831
// Add the same node ids with the same relationships. This fixes parent/child hierarchy.
832832
// We may have moved more nodes than in the graph. Or the graph is not affected at all by this movement.
833833

@@ -836,13 +836,19 @@ public void HandleCodeGraphRefactored(CodeGraphRefactored message)
836836

837837
// Is the canvas graph affected at all?
838838
var originalGraph = moved.Graph;
839-
var movedIds = originalGraph.Nodes[moved.SourceId].GetChildrenIncludingSelf().ToHashSet();
839+
var movedIds = new HashSet<string>();
840+
foreach (var element in moved.SourceIds.Select(movedId => originalGraph.Nodes[movedId]))
841+
{
842+
movedIds.UnionWith(element.GetChildrenIncludingSelf());
843+
}
844+
840845
if (!movedIds.Intersect(ids).Any())
841846
{
847+
// None of the moved ids is in the graph
842848
return;
843849
}
844850

845-
// I don't know where the element was moved to. I add its parent.
851+
// Add the new parent to ensure the moved elements are visible with correct hierarchy
846852
// Since I cant move an assembly parent is never null
847853
ids.Add(moved.NewParentId);
848854

CSharpCodeAnalyst/Areas/TreeArea/TreeViewModel.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,19 @@ private void OnSelectedItemChanged(TreeItemViewModel item)
9797

9898
private bool RefactoringCanMoveCodeElement(TreeItemViewModel tvm)
9999
{
100-
return _refactoringService.CanMoveCodeElement(tvm?.CodeElement?.Id);
100+
var id = tvm?.CodeElement?.Id;
101+
return id != null && _refactoringService.CanMoveCodeElements([id]);
101102
}
102103

103104
private void RefactoringMoveCodeElement(TreeItemViewModel? tvm)
104105
{
105-
_refactoringService.MoveCodeElement(tvm?.CodeElement?.Id);
106+
var id = tvm?.CodeElement?.Id;
107+
if (id == null)
108+
{
109+
return;
110+
}
111+
112+
_refactoringService.MoveCodeElements([id]);
106113
}
107114

108115
private bool RefactoringCanSetMovementTarget(TreeItemViewModel tvm)
@@ -119,7 +126,7 @@ private void RefactoringSetMovementTarget(TreeItemViewModel tvm)
119126

120127
private static void OnCopyToClipboard(TreeItemViewModel vm)
121128
{
122-
var text = vm?.CodeElement?.FullName;
129+
var text = vm.CodeElement?.FullName;
123130
if (string.IsNullOrEmpty(text))
124131
{
125132
return;
@@ -192,7 +199,14 @@ public void HandleCodeGraphRefactored(CodeGraphRefactored message)
192199
{
193200
// This may be slow but easy.
194201
LoadCodeGraph(moved.Graph);
195-
_messaging.Publish(new LocateInTreeRequest(moved.SourceId));
202+
if (moved.SourceIds.Count == 1)
203+
{
204+
_messaging.Publish(new LocateInTreeRequest(moved.SourceIds.Single()));
205+
}
206+
else
207+
{
208+
_messaging.Publish(new LocateInTreeRequest(moved.NewParentId));
209+
}
196210
}
197211
else if (message is RelationshipsDeleted relationshipsDeleted)
198212
{

CSharpCodeAnalyst/Messages/CodeGraphRefactored.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,10 @@ internal class CodeElementsDeleted(CodeGraph codeGraph, string deletedElementId,
2727
internal class RelationshipsDeleted(CodeGraph codeGraph, List<Relationship> deleted) : CodeGraphRefactored(codeGraph)
2828
{
2929
public List<Relationship> Deleted { get; } = deleted;
30-
3130
}
3231

33-
internal class CodeElementsMoved(CodeGraph codeGraph, string sourceId, string oldParentId, string newParentId) : CodeGraphRefactored(codeGraph)
32+
internal class CodeElementsMoved(CodeGraph codeGraph, HashSet<string> sourceIds, string newParentId) : CodeGraphRefactored(codeGraph)
3433
{
35-
public string SourceId { get; } = sourceId;
36-
public string OldParentId { get; } = oldParentId;
34+
public HashSet<string> SourceIds { get; } = sourceIds;
3735
public string NewParentId { get; } = newParentId;
3836
}

0 commit comments

Comments
 (0)