github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/utils/readline/timer.go (about)

     1  package readline
     2  
     3  import (
     4  	"context"
     5  	"sync/atomic"
     6  
     7  	"github.com/lmorg/murex/utils/lists"
     8  )
     9  
    10  func delayedSyntaxTimer(rl *Instance, i int32) {
    11  	if rl.PasswordMask != 0 || rl.DelayedSyntaxWorker == nil {
    12  		return
    13  	}
    14  
    15  	if rl.cacheSyntax.Get(rl.line.Runes()) != "" {
    16  		return
    17  	}
    18  
    19  	if rl.line.CellLen()+rl.promptLen > rl.termWidth {
    20  		// line wraps, which is hard to do with random ANSI escape sequences
    21  		// so better we don't bother trying.
    22  		return
    23  	}
    24  
    25  	newLine := rl.DelayedSyntaxWorker(rl.line.Runes())
    26  	var sLine string
    27  
    28  	if rl.SyntaxHighlighter != nil {
    29  		sLine = rl.SyntaxHighlighter(newLine)
    30  	} else {
    31  		sLine = string(newLine)
    32  	}
    33  	rl.cacheSyntax.Append(rl.line.Runes(), sLine)
    34  
    35  	if atomic.LoadInt32(&rl.delayedSyntaxCount) != i {
    36  		return
    37  	}
    38  
    39  	output := rl.moveCursorToStartStr()
    40  	output += sLine
    41  	output += rl.moveCursorFromEndToLinePosStr()
    42  	print(output)
    43  }
    44  
    45  // DelayedTabContext is a custom context interface for async updates to the tab completions
    46  type DelayedTabContext struct {
    47  	rl      *Instance
    48  	Context context.Context
    49  	cancel  context.CancelFunc
    50  }
    51  
    52  // AppendSuggestions updates the tab completions with additional suggestions asynchronously
    53  func (dtc *DelayedTabContext) AppendSuggestions(suggestions []string) {
    54  	if dtc == nil || dtc.rl == nil {
    55  		return
    56  	}
    57  
    58  	if !dtc.rl.modeTabCompletion {
    59  		return
    60  	}
    61  
    62  	max := dtc.rl.MaxTabCompleterRows * 20
    63  
    64  	if len(dtc.rl.tcSuggestions) == 0 {
    65  		dtc.rl.ForceHintTextUpdate(" ")
    66  	}
    67  
    68  	dtc.rl.tabMutex.Lock()
    69  
    70  	if dtc.rl.tcDescriptions == nil {
    71  		dtc.rl.tcDescriptions = make(map[string]string)
    72  	}
    73  
    74  	for i := range suggestions {
    75  		select {
    76  		case <-dtc.Context.Done():
    77  			dtc.rl.tabMutex.Unlock()
    78  			return
    79  
    80  		default:
    81  			if dtc.rl.tcDescriptions[suggestions[i]] != "" ||
    82  				(len(dtc.rl.tcSuggestions) < max && lists.Match(dtc.rl.tcSuggestions, suggestions[i])) {
    83  				// dedup
    84  				continue
    85  			}
    86  			dtc.rl.tcDescriptions[suggestions[i]] = dtc.rl.tcPrefix + suggestions[i]
    87  			dtc.rl.tcSuggestions = append(dtc.rl.tcSuggestions, suggestions[i])
    88  		}
    89  	}
    90  
    91  	dtc.rl.tabMutex.Unlock()
    92  
    93  	output := dtc.rl.clearHelpersStr()
    94  	//dtc.rl.ForceHintTextUpdate(" ")
    95  	output += dtc.rl.renderHelpersStr()
    96  	print(output)
    97  }
    98  
    99  // AppendDescriptions updates the tab completions with additional suggestions + descriptions asynchronously
   100  func (dtc *DelayedTabContext) AppendDescriptions(suggestions map[string]string) {
   101  	if dtc.rl == nil {
   102  		// This might legitimately happen with some tests
   103  		return
   104  	}
   105  
   106  	if !dtc.rl.modeTabCompletion {
   107  		return
   108  	}
   109  
   110  	max := dtc.rl.MaxTabCompleterRows * 20
   111  
   112  	if len(dtc.rl.tcSuggestions) == 0 {
   113  		dtc.rl.ForceHintTextUpdate(" ")
   114  	}
   115  
   116  	dtc.rl.tabMutex.Lock()
   117  
   118  	for k := range suggestions {
   119  		select {
   120  		case <-dtc.Context.Done():
   121  			dtc.rl.tabMutex.Unlock()
   122  			return
   123  
   124  		default:
   125  			if dtc.rl.tcDescriptions[k] != "" ||
   126  				(len(dtc.rl.tcSuggestions) < max && lists.Match(dtc.rl.tcSuggestions, k)) {
   127  				// dedup
   128  				continue
   129  			}
   130  			dtc.rl.tcDescriptions[k] = suggestions[k]
   131  			dtc.rl.tcSuggestions = append(dtc.rl.tcSuggestions, k)
   132  		}
   133  	}
   134  
   135  	dtc.rl.tabMutex.Unlock()
   136  
   137  	output := dtc.rl.clearHelpersStr()
   138  	//dtc.rl.ForceHintTextUpdate(" ")
   139  	output += dtc.rl.renderHelpersStr()
   140  	print(output)
   141  }
   142  
   143  func delayedPreviewTimer(rl *Instance, fn PreviewFuncT, size *PreviewSizeT, item string) {
   144  	var ctx context.Context
   145  
   146  	callback := func(lines []string, pos int, err error) {
   147  		select {
   148  		case <-ctx.Done():
   149  			return
   150  		default:
   151  			// continue
   152  		}
   153  
   154  		if err != nil {
   155  			rl.ForceHintTextUpdate(err.Error())
   156  			return
   157  		}
   158  
   159  		output, err := rl.previewDrawStr(lines[pos:], size)
   160  
   161  		if err != nil {
   162  			rl.previewCache = nil
   163  			print(output)
   164  			return
   165  		}
   166  
   167  		rl.previewCache = &previewCacheT{
   168  			item:  item,
   169  			pos:   pos,
   170  			len:   size.Height,
   171  			lines: lines,
   172  			size:  size,
   173  		}
   174  
   175  		print(output)
   176  	}
   177  
   178  	ctx, rl.previewCancel = context.WithCancel(context.Background())
   179  	fn(ctx, rl.line.Runes(), item, rl.PreviewImages, size, callback)
   180  }