github.com/elves/elvish@v0.15.0/pkg/edit/navigation.go (about)

     1  package edit
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/elves/elvish/pkg/cli"
     7  	"github.com/elves/elvish/pkg/cli/addons/navigation"
     8  	"github.com/elves/elvish/pkg/eval"
     9  	"github.com/elves/elvish/pkg/eval/vals"
    10  	"github.com/elves/elvish/pkg/eval/vars"
    11  	"github.com/elves/elvish/pkg/parse"
    12  )
    13  
    14  //elvdoc:var selected-file
    15  //
    16  // Name of the currently selected file in navigation mode. $nil if not in
    17  // navigation mode.
    18  
    19  //elvdoc:var navigation:binding
    20  //
    21  // Keybinding for the navigation mode.
    22  
    23  //elvdoc:fn navigation:start
    24  //
    25  // Start the navigation mode.
    26  
    27  //elvdoc:fn navigation:insert-selected
    28  //
    29  // Inserts the selected filename.
    30  
    31  func navInsertSelected(app cli.App) {
    32  	fname := navigation.SelectedName(app)
    33  	if fname == "" {
    34  		// User pressed Alt-Enter or Enter in an empty directory with nothing
    35  		// selected; don't do anything.
    36  		return
    37  	}
    38  
    39  	app.CodeArea().MutateState(func(s *cli.CodeAreaState) {
    40  		dot := s.Buffer.Dot
    41  		if dot != 0 && !strings.ContainsRune(" \n", rune(s.Buffer.Content[dot-1])) {
    42  			// The dot is not at the beginning of a buffer, and the previous
    43  			// character is not a space or newline. Insert a space.
    44  			s.Buffer.InsertAtDot(" ")
    45  		}
    46  		// Insert the selected filename.
    47  		s.Buffer.InsertAtDot(parse.Quote(fname))
    48  	})
    49  }
    50  
    51  //elvdoc:fn navigation:insert-selected-and-quit
    52  //
    53  // Inserts the selected filename and closes the navigation addon.
    54  
    55  func navInsertSelectedAndQuit(app cli.App) {
    56  	navInsertSelected(app)
    57  	closeListing(app)
    58  }
    59  
    60  //elvdoc:fn navigation:trigger-filter
    61  //
    62  // Toggles the filtering status of the navigation addon.
    63  
    64  func navToggleFilter(app cli.App) {
    65  	navigation.MutateFiltering(app, func(b bool) bool { return !b })
    66  }
    67  
    68  //elvdoc:fn navigation:trigger-shown-hidden
    69  //
    70  // Toggles whether the navigation addon should be showing hidden files.
    71  
    72  func navToggleShowHidden(app cli.App) {
    73  	navigation.MutateShowHidden(app, func(b bool) bool { return !b })
    74  }
    75  
    76  //elvdoc:var navigation:width-ratio
    77  //
    78  // A list of 3 integers, used for specifying the width ratio of the 3 columns in
    79  // navigation mode.
    80  
    81  func convertNavWidthRatio(v interface{}) [3]int {
    82  	var (
    83  		numbers []int
    84  		hasErr  bool
    85  	)
    86  	vals.Iterate(v, func(elem interface{}) bool {
    87  		var i int
    88  		err := vals.ScanToGo(elem, &i)
    89  		if err != nil {
    90  			hasErr = true
    91  			return false
    92  		}
    93  		numbers = append(numbers, i)
    94  		return true
    95  	})
    96  	if hasErr || len(numbers) != 3 {
    97  		// TODO: Handle the error.
    98  		return [3]int{1, 3, 4}
    99  	}
   100  	var ret [3]int
   101  	copy(ret[:], numbers)
   102  	return ret
   103  }
   104  
   105  func initNavigation(ed *Editor, ev *eval.Evaler, nb eval.NsBuilder) {
   106  	bindingVar := newBindingVar(EmptyBindingMap)
   107  	binding := newMapBinding(ed, ev, bindingVar)
   108  	widthRatioVar := newListVar(vals.MakeList(1.0, 3.0, 4.0))
   109  
   110  	selectedFileVar := vars.FromGet(func() interface{} {
   111  		name := navigation.SelectedName(ed.app)
   112  		if name == "" {
   113  			return nil
   114  		}
   115  		return name
   116  	})
   117  
   118  	app := ed.app
   119  	nb.Add("selected-file", selectedFileVar)
   120  	nb.AddNs("navigation",
   121  		eval.NsBuilder{
   122  			"binding":     bindingVar,
   123  			"width-ratio": widthRatioVar,
   124  		}.AddGoFns("<edit:navigation>", map[string]interface{}{
   125  			"start": func() {
   126  				navigation.Start(app, navigation.Config{
   127  					Binding: binding,
   128  					WidthRatio: func() [3]int {
   129  						return convertNavWidthRatio(widthRatioVar.Get())
   130  					},
   131  				})
   132  			},
   133  			"left":      func() { navigation.Ascend(app) },
   134  			"right":     func() { navigation.Descend(app) },
   135  			"up":        func() { navigation.Select(app, cli.Prev) },
   136  			"down":      func() { navigation.Select(app, cli.Next) },
   137  			"page-up":   func() { navigation.Select(app, cli.PrevPage) },
   138  			"page-down": func() { navigation.Select(app, cli.NextPage) },
   139  
   140  			"file-preview-up":   func() { navigation.ScrollPreview(app, -1) },
   141  			"file-preview-down": func() { navigation.ScrollPreview(app, 1) },
   142  
   143  			"insert-selected":          func() { navInsertSelected(app) },
   144  			"insert-selected-and-quit": func() { navInsertSelectedAndQuit(app) },
   145  
   146  			"trigger-filter":       func() { navToggleFilter(app) },
   147  			"trigger-shown-hidden": func() { navToggleShowHidden(app) },
   148  		}).Ns())
   149  }