Index: editors/micro/Makefile =================================================================== --- editors/micro/Makefile +++ editors/micro/Makefile @@ -3,6 +3,7 @@ PORTNAME= micro PORTVERSION= 1.4.1 DISTVERSIONPREFIX= v +PORTREVISION= 1 CATEGORIES= editors MAINTAINER= swills@FreeBSD.org @@ -14,6 +15,7 @@ USES= go USE_GITHUB= yes + GH_ACCOUNT= zyedidia GH_SUBDIR= src/github.com/zyedidia/${PORTNAME} GH_TUPLE= \ @@ -35,7 +37,7 @@ zyedidia:glob:dd4023a:glob/src/github.com/zyedidia/glob \ zyedidia:poller:ab09682:poller/src/github.com/zyedidia/poller \ zyedidia:pty:3036466:pty/src/github.com/zyedidia/pty \ - zyedidia:tcell:208b6e8:tcell/src/github.com/zyedidia/tcell \ + zyedidia:tcell:869faf86d:tcell/src/github.com/zyedidia/tcell \ zyedidia:terminal:1760577:terminal/src/github.com/zyedidia/terminal \ golang:net:1a68b13:net/src/golang.org/x/net \ golang:text:210eee5:text/src/golang.org/x/text \ @@ -48,7 +50,11 @@ PLIST_FILES= bin/micro PORTDOCS= README.md -OPTIONS_DEFINE= DOCS +OPTIONS_DEFINE= AUTOCOMPLETE DOCS + +AUTOCOMPLETE_DESC= Enable autocompletion (experimental) + +AUTOCOMPLETE_EXTRA_PATCHES= ${PATCHDIR}/extra-patch-autocomplete do-build: @(cd ${WRKSRC} ; \ Index: editors/micro/distinfo =================================================================== --- editors/micro/distinfo +++ editors/micro/distinfo @@ -1,4 +1,4 @@ -TIMESTAMP = 1534863849 +TIMESTAMP = 1561876989 SHA256 (zyedidia-micro-v1.4.1_GH0.tar.gz) = 839b30bb39df45f43ee72583db9fa3b59611228d1b6a65a9e77a999e85b94d01 SIZE (zyedidia-micro-v1.4.1_GH0.tar.gz) = 520409 SHA256 (blang-semver-4a1e882_GH0.tar.gz) = e392b8b07bcc674ac15c0174b48f08528651f21bfbe448b91349825b68bba930 @@ -37,8 +37,8 @@ SIZE (zyedidia-poller-ab09682_GH0.tar.gz) = 15914 SHA256 (zyedidia-pty-3036466_GH0.tar.gz) = 2f5daa0b8df213f7d092bc802c0bbde4b5f1873e9b7762f7c400aa4abc2ffec2 SIZE (zyedidia-pty-3036466_GH0.tar.gz) = 5742 -SHA256 (zyedidia-tcell-208b6e8_GH0.tar.gz) = 39fe9c3f97e43bdb61de9893f6e74476e31d3fd8019f8a7b71357f8ab30fc8f9 -SIZE (zyedidia-tcell-208b6e8_GH0.tar.gz) = 167401 +SHA256 (zyedidia-tcell-869faf86d_GH0.tar.gz) = 75458e3d7f3a84f9c61f6ebdc0336f5fa2e62b955a901efd433569e48faf7714 +SIZE (zyedidia-tcell-869faf86d_GH0.tar.gz) = 167269 SHA256 (zyedidia-terminal-1760577_GH0.tar.gz) = bd521e37c4d1b08f9e4e0fc619da5826c94f88589363541be8e517564231f60a SIZE (zyedidia-terminal-1760577_GH0.tar.gz) = 12884 SHA256 (golang-net-1a68b13_GH0.tar.gz) = d57f23fa50027ed9846060ec7e57f02e3f877d8cc6b230c9b2887a1109749529 Index: editors/micro/files/extra-patch-autocomplete =================================================================== --- /dev/null +++ editors/micro/files/extra-patch-autocomplete @@ -0,0 +1,2164 @@ +# Added autocomplete for Go, based on gocode +# https://github.com/zyedidia/micro/pull/977 + +--- README.md.orig 2018-07-20 00:24:02 UTC ++++ README.md +@@ -66,8 +66,11 @@ You can also check out the website for Micro at https: + * Easily configurable + * Macros + * Common editor things such as undo/redo, line numbers, Unicode support, softwrap... ++* Autocomplete ++ * See [#174](https://github.com/zyedidia/micro/issues/174) ++ * See instructions for enabling below. + +-Although not yet implemented, I hope to add more features such as autocompletion ([#174](https://github.com/zyedidia/micro/issues/174)) or a tree view ([#249](https://github.com/zyedidia/micro/issues/249)) in the future. ++Although not yet implemented, I hope to add more features such as a tree view ([#249](https://github.com/zyedidia/micro/issues/249)) in the future. + + # Installation + +@@ -177,6 +180,20 @@ Many of the Windows terminals don't support more than + that micro's default colorscheme won't look very good. You can either set + the colorscheme to `simple`, or download a better terminal emulator, like + mintty. ++ ++### Autocomplete ++ ++* Beta - use at your own risk. ++* Please check for duplicate bugs before reporting anything you find. ++ ++#### All files ++ ++* Works out common words from text being edited. ++* To enable it, press CtrlE to go to command mode, and enter `setlocal autocomplete true`. This will enable autocomplete for the file until closed. The setting will not persist. ++ ++#### .go files ++ ++* Uses `gocode` which must be installed and available on the path. + + ### Plan9, Cygwin + +--- cmd/micro/actions.go.orig 2018-07-20 00:24:02 UTC ++++ cmd/micro/actions.go +@@ -721,6 +721,12 @@ func (v *View) InsertNewline(usePlugin bool) bool { + } + v.Cursor.LastVisualX = v.Cursor.GetVisualX() + ++ // Allow the completer to access the newline. ++ err := v.Completer.Process('\n') ++ if err != nil { ++ TermMessage(err) ++ } ++ + if usePlugin { + return PostActionCall("InsertNewline", v) + } +--- cmd/micro/completer.go.orig 2019-06-30 06:46:32 UTC ++++ cmd/micro/completer.go +@@ -0,0 +1,387 @@ ++package main ++ ++import ( ++ "fmt" ++ "strings" ++ ++ "github.com/zyedidia/micro/cmd/micro/optionprovider" ++ "github.com/zyedidia/tcell" ++) ++ ++// OptionProvider is the signature of a function which returns all of the available options, potentially using the prefix ++// data. For example, given input "abc\nab", start offset 4 and end offset 5, then the prefix is "ab", and the result ++// should be the option "abc". ++// Logger provides logging. Can be satisfied with t.Logf for tests, or LogToMessenger. ++type OptionProvider func(logger func(s string, values ...interface{}), buffer []byte, startOffset, endOffset int) (options []optionprovider.Option, startOffsetDelta int, err error) ++ ++// ContentSetter is the signature of a function which allows the content of a cell to be set. ++type ContentSetter func(x int, y int, mainc rune, combc []rune, style tcell.Style) ++ ++// CompleterEnabledFlagFromView gets whether autocomplete is enabled from the buffer settings. ++func CompleterEnabledFlagFromView(v *View) func() bool { ++ return func() bool { ++ setting, hasSetting := v.Buf.Settings["autocomplete"] ++ if !hasSetting { ++ return false ++ } ++ enabled, isBool := setting.(bool) ++ if !isBool { ++ return false ++ } ++ return enabled ++ } ++} ++ ++// CurrentBytesAndOffsetFromView gets bytes from a view. ++func CurrentBytesAndOffsetFromView(v *View) func() (bytes []byte, offset int) { ++ return func() (bytes []byte, offset int) { ++ bytes = v.Buf.Buffer(false).Bytes() ++ offset = ByteOffset(v.Cursor.Loc, v.Buf) ++ return ++ } ++} ++ ++// LocationOffsetFromView provides the offset of a given location. ++func LocationOffsetFromView(v *View) func(Loc) (offset int) { ++ return func(l Loc) (offset int) { ++ return ByteOffset(l, v.Buf) ++ } ++} ++ ++// CurrentLocationFromView gets the current location from a view. ++func CurrentLocationFromView(v *View) func() Loc { ++ return func() Loc { ++ return v.Cursor.Loc ++ } ++} ++ ++// ReplaceFromBuffer replaces text in a buffer. ++func ReplaceFromBuffer(buf *Buffer) func(from, to Loc, with string) { ++ return func(from, to Loc, with string) { ++ LogToMessenger()("replacing from %v to %v with %s", from, to, with) ++ buf.Replace(from, to, with) ++ buf.Cursor.GotoLoc(Loc{X: from.X + len(with), Y: from.Y}) ++ } ++} ++ ++// ContentSetterForView sets the content of a cell for the x, y coordinate of a document. ++func ContentSetterForView(v *View) ContentSetter { ++ return func(x int, y int, mainc rune, combc []rune, style tcell.Style) { ++ targetY := y - v.Topline ++ targetX := x + v.leftCol + v.lineNumOffset ++ screen.SetContent(targetX, targetY, mainc, combc, style) ++ } ++} ++ ++// LogToMessenger logs to the global messenger. ++func LogToMessenger() func(s string, values ...interface{}) { ++ return func(s string, values ...interface{}) { ++ messenger.AddLog(fmt.Sprintf(s, values...)) ++ } ++} ++ ++// Completer completes code as you type. ++type Completer struct { ++ // Active is the state which determines whether the completer is active (visible) or not. ++ Active bool ++ // X stores the X position of the suggestion box ++ X int ++ // Y stores the Y position of the suggestion box ++ Y int ++ // Options stores the current list of suggestions. ++ Options []optionprovider.Option ++ // ActiveIndex store the index of the active option (the one that will be selected). ++ ActiveIndex int ++ // Activators are insertions that start autocomplete, e.g. a "." or an opening bracket "(". ++ Activators map[rune]int ++ // Deactivators are insertions that stop autocomplete, e.g. a closing bracket, or a semicolon. ++ Deactivators []rune ++ // Provider is the provider of completion options, e.g. gocode, or another provider such as a language server. ++ Provider OptionProvider ++ // Logger is where log messages are written via fmt.Sprintf. ++ Logger func(s string, values ...interface{}) ++ // CurrentBytesAndOffset is a function which returns the bytes and the current offset position from the current view. ++ CurrentBytesAndOffset func() (bytes []byte, offset int) ++ // CurrentLocation is a function which returns the current location of the cursor. ++ CurrentLocation func() Loc ++ // LocationOffset is a function which returns the offset of a given location. ++ LocationOffset func(Loc) int ++ // Replacer is a function which replaces text. ++ Replacer func(from, to Loc, with string) ++ // Setter is a function which draws to the console at a given location. ++ Setter ContentSetter ++ // OptionStyleInactive is the style for completer options which are not currently highlighted. ++ OptionStyleInactive tcell.Style ++ // OptionStyleActive is the style for completer options which are currently highlighted. ++ OptionStyleActive tcell.Style ++ // Enabled determines whether the view has the enabled flag set or not. ++ Enabled func() bool ++ // PreviousLocation stores the last known location of the cursor. ++ PreviousLocation Loc ++} ++ ++// defaultActivators sets whether the character should start autocompletion. The value of zero means that ++// the character itself is not included in the replacement, -1 means that it is. ++var defaultActivators = map[rune]int{ ++ '.': 0, ++ '(': 0, ++ 'a': -1, 'b': -1, 'c': -1, 'd': -1, 'e': -1, 'f': -1, 'g': -1, 'h': -1, 'i': -1, 'j': -1, 'k': -1, 'l': -1, 'm': -1, 'n': -1, 'o': -1, 'p': -1, 'q': -1, 'r': -1, 's': -1, 't': -1, 'u': -1, 'v': -1, 'w': -1, 'x': -1, 'y': -1, 'z': -1, ++ 'A': -1, 'B': -1, 'C': -1, 'D': -1, 'E': -1, 'F': -1, 'G': -1, 'H': -1, 'I': -1, 'J': -1, 'K': -1, 'L': -1, 'M': -1, 'N': -1, 'O': -1, 'P': -1, 'Q': -1, 'R': -1, 'S': -1, 'T': -1, 'U': -1, 'V': -1, 'W': -1, 'X': -1, 'Y': -1, 'Z': -1, ++} ++ ++const defaultDeactivators = "), \n." ++ ++// NewCompleterForView creates a new autocompleter with defaults for writing to the console. ++func NewCompleterForView(v *View) *Completer { ++ var provider OptionProvider ++ ++ // Load the provider based on filename. ++ fileName := v.Buf.GetName() ++ if strings.HasSuffix(fileName, ".go") { ++ provider = optionprovider.GoCode ++ } else { ++ provider = optionprovider.Generic ++ } ++ ++ // If no matching provider was found, we can't autocomplete. ++ if provider == nil { ++ provider = optionprovider.Noop ++ } ++ ++ return NewCompleter(defaultActivators, []rune(defaultDeactivators), ++ provider, ++ LogToMessenger(), ++ CurrentBytesAndOffsetFromView(v), ++ CurrentLocationFromView(v), ++ LocationOffsetFromView(v), ++ ReplaceFromBuffer(v.Buf), ++ ContentSetterForView(v), ++ colorscheme["default"].Reverse(true), ++ colorscheme["default"], ++ CompleterEnabledFlagFromView(v), ++ ) ++} ++ ++// NewCompleter creates a new completer with all options exposed. See NewCompleterForView for more common usage. ++func NewCompleter(activators map[rune]int, ++ deactivators []rune, ++ provider OptionProvider, ++ logger func(s string, values ...interface{}), ++ currentBytesAndOffset func() (bytes []byte, offset int), ++ currentLocation func() Loc, ++ locationOffset func(Loc) int, ++ replacer func(from, to Loc, with string), ++ setter ContentSetter, ++ optionStyleInactive tcell.Style, ++ optionStyleActive tcell.Style, ++ enabled func() bool) *Completer { ++ return &Completer{ ++ Activators: activators, ++ Deactivators: deactivators, ++ Provider: provider, ++ Logger: logger, ++ CurrentBytesAndOffset: currentBytesAndOffset, ++ CurrentLocation: currentLocation, ++ LocationOffset: locationOffset, ++ Replacer: replacer, ++ Setter: setter, ++ OptionStyleInactive: optionStyleInactive, ++ OptionStyleActive: optionStyleActive, ++ Enabled: enabled, ++ } ++} ++ ++// Process handles incoming events from the view and starts looking up via autocomplete. ++func (c *Completer) Process(r rune) error { ++ if !c.Enabled() { ++ return nil ++ } ++ ++ if c.Provider == nil { ++ return nil ++ } ++ ++ // Hide the autocomplete view if needed. ++ if c.Active && containsRune(c.Deactivators, r) { ++ c.Logger("completer.Process: deactivating, because received %v", string(r)) ++ c.Active = false ++ } ++ ++ if !c.Active { ++ // Check to work out whether we should activate the autocomplete. ++ if indexAdjustment, ok := c.Activators[r]; ok { ++ c.Logger("completer.Process: activating, because received %v", string(r)) ++ c.Active = true ++ currentLocation := c.CurrentLocation() ++ c.PreviousLocation = currentLocation ++ c.X, c.Y = currentLocation.X+indexAdjustment, currentLocation.Y ++ c.Logger("completer.Process: SetStartPosition to %d, %d", c.X, c.Y) ++ } ++ } ++ ++ if !c.Active { ++ // We're not active. ++ return nil ++ } ++ ++ // Get options. ++ //TODO: We only need the answer by the time Display is called, so we could let the rest of the ++ // program continue until we're ready to receive the value by using a go routine or channel. ++ bytes, currentOffset := c.CurrentBytesAndOffset() ++ startOffset := c.LocationOffset(Loc{X: c.X, Y: c.Y}) ++ options, delta, err := c.Provider(c.Logger, bytes, startOffset, currentOffset) ++ if err != nil { ++ return err ++ } ++ c.X += delta ++ c.Options = options ++ c.ActiveIndex = 0 ++ // If there are no options, just deactivate. ++ if len(options) == 0 { ++ c.Logger("completer.Process: Deactivating because there are no options") ++ c.Active = false ++ } ++ return err ++} ++ ++// HandleEvent handles incoming key presses if the completer is active. ++// It returns true if it took over the key action, or false if it didn't. ++func (c *Completer) HandleEvent(key tcell.Key) bool { ++ if !c.Enabled() { ++ c.Logger("completer.HandleEvent: not enabled") ++ return false ++ } ++ if !c.Active { ++ c.Logger("completer.HandleEvent: not active") ++ return false ++ } ++ ++ // Handle selecting various options in the list. ++ switch key { ++ case tcell.KeyUp: ++ if c.ActiveIndex > 0 { ++ c.ActiveIndex-- ++ } ++ break ++ case tcell.KeyDown: ++ if c.ActiveIndex < len(c.Options)-1 { ++ c.ActiveIndex++ ++ } ++ break ++ case tcell.KeyEsc: ++ c.Active = false ++ break ++ case tcell.KeyTab, tcell.KeyEnter: ++ // Complete the text. ++ if toUse, ok := getOption(c.ActiveIndex, c.Options); ok { ++ c.Replacer(Loc{X: c.X, Y: c.Y}, c.CurrentLocation(), toUse) ++ } ++ c.Active = false ++ break ++ default: ++ // Not part of the keys that the autocomplete menu handles. ++ return false ++ } ++ ++ // The completer handled the key. ++ return true ++} ++ ++func getOption(i int, options []optionprovider.Option) (toUse string, ok bool) { ++ if len(options) == 0 { ++ return "", false ++ } ++ if i > len(options)-1 { ++ return "", false ++ } ++ if i < 0 { ++ i = 0 ++ } ++ return options[i].Text(), true ++} ++ ++// DeactivateIfOutOfBounds for example, if duplicating lines or backspacing past the start of the completion. ++func (c *Completer) DeactivateIfOutOfBounds() { ++ // Disable autocomplete if we've switched lines (e.g. by duplicating a line, or moving the cursor away) ++ // of if the X position is equal to or less than current. ++ if !c.Active { ++ return ++ } ++ cur := c.CurrentLocation() ++ beforeStart := cur.X < c.X ++ movedMoreThanOneXSinceLastCheck := distance(c.PreviousLocation.X, cur.X) > 1 ++ c.Logger("completed.DeactivateIfOutOfBounds: Previous loc %v, current loc %v, distance: %v", cur, c.PreviousLocation, distance(c.PreviousLocation.X, cur.X)) ++ movedLine := cur.Y != c.Y ++ if beforeStart || movedMoreThanOneXSinceLastCheck || movedLine { ++ c.Logger("completer.DeactivateIfOutOfBounds: deactivating") ++ c.Active = false ++ } ++ c.PreviousLocation = cur ++} ++ ++func distance(a, b int) int { ++ if a == b { ++ return 0 ++ } ++ if a > b { ++ return a - b ++ } ++ return b - a ++} ++ ++// Display the suggestion box. ++func (c *Completer) Display() { ++ if !c.Enabled() { ++ c.Logger("completer.Display: not enabled") ++ return ++ } ++ if !c.Active { ++ return ++ } ++ ++ c.Logger("completer.Display: showing %d options", len(c.Options)) ++ width := getWidth(c.Options) ++ start := c.CurrentLocation() ++ for iy, o := range c.Options { ++ y := start.Y + iy + 1 // +1 to draw a line below the cursor. ++ ++ // If it's active, show it differently. ++ style := c.OptionStyleInactive ++ if c.ActiveIndex == iy { ++ style = c.OptionStyleActive ++ } ++ ++ // Draw the runes. ++ for ix, r := range padRight(o.Text(), width+1) { ++ x := start.X + ix ++ c.Setter(x, y, r, nil, style) ++ } ++ } ++} ++ ++func getWidth(options []optionprovider.Option) (max int) { ++ for _, o := range options { ++ if l := len(o.Text()); l > max { ++ max = l ++ } ++ } ++ return ++} ++ ++func padRight(s string, minSize int) string { ++ extra := minSize - len(s) ++ if extra > 0 { ++ padding := make([]byte, extra) ++ return s + string(padding) ++ } ++ return s ++} ++ ++func containsRune(array []rune, r rune) bool { ++ for _, r1 := range array { ++ if r1 == r { ++ return true ++ } ++ } ++ return false ++} +--- cmd/micro/completer_test.go.orig 2019-06-30 06:46:32 UTC ++++ cmd/micro/completer_test.go +@@ -0,0 +1,862 @@ ++package main ++ ++import ( ++ "bytes" ++ "reflect" ++ "testing" ++ ++ "github.com/zyedidia/micro/cmd/micro/optionprovider" ++ "github.com/zyedidia/tcell" ++) ++ ++var noopReplacer = func(from, to Loc, with string) {} ++var noopContentSetter = func(x int, y int, mainc rune, combc []rune, style tcell.Style) {} ++var enabledFlagSetToTrue = func() bool { return true } ++var enabledFlagSetToFalse = func() bool { return false } ++ ++var optionStyleInactive = tcell.StyleDefault.Reverse(true) ++ ++const optionStyleActive = tcell.StyleDefault ++ ++func TestCompleterDoesNothingWhenNotEnabledOrProviderNotSet(t *testing.T) { ++ var currentBytesAndOffsetCalled, currentLocationCalled, locationOffsetCalled, providerCalled bool ++ ++ currentBytesAndOffset := func() (bytes []byte, offset int) { ++ currentBytesAndOffsetCalled = true ++ return []byte("fmt.Println("), 3 ++ } ++ currentLocation := func() Loc { ++ currentLocationCalled = true ++ return Loc{X: 1, Y: 2} ++ } ++ locationOffset := func(Loc) int { ++ locationOffsetCalled = true ++ return 0 ++ } ++ provider := func(l func(s string, values ...interface{}), buffer []byte, startOffset, currentOffset int) (options []optionprovider.Option, delta int, err error) { ++ providerCalled = true ++ return ++ } ++ ++ activators := map[rune]int{ ++ '(': 0, ++ } ++ deactivators := []rune{')', ';'} ++ ++ c := NewCompleter(activators, ++ deactivators, ++ provider, ++ t.Logf, ++ currentBytesAndOffset, ++ currentLocation, ++ locationOffset, ++ noopReplacer, ++ noopContentSetter, ++ optionStyleInactive, ++ optionStyleActive, ++ enabledFlagSetToFalse) ++ ++ // It's not enabled, so nothing should be called. ++ err := c.Process('(') ++ if err != nil { ++ t.Fatalf("not enabled: failed to process with error: %v", err) ++ } ++ if currentBytesAndOffsetCalled || currentLocationCalled || locationOffsetCalled { ++ t.Errorf("not enabled: when disabled, no functions should be called") ++ } ++ ++ // It's enabled, but the provider is nil. ++ c.Enabled = enabledFlagSetToTrue ++ c.Provider = nil ++ ++ err = c.Process('(') ++ if err != nil { ++ t.Fatalf("enabled: failed to process with error: %v", err) ++ } ++ if currentBytesAndOffsetCalled || currentLocationCalled || locationOffsetCalled || providerCalled { ++ t.Errorf("enabled: when disabled, no functions should be called") ++ } ++} ++ ++func TestCompleterIsDeactivatedByDeactivatorRunes(t *testing.T) { ++ activators := map[rune]int{ ++ '(': 0, ++ } ++ deactivators := []rune{')', ';'} ++ currentBytesAndOffset := func() (bytes []byte, offset int) { ++ return []byte("fmt.Println("), 3 ++ } ++ currentLocation := func() Loc { ++ return Loc{X: 1, Y: 2} ++ } ++ locationOffset := func(Loc) int { ++ return 3 ++ } ++ provider := func(l func(s string, values ...interface{}), buffer []byte, startOffset, currentOffset int) (options []optionprovider.Option, delta int, err error) { ++ options = []optionprovider.Option{ ++ optionprovider.New("text", "hint"), ++ } ++ return ++ } ++ ++ c := NewCompleter(activators, ++ deactivators, ++ provider, ++ t.Logf, ++ currentBytesAndOffset, ++ currentLocation, ++ locationOffset, ++ noopReplacer, ++ noopContentSetter, ++ optionStyleInactive, ++ optionStyleActive, ++ enabledFlagSetToTrue) ++ ++ c.Active = true ++ ++ err := c.Process(')') ++ if err != nil { ++ t.Fatalf("failed to process with error: %v", err) ++ } ++ if c.Active { ++ t.Errorf("expected ')' to deactivate the completer, but it didn't") ++ } ++} ++ ++func TestCompleterIsActivatedByActivatorRunes(t *testing.T) { ++ var providerReceivedBytes []byte ++ var providerReceivedOffset int ++ ++ expectedOptions := []optionprovider.Option{ ++ optionprovider.New("text", "hint"), ++ optionprovider.New("text1", "hint1"), ++ } ++ ++ activators := map[rune]int{ ++ '(': 0, ++ } ++ deactivators := []rune{')', ';'} ++ currentBytesAndOffset := func() (bytes []byte, offset int) { ++ return []byte("fmt.Println("), 3 ++ } ++ currentLocation := func() Loc { ++ return Loc{X: 1, Y: 2} ++ } ++ locationOffset := func(Loc) int { ++ return 3 ++ } ++ provider := func(l func(s string, values ...interface{}), buffer []byte, startOffset, currentOffset int) (options []optionprovider.Option, delta int, err error) { ++ providerReceivedBytes = buffer ++ providerReceivedOffset = currentOffset ++ options = expectedOptions ++ return ++ } ++ c := NewCompleter(activators, ++ deactivators, ++ provider, ++ t.Logf, ++ currentBytesAndOffset, ++ currentLocation, ++ locationOffset, ++ noopReplacer, ++ noopContentSetter, ++ optionStyleInactive, ++ optionStyleActive, ++ enabledFlagSetToTrue) ++ ++ err := c.Process('(') ++ if err != nil { ++ t.Fatalf("failed to process with error: %v", err) ++ } ++ if !c.Active { ++ t.Errorf("expected '(' to activate the completer, but it didn't") ++ } ++ if c.X != 1 && c.Y != 2 { ++ t.Errorf("expected activating the completer to set the start position to {1, 2} but got {%v, %v}", c.X, c.Y) ++ } ++ if !reflect.DeepEqual([]byte("fmt.Println("), providerReceivedBytes) { ++ t.Errorf("expected the provider to receive '%v', but got '%v'", "fmt.Println(", string(providerReceivedBytes)) ++ } ++ if !reflect.DeepEqual(3, providerReceivedOffset) { ++ t.Errorf("expected the provider to receive '%v', but got '%v'", "fmt.Println(", string(providerReceivedBytes)) ++ } ++ if !reflect.DeepEqual(expectedOptions, c.Options) { ++ t.Errorf("expected options %v, but got %v", expectedOptions, c.Options) ++ } ++ if c.ActiveIndex != 0 { ++ t.Errorf("expected the active index to be reset to 0 after a refresh, but it was set to %v", c.ActiveIndex) ++ } ++} ++ ++func TestCompleterIsDeactivatedByNotReceivingAnyOptions(t *testing.T) { ++ activators := map[rune]int{ ++ '(': 0, ++ } ++ deactivators := []rune{')', ';'} ++ currentBytesAndOffset := func() (bytes []byte, offset int) { ++ return []byte("fmt.Print"), len("fmt.Print") ++ } ++ currentLocation := func() Loc { ++ return Loc{X: len("fmt.Print"), Y: 0} ++ } ++ locationOffset := func(Loc) int { ++ return 0 ++ } ++ provider := func(l func(s string, values ...interface{}), buffer []byte, startOffset, currentOffset int) (options []optionprovider.Option, delta int, err error) { ++ options = []optionprovider.Option{} // No options. ++ return ++ } ++ ++ c := NewCompleter(activators, ++ deactivators, ++ provider, ++ t.Logf, ++ currentBytesAndOffset, ++ currentLocation, ++ locationOffset, ++ noopReplacer, ++ noopContentSetter, ++ optionStyleInactive, ++ optionStyleActive, ++ enabledFlagSetToTrue) ++ ++ c.Active = true ++ ++ err := c.Process('l') ++ if err != nil { ++ t.Fatalf("failed to process with error: %v", err) ++ } ++ if c.Active { ++ t.Errorf("expected receiving no autocomplete options to deactivate the completer, but it didn't") ++ } ++} ++ ++func TestCompleterIsRestartedIfARuneIsAnActivatorAndDeactivator(t *testing.T) { ++ activators := map[rune]int{ ++ '.': 0, ++ } ++ deactivators := []rune{'.'} ++ currentBytesAndOffset := func() (bytes []byte, offset int) { ++ return []byte("test test"), 9 ++ } ++ currentLocation := func() Loc { ++ return Loc{X: 9, Y: 0} ++ } ++ locationOffset := func(Loc) int { ++ return 9 ++ } ++ provider := func(l func(s string, values ...interface{}), buffer []byte, startOffset, currentOffset int) (options []optionprovider.Option, delta int, err error) { ++ options = []optionprovider.Option{ ++ optionprovider.New("test", "test"), ++ } ++ return ++ } ++ ++ c := NewCompleter(activators, ++ deactivators, ++ provider, ++ t.Logf, ++ currentBytesAndOffset, ++ currentLocation, ++ locationOffset, ++ noopReplacer, ++ noopContentSetter, ++ optionStyleInactive, ++ optionStyleActive, ++ enabledFlagSetToTrue) ++ ++ c.Active = true ++ ++ err := c.Process('.') ++ if err != nil { ++ t.Fatalf("failed to process with error: %v", err) ++ } ++ if !c.Active { ++ t.Errorf("expected '.' to deactivate, then reactivate the completer, but it didn't") ++ } ++ if c.X != 9 { ++ t.Errorf("expected the start position to be reset to x:9, but was %v", c.X) ++ } ++} ++ ++func TestCompleterIsNotTriggeredByOtherRunesWhenInactive(t *testing.T) { ++ activators := map[rune]int{ ++ '(': 0, ++ } ++ deactivators := []rune{')', ';'} ++ currentBytesAndOffset := func() (bytes []byte, offset int) { ++ return []byte("fmt.Println("), 3 ++ } ++ currentLocation := func() Loc { ++ return Loc{X: 1, Y: 2} ++ } ++ locationOffset := func(Loc) int { ++ return 0 ++ } ++ provider := func(l func(s string, values ...interface{}), buffer []byte, startOffset, currentOffset int) (options []optionprovider.Option, delta int, err error) { ++ options = []optionprovider.Option{ ++ optionprovider.New("text", "hint"), ++ } ++ return ++ } ++ c := NewCompleter(activators, ++ deactivators, ++ provider, ++ t.Logf, ++ currentBytesAndOffset, ++ currentLocation, ++ locationOffset, ++ noopReplacer, ++ noopContentSetter, ++ optionStyleInactive, ++ optionStyleActive, ++ enabledFlagSetToTrue) ++ ++ err := c.Process('a') ++ if err != nil { ++ t.Fatalf("failed to process with error: %v", err) ++ } ++ if c.Active { ++ t.Errorf("expected 'a' to do nothing to activate the completer, but the completer was activated") ++ } ++} ++ ++func TestCompleterAdjustsStartPositionIfOptionProviderMovesIt(t *testing.T) { ++ activators := map[rune]int{ ++ '.': 0, ++ } ++ deactivators := []rune{'.'} ++ currentBytesAndOffset := func() (bytes []byte, offset int) { ++ return []byte("test test"), 9 ++ } ++ currentLocation := func() Loc { ++ return Loc{X: 9, Y: 0} ++ } ++ locationOffset := func(Loc) int { ++ return 9 ++ } ++ provider := func(l func(s string, values ...interface{}), buffer []byte, startOffset, currentOffset int) (options []optionprovider.Option, startPositionDelta int, err error) { ++ options = []optionprovider.Option{ ++ optionprovider.New("test", "test"), ++ } ++ startPositionDelta = -1 ++ return ++ } ++ ++ c := NewCompleter(activators, ++ deactivators, ++ provider, ++ t.Logf, ++ currentBytesAndOffset, ++ currentLocation, ++ locationOffset, ++ noopReplacer, ++ noopContentSetter, ++ optionStyleInactive, ++ optionStyleActive, ++ enabledFlagSetToTrue) ++ ++ c.Active = true ++ c.X = 7 ++ err := c.Process('e') ++ if err != nil { ++ t.Fatalf("failed to process with error: %v", err) ++ } ++ if c.X != 6 { ++ t.Errorf("expected the start position to shift by the -1 delta returned by the options, but was %v", c.X) ++ } ++} ++ ++func TestCompleterHandleEventNotEnabled(t *testing.T) { ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, nil, nil, nil, nil, optionStyleInactive, optionStyleActive, enabledFlagSetToFalse) ++ ++ handled := c.HandleEvent(tcell.KeyRune) ++ if handled { ++ t.Error("when the completer is not enabled, handling events should not take place") ++ } ++} ++ ++func TestCompleterHandleEventInactive(t *testing.T) { ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, nil, nil, nil, nil, optionStyleInactive, optionStyleActive, enabledFlagSetToTrue) ++ ++ handled := c.HandleEvent(tcell.KeyRune) ++ if handled { ++ t.Error("when the completer is inactive, handling events should not take place") ++ } ++} ++ ++func TestCompleterHandleEventKeyUp(t *testing.T) { ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, nil, nil, nil, nil, optionStyleInactive, optionStyleActive, enabledFlagSetToTrue) ++ ++ c.Active = true ++ c.ActiveIndex = 10 ++ ++ handled := c.HandleEvent(tcell.KeyUp) ++ if !handled { ++ t.Error("when the completer is active, KeyUp should be handled") ++ } ++ if c.ActiveIndex != 9 { ++ t.Errorf("KeyUp should decrease the active index from 10 to 9, but the result was %v", c.ActiveIndex) ++ } ++ ++ // Check that it's not possible to go before option index zero. ++ c.ActiveIndex = 0 ++ c.HandleEvent(tcell.KeyUp) ++ if c.ActiveIndex != 0 { ++ t.Errorf("Once the top of the selections are reached, it shouldn't be possible to go any further, but the result was %v", c.ActiveIndex) ++ } ++} ++ ++func TestCompleterHandleEventKeyDown(t *testing.T) { ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, nil, nil, nil, nil, optionStyleInactive, optionStyleActive, enabledFlagSetToTrue) ++ ++ c.Active = true ++ c.Options = []optionprovider.Option{ ++ {H: "hint0", T: "text0"}, ++ {H: "hint1", T: "text1"}, ++ } ++ c.ActiveIndex = 0 ++ ++ handled := c.HandleEvent(tcell.KeyDown) ++ if !handled { ++ t.Error("when the completer is active, KeyDown should be handled") ++ } ++ if c.ActiveIndex != 1 { ++ t.Errorf("KeyDown should increase the active index from 0 to 1, but the result was %v", c.ActiveIndex) ++ } ++ ++ // Check that it's not possible to exceed the number of options. ++ c.HandleEvent(tcell.KeyDown) ++ if c.ActiveIndex != 1 { ++ t.Errorf("Once the bottom of the selections are reached, it shouldn't be possible to go any further, but the result was %v", c.ActiveIndex) ++ } ++} ++ ++func TestCompleterHandleEventKeyEscape(t *testing.T) { ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, nil, nil, nil, nil, optionStyleInactive, optionStyleActive, enabledFlagSetToTrue) ++ ++ c.Active = true ++ ++ handled := c.HandleEvent(tcell.KeyEscape) ++ if !handled { ++ t.Error("when the completer is active, KeyEscape should be handled") ++ } ++ if c.Active { ++ t.Error("KeyEscape should stop the completer from being active") ++ } ++} ++ ++func TestCompleterHandleEventKeyTab(t *testing.T) { ++ testCompleterHandleEventCompletion(tcell.KeyTab, t) ++} ++ ++func TestCompleterHandleEventKeyEnter(t *testing.T) { ++ testCompleterHandleEventCompletion(tcell.KeyEnter, t) ++} ++ ++func testCompleterHandleEventCompletion(key tcell.Key, t *testing.T) { ++ expectedFrom := Loc{X: 0, Y: 1} ++ expectedTo := Loc{X: 1, Y: 2} ++ ++ currentLocation := func() Loc { return expectedTo } ++ locationOffset := func(Loc) int { ++ return 0 ++ } ++ ++ var receivedFrom, receivedTo Loc ++ var receivedWith string ++ ++ replacer := func(from, to Loc, with string) { ++ receivedFrom = from ++ receivedTo = to ++ receivedWith = with ++ } ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, currentLocation, locationOffset, replacer, nil, optionStyleInactive, optionStyleActive, enabledFlagSetToTrue) ++ ++ c.X = expectedFrom.X ++ c.Y = expectedFrom.Y ++ c.Active = true ++ c.Options = []optionprovider.Option{ ++ {H: "hint0", T: "text0"}, ++ {H: "hint1", T: "text1"}, ++ } ++ c.ActiveIndex = 1 ++ ++ handled := c.HandleEvent(key) ++ if !handled { ++ t.Error("when the completer is active, the completion should be handled") ++ } ++ if expectedFrom != receivedFrom { ++ t.Errorf("expected from location %v but got %v", expectedFrom, receivedFrom) ++ } ++ if expectedTo != receivedTo { ++ t.Errorf("expected to location %v but got %v", expectedTo, receivedTo) ++ } ++ if "text1" != receivedWith { ++ t.Errorf("expected to receive a replacement of 'text1', but got %v", receivedWith) ++ } ++} ++ ++func TestCompleterHandleEventKeyWhenActive(t *testing.T) { ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, nil, nil, nil, nil, optionStyleInactive, optionStyleActive, enabledFlagSetToTrue) ++ ++ c.Active = true ++ ++ handled := c.HandleEvent(tcell.KeyRune) ++ if handled { ++ t.Error("when the completer is active, KeyRune should have no effect") ++ } ++} ++ ++func TestCompleterGetOption(t *testing.T) { ++ defaultOptions := []optionprovider.Option{ ++ optionprovider.New("text", "hint"), ++ optionprovider.New("text1", "hint1"), ++ } ++ ++ tests := []struct { ++ index int ++ options []optionprovider.Option ++ expectedText string ++ expectedOK bool ++ }{ ++ { ++ index: -1, ++ options: defaultOptions, ++ expectedText: "text", // Use the first entry by default. ++ expectedOK: true, ++ }, ++ { ++ index: -1, ++ options: []optionprovider.Option{}, ++ expectedText: "", // If there are no options, don't use anything. ++ expectedOK: false, ++ }, ++ { ++ index: 0, ++ options: defaultOptions, ++ expectedText: "text", ++ expectedOK: true, ++ }, ++ { ++ index: 1, ++ options: defaultOptions, ++ expectedText: "text1", ++ expectedOK: true, ++ }, ++ { ++ index: 2, ++ options: defaultOptions, ++ expectedText: "", ++ expectedOK: false, ++ }, ++ } ++ ++ for _, test := range tests { ++ actualText, actualOK := getOption(test.index, test.options) ++ if test.expectedText != actualText { ++ t.Errorf("for index %v, expected '%v', but got '%v'", test.index, test.expectedText, actualText) ++ } ++ if test.expectedOK != actualOK { ++ t.Errorf("for index %v, expected '%v', but got '%v'", test.index, test.expectedOK, actualOK) ++ } ++ } ++} ++ ++func TestCompleterDisplayDoesNotWriteToConsoleWhenNotEnabled(t *testing.T) { ++ var setterCalled bool ++ setter := func(x int, y int, mainc rune, combc []rune, style tcell.Style) { ++ setterCalled = true ++ } ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, nil, nil, nil, setter, optionStyleInactive, optionStyleActive, enabledFlagSetToFalse) ++ ++ c.Display() ++ if setterCalled { ++ t.Error("when the completer is not enabled, expected no content to be written to the screen") ++ } ++} ++ ++func TestCompleterDisplayDoesNotWriteToConsoleWhenInactive(t *testing.T) { ++ var setterCalled bool ++ setter := func(x int, y int, mainc rune, combc []rune, style tcell.Style) { ++ setterCalled = true ++ } ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, nil, nil, nil, setter, optionStyleInactive, optionStyleActive, enabledFlagSetToTrue) ++ ++ c.Display() ++ if setterCalled { ++ t.Error("when the completer is inactive, expected no content to be written to the screen") ++ } ++} ++ ++type rs struct { ++ Rune rune ++ Style tcell.Style ++} ++ ++type displayMap map[Loc]rs ++ ++func (a displayMap) Eq(b displayMap) bool { ++ if len(a) != len(b) { ++ return false ++ } ++ for locA, rsA := range a { ++ rsB, ok := b[locA] ++ if !ok { ++ return false ++ } ++ if rsA.Rune != rsB.Rune { ++ return false ++ } ++ if rsA.Style != rsB.Style { ++ return false ++ } ++ } ++ return true ++} ++ ++func (a displayMap) String() string { ++ buf := bytes.NewBuffer([]byte{}) ++ ++ if len(a) == 0 { ++ return "" ++ } ++ ++ minX, maxX := a.X() ++ minY, maxY := a.Y() ++ ++ for y := minY; y <= maxY; y++ { ++ for x := minX; x <= maxX; x++ { ++ l := Loc{X: x, Y: y} ++ // Get the value of the rune. ++ v, ok := a[l] ++ if ok { ++ buf.WriteRune(v.Rune) ++ } else { ++ buf.WriteRune(' ') ++ } ++ } ++ if y < maxY { ++ buf.WriteRune('\n') ++ } ++ } ++ return buf.String() ++} ++ ++func (a displayMap) X() (min, max int) { ++ first := true ++ for k := range a { ++ if first { ++ min = k.X ++ max = k.X ++ first = false ++ } ++ if k.X < min { ++ min = k.X ++ } ++ if k.X > max { ++ max = k.X ++ } ++ } ++ return ++} ++ ++func (a displayMap) Y() (min, max int) { ++ first := true ++ for k := range a { ++ if first { ++ min = k.Y ++ max = k.Y ++ first = false ++ } ++ if k.Y < min { ++ min = k.Y ++ } ++ if k.Y > max { ++ max = k.Y ++ } ++ } ++ return ++} ++ ++func TestDisplayMapString(t *testing.T) { ++ m := make(displayMap) ++ ++ if m.String() != "" { ++ t.Errorf("an empty display should be an empty string, but got '%v'", m.String()) ++ } ++ ++ m[Loc{Y: 0, X: 0}] = rs{Rune: 'a'} ++ if m.String() != "a" { ++ t.Errorf("expected 'a', got '%v'", m.String()) ++ } ++ m[Loc{Y: 0, X: 2}] = rs{Rune: 'c'} ++ if m.String() != "a c" { ++ t.Errorf("expected 'a c', got '%v'", m.String()) ++ } ++} ++ ++func TestCompleterDisplayRendersOptionsWhenActive(t *testing.T) { ++ acs := optionStyleInactive ++ act := optionStyleActive ++ ++ tests := []struct { ++ name string ++ options []optionprovider.Option ++ selectedOptionIndex int ++ expected displayMap ++ }{ ++ { ++ name: "no options", ++ options: []optionprovider.Option{}, ++ selectedOptionIndex: -1, ++ expected: displayMap{}, ++ }, ++ { ++ name: "single option", ++ options: []optionprovider.Option{ ++ optionprovider.New("Text", "Hint"), ++ }, ++ selectedOptionIndex: -1, ++ expected: displayMap{ ++ Loc{Y: 1, X: 0}: rs{'T', acs}, Loc{Y: 1, X: 1}: rs{'e', acs}, Loc{Y: 1, X: 2}: rs{'x', acs}, Loc{Y: 1, X: 3}: rs{'t', acs}, Loc{Y: 1, X: 4}: rs{rune(0), acs}, ++ }, ++ }, ++ { ++ name: "multiple options", ++ options: []optionprovider.Option{ ++ optionprovider.New("Text", "Hint"), ++ optionprovider.New("Text2", "Hint2"), ++ }, ++ selectedOptionIndex: -1, ++ expected: displayMap{ ++ Loc{Y: 1, X: 0}: rs{'T', acs}, Loc{Y: 1, X: 1}: rs{'e', acs}, Loc{Y: 1, X: 2}: rs{'x', acs}, Loc{Y: 1, X: 3}: rs{'t', acs}, Loc{Y: 1, X: 4}: rs{0, acs}, Loc{Y: 1, X: 5}: rs{0, acs}, ++ Loc{Y: 2, X: 0}: rs{'T', acs}, Loc{Y: 2, X: 1}: rs{'e', acs}, Loc{Y: 2, X: 2}: rs{'x', acs}, Loc{Y: 2, X: 3}: rs{'t', acs}, Loc{Y: 2, X: 4}: rs{'2', acs}, Loc{Y: 2, X: 5}: rs{0, acs}, ++ }, ++ }, ++ { ++ name: "multiple options, last selected", ++ options: []optionprovider.Option{ ++ optionprovider.New("Text", "Hint"), ++ optionprovider.New("Text2", "Hint2"), ++ }, ++ selectedOptionIndex: 1, ++ expected: displayMap{ ++ Loc{Y: 1, X: 0}: rs{'T', acs}, Loc{Y: 1, X: 1}: rs{'e', acs}, Loc{Y: 1, X: 2}: rs{'x', acs}, Loc{Y: 1, X: 3}: rs{'t', acs}, Loc{Y: 1, X: 4}: rs{0, acs}, Loc{Y: 1, X: 5}: rs{0, acs}, ++ Loc{Y: 2, X: 0}: rs{'T', act}, Loc{Y: 2, X: 1}: rs{'e', act}, Loc{Y: 2, X: 2}: rs{'x', act}, Loc{Y: 2, X: 3}: rs{'t', act}, Loc{Y: 2, X: 4}: rs{'2', act}, Loc{Y: 2, X: 5}: rs{0, act}, ++ }, ++ }, ++ } ++ ++ for _, test := range tests { ++ testCompleterDisplayRendersOptionsWhenActive(test.name, test.options, test.selectedOptionIndex, test.expected, t) ++ } ++} ++ ++func testCompleterDisplayRendersOptionsWhenActive(name string, ++ options []optionprovider.Option, ++ activeIndex int, ++ expected displayMap, ++ t *testing.T) { ++ actual := make(displayMap) ++ ++ currentLocation := func() Loc { ++ return Loc{X: 0, Y: 0} ++ } ++ locationOffset := func(Loc) int { ++ return 0 ++ } ++ ++ var setterCalled bool ++ setter := func(x int, y int, mainc rune, combc []rune, style tcell.Style) { ++ setterCalled = true ++ actual[Loc{X: x, Y: y}] = rs{Rune: mainc, Style: style} ++ } ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, currentLocation, locationOffset, nil, setter, optionStyleInactive, optionStyleActive, enabledFlagSetToTrue) ++ c.Active = true ++ c.ActiveIndex = activeIndex ++ c.Options = options ++ ++ c.Display() ++ if !expected.Eq(actual) { ++ t.Errorf("%s: expected characters '%v', got '%v'", name, expected.String(), actual.String()) ++ t.Errorf("%s: expected '%v', got '%v'", name, map[Loc]rs(expected), map[Loc]rs(actual)) ++ } ++} ++ ++func TestDeactivateIfOutOfBounds(t *testing.T) { ++ tests := []struct { ++ name string ++ previousLocation Loc ++ currentLocation Loc ++ currentlyActive bool ++ expectedActive bool ++ }{ ++ { ++ name: "No change keeps active", ++ previousLocation: Loc{X: 30, Y: 0}, ++ currentLocation: Loc{X: 30, Y: 0}, ++ currentlyActive: true, ++ expectedActive: true, ++ }, ++ { ++ name: "Moving forward retains active", ++ previousLocation: Loc{X: 30, Y: 0}, ++ currentLocation: Loc{X: 31, Y: 0}, ++ currentlyActive: true, ++ expectedActive: true, ++ }, ++ { ++ name: "Moving back to the start cancels", ++ previousLocation: Loc{X: 31, Y: 0}, ++ currentLocation: Loc{X: 30, Y: 0}, ++ currentlyActive: true, ++ expectedActive: false, ++ }, ++ { ++ name: "Moving before the start cancels", ++ previousLocation: Loc{X: 31, Y: 0}, ++ currentLocation: Loc{X: 25, Y: 0}, ++ currentlyActive: true, ++ expectedActive: false, ++ }, ++ { ++ name: "Moving to the line start cancels", ++ previousLocation: Loc{X: 31, Y: 0}, ++ currentLocation: Loc{X: 0, Y: 0}, ++ currentlyActive: true, ++ expectedActive: false, ++ }, ++ { ++ name: "Moving more than one character to the right cancels (could be a paste, or mouse click)", ++ previousLocation: Loc{X: 31, Y: 0}, ++ currentLocation: Loc{X: 35, Y: 0}, ++ currentlyActive: true, ++ expectedActive: false, ++ }, ++ { ++ name: "Switching lines cancels", ++ previousLocation: Loc{X: 30, Y: 0}, ++ currentLocation: Loc{X: 31, Y: 1}, ++ currentlyActive: true, ++ expectedActive: false, ++ }, ++ } ++ ++ for _, test := range tests { ++ currentLocation := func() Loc { ++ return test.currentLocation ++ } ++ c := NewCompleter(nil, nil, nil, t.Logf, nil, currentLocation, nil, nil, nil, optionStyleInactive, optionStyleActive, enabledFlagSetToTrue) ++ c.X = test.previousLocation.X ++ c.Y = test.previousLocation.Y ++ c.PreviousLocation = test.previousLocation ++ c.Active = test.currentlyActive ++ ++ c.DeactivateIfOutOfBounds() ++ ++ if c.Active != test.expectedActive { ++ t.Errorf("%s: expected active to now be %v, but was %v", test.name, test.expectedActive, c.Active) ++ } ++ } ++} +--- cmd/micro/lineArray.go.orig 2018-07-20 00:24:02 UTC ++++ cmd/micro/lineArray.go +@@ -2,6 +2,7 @@ package main + + import ( + "bufio" ++ "bytes" + "io" + "unicode/utf8" + +@@ -115,33 +116,31 @@ func NewLineArray(size int64, reader io.Reader) *LineA + return la + } + +-// Returns the String representation of the LineArray +-func (la *LineArray) String() string { +- str := "" ++// Buffer gets the lineArray as a bytes.Buffer. ++func (la *LineArray) Buffer(useCrlf bool) *bytes.Buffer { ++ buf := bytes.NewBuffer([]byte{}) + for i, l := range la.lines { +- str += string(l.data) ++ buf.Write(l.data) + if i != len(la.lines)-1 { +- str += "\n" ++ if useCrlf { ++ buf.WriteRune('\r') ++ } ++ buf.WriteRune('\n') + } + } +- return str ++ return buf + } + + // SaveString returns the string that should be written to disk when + // the line array is saved + // It is the same as string but uses crlf or lf line endings depending + func (la *LineArray) SaveString(useCrlf bool) string { +- str := "" +- for i, l := range la.lines { +- str += string(l.data) +- if i != len(la.lines)-1 { +- if useCrlf { +- str += "\r" +- } +- str += "\n" +- } +- } +- return str ++ return la.Buffer(useCrlf).String() ++} ++ ++// Returns the String representation of the LineArray ++func (la *LineArray) String() string { ++ return la.SaveString(false) + } + + // NewlineBelow adds a newline below the given line number +--- cmd/micro/linearray_test.go.orig 2019-06-30 06:46:32 UTC ++++ cmd/micro/linearray_test.go +@@ -0,0 +1,116 @@ ++package main ++ ++import ( ++ "strings" ++ "testing" ++) ++ ++func TestLineArraySubstr(t *testing.T) { ++ tests := []struct { ++ name string ++ input string ++ start, end Loc ++ expected string ++ }{ ++ { ++ name: "first line", ++ input: "abc\ndef", ++ start: Loc{Y: 0, X: 0}, ++ end: Loc{Y: 0, X: 3}, ++ expected: "abc", ++ }, ++ { ++ name: "second line, first char", ++ input: "abc\ndef", ++ start: Loc{Y: 1, X: 0}, ++ end: Loc{Y: 1, X: 1}, ++ expected: "d", ++ }, ++ { ++ name: "middle lines lines", ++ input: "abc\ndef\nghi", ++ start: Loc{Y: 0, X: 0}, ++ end: Loc{Y: 1, X: 2}, ++ expected: "abc\nde", ++ }, ++ { ++ name: "all lines", ++ input: "abc\ndef", ++ start: Loc{Y: 0, X: 0}, ++ end: Loc{Y: 1, X: 3}, ++ expected: "abc\ndef", ++ }, ++ } ++ ++ for _, test := range tests { ++ r := strings.NewReader(test.input) ++ la := NewLineArray(10, r) ++ actual := la.Substr(test.start, test.end) ++ if test.expected != actual { ++ t.Errorf("%s: expected '%v', got '%v'", test.name, test.expected, actual) ++ } ++ } ++} ++ ++func TestLineArrayDeleteLine(t *testing.T) { ++ tests := []struct { ++ name string ++ input string ++ line int ++ expected string ++ }{ ++ { ++ name: "remove first line", ++ input: "abc", ++ line: 0, ++ expected: "", ++ }, ++ { ++ name: "remove second line", ++ input: "abc\ndef", ++ line: 1, ++ expected: "abc", ++ }, ++ { ++ name: "remove second line of 3", ++ input: "abc\ndef\nhij", ++ line: 1, ++ expected: "abc\nhij", ++ }, ++ } ++ ++ for _, test := range tests { ++ r := strings.NewReader(test.input) ++ la := NewLineArray(10, r) ++ la.DeleteLine(test.line) ++ if la.String() != test.expected { ++ t.Errorf("%s: expected '%v', got '%v'", test.name, test.expected, la.String()) ++ } ++ } ++} ++ ++func TestLineArrayRemove(t *testing.T) { ++ tests := []struct { ++ name string ++ input string ++ start, end Loc ++ expected string ++ }{ ++ { ++ name: "remove first character", ++ input: "abc", ++ start: Loc{Y: 0, X: 0}, ++ end: Loc{Y: 0, X: 1}, ++ expected: "bc", ++ }, ++ } ++ ++ for _, test := range tests { ++ r := strings.NewReader(test.input) ++ la := NewLineArray(10, r) ++ la.remove(test.start, test.end) ++ if la.String() != test.expected { ++ t.Errorf("%s: expected '%v', got '%v'", test.name, test.expected, la.String()) ++ } ++ } ++} +--- cmd/micro/loc_test.go.orig 2019-06-30 06:46:32 UTC ++++ cmd/micro/loc_test.go +@@ -0,0 +1,33 @@ ++package main ++ ++import "testing" ++ ++func TestFromCharPos(t *testing.T) { ++ tests := []struct { ++ name string ++ input string ++ charpos int ++ expected Loc ++ }{ ++ { ++ name: "first line", ++ input: "abc\ndef", ++ charpos: 0, ++ expected: Loc{Y: 0, X: 0}, ++ }, ++ { ++ name: "secnod line", ++ input: "abc\ndef", ++ charpos: 5, ++ expected: Loc{Y: 1, X: 1}, ++ }, ++ } ++ ++ for _, test := range tests { ++ b := NewBufferFromString(test.input, "/dev/null") ++ actual := FromCharPos(test.charpos, b) ++ if test.expected != actual { ++ t.Errorf("%s: expected '%v', got '%v'", test.name, test.expected, actual) ++ } ++ } ++} +--- cmd/micro/micro.go.orig 2018-07-20 00:24:02 UTC ++++ cmd/micro/micro.go +@@ -12,13 +12,13 @@ import ( + + "github.com/go-errors/errors" + "github.com/mattn/go-isatty" +- "github.com/mitchellh/go-homedir" +- "github.com/yuin/gopher-lua" ++ homedir "github.com/mitchellh/go-homedir" ++ lua "github.com/yuin/gopher-lua" + "github.com/zyedidia/clipboard" + "github.com/zyedidia/micro/cmd/micro/terminfo" + "github.com/zyedidia/tcell" + "github.com/zyedidia/tcell/encoding" +- "layeh.com/gopher-luar" ++ luar "layeh.com/gopher-luar" + ) + + const ( +--- cmd/micro/optionprovider/generic.go.orig 2019-06-30 06:46:32 UTC ++++ cmd/micro/optionprovider/generic.go +@@ -0,0 +1,140 @@ ++package optionprovider ++ ++import ( ++ "regexp" ++ "sort" ++ "strings" ++ "unicode" ++) ++ ++var word = regexp.MustCompile(`[a-zA-Z]+\d*`) ++var stopList = map[string]interface{}{ ++ "for": false, ++ "if": false, ++ "then": false, ++ "let": false, ++ "const": false, ++ "when": false, ++ "while": false, ++ "the": false, ++ "to": false, ++ "and": false, ++} ++var maxSuggestions = 10 ++ ++// Generic is an OptionProvider which provides options to the autocompletion system based on the ++// words in the current buffer. It returns a delta of the start index if the start position needs ++// to change. ++func Generic(logger func(s string, values ...interface{}), buffer []byte, startOffset, currentOffset int) (options []Option, startDelta int, err error) { ++ s := string(buffer) ++ ++ // Find the best matches. ++ prefix := prefix(lastCharacters(s, currentOffset, 10)) ++ startDelta = currentOffset - startOffset - len(prefix) ++ ++ // Calculate common words. ++ words := word.FindAllString(s, -1) ++ ++ // Count words, but remove common syntax and the prefix from the autocomplete list. ++ counts := wordCounts(words, map[string]interface{}{prefix: false}) ++ orderedWords := orderByFrequencyDesc(counts) ++ ++ if len(prefix) > 0 { ++ // We have a prefix, so write out prefix matches. ++ for i := 0; i < len(orderedWords) && len(options) < maxSuggestions; i++ { ++ if strings.HasPrefix(orderedWords[i], prefix) { ++ options = append(options, New(orderedWords[i], "")) ++ } ++ } ++ return ++ } ++ ++ // Write out all matches. ++ for i := 0; i < len(orderedWords) && len(options) < maxSuggestions; i++ { ++ options = append(options, New(orderedWords[i], "")) ++ } ++ ++ return ++} ++ ++func lastCharacters(s string, end, charactersBefore int) string { ++ if end == 0 { ++ return "" ++ } ++ start := end - charactersBefore ++ if start < 0 { ++ return s[0:end] ++ } ++ return s[start:end] ++} ++ ++func prefix(s string) string { ++ text := []*unicode.RangeTable{unicode.Letter, unicode.Digit} ++ last := 0 ++ for i, r := range s { ++ if !unicode.IsOneOf(text, r) { ++ last = i + 1 ++ } ++ } ++ return s[last:] ++} ++ ++func wordCounts(words []string, additionalStopList map[string]interface{}) (counts map[string]int) { ++ counts = make(map[string]int) ++ for _, w := range words { ++ if !inAnyStopList(w, stopList, additionalStopList) { ++ counts[w]++ ++ } ++ } ++ return ++} ++ ++func inAnyStopList(w string, lists ...map[string]interface{}) bool { ++ for _, l := range lists { ++ if _, inStopList := l[w]; inStopList { ++ return true ++ } ++ } ++ return false ++} ++ ++func orderByFrequencyDesc(counts map[string]int) []string { ++ cs := NewCountSorter(counts) ++ sort.Sort(cs) ++ return cs.Keys ++} ++ ++// CountSorter provides a way of sorting map[string]int values (most frequent first). ++type CountSorter struct { ++ Data map[string]int ++ Keys []string ++} ++ ++// NewCountSorter supports the sort.Data interface. The Keys field is populated, and the ++// struct can then be sorted. The Keys field then contains the keys, ordered by the count. ++func NewCountSorter(data map[string]int) *CountSorter { ++ cs := new(CountSorter) ++ cs.Data = make(map[string]int) ++ cs.Keys = make([]string, len(data)) ++ var i int ++ for k, v := range data { ++ cs.Keys[i] = k ++ cs.Data[k] = v ++ i++ ++ } ++ return cs ++} ++ ++func (cs *CountSorter) Len() int { return len(cs.Data) } ++func (cs *CountSorter) Less(i, j int) bool { ++ countI := cs.Data[cs.Keys[i]] ++ countJ := cs.Data[cs.Keys[j]] ++ if countI == countJ { ++ // Alphabetic sorting. ++ // Go's sorting rules mean that capital letters come before lowercase. ++ return cs.Keys[i] < cs.Keys[j] ++ } ++ // Sort by largest count first. ++ return countI > countJ ++} ++func (cs *CountSorter) Swap(i, j int) { cs.Keys[i], cs.Keys[j] = cs.Keys[j], cs.Keys[i] } +--- cmd/micro/optionprovider/generic_test.go.orig 2019-06-30 06:46:32 UTC ++++ cmd/micro/optionprovider/generic_test.go +@@ -0,0 +1,185 @@ ++package optionprovider ++ ++import ( ++ "reflect" ++ "testing" ++) ++ ++func TestGeneric(t *testing.T) { ++ tests := []struct { ++ name string ++ text string ++ from string ++ to string ++ expected []Option ++ expectedDelta int ++ }{ ++ { ++ name: "words are sorted alphabetically if they appear the same number of times", ++ text: "fish dog cat ", ++ expected: []Option{New("cat", ""), New("dog", ""), New("fish", "")}, ++ }, ++ { ++ name: "capitals preceed lowercase", ++ text: "A a B b C c", ++ expected: []Option{ ++ New("A", ""), New("B", ""), New("C", ""), ++ New("a", ""), New("b", ""), New("c", ""), ++ }, ++ }, ++ { ++ name: "bare numbers are not included", ++ text: "1 2 3 1.23 a", ++ expected: []Option{New("a", "")}, ++ }, ++ { ++ name: "words that include a number are included", ++ text: "a1", ++ expected: []Option{New("a1", "")}, ++ }, ++ { ++ name: "words are ordered by their frequency descending (most common words are first in the list)", ++ text: "ccc ccc ccc bb bb a", ++ expected: []Option{New("ccc", ""), New("bb", ""), New("a", "")}, ++ }, ++ { ++ name: "prefix matches preceed other matches", ++ text: "common common common something", ++ from: "common common common ", ++ to: "common common common s", ++ expected: []Option{New("something", "")}, ++ }, ++ { ++ name: "the autocomplete looks for the previous word boundary to see if you're partway through a word to limit results", ++ text: "A AB ABC ABCD", ++ from: "A AB ", ++ to: "A AB AB", ++ expected: []Option{New("ABC", ""), New("ABCD", "")}, ++ }, ++ { ++ name: "realistic example", ++ text: `fmt.Println("hello") fmt.P`, ++ from: `fmt.Println("hello") fmt.`, ++ to: `fmt.Println("hello") fmt.P`, ++ expected: []Option{New("Println", "")}, ++ }, ++ { ++ name: "go back further than the start position", ++ text: `testing`, ++ from: `test`, ++ to: `testi`, ++ expected: []Option{New("testing", "")}, ++ expectedDelta: -4, ++ }, ++ } ++ ++ for _, test := range tests { ++ options, delta, err := Generic(t.Logf, []byte(test.text), len(test.from), len(test.to)) ++ if err != nil { ++ t.Fatalf("%s: generic complete failed with error %v", test.name, err) ++ continue ++ } ++ if !reflect.DeepEqual(options, test.expected) { ++ t.Errorf("%s: expected '%v', got '%v'", test.name, test.expected, options) ++ } ++ if delta != test.expectedDelta { ++ t.Errorf("%s: expected delta %v, got %v", test.name, test.expectedDelta, delta) ++ } ++ } ++} ++ ++func TestLastCharacters(t *testing.T) { ++ tests := []struct { ++ input string ++ end int ++ length int ++ expected string ++ }{ ++ { ++ input: "ABC", ++ end: 3, ++ length: 1, ++ expected: "C", ++ }, ++ { ++ input: "ABC", ++ end: 3, ++ length: 2, ++ expected: "BC", ++ }, ++ { ++ input: "ABC", ++ end: 3, ++ length: 3, ++ expected: "ABC", ++ }, ++ { ++ input: "ABC", ++ end: 3, ++ length: 4, ++ expected: "ABC", ++ }, ++ { ++ input: "ABCD", ++ end: 3, ++ length: 3, ++ expected: "ABC", ++ }, ++ { ++ input: "ABCD", ++ end: 2, ++ length: 6, ++ expected: "AB", ++ }, ++ { ++ input: "ABCD", ++ end: 0, ++ length: 6, ++ expected: "", ++ }, ++ } ++ ++ for _, test := range tests { ++ actual := lastCharacters(test.input, test.end, test.length) ++ if actual != test.expected { ++ t.Errorf("for input '%v', the %v characters before index %v, expected: '%v', but got '%v'", ++ test.input, test.length, test.end, test.expected, actual) ++ } ++ } ++} ++ ++func TestPrefix(t *testing.T) { ++ tests := []struct { ++ input string ++ expected string ++ }{ ++ { ++ input: "fmt.Prin", ++ expected: "Prin", ++ }, ++ { ++ input: "Test t", ++ expected: "t", ++ }, ++ { ++ input: "1234 123", ++ expected: "123", ++ }, ++ { ++ input: "word-connection", ++ expected: "connection", ++ }, ++ { ++ input: `"quote`, ++ expected: "quote", ++ }, ++ } ++ ++ for _, test := range tests { ++ actual := prefix(test.input) ++ if actual != test.expected { ++ t.Errorf("for input '%v', expected: '%v', but got '%v'", ++ test.input, test.expected, actual) ++ } ++ } ++} +--- cmd/micro/optionprovider/gocode.go.orig 2019-06-30 06:46:32 UTC ++++ cmd/micro/optionprovider/gocode.go +@@ -0,0 +1,98 @@ ++package optionprovider ++ ++import ( ++ "encoding/json" ++ "fmt" ++ "os/exec" ++ "strconv" ++) ++ ++// GoCode is an OptionProvider which provides options to the autocompletion system. ++func GoCode(logger func(s string, values ...interface{}), buffer []byte, startOffset, currentOffset int) (options []Option, startOffsetDelta int, err error) { ++ logger("autocompleter.GoCode: received offset %v - '...%v'->'%v'", ++ currentOffset, ++ string(previousX(buffer, startOffset, 10)), ++ string(buffer[startOffset:currentOffset])) ++ cmd := exec.Command("gocode", "-f=json", "autocomplete", strconv.Itoa(currentOffset)) ++ stdin, err := cmd.StdinPipe() ++ if err != nil { ++ return ++ } ++ _, err = stdin.Write(buffer) ++ if err != nil { ++ return ++ } ++ err = stdin.Close() ++ if err != nil { ++ return ++ } ++ stdoutStderr, err := cmd.CombinedOutput() ++ if err != nil { ++ if execErr, isExecError := err.(*exec.Error); isExecError { ++ if execErr.Err.Error() == exec.ErrNotFound.Error() { ++ logger("autocompleter.GoCode: failed to run because GoCode is not on the path, defaulting to Generic") ++ return Generic(logger, buffer, startOffset, currentOffset) ++ } ++ } ++ return ++ } ++ ++ // Unmarshal the JSON, it's an awkward format (mixed array) ++ // [1, [ { "class": "", "name": "", "type": "" } ]] ++ results := []interface{}{} ++ err = json.Unmarshal(stdoutStderr, &results) ++ if err != nil { ++ err = fmt.Errorf("gocode: failed to unmarshal output '%v': %v", string(stdoutStderr), err) ++ } ++ ++ if len(results) > 0 { ++ // The number represents how far back the text should go. ++ if startOffsetDeltaFloat, isFloat := results[0].(float64); isFloat { ++ startOffsetDelta = currentOffset - startOffset - int(startOffsetDeltaFloat) ++ } ++ ++ if firstElement, isArray := results[1].([]interface{}); isArray { ++ results = firstElement ++ } ++ } ++ ++ // Convert the array of maps into something useful. ++ for _, r := range results { ++ m, mok := r.(map[string]interface{}) ++ if mok { ++ options = append(options, mapToOption(m)) ++ } ++ // Return 10 results at most. ++ if len(options) > 10 { ++ break ++ } ++ } ++ ++ return ++} ++ ++func previousX(b []byte, index, x int) []byte { ++ from := 0 ++ if index-x > 0 { ++ from = index - x ++ } ++ return b[from:index] ++} ++ ++func mapToOption(m map[string]interface{}) Option { ++ // Available values are "class", "name", "type" and "package" ++ o := Option{} ++ if nv, ok := m["name"]; ok { ++ if n, nok := nv.(string); nok { ++ // text ++ o.T = n ++ } ++ } ++ if tv, ok := m["type"]; ok { ++ if t, tok := tv.(string); tok { ++ // hint ++ o.H = t ++ } ++ } ++ return o ++} +--- cmd/micro/optionprovider/noop.go.orig 2019-06-30 06:46:32 UTC ++++ cmd/micro/optionprovider/noop.go +@@ -0,0 +1,8 @@ ++package optionprovider ++ ++var noopOptions = []Option{} ++ ++// Noop is an option provider that does nothing. ++func Noop(l func(s string, values ...interface{}), buffer []byte, startOffset, currentOffset int) (options []Option, startOffsetDelta int, err error) { ++ return noopOptions, 0, nil ++} +--- cmd/micro/optionprovider/option.go.orig 2019-06-30 06:46:32 UTC ++++ cmd/micro/optionprovider/option.go +@@ -0,0 +1,21 @@ ++package optionprovider ++ ++// Option describes a multiple choice selection. ++type Option struct { ++ T, H string ++} ++ ++// New creates a new option value. ++func New(text, hint string) Option { ++ return Option{T: text, H: hint} ++} ++ ++// Text is the string that will be inserted. ++func (o Option) Text() string { ++ return o.T ++} ++ ++// Hint describes the text, e.g. if it's a method. ++func (o Option) Hint() string { ++ return o.H ++} +--- cmd/micro/runtime.go.orig 2018-07-20 00:24:02 UTC ++++ cmd/micro/runtime.go +@@ -1012,7 +1012,7 @@ func runtimeHelpOptionsMd() (*asset, error) { + return a, nil + } + +-var _runtimeHelpPluginsMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x3a\x6b\x8f\xdc\x36\x92\xdf\xf5\x2b\xea\x3a\x38\xb8\x3b\x68\x6b\xb2\xb8\x6f\x03\x78\x01\x3b\x7e\xc4\x7b\x8e\x6d\x78\x9c\x04\x8b\x20\x80\xd8\x52\xa9\x9b\x19\x8a\xd4\x92\xd4\xf4\xf4\x19\xd9\xdf\x7e\xa8\x2a\x92\x52\xf7\x4c\xb2\xb9\x3b\x5c\x80\x78\xd4\x12\x59\x2f\xd6\xbb\xf8\x15\x7c\x34\xd3\x5e\xdb\x50\x55\xdf\xeb\xd6\x3b\x08\xd3\x38\x3a\x1f\x03\xb4\x1e\x55\xd4\x76\x0f\xa3\x2c\x80\xa3\x8e\x07\x50\x10\xf4\x30\x1a\x84\x77\x93\x82\x70\x0a\x11\x87\x1a\x5e\xdd\xa1\x3f\xa5\x75\x70\x50\x01\x54\x35\x28\x6d\x21\xb4\x5e\x8f\x11\x8e\x07\xdd\x1e\x40\x07\xf0\x93\x05\x15\x21\x44\xe5\xe3\x34\xa6\xf7\xe1\xe0\x26\xd3\xc1\x0e\x61\x34\xaa\xc5\x0e\xb4\x85\xaa\xf9\xe7\x55\xdd\x3a\xdb\xeb\xfd\xd5\x40\x64\x5d\x25\x22\xd2\xdf\xf7\x6a\xc0\xc5\x63\x6d\x26\xd5\xd4\x55\xf5\xf9\x80\x1e\x41\xd1\xff\x60\xa7\x61\x87\x1e\x5c\x0f\xad\x32\x66\xa7\xda\x5b\xe8\x27\xdb\x46\xed\x88\x15\xc6\x7c\x72\x13\xb4\xca\x0a\xa7\x48\x78\x4f\x6e\xf2\x99\x8f\xe8\x2a\xa2\xb7\x75\x1d\x12\xd1\x51\x0f\x18\xc0\xc5\x03\x7a\x88\x07\x65\x33\x17\x35\x7c\x3e\x20\x58\x35\x90\xa8\x42\x7b\xc0\x01\x89\xd5\xc6\xd9\xe7\x8c\x6c\x7d\xa7\xf1\xb8\x69\x6a\x78\xed\x7c\x85\xf7\x8a\x85\xa7\x0a\x2d\xe7\xc2\x41\x16\x24\xa1\x82\x78\x40\x98\x02\x7a\x08\xea\x0e\x03\xff\xdc\x4d\x7d\x8f\x1e\x8e\x49\x5e\xd7\x55\xd5\x34\x8d\x99\x54\x55\x80\x39\x7b\xa3\xee\x50\x50\x56\x00\x00\x75\x5d\xf3\x5f\x8f\x71\xf2\x16\x7a\x65\x02\x56\x68\x3b\xda\xc9\xf2\x82\x86\x16\x37\x70\xa7\xbc\x56\x3b\xc3\xb4\x2b\xf0\xd8\xa3\x47\xdb\x22\x44\xc7\xa8\x69\x11\x3f\x28\x41\xa4\x03\xec\x90\x38\xc6\x7b\x6c\xa7\x88\x1d\x38\x5b\x57\x9f\x0f\x3a\x30\x00\x33\xb8\x10\x41\x99\xa3\x3a\x09\xe9\xed\xe4\x3d\xda\xc8\x70\xb6\x17\xe2\xdf\x63\x14\xdd\x6a\xbe\x9d\xfc\x8f\x1a\x8f\xeb\x4d\x03\x2a\x54\x47\x34\xa6\xae\xaa\xe7\xc6\x80\xba\x53\xda\x30\x79\x2a\x9d\x20\x1d\xb2\xd1\x21\x8a\xbe\x10\x8a\x5b\x3c\xed\xb4\xed\xb4\xdd\x07\x08\x98\xe4\xd1\xf3\xa7\x03\x9a\xb1\xae\xaa\xd7\xce\x17\x65\x08\xc4\xd9\xe0\xa6\x50\x40\x6e\x99\x20\x56\x1e\x13\x1c\xec\xf5\x1d\x0a\x60\xbc\x23\xca\xb5\xed\xdd\xa3\x12\xff\x9e\x80\x7c\xf4\x18\xc2\x5a\xb8\xe3\xf5\x22\x7e\xe3\x5a\x65\xe0\x7e\x0b\x27\x78\x26\xef\xaf\x3f\xba\xa0\x59\x2f\x36\xd5\x1f\x9f\x4c\xc0\x85\xc2\x26\x23\x61\xca\xd2\x0e\x05\x3b\xe7\x0c\x92\x26\x8e\xd8\xea\xfe\x44\xc7\x71\x3c\x60\xd2\xd0\x74\x66\xb2\xb1\xda\x21\x78\x24\x6a\x48\x60\xe9\x4c\xdb\xc9\x07\xe7\xc1\x79\xb0\x2e\x82\xea\x63\xda\x37\x1f\x71\xeb\x48\x5b\x23\xd6\x55\xf5\xde\x45\xd2\x49\x32\x04\x26\x6d\x96\xa3\x6b\xdb\xc9\x3f\xdc\x4e\x6e\x60\x87\x68\x0b\x90\xae\x86\xb7\x3d\xcb\xf8\xa8\x6c\xac\xd4\x6c\x97\x3b\xec\x9d\xc7\x0b\xdc\x59\xb1\xb6\x64\x05\xd0\x8c\x1e\x93\x3d\x91\x2d\xbd\xa5\x93\x21\x02\x55\xe0\x7d\x55\x16\x85\xc8\x06\xbb\x24\x13\x8d\xa1\x88\x24\xf1\xb9\xc0\x32\x7b\x9e\xa2\xc5\xcc\x46\x45\x6b\xcc\xa4\xc4\xf6\x33\xfd\x81\x74\xd1\x8a\x03\x98\x02\xf6\x93\x99\x39\x88\x0e\x6e\xad\x3b\x82\xda\xb9\x69\xe1\xef\x58\xae\x76\x66\xaa\x6a\x9c\x25\x05\xff\x30\x62\xf6\x0b\xf3\x62\x02\x86\x1d\x91\x6b\xc9\x0d\x90\x0b\xc3\xa3\x1c\xa2\x0e\xe0\x46\x24\xb6\x94\xed\x98\x03\x8b\xc7\x2a\x7f\x1a\x55\x08\x6c\x06\xe4\x8a\xc4\x00\x13\x7d\xbd\xf3\x10\x30\xb2\x17\x17\x55\x74\xa3\xe8\xd3\x4e\x05\xb6\x59\x16\x5e\xaf\x0d\xc6\xd3\x88\x5b\xde\x90\x7d\x14\x09\x92\x36\xba\xbe\x87\x26\xaa\x5d\x88\x2e\x8c\xaa\xc5\xd0\x80\xb3\xe6\xc4\x6b\xdf\x38\xa0\xcd\x2c\x64\x86\x75\x22\x03\xaa\x84\xd8\xba\xaa\x9e\x3e\x7d\xfa\x7b\x5e\x79\xd6\x6d\xe2\x29\xbb\x9f\x20\x3a\xc6\x8b\x8b\xcd\x47\xc7\x6a\xa3\x2d\x38\xdf\xd1\xf1\x38\x50\x6d\x8b\x41\x5c\x8b\xb6\x96\x9d\xa2\xbf\x65\xd3\x77\x3d\x70\xc4\xa8\xe1\x3b\x42\xcb\xee\x8c\x1c\x05\xac\x69\x31\xb1\x09\x41\xef\xad\x8a\x93\xc7\x40\x4c\x54\x0b\x4a\x3c\x26\xbb\x9f\x02\xb1\xfe\xc6\x3d\x09\x69\x0b\x47\xba\xcd\x75\x55\x7d\x0d\xcd\x87\x9b\xe6\x7a\x76\x98\x72\x80\xb4\x4d\xe8\xf9\x70\x23\x04\xf0\x99\x8a\xdb\x33\x27\xf2\xee\x22\x4d\x4b\x84\xc8\x29\x91\xec\x01\x82\x1a\x10\x54\x10\x64\x6f\x3e\x7c\xb8\x29\xa0\xb7\x10\x1c\x34\x9d\xf2\x47\x6d\x9b\x2d\x34\x47\x6d\x3b\x77\x0c\xf4\x68\xb4\x9d\xee\xe9\xa1\xf7\x88\xbb\xd0\x35\x75\x5d\x6f\x98\x38\x89\x9a\x2f\xb5\x6f\xae\xa1\x75\x36\x2a\x0a\xdd\x44\xd7\xa8\xe2\x21\x9b\xbe\x10\x28\x4b\x27\xaf\x58\x3d\xf9\x20\x19\x04\x1d\x76\x73\x9d\xe5\xe6\x7a\x50\xc6\xf0\x36\xfa\xb0\xe0\x49\x93\x9c\x50\xb0\x4e\xfe\xb3\xda\x35\xd7\xe9\x44\x3a\xbc\xcf\xbe\x37\x7b\x7e\xde\x9b\x5c\x35\x3f\x13\x70\xde\x3b\x60\x08\x68\xf7\x48\x14\x1b\x8c\x81\x0f\x3b\xa0\xed\x80\xbe\xa8\x3d\x86\x4c\x36\xc7\x43\x72\xe2\x12\xb0\x47\xef\x86\x31\x0a\xcd\xef\xf1\xf8\x82\xc3\xe3\x3a\xe2\x7d\xdc\x0a\xb7\x21\x7a\x6d\xf7\x1b\xf8\x5a\x3e\x91\x44\x78\x67\x48\xc6\x95\x02\x6a\xef\xdd\x00\x4a\x4e\xbe\x62\x97\xac\x3a\x52\x29\xc9\x77\x44\x21\x08\xde\x39\xa2\xd7\xde\x0d\xaf\xb5\xc1\xf5\xff\x05\x55\x01\xfb\x06\xe3\x3b\x54\x14\xbd\x7e\x3a\xe8\x88\x6c\x69\xeb\x0d\xbb\xf8\xe6\x3a\xb9\x35\x39\x47\x23\xcb\x48\xef\xd2\xba\x2c\xea\x02\x55\x88\x61\xb8\x6f\xc3\x4f\xce\x77\xdf\x1e\x94\x5f\x87\xe8\x0b\x99\x17\x70\x1f\xf1\x91\xb2\x52\xec\xe7\xc9\xd1\xf9\x8e\x00\xb7\x07\xe5\x55\x1b\xd1\x3f\x61\xe0\x9f\x26\x8b\x37\xd1\xaf\x3d\xa9\x37\x6e\xd2\x9e\x05\x5c\x95\xc1\x24\x4d\xa4\xc7\x42\x29\x6f\x62\x38\xef\x5c\xbb\xe6\x20\xa9\x6d\xdc\xc0\x3b\xd7\x9e\x81\x20\xf1\xd1\x92\x86\x80\x4d\xad\x28\xcd\x4f\x62\xef\x2f\xb5\xc7\x36\x3a\x7f\x5a\x3f\x8a\xdd\x3b\x47\x5e\x9d\x4f\xc8\x92\x9d\xcd\xa1\x8f\x95\x32\x79\x0d\x62\xad\xcb\x90\x18\xfc\xdf\x9c\xb6\x1f\x55\x3c\x84\x75\xa7\x7d\x5d\xd7\x45\x70\x05\x49\xeb\x86\x9d\xb6\x18\x60\x98\x4c\xd4\xe4\x30\x33\x04\x2d\x0a\x4b\x39\x9e\x31\x67\x87\x5c\xa8\xa5\xa4\xf5\x4c\x6f\x96\x54\x1b\x03\xbb\x49\x0e\xc1\xa8\x10\x01\x0d\x0e\x44\xac\xeb\x19\xd2\x96\x40\xc6\xd3\xa8\x29\x60\x9c\x8a\x69\x3f\x09\x17\x2c\xbc\xc1\xf8\x81\xbd\xfd\x9a\x39\x7f\x88\x88\x73\x04\x65\xa6\xa2\x3f\x1e\xff\x31\x21\xa7\x54\x12\x26\x18\xcc\xf3\xae\x7b\x08\x66\x9b\x36\x6a\x1b\xd1\xf7\xaa\xc5\x2f\xbf\x11\xe4\x40\xd6\x3b\x9f\xaf\x40\x11\x3b\x12\x5f\x97\x3e\xc8\xe6\x75\xb3\xd8\xde\xc0\x80\x8a\x83\xc1\x49\x3c\xae\xb6\xf0\xc6\x89\x4b\xbb\x29\x9c\x08\xc4\x8c\x7d\x66\xe9\x71\xc4\x39\x77\xa5\xc5\x12\x1b\x89\x84\xa3\x36\x86\x36\xf0\xb7\xb4\x72\x6f\xdc\x8e\xa4\xb9\x85\xc9\x1a\x0a\x2c\x3a\xa6\xc8\x21\x51\x93\xe2\x9d\x2c\xad\x33\x45\xef\xe8\xcb\x1f\x90\xb5\x95\xd0\xfd\xf5\x8f\x1c\xe6\x7f\x9f\x44\x3e\xcd\x72\x12\x8c\x4f\x5c\xeb\xbc\x56\x7c\x07\x23\x7e\xa1\x6d\xf7\x9f\x78\x5a\xdf\xe2\x69\x5b\xd2\x98\x22\x06\xca\x7f\x03\x34\xb7\x78\x6a\x88\xf9\x46\x16\x34\xbc\xf3\x7b\x75\x8b\xdf\xba\x61\x50\xb6\xe3\x93\xdc\xce\x25\x48\x26\x38\x65\x3a\x1c\x0a\xeb\xba\xfe\xb6\xfc\x24\xd8\x6c\xfe\xc5\xa5\xb5\x02\x29\xe5\xed\x04\x2f\x67\x32\x2c\x5e\x62\x02\x9a\x8c\xa0\x91\xf4\x20\x27\x59\x35\xfc\x10\x10\xbe\x21\x80\x94\x43\x2c\xb1\x46\xc7\xd5\xc0\x7b\x37\xe3\xae\x97\xd4\xa7\x77\xeb\x0b\xd2\x37\xcd\xf5\x39\x79\xcd\xbc\x96\x25\x41\x29\xa4\x90\xba\x10\x83\xc8\x65\xae\x39\xce\x2d\x63\x59\xb1\xf0\xc2\xef\x94\xed\x4c\x11\x61\x3b\x74\x4b\x93\x9a\xec\xf2\x70\x93\x74\x78\xdb\xab\x7b\x6c\x97\x72\x2f\xd2\x56\x7e\x1f\xe0\xe7\x5f\xb2\x63\x59\xe7\xf7\xe8\xbd\xf3\x04\x94\xe4\x05\x0a\xd6\xe1\x80\xc6\x6c\xce\x45\x7e\x66\x4e\xca\xef\x27\xf2\x10\xa1\x86\x4f\x4b\x0e\x64\xc3\x93\x00\x6e\x8a\xe3\x14\x39\xd1\x52\x30\xba\x10\x34\x25\x2e\x8c\xa8\xce\x8e\xfc\x86\xb0\x3c\xc2\xdd\x23\x84\x7d\xa2\x32\x1e\x98\xac\x8c\x24\xa5\x9e\x53\xc0\x70\xc6\x72\x43\x54\x4e\xb6\x4b\xb5\xc1\xc1\xb9\x8e\x9d\x1b\x17\x32\x9d\xc3\x00\xc1\x0d\xe4\xc0\x3c\x27\x5c\xa4\x0f\x9c\xa0\x67\x8e\x60\xad\x6b\xac\xe1\x1f\x13\xfb\xf1\xf2\x7a\xc3\x55\x37\x6b\x50\xd6\x04\xff\xbf\xe6\xfc\x85\x6a\x6f\xf7\xde\x4d\xb6\x63\x19\x5c\x1c\xed\x23\xcc\x66\xeb\xdc\x95\x8d\x05\xd6\x5b\xf2\x68\x64\x76\x77\x78\x09\x6c\x0b\x47\xa5\x23\x47\xde\x2d\x69\xf9\x07\x21\x8d\x7e\xff\x49\x21\x13\xbf\xbb\x13\xb4\xc6\xb1\xb0\x24\x89\x23\x7a\x72\x72\xb9\xe0\x5d\x5c\xb3\x50\x62\x4e\x5c\x7a\x35\x84\xbf\xe1\xc4\xd3\x4f\xb8\x25\x81\x70\x02\x25\x26\xbb\x63\x71\x4e\x21\x17\x41\x12\x58\xbc\xe3\x3c\x1b\xef\x75\x64\xeb\x1c\xbd\x54\xc3\x9c\xc5\xa1\x1f\xb4\x55\x46\x92\x1a\x3d\x0c\xd8\x69\x15\x91\x92\x5d\xcc\xc5\x03\x15\xd9\x44\xe6\x96\xc2\x9a\x3b\xa6\x58\x5b\x72\xb9\xe8\xe6\xce\x42\x3a\xaa\x14\x8b\x12\x66\x21\xbc\x48\x6b\x41\xfd\xf9\x39\x87\xa8\x6c\xa7\x7c\x97\xa0\x14\x3f\xcf\x75\xaf\x94\x84\x35\xcc\x35\xac\xfe\x1d\xa0\xac\x8c\x0b\xc9\x65\x0c\x01\x06\x75\xe2\xd4\x68\x87\x07\x75\xc7\xa2\xb2\xce\x0f\xec\xab\x77\xd8\x2a\xae\x51\x75\x50\x31\x9e\x1a\xc1\xbc\xac\xeb\x8b\x7a\x7c\x46\x3f\xbc\x1a\x26\xa3\xa2\xf3\x7f\x4e\x35\xb6\x73\x91\x99\x0d\x92\x55\xa4\xb9\x86\x1b\x29\x1c\x88\x96\xc7\x54\xaf\x01\xbc\x6f\x71\x8c\xe7\x5a\x21\x7d\x26\xf2\x21\x49\x89\xb3\x9f\x0b\xa3\xa1\xa0\x47\x4a\x57\x0e\x16\x13\xa9\xdc\x43\xe2\x66\x51\x26\xa6\x01\x6d\x89\x44\xbd\xcc\xf1\x16\xd5\xb0\xe2\xfa\xf9\xa2\xc9\x95\x4f\x64\x51\xe9\x2e\xcf\x9a\x90\xb0\xa2\x49\x4b\xed\x51\x7d\x28\x30\x46\xef\xee\x74\x47\xca\x2a\x26\xdf\x6b\x1f\xc4\xce\x39\x58\x67\x4f\x51\x12\xbe\x44\x1b\xe1\x58\xeb\xa2\xf2\x80\xc3\x18\x4f\x8f\xa8\x03\x1f\xdb\x86\xf9\x5e\x76\x3e\xe8\x4b\xe2\x49\x19\x4d\x3b\x83\xe0\x4b\x5d\x53\xa9\xa9\x59\x8b\xdc\x88\x5e\xfa\xa7\x52\x3b\x06\x58\x73\xd1\xb6\x05\x29\xe8\xb6\xd0\x79\xb5\x77\xb6\x37\x27\xce\xe8\xa8\x66\xde\x85\x6e\x0b\xa9\xa2\xdb\x48\x98\x24\xe2\x9b\xa4\x36\x37\x19\x49\x03\xeb\x80\x08\x3b\x34\xee\xb8\x29\x6d\xa0\xe8\xa0\x43\x39\x3b\x24\x96\x16\xa7\xcb\x45\x01\x93\x41\x14\x17\x62\x45\x2f\x1f\x40\xbf\x86\x17\x0f\x3b\x4b\x09\xe0\x03\xdd\x38\x03\x98\x3a\x0a\x70\x87\x3e\x48\xff\x8d\x30\x8b\xa3\x12\xc3\x0b\xd9\x59\x25\xdc\x8e\x4a\x93\x8f\x2e\xac\x8d\x6b\x29\xf3\xdf\x52\xb6\x93\xab\xa8\x0d\xd9\xe2\x65\x5c\xce\x25\x08\xb9\x73\x9d\xbb\x7c\x6a\x8e\x86\x5c\x48\x70\xab\x2b\xa7\xb0\x9f\xd0\x38\x45\x6c\xad\x3f\xe1\x86\x1e\xa5\xcf\x4a\x26\x20\x45\xd2\x8b\x53\xc4\x0f\x7d\x1f\x30\xfe\x21\x19\x78\xaf\x5a\xaa\x7b\x8d\xbe\xc5\x05\xe9\xc5\xd0\xc8\x7e\x58\xd4\xad\x9b\x28\x76\xed\x4e\x94\x8d\x68\x1b\x22\xaa\x8e\xa8\xa4\x82\x27\xa4\xfa\x62\x77\x33\xaa\xa3\x25\x27\xf0\x7e\x99\x15\xb4\x43\xf7\x7c\x99\x18\x6c\xc1\xd9\x9b\xd8\xb9\x29\xa6\x27\xf4\x9e\x9e\x5e\xdd\xeb\x58\x36\x91\x2f\xe5\x74\xa2\xae\xeb\xb3\x8c\xe8\x26\x2a\x1f\xc3\x59\x7c\x48\xe5\x6c\x32\xa7\x87\x91\x0c\x9a\x8c\xb0\x49\x8f\xe8\x7d\x03\x29\xfc\x34\x82\xb9\xe1\x56\xc9\x59\x2f\x75\x69\xef\xe1\x5f\x18\x7c\x29\x54\x73\x37\xf7\xa0\xc6\x11\x4b\xc2\x3e\x53\x33\x87\x81\x26\xf3\x28\xa8\xcf\x93\x04\xc6\x56\x7c\x3e\x65\x90\xa9\x27\x76\x61\xfd\x33\x81\xe5\x10\x48\x40\x67\x9e\xf8\xff\x45\xdc\xff\x2a\x7d\x38\x73\x32\x28\x2a\x24\x6b\x53\xd2\xcc\x3e\x89\x5d\x1c\xa5\x4a\xd8\x51\x1e\x90\xb3\x03\x16\xab\x34\xac\xa4\x29\x9a\x53\xb2\xb7\x64\x6f\xe2\x55\x53\x7b\x53\x72\xe0\x70\x80\xa7\x6d\x53\x17\x19\x60\xca\xf8\xbe\xa6\x75\xf5\xb7\x43\x47\x3e\x2a\xaa\xb3\xfa\x8a\x13\xa8\xdc\x13\xb0\x49\xae\x21\x76\xba\x34\xd9\x7f\x75\x3b\x2e\x76\x93\x33\x2f\x02\x76\xe3\x39\x70\x82\x77\x4b\xe7\xa4\x78\x8b\x0c\x0d\x28\xbc\x06\xc4\x41\x6c\x4b\x41\xa0\xd0\x3a\xf7\x9f\x4a\x2b\x70\xd6\x30\xca\x22\xc5\xd1\x0a\x7a\xb7\xfb\x15\xdb\x18\xaa\xd2\x01\xde\x49\x6d\x3c\xef\xa0\xc0\x0d\x03\x15\x98\x03\xc6\x83\xeb\x48\xab\x16\x13\x87\xb9\x6b\xc0\x29\x89\xc0\xab\x44\x91\x79\xa6\x95\x9a\x60\xea\xf1\x19\x92\x32\x86\x67\x3c\xb9\x7f\x5a\xcd\xb0\xaf\x79\x28\x23\x21\xa5\xa9\xe1\xef\x69\x0b\xb9\x70\xa1\x70\xc1\x6a\xc9\x51\x33\x1a\x51\x96\x6a\x39\xdd\x38\xa0\x19\x21\xba\x51\xb7\x12\x29\x73\x00\x70\xf6\x01\x91\xb4\xa5\xe5\xa2\xe8\xbc\xc5\x33\xb7\xb8\x8b\x0d\xcf\xdd\x70\x3f\xd9\x1a\xde\xa6\x39\x93\x47\xd2\x27\x3a\xff\x3d\x5a\xf4\x9c\xf1\x84\xa8\xdb\xdb\x94\xde\xc9\xe0\x82\x75\x70\x50\xfc\x52\xcd\x13\x34\x50\x77\x4e\x33\x8c\xc9\x07\xca\xa7\x46\xef\x76\x06\x87\x70\xde\x6a\xd6\x7d\x92\xa3\x31\x0f\xc4\x46\x89\xd9\x86\xd2\x0d\xf6\x46\xf4\x86\x64\xf8\xb7\x29\x44\x19\x0c\x3c\x2e\x65\x08\x12\x70\x2a\x8a\xf5\xce\x3e\x89\xa9\x24\x2d\x20\x40\xed\x95\xa6\xe2\xf2\x87\x90\x4d\x75\x71\xec\xdb\x72\xae\x5c\xa8\x2c\xba\xcd\xa9\x8f\xa7\x42\x70\xad\x56\xc5\xa6\xd8\x74\xb9\x54\xdc\x9d\x92\x2d\xce\x94\xd5\x2f\xa6\xbe\xc9\x53\xaf\xd2\xe1\x5c\x00\x6d\x5e\x6b\x83\x9f\x4f\x23\x36\x5b\x68\x3e\xaa\x78\x68\xb6\x55\x43\x61\xa1\xa9\x6b\x19\x6b\xce\x7a\x91\x74\xf7\x11\xf5\x2b\x3e\x00\x97\x3d\xd5\xb9\x51\xad\x3c\x5e\x9f\x37\x5c\xeb\xef\xa5\xc1\xba\x1e\xc2\x9e\x9c\xd9\x59\x3b\xe7\x7c\xe5\x2b\x4a\x3a\xff\xc4\xba\xbf\x63\x78\xef\x3e\x72\x5d\xb1\x4e\xe5\x45\x29\x24\x39\x9f\xe5\x7a\xe7\x62\xd3\xd9\xfa\x2d\x1c\x74\x88\xce\x9f\x3e\x73\xe3\xfd\x41\x6f\x82\x5f\x2f\x7a\x13\x73\xed\xf4\x18\xe8\xe7\x5d\xf7\xce\xed\x1f\x27\xbc\xfa\xea\xab\xaf\xbe\x12\xd7\x5b\x55\x6f\xdc\xa2\x21\x32\x0f\x08\x58\xc7\x6a\x8e\x7f\x6c\x2f\x52\xe1\x5e\x93\x82\xf1\x30\x70\xef\xaa\x87\xe2\xdc\xc8\x10\x2f\x25\x2d\xae\x4c\x0d\xcb\xca\xeb\x8b\x95\x8b\xc1\x18\xbb\xd9\x54\xcc\x64\x81\xfc\x9a\xb4\xfd\x11\x40\x17\xa2\x5b\xad\xb6\xf0\x4d\x02\xfa\x12\x77\xd3\x9e\xec\xdd\xb8\xfd\x9e\x34\x63\x39\xdd\x26\xa5\xd9\x21\x74\xce\xa6\xce\x08\x27\x93\xcc\x62\xb6\xca\xd6\x75\x54\xb9\x3c\x40\x99\x44\xba\x4a\x3c\xc0\x9e\xca\x78\x9e\xf1\xac\xb6\x02\xfd\xc7\xa4\x72\x9f\xdd\x47\xaf\x6d\xfc\x0e\x3d\x66\x46\x2d\xc8\x35\x83\xe8\xd8\xff\x2d\x49\xca\x64\xa6\xc4\x7f\xf4\x64\x1e\xcd\xb7\xd1\x9b\x57\x0d\x69\xb5\x95\xc6\x60\x63\xdc\xbe\xa1\xea\xb5\xca\x1b\x64\x2e\x22\xc1\x9f\xd2\x68\x29\xf7\xed\xa9\x40\x0c\x54\x09\x70\x95\xba\xc4\x97\xb3\x93\x4e\x87\xd1\xa8\x13\x76\x15\x31\x51\x57\xa4\x17\xf0\x9c\xad\x33\xdb\xd3\x1b\x37\x1f\x8a\xd1\x3b\xaf\xfc\xa9\xaa\x38\xbc\xce\x86\x49\x0e\x8d\xc1\x97\xc9\xe1\x3c\xa7\xe2\xa8\x93\x02\xd5\x1c\x90\x52\x1e\xf0\xc6\x55\x97\xc0\xeb\xaa\xba\xd1\xc3\x68\x4e\xa0\x07\xca\xac\x53\x97\xb7\xbd\x25\x79\x9f\xdc\xf4\xa4\x4b\x81\x52\xc6\x81\xb6\xf8\x01\x52\x57\x1d\xcf\x02\xd1\x3c\xb4\x96\x66\xa6\x76\x53\xd4\x06\x9e\x25\xd0\xeb\x95\x76\x57\xf2\x6e\xb5\x49\x4b\xfa\x21\x2e\xbe\xf7\x43\x5c\x6d\xaa\xf4\x89\x12\x03\xee\x52\xd0\x02\xde\x55\x7f\x42\xd5\xf1\x20\x64\x75\xe3\x06\xa4\xa7\x3a\xde\xf3\x16\xdd\xf3\xca\x7f\x3e\x03\xab\x39\x2e\x71\xd2\x07\xb3\x2a\x89\x57\x59\xf1\x1f\x1e\xbb\x70\x13\x48\x1b\xbc\x86\x0b\x58\x48\x51\x86\x36\x3f\x7d\x0a\x2f\x29\x39\xa1\x4a\x22\x87\x7a\x8a\xce\x16\x94\xf7\x8a\xa5\xcc\x79\x77\x5e\x2c\xfe\xfd\x66\x24\x45\xec\x53\x77\xd2\xd9\x3b\xf4\x9c\xaf\x73\xa9\x9a\xc6\x25\xf3\xc0\x3e\x44\x62\xaf\x1f\x62\x9d\xf6\xad\x57\xff\x1e\x56\x92\x15\xa5\xb1\x3d\x91\xe1\x38\x01\xe1\x32\xa2\x74\xe5\x98\x7a\x3e\x0e\x36\x5a\xe2\xe9\xdf\xf2\x06\x72\xe6\x65\xc2\xff\x5d\x1e\x8a\x2e\x8e\x36\x88\x92\xfe\x8e\xc6\x49\x90\x29\x81\x89\x35\xab\xae\xde\xa3\xf2\x54\xf0\x1a\xb3\xd0\xac\x0c\x26\x2c\x40\x13\xae\xb9\xf2\x63\x0d\x51\x56\x6a\x9a\x2a\x67\x1f\x12\x54\xce\x07\xa2\x73\x71\x97\x51\x1b\xe7\x6e\x21\x1e\xbc\x9b\xf6\x07\x20\xcd\xaa\xf7\xae\xa9\xd6\x97\xd7\x79\x50\x85\x13\x37\x5f\x6d\x87\x9e\x99\xd9\x88\x3b\xa9\xfa\x21\x56\xda\x55\x45\xf1\x2a\x8b\xb1\x1a\x54\x3c\xf0\x3f\x57\x9e\x8a\x0d\x17\x2a\x3f\xd9\xa8\x07\xac\x78\x7e\x42\x72\xe5\x07\x39\x2d\x4a\xf4\xf6\x78\x3f\x56\xdc\x0d\x09\x15\x2f\x64\xc1\x12\x63\x9d\x6b\xb9\x34\x48\x93\x4d\x32\x06\xd5\x1e\x92\x05\x2e\x2f\x56\xcc\x81\x3e\x88\xbd\x11\x6f\x55\xe6\xed\xf7\x0e\xe2\x0c\x7c\x2d\xfe\xa2\x63\xdd\xe5\xd4\x8c\x27\xa9\x5b\x08\x27\x1b\xd5\x7d\xfe\xc5\x9d\x6e\xe3\xbc\x5c\x11\x0a\x17\xb7\x8d\xaa\xea\xef\x0b\x0b\xe6\x10\xfe\xbc\xeb\x3e\x89\x00\xd8\xb8\xa4\x67\x2f\x63\xfa\xb3\xc9\xd1\x1c\xb6\x48\x99\xbb\xae\xa2\x80\xef\xa6\x00\xb7\x3c\x0d\x70\x7d\x1a\xd1\xcb\x14\x3d\x23\x3c\x73\x11\xdb\x94\x81\x65\xb7\x22\x80\x40\x31\x3b\x15\x67\x9a\x17\xdb\x73\x49\xd7\x44\x0c\xb1\x11\x29\x4a\xbe\x98\x66\xb3\x4a\x3e\xd5\x43\xd7\x30\xfe\x2d\xfb\x2a\xce\xf0\x96\x4e\x70\x76\x4e\x17\xec\xae\x68\xf7\x6a\x0b\x2b\x22\x81\xfe\x26\x68\xab\x14\x48\x7e\x08\x0f\x44\x14\x5e\x7b\x37\xcc\x03\xc1\xa5\xc0\x3a\xed\x59\x6a\x11\xbd\xdd\x34\x33\x7f\xe5\x3a\x42\x55\x64\xc4\x83\x30\x01\x5a\xc3\x67\xc7\x16\x9c\x5a\x68\x36\xa6\x31\x9c\xca\x2b\xc4\xda\x29\x32\x37\xe4\x05\x97\x0c\xf4\x29\xa5\xdb\xc2\xd9\xf8\x8d\xf4\xa0\x79\xa7\x43\x5c\x12\x5e\x16\x97\x65\x15\xe9\x2c\xd9\xf4\x12\x51\xc8\xb1\x69\x8a\x2e\xdf\x4a\x29\x85\x62\xa9\x86\xab\xea\x86\x0b\x0b\xbe\x43\x93\xae\x72\x38\x30\xa8\xbc\x85\x83\x3b\xe6\x71\xc8\xc5\x48\x45\x32\x9c\x8b\xf1\xc8\x59\xdc\x28\x6a\x26\x55\xee\x4f\x3a\x1e\xd6\x37\x92\x79\xf1\x1b\xb9\xf1\x24\x6f\xe0\x59\x7a\xb8\x9e\xc6\x11\xfd\x3a\x7f\x53\x3e\xf2\x27\xe5\x63\xfe\x02\xcb\x5b\x50\xc2\x7e\x1d\xa6\x5d\x06\xfd\x97\x6d\x7a\x67\xd0\xae\x05\xcf\xe6\xd9\x33\x7e\x60\x5f\x3a\xdf\xc4\xca\x12\x59\x73\x67\x72\x79\xff\x4a\xf1\x60\xa3\x0c\x99\x9e\xc1\x97\xd5\x77\x68\x8c\x23\xb5\xfa\xc9\x79\xd3\xd1\xc3\x6b\xc7\xbf\x5f\x28\xbf\xfa\x6d\xb1\xd7\x63\x98\x0c\x11\xfd\xe5\x37\x71\xfc\x74\x30\x7a\x7b\x47\xf6\x3b\x2a\xed\xc3\xfa\x1c\xfa\x06\x3a\x1e\xe9\xf1\x7f\xba\x5f\x0a\xeb\x6e\x2b\x4d\xd3\xcd\x1c\x09\xf3\x7f\x91\xb2\xa5\x5a\xdb\x80\x3e\xae\x05\xe5\x16\xee\x36\x65\x0d\x4a\xf7\xa5\xfc\x4d\xe2\x92\x95\x17\x72\xe8\x9d\x5b\x2b\xbf\xdf\x5c\x84\xda\x9c\x71\xf2\x27\xde\xb1\x9c\x08\xae\x7a\x61\x3f\x69\x4c\x2d\x3f\x2f\xa6\x6e\xe5\x6b\x16\xf5\x6a\x93\xac\x91\xb4\xf2\x25\xf6\x8a\x44\x35\xe6\xfb\xa4\x0b\x07\xc3\x6e\x28\x7d\xd8\x96\xc2\xb7\x4b\x3b\x1a\x45\x0a\x6d\x5c\xc0\xa4\x85\x86\xf3\xf4\xa6\x80\x5a\x53\x3d\x30\xdf\xf4\x93\xce\x7b\xb9\xc4\xe2\x11\x3c\x8e\x29\xce\x40\x93\x2c\x26\xdf\x18\x6d\x36\x94\x1e\x1c\xd1\x18\x49\x13\x4e\x55\xbe\xcf\x5a\x6e\x1a\x9d\x43\x77\x7d\xaf\x5b\xad\x0c\xb4\x07\x65\x2d\x9a\xea\x67\x4a\x0b\x7f\x59\x1f\x62\x1c\xc3\xf5\xd5\xd5\x5e\xc7\xc3\xb4\x23\x19\xc8\xd5\xd4\xa7\xd8\xe9\xe8\x7c\xc2\xf7\x34\xed\xda\x24\x5b\x95\xdb\xb5\xf0\xbd\xb2\x6a\x8f\x3e\x5f\xb2\xe5\xaa\x92\x9b\x0a\xb0\x9b\xb4\xe1\xbe\x7b\x72\xac\x83\xac\xbc\xa8\xef\xb4\xbd\x73\xb7\x38\xcf\xfe\x9a\xbf\xe6\xf5\x75\x5d\x37\xa5\xe5\x23\x42\x4f\xe3\x64\xdd\xcd\xe3\x8b\x45\x7e\x38\xcb\xbf\x49\x9f\x9b\x65\x47\x41\xca\xcd\x4c\x45\x8f\xb1\x3d\x60\x28\x77\x80\x4b\x7e\x92\xd8\x0c\xb0\x2e\x17\xd8\x52\x04\x9d\xaf\x0d\xa5\xd0\x36\x60\x54\x9c\x39\xe5\xbb\x6e\x91\x2f\xcb\x05\xb9\x2d\x57\xc3\x8b\x53\xd6\x84\x6d\x3a\x53\x6e\xa9\x2f\xd6\x9c\x9d\x4b\x95\x50\xcf\x37\xe7\xf2\xbd\x46\x15\xe1\xcf\x1d\x0e\x37\x8f\x92\x48\x2a\x8a\x04\x1c\xd9\xdc\x91\x6f\x15\xfa\xee\xe9\xa8\x7c\x3c\xcd\x2c\x2e\xea\x6a\x81\x93\xbf\x34\x79\x8c\x4f\x5a\x9b\xe1\xc9\x2d\x0c\x6e\x13\xdb\xdb\x33\x80\x45\xf1\x9c\x4c\xc3\xb8\x35\xac\x8c\x91\x2c\x65\x99\x74\x64\xc9\x65\x5d\xc8\xc9\x65\xc2\x4f\xea\x5e\x90\xd7\xb9\x7c\x5c\x44\xef\x71\xda\x19\x1d\x0e\x73\x2b\x86\x3e\x73\xf7\xab\xc3\x94\x2d\x17\x35\x97\x15\x12\xc2\xd3\x6d\xd1\x69\xe4\x2e\xf9\x32\xde\x3b\x6b\xb4\x45\x58\x47\x07\x6f\x58\xc6\x54\x9f\xf5\xe8\xd5\xce\x9c\x36\x52\x70\x71\x44\x6d\x88\xb6\xfa\xd7\x40\x11\x85\x62\x56\xba\x14\xcc\x71\x32\x27\xdf\x51\x25\x53\xcb\x9a\x31\xd7\x4e\x39\x39\x29\x77\xf7\xec\x79\x19\x43\x80\xab\x9f\xbf\x54\x00\xab\xf7\x6a\xc0\xd5\x35\xac\x64\x0b\x05\xd9\xd5\x96\xde\xbf\x44\xb9\x76\xae\x9d\xa5\xcf\xf3\x2d\x40\xab\x5b\x8e\xe2\xad\x0e\xe4\x7b\xca\x2a\xbe\x32\x98\x4f\x47\x60\x7c\x56\xfb\xb0\xba\x86\x9f\x57\xe3\x29\x1e\x9c\x25\xcf\x48\x3e\x49\xdb\xfd\xea\x17\x5e\xf0\xa3\x0c\x38\x78\x11\x3b\xda\x2f\xc9\x57\xe7\x2f\x84\xfa\x2f\xf5\x37\xf5\x37\x0c\x90\xbf\xfc\xe0\x0d\xbd\x7d\xc4\x8f\x4c\x01\xb3\x8a\x5e\x29\xdf\x1e\xf4\x1d\x5e\xdd\xf1\xee\xfa\xbf\xf4\x38\x43\xf8\x84\xff\x98\xb4\x27\xae\xbf\x94\xd0\xb0\x62\x3d\x27\xc0\x7f\x7d\x46\x5b\xfe\x63\x95\x3e\x49\x14\xa3\x7f\x7f\xa9\x7e\xfb\xa5\x5c\x2b\xb6\xa9\x44\x86\x71\xe2\x29\x25\x5f\xf9\xf9\x1f\x98\x8e\x92\x44\x57\xb1\x7a\x57\x39\x61\x52\xc7\xb3\x93\xcf\xb3\x9e\x8b\x2b\xf5\xac\xb7\x04\xf4\xc4\xa9\xd5\xa0\x6e\x11\xa6\xb1\x93\x31\x19\xb9\xb5\x5c\x99\x3b\x7f\xbb\x5d\x8c\xf5\x58\xf5\x5c\xbf\x84\x15\xe6\x32\x3b\x55\x1c\x4b\xc5\x4a\xf3\xa7\x2a\x0f\x0f\x93\x56\xad\xdf\xb1\x7d\x1c\x74\xb8\x86\xe6\xc7\x57\x9f\x6e\xde\x7e\x78\x0f\xcf\xf2\x41\x35\x1b\xf8\x68\x50\x05\x14\xc2\xc2\xe4\x31\x25\x4b\xd5\xcf\x01\x87\x3b\xf4\x12\x04\xae\xaf\xae\xe4\x67\xed\xfc\xfe\x6a\xc3\xca\x9b\x10\xf2\x5c\xeb\xbf\x03\x00\x00\xff\xff\x7c\x8c\xe6\x56\x53\x31\x00\x00") ++var _runtimeHelpPluginsMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x7b\x6d\x8f\xdc\x36\x92\xf0\x77\xfd\x8a\x7a\x3a\x78\xe0\xee\xa0\xad\xc9\xe2\xbe\x0d\xe0\x05\x6c\xc7\x76\xbc\xe7\xd8\x86\xc7\x49\x2e\x08\x02\x88\x2d\x95\xba\x99\xa1\x48\x2d\x49\x4d\x4f\x9f\x91\xfd\xed\x87\xaa\x22\x29\x75\xcf\x64\x37\x77\x87\x0b\x90\x8c\x5a\x22\xeb\x8d\xf5\x5e\xcc\x57\xf0\xd1\x4c\x7b\x6d\x43\x55\x7d\xaf\x5b\xef\x20\x4c\xe3\xe8\x7c\x0c\xd0\x7a\x54\x51\xdb\x3d\x8c\xb2\x00\x8e\x3a\x1e\x40\x41\xd0\xc3\x68\x10\xde\x4d\x0a\xc2\x29\x44\x1c\x6a\x78\x75\x87\xfe\x94\xd6\xc1\x41\x05\x50\xd5\xa0\xb4\x85\xd0\x7a\x3d\x46\x38\x1e\x74\x7b\x00\x1d\xc0\x4f\x16\x54\x84\x10\x95\x8f\xd3\x98\xde\x87\x83\x9b\x4c\x07\x3b\x84\xd1\xa8\x16\x3b\xd0\x16\xaa\xe6\x1f\x57\x75\xeb\x6c\xaf\xf7\x57\x03\x91\x75\x95\x88\x48\x7f\xdf\xab\x01\x17\x8f\xb5\x99\x54\x53\x57\xd5\xe7\x03\x7a\x04\x45\xff\x82\x9d\x86\x1d\x7a\x70\x3d\xb4\xca\x98\x9d\x6a\x6f\xa1\x9f\x6c\x1b\xb5\x23\x56\x18\xf3\xc9\x4d\xd0\x2a\x2b\x9c\x22\xe1\x3d\xb9\xc9\x67\x3e\xa2\xab\x88\xde\xd6\x75\x48\x44\x47\x3d\x60\x00\x17\x0f\xe8\x21\x1e\x94\xcd\x5c\xd4\xf0\xf9\x80\x60\xd5\x40\xa2\x0a\xed\x01\x07\x24\x56\x1b\x67\x9f\x33\xb2\xf5\x9d\xc6\xe3\xa6\xa9\xe1\xb5\xf3\x15\xde\x2b\x16\x9e\x2a\xb4\x9c\x0b\x07\x59\x90\x84\x0a\xe2\x01\x61\x0a\xe8\x21\xa8\x3b\x0c\xfc\x73\x37\xf5\x3d\x7a\x38\x26\x79\x5d\x57\x55\xd3\x34\x66\x52\x55\x01\xe6\xec\x8d\xba\x43\x41\x59\x01\x00\xd4\x75\xcd\x7f\x3d\xc6\xc9\x5b\xe8\x95\x09\x58\xa1\xed\x68\x27\xcb\x0b\x1a\x5a\xdc\xc0\x9d\xf2\x5a\xed\x0c\xd3\xae\xc0\x63\x8f\x1e\x6d\x8b\x10\x1d\xa3\xa6\x45\xfc\xa0\x04\x91\x0e\xb0\x43\xe2\x18\xef\xb1\x9d\x22\x76\xe0\x6c\x5d\x7d\x3e\xe8\xc0\x00\xcc\xe0\x42\x04\x65\x8e\xea\x24\xa4\xb7\x93\xf7\x68\x23\xc3\xd9\x5e\x88\x7f\x8f\x51\x74\xab\x79\x39\xf9\x1f\x35\x1e\xd7\x9b\x06\x54\xa8\x8e\x68\x4c\x5d\x55\xcf\x8d\x01\x75\xa7\xb4\x61\xf2\x54\x3a\x41\x3a\x64\xa3\x43\x14\x7d\x21\x14\xb7\x78\xda\x69\xdb\x69\xbb\x0f\x10\x30\xc9\xa3\xe7\x4f\x07\x34\x63\x5d\x55\xaf\x9d\x2f\xca\x10\x88\xb3\xc1\x4d\xa1\x80\xdc\x32\x41\xac\x3c\x26\x38\xd8\xeb\x3b\x14\xc0\x78\x47\x94\x6b\xdb\xbb\x47\x25\xfe\x3d\x01\xf9\xe8\x31\x84\xb5\x70\xc7\xeb\x45\xfc\xc6\xb5\xca\xc0\xfd\x16\x4e\xf0\x4c\xde\x5f\x7f\x74\x41\xb3\x5e\x6c\xaa\x7f\x7e\x32\x01\x17\x0a\x9b\x8c\x84\x29\x4b\x3b\x14\xec\x9c\x33\x48\x9a\x38\x62\xab\xfb\x13\x1d\xc7\xf1\x80\x49\x43\xd3\x99\xc9\xc6\x6a\x87\xe0\x91\xa8\x21\x81\xa5\x33\x6d\x27\x1f\x9c\x07\xe7\xc1\xba\x08\xaa\x8f\x69\xdf\x7c\xc4\xad\x23\x6d\x8d\x58\x57\xd5\x7b\x17\x49\x27\xc9\x10\x98\xb4\x59\x8e\xae\x6d\x27\xff\x70\x3b\xb9\x81\x1d\xa2\x2d\x40\xba\x1a\xde\xf6\x2c\xe3\xa3\xb2\xb1\x52\xb3\x5d\xee\xb0\x77\x1e\x2f\x70\x67\xc5\xda\x92\x15\x40\x33\x7a\x4c\xf6\x44\xb6\xf4\x96\x4e\x86\x08\x54\x81\xf7\x55\x59\x14\x22\x1b\xec\x92\x4c\x34\x86\x22\x92\xc4\xe7\x02\xcb\xec\x79\x8a\x16\x33\x1b\x15\xad\x31\x93\x12\xdb\xcf\xf4\x07\xd2\x45\x2b\x0e\x60\x0a\xd8\x4f\x66\xe6\x20\x3a\xb8\xb5\xee\x08\x6a\xe7\xa6\x85\xbf\x63\xb9\xda\x99\xa9\xaa\x71\x96\x14\xfc\xc3\x88\xd9\x2f\xcc\x8b\x09\x18\x76\x44\xae\x25\x37\x40\x2e\x0c\x8f\x72\x88\x3a\x80\x1b\x91\xd8\x52\xb6\x63\x0e\x2c\x1e\xab\xfc\x69\x54\x21\xb0\x19\x90\x2b\x12\x03\x4c\xf4\xf5\xce\x43\xc0\xc8\x5e\x5c\x54\xd1\x8d\xa2\x4f\x3b\x15\xd8\x66\x59\x78\xbd\x36\x18\x4f\x23\x6e\x79\x43\xf6\x51\x24\x48\xda\xe8\xfa\x1e\x9a\xa8\x76\x21\xba\x30\xaa\x16\x43\x03\xce\x9a\x13\xaf\x7d\xe3\x80\x36\xb3\x90\x19\xd6\x89\x0c\xa8\x12\x62\xeb\xaa\x7a\xfa\xf4\xe9\x1f\x79\xe5\x59\xb7\x89\xa7\xec\x7e\x82\xe8\x18\x2f\x2e\x36\x1f\x1d\xab\x8d\xb6\xe0\x7c\x47\xc7\xe3\x40\xb5\x2d\x06\x71\x2d\xda\x5a\x76\x8a\xfe\x96\x4d\xdf\xf5\xc0\x11\xa3\x86\xef\x08\x2d\xbb\x33\x72\x14\xb0\xa6\xc5\xc4\x26\x04\xbd\xb7\x2a\x4e\x1e\x03\x31\x51\x2d\x28\xf1\x98\xec\x7e\x0a\xc4\xfa\x1b\xf7\x24\xa4\x2d\x1c\xe9\x36\xd7\x55\xf5\x35\x34\x1f\x6e\x9a\xeb\xd9\x61\xca\x01\xd2\x36\xa1\xe7\xc3\x8d\x10\xc0\x67\x2a\x6e\xcf\x9c\xc8\xbb\x8b\x34\x2d\x11\x22\xa7\x44\xb2\x07\x08\x6a\x40\x50\x41\x90\xbd\xf9\xf0\xe1\xa6\x80\xde\x42\x70\xd0\x74\xca\x1f\xb5\x6d\xb6\xd0\x1c\xb5\xed\xdc\x31\xd0\xa3\xd1\x76\xba\xa7\x87\xde\x23\xee\x42\xd7\xd4\x75\xbd\x61\xe2\x24\x6a\x7e\xab\x7d\x73\x0d\xad\xb3\x51\x51\xe8\x26\xba\x46\x15\x0f\xd9\xf4\x85\x40\x59\x3a\x79\xc5\xea\xc9\x07\xc9\x20\xe8\xb0\x9b\xeb\x2c\x37\xd7\x83\x32\x86\xb7\xd1\x87\x05\x4f\x9a\xe4\x84\x82\x75\xf2\x9f\xd5\xae\xb9\x4e\x27\xd2\xe1\x7d\xf6\xbd\xd9\xf3\xf3\xde\xe4\xaa\xf9\x99\x80\xf3\xde\x01\x43\x40\xbb\x47\xa2\xd8\x60\x0c\x7c\xd8\x01\x6d\x07\xf4\x45\xed\x31\x64\xb2\x39\x1e\x92\x13\x97\x80\x3d\x7a\x37\x8c\x51\x68\x7e\x8f\xc7\x17\x1c\x1e\xd7\x11\xef\xe3\x56\xb8\x0d\xd1\x6b\xbb\xdf\xc0\xd7\xf2\x89\x24\xc2\x3b\x43\x32\xae\x14\x50\x7b\xef\x06\x50\x72\xf2\x15\xbb\x64\xd5\x91\x4a\x49\xbe\x23\x0a\x41\xf0\xce\x11\xbd\xf6\x6e\x78\xad\x0d\xae\xff\x37\xa8\x0a\xd8\x37\x18\xdf\xa1\xa2\xe8\xf5\xd3\x41\x47\x64\x4b\x5b\x6f\xd8\xc5\x37\xd7\xc9\xad\xc9\x39\x1a\x59\x46\x7a\x97\xd6\x65\x51\x17\xa8\x42\x0c\xc3\x7d\x1b\x7e\x72\xbe\x7b\x79\x50\x7e\x1d\xa2\x2f\x64\x5e\xc0\x7d\xc4\x47\xca\x4a\xb1\x9f\x27\x47\xe7\x3b\x02\xdc\x1e\x94\x57\x6d\x44\xff\x84\x81\x7f\x9a\x2c\xde\x44\xbf\xf6\xa4\xde\xb8\x49\x7b\x16\x70\x55\x06\x93\x34\x91\x1e\x0b\xa5\xbc\x89\xe1\xbc\x73\xed\x9a\x83\xa4\xb6\x71\x03\xef\x5c\x7b\x06\x82\xc4\x47\x4b\x1a\x02\x36\xb5\xa2\x34\x3f\x89\xbd\x7f\xab\x3d\xb6\xd1\xf9\xd3\xfa\x51\xec\xde\x39\xf2\xea\x7c\x42\x96\xec\x6c\x0e\x7d\xac\x94\xc9\x6b\x10\x6b\x5d\x86\xc4\xe0\xff\xe6\xb4\xfd\xa8\xe2\x21\xac\x3b\xed\xeb\xba\x2e\x82\x2b\x48\x5a\x37\xec\xb4\xc5\x00\xc3\x64\xa2\x26\x87\x99\x21\x68\x51\x58\xca\xf1\x8c\x39\x3b\xe4\x42\x2d\x25\xad\x67\x7a\xb3\xa4\xda\x18\xd8\x4d\x72\x08\x46\x85\x08\x68\x70\x20\x62\x5d\xcf\x90\xb6\x04\x32\x9e\x46\x4d\x01\xe3\x54\x4c\xfb\x49\xb8\x60\xe1\x0d\xc6\x0f\xec\xed\xd7\xcc\xf9\x43\x44\x9c\x23\x28\x33\x15\xfd\xf1\xf8\xf7\x09\x39\xa5\x92\x30\xc1\x60\x9e\x77\xdd\x43\x30\xdb\xb4\x51\xdb\x88\xbe\x57\x2d\x7e\xf9\x9d\x20\x07\xb2\xde\xf9\x7c\x05\x8a\xd8\x91\xf8\xba\xf4\x41\x36\xaf\x9b\xc5\xf6\x06\x06\x54\x1c\x0c\x4e\xe2\x71\xb5\x85\x37\x4e\x5c\xda\x4d\xe1\x44\x20\x66\xec\x33\x4b\x8f\x23\xce\xb9\x2b\x2d\x96\xd8\x48\x24\x1c\xb5\x31\xb4\x81\xbf\xa5\x95\x7b\xe3\x76\x24\xcd\x2d\x4c\xd6\x50\x60\xd1\x31\x45\x0e\x89\x9a\x14\xef\x64\x69\x9d\x29\x7a\x47\x5f\xfe\x09\x59\x5b\x09\xdd\x5f\xff\xc8\x61\xfe\x8f\x49\xe4\xd3\x2c\x27\xc1\xf8\xc4\xb5\xce\x6b\xc5\x77\x30\xe2\x17\xda\x76\xff\x8e\xa7\xf5\x2d\x9e\xb6\x25\x8d\x29\x62\xa0\xfc\x37\x40\x73\x8b\xa7\x86\x98\x6f\x64\x41\xc3\x3b\xbf\x57\xb7\xf8\xd2\x0d\x83\xb2\x1d\x9f\xe4\x76\x2e\x41\x32\xc1\x29\xd3\xe1\x50\x58\xd7\xf5\xcb\xf2\x93\x60\xb3\xf9\x17\x97\xd6\x0a\xa4\x94\xb7\x13\xbc\x9c\xc9\xb0\x78\x89\x09\x68\x32\x82\x46\xd2\x83\x9c\x64\xd5\xf0\x43\x40\xf8\x86\x00\x52\x0e\xb1\xc4\x1a\x1d\x57\x03\xef\xdd\x8c\xbb\x5e\x52\x9f\xde\xad\x2f\x48\xdf\x34\xd7\xe7\xe4\x35\xf3\x5a\x96\x04\xa5\x90\x42\xea\x42\x0c\x22\x97\xb9\xe6\x38\xb7\x8c\x65\xc5\xc2\x0b\xbf\x53\xb6\x33\x45\x84\xed\xd0\x2d\x4d\x6a\xb2\xcb\xc3\x4d\xd2\xe1\x6d\xaf\xee\xb1\x5d\xca\xbd\x48\x5b\xf9\x7d\x80\x5f\x7e\xcd\x8e\x65\x9d\xdf\xa3\xf7\xce\x13\x50\x92\x17\x28\x58\x87\x03\x1a\xb3\x39\x17\xf9\x99\x39\x29\xbf\x9f\xc8\x43\x84\x1a\x3e\x2d\x39\x90\x0d\x4f\x02\xb8\x29\x8e\x53\xe4\x44\x4b\xc1\xe8\x42\xd0\x94\xb8\x30\xa2\x3a\x3b\xf2\x1b\xc2\xf2\x08\x77\x8f\x10\xf6\x89\xca\x78\x60\xb2\x32\x92\x94\x7a\x4e\x01\xc3\x19\xcb\x0d\x51\x39\xd9\x2e\xd5\x06\x07\xe7\x3a\x76\x6e\x5c\xc8\x74\x0e\x03\x04\x37\x90\x03\xf3\x9c\x70\x91\x3e\x70\x82\x9e\x39\x82\xb5\xae\xb1\x86\xbf\x4f\xec\xc7\xcb\xeb\x0d\x57\xdd\xac\x41\x59\x13\xfc\xff\x98\xf3\x17\xaa\xbd\xdd\x7b\x37\xd9\x8e\x65\x70\x71\xb4\x8f\x30\x9b\xad\x73\x57\x36\x16\x58\x6f\xc9\xa3\x91\xd9\xdd\xe1\x25\xb0\x2d\x1c\x95\x8e\x1c\x79\xb7\xa4\xe5\x1f\x84\x34\xfa\xfd\x27\x85\x4c\xfc\xee\x4e\xd0\x1a\xc7\xc2\x92\x24\x8e\xe8\xc9\xc9\xe5\x82\x77\x71\xcd\x42\x89\x39\x71\xe9\xd5\x10\xfe\x86\x13\x4f\x3f\xe1\x96\x04\xc2\x09\x94\x98\xec\x8e\xc5\x39\x85\x5c\x04\x49\x60\xf1\x8e\xf3\x6c\xbc\xd7\x91\xad\x73\xf4\x52\x0d\x73\x16\x87\x7e\xd0\x56\x19\x49\x6a\xf4\x30\x60\xa7\x55\x44\x4a\x76\x31\x17\x0f\x54\x64\x13\x99\x5b\x0a\x6b\xee\x98\x62\x6d\xc9\xe5\xa2\x9b\x3b\x0b\xe9\xa8\x52\x2c\x4a\x98\x85\xf0\x22\xad\x05\xf5\xe7\xe7\x1c\xa2\xb2\x9d\xf2\x5d\x82\x52\xfc\x3c\xd7\xbd\x52\x12\xd6\x30\xd7\xb0\xfa\x0f\x80\xb2\x32\x2e\x24\x97\x31\x04\x18\xd4\x89\x53\xa3\x1d\x1e\xd4\x1d\x8b\xca\x3a\x3f\xb0\xaf\xde\x61\xab\xb8\x46\xd5\x41\xc5\x78\x6a\x04\xf3\xb2\xae\x2f\xea\xf1\x19\xfd\xf0\x6a\x98\x8c\x8a\xce\xff\x39\xd5\xd8\xce\x45\x66\x36\x48\x56\x91\xe6\x1a\x6e\xa4\x70\x20\x5a\x1e\x53\xbd\x06\xf0\xbe\xc5\x31\x9e\x6b\x85\xf4\x99\xc8\x87\x24\x25\xce\x7e\x2e\x8c\x86\x82\x1e\x29\x5d\x39\x58\x4c\xa4\x72\x0f\x89\x9b\x45\x99\x98\x06\xb4\x25\x12\xf5\x32\xc7\x5b\x54\xc3\x8a\xeb\xe7\x8b\x26\x57\x3e\x91\x45\xa5\xbb\x3c\x6b\x42\xc2\x8a\x26\x2d\xb5\x47\xf5\xa1\xc0\x18\xbd\xbb\xd3\x1d\x29\xab\x98\x7c\xaf\x7d\x10\x3b\xe7\x60\x9d\x3d\x45\x49\xf8\x12\x6d\x84\x63\xad\x8b\xca\x03\x0e\x63\x3c\x3d\xa2\x0e\x7c\x6c\x1b\xe6\x7b\xd9\xf9\xa0\x2f\x89\x27\x65\x34\xed\x0c\x82\x2f\x75\x4d\xa5\xa6\x66\x2d\x72\x23\x7a\xe9\x9f\x4a\xed\x18\x60\xcd\x45\xdb\x16\xa4\xa0\xdb\x42\xe7\xd5\xde\xd9\xde\x9c\x38\xa3\xa3\x9a\x79\x17\xba\x2d\xa4\x8a\x6e\x23\x61\x92\x88\x6f\x92\xda\xdc\x64\x24\x0d\xac\x03\x22\xec\xd0\xb8\xe3\xa6\xb4\x81\xa2\x83\x0e\xe5\xec\x90\x58\x5a\x9c\x2e\x17\x05\x4c\x06\x51\x5c\x88\x15\xbd\x7c\x00\xfd\x1a\x5e\x3c\xec\x2c\x25\x80\x0f\x74\xe3\x0c\x60\xea\x28\xc0\x1d\xfa\x20\xfd\x37\xc2\x2c\x8e\x4a\x0c\x2f\x64\x67\x95\x70\x3b\x2a\x4d\x3e\xba\xb0\x36\xae\xa5\xcc\x7f\x4b\xd9\x4e\xae\xa2\x36\x64\x8b\x97\x71\x39\x97\x20\xe4\xce\x75\xee\xf2\xa9\x39\x1a\x72\x21\xc1\xad\xae\x9c\xc2\x7e\x42\xe3\x14\xb1\xb5\xfe\x84\x1b\x7a\x94\x3e\x2b\x99\x80\x14\x49\x2f\x4e\x11\x3f\xf4\x7d\xc0\xf8\x4f\xc9\xc0\x7b\xd5\x52\xdd\x6b\xf4\x2d\x2e\x48\x2f\x86\x46\xf6\xc3\xa2\x6e\xdd\x44\xb1\x6b\x77\xa2\x6c\x44\xdb\x10\x51\x75\x44\x25\x15\x3c\x21\xd5\x17\xbb\x9b\x51\x1d\x2d\x39\x81\xf7\xcb\xac\xa0\x1d\xba\xe7\xcb\xc4\x60\x0b\xce\xde\xc4\xce\x4d\x31\x3d\xa1\xf7\xf4\xf4\xea\x5e\xc7\xb2\x89\x7c\x29\xa7\x13\x75\x5d\x9f\x65\x44\x37\x51\xf9\x18\xce\xe2\x43\x2a\x67\x93\x39\x3d\x8c\x64\xd0\x64\x84\x4d\x7a\x44\xef\x1b\x48\xe1\xa7\x11\xcc\x0d\xb7\x4a\xce\x7a\xa9\x4b\x7b\x0f\xff\xc2\xe0\x4b\xa1\x9a\xbb\xb9\x07\x35\x8e\x58\x12\xf6\x99\x9a\x39\x0c\x34\x99\x47\x41\x7d\x9e\x24\x30\xb6\xe2\xf3\x29\x83\x4c\x3d\xb1\x0b\xeb\x9f\x09\x2c\x87\x40\x02\x3a\xf3\xc4\xff\x27\xe2\xfe\x57\xe9\xc3\x99\x93\x41\x51\x21\x59\x9b\x92\x66\xf6\x49\xec\xe2\x28\x55\xc2\x8e\xf2\x80\x9c\x1d\xb0\x58\xa5\x61\x25\x4d\xd1\x9c\x92\xbd\x25\x7b\x13\xaf\x9a\xda\x9b\x92\x03\x87\x03\x3c\x6d\x9b\xba\xc8\x00\x53\xc6\xf7\x35\xad\xab\x5f\x0e\x1d\xf9\xa8\xa8\xce\xea\x2b\x4e\xa0\x72\x4f\xc0\x26\xb9\x86\xd8\xe9\xd2\x64\xff\xcd\xed\xb8\xd8\x4d\xce\xbc\x08\xd8\x8d\xe7\xc0\x09\xde\x2d\x9d\x93\xe2\x2d\x32\x34\xa0\xf0\x1a\x10\x07\xb1\x2d\x05\x81\x42\xeb\xdc\x7f\x2a\xad\xc0\x59\xc3\x28\x8b\x14\x47\x2b\xe8\xdd\xee\x37\x6c\x63\xa8\x4a\x07\x78\x27\xb5\xf1\xbc\x83\x02\x37\x0c\x54\x60\x0e\x18\x0f\xae\x23\xad\x5a\x4c\x1c\xe6\xae\x01\xa7\x24\x02\xaf\x12\x45\xe6\x99\x56\x6a\x82\xa9\xc7\x67\x48\xca\x18\x9e\xf1\xe4\xfe\x69\x35\xc3\xbe\xe6\xa1\x8c\x84\x94\xa6\x86\x9f\xd3\x16\x72\xe1\x42\xe1\x82\xd5\x92\xa3\x66\x34\xa2\x2c\xd5\x72\xba\x71\x40\x33\x42\x74\xa3\x6e\x25\x52\xe6\x00\xe0\xec\x03\x22\x69\x4b\xcb\x45\xd1\x79\x8b\x67\x6e\x71\x17\x1b\x9e\xbb\xe1\x7e\xb2\x35\xbc\x4d\x73\x26\x8f\xa4\x4f\x74\xfe\x7b\xb4\xe8\x39\xe3\x09\x51\xb7\xb7\x29\xbd\x93\xc1\x05\xeb\xe0\xa0\xf8\xa5\x9a\x27\x68\xa0\xee\x9c\x66\x18\x93\x0f\x94\x4f\x8d\xde\xed\x0c\x0e\xe1\xbc\xd5\xac\xfb\x24\x47\x63\x1e\x88\x8d\x12\xb3\x0d\xa5\x1b\xec\x8d\xe8\x0d\xc9\xf0\x6f\x53\x88\x32\x18\x78\x5c\xca\x10\x24\xe0\x54\x14\xeb\x9d\x7d\x12\x53\x49\x5a\x40\x80\xda\x2b\x4d\xc5\xe5\x0f\x21\x9b\xea\xe2\xd8\xb7\xe5\x5c\xb9\x50\x59\x74\x9b\x53\x1f\x4f\x85\xe0\x5a\xad\x8a\x4d\xb1\xe9\x72\xa9\xb8\x3b\x25\x5b\x9c\x29\xab\x5f\x4c\x7d\x93\xa7\x5e\xa5\xc3\xb9\x00\xda\xbc\xd6\x06\x3f\x9f\x46\x6c\xb6\xd0\x7c\x54\xf1\xd0\x6c\xab\x86\xc2\x42\x53\xd7\x32\xd6\x9c\xf5\x22\xe9\xee\x23\xea\x57\x7c\x00\x2e\x7b\xaa\x73\xa3\x5a\x79\xbc\x3e\x6f\xb8\xd6\xdf\x4b\x83\x75\x3d\x84\x3d\x39\xb3\xb3\x76\xce\xf9\xca\x57\x94\x74\xfe\x89\x75\x3f\x63\x78\xef\x3e\x72\x5d\xb1\x4e\xe5\x45\x29\x24\x39\x9f\xe5\x7a\xe7\x62\xd3\xd9\xfa\x2d\x1c\x74\x88\xce\x9f\x3e\x73\xe3\xfd\x41\x6f\x82\x5f\x2f\x7a\x13\x73\xed\xf4\x18\xe8\xe7\x5d\xf7\xce\xed\x1f\x27\x9c\x05\xfb\x7c\x8a\x2e\x8f\x78\x38\x99\x91\x73\x61\x59\xdf\x69\x25\xe2\x9c\x8f\xf2\x65\x5e\x3a\x0b\xb6\x86\xcf\x0e\x78\x28\xce\x19\x27\x29\xcd\x19\x4c\xf2\x1d\xc9\x24\x58\xfe\xd2\x48\x10\xc0\xce\x7e\x9a\x2c\x36\x69\xc6\xc8\x19\x5c\xab\x3b\x9c\xe7\x78\xae\xb4\xaa\x9a\x82\xba\x7e\xce\x59\x7e\x03\xbd\x51\x6c\x83\x64\x23\x70\x95\x6a\x0d\xa8\x00\xbe\x96\x72\xbc\x75\x36\x7a\x67\xc2\xd9\x58\xb0\xd3\xca\xb8\x3d\xec\x1c\xb7\xe8\xf3\x40\x48\x07\xe8\x74\x18\x8d\x3a\x51\x5a\xf8\x35\x3c\x27\xbd\x7f\x04\xf5\x7f\x70\x2a\xb0\x7c\xf3\xf3\x2c\x89\x90\xda\x00\xa5\xd7\xb5\xc3\xbd\x4e\x73\x8f\x3e\xc5\x6c\x54\x39\x39\x28\xee\x06\xd3\xe0\x9f\x62\x5a\xe9\x8c\x05\x34\xd8\x92\x7d\x71\x75\xc9\x7b\x17\x52\x65\x12\xbb\xae\x90\x1f\xdd\x4c\x52\x2d\x5d\xb9\x40\x9a\x70\x93\x39\x10\x91\xbd\xb5\x1d\xde\x37\xf0\x54\x02\x65\x21\xf3\xa0\xf7\x07\xa3\xf7\x07\xec\x4a\x5f\xf3\x01\x46\xf6\xd0\x75\x55\x66\xdc\x68\xdd\xb4\x97\x81\x8a\xd7\xfb\x7d\x11\x2e\xcb\xb0\x04\x25\xe1\x85\x9d\x3b\xf7\x20\xa8\x08\x86\x69\xbc\xea\xdc\x31\xb3\xb8\x98\x53\x27\x6e\xea\xaa\xfa\xea\xab\xaf\xbe\x92\xa4\xa0\xaa\xde\xb8\x45\xab\x6e\x1e\x5d\xb1\xf7\xab\xe5\x38\xc8\x93\x4b\xef\xe5\x9a\x5c\x1f\x8f\xa9\xf7\xae\x7a\x68\xe8\x1b\x19\x2f\xa7\x74\xda\x95\x79\x76\x59\x79\x7d\xb1\x72\x31\xb2\xe5\x04\x20\x95\xd9\xd9\x54\x7f\x4b\x7e\xf8\x11\x40\x17\x46\xbd\x5a\x6d\xe1\x9b\x04\xf4\x5b\xdc\x4d\x7b\x8a\x44\xc6\xed\xf7\xa4\x1d\xcb\x7b\x17\xe4\xce\x76\x08\x9d\xb3\xa9\x67\xc7\x65\x0e\xb3\x98\xe3\x45\xeb\x3a\xaa\xa9\x1f\xa0\x4c\xc6\xbe\x4a\x3c\xc0\xde\x21\x45\x4b\x8f\xb0\xda\x0a\xf4\x1f\x93\xa6\x7e\x76\x1f\xbd\xb6\xf1\x3b\xf4\x98\x19\xb5\x20\x17\x60\xd8\xe4\xf0\x8c\xa4\x4c\x66\x2a\x49\x47\x4f\x8e\xbb\x79\x19\xbd\x79\xd5\xd0\xc1\x59\x69\x59\x37\xc6\xed\x9b\x2d\x28\xa8\xf2\x06\x99\xd8\x49\x5a\x4a\x05\x9e\x28\x81\x3d\x15\x88\x81\xcc\x9e\x35\x7c\x89\x2f\xe7\xcd\xc5\x22\x2b\x62\xa2\xae\x48\x2f\xe0\xb9\xf8\xa7\xe4\xe9\xdf\xb8\xf9\x50\x8c\xde\x79\xe5\x4f\x55\xc5\x89\xdf\x1c\x32\x28\xd4\x32\xf8\x32\xd3\x9e\x27\xa8\x9c\x0f\x25\xf5\x9b\x53\xa5\xa4\xff\x6f\x5c\x75\x09\xbc\xae\xaa\x1b\xf2\x75\x27\xf2\x78\xce\xc7\x34\x7f\x68\x6f\x49\xde\x27\x37\x3d\xe9\x52\x0a\x27\x83\x6a\x5b\x22\x14\xa9\xab\x8e\x67\x29\xd2\x7c\x9d\x42\xda\xec\xda\x4d\x51\x1b\x78\x96\x40\xaf\x57\xda\x5d\xc9\xbb\xd5\x26\x2d\xe9\x87\xb8\xf8\xde\x0f\x71\xb5\xa9\xd2\x27\x4a\x59\xb9\x7f\x46\x0b\x78\x57\xfd\x09\x55\xc7\x23\xba\xd5\x8d\x1b\x90\x9e\xea\x78\xcf\x5b\x74\xcf\x2b\xff\xf1\x0c\xac\xe6\x8c\x89\xcb\x11\x98\x55\x49\xe2\xdd\x8a\xff\xf0\x40\x90\xdb\x93\xda\xe0\x35\x5c\xc0\x42\xca\x7f\x68\xf3\xd3\xa7\xf0\x2d\xa5\xcd\x54\xe3\xe6\x24\x94\x7c\xbf\x05\xe5\xbd\xf8\x04\xae\x08\xf3\x62\xc9\x3c\x6e\x46\x52\xc4\x3e\xf5\xcd\x9d\xbd\x43\xcf\x95\x24\x37\x51\xd2\x20\x6f\xbe\x4a\x12\x22\xb1\xd7\x0f\xb1\x4e\xfb\xd6\xab\xff\x1f\x56\x92\xaf\xa7\x0b\x25\x44\x86\xe3\xd4\x98\x0b\xdc\xd2\x2f\x66\xea\xf9\x38\xd8\x68\x89\xa7\xff\x97\x37\x50\x9a\x51\xee\x9e\x7c\x97\xc7\xf5\x8b\xa3\x0d\xb3\x1b\x7e\x44\xe3\x24\xfd\x29\x29\x13\x6b\x56\x5d\xbd\x47\xe5\xcd\x89\x53\xd2\x59\xb3\x32\x98\xb0\x00\x4d\xb8\xe6\x9e\x04\x6b\x88\xb2\x52\x6d\x57\x39\x2f\x96\x98\x71\x3e\xaa\x9f\xdb\x0e\x19\xb5\x71\xee\x16\xe2\xc1\xb3\x6b\x26\xcd\xaa\xf7\xae\xa9\xd6\x97\x17\xcd\x50\x85\x13\x8f\x05\x6c\x87\x9e\x99\xd9\x88\x3b\xa9\xfa\x21\x56\xda\x55\x45\xf1\x2a\x8b\xb1\x1a\x54\x3c\xf0\x7f\xae\x3c\x95\xc1\x2e\x54\x7e\xb2\x51\x0f\x58\xf1\x64\x8f\xe4\xca\x0f\x72\x5a\x54\x82\xec\xf1\x7e\xac\xb8\x4f\x17\x2a\x5e\xc8\x82\x25\xc6\x3a\xd7\x72\xd1\x9a\x66\xee\x64\x0c\xaa\x3d\x24\x0b\x5c\x5e\xf9\x99\x53\xd0\x20\xf6\x46\xbc\x55\x99\xb7\x3f\x3a\x88\x33\xf0\xb5\xf8\x8b\x8e\x75\x97\x8b\x06\x9e\xf1\x6f\x21\x9c\x6c\x54\xf7\xf9\x17\xcf\x60\x8c\xf3\x72\x79\x2d\x5c\xdc\x83\xab\xaa\x9f\x17\x16\x2c\xb1\xb4\xeb\x3e\x89\x00\xd8\xb8\x64\x9a\x24\x17\x48\xce\x66\x9a\x73\xd8\x22\x65\xee\xba\x8a\xf2\x04\x37\x05\xb8\xe5\x39\x95\xeb\xd3\xe5\x11\xb9\xdf\x91\x11\x9e\xb9\x88\x6d\xaa\x0d\xb2\x5b\x11\x40\xa0\x98\x9d\x8a\x6b\xa0\x8b\xed\xb9\xd9\xd0\x44\x0c\xb1\x11\x29\x4a\xaa\x91\x6e\x0d\x28\xf9\x54\x0f\x5d\xc3\xf8\xb7\xec\xab\xb8\xf6\x58\x3a\xc1\xd9\x39\x5d\xb0\xbb\xa2\xdd\xab\x2d\xac\x88\x04\xfa\x9b\xa0\xad\x52\x20\xf9\x21\x3c\x10\x51\x78\xed\xdd\x30\x8f\xaa\x97\x02\xeb\xb4\x67\xa9\x45\xf4\x76\xd3\xcc\xfc\x95\x8b\x32\x55\x91\x11\x8f\x68\x05\x28\xa7\x9c\x64\xc1\xa9\xb9\x6b\x63\x1a\x10\xab\xbc\x42\xac\x9d\x22\x73\x43\x5e\x70\xc9\x40\x9f\x8a\x8d\x2d\x9c\x0d\x86\x49\x0f\x9a\x77\x3a\xc4\x25\xe1\x65\x71\x59\x56\x91\xce\x92\x4d\x2f\x11\x85\x1c\x9b\x96\x09\x53\x6e\x61\x94\x3e\x4d\x55\xdd\x70\xc9\xcb\xb7\xbb\xd2\x25\x23\x07\x06\x95\xb7\x70\x70\xc7\x3c\xa8\xbb\x18\xf6\xa5\x84\xf3\x7c\x70\x77\x16\x37\x8a\x9a\x49\xff\xe5\x27\x1d\x0f\xeb\x1b\xa9\x09\xf8\x8d\xdc\xc5\x93\x37\xf0\x2c\x3d\x5c\x4f\xe3\x88\x7e\x9d\xbf\x29\x1f\xf9\x93\xf2\x31\x7f\x81\xe5\xfd\x3c\x61\xbf\x0e\xd3\x2e\x83\xfe\xcb\x36\xbd\x33\x68\xd7\x82\x67\xf3\xec\x19\x3f\xb0\x2f\x9d\xef\x08\x66\x89\xac\xb9\x67\xbe\xbc\x19\xa8\x78\xe4\x56\xc6\x9f\xcf\xe0\xcb\xea\x3b\x34\xc6\x91\x5a\xfd\xe4\xbc\xe9\xe8\xe1\xb5\xe3\xdf\x2f\x94\x5f\xfd\xbe\xd8\xeb\x31\x4c\x86\x88\xfe\xf2\xbb\x38\x7e\x3a\x18\xbd\xbd\x23\xfb\x1d\x95\xf6\x61\x7d\x0e\x7d\x03\x1d\x0f\x9b\xf9\x1f\xdd\x2f\x85\x75\xb7\x95\x76\xfe\x66\x8e\x84\xf9\x9f\xc8\x15\x8e\xb6\x01\x7d\x5c\x0b\xca\x2d\xdc\x6d\xca\x1a\x94\xbe\x60\xf9\x9b\xc4\x25\x2b\x2f\xe4\xd0\x3b\xb7\x56\x7e\xbf\xb9\x08\xb5\x39\xe3\xe4\x4f\xbc\x63\x39\xab\x5e\xf5\xc2\x7e\xd2\x98\x5a\x7e\x5e\xcc\x83\xcb\xd7\x2c\xea\xd5\x26\x59\x23\x69\xe5\xb7\xd8\x2b\x12\xd5\x98\x6f\x3a\x2f\x1c\x0c\xbb\xa1\xf4\x61\x5b\x5a\x32\x5d\xda\xd1\x70\x05\x60\x5c\xc0\xa4\x85\x86\x2b\xc8\xa6\x80\x5a\x53\xa5\x3a\xdf\x41\x95\x99\x50\xb9\x5e\xe5\xb9\xb8\x49\x71\x06\x9a\x64\x31\xf9\x2e\x73\xb3\xa1\xf4\xe0\x88\xc6\x48\x9a\x70\xaa\xf2\x4d\xeb\x72\x07\xee\x1c\xba\xeb\x7b\xdd\x6a\x65\xa0\x3d\x28\x6b\xd1\x54\xbf\x50\x5a\xf8\xeb\xfa\x10\xe3\x18\xae\xaf\xae\xf6\x3a\x1e\xa6\x1d\xc9\x40\x2e\x4d\x3f\xc5\x4e\x47\xe7\x13\xbe\xa7\x69\xd7\x26\xd9\xaa\xdc\xfb\x86\xef\x95\x55\x7b\xf4\xf9\xfa\x37\xf7\x3b\xb8\xdd\x05\xbb\x49\x1b\x9e\x08\x25\xc7\x3a\xc8\xca\x8b\xce\x83\xb6\x77\xee\x16\xe7\xa9\x74\xf3\xd7\xbc\xbe\xae\xeb\xa6\x34\x23\x45\xe8\xe9\xa2\x83\xee\xe6\xc1\xda\x22\x3f\x9c\xe5\xdf\xa4\xcf\xcd\xb2\xd7\x25\xf5\x7a\xa6\xa2\xc7\xd8\x1e\x30\x94\xdb\xe9\x25\x3f\x49\x6c\x06\x58\x97\xab\x95\x29\x82\xce\x17\xda\x52\x68\x1b\x30\x2a\xce\x9c\xf2\x2d\xcc\xc8\xd7\x38\x83\xdc\xe3\xac\xe1\xc5\x29\x6b\xc2\x36\x9d\x29\x0f\x7b\x16\x6b\xce\xce\xa5\x4a\xa8\xe7\x3b\x9d\xf9\xc6\xad\x8a\xf0\xe7\x0e\x87\xdb\x9a\x49\x24\x15\x45\x02\x8e\x6c\x54\x2c\xc6\x83\xf6\xdd\xd3\x51\xf9\x78\x9a\x59\x5c\x74\x7c\x04\x4e\xfe\xd2\xe4\xd2\x93\xb4\x36\xc3\x93\xfb\x41\x3c\xc0\xb0\xb7\x67\x00\x8b\xe2\xb9\x54\xa2\x6a\x1b\xa2\x32\x46\xb2\x94\x65\xd2\x91\x25\x97\x75\x21\x27\x97\x09\x3f\xa9\x7b\x41\x5e\xe7\xf2\x71\x11\xbd\xc7\x69\x67\x74\x38\xcc\x4d\x42\xfa\xcc\x7d\xd9\x0e\x53\xb6\x5c\xd4\x5c\x56\x48\x08\x4f\xf7\x98\xa7\x91\xe7\x37\xcb\x78\xef\xac\xd1\x16\x61\x1d\x1d\xbc\x61\x19\x53\x7d\xd6\xa3\x57\x3b\x73\xda\x48\xc1\xc5\x11\xb5\x21\xda\xea\xdf\x02\x45\x14\x8a\x59\xe9\xba\x3a\xc7\xc9\x9c\x7c\x47\x95\x4c\x2d\x6b\xc6\x5c\x3b\xe5\xe4\xa4\xdc\x2a\xb5\xe7\x65\x0c\x01\xae\x7e\xf9\x52\x01\xac\xde\xab\x01\x57\xd7\xb0\x92\x2d\x14\x64\x57\x5b\x7a\xff\x2d\xca\xff\x10\xa1\x9d\xa5\xcf\xf3\xfd\x54\xab\x5b\x8e\xe2\xad\x0e\xe4\x7b\xca\x2a\xbe\xcc\x9a\x4f\x47\x60\x7c\x56\xfb\xb0\xba\x86\x5f\x56\xe3\x29\x1e\x9c\x25\xcf\x48\x3e\x49\xdb\xfd\xea\x57\x5e\xf0\xa3\x8c\xde\x78\x11\x3b\xda\x2f\xc9\x57\xe7\x2f\x84\xfa\x2f\xf5\x37\xf5\x37\x0c\x90\xbf\xfc\xe0\x0d\xbd\x7d\xc4\x8f\x4c\x01\xb3\x8a\x5e\x29\xdf\x1e\xf4\x1d\x5e\xdd\xf1\xee\xfa\x3f\xf5\x38\x43\xf8\x84\x7f\x9f\xb4\x27\xae\xbf\x94\xd0\xb0\x62\x3d\x27\xc0\x7f\x7d\x46\x5b\xfe\x6d\x95\x3e\x49\x14\xa3\xff\xfe\x5a\xfd\xfe\x6b\xb9\xf0\x6e\x53\x89\x0c\xe3\xc4\xf3\x73\xbe\x8c\xf6\xdf\x30\x1d\x25\x89\xae\x62\xf5\xae\x72\xc2\xa4\x8e\x67\x27\x9f\xa7\x90\x17\xff\xb3\x07\xeb\x2d\x01\x3d\x71\x6a\x35\xa8\x5b\x84\x69\xec\x64\x80\x4b\x6e\x2d\x57\xe6\xce\xdf\x6e\x17\x03\x67\x56\x3d\xd7\x2f\x61\x85\xb9\xcc\x4e\x15\xc7\x52\xb1\xd2\x64\xb4\xca\x63\xed\xa4\x55\xeb\x77\x6c\x1f\x07\x1d\xae\xa1\xf9\xf1\xd5\xa7\x9b\xb7\x1f\xde\xc3\xb3\x7c\x50\xcd\x06\x3e\x1a\x54\x01\x85\xb0\x30\x79\x4c\xc9\x52\xf5\x4b\xc0\xe1\x0e\xbd\x04\x81\xeb\xab\x2b\xf9\x59\x3b\xbf\xbf\xda\xb0\xf2\x26\x84\x3c\x71\xfd\xaf\x00\x00\x00\xff\xff\x99\xf6\x95\xb3\xed\x33\x00\x00") + + func runtimeHelpPluginsMdBytes() ([]byte, error) { + return bindataRead( +--- cmd/micro/settings.go.orig 2018-07-20 00:24:02 UTC ++++ cmd/micro/settings.go +@@ -241,6 +241,7 @@ func DefaultGlobalSettings() map[string]interface{} { + "tabstospaces": false, + "termtitle": false, + "useprimary": true, ++ "autocomplete": false, + } + } + +@@ -279,6 +280,7 @@ func DefaultLocalSettings() map[string]interface{} { + "tabsize": float64(4), + "tabstospaces": false, + "useprimary": true, ++ "autocomplete": false, + } + } + +--- cmd/micro/shellwords/shellwords.go.orig 2018-07-20 00:24:02 UTC ++++ cmd/micro/shellwords/shellwords.go +@@ -87,8 +87,6 @@ loop: + backtick = "" + backQuote = !backQuote + continue +- backtick = "" +- backQuote = !backQuote + } + case ')': + if !singleQuoted && !doubleQuoted && !backQuote { +@@ -102,8 +100,6 @@ loop: + backtick = "" + dollarQuote = !dollarQuote + continue +- backtick = "" +- dollarQuote = !dollarQuote + } + case '(': + if !singleQuoted && !doubleQuoted && !backQuote { +--- cmd/micro/view.go.orig 2018-07-20 00:24:02 UTC ++++ cmd/micro/view.go +@@ -102,6 +102,9 @@ type View struct { + // The scrollbar + scrollbar *ScrollBar + ++ // Autocomplete function ++ Completer *Completer ++ + // Virtual terminal + term *Terminal + } +@@ -151,6 +154,9 @@ func NewViewWidthHeight(buf *Buffer, w, h int) *View { + } + } + ++ // Load the autocompleter, based on the filetype. ++ v.Completer = NewCompleterForView(v) ++ + return v + } + +@@ -506,7 +512,7 @@ func (v *View) MoveToMouseClick(x, y int) { + v.Cursor.LastVisualX = v.Cursor.GetVisualX() + } + +-// Execute actions executes the supplied actions ++// ExecuteActions executes the supplied actions + func (v *View) ExecuteActions(actions []func(*View, bool) bool) bool { + relocate := false + readonlyBindingsList := []string{"Delete", "Insert", "Backspace", "Cut", "Play", "Paste", "Move", "Add", "DuplicateLine", "Macro"} +@@ -595,6 +601,12 @@ func (v *View) HandleEvent(event tcell.Event) { + } + } + case *tcell.EventKey: ++ // See whether the autocomplete should take over the keys. ++ if v.Completer.HandleEvent(e.Key()) { ++ // The completer has taken over the key, so break. ++ break ++ } ++ + // Check first if input is a key binding, if it is we 'eat' the input and don't insert a rune + isBinding := false + for key, actions := range bindings { +@@ -623,7 +635,7 @@ func (v *View) HandleEvent(event tcell.Event) { + + if !isBinding && e.Key() == tcell.KeyRune { + // Check viewtype if readonly don't insert a rune (readonly help and log view etc.) +- if v.Type.Readonly == false { ++ if !v.Type.Readonly { + for _, c := range v.Buf.cursors { + v.SetCursor(c) + +@@ -641,6 +653,12 @@ func (v *View) HandleEvent(event tcell.Event) { + v.Buf.Insert(v.Cursor.Loc, string(e.Rune())) + } + ++ // Allow the completer to access the rune. ++ err := v.Completer.Process(e.Rune()) ++ if err != nil { ++ TermMessage(err) ++ } ++ + for pl := range loadedPlugins { + _, err := Call(pl+".onRune", string(e.Rune()), v) + if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") { +@@ -733,6 +751,10 @@ func (v *View) HandleEvent(event tcell.Event) { + // This is (hopefully) a temporary solution + v.Relocate() + } ++ ++ // Check to see whether the cursor has moved out of the autocomplete range, ++ // and the completer should exit. ++ v.Completer.DeactivateIfOutOfBounds() + } + + func (v *View) mainCursor() bool { +@@ -1075,6 +1097,9 @@ func (v *View) DisplayView() { + screen.SetContent(v.x, yOffset+i, '|', nil, dividerStyle.Reverse(true)) + } + } ++ ++ // Draw the autocomplete display on top of everything. ++ v.Completer.Display() + } + + // ShowMultiCursor will display a cursor at a location +--- runtime/help/plugins.md.orig 2018-07-20 00:24:02 UTC ++++ runtime/help/plugins.md +@@ -185,6 +185,16 @@ The possible methods which you can call using the `mes + * `messenger.Prompt(prompt, historyType string, completionType Completion) (string, bool)` + * `messenger.AddLog(msg ...interface{})` + ++The Autocompleter is accessible via the `CurView().Completer` variable. To implement an autocompleter as a plugin: ++ ++* Handle the `onRune` event to decide whether to set the `Completer.Active` flag to true / false. ++ * This controls whether the dialog box of options is displayed. ++* Also set the `Completer.X` and `Completer.Y` variables. This sets the beginning of the area which would be replaced by an option selected from the autocomplete. ++* Add options to `Complete.Options` ++* Set the `ActiveIndex` - this sets the highlighed value in the autocomplete list. ++ ++This is enough to trigger the display of the option list and allow up/down selection of the options. ++ + #### Note + + Go function signatures use `.` and lua uses `:` so