mirror of
https://github.com/felixangell/phi.git
synced 2024-10-05 12:17:20 +03:00
started to cleanup the command processing
This commit is contained in:
parent
f26a93c1db
commit
2c52ac099c
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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{},
|
||||
|
@ -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() {
|
||||
|
111
internal/command_handler/command_handler.go
Normal file
111
internal/command_handler/command_handler.go
Normal 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
|
||||
}
|
32
internal/command_handler/command_handler_test.go
Normal file
32
internal/command_handler/command_handler_test.go
Normal 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)
|
||||
}
|
49
internal/command_handler/int_hash_set.go
Normal file
49
internal/command_handler/int_hash_set.go
Normal 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
|
||||
}
|
40
internal/command_handler/int_hash_set_test.go
Normal file
40
internal/command_handler/int_hash_set_test.go
Normal 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))
|
||||
}
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user