Skip to content
This repository has been archived by the owner on Dec 16, 2021. It is now read-only.

Add new feature: Vim mode #368

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
10de420
Started working on vim event modifiers
invalid-email-address Nov 13, 2020
f0515f4
Added most of the new vim keys (needs rework)
invalid-email-address Nov 14, 2020
a15d76c
Partially working vim movements. Cannot get ESC key to work.
invalid-email-address Nov 15, 2020
26d6a15
Removed some useless lines
invalid-email-address Nov 16, 2020
714e23b
Vim mode working. Added simulation keys inside some lists.
invalid-email-address Nov 16, 2020
ce00566
Dynamic status bar and shortcuts menu
invalid-email-address Nov 16, 2020
56fcff0
Dynamic status bar and shortcuts menu
invalid-email-address Nov 16, 2020
2016462
Fixed some keybindings
invalid-email-address Nov 16, 2020
2c86b71
Merged from upstream
invalid-email-address Nov 16, 2020
7219309
Fixed little issues
invalid-email-address Nov 16, 2020
155c160
Edited changelog
invalid-email-address Nov 16, 2020
ad82e0e
Fixed go.sum
invalid-email-address Nov 16, 2020
9e0b15d
Vim directional keys working everywhere (only in visual mode)
steewbsd Nov 17, 2020
1b1c0c1
Added brief manpage of vim mode
steewbsd Nov 17, 2020
86df4fd
Fixed normal mode binding
steewbsd Nov 17, 2020
a2cc7de
Removed useless go sum lines
steewbsd Nov 17, 2020
5050954
Added replace instruction in go.mod
steewbsd Nov 17, 2020
422d825
Removed upstream fork from go.mod
steewbsd Nov 17, 2020
87a8946
Fixed escape binding
steewbsd Nov 17, 2020
b55c343
Fixed shortcuts dialog inconsistency
steewbsd Nov 17, 2020
b38cbd9
Fixed weird behaviour in text input
steewbsd Nov 17, 2020
6de4e41
Changed expand selection shortcuts, conflicted with movement
steewbsd Nov 18, 2020
eea5b6b
Vim can now be toggled in command mode
steewbsd Nov 18, 2020
1cc4e20
Added vim command synopsis toggle line
steewbsd Nov 18, 2020
9e8bd4c
Moved VimMode to app instead of config
steewbsd Nov 19, 2020
b2bd175
Merged with upstream, fixed conflicts
steewbsd Nov 19, 2020
9d79a7f
Merge branch 'master' of https://github.com/Bios-Marcel/cordless into…
steewbsd Nov 20, 2020
076d73b
"Reverted merge
steewbsd Nov 21, 2020
8774ec7
Fixed windows build
steewbsd Nov 21, 2020
1e22b40
Fixed windows build
steewbsd Nov 21, 2020
e7e4512
Fixed SIGSEGV
steewbsd Nov 21, 2020
dfdc8c6
Attempt to fix appveyor compilation
steewbsd Nov 21, 2020
30b8b96
Fixed go.mod and go.sum
steewbsd Nov 22, 2020
68d0832
Fixed format and qol
steewbsd Nov 22, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/Bios-Marcel/cordless/util/files"
"github.com/Bios-Marcel/cordless/util/vim"
)

const (
Expand Down Expand Up @@ -155,6 +156,8 @@ type Config struct {
// download files to. If FileOpenSaveFilesPermanently has been set to
// true, then all opened files are saved in this folder for example.
FileDownloadSaveLocation string

VimMode *vim.Vim
}

// Account has a name and a token. The name is just for the users recognition.
Expand Down Expand Up @@ -211,6 +214,7 @@ func createDefaultConfig() *Config {
FileOpenHandlers: make(map[string]string),
FileOpenSaveFilesPermanently: false,
FileDownloadSaveLocation: "~/Downloads",
VimMode: &vim.Vim{CurrentMode: vim.Disabled},
}
}

Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/Bios-Marcel/shortnotforlong v1.1.1
github.com/alecthomas/chroma v0.7.3
github.com/atotto/clipboard v0.1.2
github.com/bunyk/gokeybr v0.0.0-20201019133936-f9e4ed3fdc5d // indirect
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Installed some things while working on this, thought I had reverted go.sum and go.mod. Will fix

github.com/gdamore/tcell/v2 v2.0.0
github.com/gen2brain/beeep v0.0.0-20200526185328-e9c15c258e28
github.com/google/go-github/v29 v29.0.3
Expand All @@ -20,6 +21,8 @@ require (
github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac
github.com/sergi/go-diff v1.1.0
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
golang.org/x/tools v0.0.0-20201112171726-b38955972a18 // indirect
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as previous

golang.org/x/tools/gopls v0.5.2 // indirect
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as previous

gopkg.in/sourcemap.v1 v1.0.5 // indirect
rsc.io/qr v0.2.0
)
372 changes: 311 additions & 61 deletions shortcuts/shortcuts.go

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion shortcuts/shortcuts_nix.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ package shortcuts
import tcell "github.com/gdamore/tcell/v2"

func addDeleteLeftShortcut() *Shortcut {
return addShortcut("delete_left", "Delete left", multilineTextInput, tcell.NewEventKey(tcell.KeyBackspace2, rune(tcell.KeyBackspace2), tcell.ModNone))
// FIXME don't know what to do with this vim binding
return addShortcut("delete_left", "Delete left", multilineTextInput, tcell.NewEventKey(tcell.KeyBackspace2, rune(tcell.KeyBackspace2), tcell.ModNone),addVimEvent(NullVimEvent,nil,NullVimEvent))
}
2 changes: 1 addition & 1 deletion shortcuts/shortcuts_win.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ package shortcuts
import tcell "github.com/gdamore/tcell/v2"

func addDeleteLeftShortcut() *Shortcut {
return addShortcut("delete_left", "Delete left", multilineTextInput, tcell.NewEventKey(tcell.KeyBackspace, rune(tcell.KeyBackspace), tcell.ModNone))
return addShortcut("delete_left", "Delete left", multilineTextInput, tcell.NewEventKey(tcell.KeyBackspace, rune(tcell.KeyBackspace), tcell.ModNone),addVimKey(NullVimKey,nil,NullVimKey))
}
22 changes: 17 additions & 5 deletions ui/components/bottombar.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import (
type BottomBar struct {
*sync.Mutex
*tview.Box
items []*bottomBarItem
items []*BottomBarItem
}

type bottomBarItem struct {
content string
// BottomBarItem represents an entry in a BottomBar. It simply holds a static
// text that can technically be changed after adding the item.
type BottomBarItem struct {
Content string
}

// Draw draws this primitive onto the screen. Implementers can call the
Expand All @@ -45,7 +47,7 @@ func (b *BottomBar) Draw(screen tcell.Screen) bool {

xPos, yPos, _, _ := b.GetInnerRect()
for _, item := range b.items {
gr := uniseg.NewGraphemes(item.content)
gr := uniseg.NewGraphemes(item.Content)
for gr.Next() {
r := gr.Runes()
width := runewidth.StringWidth(gr.Str())
Expand All @@ -69,7 +71,7 @@ func (b *BottomBar) Draw(screen tcell.Screen) bool {
func (b *BottomBar) AddItem(text string) {
b.Lock()
defer b.Unlock()
b.items = append(b.items, &bottomBarItem{text})
b.items = append(b.items, &BottomBarItem{text})
}

// NewBottomBar creates a new bar to be put at the bottom aplication.
Expand All @@ -83,3 +85,13 @@ func NewBottomBar() *BottomBar {

return bottomBar
}

// AddDynamicItem adds and item without content that can be filled from outside
// by assigning a new value to the Content field of the returned item.
func (b *BottomBar) AddDynamicItem() *BottomBarItem {
newItem := &BottomBarItem{}
b.Lock()
defer b.Unlock()
b.items = append(b.items, newItem)
return newItem
}
12 changes: 12 additions & 0 deletions ui/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,18 @@ func NewEditor() *Editor {

if shortcuts.MoveCursorLeft.Equals(event) {
editor.MoveCursorLeft()

// Vim mode shortcuts
} else if shortcuts.VimInsertMode.Equals(event) {
config.Current.VimMode.Insert()
return nil
} else if shortcuts.VimVisualMode.Equals(event) {
config.Current.VimMode.Visual()
return nil
} else if shortcuts.VimNormalMode.Equals(event) {
config.Current.VimMode.Normal()


} else if shortcuts.ExpandSelectionToLeft.Equals(event) {
editor.SelectionToLeft()
} else if shortcuts.MoveCursorRight.Equals(event) {
Expand Down
55 changes: 55 additions & 0 deletions ui/shortcutdialog/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ const (
actionCellIndex int = iota
scopeCellIndex
shortcutCellIndex
vimNormalCellIndex
vimInsertCellIndex
vimVisualCellIndex
)

// ShortcutTable is a component that displays shortcuts and allows changing
// them.
type ShortcutTable struct {
table *tview.Table
shortcuts []*shortcuts.Shortcut
vimBindings []*shortcuts.VimEvent
selection int
focusNext func()
focusPrevious func()
Expand All @@ -42,6 +46,9 @@ func NewShortcutTable() *ShortcutTable {
table.SetCell(0, actionCellIndex, createHeaderCell("Action"))
table.SetCell(0, scopeCellIndex, createHeaderCell("Scope"))
table.SetCell(0, shortcutCellIndex, createHeaderCell("Shortcut"))
table.SetCell(0, vimNormalCellIndex, createHeaderCell("Vim: N"))
table.SetCell(0, vimInsertCellIndex, createHeaderCell("Vim: I"))
table.SetCell(0, vimVisualCellIndex, createHeaderCell("Vim: V"))

table.SetInputCapture(shortcutsTable.handleInput)

Expand Down Expand Up @@ -90,6 +97,54 @@ func (shortcutTable *ShortcutTable) SetShortcuts(shortcuts []*shortcuts.Shortcut
SetMaxWidth(1)
shortcutTable.table.SetCell(row, shortcutCellIndex, eventCell)

normalKey := ""
insertKey := ""
visualKey := ""

// HACK
// Empty tcell EventKey corresponds to Ctrl-Space
if shortcut.VimModifier.NormalEvent == nil {
normalKey = EventToString(shortcut.Event)
} else if shortcut.VimModifier.NormalEvent.Rune() == rune(tcell.KeyCtrlSpace){
normalKey = ""
} else {
normalKey = EventToString(shortcut.VimModifier.NormalEvent)
}

if shortcut.VimModifier.InsertEvent == nil {
insertKey = EventToString(shortcut.Event)
} else if shortcut.VimModifier.InsertEvent.Rune() == rune(tcell.KeyCtrlSpace){
insertKey = ""
} else {
insertKey = EventToString(shortcut.VimModifier.InsertEvent)
}

if shortcut.VimModifier.VisualEvent == nil {
visualKey = EventToString(shortcut.Event)
} else if shortcut.VimModifier.VisualEvent.Rune() == rune(tcell.KeyCtrlSpace){
visualKey = ""
} else {
visualKey = EventToString(shortcut.VimModifier.VisualEvent)
}

vimNormalCell := tview.NewTableCell(fmt.Sprintf("%s",
normalKey)).
SetExpansion(1).
SetMaxWidth(1)
shortcutTable.table.SetCell(row, vimNormalCellIndex, vimNormalCell)

vimInsertCell := tview.NewTableCell(fmt.Sprintf("%s",
insertKey)).
SetExpansion(1).
SetMaxWidth(1)
shortcutTable.table.SetCell(row, vimInsertCellIndex, vimInsertCell)

vimVisualCell := tview.NewTableCell(fmt.Sprintf("%s",
visualKey)).
SetExpansion(1).
SetMaxWidth(1)
shortcutTable.table.SetCell(row, vimVisualCellIndex, vimVisualCell)

row++
}
}
Expand Down
76 changes: 73 additions & 3 deletions ui/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/Bios-Marcel/cordless/util/files"
"github.com/Bios-Marcel/cordless/util/fuzzy"
"github.com/Bios-Marcel/cordless/util/text"
"github.com/Bios-Marcel/cordless/util/vim"
"github.com/Bios-Marcel/cordless/version"

"github.com/Bios-Marcel/discordemojimap"
Expand All @@ -42,6 +43,7 @@ import (
"github.com/Bios-Marcel/cordless/ui/shortcutdialog"
"github.com/Bios-Marcel/cordless/ui/tviewutil"
"github.com/Bios-Marcel/cordless/util/maths"
//"github.com/Bios-Marcel/cordless/util/vim"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's with this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot _:(

)

var (
Expand Down Expand Up @@ -91,6 +93,8 @@ type Window struct {

bareChat bool
activeView ActiveView

vimStatus *components.BottomBarItem
}

type ActiveView bool
Expand Down Expand Up @@ -742,7 +746,13 @@ func NewWindow(app *tview.Application, session *discordgo.Session, readyEvent *d

window.userList = NewUserTree(window.session.State)

if config.Current.OnTypeInListBehaviour == config.SearchOnTypeInList {
// Disable search on type when in Vim mode. TODO add / shourtcut to search.
if config.Current.VimMode.CurrentMode != vim.Disabled {
guildList.SetSearchOnTypeEnabled(false)
channelTree.SetSearchOnTypeEnabled(false)
window.userList.internalTreeView.SetSearchOnTypeEnabled(false)
window.privateList.internalTreeView.SetSearchOnTypeEnabled(false)
} else if config.Current.OnTypeInListBehaviour == config.SearchOnTypeInList {
guildList.SetSearchOnTypeEnabled(true)
channelTree.SetSearchOnTypeEnabled(true)
window.userList.internalTreeView.SetSearchOnTypeEnabled(true)
Expand All @@ -758,6 +768,17 @@ func NewWindow(app *tview.Application, session *discordgo.Session, readyEvent *d
}

newGuildHandler := func(event *tcell.EventKey) *tcell.EventKey {

// Workaround for vim bindings inside list
if shortcuts.VimSimKeyDown.Equals(event) {
return tcell.NewEventKey(tcell.KeyDown, rune(tcell.KeyDown), tcell.ModNone)
} else if shortcuts.VimSimKeyUp.Equals(event) {
return tcell.NewEventKey(tcell.KeyUp, rune(tcell.KeyUp), tcell.ModNone)
} else if shortcuts.VimSimKeyRight.Equals(event) {
return tcell.NewEventKey(tcell.KeyRight, rune(tcell.KeyRight), tcell.ModNone)
} else if shortcuts.VimSimKeyLeft.Equals(event) {
return tcell.NewEventKey(tcell.KeyLeft, rune(tcell.KeyLeft), tcell.ModNone)
}
if shortcuts.GuildListMarkRead.Equals(event) {
selectedGuildNode := guildList.GetCurrentNode()
if selectedGuildNode != nil && !readstate.HasGuildBeenRead(selectedGuildNode.GetReference().(string)) {
Expand Down Expand Up @@ -787,6 +808,17 @@ func NewWindow(app *tview.Application, session *discordgo.Session, readyEvent *d
}

newChannelListHandler := func(event *tcell.EventKey) *tcell.EventKey {

// Workaround for vim bindings inside list
if shortcuts.VimSimKeyDown.Equals(event) {
return tcell.NewEventKey(tcell.KeyDown, rune(tcell.KeyDown), tcell.ModNone)
} else if shortcuts.VimSimKeyUp.Equals(event) {
return tcell.NewEventKey(tcell.KeyUp, rune(tcell.KeyUp), tcell.ModNone)
} else if shortcuts.VimSimKeyRight.Equals(event) {
return tcell.NewEventKey(tcell.KeyRight, rune(tcell.KeyRight), tcell.ModNone)
} else if shortcuts.VimSimKeyLeft.Equals(event) {
return tcell.NewEventKey(tcell.KeyLeft, rune(tcell.KeyLeft), tcell.ModNone)
}
if shortcuts.ChannelTreeMarkRead.Equals(event) {
selectedChannelNode := channelTree.GetCurrentNode()
if selectedChannelNode != nil {
Expand Down Expand Up @@ -862,6 +894,10 @@ func NewWindow(app *tview.Application, session *discordgo.Session, readyEvent *d
bottomBar := components.NewBottomBar()
bottomBar.AddItem(fmt.Sprintf("Logged in as: '%s'", tviewutil.Escape(session.State.User.Username)))
bottomBar.AddItem(fmt.Sprintf("View / Change shortcuts: %s", shortcutdialog.EventToString(shortcutsDialogShortcut)))
// bottomBar.AddItem(fmt.Sprintf("Vim: %s", config.Current.VimMode.EnabledString()))
window.vimStatus = bottomBar.AddDynamicItem()
// Default content
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment provides no value.

window.vimStatus.Content = fmt.Sprintf("Vim: %s", config.Current.VimMode.CurrentModeString())
window.rootContainer.AddItem(bottomBar, 1, 0, false)
}

Expand Down Expand Up @@ -955,8 +991,16 @@ important changes of the last two versions officially released.

[::b]THIS VERSION
- Features
- Changes
- Bugfixes
[::b]2020-11-16
- Features
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be one item named Vim mode and sub-items should only list necessary infos. The changelog is not documentation (I am not saying we don't need docs)

Also, the - starts a new item, not a new line. This is basically plain markdown ^^

- Added Vim mode
- Enable vim mode in your config file, setting VimMode to 0.
- Navigate menus with h j k l in normal mode, enter focus with either
- insert mode or visual mode.
- Navigate inside lists with vim keys.
- Use some of your known bindings inside chat view, or selection mode.
- More vim features will be added in next updates, and bugs will try to be
- fixed :)
[::b]2020-10-24
- Features
- DM people via "p" in the chatview or use the dm-open command
Expand Down Expand Up @@ -2114,8 +2158,23 @@ func (window *Window) handleGlobalShortcuts(event *tcell.EventKey) *tcell.EventK
//window#Shutdown unnecessary, as we shut the whole process down.
window.app.Stop()
return nil
} else if shortcuts.VimInsertMode.Equals(event) {
config.Current.VimMode.Insert()
window.vimStatus.Content = fmt.Sprintf("Vim: %s",config.Current.VimMode.CurrentModeString())
return nil
} else if shortcuts.VimVisualMode.Equals(event) {
config.Current.VimMode.Visual()
window.vimStatus.Content = fmt.Sprintf("Vim: %s",config.Current.VimMode.CurrentModeString())
return nil
} else if shortcuts.VimNormalMode.Equals(event) {
config.Current.VimMode.Normal()
window.vimStatus.Content = fmt.Sprintf("Vim: %s",config.Current.VimMode.CurrentModeString())
return nil
}

window.app.QueueUpdateDraw(func() {
})

// Maybe compare directly to table?
if config.Current.DesktopNotificationsUserInactivityThreshold > 0 {
window.userActive = true
Expand All @@ -2133,6 +2192,17 @@ func (window *Window) handleChatWindowShortcuts(event *tcell.EventKey) *tcell.Ev
return event
}

// Workaround for vim bindings inside list
if shortcuts.VimSimKeyDown.Equals(event) {
return tcell.NewEventKey(tcell.KeyDown, rune(tcell.KeyDown), tcell.ModNone)
} else if shortcuts.VimSimKeyUp.Equals(event) {
return tcell.NewEventKey(tcell.KeyUp, rune(tcell.KeyUp), tcell.ModNone)
} else if shortcuts.VimSimKeyRight.Equals(event) {
return tcell.NewEventKey(tcell.KeyRight, rune(tcell.KeyRight), tcell.ModNone)
} else if shortcuts.VimSimKeyLeft.Equals(event) {
return tcell.NewEventKey(tcell.KeyLeft, rune(tcell.KeyLeft), tcell.ModNone)
}

if shortcuts.DirectionalFocusHandling(event, window.app) == nil {
return nil
}
Expand Down
Loading