package main
import (
- "os"
+ "fmt"
+ "reflect"
"strconv"
"strings"
"time"
// The ViewType defines what kind of view this is
type ViewType struct {
- kind int
- readonly bool // The file cannot be edited
- scratch bool // The file cannot be saved
+ Kind int
+ Readonly bool // The file cannot be edited
+ Scratch bool // The file cannot be saved
}
var (
vtHelp = ViewType{1, true, true}
vtLog = ViewType{2, true, true}
vtScratch = ViewType{3, false, true}
+ vtRaw = ViewType{4, true, true}
+ vtTerm = ViewType{5, true, true}
)
// The View struct stores information about a view into a buffer.
// The buffer
Buf *Buffer
// The statusline
- sline Statusline
+ sline *Statusline
// Since tcell doesn't differentiate between a mouse release event
// and a mouse move event with no keys pressed, we need to keep
// mouse release events
mouseReleased bool
+ // We need to keep track of insert key press toggle
+ isOverwriteMode bool
// This stores when the last click was
// This is useful for detecting double and triple clicks
lastClickTime time.Time
// Same here, just to keep track for mouse move events
tripleClick bool
+ // The cellview used for displaying and syntax highlighting
cellview *CellView
splitNode *LeafNode
+
+ // The scrollbar
+ scrollbar *ScrollBar
+
+ // Virtual terminal
+ term *Terminal
}
// NewView returns a new fullscreen view
v.messages = make(map[string][]GutterMessage)
- v.sline = Statusline{
+ v.sline = &Statusline{
+ view: v,
+ }
+
+ v.scrollbar = &ScrollBar{
view: v,
}
v.Height--
}
+ v.term = new(Terminal)
+
for pl := range loadedPlugins {
_, err := Call(pl+".onViewOpen", v)
if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
}
}
+// StartTerminal execs a command in this view
+func (v *View) StartTerminal(execCmd []string, wait bool, getOutput bool, luaCallback string) error {
+ err := v.term.Start(execCmd, v, getOutput)
+ v.term.wait = wait
+ v.term.callback = luaCallback
+ if err == nil {
+ v.term.Resize(v.Width, v.Height)
+ v.Type = vtTerm
+ }
+ return err
+}
+
+// CloseTerminal shuts down the tty running in this view
+// and returns it to the default view type
+func (v *View) CloseTerminal() {
+ v.term.Stop()
+}
+
// ToggleTabbar creates an extra row for the tabbar if necessary
func (v *View) ToggleTabbar() {
if len(tabs) > 1 {
}
func (v *View) paste(clip string) {
- leadingWS := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
+ leadingWS := ""
+ if v.Cursor.X > 0 {
+ leadingWS = GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
+ }
if v.Cursor.HasSelection() {
v.Cursor.DeleteSelection()
// Set mouseReleased to true because we assume the mouse is not being pressed when
// the editor is opened
v.mouseReleased = true
+ // Set isOverwriteMode to false, because we assume we are in the default mode when editor
+ // is opened
+ v.isOverwriteMode = false
v.lastClickTime = time.Time{}
+
+ GlobalPluginCall("onBufferOpen", v.Buf)
+ GlobalPluginCall("onViewOpen", v)
}
// Open opens the given file in the view
-func (v *View) Open(filename string) {
- filename = ReplaceHome(filename)
- file, err := os.Open(filename)
- fileInfo, _ := os.Stat(filename)
-
- if err == nil && fileInfo.IsDir() {
- messenger.Error(filename, " is a directory")
- return
- }
-
- defer file.Close()
-
- var buf *Buffer
+func (v *View) Open(path string) {
+ buf, err := NewBufferFromFile(path)
if err != nil {
- messenger.Message(err.Error())
- // File does not exist -- create an empty buffer with that name
- buf = NewBufferFromString("", filename)
- } else {
- buf = NewBuffer(file, FSize(file), filename)
+ messenger.Error(err)
+ return
}
v.OpenBuffer(buf)
}
return 0, 0
}
+// Bottomline returns the line number of the lowest line in the view
+// You might think that this is obviously just v.Topline + v.Height
+// but if softwrap is enabled things get complicated since one buffer
+// line can take up multiple lines in the view
func (v *View) Bottomline() int {
if !v.Buf.Settings["softwrap"].(bool) {
return v.Topline + v.Height
if cy > v.Topline+height-1-scrollmargin && cy < v.Buf.NumLines-scrollmargin {
v.Topline = cy - height + 1 + scrollmargin
ret = true
- } else if cy >= v.Buf.NumLines-scrollmargin && cy > height {
+ } else if cy >= v.Buf.NumLines-scrollmargin && cy >= height {
v.Topline = v.Buf.NumLines - height
ret = true
}
return ret
}
+// GetMouseClickLocation gets the location in the buffer from a mouse click
+// on the screen
+func (v *View) GetMouseClickLocation(x, y int) (int, int) {
+ x -= v.lineNumOffset - v.leftCol + v.x
+ y += v.Topline - v.y
+
+ if y-v.Topline > v.Height-1 {
+ v.ScrollDown(1)
+ y = v.Height + v.Topline - 1
+ }
+ if y < 0 {
+ y = 0
+ }
+ if x < 0 {
+ x = 0
+ }
+
+ newX, newY := v.GetSoftWrapLocation(x, y)
+ if newX > Count(v.Buf.Line(newY)) {
+ newX = Count(v.Buf.Line(newY))
+ }
+
+ return newX, newY
+}
+
// MoveToMouseClick moves the cursor to location x, y assuming x, y were given
// by a mouse click
func (v *View) MoveToMouseClick(x, y int) {
}
x, y = v.GetSoftWrapLocation(x, y)
- // x = v.Cursor.GetCharPosInLine(y, x)
if x > Count(v.Buf.Line(y)) {
x = Count(v.Buf.Line(y))
}
for _, action := range actions {
readonlyBindingsResult := false
funcName := ShortFuncName(action)
- if v.Type.readonly == true {
+ curv := CurView()
+ if curv.Type.Readonly == true {
// check for readonly and if true only let key bindings get called if they do not change the contents.
for _, readonlyBindings := range readonlyBindingsList {
if strings.Contains(funcName, readonlyBindings) {
}
if !readonlyBindingsResult {
// call the key binding
- relocate = action(v, true) || relocate
+ relocate = action(curv, true) || relocate
// Macro
if funcName != "ToggleMacro" && funcName != "PlayMacro" {
if recordingMacro {
// HandleEvent handles an event passed by the main loop
func (v *View) HandleEvent(event tcell.Event) {
+ if v.Type == vtTerm {
+ v.term.HandleEvent(event)
+ return
+ }
+
+ if v.Type == vtRaw {
+ v.Buf.Insert(v.Cursor.Loc, reflect.TypeOf(event).String()[7:])
+ v.Buf.Insert(v.Cursor.Loc, fmt.Sprintf(": %q\n", event.EscSeq()))
+
+ switch e := event.(type) {
+ case *tcell.EventKey:
+ if e.Key() == tcell.KeyCtrlQ {
+ v.Quit(true)
+ }
+ }
+
+ return
+ }
+
// This bool determines whether the view is relocated at the end of the function
// By default it's true because most events should cause a relocate
relocate := true
case *tcell.EventRaw:
for key, actions := range bindings {
if key.keyCode == -1 {
- if e.EscapeCode() == key.escape {
+ if e.EscSeq() == key.escape {
for _, c := range v.Buf.cursors {
ok := v.SetCursor(c)
if !ok {
}
}
}
+
if !isBinding && e.Key() == tcell.KeyRune {
// Check viewtype if readonly don't insert a rune (readonly help and log view etc.)
- if v.Type.readonly == false {
+ if v.Type.Readonly == false {
for _, c := range v.Buf.cursors {
v.SetCursor(c)
v.Cursor.DeleteSelection()
v.Cursor.ResetSelection()
}
- v.Buf.Insert(v.Cursor.Loc, string(e.Rune()))
+
+ if v.isOverwriteMode {
+ next := v.Cursor.Loc
+ next.X++
+ v.Buf.Replace(v.Cursor.Loc, next, string(e.Rune()))
+ } else {
+ v.Buf.Insert(v.Cursor.Loc, string(e.Rune()))
+ }
for pl := range loadedPlugins {
_, err := Call(pl+".onRune", string(e.Rune()), v)
}
case *tcell.EventPaste:
// Check viewtype if readonly don't paste (readonly help and log view etc.)
- if v.Type.readonly == false {
+ if v.Type.Readonly == false {
if !PreActionCall("Paste", v) {
break
}
// DisplayView draws the view to the screen
func (v *View) DisplayView() {
+ if v.Type == vtTerm {
+ v.term.Display()
+ return
+ }
+
if v.Buf.Settings["softwrap"].(bool) && v.leftCol != 0 {
v.leftCol = 0
}
- if v.Type == vtLog {
- // Log views should always follow the cursor...
+ if v.Type == vtLog || v.Type == vtRaw {
+ // Log or raw views should always follow the cursor...
v.Relocate()
}
}
colorcolumn := int(v.Buf.Settings["colorcolumn"].(float64))
- if colorcolumn != 0 {
+ if colorcolumn != 0 && xOffset+colorcolumn-v.leftCol < v.Width {
style := GetColor("color-column")
fg, _, _ := style.Decompose()
st := defStyle.Background(fg)
- screen.SetContent(xOffset+colorcolumn, yOffset+visualLineN, ' ', nil, st)
+ screen.SetContent(xOffset+colorcolumn-v.leftCol, yOffset+visualLineN, ' ', nil, st)
}
screenX = v.x
screen.HideCursor()
}
_, screenH := screen.Size()
+
+ if v.Buf.Settings["scrollbar"].(bool) {
+ v.scrollbar.Display()
+ }
+
if v.Buf.Settings["statusline"].(bool) {
v.sline.Display()
} else if (v.y + v.Height) != screenH-1 {