editor slightly handles modification sys notification

the editor will now reload a file if it has been modified outside of the editor. still a huge wip.
This commit is contained in:
Felix Angell 2018-05-14 19:18:13 +01:00
parent e4dd07aead
commit aa723f172f
2 changed files with 126 additions and 2 deletions

View File

@ -17,6 +17,7 @@ import (
"github.com/felixangell/phi/cfg"
"github.com/felixangell/phi/lex"
"github.com/felixangell/strife"
"github.com/sqweek/dialog"
"github.com/veandco/go-sdl2/sdl"
)
@ -184,6 +185,44 @@ func (s *selection) renderAt(ctx *strife.Renderer, xOff int, yOff int) {
}
}
func (b *Buffer) reload() {
// if the file doesn't exist, try to create it before reading it
if _, err := os.Stat(b.filePath); os.IsNotExist(err) {
// this shouldn't really happen, for some
// reason the file no longer exists?
log.Println("File does not exist when reloading?! " + b.filePath)
return
}
// if the file has modifications made to it
// ask if the user wants to reload the file or not
// otherwise re-load it anyway.
if b.modified {
ok := dialog.Message("This file has been modified, would you like to reload?").YesNo()
if !ok {
return
}
}
contents, err := ioutil.ReadFile(b.filePath)
if err != nil {
panic(err)
}
b.contents = []*rope.Rope{}
lines := strings.Split(string(contents), "\n")
for _, line := range lines {
b.appendLine(line)
}
// TODO perhaps when we reload the current line might not exist or something
// try and set the cursor to what it was before but maybe make sure its not out
// of bounds, etc.
b.modified = false
}
func (b *Buffer) OpenFile(filePath string) {
b.filePath = filePath
@ -212,6 +251,9 @@ func (b *Buffer) OpenFile(filePath string) {
panic(err)
}
// add the file to the watcher.
b.parent.registerFile(filePath, b)
lines := strings.Split(string(contents), "\n")
for _, line := range lines {
b.appendLine(line)

View File

@ -1,15 +1,35 @@
package gui
import (
"fmt"
"log"
"runtime"
"unicode"
"github.com/felixangell/phi/cfg"
"github.com/felixangell/strife"
"github.com/fsnotify/fsnotify"
"github.com/veandco/go-sdl2/sdl"
)
type bufferEvent interface {
Process(view *View)
String() string
}
type ReloadBufferEvent struct {
buff *Buffer
}
func (r *ReloadBufferEvent) Process(view *View) {
log.Println("reloading buffer", r.buff.filePath)
r.buff.reload()
}
func (r *ReloadBufferEvent) String() string {
return "reload-buffer-event"
}
// View is an array of buffers basically.
type View struct {
BaseComponent
@ -18,12 +38,18 @@ type View struct {
buffers []*BufferPane
focusedBuff int
commandPalette *CommandPalette
watcher *fsnotify.Watcher
bufferMap map[string]*Buffer
bufferEvents chan bufferEvent
}
func NewView(width, height int, conf *cfg.TomlConfig) *View {
view := &View{
conf: conf,
buffers: []*BufferPane{},
conf: conf,
buffers: []*BufferPane{},
bufferMap: map[string]*Buffer{},
bufferEvents: make(chan bufferEvent),
}
view.Translate(width, height)
@ -32,9 +58,65 @@ func NewView(width, height int, conf *cfg.TomlConfig) *View {
view.commandPalette = NewCommandPalette(*conf, view)
view.UnfocusBuffers()
var err error
view.watcher, err = fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
// ?
}
// goroutine to handle all of the fsnotify events
// converts them into events phi can handle cleanly.
go func() {
for {
select {
case event := <-view.watcher.Events:
log.Println("evt: ", event)
if event.Op&fsnotify.Write == fsnotify.Write {
// modified so we specify a reload event
buff, ok := view.bufferMap[event.Name]
if !ok {
panic(fmt.Sprintf("no such buffer for file '%s'", event.Name))
break
}
view.bufferEvents <- &ReloadBufferEvent{buff}
log.Println("modified file:", event.Name)
}
case err := <-view.watcher.Errors:
log.Println("error:", err)
}
}
}()
// handles all of the phi events
go func() {
for {
event := <-view.bufferEvents
event.Process(view)
}
}()
return view
}
func (n *View) registerFile(path string, buff *Buffer) {
log.Println("Registering file ", path)
err := n.watcher.Add(path)
if err != nil {
log.Println(fmt.Sprintf("Failed to register file '%s'", path), "to buffer ", buff.index)
return
}
n.bufferMap[path] = buff
}
func (n *View) Close() {
n.watcher.Close()
}
func (n *View) hidePalette() {
p := n.commandPalette
p.clearInput()