package completion

import (
	

	
	
	
	
)

// Engine is responsible for all completion tasks: generating, computing,
// displaying and updating completion values and inserted candidates.
type Engine struct {
	config        *inputrc.Config // The inputrc contains options relative to completion.
	cached        Completer       // A cached completer function to use when updating.
	autoCompleter Completer       // Completer used by things like autocomplete
	hint          *ui.Hint        // The completions can feed hint/usage messages

	// Line parameters
	keys       *core.Keys      // The input keys reader
	line       *core.Line      // The real input line of the shell.
	cursor     *core.Cursor    // The cursor of the shell.
	selection  *core.Selection // The line selection
	compLine   *core.Line      // A line that might include a virtually inserted candidate.
	compCursor *core.Cursor    // The adjusted cursor.
	keymap     *keymap.Engine  // The main/local keymaps of the shell

	// Completion parameters
	groups      []*group      // All of our suggestions tree is in here
	sm          SuffixMatcher // The suffix matcher is kept for removal after actually inserting the candidate.
	selected    Candidate     // The currently selected item, not yet a real part of the input line.
	prefix      string        // The current tab completion prefix against which to build candidates
	suffix      string        // The current word suffix
	inserted    []rune        // The selected candidate (inserted in line) without prefix or suffix.
	usedY       int           // Comprehensive size offset (terminal rows) of the currently built completions.
	auto        bool          // Is the engine autocompleting ?
	autoForce   bool          // Special autocompletion mode (isearch-style)
	skipDisplay bool          // Don't display completions if there are some.

	// Incremental search
	IsearchRegex       *regexp.Regexp // Holds the current search regex match
	isearchBuf         *core.Line     // The isearch minibuffer
	isearchCur         *core.Cursor   // Cursor position in the minibuffer.
	isearchName        string         // What is being incrementally searched for.
	isearchInsert      bool           // Whether to insert the first match in the line
	isearchForward     bool           // Match results in forward order, or backward.
	isearchSubstring   bool           // Match results as a substring (regex), or as a prefix.
	isearchReplaceLine bool           // Replace the current line with the search result
	isearchStartBuf    string         // The buffer before starting isearch
	isearchStartCursor int            // The cursor position before starting isearch
	isearchLast        string         // The last non-incremental buffer.
	isearchModeExit    keymap.Mode    // The main keymap to restore after exiting isearch
}

// NewEngine initializes a new completion engine with the shell operating parameters.
func ( *ui.Hint,  *keymap.Engine,  *inputrc.Config) *Engine {
	return &Engine{
		config: ,
		hint:   ,
		keymap: ,
	}
}

// Init is used once at shell creation time to pass further parameters to the engine.
func ( *Engine,  *core.Keys,  *core.Line,  *core.Cursor,  *core.Selection,  Completer) {
	.keys = 
	.line = 
	.cursor = 
	.selection = 
	.compLine = 
	.compCursor = 
	.autoCompleter = 
}

// Generate uses a list of completions to group/order and prepares completions before printing them.
// If either no completions or only one is available after all constraints are applied, the engine
// will automatically insert/accept and/or reset itself.
func ( *Engine) ( Values) {
	.prepare()

	if .noCompletions() {
		.ClearMenu(true)
	}

	// Incremental search is a special case, because the user may
	// want to keep searching for another match, so we don't drop
	// the completion list and exit the incremental search mode.
	if .hasUniqueCandidate() && .keymap.Local() != keymap.Isearch {
		.acceptCandidate()
		.ClearMenu(true)
	}
}

// GenerateWith generates completions with a completer function, itself cached
// so that the next time it must update its results, it can reuse this completer.
func ( *Engine) ( Completer) {
	.cached = 
	if .cached == nil {
		return
	}

	// Call the provided/cached completer
	// and use the completions as normal
	.Generate(.cached())
}

// GenerateCached simply recomputes the grid of completions with the pool
// of completions already in memory. This might produce a bigger/smaller/
// different completion grid, for example if it's called on terminal resize.
func ( *Engine) () {
	.GenerateWith(.cached)
}

// SkipDisplay avoids printing completions below the
// input line, but still enables cycling through them.
func ( *Engine) () {
	.skipDisplay = true
}

// Select moves the completion selector by some X or Y value,
// and updates the inserted candidate in the input line.
func ( *Engine) (,  int) {
	 := .currentGroup()

	if  == nil || len(.rows) == 0 {
		return
	}

	// Ensure the completion keymaps are set.
	.adjustSelectKeymap()

	// Some keys used to move around completions
	// will influence the coordinates' offsets.
	,  = .adjustCycleKeys(, )

	// If we already have an inserted candidate
	// remove it before inserting the new one.
	if len(.selected.Value) > 0 {
		.cancelCompletedLine()
	}

	defer .refreshLine()

	// Move the selector
	,  := .moveSelector(, )
	if ! {
		return
	}

	var  *group

	if  {
		.cycleNextGroup()
		 = .currentGroup()
		.firstCell()
	} else {
		.cyclePreviousGroup()
		 = .currentGroup()
		.lastCell()
	}
}

// SelectTag allows to select the first value of the next tag (next=true),
// or the last value of the previous tag (next=false).
func ( *Engine) ( bool) {
	// Ensure the completion keymaps are set.
	.adjustSelectKeymap()

	if len(.groups) <= 1 {
		return
	}

	// If the completion candidate is not empty,
	// it's also inserted in the line, so remove it.
	if len(.selected.Value) > 0 {
		.cancelCompletedLine()
	}

	// In the end we will update the line with the
	// newly/currently selected completion candidate.
	defer .refreshLine()

	if  {
		.cycleNextGroup()
		 := .currentGroup()
		.firstCell()
	} else {
		.cyclePreviousGroup()
		 := .currentGroup()
		.firstCell()
	}
}

// Cancel exits the current completions with the following behavior:
// - If inserted is true, any inserted candidate is removed.
// - If cached is true, any cached completer function is dropped.
//
// This function does not exit the completion keymap, so
// any active completion menu will still be kept/displayed.
func ( *Engine) (,  bool) {
	if  {
		.cached = nil
		.hint.Reset()
	}

	if len(.selected.Value) == 0 && ! {
		return
	}

	defer .cancelCompletedLine()

	// Either drop the inserted candidate,
	// or make it part of the real input line.
	if  {
		.compLine.Set(*.line...)
		.compCursor.Set(.cursor.Pos())
	} else {
		.line.Set(*.compLine...)
		.cursor.Set(.compCursor.Pos())
	}
}

// ResetForce drops any currently inserted candidate from the line,
// drops any cached completer function and generated list, and exits
// the incremental-search mode.
// All those steps are performed whether or not the engine is active.
// If revertLine is true, the line will be reverted to its original state.
func ( *Engine) () {
	.Cancel(!.autoForce, true)
	.ClearMenu(true)

	 := .keymap.Local() == keymap.Isearch ||
		.keymap.Local() == keymap.MenuSelect

	.IsearchStop()
}

// Reset accepts the currently inserted candidate (if any), clears the current
// list of completions and exits the incremental-search mode if active.
// If the completion engine was not active to begin with, nothing will happen.
func ( *Engine) () {
	.autoForce = false
	if !.IsActive() {
		.ClearMenu(true)
		return
	}

	.Cancel(false, true)
	.ClearMenu(true)
	.IsearchStop(false)
}

// ClearMenu exits the current completion keymap (if set) and clears
// the current list of generated completions (if completions is true).
func ( *Engine) ( bool) {
	.skipDisplay = false

	.resetValues(, false)

	if .keymap.Local() == keymap.MenuSelect {
		.keymap.SetLocal("")
	}
}

// IsActive indicates if the engine is currently in possession of a
// non-empty list of generated completions (following all constraints).
func ( *Engine) () bool {
	 := .keymap.Local() == keymap.MenuSelect

	 := .keymap.Local() == keymap.Isearch ||
		.auto || .autoForce

	, ,  := .NonIncrementallySearching()

	return ( || ) && !
}

// IsInserting returns true if a candidate is currently virtually inserted.
func ( *Engine) () bool {
	return .selected.Value != ""
}

// Matches returns the number of completion candidates
// matching the current line/settings requirements.
func ( *Engine) () int {
	,  := .completionCount()
	return 
}

// Line returns the relevant input line at the time this function is called:
// if a candidate is currently selected, the line returned is the one containing
// the candidate. If no candidate is selected, the normal input line is returned.
// When the line returned is the completed one, the corresponding, adjusted cursor.
func ( *Engine) () (*core.Line, *core.Cursor) {
	if len(.selected.Value) > 0 {
		return .compLine, .compCursor
	}

	return .line, .cursor
}

// Autocomplete generates the correct completions in autocomplete mode.
// We don't do it when we are currently in the completion keymap,
// since that means completions have already been computed.
func ( *Engine) () {
	.auto = .needsAutoComplete()

	// Clear the current completion list when we are at the
	// beginning of the line, and not currently completing.
	if .auto || (!.IsActive() && .cursor.Pos() == 0) {
		.resetValues(true, false)
	}

	// We are not auto when either: autocomplete is disabled,
	// incremental-search mode is active, or a completion is
	// currently selected in the menu.
	if !.auto {
		return
	}

	// Regenerate the completions.
	if .cached != nil {
		.prepare(.cached())
	} else if .autoCompleter != nil {
		.prepare(.autoCompleter())
	}
}

// AutocompleteForce forces as-you-type autocomplete on the
// real input line, even if the current cursor position is 0.
func ( *Engine) () {
	.autoForce = true
}

// AutoCompleting returns true if the completion engine is an
// autocompletion mode that has been triggered by a particular
// command (like history-search-forward/backward).
func ( *Engine) () bool {
	return .keymap.Local() == keymap.Isearch || .autoForce
}