github.com/aretext/aretext@v1.3.0/state/task.go (about) 1 package state 2 3 import ( 4 "context" 5 "log" 6 ) 7 8 // TaskFunc is a task that runs asynchronously. 9 // It accepts a context so that the user can cancel the task if it takes too long. 10 type TaskFunc func(context.Context) func(*EditorState) 11 12 // TaskState represents the state of the currently running task. 13 type TaskState struct { 14 // resultChan receives actions to perform once the task completes. 15 // This is used by the main event loop to update the editor state 16 // once the task completes (meaning the user didn't cancel it). 17 resultChan chan func(*EditorState) 18 19 // cancelFunc is the function to cancel the task's context. 20 cancelFunc context.CancelFunc 21 22 // prevInputMode is the input mode to set once the task completes or is cancelled. 23 prevInputMode InputMode 24 } 25 26 // StartTask starts a task executing asynchronously in a separate goroutine. 27 // If the task completes, it will send an action to state.TaskResultChan() 28 // which the main event loop will receive and execute. 29 // This will also set the input mode to InputModeTask so that the user 30 // can press ESC to cancel the task. 31 func StartTask(state *EditorState, task TaskFunc) { 32 CancelTaskIfRunning(state) 33 34 resultChan := make(chan func(*EditorState), 1) 35 ctx, cancelFunc := context.WithCancel(context.Background()) 36 state.task = &TaskState{ 37 resultChan: resultChan, 38 cancelFunc: cancelFunc, 39 prevInputMode: state.inputMode, 40 } 41 setInputMode(state, InputModeTask) 42 43 log.Printf("Starting task goroutine...\n") 44 go func(ctx context.Context) { 45 action := task(ctx) 46 resultChan <- func(state *EditorState) { 47 prevInputMode := state.task.prevInputMode 48 state.task = nil 49 setInputMode(state, prevInputMode) // from InputModeTask -> prevInputMode 50 action(state) 51 } 52 }(ctx) 53 } 54 55 // CancelTaskIfRunning cancels the current task if one is running; otherwise, it does nothing. 56 func CancelTaskIfRunning(state *EditorState) { 57 if state.task != nil { 58 log.Printf("Cancelling current task...\n") 59 prevInputMode := state.task.prevInputMode 60 state.task.cancelFunc() 61 state.task = nil 62 setInputMode(state, prevInputMode) // from InputModeTask -> prevInputMode 63 } 64 }