From e3ae38e54a71d7cb3ae663ac9ca9339c981b6dff Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Fri, 2 Aug 2019 14:48:59 -0700 Subject: [PATCH] Autoclose plugin support --- cmd/micro/initlua.go | 13 + internal/action/bufpane.go | 36 +- internal/buffer/buffer.go | 6 + internal/config/plugin.go | 28 + internal/config/runtime.go | 2 +- internal/lua/lua.go | 24 + internal/util/lua.go | 41 + runtime/plugins/autoclose/autoclose.lua | 1125 +---------------------- 8 files changed, 179 insertions(+), 1096 deletions(-) create mode 100644 internal/util/lua.go diff --git a/cmd/micro/initlua.go b/cmd/micro/initlua.go index 91e56e68..269fbe9d 100644 --- a/cmd/micro/initlua.go +++ b/cmd/micro/initlua.go @@ -11,6 +11,7 @@ import ( ulua "github.com/zyedidia/micro/internal/lua" "github.com/zyedidia/micro/internal/screen" "github.com/zyedidia/micro/internal/shell" + "github.com/zyedidia/micro/internal/util" ) func init() { @@ -24,6 +25,8 @@ func LuaImport(pkg string) *lua.LTable { return luaImportMicro() case "micro/shell": return luaImportMicroShell() + case "micro/util": + return luaImportMicroUtil() default: return ulua.Import(pkg) } @@ -52,3 +55,13 @@ func luaImportMicroShell() *lua.LTable { return pkg } + +func luaImportMicroUtil() *lua.LTable { + pkg := ulua.L.NewTable() + + ulua.L.SetField(pkg, "RuneAt", luar.New(ulua.L, util.LuaRuneAt)) + ulua.L.SetField(pkg, "GetLeadingWhitespace", luar.New(ulua.L, util.LuaGetLeadingWhitespace)) + ulua.L.SetField(pkg, "IsWordChar", luar.New(ulua.L, util.LuaIsWordChar)) + + return pkg +} diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index 669e2de4..fcc15bba 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -124,6 +124,27 @@ func NewBufPaneFromBuf(buf *buffer.Buffer) *BufPane { return NewBufPane(buf, w) } +// PluginCB calls all plugin callbacks with a certain name and +// displays an error if there is one and returns the aggregrate +// boolean response +func (h *BufPane) PluginCB(cb string) bool { + b, err := config.RunPluginFnBool(cb, luar.New(ulua.L, h)) + if err != nil { + screen.TermMessage(err) + } + return b +} + +// PluginCBRune is the same as PluginCB but also passes a rune to +// the plugins +func (h *BufPane) PluginCBRune(cb string, r rune) bool { + b, err := config.RunPluginFnBool(cb, luar.New(ulua.L, h), luar.New(ulua.L, string(r))) + if err != nil { + screen.TermMessage(err) + } + return b +} + func (h *BufPane) OpenBuffer(b *buffer.Buffer) { h.Buf.Close() h.Buf = b @@ -236,14 +257,21 @@ func (h *BufPane) DoKeyEvent(e Event) bool { for _, c := range cursors { h.Buf.SetCurCursor(c.Num) h.Cursor = c - if action(h) { + if !h.PluginCB("pre" + estr) { + // canceled by plugin + continue + } + if action(h) && h.PluginCB("on"+estr) { h.Relocate() } } return true } } - if action(h) { + if !h.PluginCB("pre" + estr) { + return false + } + if action(h) && h.PluginCB("on"+estr) { h.Relocate() } return true @@ -277,6 +305,9 @@ func (h *BufPane) DoRuneInsert(r rune) { for _, c := range cursors { // Insert a character h.Buf.SetCurCursor(c.Num) + if !h.PluginCBRune("preRune", r) { + continue + } if c.HasSelection() { c.DeleteSelection() c.ResetSelection() @@ -289,6 +320,7 @@ func (h *BufPane) DoRuneInsert(r rune) { } else { h.Buf.Insert(c.Loc, string(r)) } + h.PluginCBRune("onRune", r) } } diff --git a/internal/buffer/buffer.go b/internal/buffer/buffer.go index b7583799..9082db3b 100644 --- a/internal/buffer/buffer.go +++ b/internal/buffer/buffer.go @@ -6,6 +6,7 @@ import ( "errors" "io" "io/ioutil" + "log" "os" "path/filepath" "strconv" @@ -293,6 +294,7 @@ func (b *Buffer) SetName(s string) { func (b *Buffer) Insert(start Loc, text string) { if !b.Type.Readonly { + log.Println("INSERT", start, text) b.EventHandler.cursors = b.cursors b.EventHandler.active = b.curCursor b.EventHandler.Insert(start, text) @@ -747,3 +749,7 @@ func ParseCursorLocation(cursorPositions []string) (Loc, error) { return startpos, err } + +func (b *Buffer) Line(i int) string { + return string(b.LineBytes(i)) +} diff --git a/internal/config/plugin.go b/internal/config/plugin.go index e7976c6a..8b9d93e3 100644 --- a/internal/config/plugin.go +++ b/internal/config/plugin.go @@ -17,6 +17,7 @@ func LoadAllPlugins() { } // RunPluginFn runs a given function in all plugins +// returns an error if any of the plugins had an error func RunPluginFn(fn string, args ...lua.LValue) error { var reterr error for _, p := range Plugins { @@ -31,6 +32,33 @@ func RunPluginFn(fn string, args ...lua.LValue) error { return reterr } +// RunPluginFnBool runs a function in all plugins and returns +// false if any one of them returned false +// also returns an error if any of the plugins had an error +func RunPluginFnBool(fn string, args ...lua.LValue) (bool, error) { + var reterr error + retbool := true + for _, p := range Plugins { + if !p.IsEnabled() { + continue + } + val, err := p.Call(fn, args...) + if err == ErrNoSuchFunction { + continue + } + if err != nil { + reterr = errors.New("Plugin " + p.Name + ": " + err.Error()) + continue + } + if v, ok := val.(lua.LBool); !ok { + reterr = errors.New(p.Name + "." + fn + " should return a boolean") + } else { + retbool = retbool && bool(v) + } + } + return retbool, reterr +} + type Plugin struct { Name string // name of plugin Info RuntimeFile // json file containing info diff --git a/internal/config/runtime.go b/internal/config/runtime.go index 236f7758..8ed43a0a 100644 --- a/internal/config/runtime.go +++ b/internal/config/runtime.go @@ -1031,7 +1031,7 @@ func runtimeHelpTutorialMd() (*asset, error) { return a, nil } -var _runtimePluginsAutocloseAutocloseLua = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\xff\x77\xda\x3a\xf2\xe8\xef\xfc\x15\x73\xd3\xed\x01\x6e\x6c\x8a\x81\xdb\x92\xdc\xd2\x77\x28\x21\x2d\xe7\xa5\x21\x0f\xc8\xed\x76\xb3\xb9\x5d\x63\x8b\xe0\xd6\xd8\xac\x2d\xd2\xf4\xf5\x74\xff\xf6\x77\x34\x92\x6c\xc9\x5f\x80\xf4\xdb\xe9\xdb\xcf\xcd\x0f\xc4\x96\xa5\xd1\x7c\xd3\x68\x34\x1a\xcb\xa6\x09\x7f\x1b\xb9\xc7\xb0\xa1\x8b\x6e\xc3\xdf\xd8\x60\x3d\x39\x82\x56\xb3\x79\x64\x36\x3b\x66\xb3\x0d\x56\xf7\xd8\x6a\x1e\x37\xdb\xff\x80\xb5\x1d\x53\x1b\xfe\x56\x31\xcd\x8a\x69\xc2\x45\x14\xde\x7a\x2e\x89\xe1\x72\x76\x6a\x76\xc1\xfe\x60\x47\x04\x62\x1a\x79\xc1\x0d\x2c\x36\x81\x43\xbd\x30\x88\xc1\x5b\xad\x7d\xb2\x22\x01\x25\x2e\x78\x01\xac\x37\x11\x01\x7f\x63\x1f\x33\x08\xbf\x62\x9f\x3e\x09\x6a\x71\x3d\xbd\x8f\x37\xf3\x5a\x6c\x80\x67\xc0\x3b\xa5\x34\x22\xb7\x24\x8a\x89\x56\xd3\x59\xda\x51\x6d\x13\x78\x4e\xe8\x12\xa5\x58\x94\x14\x00\xb9\x21\x81\x80\x1e\x6f\xe6\x6f\x7d\x12\x28\xcf\x16\x5e\xe0\xd6\x62\x1a\x19\x10\x91\x1b\x72\x67\x80\x17\x78\xd4\x80\xb5\x6f\x7b\x6a\xb5\x95\x4d\x9d\x65\xae\x9e\xda\x47\xbe\x86\xed\xfb\x6a\x05\x44\x41\x79\x1c\x91\xb5\x6f\x80\xef\xad\x38\x1c\x56\x73\xb4\xc0\xaa\xae\x4d\x6d\x14\x49\xcd\x09\x03\x6a\x7b\x01\xe3\x2d\x5d\x12\xf0\xc3\x0f\x24\x7a\x6a\x3e\xdb\xac\xd7\x24\x02\xc7\x8e\x09\xac\xec\xf5\xda\x0b\x6e\xe2\x3a\x78\x31\xf8\xa1\xed\x12\xd7\x60\x75\x63\xc2\x00\xda\xae\xeb\x31\x81\xd8\xbe\x22\x1b\x26\x30\xfb\xd6\xf6\x7c\x7b\xee\x13\x45\x22\x08\x55\xe3\x34\xf6\xc7\x4b\x58\x61\xdf\x57\xc1\xcc\xc9\xd2\xbe\x25\x60\xc7\xac\x3b\x2f\x82\x20\x0c\x34\x9d\x70\xc2\x4d\x40\x49\xb4\xb6\x23\x1a\xc3\x07\x8f\x2e\x91\x04\x72\xe7\x90\x35\x03\xc0\x00\xd2\xa5\x4d\x45\x1b\x26\x54\xdb\xa1\x24\xe2\xf8\x6d\x62\x54\x9c\x98\x12\xdb\x85\x70\x01\xf3\x8f\x94\xc4\xb0\x08\x23\xc6\x55\xd8\x04\x1e\x8d\x1b\x95\x8a\x69\x5e\x5d\x55\x06\xe1\xfa\x63\xe4\xdd\x2c\x29\xd4\x9c\x3a\xd3\xe0\xc7\x66\xab\xd9\x7c\x62\xc0\xff\xfe\xe8\x13\x98\xae\x3c\xba\xac\x30\xcc\xb1\x4e\x0c\x11\x89\x49\x74\x4b\xdc\x46\xa5\x32\x08\x03\x1a\x79\xf3\x0d\x0d\xa3\xf8\xb8\x02\x00\xd0\xf7\xbd\x55\x78\x0b\x53\x4a\xd6\x76\x50\xa9\x4c\x88\xeb\xc5\xbc\x8a\x17\x06\x60\x07\x2e\x43\x8c\x29\x74\x1c\x6e\x22\x87\x60\xc9\xdc\x0b\xec\xe8\x23\xc3\x6d\x15\x1b\x9c\xd0\x30\xc2\xff\xe1\x86\x56\x56\xa1\xeb\x2d\x3c\xc7\x66\x00\x0c\x24\x6d\x4d\xa2\x95\x47\xd9\xc0\x58\xf3\xa1\xe4\x72\x3e\x30\xee\x2c\x42\xdf\x0f\x3f\x30\x71\x3b\x61\xc0\x45\xc7\xf9\xb1\x22\xf4\xb8\x82\x28\xfe\x0a\x3a\x56\x31\x63\x8f\x40\x87\xe9\x3f\xac\x36\x31\x85\x88\x30\xbd\x41\x98\xf6\x3c\xbc\x65\x8f\x24\x97\x82\x90\x7a\x0e\x31\x10\x18\x00\x5d\x32\xbd\xf1\x62\xca\xc0\xa8\x9d\x06\x6e\x06\x23\xd7\x8b\x1d\xdf\xf6\x56\x24\x6a\x94\x20\xe2\x05\x2a\x33\x24\x22\xeb\x28\x74\x37\x0e\x29\xc2\x45\xe0\x20\x30\xfa\x22\x5c\x80\x53\x29\x20\xb9\xa1\xb3\x61\x56\xc7\x96\xf2\x7a\x14\x46\x10\xd2\x25\x89\x60\x65\x53\x12\x79\xb6\x1f\xa7\x6c\x4f\x94\x52\x25\x43\x12\x77\x4e\x3c\x6c\xc7\x9e\x07\xf6\x8a\x30\x9c\x90\x84\x0d\x5d\x86\x4c\xdb\xd3\x47\x28\x02\x8f\xc6\x0c\xe7\x44\xa1\x60\x65\x7f\x84\xb9\x44\x0c\xf5\x99\x86\x40\x02\x37\x8c\x62\xc2\x34\x64\x1d\x85\xab\x90\x12\xe0\xfc\xa1\x31\xb8\x24\xf2\x6e\x89\x0b\x8b\x28\x5c\x71\x5e\xc4\xe1\x82\xe2\x58\x92\xda\xc4\x81\xc5\x6b\xe2\x30\xa5\x82\x75\xe4\x31\x55\x8b\x98\x3a\x05\x5c\xb1\xe2\x18\x69\xa8\xcc\x5e\x8e\xa6\x30\x1d\x9f\xce\x5e\xf7\x27\x43\x18\x4d\xe1\x62\x32\xfe\x63\x74\x32\x3c\x81\xe7\x6f\x60\xf6\x72\x08\x83\xf1\xc5\x9b\xc9\xe8\xc5\xcb\x19\xbc\x1c\x9f\x9d\x0c\x27\x53\xe8\x9f\x9f\xc0\x60\x7c\x3e\x9b\x8c\x9e\x5f\xce\xc6\x93\x29\x1c\xf4\xa7\x30\x9a\x1e\x54\xd8\x83\xfe\xf9\x1b\x18\xfe\xfd\x62\x32\x9c\x4e\x61\x3c\x81\xd1\xab\x8b\xb3\xd1\xf0\x04\x5e\xf7\x27\x93\xfe\xf9\x6c\x34\x9c\x1a\x30\x3a\x1f\x9c\x5d\x9e\x8c\xce\x5f\x18\xf0\xfc\x72\x06\xe7\xe3\x19\x9c\x8d\x5e\x8d\x66\xc3\x13\x98\x8d\x0d\xd6\x69\x25\xdf\x0c\xc6\xa7\xf0\x6a\x38\x19\xbc\xec\x9f\xcf\xfa\xcf\x47\x67\xa3\xd9\x1b\x44\xe4\x74\x34\x3b\x67\x7d\x9d\x8e\x27\xd0\x87\x8b\xfe\x64\x36\x1a\x5c\x9e\xf5\x27\x70\x71\x39\xb9\x18\x4f\x87\xd0\x9f\x0c\x2b\x27\xa3\xe9\xe0\xac\x3f\x7a\x35\x3c\x69\xc0\xe8\x1c\xce\xc7\x30\xfc\x63\x78\x3e\x83\xe9\xcb\xfe\xd9\x59\x86\xca\xf1\xeb\xf3\xe1\x84\xa1\xae\x91\xf8\x7c\x08\x67\xa3\xfe\xf3\xb3\x61\x05\x3b\x3a\x7f\x03\x27\xa3\xc9\x70\x30\x63\xd4\xa4\x57\x83\xd1\xc9\xf0\x7c\xd6\x3f\x33\x60\x7a\x31\x1c\x8c\xd8\xc5\xf0\xef\xc3\x57\x17\x67\xfd\xc9\x1b\x43\xc0\x9c\x0e\xff\xcf\xe5\xf0\x7c\x36\xea\x9f\x55\x4e\xfa\xaf\xfa\x2f\x86\x53\xa8\xed\xe0\xc8\xc5\x64\x3c\xb8\x9c\x0c\x5f\x31\x94\xc7\xa7\x30\xbd\x7c\x3e\x9d\x8d\x66\x97\xb3\x21\xbc\x18\x8f\x4f\x18\x9f\x2b\xd3\xe1\xe4\x8f\xd1\x60\x38\xfd\x1d\xce\xc6\x53\x64\xd6\xe5\x74\x68\xc0\x49\x7f\xd6\xc7\x8e\x2f\x26\xe3\xd3\xd1\x6c\xfa\x3b\xbb\x7e\x7e\x39\x1d\x21\xcf\x46\xe7\xb3\xe1\x64\x72\x79\x31\x1b\x8d\xcf\xeb\xf0\x72\xfc\x7a\xf8\xc7\x70\x52\x19\xf4\x2f\xa7\xc3\x13\x64\xee\xf8\x1c\x49\x9d\xbd\x1c\x8e\x27\x6f\x18\x50\xc6\x03\xe4\xbd\x01\xaf\x5f\x0e\x67\x2f\x87\x13\xc6\x4f\xe4\x54\x9f\xb1\x60\x3a\x9b\x8c\x06\x33\xa5\x5a\x65\x3c\x81\xd9\x78\x32\x53\x68\x84\xf3\xe1\x8b\xb3\xd1\x8b\xe1\xf9\x60\xc8\xb0\x19\x33\x28\xaf\x47\xd3\x61\x1d\xfa\x93\xd1\x94\x55\x18\xf1\x6e\x5f\xf7\xdf\xc0\xf8\x12\x49\x66\x22\xba\x9c\x0e\x2b\x78\xa9\x28\xac\x81\x82\x84\xd1\x29\xf4\x4f\xfe\x18\x31\xb4\x45\xe5\x8b\xf1\x74\x3a\x12\x6a\x82\x2c\x1b\xbc\x04\xce\xee\x46\xc5\x34\xaf\xaf\x2b\x38\x4f\x3d\x3f\x3f\xe5\xa3\x68\x72\x3a\x80\xf6\xe3\xd6\x91\x98\xc0\x2e\x67\xa7\x5d\x33\x74\x28\xa1\x31\xf4\xe0\xd7\x1a\x2f\x60\x53\x0f\xd4\x93\xe7\x78\x0b\xd0\xe3\x77\x16\x3c\xe2\x17\x2d\x79\xd1\x96\x17\x9d\xa4\x89\xc5\xc7\x65\x0f\x1e\xde\x35\x9b\xe6\x93\xd3\xe4\x41\x2b\x7d\x30\x68\x99\x27\xa7\xbc\x94\xda\x9e\x9f\x54\x69\xa7\x55\x86\x4d\x78\x78\xd7\x6f\x9a\xcf\x95\x7a\xf0\x88\x3d\xb0\xcc\xe1\x00\x5a\x35\xa5\xb8\x0e\x8f\x18\x08\xfd\xef\xe1\xdd\xf0\x04\x1e\xde\x75\x9b\xe6\x51\x0e\xc4\xd0\x1c\x9e\x66\x40\x24\x38\x74\x52\x1c\x4e\x19\x0e\x47\x88\x43\xb6\x3f\xf6\xd4\x32\x4f\xdb\xd0\xde\x03\x91\xd3\x0e\x47\xa4\x5b\xda\x29\xde\xf2\x4e\xbb\xac\x3f\x26\xa3\x8a\x1f\x3a\xb6\x8f\xb3\x3d\x47\x88\x3b\x96\x0d\x56\x20\x9e\x09\xf1\xa4\xcf\x58\x81\x78\xe6\x6e\x56\x6b\xed\x19\x2b\x10\xcf\x98\xa3\xa7\x3d\x63\x05\xf2\x59\x18\xad\x6c\xaa\x3e\xc3\x02\xf1\xd4\x27\x01\x68\x2d\x7d\x12\xc8\x47\xcc\x41\xd2\x1e\xb1\x02\xf1\x30\x22\x6b\xbd\x5d\x44\x24\x32\xf1\x66\xae\x3f\x8a\x37\x73\xf1\x88\xfb\x76\xca\x23\x2c\x40\xbd\x8e\x08\xdd\x44\x41\xcc\xe7\x9d\xcd\x6a\x4e\xa2\xd4\x35\xc2\x09\x66\xfe\x11\x9f\x65\x3c\x2a\xb0\x29\xe7\xa8\x87\x9e\x0b\xfa\x85\x7e\x1c\x82\x1b\x6e\xe6\x3e\x89\x99\x07\x67\xe7\xda\xdc\xda\xbe\xe7\xda\x34\x94\xc4\x48\xbf\x2f\xf1\xc0\x79\xb7\xe8\x6d\xd7\x71\x5e\x62\x60\xa3\x1b\x9c\x7e\xc1\x25\x0b\x7b\xe3\xd3\x18\x1f\x78\xd0\x03\x8f\xcd\x78\x56\x25\x57\xd1\x59\x12\xe7\xbd\x17\xdc\xf0\x8a\x0b\xa0\x1f\xd7\xcc\xd9\x87\xff\xf4\xe0\x80\xd3\x7f\xc0\x48\x0a\x2a\x52\xb3\x48\x14\x85\x51\xed\x60\x6e\xbb\x29\x90\x07\x16\x9b\x5a\xab\x1a\x66\x55\xa8\x89\x45\x09\xb9\x5b\x13\x87\x32\xb7\xf8\x26\xa4\x70\xd0\x68\xc8\x4e\x1a\x0d\x38\xa8\x1f\x70\xe4\x49\xe0\x6a\x28\x78\x1c\x05\xce\xe5\x7d\x50\x68\x15\xa2\x20\xa4\x54\x82\x82\x97\x43\x01\x2f\x84\x9e\x43\x0f\xa5\x26\x38\x2c\x39\xe7\x12\xca\xa6\xf9\x80\x08\xb9\x07\x84\x30\x57\x86\x79\xc6\x89\xf0\x0c\x98\xdb\x4c\x1d\xc2\x20\x35\x80\xa2\xb9\x10\x2b\x6f\x0d\x96\xa4\xd9\x81\x67\xd0\x44\x3f\xcb\x81\xa7\x3d\xb0\x5a\x4f\x74\x92\x13\x43\x97\x94\x70\x5d\x94\x22\x25\x7e\x4c\x38\x98\x1e\x58\x47\x9d\x14\x52\xab\xd5\x2e\x86\xd4\x4a\x4a\x04\xb9\x2d\x95\x5e\x38\x04\x4b\xd0\x2c\x30\x0c\x42\xca\xea\x68\xb0\x14\x61\x70\xf5\x15\x12\xe7\x1c\xb2\x99\x8b\x4d\xec\xc8\xff\x28\x18\xac\x31\xb9\x90\x1f\x2d\xb5\x47\xa7\x05\x4f\xc1\x6a\x75\x99\xe6\x3a\x2d\x78\x06\xd6\x91\x55\xda\xff\x28\x40\x48\xd9\x61\x54\xd6\xb3\xe0\x5e\x2b\xcf\xbd\x56\x4b\xe5\x5e\xfb\xa8\x98\x7b\xed\x3d\xb8\x97\xa9\xd1\xce\xd4\x68\x15\xf2\x17\x3d\x5b\xca\x6a\xff\x48\x4e\x43\x2f\x25\xbc\xc6\xf9\xfe\xb8\xa9\xf2\xbd\xfe\x35\x8c\x97\xdc\x65\x9d\xb4\x9f\xa8\x9d\xa8\xc2\xfd\xed\xe8\x5b\x74\xf2\x1d\x75\x26\xc7\xc3\xb6\xc6\xc3\xb6\xd2\x73\xfb\xbb\x68\x6b\xbb\x40\x5b\x3b\x8a\xd5\x68\x75\x3a\xc5\xda\xda\xf9\x26\xda\x9a\xa9\xd1\xc9\xd4\x68\xef\xd0\x67\x79\xd5\xf9\xf1\x9a\x2d\x98\x24\x94\xae\xd3\xf9\x2e\x9a\xdd\xe9\x94\x69\x76\xa7\xfd\x97\x66\xef\xdb\x73\x47\xeb\xb9\xa3\xf4\xdc\xf9\x2e\x63\xaa\x93\x8e\xa9\xac\x7f\xb1\x1d\x20\x03\x86\x00\x4b\xfd\x42\x25\xa2\xe6\x05\x89\x87\xc7\xd5\xbb\xc8\xa9\x63\x6e\x6e\x2d\xce\xfb\x72\xf7\x76\xd1\x98\x2f\xf2\xde\xb8\xc5\xb8\xaf\xed\x45\x31\xab\xec\x86\xb0\x8e\xbc\x80\xd6\xaa\x07\x55\x83\x86\xbc\x5d\xed\x7d\xdd\xd0\xee\x6f\xf1\xbe\x9e\x78\x63\xb0\xdb\xe1\xf3\x49\xf0\x05\xae\x9e\xe2\x67\xad\x43\xb6\x16\xb4\x94\x12\xee\x58\xf5\x40\x44\xa8\xd3\x07\x3e\x09\x6e\xe8\x12\x7a\xd0\xe4\x00\x3e\x2c\x3d\x9f\x20\x80\xa7\x3d\xd1\xca\x0d\x53\x3b\x25\x6b\x8b\x8b\x43\x48\xbd\x27\xde\x29\xfb\x3d\xd4\x1d\x6a\x66\xcd\xd6\x61\x9c\xc1\x54\x28\x0b\x07\x94\x88\x5d\x89\xb6\xbb\x24\xa0\x9e\x63\xfb\xfe\x47\xc6\x98\x74\x4d\x21\xc2\xad\x3c\xc0\xe8\xa1\x81\x78\x87\xf1\xc4\x6c\xd0\x95\xc1\xcb\x86\x5b\x8b\x94\x84\xc1\x4c\x23\xec\x59\x55\xd1\xdc\xfe\x77\xd0\x83\x77\x6c\xe8\x98\xd6\x37\xe2\xb7\x69\x42\x18\xf8\x1f\x21\x26\x14\x7c\xa6\x8a\xb8\xaa\x78\x07\x1e\x73\x83\x6f\x6c\xea\xdd\x12\xb5\x35\xf4\xa0\xe6\xb1\x59\xaa\x29\x08\x67\x97\x75\xd6\x44\xd9\x7f\x48\xeb\xc7\xd4\x8e\xe8\x80\xad\x2e\x93\x76\x75\x6c\x88\xbd\xf8\x70\xc8\xa7\x2a\xa5\x05\x09\xdc\x81\x0c\x16\xd4\xde\x29\x2d\xde\xc9\x16\xef\xb0\x85\x44\xde\xb1\x83\x2a\x05\x8c\x9a\x63\x67\x30\x27\x8b\x30\x22\x0c\xce\x2f\x72\x74\xa5\x58\x3c\x4b\xe0\x6b\xa3\x4b\xe8\xc2\xc1\x81\xae\x22\xa6\xc9\x0d\x58\xb8\x58\xc4\x84\xc6\x4c\x0d\xd6\x76\x1c\xeb\xea\x90\xa5\xf6\xf9\x47\x4a\x0c\x12\xb8\xec\x3f\x13\x8a\xc1\xe5\xfe\x85\xca\xad\x9a\x4e\xf9\xbc\xa7\x10\x94\xb3\x9c\x09\x0e\x7c\x2c\x14\x1b\xca\xdd\x43\xa5\xb8\xdf\x42\xe6\x09\xe8\x69\x9f\x60\x2a\xa3\x92\xfd\xcd\x23\x62\xbf\xd7\x30\xd1\x30\xca\x48\x48\x74\xc7\xfa\xd0\xa8\x41\x04\x0f\x2d\x7d\x61\x99\xaa\xcb\x53\x10\x11\x23\x6c\x28\x11\x62\x6a\xd4\x84\x1c\x13\x84\xc0\xe5\x0e\x56\x22\x37\xd9\xae\x2e\x0d\xc2\xd5\x15\x9f\x0c\xd6\xbe\xed\xe4\x47\x78\xba\x26\xb4\xe5\xa6\x11\x50\x7b\xee\x93\xa2\x61\x2e\x81\xb0\x1e\x45\xe5\x6f\x30\x31\xec\x30\xe5\xa2\xd3\xaf\x5c\xb9\x4b\x74\x11\x0d\x24\xf0\x7e\xcb\xf7\x14\x0b\x6c\x5c\x86\x84\xec\xe6\xdb\xcc\x2c\x89\x52\x2b\x65\x01\xf9\x10\x53\x66\x8c\x0e\x0e\xf6\x19\x91\x69\x4c\xa6\xb7\x65\x4a\x01\x2d\xbe\x20\x74\x6a\x1d\xf2\x1f\x38\x54\xa0\x98\xda\x0a\x3c\x41\x46\x5c\x34\x1a\x20\x59\x70\xe5\x5c\xa3\x83\x54\x2f\x1e\xb3\x3a\x69\x59\xad\xe6\xe0\xb8\x06\xe3\x1c\x24\x67\x32\xc5\x70\xf1\x88\x98\x98\xc9\x3c\x0a\xef\x83\xf0\x43\x0c\xf6\x3c\xdc\x50\x10\x9b\xbe\x10\xe3\x86\x33\xdf\x12\x75\xc2\xe0\x96\x44\x31\x9b\x19\x8b\x74\x9b\x83\x93\xfc\x17\x78\x28\x92\x67\x1c\x61\xb7\x6f\x7d\xe7\xed\xc6\x49\x46\x57\x21\x6a\x3c\xfe\xf7\xcd\x50\xe3\xe0\xf6\x40\x6d\xe3\xbc\xf5\x05\x6a\x22\xe6\x5d\x84\x9d\xd8\x3c\xd7\x9d\x00\x0a\xf1\x66\xbd\x0e\x23\x2a\x76\xf0\x8b\x87\x3f\x6f\xf8\x4d\xdc\xc1\x9d\xa3\x1e\xfb\xfa\x3a\x27\xae\x74\x60\x71\x4d\xcc\x0e\xad\x7b\x0e\xb7\x67\xd0\xd4\xe6\xbe\x5c\x7c\x4e\x1b\x5e\xbc\x1d\x0f\x85\xb5\xba\x4a\x50\xed\xc8\x52\xa1\xe8\xe3\x24\x3b\x0d\x95\x42\xd7\xe6\xc6\xdd\x63\x7e\xdb\x00\xde\x7f\xf4\xe7\xf1\xdc\x36\x8e\x4d\x13\x96\x94\xae\x8f\x1f\x3d\x22\x41\xe3\x83\xf7\xde\x5b\x13\xd7\xb3\x1b\x61\x74\xf3\x88\xdd\x3d\xba\xa4\x8b\xae\x52\xc9\x25\xb7\xc4\x0f\xd7\x24\x6a\x38\x61\x14\x06\xb6\x6f\xcf\xe3\x86\x13\xae\x1e\xb1\xd1\xf3\x68\x43\x17\x66\xd7\x4c\xc7\x8d\xb9\xa1\x9e\xef\xd1\x8f\x65\x61\xe9\x34\x31\x44\xe8\xa7\x1c\x86\x4f\x7b\xd0\xbc\x7b\x72\xca\xa7\x5c\x81\xb5\xd6\x40\x9b\xe4\x6b\x99\x66\xa7\x99\x25\x34\xef\x9d\x4d\xbf\x4d\x36\x6b\xdf\x0d\x9a\x70\x08\x2b\x9b\x2e\x1b\x0b\x3f\x0c\x13\xa0\xf0\x08\x9a\x77\x9d\x66\xfd\xf7\x82\x86\x16\x36\xec\xb2\x86\x49\xf5\x87\xd9\xea\x2a\xa2\xd8\x9b\xc1\xdb\x8a\x2a\x24\x70\x7f\x2f\xc1\xf9\xf4\x74\x27\xd2\x43\xd6\x77\x19\xd6\x56\xb3\xb9\x1b\xef\x72\x8a\xf3\xa4\xa4\x10\x5a\x5f\x43\x39\xff\xd7\xda\xcd\x00\xab\x59\xca\x02\xac\xd4\x93\xaa\x51\x80\x61\x3b\x45\xb0\x18\x3b\x2c\xe5\x7f\x3d\x95\x83\x2a\x0b\x8a\x28\xff\x4e\x70\xad\xef\x04\xb7\x89\x9a\xc4\xe0\xb2\x8a\xbf\xe7\xc2\x17\x65\xd2\xe1\xff\xda\x39\x21\xe1\x5c\x00\xd5\x4b\x21\x27\xc7\x0e\x82\x90\x2d\x83\xe0\x26\x22\x36\xc5\x6c\x0b\x3b\x80\xcb\x43\x2e\xbb\x5f\xaa\xdc\xa0\x88\x15\xcb\xd2\x5b\xd0\xb7\x8f\x19\x01\xad\x3f\x1f\x6b\x85\x56\x0b\x0b\xad\x96\x5e\xda\xe5\xa5\x5d\x09\x41\xc9\x12\xab\x28\xd7\xd0\x4b\xcc\x08\x4f\xd0\x62\x8b\x5b\x03\xcd\xef\xdb\xc4\xf6\x2a\x9b\x58\xa0\x2e\x6e\xbd\x44\xfb\x3c\x78\x06\xef\x34\xf3\x92\xdd\xd1\x59\xaa\xeb\x2b\x6f\x91\xf4\xa0\xab\x68\x89\x41\xa7\x91\xa1\x63\xc4\x27\x00\x90\x6e\x9c\xf2\x3c\xb9\x30\xad\x43\x6c\x5d\xcf\x07\x99\x04\x32\x6f\xb9\x69\x4f\x12\xf0\x68\x64\x78\x86\x57\x37\xa0\x99\xc1\x47\xea\xce\x03\x67\x59\x34\x01\x4b\xb6\xaa\xa4\xc5\x6c\x19\xc6\x03\x66\x90\xb2\x1a\x67\x35\x67\x59\x57\x1d\xf7\xa4\x76\xab\x68\xb4\xce\x51\xbf\xe6\xc2\xf4\x88\xf6\x86\x65\xe4\x63\xc2\xa1\x4b\x9a\x06\xfb\x95\x15\x9b\x26\x33\xcd\xbc\xad\xc9\x46\x48\xd2\x22\x45\x08\x5b\xfd\x2a\xb5\x8b\x6b\xba\x95\x5d\x5a\x24\x18\xb6\x77\x60\x88\xbf\x2d\x0d\xcf\xf6\x16\x3c\xf1\xb7\xa5\x60\x3b\x54\xb1\xe5\xc0\xf6\x41\xdc\x6a\x49\xcc\x33\x94\xb4\x4a\x29\xe9\xec\x45\x09\xfe\xb6\x35\x7a\x3a\x3b\xe9\xc1\xdf\xb6\x42\xd5\x69\x09\x55\x1c\xfa\x5e\x04\x76\x33\x04\x26\x14\xb7\x32\x14\xb7\x0b\x1d\x14\x01\xd5\xd0\x32\x45\x71\xb0\x1f\x5a\xda\x70\x97\x23\x46\xba\x32\x13\x11\x5c\xb5\x03\xf0\x28\x89\x6c\x1a\x46\xcc\xc5\x73\x96\x7a\xd8\x95\xdc\x31\xc7\x7a\x2e\x7c\x58\x0c\x1b\xd1\x58\x6c\xbb\x07\x94\x44\xb7\xb6\x5f\xe4\xb2\xc8\xd4\x54\x86\x49\x92\x9c\xca\xd0\x16\x37\x90\x0c\x3c\x59\x90\x98\xa0\x54\x60\x6f\xf3\x2b\x4d\x11\x98\xc0\x11\xcb\x1c\x33\x85\x13\xa9\xad\x7b\xef\xad\xeb\x6a\xf0\x84\x15\xf0\xe1\xaa\x40\x4d\x2e\x0f\xf9\x73\x35\x36\x9b\x7a\xd3\x6f\x31\xdb\x13\x43\x75\xfa\x43\x1e\xe8\x12\x34\x48\x58\xca\x34\xb2\x26\x36\xd5\x3c\x5f\xd5\x2e\xea\x01\x16\xc5\xaa\xaa\x0d\x94\xee\x99\xce\xa4\x77\x87\x19\x9f\x5a\x5f\x2a\xec\x63\x5e\xa5\xf9\x43\x64\x34\x0a\x0e\x15\x4b\xce\x95\x96\x7a\x3a\x27\x12\x89\x55\x32\x1c\xf1\xed\x98\xaa\xa0\x94\x4d\x75\xc1\x32\xdf\x73\x88\x62\xd6\x91\x85\x06\x6b\x56\xcf\xce\xbf\x58\x55\x04\x85\x0c\x84\x9c\x28\xbf\x32\x73\x26\x1a\x37\xf7\x82\x98\xd8\x91\xb3\xac\xc5\x61\x44\x89\x3b\xb3\xe7\x3e\x31\x98\x5e\xaf\x0c\x70\xc2\xd5\x5a\x5d\x3a\x2d\x89\xed\x1a\x80\x59\x3a\x3d\xb0\x0c\x78\xa0\xb4\x51\xaa\xad\x3c\x57\x77\x29\x6a\xac\x21\x1c\x62\xcb\xfa\xa3\x56\xe2\x88\xe3\xee\x5b\xb8\x5a\xeb\x66\x87\xaf\x96\x6a\xd8\x8d\x89\x7d\xd6\xe1\x19\xe4\x56\x4a\x4c\x3b\xd3\xee\xaf\x68\xc8\xf7\x39\x6a\x2b\xcf\xad\x5f\xc3\x33\x24\x21\x1f\xea\x63\x7f\x82\x80\x95\xa7\x6b\x8d\x36\x19\xca\x3f\xc4\xbc\xa0\x6e\x46\xe3\xf6\x23\x19\x32\xf1\xc3\x6d\x64\x20\xdd\xd7\x4c\x69\xf2\x84\x08\x51\xd3\x68\x43\x0c\xd0\x5b\x24\xd3\x7a\x19\x5c\x44\xe8\x5e\x70\xb1\x45\xde\x5d\x90\xb6\xc3\x96\x85\x52\xc5\x32\x1a\xe6\xf8\x76\x1c\xbf\xb2\xa9\xb3\x7c\x41\x02\x6e\x2d\x6b\x58\x96\xa4\xd4\x83\x36\x6d\xb0\x91\xf8\xe9\xb3\x52\x18\xd9\xc1\x4d\xbe\xd4\xbb\x09\xc2\x08\x1d\xb5\x04\x01\xa5\x7a\x41\xf9\xc2\x8b\x62\xea\x13\xca\x7c\xca\x1e\xd2\xa8\x39\x2b\x98\xac\x9f\x34\x53\x3b\xa2\xc2\x34\x08\xc3\x8c\xb8\xd7\xd5\x1a\xcc\x08\xe2\x2d\xa6\xdc\x18\xf0\xd6\x60\xfe\xab\xc7\x66\x07\x55\x69\xd1\x56\xf6\xd2\x04\xe0\x64\x08\x08\x52\xd8\x0c\xc1\x6e\x91\x2b\x79\xcd\x95\xdb\xba\x07\x0f\x0f\x8a\xd5\x3a\xe1\x48\x42\x9b\xaa\xd9\x49\x73\xb3\xa4\x39\xc6\x36\x1b\xcc\x1c\x44\x14\x17\x01\x22\xb2\x24\xa7\x46\xa7\x5e\xcf\xb5\x91\xbc\xde\xde\xe3\x9f\x65\x08\x73\xf2\x55\xc1\x14\xd6\x83\x24\x64\x54\xfd\xe5\x97\x5f\xaa\x79\x34\x0a\x47\x2e\xa0\x0d\x96\x62\xcd\x61\x08\x05\xa3\x58\xc5\xba\x7a\x5d\x2d\xc6\x46\xdf\x0e\x28\xed\x5e\x10\xc7\x39\x54\x4a\xd6\x97\x30\xbd\x94\x5a\x0e\x2c\x22\xab\xf0\x96\x70\x60\x75\xc0\xf0\xff\x2a\xbc\x65\x2e\x48\xd5\xac\xee\x46\x82\x8f\x36\x03\x3e\x15\x40\xcb\xe2\xf6\x39\x8f\x1c\x28\x5a\x91\x8e\x40\x0d\xfd\x2c\xd3\xd5\x9d\x5d\x2e\x80\xfb\x0d\x88\xaa\xcd\x25\xc5\x68\x7d\x68\x1f\xe3\xfb\x05\x24\x26\x01\x8d\xf1\xcd\x10\xae\x59\x71\x03\x6a\xe3\xf3\xb3\x37\xd0\x9f\x0e\x46\xa3\x3c\xde\xc5\x3c\x78\xfc\x9b\x01\x47\xcd\xcf\xc8\xc6\x3e\x98\xf0\x8f\x3d\xdb\x1d\x3d\x31\xc0\x6a\xb5\x78\x43\x1b\x4c\xf8\xbf\xe5\x7a\xe6\x28\xd8\x3b\x39\xec\xf1\xfd\x81\xd0\x57\x76\x6d\x1a\x7b\xe2\xd0\x34\xa0\x6d\x15\x48\xa8\x48\xe5\xac\xd6\x93\x7a\x39\x86\xae\x82\xa1\x9b\xc3\xd0\xf5\x6e\xf0\xd5\x9b\xfd\xb0\xea\x74\x0d\xf8\xed\x09\x67\x4c\x13\x4c\x38\x2a\xef\xf6\x46\xe9\xf6\x26\xd7\x2d\xe6\x0e\xe0\x76\x8c\xb2\xa1\x25\xc2\xda\xf1\xda\x76\xc8\xbe\x18\x59\x06\x74\x77\xb1\x29\xad\xdc\xd9\x87\xab\x49\xf5\x76\xdb\x00\xab\xdd\xda\x1f\x7c\xbb\x63\x80\xf5\xdb\xd1\xfe\x0d\x1e\x5b\x8c\x9f\xf7\x68\xf1\xdb\x13\xd6\xa4\x6b\x1d\xed\x4f\x45\xb7\xd5\x6c\x1b\xd0\x6d\xdd\x83\xf0\x6e\x8b\x51\xd2\x6d\xb5\xf7\x67\x6d\xb7\xd5\x69\xb2\x26\xdd\xc7\xf7\x68\xd2\xed\xe2\x40\xeb\x3e\xf9\xbc\x45\x7f\x7d\x45\x91\xfc\xbc\x7d\x08\x3f\x90\x08\xb7\x62\xbe\xc6\x52\xdc\x67\xc4\xaf\x15\x7c\xd6\x79\xc5\x66\x8e\xd3\x86\xbf\xae\xa4\x8c\xfa\x2f\x41\x8a\xa9\x5f\x27\xcb\x9a\x2d\xaa\xd1\x35\xe0\x71\x67\xef\xea\x47\x96\x01\x47\xfb\x0b\xcb\x6a\xb1\xc1\xd0\xca\x36\xd0\x38\x13\x2b\x9c\x89\x73\x9c\xc1\x61\xfd\x05\x96\xf0\x88\x0d\xc2\x3d\x2d\x61\xbb\xb5\xa7\xc5\x6c\xb7\xf7\xac\xf8\xb8\xb9\x5f\xc5\xdf\x9e\xec\xac\x99\x2a\xbe\x75\xd4\x62\x63\xa5\xb9\xd3\xb4\x08\xe0\xdd\xd6\xbe\x84\x75\x5b\xfb\x52\xd6\x6d\xb5\x8f\xf6\xad\xd9\x7d\xb2\xef\x44\xd4\xea\x76\xb7\x28\xc8\x46\x51\x90\x4d\x4e\x41\x70\x1f\xf7\xab\x87\xf2\xf6\x49\x5f\x43\xe7\x83\x82\xce\x87\x1c\x3a\xb6\xbf\x5e\xda\xc1\x66\x45\x22\xcf\xf9\xda\xa1\xbc\x7d\xde\xfc\x52\x62\xca\xdb\xdd\xc7\x9e\xdd\x29\x5c\xb8\xcb\x71\x61\x49\xee\x6c\x97\x38\xde\xca\xfe\xb6\xbe\xc2\x76\x9a\x9f\x28\x34\x9f\xde\x87\xe6\xa6\x42\xf3\xe2\x2f\x37\xff\xbb\xb8\xf9\xa0\xae\x56\xf5\xe6\x39\xaa\x77\x33\xf8\xbe\xcc\x2d\x64\xec\x97\x30\xf5\x5b\x32\xb4\x9c\x99\xfb\x32\x4e\xcd\x02\xd0\x63\x1d\x7a\x88\xa6\x92\xa2\x1e\x87\x92\x63\x5a\x3c\x23\x09\xdd\x78\xc1\x04\xa9\xaa\x31\xe3\x35\x48\xb6\xd0\x41\x84\x3a\xde\x1a\xf8\xae\xb5\xc7\x53\x7a\x39\x03\xea\x05\x61\xba\xe8\xca\xba\x86\xa7\x3c\x10\xcb\xa0\xf0\x04\x08\x79\xf3\xb4\x07\xd1\x55\xeb\xba\x58\xb6\x4a\x44\xaa\x7c\xd1\xa8\x5c\x17\x06\xa5\x20\x55\x23\x19\x10\x28\x0a\x7b\x25\x51\xf0\x3c\xb5\x4a\xad\x34\x60\x2a\x54\x2d\xa9\x8d\x7b\x7f\xe5\x1c\x23\x81\x6b\xa4\xa1\xa2\xc2\x50\xda\x5e\x08\x24\x8c\xfb\x4f\x0f\x4c\x2b\x59\x24\xd7\xee\x8b\x58\x29\x66\x69\x1a\x39\x4f\x1f\x94\x49\xbe\xf8\x6e\x3c\xb9\xa3\x91\x9d\x64\xed\x18\xd8\x3d\x2f\x8b\x48\xbc\xf1\x29\xdc\xda\xfe\xa6\x30\x81\x30\xde\xcc\x5f\x7b\x74\xf9\x3c\x7d\x49\x10\x37\x59\xe2\xf9\xd7\x24\x0d\xc7\xf3\xa2\xdd\x8f\xbf\x52\x87\xe1\xaf\xd4\x61\xd5\x3c\x24\x37\x7f\xa5\x0e\xef\x4a\x1d\x4e\xae\x50\x0a\x32\x9d\x50\x84\xe7\x6d\x67\x89\xdb\x50\x84\xae\x08\xb5\x71\x12\xa9\x7d\xfa\x6c\x7c\x42\xd0\x6f\xdf\xae\xf8\x26\x6d\xf5\xfd\x6d\xb5\xf2\xb9\xae\x36\xba\xc0\x10\xde\x3d\x5b\x26\xe6\x03\x6d\x36\x89\xd2\xfd\x03\x71\x5e\x4e\x6e\xff\x40\x54\x84\x1e\x7c\x4a\x67\xaa\xe4\x9d\x88\x1e\x7c\xfa\x6c\x24\xe5\x8e\xbd\xa6\x9b\x48\xdd\x58\xf8\xac\xce\x14\x05\x41\x47\xa4\xe4\x0a\xfb\xbe\x86\x1e\xc8\xde\xf2\xf6\x3c\x25\x39\xad\xad\x55\x16\x62\xcc\x90\xc9\xd3\x42\x6b\xec\x3e\xb7\xad\x97\xce\x0d\x83\x7a\x76\x7e\x65\xcf\x58\x79\xf1\x14\x2a\x3a\x3e\x0e\xc8\x1d\x3d\x65\x35\xf3\xce\x87\x5a\x65\x4a\xa3\xda\x1e\xfe\x92\x6c\xc2\x3c\x7d\x9a\x6d\x50\x30\x39\x97\x91\x4c\xed\xe8\x7b\x11\xbc\xd8\xf8\xfe\x84\xa1\x37\x0e\xce\xbf\x0b\xed\x25\x1c\xbd\x07\xf9\x2b\x2f\xd8\xc4\x3f\x88\xfe\x02\xe2\xb2\x9b\x98\xe5\x74\xed\xa0\xe3\xdf\x1b\x12\x23\xb2\x3f\xb3\x28\xbf\x8c\xda\x22\x72\x85\xe9\xa8\x79\x6e\x39\xad\x6f\xf5\xde\x53\x2f\x42\xf4\xdb\x90\xf6\xe7\xca\x73\xaf\x99\xff\x6b\x16\x3f\xb1\xae\x0b\xe0\x88\x1a\xae\x92\xa7\x25\x1b\xf3\xf9\xd9\x28\x03\x56\xf2\xa0\x75\x5d\x84\x2e\x26\x76\xef\xd1\x07\xe6\xaa\x28\x37\x70\x08\x7e\x4e\xd2\x29\xce\x3d\x01\xb8\x50\xe4\xb8\xb0\x60\x73\x97\x01\x7e\x76\x2d\xb1\x9f\xa0\x8b\x84\xbd\x43\xe0\xf0\x9d\xcd\x9c\xa0\x7d\xca\xa6\xdb\xfb\x68\x4d\x89\x10\x15\x2d\x92\x99\x3c\x7b\x10\xb9\x2f\x8e\xe1\xfa\xab\x51\x6c\x65\x50\xcc\x39\x46\x5f\x31\xf8\xe6\xb6\x6f\x07\x0e\x89\x6a\x31\x8d\xb2\x69\x67\xf1\x66\x55\x90\x70\x34\x77\x0c\x20\x4e\x26\xa7\x11\x2c\x03\x2c\xb1\x32\x4f\xca\x5a\x46\xfe\xb5\x72\xb1\x6d\xcf\x3c\xfc\xb9\x53\x67\xaa\x4d\x82\x1a\x51\xac\x9c\x06\x5e\x2e\xf2\xe7\x4e\x66\xd5\x4f\xee\x67\x17\x9d\x01\xba\x9d\x0e\xae\x00\x18\x59\xcf\xa0\x59\x3c\x60\x38\xcd\xec\x37\xcb\x64\x01\x0a\x2b\xf4\xca\x9a\xef\x90\x46\x56\x2a\x65\xcd\x8a\xa7\xce\x84\x90\xb9\xb3\x0b\xf9\x6c\x6a\xd6\x7e\x1d\x6c\xa5\x37\x8c\x44\xef\x66\xc1\x3b\xda\x3a\x06\xcd\xad\x7c\x29\x1a\xfd\xa5\x18\xec\x46\x1c\x76\xed\x82\xe7\xd6\xe3\x15\x05\x68\x23\x71\x68\xb9\x25\x28\x1a\x98\x7b\xb9\x00\xc9\xf8\x24\xff\x46\xc3\x54\x62\x55\xb6\x28\x87\xb7\x00\x75\x4e\x80\x67\x0a\x00\x3b\x62\xdd\xa1\xfa\x26\x88\x47\xe1\x0a\x7b\xc2\x65\xb3\xd6\x50\xeb\xda\x0b\x6e\xce\x48\xc1\x86\x7f\x5a\x25\x5c\x17\xa5\x74\x14\x10\x14\x78\xfe\x2e\xc3\xe2\xdb\x31\xd2\xa5\x14\x95\xe6\x16\x09\x5b\x20\xa1\x2a\x09\x42\xb5\x44\x0c\x59\xfb\x71\x43\x02\x3d\x81\x08\x97\x04\xe5\xc6\xa0\x30\xe8\x73\x43\xb2\xc9\x99\x0a\x35\xf5\x9a\x96\x43\xa5\xac\x68\x44\x42\xd2\x3c\xde\x9a\x91\xa4\x32\xc9\x5b\x94\xa5\x5b\x68\xd1\xce\x9c\x2e\x1a\x72\xf5\x52\x98\xee\x25\x97\x6a\x6a\xd0\xa9\x20\xd0\x2b\xf8\x5e\x96\x2c\x84\x07\xb3\x56\xad\x56\xbb\xf3\xdb\xe3\x27\xdd\xa3\xaa\xc1\x68\xb3\x0c\x54\x84\x12\x07\x52\xb4\x94\x32\x2e\xaf\x74\x0f\x0a\x25\xb0\x82\x98\x7d\x22\x79\xd9\x9f\xce\x5c\xf5\xaf\xc8\xa0\xee\x83\x84\xf4\x3d\x93\xf4\x3c\xa7\x5e\xb2\x79\x90\xec\xcf\xcc\x4b\x32\x99\xfe\xff\x63\x8e\x98\xcf\x0b\x9f\xcd\x8d\x24\xc3\x2e\xf1\x10\xe4\x68\x43\xf5\x3f\x64\xba\x82\xff\x8f\x4a\xb0\xdb\x45\xe3\xfc\x1e\x53\x80\x42\x65\xd1\x90\xa8\x3e\xac\x42\xa3\x01\xce\x7e\x33\x6d\x49\xcc\xbf\x14\x81\x44\xf4\xbf\xfe\x10\xd1\xb3\x45\xfc\x37\x12\x7c\x19\x3b\x21\xcd\x01\xf4\xc4\x29\x28\x28\x59\xb0\x17\x94\x44\x50\x95\x6f\x2f\x0a\x71\x33\x49\xc7\x25\xa8\x14\x31\x58\x1b\x2f\x87\x3f\xd7\x78\xf9\x9f\xc6\x7e\xf3\x47\xb0\x9f\x47\x5e\xfe\x8b\xb8\xf6\xbf\x7e\x04\xd7\x92\x38\xcf\x7f\x11\xe3\xfe\xdc\xce\xb8\xb9\xf2\xce\x5a\x29\xae\x39\x0f\xb7\x2c\xe3\xf8\xa7\xa1\xfa\x6f\x3b\xa8\x26\xac\x16\x5b\xe9\x72\x6f\x75\x3f\xf2\x69\x38\x0c\xdc\x9f\x9e\xf4\xab\x9f\xc7\xbc\x97\xfa\x39\xa2\x59\xe2\xd2\x14\xb9\x10\x39\xef\x66\x97\x1f\x58\xfb\x79\x08\x87\xef\xe0\x24\xcb\x08\x94\x01\x9f\x4a\x32\x56\xf4\x4c\x91\xd8\x80\x07\xd9\xb6\x5f\xe6\x17\x6a\xe1\x3d\x27\xbe\x7a\xe0\xc4\xd7\x25\x84\x63\x7c\xa2\xd8\x2b\xb5\xea\x28\xa5\xba\xc8\xa6\xca\x05\xd6\x72\xc8\x5e\x37\xc8\x6a\x4d\x3f\x8a\x01\xb7\x5b\xf1\xeb\xff\x35\xf2\x4f\xa2\xe0\x8c\x76\x2d\xdf\xa6\x44\x82\xf2\x65\x33\x7b\xbd\x9d\xba\x8c\x0d\x12\x9c\x3e\x86\x83\xda\x01\xe0\x49\xfa\xc1\x4d\xc1\xfb\x27\xdb\x50\xdd\x5b\x77\xc2\x75\xcd\xb1\xd7\xbb\xc6\x70\xe3\xe7\x91\xe1\x2e\xe3\xa5\xc6\xa6\x9c\x41\x3d\xc9\x5f\x19\x88\xcc\x95\x9d\xfa\xfa\x70\x1b\xad\xa5\x2f\x3b\xc1\xb6\x79\xe6\xe7\x63\x51\x61\xbc\xe4\xde\xf1\xc2\x0a\xe8\xd9\x01\x0f\x9c\x38\x1f\x31\x2e\x55\xee\x7a\x56\xb9\x15\x48\xc5\xec\xfa\x32\xf6\x64\xd2\xcf\xca\x9b\x17\x04\xc3\xbc\x45\xc6\xcd\x50\x23\x8b\x31\x8d\x98\x52\xed\x1f\x43\xcc\x85\x74\x73\x1a\xb3\x2d\xd8\xa8\xc6\xde\xf4\xd8\xac\x0c\x92\x6a\x07\x48\x10\x7f\x91\x76\xc4\xee\x90\x54\xcc\x86\x90\xd7\x87\xa0\x9f\x2f\xa0\xc2\x9b\xe2\x21\x3d\xdb\xc0\xf1\x63\x7c\x92\xcb\x32\x60\x31\x8d\x30\x18\x5c\x0e\x8d\xdb\xd5\xd0\x77\x65\x3d\x84\x89\xbc\xca\xd4\xd1\xbb\xd4\xd1\x89\x72\x9d\xe8\xfa\x1c\x4b\x84\x33\x7b\x57\x71\xd2\x52\x62\x50\xaa\xe2\xa9\xce\xe4\x36\x86\xbf\x05\x75\x79\xf9\xe4\xde\xc8\xfe\x2a\xfa\x17\x29\x9e\x3f\x88\x33\x5b\x95\xe8\x4b\xc5\xae\x6d\xda\x14\x73\xee\x27\xe1\x8c\xc6\x9a\x75\x14\x3a\x24\x8e\xb3\xfc\x30\x80\x9f\xb9\x80\x3b\x14\x95\xc2\x01\x9b\x52\x1b\x8b\x55\x1e\xff\x9f\xe4\x20\x42\x3a\x24\xc5\x06\x48\x0f\x6a\xbc\x4e\x9a\xd0\x97\xb4\x49\x32\x07\x69\x54\x87\x43\x51\x7e\x98\x03\x95\x6e\x67\x68\xa0\xb7\xd8\x80\x92\x0a\xc2\x28\xf6\xb2\x1d\xe7\x70\xc7\x13\x2b\x74\x66\x8b\x27\x68\x0c\x95\x77\xa4\xef\x2f\x5a\x4b\x13\x50\x72\x63\x9a\xca\x7e\xcc\x05\x4f\xee\xcc\x8e\x2e\xad\xce\xf3\x8f\x34\x7b\x4e\x93\x38\x88\x06\x92\x84\x43\xe6\xf3\xa5\x88\x17\x1c\x20\x20\x39\xf7\x34\xcb\xa3\x42\xe7\xc0\x34\xaf\xae\xe4\x9c\xc8\x50\x4c\x5b\x95\x3b\x13\xe2\x8c\x63\x3c\xe2\x61\xfe\x91\x92\xaa\x91\x60\x5f\xec\x33\x38\xcb\xb4\x46\xba\x67\x9d\xa4\xce\x26\x9b\xd7\x49\xd7\x66\x82\x8f\xc9\xca\xbf\x0d\xf4\x9d\x80\x14\x20\xc9\x65\xd1\x06\x74\xa9\x2b\xb6\x17\x26\x92\xc8\xf4\x6a\xbf\x80\x79\x5e\x89\xe0\xfa\x3a\x57\x0b\xdf\x0c\xd7\x12\x00\x92\xfe\xca\xbb\x33\x4d\x21\x52\x67\x69\x47\x55\x03\xe9\xd0\xd2\xfb\x97\x05\xce\x60\x62\x44\xf8\xc6\x6d\x72\x7b\x5d\xdb\xda\xb4\x90\x79\xe5\xb0\x4c\x6b\x7b\x86\x8a\xea\x4c\xe9\xa6\x25\xa7\xbe\x5a\x9e\x91\xb2\xa3\x28\xff\x78\xfe\xff\xda\xf6\x22\xe5\x50\x6f\x06\x32\x59\x49\x17\x25\xef\x78\x0b\xac\x2a\xd6\xad\x7b\xbe\x2e\x93\x2c\xeb\x59\xd3\x2b\xeb\xfa\xde\xaf\xca\xe4\x00\x69\x22\x17\x50\xc5\x45\xab\x68\xe1\xbe\xeb\x5d\x16\x99\x54\xab\xb2\x54\x1b\xa0\x96\x01\x9b\x60\x6d\x3b\xef\x6b\xf9\x40\x43\x6e\x9a\x12\xd0\x64\xc2\xa8\x3c\x75\xa8\xe0\x1b\x45\x6a\x7a\xfd\xf6\xaf\x56\x42\x41\x8a\xac\x96\xce\x9a\x6e\xc8\x6f\xcb\xb2\xd5\x51\x3b\x16\x13\xa9\x38\x35\x09\xbf\x7e\x99\xc1\x16\x6b\x16\xa1\x5b\xf6\xf5\x4c\xd4\x92\x00\xb7\xd4\xf1\x5f\x26\xbb\x7f\x11\x6e\x30\xc2\xf8\xa9\x94\xe4\x7a\x92\xc5\x8b\x75\xaf\xac\xcc\x9b\x25\xc9\x83\x76\xc1\x2b\x27\xc9\xc9\x50\x28\x2b\xac\x67\x40\x3b\x2f\x2b\xd0\xcf\x62\x4d\x74\x49\x76\x29\xaf\x64\x5a\x9d\xf2\x4e\x85\xe4\xcc\x4d\x29\x6b\xca\xbe\x1b\xca\x3b\xbd\x21\x77\xcc\xa7\x90\xdd\xf2\x0a\x96\x61\xe1\x71\xab\xd5\x3f\xab\xdc\xc5\xe0\xf5\xc2\x08\xc4\x3e\x23\xde\x2b\x7c\x64\xf6\x51\xbc\x9d\x60\xa9\x92\x2d\xcd\x62\xd8\xce\x78\x09\xae\xfe\x39\xcf\xe7\xac\x00\x40\x98\x67\xd1\xbd\x64\x54\x2e\x01\x28\x01\x80\xaf\x52\x06\x2e\x58\x8c\x9e\x22\xa9\x95\x4a\x4e\x6f\xb9\x3d\x1d\xf4\x3e\xe2\xcc\x0e\xdb\xa2\x83\x95\xe4\xf9\xbc\xfc\x9b\xae\x76\x74\xa3\xbd\x1e\x12\xa1\xaf\x54\xad\x4a\x5d\xc5\xf3\x6c\x59\x55\x1e\x03\xe4\x3a\x52\x2d\x3a\x8e\xad\x64\x2b\x58\x9c\x58\xbb\x59\x25\x25\x98\xfb\xc1\x8c\xb3\x96\x7a\xc2\x7a\xc8\xfb\x41\xca\x29\x34\x65\xc9\x17\xdf\x2d\x24\xc3\x59\xc1\x7e\x1b\x0d\x70\x76\xdb\xde\x22\x38\x01\x26\x71\x29\x29\x11\x45\x14\xb0\x4a\xa5\xc8\x6b\x58\x30\x69\x5d\x05\x9b\x55\xde\x63\xf8\x56\x54\xc0\x8e\x4d\xfd\xa2\xe8\x0e\x0f\x8d\x65\x34\x05\x67\xb8\x6a\xee\xa5\x1e\xc4\x65\xed\x5f\x21\x25\x16\x9a\x77\xbc\x6c\x5e\xe3\xb5\x50\xbc\x62\x90\x52\x85\xab\x39\xc3\xf9\x80\xc1\x28\xce\x44\x4c\xfb\xac\x89\x31\xc8\xea\x1a\x60\xd5\xeb\x4a\x87\x85\x2c\x54\x9a\x0a\x1c\x73\x4d\x32\x6b\x5c\x31\x56\x23\x42\x71\xe8\x29\x46\x35\xfd\x68\x9e\x6e\x52\xb7\x7e\x69\x99\x81\xc4\x4b\xe6\xcb\xe2\x7f\xfe\xb6\x19\x14\x0f\x57\xf1\xfe\x59\x44\x6e\xf9\x6e\x97\x5a\x31\x3d\x2c\x2a\x6f\xc4\x31\x4d\xa9\x68\x2e\xf3\x68\xad\xae\x1e\x6d\x15\x24\x09\x89\x7c\x15\xf3\x80\xd7\x94\xdf\x63\xe3\x28\xfe\xa7\x07\x41\xfe\x08\x6a\x14\x50\x0f\x3e\x5d\x35\xaf\xb3\xae\x6d\x81\x31\x33\x72\x33\xdd\xe7\x9c\x12\xa1\x42\xeb\x0e\x13\xa7\x3c\x85\xc8\x8f\x86\x96\x0d\x71\xb6\x29\xb6\x7d\x80\xeb\x21\xc9\xb7\x42\xcb\xcf\x68\x0f\xb4\x92\x02\x36\xe5\xf5\xa0\x0c\xc9\xba\x01\x81\x6a\x9e\x59\x1d\xee\xcc\xf2\x8f\x9c\xab\xab\x61\x5e\x14\x6f\xe6\x29\xe7\x78\x91\x3c\xfb\xbc\xa7\x9e\x84\xce\x1f\x39\x7c\x12\x93\xaf\x92\xf1\xc2\xf4\xb8\xca\xec\xf1\xb2\x0d\x6e\x87\xb5\x7c\x40\xfe\x60\x9e\xae\x82\xb4\x06\xe9\x37\x2a\xe5\xc4\xcb\xcb\xf9\x6b\xac\xb2\x9c\xfb\x12\xbc\x07\xf1\x44\x55\x43\xf1\x44\x7c\x62\xb2\x97\x0c\x09\x5e\x9e\x7e\x22\x13\xbf\x8d\xc9\xbb\xe5\x9f\xbf\xec\x89\xef\x60\x0a\x6e\xe1\x59\xf4\x3d\x7e\x62\x8a\x20\x15\x4f\xce\xef\xf1\x93\x17\x24\xb7\xe4\x37\x2e\x23\xb2\xae\x54\xd2\xfc\xf2\xa5\x1d\xf5\xa9\xf0\x15\xeb\x72\xe2\xf3\xe0\x69\x0f\xa4\x2c\x78\x3c\xa2\xe8\x05\x45\x29\x9a\xe4\x34\x5e\x01\xa1\xe8\x35\x5a\xe5\x65\x46\x94\xbc\xb7\x80\x17\x84\x8e\xf1\x13\xe3\xb5\x03\x7b\x43\x43\xc7\x0f\x63\x72\x80\xd6\x2e\xf0\xfc\xb4\xc3\xbe\xeb\xe6\xab\xc9\x81\xab\x68\x51\xf2\xf0\x82\xad\x7b\x98\x3e\x1d\xfc\xf3\xe0\x9f\x07\x07\x06\x1c\x54\xab\xec\xf7\x5f\xff\x62\xbf\xb5\x3a\xfb\xfd\xf4\x99\xfd\x5e\x5d\x1f\x7c\x56\x5a\x9f\x93\x0f\xbe\x17\x28\xed\x73\x75\x53\xc6\x85\xc1\x64\x13\x90\x5a\x64\xc0\x6d\xc2\x36\x36\x61\x97\x10\x55\xc0\x3e\x7d\x7d\xc1\x1c\x03\x4f\x1c\xe0\x98\xa1\x44\xb1\x27\xde\x02\x22\xfe\xaa\x06\x8a\x4d\xaf\x78\xe5\x5d\x1b\xd0\x2a\xc8\x0a\x10\xcb\xc6\x4d\x74\xe6\x05\x4c\x9f\x6f\x1b\xcf\x37\x8b\x63\x76\x53\xbb\x6d\x0c\x36\x51\x1c\x46\x8d\x37\x4a\x70\x4d\xf4\x24\x3a\x11\xed\x0c\x48\xea\xfe\xfd\x90\xef\x8a\xde\x0f\x0b\xf6\x77\x7b\xfc\xdc\x76\xde\xe3\x91\x31\x35\x9c\x60\xf3\x3e\xc1\xed\x31\xef\x65\xe2\xdd\x2c\x69\x59\xa5\x82\xe3\xde\xd4\x95\xb4\x20\x20\x45\x18\x8f\xaa\xc4\xcf\xae\x8d\xe2\xd7\x61\x84\x2f\x69\xd6\xca\xe9\x33\xc5\x04\xb9\xb5\xc6\x76\x0e\x30\x08\xfb\x9e\x54\x57\xf2\xda\xfc\x6e\x61\x17\x7d\x95\xee\x0b\x85\xad\xf0\xaa\xa7\x8c\x7d\x01\xa7\x2e\x3f\xcb\xb7\x17\xfb\x0e\x4b\x89\x37\x4d\x3c\x85\xb7\x6a\x56\x61\x49\x22\x02\x1e\xbe\xbd\xec\x92\x88\x2c\x48\x20\xbe\x3d\xbf\x0e\xf1\x20\x5e\xf6\x20\x01\x79\x16\x3a\xe2\x30\x5f\x2f\xc6\xa1\xba\xb2\xe5\x77\xb4\x56\xb6\x9b\x77\xfe\x4c\x13\x3e\x2c\x49\x20\xbe\xe3\x41\xbd\xe0\x06\x6e\xf0\x2d\x69\xfc\x80\x3b\x0d\xc1\xdf\xd8\x45\x8d\x46\x14\x3f\xd4\x8a\x55\xe6\x44\x20\x16\x31\xd4\x5c\x98\x13\xc7\xde\xc4\x1c\xc7\xc4\x0c\xf0\x8f\x60\xc4\x60\x43\x10\x06\x09\xea\xbc\xa3\xbc\x6a\xa3\x28\x46\x3c\xf0\x61\xaa\xc4\x19\x5b\xc7\x52\xf9\x20\x39\x23\x8b\x7b\x8e\x91\xec\x75\x62\x90\x13\x92\xd6\x11\xe1\x28\x0a\x73\x58\xfb\x36\x06\x6e\x4f\xbd\xd4\xaa\x32\x13\x0b\xbd\x2d\xc3\x50\xad\x1f\x90\x3b\xba\xb3\xc1\xa1\xa5\x36\xf9\xc0\xcc\xfc\x0b\x42\xcf\x88\xed\x7a\xc1\xcd\xeb\xa5\x47\x09\x37\x4e\x52\xef\x8b\x8d\xb3\x36\x51\xe8\xe6\x39\xc1\x5a\x1b\xb7\x6a\x83\xd2\x91\xcb\x18\x9c\xd0\xb0\xbd\x79\xb9\x7d\xd5\x45\x57\x6a\x63\x79\xb5\x99\x3d\x2f\xad\xb2\x45\x57\x0f\xfe\x19\x1c\x30\xdf\xee\x43\x41\x5e\xc7\xed\x31\x06\xda\xc6\x8b\xb3\xad\xfd\xef\x56\xdf\xdc\xa1\x21\xbb\xb4\x58\x75\x3e\x71\xb1\x9b\x53\xeb\x74\xee\xf9\x91\x73\xf6\x3d\x14\x1f\xbe\xc5\xc4\x2b\xcf\x71\x29\x19\x31\xf7\x9f\x51\x6e\x8f\x4f\x88\x4f\x68\x4e\x9a\x3b\x99\xff\xff\x02\x00\x00\xff\xff\x27\x06\x53\xaa\xbb\x88\x00\x00" +var _runtimePluginsAutocloseAutocloseLua = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x56\xd1\x6a\xe3\x3a\x10\x7d\xcf\x57\x0c\xbe\x0f\xb1\x69\x9c\x4b\xee\xd3\xa5\xe0\x85\x6d\xb7\x94\x40\xe8\x2e\x69\x4b\x77\x69\x0b\x95\xe5\x71\x2c\xea\x48\x66\x3c\xda\x74\x29\xfd\xf7\x45\x76\x92\xda\xa9\xeb\x26\x50\x3f\x28\xb6\x66\x8e\x66\x74\x74\x66\x94\xdc\x48\x91\x83\xb5\xac\x72\x88\x40\x2d\x0b\x43\xec\x7b\x4b\x25\xc9\xfc\xeb\x26\xbd\x60\x50\xbb\x54\x53\xbb\x2e\x5b\xab\xe5\xf4\xff\x86\xd1\x7d\x6e\x6d\xc2\xb2\x91\xb9\x29\xf1\x87\x50\x54\x42\x04\xcf\xde\x9d\x77\xe7\x79\x23\xf0\x86\x43\x37\x3e\x3c\xb8\xd1\x0f\xdc\xf8\xfc\xe2\xc6\xdb\x7b\xef\xa5\x81\xbe\xc0\x55\xae\x74\x03\xff\xc6\x77\x90\x5a\x2d\x59\x19\x0d\x32\x13\xf4\x95\xfd\x92\x69\x04\x2a\x18\x00\x00\x84\x21\xe4\x56\x80\xd2\x09\x3e\x29\xbd\x00\x55\x82\xd1\x08\x26\x4d\x21\x25\xb3\x84\x85\xa9\xdc\x08\xd9\x92\xae\xb9\x18\xcf\xad\xc6\xed\x32\xe1\x24\x18\xa0\x4e\x1a\x51\x8c\x76\x0e\x7e\x5c\x8c\x80\xea\x20\xa9\x21\x50\x10\xc1\x64\x04\xff\xec\xec\x38\xa9\xd7\x77\x8f\x4a\x81\x20\x8a\x36\x59\xb6\x1d\x6f\xd5\xfd\x08\xfe\x0b\x80\x33\xd4\x5b\x84\x7b\x6a\x26\xa4\xa5\x99\xd2\x08\x11\xc4\xc5\xf8\xc4\xa6\xc7\xee\xcb\x8f\x8b\xf1\xa9\xa5\xd2\xd0\xf8\x57\x30\x68\xa1\x54\xba\x09\xb3\x46\x8e\xe0\xd5\xf9\xe7\xd1\x24\x38\x3c\x11\xf7\xc4\xc5\xf1\x89\x90\x8f\x65\x21\x24\xfa\x41\x97\xb9\x0e\x31\x57\x8b\x8c\xbb\x1c\x08\xc5\x63\x6b\xb6\xa2\x76\x27\xf3\x46\xa6\xf0\x05\x26\x20\x74\x02\x7e\x7d\x34\xd3\xf2\xc6\x50\x72\x9a\x09\xf2\x7b\xf6\x17\x4e\x82\x00\x0c\xf5\x51\x10\x7e\x44\x81\x5b\xa2\x9b\x83\xce\x3d\x74\xbd\x7f\x7c\xe0\x93\xcf\x3c\xf0\x26\x6d\x51\x54\x95\x65\x25\xe5\x53\x63\x35\x4f\xf5\x25\x93\xd2\x8b\x0d\x19\x15\x3f\xda\x30\x1c\xc2\xeb\xd1\xbb\xa4\x84\xa1\x9b\x87\x61\x38\x84\x0c\x09\x5d\x99\xb1\x81\x04\x09\x53\xd4\x12\x2b\x63\x61\x94\x66\x24\x67\x78\x5d\x73\x66\x24\xac\x32\x25\x33\x07\x71\x0c\x2d\x05\x2b\x29\xf2\xfc\x0f\x2c\x45\x82\x5d\x81\x56\x19\x6a\x90\x46\xff\x46\x62\x57\xd2\x0b\x03\x25\x93\x95\x5c\xc5\xcc\xad\xe8\x02\x4d\x19\x34\x62\x52\xb9\xc4\xb8\xce\x8c\x5c\x6e\x09\xc4\x28\x85\x2d\xeb\x24\xb7\x55\x8e\x4f\x05\xba\x25\x05\x68\xa3\xb7\xb9\xd7\x81\x3a\x84\x5f\x9d\xd1\x54\x97\x48\xec\x87\xad\xed\x8d\x7a\xeb\xac\xa7\x88\x66\x98\xee\x5f\x43\xbb\xef\x9b\xdf\x75\x63\x4b\x45\x5e\xe2\x4e\x1f\x2b\x08\xeb\x84\xd7\x2d\xd6\x8f\x8b\x3a\xda\xde\x0a\x6c\x39\x3b\xa9\x41\xd4\x57\x73\x4d\x80\xc6\x27\xfe\x18\x71\x34\x69\x62\x56\xee\x02\xa8\xf5\x7a\x8e\x3c\x43\x91\x28\xbd\xb8\xc9\x14\x63\xdd\x90\x36\xd2\x1e\x74\x36\xe5\xd6\x45\xd2\x6e\xcb\xdb\xec\x5b\xb5\xda\x04\xbc\x5b\xad\x2a\x6d\x6c\xa5\x1f\xde\xd3\x54\xdb\xe7\xd0\xa9\x89\xda\xe5\x4a\xc4\x9d\xe6\x3e\xf9\x79\x77\xda\x83\xf1\x18\x56\x65\xe7\xc2\x97\x2c\x88\xbf\xa7\xb3\x77\x23\xf7\xaa\xb1\xa5\xaf\x7d\x44\xd9\x54\x25\x93\xed\x10\xe5\xeb\x0d\xb3\x11\xe4\x7e\xf7\xeb\x41\xb2\x85\x4f\xb9\x24\xdd\xcd\xd4\x27\xf8\xc3\xbb\x7f\x5c\x1c\x7f\xc3\x1c\xb9\x79\x16\x6f\xe8\xab\xfe\x7f\x8d\x67\x66\xe1\x7b\xf3\xb3\xab\xeb\xf9\xc5\xf4\xe2\x1c\xae\xae\xe7\x67\x5e\xd0\x49\xef\xdf\x00\x00\x00\xff\xff\x35\x67\xdf\x19\xe9\x09\x00\x00" func runtimePluginsAutocloseAutocloseLuaBytes() ([]byte, error) { return bindataRead( diff --git a/internal/lua/lua.go b/internal/lua/lua.go index e0392e95..ddbcf3de 100644 --- a/internal/lua/lua.go +++ b/internal/lua/lua.go @@ -16,6 +16,7 @@ import ( "runtime" "strings" "time" + "unicode/utf8" lua "github.com/yuin/gopher-lua" luar "layeh.com/gopher-luar" @@ -66,6 +67,8 @@ func Import(pkg string) *lua.LTable { return importErrors() case "time": return importTime() + case "utf8": + return importUtf8() default: return nil } @@ -532,3 +535,24 @@ func importTime() *lua.LTable { return pkg } + +func importUtf8() *lua.LTable { + pkg := L.NewTable() + + L.SetField(pkg, "DecodeLastRune", luar.New(L, utf8.DecodeLastRune)) + L.SetField(pkg, "DecodeLastRuneInString", luar.New(L, utf8.DecodeLastRuneInString)) + L.SetField(pkg, "DecodeRune", luar.New(L, utf8.DecodeRune)) + L.SetField(pkg, "DecodeRuneInString", luar.New(L, utf8.DecodeRuneInString)) + L.SetField(pkg, "EncodeRune", luar.New(L, utf8.EncodeRune)) + L.SetField(pkg, "FullRune", luar.New(L, utf8.FullRune)) + L.SetField(pkg, "FullRuneInString", luar.New(L, utf8.FullRuneInString)) + L.SetField(pkg, "RuneCount", luar.New(L, utf8.RuneCount)) + L.SetField(pkg, "RuneCountInString", luar.New(L, utf8.RuneCountInString)) + L.SetField(pkg, "RuneLen", luar.New(L, utf8.RuneLen)) + L.SetField(pkg, "RuneStart", luar.New(L, utf8.RuneStart)) + L.SetField(pkg, "Valid", luar.New(L, utf8.Valid)) + L.SetField(pkg, "ValidRune", luar.New(L, utf8.ValidRune)) + L.SetField(pkg, "ValidString", luar.New(L, utf8.ValidString)) + + return pkg +} diff --git a/internal/util/lua.go b/internal/util/lua.go new file mode 100644 index 00000000..3f0393d4 --- /dev/null +++ b/internal/util/lua.go @@ -0,0 +1,41 @@ +package util + +import ( + "unicode/utf8" +) + +func LuaRuneAt(str string, runeidx int) string { + i := 0 + for len(str) > 0 { + r, size := utf8.DecodeRuneInString(str) + + str = str[size:] + + if i == runeidx { + return string(r) + } + + i++ + } + return "" +} + +func LuaGetLeadingWhitespace(s string) string { + ws := []byte{} + for len(s) > 0 { + r, size := utf8.DecodeRuneInString(s) + if r == ' ' || r == '\t' { + ws = append(ws, byte(r)) + } else { + break + } + + s = s[size:] + } + return string(ws) +} + +func LuaIsWordChar(s string) bool { + r, _ := utf8.DecodeRuneInString(s) + return IsWordChar(r) +} diff --git a/runtime/plugins/autoclose/autoclose.lua b/runtime/plugins/autoclose/autoclose.lua index e35dc177..05d64273 100644 --- a/runtime/plugins/autoclose/autoclose.lua +++ b/runtime/plugins/autoclose/autoclose.lua @@ -1,1115 +1,58 @@ --- $Id: utf8.lua 179 2009-04-03 18:10:03Z pasta $ --- --- Provides UTF-8 aware string functions implemented in pure lua: --- * utf8len(s) --- * utf8sub(s, i, j) --- * utf8reverse(s) --- * utf8char(unicode) --- * utf8unicode(s, i, j) --- * utf8gensub(s, sub_len) --- * utf8find(str, regex, init, plain) --- * utf8match(str, regex, init) --- * utf8gmatch(str, regex, all) --- * utf8gsub(str, regex, repl, limit) --- --- If utf8data.lua (containing the lower<->upper case mappings) is loaded, these --- additional functions are available: --- * utf8upper(s) --- * utf8lower(s) --- --- All functions behave as their non UTF-8 aware counterparts with the exception --- that UTF-8 characters are used instead of bytes for all units. - ---[[ -Copyright (c) 2006-2007, Kyle Smith -All rights reserved. - -Contributors: - Alimov Stepan - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---]] - --- ABNF from RFC 3629 --- --- UTF8-octets = *( UTF8-char ) --- UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 --- UTF8-1 = %x00-7F --- UTF8-2 = %xC2-DF UTF8-tail --- UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / --- %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) --- UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / --- %xF4 %x80-8F 2( UTF8-tail ) --- UTF8-tail = %x80-BF --- - -local byte = string.byte -local char = string.char -local dump = string.dump -local find = string.find -local format = string.format -local len = string.len -local lower = string.lower -local rep = string.rep -local sub = string.sub -local upper = string.upper - --- returns the number of bytes used by the UTF-8 character at byte i in s --- also doubles as a UTF-8 character validator -local function utf8charbytes (s, i) - -- argument defaults - i = i or 1 - - -- argument checking - if type(s) ~= "string" then - error("bad argument #1 to 'utf8charbytes' (string expected, got ".. type(s).. ")") - end - if type(i) ~= "number" then - error("bad argument #2 to 'utf8charbytes' (number expected, got ".. type(i).. ")") - end - - local c = byte(s, i) - - -- determine bytes needed for character, based on RFC 3629 - -- validate byte 1 - if c > 0 and c <= 127 then - -- UTF8-1 - return 1 - - elseif c >= 194 and c <= 223 then - -- UTF8-2 - local c2 = byte(s, i + 1) - - if not c2 then - error("UTF-8 string terminated early") - end - - -- validate byte 2 - if c2 < 128 or c2 > 191 then - error("Invalid UTF-8 character") - end - - return 2 - - elseif c >= 224 and c <= 239 then - -- UTF8-3 - local c2 = byte(s, i + 1) - local c3 = byte(s, i + 2) - - if not c2 or not c3 then - error("UTF-8 string terminated early") - end - - -- validate byte 2 - if c == 224 and (c2 < 160 or c2 > 191) then - error("Invalid UTF-8 character") - elseif c == 237 and (c2 < 128 or c2 > 159) then - error("Invalid UTF-8 character") - elseif c2 < 128 or c2 > 191 then - error("Invalid UTF-8 character") - end - - -- validate byte 3 - if c3 < 128 or c3 > 191 then - error("Invalid UTF-8 character") - end - - return 3 - - elseif c >= 240 and c <= 244 then - -- UTF8-4 - local c2 = byte(s, i + 1) - local c3 = byte(s, i + 2) - local c4 = byte(s, i + 3) - - if not c2 or not c3 or not c4 then - error("UTF-8 string terminated early") - end - - -- validate byte 2 - if c == 240 and (c2 < 144 or c2 > 191) then - error("Invalid UTF-8 character") - elseif c == 244 and (c2 < 128 or c2 > 143) then - error("Invalid UTF-8 character") - elseif c2 < 128 or c2 > 191 then - error("Invalid UTF-8 character") - end - - -- validate byte 3 - if c3 < 128 or c3 > 191 then - error("Invalid UTF-8 character") - end - - -- validate byte 4 - if c4 < 128 or c4 > 191 then - error("Invalid UTF-8 character") - end - - return 4 - - else - error("Invalid UTF-8 character") - end -end - --- returns the number of characters in a UTF-8 string -local function utf8len (s) - -- argument checking - if type(s) ~= "string" then - for k,v in pairs(s) do print('"',tostring(k),'"',tostring(v),'"') end - error("bad argument #1 to 'utf8len' (string expected, got ".. type(s).. ")") - end - - local pos = 1 - local bytes = len(s) - local length = 0 - - while pos <= bytes do - length = length + 1 - pos = pos + utf8charbytes(s, pos) - end - - return length -end - --- functions identically to string.sub except that i and j are UTF-8 characters --- instead of bytes -local function utf8sub (s, i, j) - -- argument defaults - j = j or -1 - - local pos = 1 - local bytes = len(s) - local length = 0 - - -- only set l if i or j is negative - local l = (i >= 0 and j >= 0) or utf8len(s) - local startChar = (i >= 0) and i or l + i + 1 - local endChar = (j >= 0) and j or l + j + 1 - - -- can't have start before end! - if startChar > endChar then - return "" - end - - -- byte offsets to pass to string.sub - local startByte,endByte = 1,bytes - - while pos <= bytes do - length = length + 1 - - if length == startChar then - startByte = pos - end - - pos = pos + utf8charbytes(s, pos) - - if length == endChar then - endByte = pos - 1 - break - end - end - - if startChar > length then startByte = bytes+1 end - if endChar < 1 then endByte = 0 end - - return sub(s, startByte, endByte) -end - ---[[ --- replace UTF-8 characters based on a mapping table -local function utf8replace (s, mapping) - -- argument checking - if type(s) ~= "string" then - error("bad argument #1 to 'utf8replace' (string expected, got ".. type(s).. ")") - end - if type(mapping) ~= "table" then - error("bad argument #2 to 'utf8replace' (table expected, got ".. type(mapping).. ")") - end - - local pos = 1 - local bytes = len(s) - local charbytes - local newstr = "" - - while pos <= bytes do - charbytes = utf8charbytes(s, pos) - local c = sub(s, pos, pos + charbytes - 1) - - newstr = newstr .. (mapping[c] or c) - - pos = pos + charbytes - end - - return newstr -end - - --- identical to string.upper except it knows about unicode simple case conversions -local function utf8upper (s) - return utf8replace(s, utf8_lc_uc) -end - --- identical to string.lower except it knows about unicode simple case conversions -local function utf8lower (s) - return utf8replace(s, utf8_uc_lc) -end -]] - --- identical to string.reverse except that it supports UTF-8 -local function utf8reverse (s) - -- argument checking - if type(s) ~= "string" then - error("bad argument #1 to 'utf8reverse' (string expected, got ".. type(s).. ")") - end - - local bytes = len(s) - local pos = bytes - local charbytes - local newstr = "" - - while pos > 0 do - local c = byte(s, pos) - while c >= 128 and c <= 191 do - pos = pos - 1 - c = byte(s, pos) - end - - charbytes = utf8charbytes(s, pos) - - newstr = newstr .. sub(s, pos, pos + charbytes - 1) - - pos = pos - 1 - end - - return newstr -end - --- http://en.wikipedia.org/wiki/Utf8 --- http://developer.coronalabs.com/code/utf-8-conversion-utility -local function utf8char(unicode) - if unicode <= 0x7F then return char(unicode) end - - if (unicode <= 0x7FF) then - local Byte0 = 0xC0 + math.floor(unicode / 0x40); - local Byte1 = 0x80 + (unicode % 0x40); - return char(Byte0, Byte1); - end; - - if (unicode <= 0xFFFF) then - local Byte0 = 0xE0 + math.floor(unicode / 0x1000); - local Byte1 = 0x80 + (math.floor(unicode / 0x40) % 0x40); - local Byte2 = 0x80 + (unicode % 0x40); - return char(Byte0, Byte1, Byte2); - end; - - if (unicode <= 0x10FFFF) then - local code = unicode - local Byte3= 0x80 + (code % 0x40); - code = math.floor(code / 0x40) - local Byte2= 0x80 + (code % 0x40); - code = math.floor(code / 0x40) - local Byte1= 0x80 + (code % 0x40); - code = math.floor(code / 0x40) - local Byte0= 0xF0 + code; - - return char(Byte0, Byte1, Byte2, Byte3); - end; - - error 'Unicode cannot be greater than U+10FFFF!' -end - -local shift_6 = 2^6 -local shift_12 = 2^12 -local shift_18 = 2^18 - -local utf8unicode -utf8unicode = function(str, i, j, byte_pos) - i = i or 1 - j = j or i - - if i > j then return end - - local ch,bytes - - if byte_pos then - bytes = utf8charbytes(str,byte_pos) - ch = sub(str,byte_pos,byte_pos-1+bytes) - else - ch,byte_pos = utf8sub(str,i,i), 0 - bytes = #ch - end - - local unicode - - if bytes == 1 then unicode = byte(ch) end - if bytes == 2 then - local byte0,byte1 = byte(ch,1,2) - local code0,code1 = byte0-0xC0,byte1-0x80 - unicode = code0*shift_6 + code1 - end - if bytes == 3 then - local byte0,byte1,byte2 = byte(ch,1,3) - local code0,code1,code2 = byte0-0xE0,byte1-0x80,byte2-0x80 - unicode = code0*shift_12 + code1*shift_6 + code2 - end - if bytes == 4 then - local byte0,byte1,byte2,byte3 = byte(ch,1,4) - local code0,code1,code2,code3 = byte0-0xF0,byte1-0x80,byte2-0x80,byte3-0x80 - unicode = code0*shift_18 + code1*shift_12 + code2*shift_6 + code3 - end - - return unicode,utf8unicode(str, i+1, j, byte_pos+bytes) -end - --- Returns an iterator which returns the next substring and its byte interval -local function utf8gensub(str, sub_len) - sub_len = sub_len or 1 - local byte_pos = 1 - local length = #str - return function(skip) - if skip then byte_pos = byte_pos + skip end - local char_count = 0 - local start = byte_pos - repeat - if byte_pos > length then return end - char_count = char_count + 1 - local bytes = utf8charbytes(str,byte_pos) - byte_pos = byte_pos+bytes - - until char_count == sub_len - - local last = byte_pos-1 - local slice = sub(str,start,last) - return slice, start, last - end -end - -local function binsearch(sortedTable, item, comp) - local head, tail = 1, #sortedTable - local mid = math.floor((head + tail)/2) - if not comp then - while (tail - head) > 1 do - if sortedTable[tonumber(mid)] > item then - tail = mid - else - head = mid - end - mid = math.floor((head + tail)/2) - end - end - if sortedTable[tonumber(head)] == item then - return true, tonumber(head) - elseif sortedTable[tonumber(tail)] == item then - return true, tonumber(tail) - else - return false - end -end -local function classMatchGenerator(class, plain) - local codes = {} - local ranges = {} - local ignore = false - local range = false - local firstletter = true - local unmatch = false - - local it = utf8gensub(class) - - local skip - for c, _, be in it do - skip = be - if not ignore and not plain then - if c == "%" then - ignore = true - elseif c == "-" then - table.insert(codes, utf8unicode(c)) - range = true - elseif c == "^" then - if not firstletter then - error('!!!') - else - unmatch = true - end - elseif c == ']' then - break - else - if not range then - table.insert(codes, utf8unicode(c)) - else - table.remove(codes) -- removing '-' - table.insert(ranges, {table.remove(codes), utf8unicode(c)}) - range = false - end - end - elseif ignore and not plain then - if c == 'a' then -- %a: represents all letters. (ONLY ASCII) - table.insert(ranges, {65, 90}) -- A - Z - table.insert(ranges, {97, 122}) -- a - z - elseif c == 'c' then -- %c: represents all control characters. - table.insert(ranges, {0, 31}) - table.insert(codes, 127) - elseif c == 'd' then -- %d: represents all digits. - table.insert(ranges, {48, 57}) -- 0 - 9 - elseif c == 'g' then -- %g: represents all printable characters except space. - table.insert(ranges, {1, 8}) - table.insert(ranges, {14, 31}) - table.insert(ranges, {33, 132}) - table.insert(ranges, {134, 159}) - table.insert(ranges, {161, 5759}) - table.insert(ranges, {5761, 8191}) - table.insert(ranges, {8203, 8231}) - table.insert(ranges, {8234, 8238}) - table.insert(ranges, {8240, 8286}) - table.insert(ranges, {8288, 12287}) - elseif c == 'l' then -- %l: represents all lowercase letters. (ONLY ASCII) - table.insert(ranges, {97, 122}) -- a - z - elseif c == 'p' then -- %p: represents all punctuation characters. (ONLY ASCII) - table.insert(ranges, {33, 47}) - table.insert(ranges, {58, 64}) - table.insert(ranges, {91, 96}) - table.insert(ranges, {123, 126}) - elseif c == 's' then -- %s: represents all space characters. - table.insert(ranges, {9, 13}) - table.insert(codes, 32) - table.insert(codes, 133) - table.insert(codes, 160) - table.insert(codes, 5760) - table.insert(ranges, {8192, 8202}) - table.insert(codes, 8232) - table.insert(codes, 8233) - table.insert(codes, 8239) - table.insert(codes, 8287) - table.insert(codes, 12288) - elseif c == 'u' then -- %u: represents all uppercase letters. (ONLY ASCII) - table.insert(ranges, {65, 90}) -- A - Z - elseif c == 'w' then -- %w: represents all alphanumeric characters. (ONLY ASCII) - table.insert(ranges, {48, 57}) -- 0 - 9 - table.insert(ranges, {65, 90}) -- A - Z - table.insert(ranges, {97, 122}) -- a - z - elseif c == 'x' then -- %x: represents all hexadecimal digits. - table.insert(ranges, {48, 57}) -- 0 - 9 - table.insert(ranges, {65, 70}) -- A - F - table.insert(ranges, {97, 102}) -- a - f - else - if not range then - table.insert(codes, utf8unicode(c)) - else - table.remove(codes) -- removing '-' - table.insert(ranges, {table.remove(codes), utf8unicode(c)}) - range = false - end - end - ignore = false - else - if not range then - table.insert(codes, utf8unicode(c)) - else - table.remove(codes) -- removing '-' - table.insert(ranges, {table.remove(codes), utf8unicode(c)}) - range = false - end - ignore = false - end - - firstletter = false - end - - table.sort(codes) - - local function inRanges(charCode) - for _,r in ipairs(ranges) do - if r[1] <= charCode and charCode <= r[2] then - return true - end - end - return false - end - if not unmatch then - return function(charCode) - return binsearch(codes, charCode) or inRanges(charCode) - end, skip - else - return function(charCode) - return charCode ~= -1 and not (binsearch(codes, charCode) or inRanges(charCode)) - end, skip - end -end - ---[[ --- utf8sub with extra argument, and extra result value -local function utf8subWithBytes (s, i, j, sb) - -- argument defaults - j = j or -1 - - local pos = sb or 1 - local bytes = len(s) - local length = 0 - - -- only set l if i or j is negative - local l = (i >= 0 and j >= 0) or utf8len(s) - local startChar = (i >= 0) and i or l + i + 1 - local endChar = (j >= 0) and j or l + j + 1 - - -- can't have start before end! - if startChar > endChar then - return "" - end - - -- byte offsets to pass to string.sub - local startByte,endByte = 1,bytes - - while pos <= bytes do - length = length + 1 - - if length == startChar then - startByte = pos - end - - pos = pos + utf8charbytes(s, pos) - - if length == endChar then - endByte = pos - 1 - break - end - end - - if startChar > length then startByte = bytes+1 end - if endChar < 1 then endByte = 0 end - - return sub(s, startByte, endByte), endByte + 1 -end -]] - -local cache = setmetatable({},{ - __mode = 'kv' -}) -local cachePlain = setmetatable({},{ - __mode = 'kv' -}) -local function matcherGenerator(regex, plain) - local matcher = { - functions = {}, - captures = {} - } - if not plain then - cache[regex] = matcher - else - cachePlain[regex] = matcher - end - local function simple(func) - return function(cC) - if func(cC) then - matcher:nextFunc() - matcher:nextStr() - else - matcher:reset() - end - end - end - local function star(func) - return function(cC) - if func(cC) then - matcher:fullResetOnNextFunc() - matcher:nextStr() - else - matcher:nextFunc() - end - end - end - local function minus(func) - return function(cC) - if func(cC) then - matcher:fullResetOnNextStr() - end - matcher:nextFunc() - end - end - local function question(func) - return function(cC) - if func(cC) then - matcher:fullResetOnNextFunc() - matcher:nextStr() - end - matcher:nextFunc() - end - end - - local function capture(id) - return function(_) - local l = matcher.captures[id][2] - matcher.captures[id][1] - local captured = utf8sub(matcher.string, matcher.captures[id][1], matcher.captures[id][2]) - local check = utf8sub(matcher.string, matcher.str, matcher.str + l) - if captured == check then - for _ = 0, l do - matcher:nextStr() - end - matcher:nextFunc() - else - matcher:reset() - end - end - end - local function captureStart(id) - return function(_) - matcher.captures[id][1] = matcher.str - matcher:nextFunc() - end - end - local function captureStop(id) - return function(_) - matcher.captures[id][2] = matcher.str - 1 - matcher:nextFunc() - end - end - - local function balancer(str) - local sum = 0 - local bc, ec = utf8sub(str, 1, 1), utf8sub(str, 2, 2) - local skip = len(bc) + len(ec) - bc, ec = utf8unicode(bc), utf8unicode(ec) - return function(cC) - if cC == ec and sum > 0 then - sum = sum - 1 - if sum == 0 then - matcher:nextFunc() - end - matcher:nextStr() - elseif cC == bc then - sum = sum + 1 - matcher:nextStr() - else - if sum == 0 or cC == -1 then - sum = 0 - matcher:reset() - else - matcher:nextStr() - end - end - end, skip - end - - matcher.functions[1] = function(_) - matcher:fullResetOnNextStr() - matcher.seqStart = matcher.str - matcher:nextFunc() - if (matcher.str > matcher.startStr and matcher.fromStart) or matcher.str >= matcher.stringLen then - matcher.stop = true - matcher.seqStart = nil - end - end - - local lastFunc - local ignore = false - local skip = nil - local it = (function() - local gen = utf8gensub(regex) - return function() - return gen(skip) - end - end)() - local cs = {} - for c, bs, be in it do - skip = nil - if plain then - table.insert(matcher.functions, simple(classMatchGenerator(c, plain))) - else - if ignore then - if find('123456789', c, 1, true) then - if lastFunc then - table.insert(matcher.functions, simple(lastFunc)) - lastFunc = nil - end - table.insert(matcher.functions, capture(tonumber(c))) - elseif c == 'b' then - if lastFunc then - table.insert(matcher.functions, simple(lastFunc)) - lastFunc = nil - end - local b - b, skip = balancer(sub(regex, be + 1, be + 9)) - table.insert(matcher.functions, b) - else - lastFunc = classMatchGenerator('%' .. c) - end - ignore = false - else - if c == '*' then - if lastFunc then - table.insert(matcher.functions, star(lastFunc)) - lastFunc = nil - else - error('invalid regex after ' .. sub(regex, 1, bs)) - end - elseif c == '+' then - if lastFunc then - table.insert(matcher.functions, simple(lastFunc)) - table.insert(matcher.functions, star(lastFunc)) - lastFunc = nil - else - error('invalid regex after ' .. sub(regex, 1, bs)) - end - elseif c == '-' then - if lastFunc then - table.insert(matcher.functions, minus(lastFunc)) - lastFunc = nil - else - error('invalid regex after ' .. sub(regex, 1, bs)) - end - elseif c == '?' then - if lastFunc then - table.insert(matcher.functions, question(lastFunc)) - lastFunc = nil - else - error('invalid regex after ' .. sub(regex, 1, bs)) - end - elseif c == '^' then - if bs == 1 then - matcher.fromStart = true - else - error('invalid regex after ' .. sub(regex, 1, bs)) - end - elseif c == '$' then - if be == len(regex) then - matcher.toEnd = true - else - error('invalid regex after ' .. sub(regex, 1, bs)) - end - elseif c == '[' then - if lastFunc then - table.insert(matcher.functions, simple(lastFunc)) - end - lastFunc, skip = classMatchGenerator(sub(regex, be + 1)) - elseif c == '(' then - if lastFunc then - table.insert(matcher.functions, simple(lastFunc)) - lastFunc = nil - end - table.insert(matcher.captures, {}) - table.insert(cs, #matcher.captures) - table.insert(matcher.functions, captureStart(cs[#cs])) - if sub(regex, be + 1, be + 1) == ')' then matcher.captures[#matcher.captures].empty = true end - elseif c == ')' then - if lastFunc then - table.insert(matcher.functions, simple(lastFunc)) - lastFunc = nil - end - local cap = table.remove(cs) - if not cap then - error('invalid capture: "(" missing') - end - table.insert(matcher.functions, captureStop(cap)) - elseif c == '.' then - if lastFunc then - table.insert(matcher.functions, simple(lastFunc)) - end - lastFunc = function(cC) return cC ~= -1 end - elseif c == '%' then - ignore = true - else - if lastFunc then - table.insert(matcher.functions, simple(lastFunc)) - end - lastFunc = classMatchGenerator(c) - end - end - end - end - if #cs > 0 then - error('invalid capture: ")" missing') - end - if lastFunc then - table.insert(matcher.functions, simple(lastFunc)) - end - - table.insert(matcher.functions, function() - if matcher.toEnd and matcher.str ~= matcher.stringLen then - matcher:reset() - else - matcher.stop = true - end - end) - - matcher.nextFunc = function(self) - self.func = self.func + 1 - end - matcher.nextStr = function(self) - self.str = self.str + 1 - end - matcher.strReset = function(self) - local oldReset = self.reset - local str = self.str - self.reset = function(s) - s.str = str - s.reset = oldReset - end - end - matcher.fullResetOnNextFunc = function(self) - local oldReset = self.reset - local func = self.func +1 - local str = self.str - self.reset = function(s) - s.func = func - s.str = str - s.reset = oldReset - end - end - matcher.fullResetOnNextStr = function(self) - local oldReset = self.reset - local str = self.str + 1 - local func = self.func - self.reset = function(s) - s.func = func - s.str = str - s.reset = oldReset - end - end - - matcher.process = function(self, str, start) - - self.func = 1 - start = start or 1 - self.startStr = (start >= 0) and start or utf8len(str) + start + 1 - self.seqStart = self.startStr - self.str = self.startStr - self.stringLen = utf8len(str) + 1 - self.string = str - self.stop = false - - self.reset = function(s) - s.func = 1 - end - - -- local lastPos = self.str - -- local lastByte - local ch - while not self.stop do - if self.str < self.stringLen then - --[[ if lastPos < self.str then - print('last byte', lastByte) - ch, lastByte = utf8subWithBytes(str, 1, self.str - lastPos - 1, lastByte) - ch, lastByte = utf8subWithBytes(str, 1, 1, lastByte) - lastByte = lastByte - 1 - else - ch, lastByte = utf8subWithBytes(str, self.str, self.str) - end - lastPos = self.str ]] - ch = utf8sub(str, self.str,self.str) - --print('char', ch, utf8unicode(ch)) - self.functions[self.func](utf8unicode(ch)) - else - self.functions[self.func](-1) - end - end - - if self.seqStart then - local captures = {} - for _,pair in pairs(self.captures) do - if pair.empty then - table.insert(captures, pair[1]) - else - table.insert(captures, utf8sub(str, pair[1], pair[2])) - end - end - return self.seqStart, self.str - 1, unpack(captures) - end - end - - return matcher -end - --- string.find -local function utf8find(str, regex, init, plain) - local matcher = cache[regex] or matcherGenerator(regex, plain) - return matcher:process(str, init) -end - --- string.match -local function utf8match(str, regex, init) - init = init or 1 - local found = {utf8find(str, regex, init)} - if found[1] then - if found[3] then - return unpack(found, 3) - end - return utf8sub(str, found[1], found[2]) - end -end - --- string.gmatch -local function utf8gmatch(str, regex, all) - regex = (utf8sub(regex,1,1) ~= '^') and regex or '%' .. regex - local lastChar = 1 - return function() - local found = {utf8find(str, regex, lastChar)} - if found[1] then - lastChar = found[2] + 1 - if found[all and 1 or 3] then - return unpack(found, all and 1 or 3) - end - return utf8sub(str, found[1], found[2]) - end - end -end - -local function replace(repl, args) - local ret = '' - if type(repl) == 'string' then - local ignore = false - local num - for c in utf8gensub(repl) do - if not ignore then - if c == '%' then - ignore = true - else - ret = ret .. c - end - else - num = tonumber(c) - if num then - ret = ret .. args[num] - else - ret = ret .. c - end - ignore = false - end - end - elseif type(repl) == 'table' then - ret = repl[args[1] or args[0]] or '' - elseif type(repl) == 'function' then - if #args > 0 then - ret = repl(unpack(args, 1)) or '' - else - ret = repl(args[0]) or '' - end - end - return ret -end --- string.gsub -local function utf8gsub(str, regex, repl, limit) - limit = limit or -1 - local ret = '' - local prevEnd = 1 - local it = utf8gmatch(str, regex, true) - local found = {it()} - local n = 0 - while #found > 0 and limit ~= n do - local args = {[0] = utf8sub(str, found[1], found[2]), unpack(found, 3)} - ret = ret .. utf8sub(str, prevEnd, found[1] - 1) - .. replace(repl, args) - prevEnd = found[2] + 1 - n = n + 1 - found = {it()} - end - return ret .. utf8sub(str, prevEnd), n -end - -local utf8 = {} -utf8.len = utf8len -utf8.sub = utf8sub -utf8.reverse = utf8reverse -utf8.char = utf8char -utf8.unicode = utf8unicode -utf8.gensub = utf8gensub -utf8.byte = utf8unicode -utf8.find = utf8find -utf8.match = utf8match -utf8.gmatch = utf8gmatch -utf8.gsub = utf8gsub -utf8.dump = dump -utf8.format = format -utf8.lower = lower -utf8.upper = upper -utf8.rep = rep - -function charAt(str, i) - if i <= utf8.len(str) then - return utf8.sub(str, i, i) - else - return "" - end -end - -if GetOption("autoclose") == nil then - AddOption("autoclose", true) -end - +local uutil = import("micro/util") +local utf8 = import("utf8") local autoclosePairs = {"\"\"", "''", "``", "()", "{}", "[]"} local autoNewlinePairs = {"()", "{}", "[]"} -function onRune(r, v) - if not GetOption("autoclose") then - return - end +function charAt(str, i) + -- lua indexing is one off from go + return uutil.RuneAt(str, i-1) +end +function onRune(bp, r) for i = 1, #autoclosePairs do if r == charAt(autoclosePairs[i], 2) then - local curLine = v.Buf:Line(v.Cursor.Y) + local curLine = bp.Buf:Line(bp.Cursor.Y) - if charAt(curLine, v.Cursor.X+1) == charAt(autoclosePairs[i], 2) then - v:Backspace(false) - v:CursorRight(false) + if charAt(curLine, bp.Cursor.X+1) == charAt(autoclosePairs[i], 2) then + bp:Backspace() + bp:CursorRight() break end - if v.Cursor.X > 1 and (IsWordChar(charAt(curLine, v.Cursor.X-1)) or charAt(curLine, v.Cursor.X-1) == charAt(autoclosePairs[i], 1)) then + if bp.Cursor.X > 1 and (uutil.IsWordChar(charAt(curLine, bp.Cursor.X-1)) or charAt(curLine, bp.Cursor.X-1) == charAt(autoclosePairs[i], 1)) then break end end if r == charAt(autoclosePairs[i], 1) then - local curLine = v.Buf:Line(v.Cursor.Y) + local curLine = bp.Buf:Line(bp.Cursor.Y) - if v.Cursor.X == utf8.len(curLine) or not IsWordChar(charAt(curLine, v.Cursor.X+1)) then - -- the '-' here is to derefence the pointer to v.Cursor.Loc which is automatically made + if bp.Cursor.X == utf8.RuneCountInString(curLine) or not uutil.IsWordChar(charAt(curLine, bp.Cursor.X+1)) then + -- the '-' here is to derefence the pointer to bp.Cursor.Loc which is automatically made -- when converting go structs to lua -- It needs to be dereferenced because the function expects a non pointer struct - v.Buf:Insert(-v.Cursor.Loc, charAt(autoclosePairs[i], 2)) - v:CursorLeft(false) + bp.Buf:Insert(-bp.Cursor.Loc, charAt(autoclosePairs[i], 2)) + bp:CursorLeft() break end end end + return false end -function preInsertNewline(v) - if not GetOption("autoclose") then - return - end - - local curLine = v.Buf:Line(v.Cursor.Y) - local curRune = charAt(curLine, v.Cursor.X) - local nextRune = charAt(curLine, v.Cursor.X+1) - local ws = GetLeadingWhitespace(curLine) +function preInsertNewline(bp) + local curLine = bp.Buf:Line(bp.Cursor.Y) + local curRune = charAt(curLine, bp.Cursor.X) + local nextRune = charAt(curLine, bp.Cursor.X+1) + local ws = uutil.GetLeadingWhitespace(curLine) for i = 1, #autoNewlinePairs do if curRune == charAt(autoNewlinePairs[i], 1) then if nextRune == charAt(autoNewlinePairs[i], 2) then - v:InsertNewline(false) - v:InsertTab(false) - v.Buf:Insert(-v.Cursor.Loc, "\n" .. ws) - v:StartOfLine(false) - v:CursorLeft(false) + bp:InsertNewline() + bp:InsertTab() + bp.Buf:Insert(-bp.Cursor.Loc, "\n" .. ws) + bp:StartOfLine() + bp:CursorLeft() return false end end @@ -1118,15 +61,11 @@ function preInsertNewline(v) return true end -function preBackspace(v) - if not GetOption("autoclose") then - return - end - +function preBackspace(bp) for i = 1, #autoclosePairs do - local curLine = v.Buf:Line(v.Cursor.Y) - if charAt(curLine, v.Cursor.X+1) == charAt(autoclosePairs[i], 2) and charAt(curLine, v.Cursor.X) == charAt(autoclosePairs[i], 1) then - v:Delete(false) + local curLine = bp.Buf:Line(bp.Cursor.Y) + if charAt(curLine, bp.Cursor.X+1) == charAt(autoclosePairs[i], 2) and charAt(curLine, bp.Cursor.X) == charAt(autoclosePairs[i], 1) then + bp:Delete() end end