2017-03-14 01:52:44 +00:00
|
|
|
package gonvim
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
2017-03-14 07:20:31 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2017-03-14 01:52:44 +00:00
|
|
|
"sync"
|
|
|
|
"unsafe"
|
|
|
|
|
2017-03-16 10:38:16 +00:00
|
|
|
"github.com/dzhou121/neovim-fzf-shim/rplugin/go/fzf"
|
2017-03-14 01:52:44 +00:00
|
|
|
"github.com/dzhou121/ui"
|
|
|
|
"github.com/neovim/go-client/nvim"
|
|
|
|
)
|
|
|
|
|
|
|
|
var editor *Editor
|
|
|
|
|
|
|
|
// Highlight is
|
|
|
|
type Highlight struct {
|
|
|
|
foreground *RGBA
|
|
|
|
background *RGBA
|
|
|
|
}
|
|
|
|
|
|
|
|
// Char is
|
|
|
|
type Char struct {
|
|
|
|
char string
|
|
|
|
highlight Highlight
|
|
|
|
}
|
|
|
|
|
|
|
|
// Editor is the editor
|
|
|
|
type Editor struct {
|
2017-04-27 11:32:04 +01:00
|
|
|
nvim *nvim.Nvim
|
|
|
|
nvimAttached bool
|
|
|
|
mode string
|
|
|
|
font *Font
|
|
|
|
rows int
|
|
|
|
cols int
|
|
|
|
cursor *CursorHandler
|
|
|
|
Foreground RGBA
|
|
|
|
Background RGBA
|
|
|
|
window *ui.Window
|
|
|
|
area *ui.Area
|
|
|
|
areaHandler *AreaHandler
|
2017-04-28 07:05:32 +01:00
|
|
|
areaBox *ui.Box
|
2017-04-27 11:32:04 +01:00
|
|
|
close chan bool
|
|
|
|
popup *PopupMenu
|
|
|
|
finder *Finder
|
|
|
|
tabline *Tabline
|
|
|
|
width int
|
|
|
|
height int
|
|
|
|
tablineHeight int
|
|
|
|
selectedBg *RGBA
|
|
|
|
matchFg *RGBA
|
2017-03-14 01:52:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func initWindow(box *ui.Box, width, height int) *ui.Window {
|
|
|
|
window := ui.NewWindow("Gonvim", width, height, false)
|
|
|
|
window.SetChild(box)
|
|
|
|
window.OnClosing(func(*ui.Window) bool {
|
|
|
|
ui.Quit()
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
window.OnContentSizeChanged(func(w *ui.Window, data unsafe.Pointer) bool {
|
2017-03-22 21:12:12 +00:00
|
|
|
if editor == nil {
|
2017-03-22 21:13:22 +00:00
|
|
|
return true
|
2017-03-22 21:12:12 +00:00
|
|
|
}
|
2017-03-14 01:52:44 +00:00
|
|
|
width, height = window.ContentSize()
|
2017-04-27 11:32:04 +01:00
|
|
|
height = height - editor.tablineHeight
|
2017-03-22 22:13:36 +00:00
|
|
|
if width == editor.width && height == editor.height {
|
2017-03-22 22:14:43 +00:00
|
|
|
return true
|
2017-03-22 22:13:36 +00:00
|
|
|
}
|
2017-03-14 07:20:31 +00:00
|
|
|
editor.width = width
|
|
|
|
editor.height = height
|
2017-04-28 07:05:32 +01:00
|
|
|
editor.areaBox.SetSize(width, height)
|
2017-03-14 01:52:44 +00:00
|
|
|
editor.area.SetSize(width, height)
|
2017-04-27 11:32:04 +01:00
|
|
|
editor.tabline.resize(width, editor.tablineHeight)
|
2017-03-14 07:20:31 +00:00
|
|
|
editor.resize()
|
2017-03-16 10:38:16 +00:00
|
|
|
editor.finder.rePosition()
|
2017-03-14 01:52:44 +00:00
|
|
|
return true
|
|
|
|
})
|
|
|
|
window.Show()
|
|
|
|
return window
|
|
|
|
}
|
|
|
|
|
|
|
|
// InitEditor inits the editor
|
|
|
|
func InitEditor() error {
|
|
|
|
if editor != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
width := 800
|
|
|
|
height := 600
|
2017-04-27 11:32:04 +01:00
|
|
|
tablineHeight := 34
|
2017-03-14 01:52:44 +00:00
|
|
|
ah := initArea()
|
2017-03-21 18:17:06 +00:00
|
|
|
cursor := &CursorHandler{}
|
|
|
|
cursorArea := ui.NewArea(cursor)
|
|
|
|
cursor.area = cursorArea
|
|
|
|
|
2017-03-14 01:52:44 +00:00
|
|
|
popupMenu := initPopupmenu()
|
2017-03-16 10:38:16 +00:00
|
|
|
finder := initFinder()
|
2017-04-27 11:32:04 +01:00
|
|
|
tabline := initTabline(width, tablineHeight)
|
2017-03-14 01:52:44 +00:00
|
|
|
|
|
|
|
box := ui.NewHorizontalBox()
|
2017-04-28 02:50:38 +01:00
|
|
|
areaBox := ui.NewHorizontalBox()
|
|
|
|
areaBox.Append(ah.area, false)
|
|
|
|
areaBox.Append(cursor.area, false)
|
|
|
|
areaBox.Append(popupMenu.box, false)
|
|
|
|
areaBox.Append(finder.box, false)
|
2017-04-27 11:32:04 +01:00
|
|
|
box.Append(tabline.box, false)
|
2017-04-28 02:50:38 +01:00
|
|
|
box.Append(areaBox, false)
|
2017-03-14 01:52:44 +00:00
|
|
|
|
2017-04-28 02:50:38 +01:00
|
|
|
areaBox.SetSize(width, height)
|
|
|
|
areaBox.SetPosition(0, tablineHeight)
|
2017-03-14 01:52:44 +00:00
|
|
|
ah.area.SetSize(width, height)
|
2017-04-27 11:32:04 +01:00
|
|
|
window := initWindow(box, width, height+tablineHeight)
|
2017-03-14 01:52:44 +00:00
|
|
|
|
|
|
|
neovim, err := nvim.NewEmbedded(&nvim.EmbedOptions{
|
|
|
|
Args: os.Args[1:],
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-03-14 07:20:31 +00:00
|
|
|
font := initFont("", 14, 0)
|
2017-03-14 01:52:44 +00:00
|
|
|
|
|
|
|
editor = &Editor{
|
2017-04-27 11:32:04 +01:00
|
|
|
nvim: neovim,
|
|
|
|
nvimAttached: false,
|
|
|
|
window: window,
|
|
|
|
area: ah.area,
|
|
|
|
areaHandler: ah,
|
2017-04-28 07:05:32 +01:00
|
|
|
areaBox: areaBox,
|
2017-04-27 11:32:04 +01:00
|
|
|
mode: "normal",
|
|
|
|
close: make(chan bool),
|
|
|
|
cursor: cursor,
|
|
|
|
popup: popupMenu,
|
|
|
|
finder: finder,
|
|
|
|
tabline: tabline,
|
|
|
|
width: width,
|
|
|
|
height: height,
|
|
|
|
tablineHeight: tablineHeight,
|
|
|
|
font: font,
|
|
|
|
cols: 0,
|
|
|
|
rows: 0,
|
|
|
|
selectedBg: newRGBA(81, 154, 186, 0.5),
|
|
|
|
matchFg: newRGBA(81, 154, 186, 1),
|
2017-03-14 01:52:44 +00:00
|
|
|
}
|
|
|
|
|
2017-03-14 07:20:31 +00:00
|
|
|
editor.resize()
|
|
|
|
editor.handleNotification()
|
2017-03-16 10:38:16 +00:00
|
|
|
editor.finder.rePosition()
|
2017-03-14 01:52:44 +00:00
|
|
|
go func() {
|
|
|
|
neovim.Serve()
|
|
|
|
editor.close <- true
|
|
|
|
}()
|
|
|
|
|
|
|
|
o := make(map[string]interface{})
|
|
|
|
o["rgb"] = true
|
2017-04-27 11:32:04 +01:00
|
|
|
o["ext_popupmenu"] = true
|
|
|
|
o["ext_tabline"] = true
|
2017-03-14 07:20:31 +00:00
|
|
|
editor.nvim.AttachUI(editor.cols, editor.rows, o)
|
2017-03-14 07:26:11 +00:00
|
|
|
editor.nvim.Subscribe("Gui")
|
2017-03-14 07:20:31 +00:00
|
|
|
editor.nvim.Command("runtime plugin/nvim_gui_shim.vim")
|
|
|
|
editor.nvim.Command("runtime! ginit.vim")
|
2017-03-16 10:38:16 +00:00
|
|
|
fzf.RegisterPlugin(editor.nvim)
|
2017-03-14 01:52:44 +00:00
|
|
|
|
|
|
|
go func() {
|
|
|
|
<-editor.close
|
|
|
|
ui.Quit()
|
|
|
|
}()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-14 07:20:31 +00:00
|
|
|
func (e *Editor) handleNotification() {
|
2017-03-14 01:52:44 +00:00
|
|
|
ah := e.areaHandler
|
2017-03-14 07:20:31 +00:00
|
|
|
e.nvim.RegisterHandler("Gui", func(updates ...interface{}) {
|
|
|
|
event := updates[0].(string)
|
|
|
|
switch event {
|
|
|
|
case "Font":
|
|
|
|
e.guiFont(updates[1:])
|
|
|
|
case "Linespace":
|
|
|
|
e.guiLinespace(updates[1:])
|
2017-03-16 10:38:16 +00:00
|
|
|
case "finder_pattern":
|
|
|
|
e.finder.showPattern(updates[1:])
|
|
|
|
case "finder_pattern_pos":
|
|
|
|
e.finder.cursorPos(updates[1:])
|
|
|
|
case "finder_show_result":
|
|
|
|
e.finder.showResult(updates[1:])
|
|
|
|
case "finder_show":
|
|
|
|
e.finder.show()
|
|
|
|
case "finder_hide":
|
|
|
|
e.finder.hide()
|
|
|
|
case "finder_select":
|
|
|
|
e.finder.selectResult(updates[1:])
|
2017-03-14 07:20:31 +00:00
|
|
|
default:
|
|
|
|
fmt.Println("unhandled Gui event", event)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
mutex := &sync.Mutex{}
|
2017-03-14 01:52:44 +00:00
|
|
|
e.nvim.RegisterHandler("redraw", func(updates ...[]interface{}) {
|
2017-03-14 07:20:31 +00:00
|
|
|
mutex.Lock()
|
2017-03-14 01:52:44 +00:00
|
|
|
for _, update := range updates {
|
|
|
|
event := update[0].(string)
|
|
|
|
args := update[1:]
|
|
|
|
switch event {
|
|
|
|
case "update_fg":
|
|
|
|
args := update[1].([]interface{})
|
|
|
|
editor.Foreground = calcColor(reflectToInt(args[0]))
|
|
|
|
case "update_bg":
|
|
|
|
args := update[1].([]interface{})
|
|
|
|
bg := calcColor(reflectToInt(args[0]))
|
|
|
|
editor.Background = bg
|
|
|
|
case "cursor_goto":
|
|
|
|
ah.cursorGoto(args)
|
|
|
|
case "put":
|
|
|
|
ah.put(args)
|
|
|
|
case "eol_clear":
|
|
|
|
ah.eolClear(args)
|
|
|
|
case "clear":
|
|
|
|
ah.clear(args)
|
|
|
|
case "resize":
|
|
|
|
ah.resize(args)
|
|
|
|
case "highlight_set":
|
|
|
|
ah.highlightSet(args)
|
|
|
|
case "set_scroll_region":
|
|
|
|
ah.setScrollRegion(args)
|
|
|
|
case "scroll":
|
|
|
|
ah.scroll(args)
|
|
|
|
case "mode_change":
|
|
|
|
ah.modeChange(args)
|
|
|
|
case "popupmenu_show":
|
|
|
|
editor.popup.show(args)
|
|
|
|
case "popupmenu_hide":
|
|
|
|
editor.popup.hide(args)
|
|
|
|
case "popupmenu_select":
|
|
|
|
editor.popup.selectItem(args)
|
2017-04-27 11:32:04 +01:00
|
|
|
case "tabline_update":
|
|
|
|
editor.tabline.update(args)
|
2017-03-14 01:52:44 +00:00
|
|
|
default:
|
|
|
|
fmt.Println("Unhandle event", event)
|
|
|
|
}
|
|
|
|
}
|
2017-03-14 07:20:31 +00:00
|
|
|
mutex.Unlock()
|
2017-03-14 01:52:44 +00:00
|
|
|
if !e.nvimAttached {
|
|
|
|
e.nvimAttached = true
|
|
|
|
}
|
|
|
|
drawCursor()
|
|
|
|
})
|
|
|
|
}
|
2017-03-14 07:20:31 +00:00
|
|
|
|
|
|
|
func (e *Editor) guiFont(args ...interface{}) {
|
|
|
|
fontArg := args[0].([]interface{})
|
|
|
|
parts := strings.Split(fontArg[0].(string), ":")
|
|
|
|
if len(parts) < 1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
height := 14
|
|
|
|
for _, p := range parts[1:] {
|
|
|
|
if strings.HasPrefix(p, "h") {
|
|
|
|
var err error
|
|
|
|
height, err = strconv.Atoi(p[1:])
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
e.font.change(parts[0], height)
|
|
|
|
e.resize()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Editor) guiLinespace(args ...interface{}) {
|
|
|
|
fontArg := args[0].([]interface{})
|
|
|
|
lineSpace, err := strconv.Atoi(fontArg[0].(string))
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
e.font.changeLineSpace(lineSpace)
|
|
|
|
e.resize()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Editor) resize() {
|
|
|
|
width := e.width
|
|
|
|
height := e.height
|
|
|
|
cols := width / editor.font.width
|
|
|
|
rows := height / editor.font.lineHeight
|
|
|
|
oldCols := editor.cols
|
|
|
|
oldRows := editor.rows
|
2017-03-14 07:26:11 +00:00
|
|
|
editor.cols = cols
|
|
|
|
editor.rows = rows
|
|
|
|
if oldCols > 0 && oldRows > 0 {
|
|
|
|
editor.nvim.TryResizeUI(cols, rows)
|
2017-03-14 07:20:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-14 01:52:44 +00:00
|
|
|
func drawCursor() {
|
|
|
|
row := editor.areaHandler.cursor[0]
|
|
|
|
col := editor.areaHandler.cursor[1]
|
|
|
|
ui.QueueMain(func() {
|
2017-04-28 02:50:38 +01:00
|
|
|
editor.cursor.area.SetPosition(col*editor.font.width, row*editor.font.lineHeight)
|
2017-03-14 01:52:44 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
mode := editor.mode
|
|
|
|
if mode == "normal" {
|
|
|
|
ui.QueueMain(func() {
|
2017-03-21 18:17:06 +00:00
|
|
|
editor.cursor.area.SetSize(editor.font.width, editor.font.lineHeight)
|
|
|
|
editor.cursor.bg = newRGBA(255, 255, 255, 0.5)
|
2017-03-14 01:52:44 +00:00
|
|
|
})
|
|
|
|
} else if mode == "insert" {
|
|
|
|
ui.QueueMain(func() {
|
2017-03-21 18:17:06 +00:00
|
|
|
editor.cursor.area.SetSize(1, editor.font.lineHeight)
|
|
|
|
editor.cursor.bg = newRGBA(255, 255, 255, 0.9)
|
2017-03-14 01:52:44 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hl *Highlight) copy() Highlight {
|
|
|
|
highlight := Highlight{}
|
|
|
|
if hl.foreground != nil {
|
|
|
|
highlight.foreground = hl.foreground.copy()
|
|
|
|
}
|
|
|
|
if hl.background != nil {
|
|
|
|
highlight.background = hl.background.copy()
|
|
|
|
}
|
|
|
|
return highlight
|
|
|
|
}
|