oo_ide/operation.rs
1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::path::PathBuf;
4
5use crate::prelude::*;
6
7use app_state::ScreenKind;
8use commands::ArgValue;
9use editor::position::Position;
10use crate::issue_registry::{IssueId, NewIssue, Severity};
11use crate::persistent_issues::PersistentNewIssue;
12use crate::task_registry::{TaskId, TaskKey, TaskStatus, TaskTrigger};
13
14#[derive(Debug, Clone)]
15pub enum ExtensionConfigOp {
16 MoveUp,
17 MoveDown,
18 Select(usize),
19 SetStatus(usize),
20}
21// View-local operations for FileSelector — defined here to avoid a circular
22// dependency between operation.rs and views/file_selector.rs.
23
24#[derive(Debug, Clone)]
25pub enum FileSelectorOp {
26 FilterInput(crate::widgets::input_field::InputFieldOp),
27 SwitchPane,
28 CollapseOrLeft,
29 ExpandOrRight,
30 ToggleDir,
31 /// Delivered by the async search task with the scored+sorted results.
32 /// The `generation` field is used to discard results from superseded
33 /// queries (user typed again before this result arrived).
34 SetResults {
35 generation: u64,
36 paths: Vec<PathBuf>,
37 },
38 /// Delivered by the async directory-read task when a lazy directory
39 /// expansion completes. Each entry is `(absolute_path, is_dir)`.
40 DirLoaded {
41 path: PathBuf,
42 entries: Vec<(PathBuf, bool)>,
43 },
44}
45
46// View-local operations for LogView.
47
48#[derive(Debug, Clone)]
49pub enum LogViewOp {
50 FilterInput(crate::widgets::input_field::InputFieldOp),
51}
52
53// View-local operations for TaskArchiveView.
54
55#[derive(Debug, Clone)]
56pub enum TaskArchiveOp {
57 FilterInput(crate::widgets::input_field::InputFieldOp),
58 /// Open the log for the highlighted task.
59 OpenSelected,
60 /// Cancel the highlighted running task.
61 CancelSelected,
62 /// Sent by app.rs when the task list changes so the view can clamp its cursor.
63 TaskUpdated { new_len: usize },
64 /// Close the inline detail pane and return to the list.
65 CloseDetail,
66 /// Populate the inline detail pane with loaded log lines.
67 ShowDetail {
68 task_id: TaskId,
69 title: String,
70 status: TaskStatus,
71 lines: Vec<String>,
72 },
73}
74
75// ---------------------------------------------------------------------------
76// View-local operations for IssueView.
77// ---------------------------------------------------------------------------
78
79/// View-local operations for crate::views::issue_view::IssueView.
80#[derive(Debug, Clone)]
81pub enum IssueViewOp {
82 FilterInput(crate::widgets::input_field::InputFieldOp),
83 /// Enter — open detail for the selected issue.
84 OpenSelected,
85 /// `r` — resolve the currently selected issue.
86 ResolveSelected,
87 /// `d` — dismiss the currently selected issue.
88 DismissSelected,
89 /// `h` — toggle visibility of dismissed/resolved issues.
90 ToggleShowDismissed,
91 /// `0`–`4` — filter by severity; `None` = show all.
92 SetSeverityFilter(Option<Severity>),
93 /// Esc in Detail mode — back to List.
94 CloseDetail,
95 /// Sent when IssueAdded / IssueRemoved / IssueUpdated arrive, so the view
96 /// can clamp its cursor.
97 RegistryChanged,
98}
99
100// View-local operations for CommandRunner — same pattern as FileSelectorOp.
101
102#[derive(Debug, Clone)]
103pub enum CommandSelectorOp {
104 FilterInput(crate::widgets::input_field::InputFieldOp),
105 SwitchPane,
106 CollapseOrLeft,
107 ExpandOrRight,
108 /// Confirm selection (Enter) from the selector pane.
109 Confirm,
110 /// In the arg-form view: move focus to next field.
111 ArgNext,
112 /// In the arg-form view: move focus to previous field.
113 ArgPrev,
114 /// In the arg-form view: set the focused field's value.
115 ArgChanged(String),
116 /// Cancel the arg form and go back to selector.
117 ArgCancel,
118}
119
120// ---------------------------------------------------------------------------
121// View-local operations for CommitWindow.
122// ---------------------------------------------------------------------------
123
124#[derive(Debug, Clone)]
125pub enum CommitOp {
126 // ── UI trigger ops (view → app → spawn_blocking) ─────────────────────
127 /// Switch between Commit and Diff tabs.
128 SwitchTab,
129 // Commit tab
130 /// Cycle focus: Staged → Unstaged → Message.
131 SwitchPane,
132 /// Stage or unstage the selected file.
133 ToggleFileStage,
134 /// Set the commit message text programmatically (e.g., clear or load a draft).
135 MessageChanged(String),
136 /// Keystroke editing ops for the message field.
137 MessageInsertChar(char),
138 MessageInsertNewline,
139 MessageBackspace,
140 MessageCursorLeft,
141 MessageCursorRight,
142 MessageCursorUp,
143 MessageCursorDown,
144 /// Open the new-branch input (clears any previous value).
145 BranchInputOpen,
146 /// Cancel the branch-name input without creating a branch.
147 BranchInputCancel,
148 /// Set the branch-name input text.
149 BranchChanged(String),
150 /// Confirm new branch from branch_input.
151 BranchCreate,
152 /// Open the branch selector popup (shows existing branches).
153 BranchSelectorOpen,
154 /// Close the branch selector popup without action.
155 BranchSelectorCancel,
156 /// Checkout the given branch (handled by app.rs with git ops).
157 BranchCheckout(String),
158 /// Execute commit (guarded: message must be non-empty).
159 Confirm,
160 // Diff tab
161 /// Toggle focus between file list and diff lines panels in the diff tab.
162 DiffFocusToggle,
163 /// Toggle staged_diff_lines for the selected diff line (local, no git call).
164 /// On a hunk header row this toggles fold instead.
165 ToggleLineStage,
166 /// Revert the selected diff line.
167 RevertLine,
168 /// Request diff for a file.
169 LoadDiff {
170 path: PathBuf,
171 staged: bool,
172 },
173 /// Async result ops (spawn_blocking → op_tx → handle_operation)
174 StatusLoaded {
175 staged: Vec<crate::vcs::StatusEntry>,
176 unstaged: Vec<crate::vcs::StatusEntry>,
177 branches: Vec<String>,
178 current_branch: String,
179 },
180 DiffLoaded {
181 path: PathBuf,
182 /// Whether this diff is staged (HEAD↔index) or unstaged (index↔workdir).
183 staged: bool,
184 lines: Vec<crate::vcs::DiffLine>,
185 },
186 GitOpSuccess(String),
187 GitOpError(String),
188}
189
190// LSP-related types and view-local operations for the editor.
191
192#[derive(Debug, Clone)]
193pub struct LspCompletionItem {
194 pub label: String,
195 pub kind: Option<u8>,
196 pub detail: Option<String>,
197 pub insert_text: Option<String>,
198}
199
200#[derive(Debug, Clone)]
201pub struct LspLocation {
202 pub path: PathBuf,
203 pub row: usize,
204 pub col: usize,
205}
206
207#[derive(Debug, Clone)]
208pub enum ClipOp {
209 /// Copy active selection (or current line if empty) to clipboard + history.
210 Copy,
211 /// Cut active selection (or current line if empty) to clipboard + history.
212 Cut,
213 /// Paste from system clipboard at cursor.
214 Paste,
215 /// Open the clipboard history picker dialog.
216 OpenHistory,
217 /// Navigate history picker up.
218 HistoryUp,
219 /// Navigate history picker down.
220 HistoryDown,
221 /// Paste the selected history item and close picker.
222 HistoryConfirm,
223 /// Close the history picker without pasting.
224 HistoryClose,
225}
226
227#[derive(Debug, Clone)]
228pub enum SelectionOp {
229 /// Extend active selection head (Shift+arrow).
230 Extend { head: Position },
231 /// Select the entire buffer.
232 SelectAll,
233 /// Clear selection (Esc).
234 Clear,
235}
236
237#[derive(Debug, Clone)]
238pub enum GoToLineOp {
239 /// Open the go-to-line bar (or focus it if already open).
240 Open,
241 /// Close the bar without committing.
242 Close,
243 /// Commit the current line number and close.
244 Confirm,
245 /// Feed a key to the line-number input field.
246 Input(crate::widgets::input_field::InputFieldOp),
247}
248
249#[derive(Debug, Clone)]
250pub enum SearchOp {
251 Open {
252 replace: bool,
253 },
254 Close,
255 QueryInput(crate::widgets::input_field::InputFieldOp),
256 ReplacementInput(crate::widgets::input_field::InputFieldOp),
257 ToggleIgnoreCase,
258 ToggleRegex,
259 ToggleSmartCase,
260 /// Toggle query ↔ replacement input focus.
261 FocusSwitch,
262 NextMatch,
263 PrevMatch,
264 ReplaceOne,
265 ReplaceAll,
266 /// Stream one project-wide file result into the editor search state.
267 /// Sent by the async project-search task for every file that contains matches.
268 AddProjectResult { file: PathBuf, result: MatchSpan },
269 /// Clear all project-wide file results — sent before a new search starts.
270 ClearProjectResults,
271 /// Select a file in the project-search file list (0-based index into `files`).
272 SelectFile(usize),
273 /// Scroll the match-results panel by N rows (positive = down, negative = up).
274 ScrollMatchPanel(i8),
275 /// Keystroke routed to the include-glob filter field (Expanded mode only).
276 IncludeGlobInput(crate::widgets::input_field::InputFieldOp),
277 /// Keystroke routed to the exclude-glob filter field (Expanded mode only).
278 ExcludeGlobInput(crate::widgets::input_field::InputFieldOp),
279}
280
281#[derive(Debug, Clone)]
282pub enum LspOp {
283 CompletionResponse {
284 items: Vec<LspCompletionItem>,
285 trigger: Option<Position>,
286 version: Option<i32>,
287 },
288 HoverResponse(Option<String>),
289 CompletionMoveUp,
290 CompletionMoveDown,
291 CompletionConfirm,
292 CompletionDismiss,
293 HoverDismiss,
294}
295
296// ---------------------------------------------------------------------------
297// Project-wide search & replace
298// ---------------------------------------------------------------------------
299
300/// A single match span within a file line.
301#[derive(Debug, Clone)]
302pub struct MatchSpan {
303 /// 0-based line index.
304 pub line: usize,
305 /// Byte offset of match start within the line.
306 pub byte_start: usize,
307 /// Byte offset of match end within the line.
308 pub byte_end: usize,
309 /// Full text of the line (no newline).
310 pub line_text: String,
311}
312
313/// All matches found in one file.
314#[derive(Debug, Clone)]
315pub struct FileMatchResult {
316 pub path: PathBuf,
317 pub matches: Vec<MatchSpan>,
318}
319
320// ---------------------------------------------------------------------------
321// Git History Viewer view-local operations
322// ---------------------------------------------------------------------------
323
324#[derive(Debug, Clone)]
325pub enum GitHistoryOp {
326 /// Keystroke routed to the filter input field.
327 FilterInput(crate::widgets::input_field::InputFieldOp),
328 /// Async result: commits loaded for the current filter + generation.
329 CommitsLoaded {
330 generation: u64,
331 commits: Vec<crate::vcs::CommitInfo>,
332 },
333 /// User confirmed a revision selection (Enter on revision list).
334 /// Intercepted by app.rs which spawns file-list + diff loading.
335 SelectRevision,
336 /// Async result: file list for the selected revision.
337 FilesLoaded {
338 oid: String,
339 files: Vec<crate::vcs::FileChange>,
340 },
341 /// User navigated to or confirmed a file selection.
342 /// Intercepted by app.rs which spawns diff loading.
343 SelectFile,
344 /// Async result: diff for the selected revision + file.
345 DiffLoaded {
346 lines: Vec<crate::vcs::DiffLine>,
347 },
348 /// An async git task failed.
349 LoadError(String),
350}
351
352// ---------------------------------------------------------------------------
353// Terminal view-local operations
354// ---------------------------------------------------------------------------
355
356#[derive(Debug, Clone)]
357pub enum TerminalOp {
358 /// Raw bytes received from the PTY process (sent by the async read task).
359 Output { id: u64, data: Vec<u8> },
360 /// Open a new tab. `command` defaults to the configured shell if `None`.
361 NewTab { command: Option<String> },
362 /// Close a tab by ID. If it was the last tab, a new shell is spawned.
363 CloseTab { id: u64 },
364 /// Switch to the tab at the given index (clamped to valid range).
365 SwitchToTab { index: usize },
366 /// Switch to the next tab (wraps around).
367 NextTab,
368 /// Switch to the previous tab (wraps around).
369 PrevTab,
370 /// Reset scroll to the bottom of the active tab.
371 ScrollReset,
372 /// Open the context menu for the given tab.
373 OpenMenu { id: u64 },
374 /// Close the context menu without taking action.
375 CloseMenu,
376 /// Confirm the highlighted context-menu item.
377 MenuConfirm,
378 /// Open the inline rename input for the given tab directly.
379 OpenRename { id: u64 },
380 /// Clear the screen of the given tab (sends VT clear sequence to PTY).
381 ClearTab { id: u64 },
382 /// Set the rename input text.
383 RenameChanged(String),
384 /// Commit the rename.
385 RenameConfirm,
386 /// Cancel the rename.
387 RenameCancel,
388 /// Open the tab selector popup (keyboard/mouse-navigable list of all tabs).
389 OpenTabSelector,
390 /// Terminal dimensions changed — resize all PTY tabs.
391 Resize { cols: u16, rows: u16 },
392 /// The PTY child process has exited.
393 ProcessExited { id: u64 },
394}
395
396#[derive(Debug, Clone)]
397pub enum SearchReplaceOp {
398 // Input field changes
399 QueryChanged(String),
400 ReplaceChanged(String),
401 IncludeGlobChanged(String),
402 ExcludeGlobChanged(String),
403 // Toggle options
404 ToggleRegex,
405 ToggleCase,
406 ToggleSmartCase,
407 // Tree navigation
408 TreeExpandOrEnter,
409 TreeCollapseOrLeave,
410 // Replace actions
411 ReplaceCurrentFile,
412 ReplaceAll,
413 // Undo / redo
414 Undo,
415 Redo,
416 // Async results
417 SearchResultsReady {
418 results: Vec<FileMatchResult>,
419 generation: u64,
420 },
421 ReplaceComplete {
422 path: PathBuf,
423 replaced_count: usize,
424 },
425 AsyncError(String),
426 ProgressUpdate {
427 scanned: usize,
428 total: usize,
429 },
430}
431
432#[derive(Debug, Clone)]
433pub enum Operation {
434 // Buffer mutations
435 InsertText {
436 path: Option<PathBuf>,
437 #[allow(dead_code)]
438 cursor: Position,
439 text: String,
440 },
441 /// Bracketed paste (or fallback-heuristic detected paste) delivered to the
442 /// active editor. Unlike `InsertText`, the full multi-line string is inserted
443 /// at once via `insert_text_raw` and also pushed to the clipboard register.
444 PasteText {
445 path: Option<PathBuf>,
446 text: String,
447 },
448 DeleteText {
449 path: Option<PathBuf>,
450 #[allow(dead_code)]
451 cursor: Position,
452 #[allow(dead_code)]
453 len: usize,
454 },
455 DeleteForward {
456 path: Option<PathBuf>,
457 #[allow(dead_code)]
458 cursor: Position,
459 },
460 DeleteWordBackward {
461 path: Option<PathBuf>,
462 },
463 DeleteWordForward {
464 path: Option<PathBuf>,
465 },
466 IndentLines {
467 path: Option<PathBuf>,
468 },
469 UnindentLines {
470 path: Option<PathBuf>,
471 },
472 DeleteLine {
473 path: Option<PathBuf>,
474 },
475 /// Replace the text from `start` to `end` (same line, byte offsets) with `text`.
476 /// Used by CompletionConfirm to delete the filter prefix before inserting.
477 ReplaceRange {
478 path: Option<PathBuf>,
479 start: Position,
480 end: Position,
481 text: String,
482 },
483 MoveCursor {
484 path: Option<PathBuf>,
485 cursor: Position,
486 },
487 Undo {
488 path: Option<PathBuf>,
489 },
490 Redo {
491 path: Option<PathBuf>,
492 },
493 ToggleFold {
494 path: Option<PathBuf>,
495 line: usize,
496 },
497 ToggleMarker {
498 path: Option<PathBuf>,
499 line: usize,
500 },
501 ToggleWordWrap,
502
503 // File lifecycle
504 OpenFile {
505 path: PathBuf,
506 },
507 SaveFile {
508 path: PathBuf,
509 },
510 ReloadFromDisk {
511 path: PathBuf,
512 accept_external: bool,
513 },
514
515 // App / navigation
516 SwitchScreen {
517 to: ScreenKind,
518 },
519 /// Close the currently open modal/temporary view.
520 ///
521 /// If the current view is already primary, this is a no-op.
522 CloseModal,
523 Quit,
524
525 // Focus navigation — handled by whichever view is active
526 Focus(crate::widgets::focusable::FocusOp),
527
528 // Generic list/tree/scroll navigation — dispatched to the active view.
529 // Each view interprets these according to its own focus state.
530 NavigateUp,
531 NavigateDown,
532 NavigatePageUp,
533 NavigatePageDown,
534 NavigateHome,
535 NavigateEnd,
536
537 // View-local ops that don't cross view boundaries
538 FileSelectorLocal(FileSelectorOp),
539 CommandSelectorLocal(CommandSelectorOp),
540 LogViewLocal(LogViewOp),
541 CommitLocal(CommitOp),
542 GitHistoryLocal(GitHistoryOp),
543 ExtensionConfig(ExtensionConfigOp),
544
545 // LSP trigger ops (handled in app.rs, forwarded to LspClient)
546 LspTriggerCompletion {
547 #[allow(dead_code)]
548 path: Option<PathBuf>,
549 },
550 LspTriggerHover {
551 #[allow(dead_code)]
552 path: Option<PathBuf>,
553 },
554 LspGoToDefinition {
555 #[allow(dead_code)]
556 path: Option<PathBuf>,
557 },
558 /// Result from a go-to-definition request, dispatched back into the queue.
559 LspGoToDefinitionResult(Vec<LspLocation>),
560 /// LSP editor-local ops (dropdown navigation, response delivery).
561 LspLocal(LspOp),
562 /// Search/replace bar ops (editor-local).
563 SearchLocal(SearchOp),
564 /// Go-to-line bar ops (editor-local).
565 GoToLineLocal(GoToLineOp),
566 /// Selection management ops (editor-local).
567 SelectionLocal(SelectionOp),
568 /// Clipboard ops (editor-local).
569 ClipboardLocal(ClipOp),
570 /// Project-wide search & replace ops.
571 SearchReplaceLocal(SearchReplaceOp),
572
573 /// Execute a registered command by id (with pre-filled args).
574 RunCommand {
575 id: crate::commands::CommandId,
576 args: HashMap<String, ArgValue>,
577 },
578
579 /// Open a generic context menu at `(x, y)` with the given items.
580 ///
581 /// Items are `(label, command_id)` pairs. `app.rs` stores the menu on
582 /// [`crate::app_state::AppState`], intercepts input, and dispatches the selected command.
583 OpenContextMenu {
584 // Each item: (label, command_id, enabled_override)
585 // - enabled_override: `Some(bool)` to explicitly set enabled state,
586 // or `None` to let the runtime compute a sensible default.
587 items: Vec<(String, crate::commands::CommandId, Option<bool>)>,
588 x: u16,
589 y: u16,
590 },
591
592 /// Close the currently open context menu without selecting any item.
593 CloseContextMenu,
594
595 // Escape hatch for plugins
596 #[allow(dead_code)]
597 Generic {
598 source: Cow<'static, str>,
599 name: Cow<'static, str>,
600 payload: HashMap<String, ArgValue>,
601 },
602
603 // Extension system
604 /// Broadcast a named event to all active extensions.
605 ExtensionEvent {
606 name: String,
607 payload: HashMap<String, String>,
608 },
609 /// Display a user-visible notification (shown in status bar).
610 ShowNotification {
611 message: String,
612 level: NotificationLevel,
613 },
614 /// Update the IDE status bar slot with custom text.
615 UpdateStatusBar {
616 text: String,
617 #[allow(unused)]
618 slot: StatusSlot,
619 },
620 /// Request a terminal be created running the given command.
621 CreateTerminal {
622 command: String,
623 },
624 /// Send raw bytes to the PTY of the terminal tab with the given ID.
625 TerminalInput {
626 id: u64,
627 data: Vec<u8>,
628 },
629 /// Terminal view-local operations.
630 TerminalLocal(TerminalOp),
631 /// Show a picker (like command palette) populated with arbitrary items.
632 ShowPicker {
633 title: String,
634 items: Vec<PickerItem>,
635 },
636
637 /// Deliver pre-computed syntax-highlight spans for an editor buffer.
638 ///
639 /// Sent by the background highlight task; handled in `app.rs`, which stores
640 /// the result in `crate::views::editor::EditorView::highlight_cache` when the version still
641 /// matches the live buffer. The next frame then renders without blocking.
642 SetEditorHighlights {
643 path: Option<std::path::PathBuf>,
644 version: editor::buffer::Version,
645 start_line: usize,
646 generation: u64,
647 spans: Vec<Vec<editor::highlight::StyledSpan>>,
648 },
649
650 /// Incremental highlight update for multiple lines.
651 ///
652 /// Unlike SetEditorHighlights, this operation updates individual lines
653 /// without clearing existing highlights. This prevents highlights from
654 /// disappearing while typing or scrolling.
655 SetEditorHighlightsChunk {
656 path: Option<std::path::PathBuf>,
657 version: editor::buffer::Version,
658 generation: u64,
659 spans: Vec<(usize, Vec<editor::highlight::StyledSpan>)>,
660 },
661
662 // -----------------------------------------------------------------------
663 // Issue registry — ephemeral commands + startup loads
664 // (no disk I/O; produced by async tasks and the persistent-load path)
665 // -----------------------------------------------------------------------
666
667 /// Add an issue to the registry (registry-only, no disk write).
668 ///
669 /// `issue.marker = Some(m)` → ephemeral (cleared via `ClearIssuesByMarker`).
670 /// `issue.marker = None` → used only for startup loads by the Persistent
671 /// Component. User-initiated persistent issues
672 /// must go through `PersistentIssueLocal(Add)`.
673 AddIssue { issue: NewIssue },
674
675 /// Remove an issue by ID (registry-only). Emits `IssueRemoved` on success.
676 RemoveIssue { id: IssueId },
677
678 /// Mark an issue as resolved (registry-only). Emits `IssueUpdated` on success.
679 ResolveIssue { id: IssueId },
680
681 /// Mark an issue as dismissed (registry-only). Emits `IssueUpdated` on success.
682 DismissIssue { id: IssueId },
683
684 /// Remove all ephemeral issues belonging to `marker`. Emits one
685 /// `IssueRemoved` per removed issue.
686 ClearIssuesByMarker { marker: String },
687
688 // -----------------------------------------------------------------------
689 // Persistent issue operations — update registry AND write to disk
690 // -----------------------------------------------------------------------
691
692 /// Persistent-issue commands: each variant updates the in-memory
693 /// [`IssueRegistry`] **and** atomically rewrites `.oo/issues.yaml`.
694 ///
695 /// [`IssueRegistry`]: crate::issue_registry::IssueRegistry
696 PersistentIssueLocal(PersistentIssueOp),
697
698 // -----------------------------------------------------------------------
699 // Issue registry — events (enqueued by apply_operation after mutations)
700 // -----------------------------------------------------------------------
701
702 /// An issue was added.
703 IssueAdded { id: IssueId },
704
705 /// An issue was removed.
706 IssueRemoved { id: IssueId },
707
708 /// An issue's `resolved` or `dismissed` flag was toggled.
709 IssueUpdated { id: IssueId },
710
711 // -----------------------------------------------------------------------
712 // Task system lifecycle events
713 // -----------------------------------------------------------------------
714
715 /// A task was created and either started immediately or added to its queue.
716 TaskScheduled(TaskId),
717
718 /// A task transitioned from `Pending` to `Running`.
719 TaskStarted(TaskId),
720
721 /// A task reached a terminal status (`Success`, `Warning`, or `Error`).
722 ///
723 /// Sent by [`crate::task_executor::TaskExecutor`] when the spawned process
724 /// exits. Handled by `apply_operation` in `app.rs`, which calls
725 /// [`crate::task_registry::TaskRegistry::mark_finished`] and starts the
726 /// next queued task if one exists.
727 TaskFinished {
728 id: TaskId,
729 status: TaskStatus,
730 },
731
732 /// A task was cancelled (running or queued).
733 TaskCancelled(TaskId),
734
735 // -----------------------------------------------------------------------
736 // Task system commands
737 // -----------------------------------------------------------------------
738
739 /// Schedule a new task. Handled by `apply_operation`, which calls
740 /// [`crate::task_registry::TaskRegistry::schedule_task`] and immediately
741 /// spawns the executor if the queue was idle.
742 ScheduleTask {
743 key: TaskKey,
744 trigger: TaskTrigger,
745 /// The shell command to execute (passed through to the process).
746 command: String,
747 },
748
749 /// Cancel a running or queued task by its ID. Handled by `apply_operation`,
750 /// which calls [`crate::task_registry::TaskRegistry::cancel`] and starts the
751 /// next queued task if one exists.
752 CancelTask(TaskId),
753
754 /// Open the log view and pre-populate its filter with the task's label so
755 /// the user can quickly inspect task-related entries.
756 OpenLogForTask { task_id: TaskId },
757
758 /// Open the Task Archive View.
759 OpenTaskArchive,
760
761 /// Delivered by app.rs after it asynchronously loads task output from the
762 /// log store. Switches to `TaskOutputView` and populates it with the lines.
763 ShowTaskOutput {
764 task_id: TaskId,
765 title: String,
766 status: TaskStatus,
767 lines: Vec<String>,
768 },
769
770 /// View-local ops for `crate::views::task_archive::TaskArchiveView`.
771 TaskArchiveLocal(TaskArchiveOp),
772
773 /// Open the Issue List View.
774 OpenIssueList,
775
776 /// View-local ops for `crate::views::issue_view::IssueView`.
777 IssueViewLocal(IssueViewOp),
778}
779
780// ---------------------------------------------------------------------------
781// Persistent issue sub-operations
782// ---------------------------------------------------------------------------
783
784/// Commands that manipulate **persistent** issues.
785///
786/// Each variant is handled by `apply_operation` in `app.rs`, which:
787/// 1. Calls the corresponding [`IssueRegistry`] mutation method.
788/// 2. Enqueues the matching `IssueAdded` / `IssueRemoved` / `IssueUpdated`
789/// event operation.
790/// 3. Spawns a fire-and-forget [`crate::persistent_issues::save_atomic`] task.
791///
792/// [`IssueRegistry`]: crate::issue_registry::IssueRegistry
793#[derive(Debug, Clone)]
794pub enum PersistentIssueOp {
795 /// Add a new persistent issue and write the updated list to disk.
796 Add { issue: PersistentNewIssue },
797 /// Remove a persistent issue by ID and write the updated list to disk.
798 Remove { id: IssueId },
799 /// Mark a persistent issue as resolved and write the updated list to disk.
800 Resolve { id: IssueId },
801 /// Mark a persistent issue as dismissed and write the updated list to disk.
802 Dismiss { id: IssueId },
803}
804
805#[derive(Debug, Clone, PartialEq, Eq)]
806pub enum NotificationLevel {
807 Info,
808 Warn,
809 Error,
810}
811
812#[derive(Debug, Clone, PartialEq, Eq)]
813pub enum StatusSlot {
814 Left,
815 Center,
816 Right,
817}
818
819#[derive(Debug, Clone)]
820#[allow(unused)]
821pub struct PickerItem {
822 #[allow(dead_code)]
823 pub label: String,
824 #[allow(dead_code)]
825 pub value: String,
826}
827
828#[derive(Debug, Clone)]
829pub struct Event {
830 /// Which subsystem produced the operation: "editor", "file_selector", "plugin.git"
831 #[allow(dead_code)]
832 pub source: Cow<'static, str>,
833 #[allow(dead_code)]
834 pub kind: EventKind,
835}
836
837#[derive(Debug, Clone)]
838pub enum EventKind {
839 #[allow(dead_code)]
840 Applied(Operation),
841}
842
843impl Event {
844 pub fn applied(source: impl Into<Cow<'static, str>>, op: Operation) -> Self {
845 Self {
846 source: source.into(),
847 kind: EventKind::Applied(op),
848 }
849 }
850}