Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
From ecbec6b49adee2f2fbd24b8cf9d1fd2e64d07e9f Mon Sep 17 00:00:00 2001
From: Derek Schrock <dereks@lifeofadishwasher.com>
Date: Sun, 30 Jun 2019 02:47:27 -0400
Subject: [PATCH] autocomp?
---
editors/micro/Makefile | 2 +-
editors/micro/distinfo | 6 +-
editors/micro/files/patch-autocomp | 2161 ++++++++++++++++++++++++++++
3 files changed, 2165 insertions(+), 4 deletions(-)
create mode 100644 editors/micro/files/patch-autocomp
diff --git a/editors/micro/Makefile b/editors/micro/Makefile
index 7b4f71e01f44..d9d95508a5ec 100644
--- a/editors/micro/Makefile
+++ b/editors/micro/Makefile
@@ -35,7 +35,7 @@ GH_TUPLE= \
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 \
diff --git a/editors/micro/distinfo b/editors/micro/distinfo
index 1e7e44e76880..03a87810c07f 100644
--- a/editors/micro/distinfo
+++ b/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 @@ SHA256 (zyedidia-poller-ab09682_GH0.tar.gz) = 8a6d6dcb4c4e34d37d991c1ba79b5cedec
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
diff --git a/editors/micro/files/patch-autocomp b/editors/micro/files/patch-autocomp
new file mode 100644
index 000000000000..7b7d64dc9c8e
--- /dev/null
+++ b/editors/micro/files/patch-autocomp
@@ -0,0 +1,2161 @@
+--- 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
--
2.22.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.