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  }