|
3 | 3 | using System.Diagnostics; |
4 | 4 | using System.Linq; |
5 | 5 | using System.Text.Json; |
| 6 | +using System.Text.RegularExpressions; |
6 | 7 | using System.Threading; |
7 | 8 | using System.Threading.Tasks; |
8 | 9 | using System.Xml.Linq; |
@@ -132,9 +133,25 @@ private void SetupEditorContextMenu() |
132 | 133 | QueryEditor.SelectAll(); |
133 | 134 | }; |
134 | 135 |
|
| 136 | + var executeFromCursorItem = new MenuItem { Header = "Execute from Cursor" }; |
| 137 | + executeFromCursorItem.Click += async (_, _) => |
| 138 | + { |
| 139 | + var text = GetTextFromCursor(); |
| 140 | + if (!string.IsNullOrWhiteSpace(text)) |
| 141 | + await CaptureAndShowPlan(estimated: false, queryTextOverride: text); |
| 142 | + }; |
| 143 | + |
| 144 | + var executeCurrentBatchItem = new MenuItem { Header = "Execute Current Batch" }; |
| 145 | + executeCurrentBatchItem.Click += async (_, _) => |
| 146 | + { |
| 147 | + var text = GetCurrentBatch(); |
| 148 | + if (!string.IsNullOrWhiteSpace(text)) |
| 149 | + await CaptureAndShowPlan(estimated: false, queryTextOverride: text); |
| 150 | + }; |
| 151 | + |
135 | 152 | QueryEditor.TextArea.ContextMenu = new ContextMenu |
136 | 153 | { |
137 | | - Items = { cutItem, copyItem, pasteItem, new Separator(), selectAllItem } |
| 154 | + Items = { cutItem, copyItem, pasteItem, new Separator(), selectAllItem, new Separator(), executeFromCursorItem, executeCurrentBatchItem } |
138 | 155 | }; |
139 | 156 | } |
140 | 157 |
|
@@ -259,6 +276,47 @@ private void OnTextEntered(object? sender, TextInputEventArgs e) |
259 | 276 | return (doc.GetText(start, offset - start), start); |
260 | 277 | } |
261 | 278 |
|
| 279 | + private string? GetSelectedTextOrNull() |
| 280 | + { |
| 281 | + var selection = QueryEditor.TextArea.Selection; |
| 282 | + if (selection.IsEmpty) return null; |
| 283 | + return selection.GetText(); |
| 284 | + } |
| 285 | + |
| 286 | + private string GetTextFromCursor() |
| 287 | + { |
| 288 | + var doc = QueryEditor.Document; |
| 289 | + var offset = QueryEditor.CaretOffset; |
| 290 | + return doc.GetText(offset, doc.TextLength - offset); |
| 291 | + } |
| 292 | + |
| 293 | + private string? GetCurrentBatch() |
| 294 | + { |
| 295 | + var doc = QueryEditor.Document; |
| 296 | + var caretOffset = QueryEditor.CaretOffset; |
| 297 | + var text = doc.Text; |
| 298 | + var goPattern = new Regex(@"^\s*GO\s*$", RegexOptions.IgnoreCase | RegexOptions.Multiline); |
| 299 | + var matches = goPattern.Matches(text); |
| 300 | + |
| 301 | + int batchStart = 0; |
| 302 | + int batchEnd = text.Length; |
| 303 | + |
| 304 | + foreach (Match m in matches) |
| 305 | + { |
| 306 | + if (m.Index + m.Length <= caretOffset) |
| 307 | + { |
| 308 | + batchStart = m.Index + m.Length; |
| 309 | + } |
| 310 | + else if (m.Index >= caretOffset) |
| 311 | + { |
| 312 | + batchEnd = m.Index; |
| 313 | + break; |
| 314 | + } |
| 315 | + } |
| 316 | + |
| 317 | + return text[batchStart..batchEnd].Trim(); |
| 318 | + } |
| 319 | + |
262 | 320 | private void SetStatus(string text, bool autoClear = true) |
263 | 321 | { |
264 | 322 | _statusClearCts?.Cancel(); |
@@ -435,15 +493,17 @@ private async void ExecuteEstimated_Click(object? sender, RoutedEventArgs e) |
435 | 493 | await CaptureAndShowPlan(estimated: true); |
436 | 494 | } |
437 | 495 |
|
438 | | - private async Task CaptureAndShowPlan(bool estimated) |
| 496 | + private async Task CaptureAndShowPlan(bool estimated, string? queryTextOverride = null) |
439 | 497 | { |
440 | 498 | if (_connectionString == null || _selectedDatabase == null) |
441 | 499 | { |
442 | 500 | SetStatus("Connect to a server first", autoClear: false); |
443 | 501 | return; |
444 | 502 | } |
445 | 503 |
|
446 | | - var queryText = QueryEditor.Text?.Trim(); |
| 504 | + var queryText = queryTextOverride?.Trim() |
| 505 | + ?? GetSelectedTextOrNull()?.Trim() |
| 506 | + ?? QueryEditor.Text?.Trim(); |
447 | 507 | if (string.IsNullOrEmpty(queryText)) |
448 | 508 | { |
449 | 509 | SetStatus("Enter a query", autoClear: false); |
|
0 commit comments