From 5fda08dfbbbe9e636fd7c28749069c81e9b5d1f3 Mon Sep 17 00:00:00 2001 From: Antoine Broyelle Date: Fri, 20 May 2016 18:37:11 +0200 Subject: [PATCH 01/12] Added modes representation A mode is a set of keybindings. All modes are stored in a map in the gui struct. We can switch between modes. --- gui.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/gui.go b/gui.go index d361afbb..19561386 100644 --- a/gui.go +++ b/gui.go @@ -18,12 +18,24 @@ type userEvent struct { h Handler } +// kbSet is a set of keybindings representing a mode +type kbSet []*keybinding + +// modeMap +type modeMap map[string]kbSet + var ( // ErrQuit is used to decide if the MainLoop finished successfully. ErrQuit = errors.New("quit") // ErrUnknownView allows to assert if a View must be initialized. ErrUnknownView = errors.New("unknown view") + + // ErrUninitModMap checks map initialization + ErrUninitModMap = errors.New("Modes Map unitialized : Cant create mode") + + // ErrUnknowMode checks map initialization + ErrUnknowMode = errors.New("unknown mode") ) // Gui represents the whole User Interface, including the views, layouts @@ -34,7 +46,8 @@ type Gui struct { views []*View currentView *View layout Handler - keybindings []*keybinding + modes modeMap + keybindings kbSet maxX, maxY int // BgColor and FgColor allow to configure the background and foreground @@ -74,9 +87,44 @@ func (g *Gui) Init() error { g.BgColor = ColorBlack g.FgColor = ColorWhite g.Editor = DefaultEditor + g.modes = make(modeMap) + return nil } +// CreateMode makes a new kbSet for the mode call name +// If the mode already exists, the kbSet will be flush +func (g *Gui) CreateMode(name string) error { + if g.modes == nil { + return ErrUninitModMap + } + g.modes[name] = make(kbSet, 0, 3) + return nil +} + +// SetCurrentMode switches the current keybindings set according to input name +func (g *Gui) SetCurrentMode(name string) error { + if g.modes == nil { + return ErrUninitModMap + } + if _, ok := g.modes[name]; !ok { + return ErrUnknowMode + } + g.keybindings = g.modes[name] + return nil +} + +// GetModeKeyBindings returns keybindings set from its name +// func (g *Gui) GetModeKeyBindings(name string) kbSet { +// if g.modes == nil { +// return nil +// } +// if _, ok := g.modes[name]; !ok { +// return nil +// } +// return g.modes[name] +// } + // Close finalizes the library. It should be called after a successful // initialization and when gocui is not needed anymore. func (g *Gui) Close() { @@ -214,7 +262,7 @@ func (g *Gui) CurrentView() *View { // SetKeybinding creates a new keybinding. If viewname equals to "" // (empty string) then the keybinding will apply to all views. key must // be a rune or a Key. -func (g *Gui) SetKeybinding(viewname string, key interface{}, mod Modifier, h KeybindingHandler) error { +func (g *Gui) SetKeybinding(modename string, viewname string, key interface{}, mod Modifier, h KeybindingHandler) error { var kb *keybinding switch k := key.(type) { @@ -225,7 +273,7 @@ func (g *Gui) SetKeybinding(viewname string, key interface{}, mod Modifier, h Ke default: return errors.New("unknown type") } - g.keybindings = append(g.keybindings, kb) + g.modes[modename] = append(g.modes[modename], kb) return nil } From 885567582a859bbe32bbf6d9d52bb4c8abe668d9 Mon Sep 17 00:00:00 2001 From: Valentin Matton Date: Sat, 21 May 2016 17:07:06 +0200 Subject: [PATCH 02/12] Changed mode implementation --- gui.go | 85 +++++++++++++++++++++++++-------------------------- keybinding.go | 3 ++ 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/gui.go b/gui.go index 19561386..20af8b40 100644 --- a/gui.go +++ b/gui.go @@ -18,12 +18,6 @@ type userEvent struct { h Handler } -// kbSet is a set of keybindings representing a mode -type kbSet []*keybinding - -// modeMap -type modeMap map[string]kbSet - var ( // ErrQuit is used to decide if the MainLoop finished successfully. ErrQuit = errors.New("quit") @@ -31,9 +25,6 @@ var ( // ErrUnknownView allows to assert if a View must be initialized. ErrUnknownView = errors.New("unknown view") - // ErrUninitModMap checks map initialization - ErrUninitModMap = errors.New("Modes Map unitialized : Cant create mode") - // ErrUnknowMode checks map initialization ErrUnknowMode = errors.New("unknown mode") ) @@ -46,8 +37,8 @@ type Gui struct { views []*View currentView *View layout Handler - modes modeMap - keybindings kbSet + modes []*Mode + currentMode *Mode maxX, maxY int // BgColor and FgColor allow to configure the background and foreground @@ -87,43 +78,48 @@ func (g *Gui) Init() error { g.BgColor = ColorBlack g.FgColor = ColorWhite g.Editor = DefaultEditor - g.modes = make(modeMap) return nil } -// CreateMode makes a new kbSet for the mode call name -// If the mode already exists, the kbSet will be flush -func (g *Gui) CreateMode(name string) error { - if g.modes == nil { - return ErrUninitModMap +// SetCurrentMode switches to the Mode with the given name +func (g *Gui) SetCurrentMode(name string) error { + for _, m := range g.modes { + if m.name == name { + g.currentMode = m + return nil + } } - g.modes[name] = make(kbSet, 0, 3) - return nil + return ErrUnknowMode } -// SetCurrentMode switches the current keybindings set according to input name -func (g *Gui) SetCurrentMode(name string) error { - if g.modes == nil { - return ErrUninitModMap - } - if _, ok := g.modes[name]; !ok { - return ErrUnknowMode +// CurrentMode returns the current mode +func (g *Gui) CurrentMode() *Mode { + return g.currentMode +} + +// Mode returns a pointer to the Mode with the given name, or error +// ErrUnknownMode if a Mode with that name does not exist. +func (g *Gui) Mode(name string) (*Mode, error) { + for _, m := range g.modes { + if m.name == name { + g.currentMode = m + return m, nil + } } - g.keybindings = g.modes[name] - return nil + return nil, ErrUnknowMode } -// GetModeKeyBindings returns keybindings set from its name -// func (g *Gui) GetModeKeyBindings(name string) kbSet { -// if g.modes == nil { -// return nil -// } -// if _, ok := g.modes[name]; !ok { -// return nil -// } -// return g.modes[name] -// } +// SetMode creates a new mode +// does nothing if there is already a mode for this name +func (g *Gui) SetMode(name string) { + if _, err := g.Mode(name); err == nil { + return + } + + m := CreateMode(name) + g.modes = append(g.modes, m) +} // Close finalizes the library. It should be called after a successful // initialization and when gocui is not needed anymore. @@ -262,18 +258,21 @@ func (g *Gui) CurrentView() *View { // SetKeybinding creates a new keybinding. If viewname equals to "" // (empty string) then the keybinding will apply to all views. key must // be a rune or a Key. -func (g *Gui) SetKeybinding(modename string, viewname string, key interface{}, mod Modifier, h KeybindingHandler) error { +func (g *Gui) SetKeybinding(modeName string, viewName string, key interface{}, mod Modifier, h KeybindingHandler) error { var kb *keybinding switch k := key.(type) { case Key: - kb = newKeybinding(viewname, k, 0, mod, h) + kb = newKeybinding(viewName, k, 0, mod, h) case rune: - kb = newKeybinding(viewname, 0, k, mod, h) + kb = newKeybinding(viewName, 0, k, mod, h) default: return errors.New("unknown type") } - g.modes[modename] = append(g.modes[modename], kb) + + if m, err := g.Mode(modeName); err == nil { + *m.GetKeyBindings() = append(*m.GetKeyBindings(), kb) + } return nil } @@ -608,7 +607,7 @@ func (g *Gui) onKey(ev *termbox.Event) error { curView = v } - for _, kb := range g.keybindings { + for _, kb := range g.currentMode.keybindings { if kb.h == nil { continue } diff --git a/keybinding.go b/keybinding.go index 61516821..978401b8 100644 --- a/keybinding.go +++ b/keybinding.go @@ -115,6 +115,9 @@ type keybinding struct { h KeybindingHandler } +// kbSet is a set of keybindings representing a mode +type kbSet []*keybinding + // newKeybinding returns a new Keybinding object. func newKeybinding(viewname string, key Key, ch rune, mod Modifier, h KeybindingHandler) (kb *keybinding) { kb = &keybinding{ From aff95c10f86ff566255ddf5f1f0f61c36daf536e Mon Sep 17 00:00:00 2001 From: Valentin Matton Date: Sun, 22 May 2016 10:58:39 +0200 Subject: [PATCH 03/12] added mode file --- mode.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 mode.go diff --git a/mode.go b/mode.go new file mode 100644 index 00000000..0887c902 --- /dev/null +++ b/mode.go @@ -0,0 +1,16 @@ +package gocui + +type Mode struct { + name string + keybindings kbSet +} + +// CreateMode makes a new kbSet for the mode call name +// If the mode already exists, the kbSet will be flush +func CreateMode(name string) *Mode { + return &Mode{name: name} +} + +func (m *Mode) GetKeyBindings() *kbSet { + return &m.keybindings +} From 5b3f3da55d07e0b25c5cc57ea87ad511c83eb3d4 Mon Sep 17 00:00:00 2001 From: Antoine Broyelle Date: Sun, 22 May 2016 12:15:35 +0200 Subject: [PATCH 04/12] Bug fix: avoid infinite scroll take into account the buffer size in MoveCursor --- edit.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/edit.go b/edit.go index eb888190..524e947b 100644 --- a/edit.go +++ b/edit.go @@ -124,6 +124,10 @@ func (v *View) MoveCursor(dx, dy int, writeMode bool) { maxX, maxY := v.Size() cx, cy := v.cx+dx, v.cy+dy x, y := v.ox+cx, v.oy+cy + if y > len(v.lines) { + cy -= y - len(v.lines) + y -= y - len(v.lines) + } var curLineWidth, prevLineWidth int // get the width of the current line @@ -152,7 +156,7 @@ func (v *View) MoveCursor(dx, dy int, writeMode bool) { // adjust cursor's x position and view's x origin if x > curLineWidth { // move to next line - if dx > 0 { // horizontal movement + if dx > 0 && y < len(v.lines) { // horizontal movement if !v.Wrap { v.ox = 0 } From 7c427be64bc13938378340a0286ea50009fd82a2 Mon Sep 17 00:00:00 2001 From: Valentin Matton Date: Sun, 22 May 2016 17:55:40 +0200 Subject: [PATCH 05/12] Added name Mode accessor --- mode.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mode.go b/mode.go index 0887c902..4635aafb 100644 --- a/mode.go +++ b/mode.go @@ -5,8 +5,6 @@ type Mode struct { keybindings kbSet } -// CreateMode makes a new kbSet for the mode call name -// If the mode already exists, the kbSet will be flush func CreateMode(name string) *Mode { return &Mode{name: name} } @@ -14,3 +12,7 @@ func CreateMode(name string) *Mode { func (m *Mode) GetKeyBindings() *kbSet { return &m.keybindings } + +func (m *Mode) Name() string { + return m.name +} From b767ca4705930c534425d3bb4107cb4b1ca172db Mon Sep 17 00:00:00 2001 From: Antoine Broyelle Date: Mon, 23 May 2016 15:46:06 +0200 Subject: [PATCH 06/12] Added accessor for buffer length (number of line) the accessor is named BufferSize --- view.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/view.go b/view.go index 176ab9e2..b2eb6d09 100644 --- a/view.go +++ b/view.go @@ -95,6 +95,11 @@ func (v *View) Name() string { return v.name } +// Size returns the number of lines contained in the buffer +func (v *View) BufferSize() int { + return len(v.lines) +} + // setRune writes a rune at the given point, relative to the view. It // checks if the position is valid and applies the view's colors, taking // into account if the cell must be highlighted. From 576579c771f6b014fcef7071610143c1e93757b9 Mon Sep 17 00:00:00 2001 From: Antoine Broyelle Date: Mon, 23 May 2016 16:23:25 +0200 Subject: [PATCH 07/12] Bugfix : convert buffer to string without ending null rude We juste delete the two last characteres --- view.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/view.go b/view.go index b2eb6d09..f19742c7 100644 --- a/view.go +++ b/view.go @@ -456,6 +456,7 @@ func (v *View) Buffer() string { for _, l := range v.lines { str += string(l) + "\n" } + str = str[:len(str)-2] return strings.Replace(str, "\x00", " ", -1) } @@ -466,6 +467,7 @@ func (v *View) ViewBuffer() string { for _, l := range v.viewLines { str += string(l.line) + "\n" } + str = str[:len(str)-2] return strings.Replace(str, "\x00", " ", -1) } From 2f6a7817b5b63f49930f7dc280b45cde6d2f5d43 Mon Sep 17 00:00:00 2001 From: Antoine Broyelle Date: Mon, 23 May 2016 17:16:58 +0200 Subject: [PATCH 08/12] Revert "Bugfix : convert buffer to string without ending null rude" This reverts commit 576579c771f6b014fcef7071610143c1e93757b9. --- view.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/view.go b/view.go index f19742c7..b2eb6d09 100644 --- a/view.go +++ b/view.go @@ -456,7 +456,6 @@ func (v *View) Buffer() string { for _, l := range v.lines { str += string(l) + "\n" } - str = str[:len(str)-2] return strings.Replace(str, "\x00", " ", -1) } @@ -467,7 +466,6 @@ func (v *View) ViewBuffer() string { for _, l := range v.viewLines { str += string(l.line) + "\n" } - str = str[:len(str)-2] return strings.Replace(str, "\x00", " ", -1) } From 8df6b2e056a6ea83b269b24520a12951bf088f87 Mon Sep 17 00:00:00 2001 From: Valentin Matton Date: Mon, 23 May 2016 17:28:13 +0200 Subject: [PATCH 09/12] Minor fix --- gui.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/gui.go b/gui.go index 20af8b40..1fe69f65 100644 --- a/gui.go +++ b/gui.go @@ -313,16 +313,18 @@ func (g *Gui) MainLoop() error { return err } for { - select { - case ev := <-g.tbEvents: - if err := g.handleEvent(&ev); err != nil { - return err - } - case ev := <-g.userEvents: - if err := ev.h(g); err != nil { - return err + /* + select { + case ev := <-g.tbEvents: + if err := g.handleEvent(&ev); err != nil { + return err + } + case ev := <-g.userEvents: + if err := ev.h(g); err != nil { + return err + } } - } + */ if err := g.consumeevents(); err != nil { return err } From 89ff42a8887a2e8e8de02baf571433f13655763d Mon Sep 17 00:00:00 2001 From: Valentin Matton Date: Tue, 24 May 2016 16:01:24 +0200 Subject: [PATCH 10/12] Fix main loop --- gui.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/gui.go b/gui.go index 1fe69f65..a3494fb9 100644 --- a/gui.go +++ b/gui.go @@ -313,18 +313,18 @@ func (g *Gui) MainLoop() error { return err } for { - /* - select { - case ev := <-g.tbEvents: - if err := g.handleEvent(&ev); err != nil { - return err - } - case ev := <-g.userEvents: - if err := ev.h(g); err != nil { - return err - } + + select { + case ev := <-g.tbEvents: + if err := g.handleEvent(&ev); err != nil { + return err } - */ + case ev := <-g.userEvents: + if err := ev.h(g); err != nil { + return err + } + } + if err := g.consumeevents(); err != nil { return err } From 19b2dff196c97a34c181fc9d8a27dc5c918512b1 Mon Sep 17 00:00:00 2001 From: Valentin Matton Date: Tue, 24 May 2016 20:40:25 +0200 Subject: [PATCH 11/12] Simple editor: remove breakline this functionality should be in the main program because it depends on the current mode and view --- edit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/edit.go b/edit.go index 524e947b..dd893763 100644 --- a/edit.go +++ b/edit.go @@ -37,8 +37,8 @@ func simpleEditor(v *View, key Key, ch rune, mod Modifier) { v.EditDelete(false) case key == KeyInsert: v.Overwrite = !v.Overwrite - case key == KeyEnter: - v.EditNewLine() + //case key == KeyEnter: + //v.EditNewLine() case key == KeyArrowDown: v.MoveCursor(0, 1, false) case key == KeyArrowUp: From 67252e8b2523ab07aadf872dba58602bbf55299f Mon Sep 17 00:00:00 2001 From: Antoine Broyelle Date: Tue, 24 May 2016 20:46:38 +0200 Subject: [PATCH 12/12] restore MoveCursor rules : revert 5b3f3da5 --- edit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edit.go b/edit.go index dd893763..3819a353 100644 --- a/edit.go +++ b/edit.go @@ -156,7 +156,7 @@ func (v *View) MoveCursor(dx, dy int, writeMode bool) { // adjust cursor's x position and view's x origin if x > curLineWidth { // move to next line - if dx > 0 && y < len(v.lines) { // horizontal movement + if dx > 0 { // horizontal movement if !v.Wrap { v.ox = 0 }