started to cleanup the command processing

This commit is contained in:
Felix Angell 2021-01-10 14:17:37 +00:00
parent f26a93c1db
commit 2c52ac099c
9 changed files with 282 additions and 105 deletions

View File

@ -3,6 +3,7 @@ package buff
import (
"fmt"
"github.com/felixangell/phi/internal/cfg"
"github.com/felixangell/phi/internal/command_handler"
"github.com/felixangell/phi/internal/gui"
"github.com/felixangell/phi/internal/lex"
"github.com/felixangell/phi/pkg/piecetable"
@ -274,7 +275,7 @@ func (b *Buffer) OpenFile(filePath string) {
b.table = piecetable.MakePieceTable(string(contents))
// because appendLine sets modified to true
// we should reset this to false since weve
// we should reset this to false since we've
// only loaded a file.
b.modified = false
}
@ -384,7 +385,6 @@ var altAlternative = map[rune]rune{
}
func (b *Buffer) deleteLine() {
// HACK FIXME
b.modified = true
if len(b.table.Lines) > 1 {
@ -405,73 +405,58 @@ func (b *Buffer) deleteLine() {
b.moveToEndOfLine()
}
func (b *Buffer) processTextInput(r rune) bool {
if altDown && r == '\t' {
func (b *Buffer) processTextInput(keyCode int) bool {
keyValue := rune(keyCode)
if altDown && keyValue == '\t' {
// nop, we dont want to
// insert tabs when we
// alt tab out of view of this app
return true
}
b.autoComplete.process(r)
b.autoComplete.process(keyValue)
// FIXME
// only do the alt alternatives on mac osx
// todo change this so it's not checking on every
// input
if runtime.GOOS == "darwin" && altDown {
if val, ok := altAlternative[r]; ok {
r = val
if val, ok := altAlternative[keyValue]; ok {
keyValue = val
}
}
if capsLockDown {
if unicode.IsLetter(r) {
r = unicode.ToUpper(r)
if unicode.IsLetter(keyValue) {
keyValue = unicode.ToUpper(keyValue)
}
}
// this logic is fucked
mainSuper, _ := controlDown, "ctrl"
source := cfg.Shortcuts.Controls
if runtime.GOOS == "darwin" {
mainSuper, _ = superDown, "super"
source = cfg.Shortcuts.Supers
}
// FIXME!
if mainSuper {
// FIXME magic numbers!
left := 1073741903
right := 1073741904
// map to left/right/etc.
// FIXME
var key string
switch int(r) {
case left:
key = "left"
case right:
key = "right"
default:
key = string(unicode.ToLower(r))
}
// FIXME(FELIX): what is source?
actionName, actionExists := source[key]
if actionExists {
return bool(ExecuteCommandIfExist(actionName, b.parent))
cmdName, ok := command_handler.DeduceCommand(superDown, controlDown, keyCode)
if ok {
return bool(ExecuteCommandIfExist(cmdName, b.parent))
}
}
if shiftDown {
// if it's a letter convert to uppercase
if unicode.IsLetter(r) {
r = unicode.ToUpper(r)
if unicode.IsLetter(keyValue) {
keyValue = unicode.ToUpper(keyValue)
} else {
// otherwise we have to look in our trusy
// shift mapping thing.
if val, ok := shiftAlternative[r]; ok {
r = val
if val, ok := shiftAlternative[keyValue]; ok {
keyValue = val
}
}
@ -485,11 +470,11 @@ func (b *Buffer) processTextInput(r rune) bool {
// rather than inserting a new one IF we are inside
// brackets.
if b.cfg.Editor.MatchBraces {
if r == ')' || r == '}' || r == ']' {
if keyValue == ')' || keyValue == '}' || keyValue == ']' {
currLine := b.table.Lines[b.curs.y]
if b.curs.x < currLine.Len() {
curr := b.table.Index(b.curs.y, b.curs.x+1)
if curr == r {
if curr == keyValue {
b.moveRight()
return true
}
@ -500,7 +485,7 @@ func (b *Buffer) processTextInput(r rune) bool {
// HACK FIXME
b.modified = true
b.table.Insert(string(r), b.curs.y, b.curs.x)
b.table.Insert(string(keyValue), b.curs.y, b.curs.x)
b.moveRight()
// we don't need to match braces
@ -511,13 +496,13 @@ func (b *Buffer) processTextInput(r rune) bool {
// TODO: shall we match single quotes and double quotes too?
matchingPair := int(r)
matchingPair := int(keyValue)
// the offset in the ASCII Table is +2 for { and for [
// but its +1 for parenthesis (
offset := 2
switch r {
switch keyValue {
case '(':
offset = 1
fallthrough
@ -1191,7 +1176,7 @@ func (b *Buffer) processInput(pred func(r int) bool) bool {
return true
}
textEntered := b.processTextInput(rune(keyCode))
textEntered := b.processTextInput(keyCode)
if textEntered {
return true
}

View File

@ -2,15 +2,13 @@ package buff
import (
"fmt"
"log"
"runtime"
"unicode"
"github.com/felixangell/phi/internal/cfg"
"github.com/felixangell/phi/internal/command_handler"
"github.com/felixangell/phi/internal/gui"
"github.com/felixangell/strife"
"github.com/fsnotify/fsnotify"
"github.com/veandco/go-sdl2/sdl"
"log"
)
type bufferEvent interface {
@ -259,50 +257,10 @@ func (n *BufferView) OnUpdate() bool {
controlDown = strife.KeyPressed(sdl.K_LCTRL) || strife.KeyPressed(sdl.K_RCTRL)
superDown = strife.KeyPressed(sdl.K_LGUI) || strife.KeyPressed(sdl.K_RGUI)
source := cfg.Shortcuts.Controls
if strife.PollKeys() && (superDown || controlDown) {
// FIXME this sucks move it.
if runtime.GOOS == "darwin" {
if superDown {
source = cfg.Shortcuts.Supers
} else if controlDown {
source = cfg.Shortcuts.Controls
}
} else {
source = cfg.Shortcuts.Controls
}
r := rune(strife.PopKey())
if r == 'l' {
cfg.DebugMode = !cfg.DebugMode
}
left := sdl.K_LEFT
right := sdl.K_RIGHT
up := sdl.K_UP
down := sdl.K_DOWN
// map to left/right/etc.
// FIXME
var key string
switch int(r) {
case left:
key = "left"
case right:
key = "right"
case up:
key = "up"
case down:
key = "down"
default:
key = string(unicode.ToLower(r))
}
actionName, actionExists := source[key]
if actionExists {
return bool(ExecuteCommandIfExist(actionName, n))
actionName, ok := command_handler.DeduceCommand(superDown, controlDown, strife.PopKey())
if ok {
ExecuteCommandIfExist(actionName, n)
}
}

View File

@ -21,8 +21,8 @@ type PhiEditorConfig struct {
// GetSyntaxConfig returns a pointer to the parsed
// syntax language file for the given file extension
// e.g. what syntax def we need for a .cpp file or a .h file
func (t *PhiEditorConfig) GetSyntaxConfig(ext string) (*LanguageSyntaxConfig, error) {
if val, ok := t.associations[ext]; ok {
func (p *PhiEditorConfig) GetSyntaxConfig(ext string) (*LanguageSyntaxConfig, error) {
if val, ok := p.associations[ext]; ok {
return val, nil
}
return nil, errors.New("no language for extension '" + ext + "'")
@ -123,20 +123,9 @@ type EditorConfig struct {
ShowLineNumbers bool `toml:"show_line_numbers"`
}
type shortcutConfig struct {
Supers map[string]string
Controls map[string]string
}
// FIXME we should define some default shorcuts somehow.
// why are these a global and not stored in the config itself?
var Shortcuts = shortcutConfig{
Supers: map[string]string{},
Controls: map[string]string{},
}
func NewDefaultConfig() *PhiEditorConfig {
log.Println("Loading default configuration")
return &PhiEditorConfig{
Render: &RenderConfig{
Aliased: true,
@ -171,6 +160,20 @@ func NewDefaultConfig() *PhiEditorConfig {
Flash: false,
BlockWidth: "block",
},
Commands: map[string]Command{
"undo": {"super+z"},
"redo": {"super+y"},
"exit": {"super+q"},
"save": {"super+s"},
"page_down": {"ctrl+down"},
"page_up": {"ctrl+up"},
"show_palette": {"super+p"},
"focus_left": {"super+left"},
"focus_right": {"super+right"},
"paste": {"super+v"},
"close_buffer": {"super+w"},
"delete_line": {"super+d"},
},
// TODO syntax defaults
associations: map[string]*LanguageSyntaxConfig{},

View File

@ -9,9 +9,6 @@ var (
DebugMode = false
DebugModeRenderColour = uint32(0xff00ff)
ScaleFactor = 1.0
// TODO this should be set from somewhere.
FontFolder = "/Library/Fonts"
)
func init() {

View File

@ -0,0 +1,111 @@
package command_handler
import (
"fmt"
"github.com/felixangell/phi/internal/cfg"
"github.com/felixangell/strife"
"strings"
)
var commandHandlerInst *CommandHandler
func DeduceCommand(superDown bool, controlDown bool, keys ...int) (string, bool) {
if commandHandlerInst == nil {
panic("CommandHandler not initialised yet!")
}
return commandHandlerInst.DeduceCommandName(superDown, controlDown, keys...)
}
func SetupCommandHandler(config *cfg.PhiEditorConfig) {
commandHandlerInst = newCommandHandler(config)
}
type CommandHandler struct {
triggers map[intHashSetHashKey]string
}
// TODO build this map up further.
var shortcutMap = map[string]int{
// for the simplicity of making the API work
// we assume that control and super map to the left
// of their keys, even if the right is pressed. it is upto
// the caller to consolidate the two (for now).
"ctrl": strife.KEY_LCTRL,
"super": strife.KEY_LGUI,
"a": strife.KEY_A,
"b": strife.KEY_B,
"c": strife.KEY_C,
"d": strife.KEY_D,
"e": strife.KEY_E,
"f": strife.KEY_F,
"g": strife.KEY_G,
"h": strife.KEY_H,
"i": strife.KEY_I,
"j": strife.KEY_J,
"k": strife.KEY_K,
"l": strife.KEY_L,
"m": strife.KEY_M,
"n": strife.KEY_N,
"o": strife.KEY_O,
"p": strife.KEY_P,
"q": strife.KEY_Q,
"r": strife.KEY_R,
"s": strife.KEY_S,
"t": strife.KEY_T,
"u": strife.KEY_U,
"v": strife.KEY_V,
"w": strife.KEY_W,
"x": strife.KEY_X,
"y": strife.KEY_Y,
"z": strife.KEY_Z,
"left": strife.KEY_LEFT,
"right": strife.KEY_RIGHT,
"up": strife.KEY_UP,
"down": strife.KEY_DOWN,
}
func mapShortcutKeyword(keyword string) int {
if val, ok := shortcutMap[keyword]; ok {
return val
}
panic(fmt.Sprintf("unsupported keyword %s", keyword))
}
func parseHashCombo(combo string) intHashSetHashKey {
hs := newIntHashSet()
parts := strings.Split(combo, "+")
for _, part := range parts {
hs.Store(mapShortcutKeyword(part))
}
return hs.Hash()
}
func (c *CommandHandler) DeduceCommandName(superDown bool, controlDown bool, keys ...int) (string, bool) {
hs := newIntHashSet(keys...)
if superDown {
hs.Store(mapShortcutKeyword("super"))
}
if controlDown {
hs.Store(mapShortcutKeyword("ctrl"))
}
cmdName, ok := c.triggers[hs.Hash()]
return cmdName, ok
}
func populateTriggers(config *cfg.PhiEditorConfig, handler *CommandHandler) {
for cmdName, cmd := range config.Commands {
hash := parseHashCombo(cmd.Shortcut)
handler.triggers[hash] = cmdName
}
}
func newCommandHandler(config *cfg.PhiEditorConfig) *CommandHandler {
handler := &CommandHandler{
triggers: map[intHashSetHashKey]string{},
}
populateTriggers(config, handler)
return handler
}

View File

@ -0,0 +1,32 @@
package command_handler
import (
"github.com/felixangell/phi/internal/cfg"
"github.com/felixangell/strife"
"github.com/stretchr/testify/assert"
"testing"
)
var dummyConfig = &cfg.PhiEditorConfig{
Commands: map[string]cfg.Command{
"delete_line": {Shortcut: "super+d"},
"close_buffer": {Shortcut: "super+w"},
},
}
func TestBuildsHashComboCorrectly(t *testing.T) {
hash := parseHashCombo("super+d")
hs := newIntHashSet(
mapShortcutKeyword("super"), strife.KEY_D)
expected := hs.Hash()
assert.Equal(t, expected, hash)
}
func TestSimpleShortcutMapsCorrectly(t *testing.T) {
ch := newCommandHandler(dummyConfig)
victim, ok := ch.DeduceCommandName(true, false, strife.KEY_D)
assert.True(t, ok)
assert.Equal(t, "delete_line", victim)
}

View File

@ -0,0 +1,49 @@
package command_handler
import (
"fmt"
"sort"
)
type intHashSet map[int]bool
func (i *intHashSet) Contains(x int) bool {
val, ok := (*i)[x]
return ok && val
}
func (i *intHashSet) Delete(x int) {
if i.Contains(x) {
(*i)[x] = false
}
}
func (i *intHashSet) Store(x int) {
(*i)[x] = true
}
type intHashSetHashKey string
func (i *intHashSet) Hash() intHashSetHashKey {
vals := make([]int, len(*i))
idx := 0
for key, _ := range *i {
vals[idx] = key
idx++
}
sort.Ints(vals)
var res string
for _, v := range vals {
res += fmt.Sprintf("%d", v)
}
return intHashSetHashKey(res)
}
func newIntHashSet(vals ...int) intHashSet {
hs := intHashSet{}
for _, val := range vals {
hs.Store(val)
}
return hs
}

View File

@ -0,0 +1,40 @@
package command_handler
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestIntHashSet_Hash(t *testing.T) {
hs := newIntHashSet(3, 2, 1, 9, 10)
assert.Equal(t, intHashSetHashKey("123910"), hs.Hash())
}
func TestIntHashSet_Contains(t *testing.T) {
hs := newIntHashSet()
hs.Store(5)
hs.Store(4)
assert.True(t, hs.Contains(5))
assert.True(t, hs.Contains(4))
assert.False(t, hs.Contains(120))
}
func TestIntHashSet_Initialise(t *testing.T) {
hs := newIntHashSet(1, 2, 3)
assert.True(t, hs.Contains(1))
assert.True(t, hs.Contains(2))
assert.True(t, hs.Contains(3))
assert.False(t, hs.Contains(1234567))
}
func TestIntHashSet_Delete(t *testing.T) {
hs := newIntHashSet()
hs.Store(5)
assert.True(t, hs.Contains(5))
hs.Delete(5)
assert.False(t, hs.Contains(5))
}

View File

@ -3,6 +3,7 @@ package editor
import (
"github.com/felixangell/phi/internal/buff"
"github.com/felixangell/phi/internal/cfg"
"github.com/felixangell/phi/internal/command_handler"
"github.com/felixangell/phi/internal/gui"
"github.com/felixangell/strife"
"io/ioutil"
@ -27,6 +28,7 @@ func (n *PhiEditor) HandleEvent(_ strife.StrifeEvent) {}
func (n *PhiEditor) ApplyConfig(conf *cfg.PhiEditorConfig) {
gui.LoadDefaultFont(conf)
command_handler.SetupCommandHandler(conf)
mainView := buff.NewView(int(1280.0*cfg.ScaleFactor), int(720.0*cfg.ScaleFactor), conf)