micro/internal/action/actions.go

1361 lines
33 KiB
Go
Raw Normal View History

2018-08-28 02:53:08 +03:00
package action
2018-08-27 22:53:10 +03:00
import (
"os"
2019-01-17 01:52:30 +03:00
"regexp"
2019-01-03 00:27:27 +03:00
"strings"
2019-01-01 00:36:54 +03:00
"time"
2018-08-29 06:30:39 +03:00
"unicode/utf8"
2018-08-27 22:53:10 +03:00
2019-01-01 00:36:54 +03:00
"github.com/zyedidia/clipboard"
2019-02-04 07:17:24 +03:00
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/internal/shell"
"github.com/zyedidia/micro/internal/util"
"github.com/zyedidia/micro/pkg/shellwords"
2018-08-27 22:53:10 +03:00
"github.com/zyedidia/tcell"
)
2019-01-01 06:07:01 +03:00
// ScrollUp is not an action
2019-01-19 23:37:59 +03:00
func (h *BufPane) ScrollUp(n int) {
2019-01-11 05:26:58 +03:00
v := h.GetView()
2019-01-01 06:07:01 +03:00
if v.StartLine >= n {
v.StartLine -= n
2019-01-11 05:26:58 +03:00
h.SetView(v)
2019-01-01 06:07:01 +03:00
}
}
// ScrollDown is not an action
2019-01-19 23:37:59 +03:00
func (h *BufPane) ScrollDown(n int) {
2019-01-11 05:26:58 +03:00
v := h.GetView()
2019-01-01 06:07:01 +03:00
if v.StartLine <= h.Buf.LinesNum()-1-n {
v.StartLine += n
2019-01-11 05:26:58 +03:00
h.SetView(v)
2019-01-01 06:07:01 +03:00
}
}
2018-08-27 22:53:10 +03:00
// MousePress is the event that should happen when a normal click happens
// This is almost always bound to left click
2019-01-19 23:37:59 +03:00
func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
2019-01-03 01:39:50 +03:00
b := h.Buf
mx, my := e.Position()
2019-01-11 05:26:58 +03:00
mouseLoc := h.GetMouseLoc(buffer.Loc{mx, my})
2019-01-03 01:39:50 +03:00
h.Cursor.Loc = mouseLoc
if h.mouseReleased {
if b.NumCursors() > 1 {
b.ClearCursors()
2019-01-11 05:26:58 +03:00
h.Relocate()
2019-01-03 01:39:50 +03:00
}
if time.Since(h.lastClickTime)/time.Millisecond < config.DoubleClickThreshold && (mouseLoc.X == h.lastLoc.X && mouseLoc.Y == h.lastLoc.Y) {
if h.doubleClick {
// Triple click
h.lastClickTime = time.Now()
h.tripleClick = true
h.doubleClick = false
h.Cursor.SelectLine()
h.Cursor.CopySelection("primary")
} else {
// Double click
h.lastClickTime = time.Now()
h.doubleClick = true
h.tripleClick = false
h.Cursor.SelectWord()
h.Cursor.CopySelection("primary")
}
} else {
h.doubleClick = false
h.tripleClick = false
h.lastClickTime = time.Now()
h.Cursor.OrigSelection[0] = h.Cursor.Loc
h.Cursor.CurSelection[0] = h.Cursor.Loc
h.Cursor.CurSelection[1] = h.Cursor.Loc
}
h.mouseReleased = false
} else if !h.mouseReleased {
if h.tripleClick {
h.Cursor.AddLineToSelection()
} else if h.doubleClick {
h.Cursor.AddWordToSelection()
} else {
h.Cursor.SetSelectionEnd(h.Cursor.Loc)
h.Cursor.CopySelection("primary")
}
}
h.lastLoc = mouseLoc
2018-08-27 22:53:10 +03:00
return false
}
// ScrollUpAction scrolls the view up
2019-01-19 23:37:59 +03:00
func (h *BufPane) ScrollUpAction() bool {
2019-01-01 07:47:24 +03:00
h.ScrollUp(util.IntOpt(h.Buf.Settings["scrollspeed"]))
2018-08-27 22:53:10 +03:00
return false
}
// ScrollDownAction scrolls the view up
2019-01-19 23:37:59 +03:00
func (h *BufPane) ScrollDownAction() bool {
2019-01-01 07:47:24 +03:00
h.ScrollDown(util.IntOpt(h.Buf.Settings["scrollspeed"]))
2018-08-27 22:53:10 +03:00
return false
}
// Center centers the view on the cursor
2019-01-19 23:37:59 +03:00
func (h *BufPane) Center() bool {
2019-01-11 05:26:58 +03:00
v := h.GetView()
2019-01-03 00:27:27 +03:00
v.StartLine = h.Cursor.Y - v.Height/2
if v.StartLine+v.Height > h.Buf.LinesNum() {
v.StartLine = h.Buf.LinesNum() - v.Height
}
if v.StartLine < 0 {
v.StartLine = 0
}
2019-01-11 05:26:58 +03:00
h.SetView(v)
2018-08-27 22:53:10 +03:00
return true
}
// CursorUp moves the cursor up
2019-01-19 23:37:59 +03:00
func (h *BufPane) CursorUp() bool {
2018-08-29 01:44:52 +03:00
h.Cursor.Deselect(true)
h.Cursor.Up()
2018-08-27 22:53:10 +03:00
return true
}
// CursorDown moves the cursor down
2019-01-19 23:37:59 +03:00
func (h *BufPane) CursorDown() bool {
2018-08-29 01:44:52 +03:00
h.Cursor.Deselect(true)
h.Cursor.Down()
2018-08-27 22:53:10 +03:00
return true
}
// CursorLeft moves the cursor left
2019-01-19 23:37:59 +03:00
func (h *BufPane) CursorLeft() bool {
2018-08-29 01:44:52 +03:00
h.Cursor.Deselect(true)
h.Cursor.Left()
2018-08-27 22:53:10 +03:00
return true
}
// CursorRight moves the cursor right
2019-01-19 23:37:59 +03:00
func (h *BufPane) CursorRight() bool {
2019-01-05 05:48:19 +03:00
h.Cursor.Deselect(false)
2018-08-29 01:44:52 +03:00
h.Cursor.Right()
2018-08-27 22:53:10 +03:00
return true
}
// WordRight moves the cursor one word to the right
2019-01-19 23:37:59 +03:00
func (h *BufPane) WordRight() bool {
2019-01-05 05:48:19 +03:00
h.Cursor.Deselect(false)
2018-08-29 01:44:52 +03:00
h.Cursor.WordRight()
2018-08-27 22:53:10 +03:00
return true
}
// WordLeft moves the cursor one word to the left
2019-01-19 23:37:59 +03:00
func (h *BufPane) WordLeft() bool {
2018-08-29 01:44:52 +03:00
h.Cursor.Deselect(true)
h.Cursor.WordLeft()
2018-08-27 22:53:10 +03:00
return true
}
// SelectUp selects up one line
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectUp() bool {
2018-08-29 01:44:52 +03:00
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.Up()
h.Cursor.SelectTo(h.Cursor.Loc)
2018-08-27 22:53:10 +03:00
return true
}
// SelectDown selects down one line
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectDown() bool {
2018-08-29 01:44:52 +03:00
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.Down()
h.Cursor.SelectTo(h.Cursor.Loc)
2018-08-27 22:53:10 +03:00
return true
}
// SelectLeft selects the character to the left of the cursor
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectLeft() bool {
2018-08-29 01:44:52 +03:00
loc := h.Cursor.Loc
count := h.Buf.End()
if loc.GreaterThan(count) {
loc = count
}
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = loc
}
h.Cursor.Left()
h.Cursor.SelectTo(h.Cursor.Loc)
2018-08-27 22:53:10 +03:00
return true
}
// SelectRight selects the character to the right of the cursor
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectRight() bool {
2018-08-29 01:44:52 +03:00
loc := h.Cursor.Loc
count := h.Buf.End()
if loc.GreaterThan(count) {
loc = count
}
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = loc
}
h.Cursor.Right()
h.Cursor.SelectTo(h.Cursor.Loc)
2018-08-27 22:53:10 +03:00
return true
}
// SelectWordRight selects the word to the right of the cursor
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectWordRight() bool {
2018-08-29 01:44:52 +03:00
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.WordRight()
h.Cursor.SelectTo(h.Cursor.Loc)
2018-08-27 22:53:10 +03:00
return true
}
// SelectWordLeft selects the word to the left of the cursor
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectWordLeft() bool {
2018-08-29 01:44:52 +03:00
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.WordLeft()
h.Cursor.SelectTo(h.Cursor.Loc)
2018-08-27 22:53:10 +03:00
return true
}
// StartOfLine moves the cursor to the start of the line
2019-01-19 23:37:59 +03:00
func (h *BufPane) StartOfLine() bool {
2018-08-29 01:44:52 +03:00
h.Cursor.Deselect(true)
if h.Cursor.X != 0 {
h.Cursor.Start()
} else {
h.Cursor.StartOfText()
}
2018-08-27 22:53:10 +03:00
return true
}
// EndOfLine moves the cursor to the end of the line
2019-01-19 23:37:59 +03:00
func (h *BufPane) EndOfLine() bool {
2018-08-29 01:44:52 +03:00
h.Cursor.Deselect(true)
h.Cursor.End()
2018-08-27 22:53:10 +03:00
return true
}
// SelectLine selects the entire current line
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectLine() bool {
2018-08-29 01:44:52 +03:00
h.Cursor.SelectLine()
2018-08-27 22:53:10 +03:00
return true
}
// SelectToStartOfLine selects to the start of the current line
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectToStartOfLine() bool {
2018-08-29 01:44:52 +03:00
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.Start()
h.Cursor.SelectTo(h.Cursor.Loc)
2018-08-27 22:53:10 +03:00
return true
}
// SelectToEndOfLine selects to the end of the current line
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectToEndOfLine() bool {
2018-08-29 01:44:52 +03:00
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.End()
h.Cursor.SelectTo(h.Cursor.Loc)
2018-08-27 22:53:10 +03:00
return true
}
// ParagraphPrevious moves the cursor to the previous empty line, or beginning of the buffer if there's none
2019-01-19 23:37:59 +03:00
func (h *BufPane) ParagraphPrevious() bool {
2018-08-29 01:44:52 +03:00
var line int
for line = h.Cursor.Y; line > 0; line-- {
if len(h.Buf.LineBytes(line)) == 0 && line != h.Cursor.Y {
h.Cursor.X = 0
h.Cursor.Y = line
break
}
}
// If no empty line found. move cursor to end of buffer
if line == 0 {
h.Cursor.Loc = h.Buf.Start()
}
2018-08-27 22:53:10 +03:00
return true
}
// ParagraphNext moves the cursor to the next empty line, or end of the buffer if there's none
2019-01-19 23:37:59 +03:00
func (h *BufPane) ParagraphNext() bool {
2018-08-29 01:44:52 +03:00
var line int
for line = h.Cursor.Y; line < h.Buf.LinesNum(); line++ {
if len(h.Buf.LineBytes(line)) == 0 && line != h.Cursor.Y {
h.Cursor.X = 0
h.Cursor.Y = line
break
}
}
// If no empty line found. move cursor to end of buffer
if line == h.Buf.LinesNum() {
h.Cursor.Loc = h.Buf.End()
}
2018-08-27 22:53:10 +03:00
return true
}
// Retab changes all tabs to spaces or all spaces to tabs depending
// on the user's settings
2019-01-19 23:37:59 +03:00
func (h *BufPane) Retab() bool {
2019-01-15 06:44:06 +03:00
h.Buf.Retab()
2018-08-27 22:53:10 +03:00
return true
}
// CursorStart moves the cursor to the start of the buffer
2019-01-19 23:37:59 +03:00
func (h *BufPane) CursorStart() bool {
2018-08-29 01:44:52 +03:00
h.Cursor.Deselect(true)
h.Cursor.X = 0
h.Cursor.Y = 0
2018-08-27 22:53:10 +03:00
return true
}
// CursorEnd moves the cursor to the end of the buffer
2019-01-19 23:37:59 +03:00
func (h *BufPane) CursorEnd() bool {
2018-08-29 01:44:52 +03:00
h.Cursor.Deselect(true)
h.Cursor.Loc = h.Buf.End()
h.Cursor.StoreVisualX()
2018-08-27 22:53:10 +03:00
return true
}
// SelectToStart selects the text from the cursor to the start of the buffer
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectToStart() bool {
2018-08-29 01:44:52 +03:00
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.CursorStart()
h.Cursor.SelectTo(h.Buf.Start())
2018-08-27 22:53:10 +03:00
return true
}
// SelectToEnd selects the text from the cursor to the end of the buffer
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectToEnd() bool {
2018-08-29 01:44:52 +03:00
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.CursorEnd()
h.Cursor.SelectTo(h.Buf.End())
2018-08-27 22:53:10 +03:00
return true
}
// InsertNewline inserts a newline plus possible some whitespace if autoindent is on
2019-01-19 23:37:59 +03:00
func (h *BufPane) InsertNewline() bool {
2019-01-01 07:47:24 +03:00
// Insert a newline
if h.Cursor.HasSelection() {
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
}
ws := util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y))
cx := h.Cursor.X
h.Buf.Insert(h.Cursor.Loc, "\n")
2019-01-01 07:47:24 +03:00
// h.Cursor.Right()
if h.Buf.Settings["autoindent"].(bool) {
if cx < len(ws) {
ws = ws[0:cx]
}
h.Buf.Insert(h.Cursor.Loc, string(ws))
2019-01-01 07:47:24 +03:00
// for i := 0; i < len(ws); i++ {
// h.Cursor.Right()
// }
// Remove the whitespaces if keepautoindent setting is off
if util.IsSpacesOrTabs(h.Buf.LineBytes(h.Cursor.Y-1)) && !h.Buf.Settings["keepautoindent"].(bool) {
line := h.Buf.LineBytes(h.Cursor.Y - 1)
h.Buf.Remove(buffer.Loc{X: 0, Y: h.Cursor.Y - 1}, buffer.Loc{X: utf8.RuneCount(line), Y: h.Cursor.Y - 1})
}
}
h.Cursor.LastVisualX = h.Cursor.GetVisualX()
2018-08-27 22:53:10 +03:00
return true
}
// Backspace deletes the previous character
2019-01-19 23:37:59 +03:00
func (h *BufPane) Backspace() bool {
2018-08-29 06:30:39 +03:00
if h.Cursor.HasSelection() {
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
} else if h.Cursor.Loc.GreaterThan(h.Buf.Start()) {
// We have to do something a bit hacky here because we want to
// delete the line by first moving left and then deleting backwards
// but the undo redo would place the cursor in the wrong place
// So instead we move left, save the position, move back, delete
// and restore the position
// If the user is using spaces instead of tabs and they are deleting
// whitespace at the start of the line, we should delete as if it's a
// tab (tabSize number of spaces)
lineStart := util.SliceStart(h.Buf.LineBytes(h.Cursor.Y), h.Cursor.X)
tabSize := int(h.Buf.Settings["tabsize"].(float64))
if h.Buf.Settings["tabstospaces"].(bool) && util.IsSpaces(lineStart) && len(lineStart) != 0 && utf8.RuneCount(lineStart)%tabSize == 0 {
loc := h.Cursor.Loc
h.Buf.Remove(loc.Move(-tabSize, h.Buf), loc)
} else {
loc := h.Cursor.Loc
h.Buf.Remove(loc.Move(-1, h.Buf), loc)
}
}
h.Cursor.LastVisualX = h.Cursor.GetVisualX()
2018-08-27 22:53:10 +03:00
return true
}
// DeleteWordRight deletes the word to the right of the cursor
2019-01-19 23:37:59 +03:00
func (h *BufPane) DeleteWordRight() bool {
2018-09-03 23:54:56 +03:00
h.SelectWordRight()
if h.Cursor.HasSelection() {
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
}
2018-08-27 22:53:10 +03:00
return true
}
// DeleteWordLeft deletes the word to the left of the cursor
2019-01-19 23:37:59 +03:00
func (h *BufPane) DeleteWordLeft() bool {
2018-09-03 23:54:56 +03:00
h.SelectWordLeft()
if h.Cursor.HasSelection() {
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
}
2018-08-27 22:53:10 +03:00
return true
}
// Delete deletes the next character
2019-01-19 23:37:59 +03:00
func (h *BufPane) Delete() bool {
2018-09-03 23:54:56 +03:00
if h.Cursor.HasSelection() {
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
} else {
loc := h.Cursor.Loc
if loc.LessThan(h.Buf.End()) {
h.Buf.Remove(loc, loc.Move(1, h.Buf))
}
}
2018-08-27 22:53:10 +03:00
return true
}
// IndentSelection indents the current selection
2019-01-19 23:37:59 +03:00
func (h *BufPane) IndentSelection() bool {
2018-09-03 23:54:56 +03:00
if h.Cursor.HasSelection() {
start := h.Cursor.CurSelection[0]
end := h.Cursor.CurSelection[1]
if end.Y < start.Y {
start, end = end, start
h.Cursor.SetSelectionStart(start)
h.Cursor.SetSelectionEnd(end)
}
startY := start.Y
endY := end.Move(-1, h.Buf).Y
endX := end.Move(-1, h.Buf).X
tabsize := int(h.Buf.Settings["tabsize"].(float64))
indentsize := len(h.Buf.IndentString(tabsize))
for y := startY; y <= endY; y++ {
2019-01-01 00:36:54 +03:00
h.Buf.Insert(buffer.Loc{X: 0, Y: y}, h.Buf.IndentString(tabsize))
2018-09-03 23:54:56 +03:00
if y == startY && start.X > 0 {
h.Cursor.SetSelectionStart(start.Move(indentsize, h.Buf))
}
if y == endY {
2019-01-01 00:36:54 +03:00
h.Cursor.SetSelectionEnd(buffer.Loc{X: endX + indentsize + 1, Y: endY})
2018-09-03 23:54:56 +03:00
}
}
2019-01-25 02:25:59 +03:00
h.Buf.RelocateCursors()
2018-09-03 23:54:56 +03:00
return true
}
return false
2018-08-27 22:53:10 +03:00
}
// OutdentLine moves the current line back one indentation
2019-01-19 23:37:59 +03:00
func (h *BufPane) OutdentLine() bool {
2019-01-01 00:36:54 +03:00
if h.Cursor.HasSelection() {
return false
}
2019-01-01 07:47:24 +03:00
for x := 0; x < len(h.Buf.IndentString(util.IntOpt(h.Buf.Settings["tabsize"]))); x++ {
2019-01-01 00:36:54 +03:00
if len(util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y))) == 0 {
break
}
h.Buf.Remove(buffer.Loc{X: 0, Y: h.Cursor.Y}, buffer.Loc{X: 1, Y: h.Cursor.Y})
}
2019-01-25 02:25:59 +03:00
h.Buf.RelocateCursors()
2018-08-27 22:53:10 +03:00
return true
}
// OutdentSelection takes the current selection and moves it back one indent level
2019-01-19 23:37:59 +03:00
func (h *BufPane) OutdentSelection() bool {
2019-01-01 00:36:54 +03:00
if h.Cursor.HasSelection() {
start := h.Cursor.CurSelection[0]
end := h.Cursor.CurSelection[1]
if end.Y < start.Y {
start, end = end, start
h.Cursor.SetSelectionStart(start)
h.Cursor.SetSelectionEnd(end)
}
startY := start.Y
endY := end.Move(-1, h.Buf).Y
for y := startY; y <= endY; y++ {
2019-01-01 07:47:24 +03:00
for x := 0; x < len(h.Buf.IndentString(util.IntOpt(h.Buf.Settings["tabsize"]))); x++ {
2019-01-01 00:36:54 +03:00
if len(util.GetLeadingWhitespace(h.Buf.LineBytes(y))) == 0 {
break
}
h.Buf.Remove(buffer.Loc{X: 0, Y: y}, buffer.Loc{X: 1, Y: y})
}
}
2019-01-25 02:25:59 +03:00
h.Buf.RelocateCursors()
2019-01-01 00:36:54 +03:00
return true
}
return false
2018-08-27 22:53:10 +03:00
}
// InsertTab inserts a tab or spaces
2019-01-19 23:37:59 +03:00
func (h *BufPane) InsertTab() bool {
2019-01-01 07:47:24 +03:00
indent := h.Buf.IndentString(util.IntOpt(h.Buf.Settings["tabsize"]))
2019-01-01 00:36:54 +03:00
tabBytes := len(indent)
bytesUntilIndent := tabBytes - (h.Cursor.GetVisualX() % tabBytes)
h.Buf.Insert(h.Cursor.Loc, indent[:bytesUntilIndent])
2018-08-27 22:53:10 +03:00
return true
}
// SaveAll saves all open buffers
2019-01-19 23:37:59 +03:00
func (h *BufPane) SaveAll() bool {
2019-01-15 00:09:46 +03:00
for _, b := range buffer.OpenBuffers {
b.Save()
}
2018-08-27 22:53:10 +03:00
return false
}
// Save the buffer to disk
2019-01-19 23:37:59 +03:00
func (h *BufPane) Save() bool {
2019-01-17 06:15:11 +03:00
// If this is an empty buffer, ask for a filename
if h.Buf.Path == "" {
h.SaveAs()
} else {
h.saveBufToFile(h.Buf.Path)
}
2018-08-27 22:53:10 +03:00
return false
}
// SaveAs saves the buffer to disk with the given name
2019-01-19 23:37:59 +03:00
func (h *BufPane) SaveAs() bool {
2019-01-17 06:15:11 +03:00
InfoBar.Prompt("Filename: ", "", "Save", nil, func(resp string, canceled bool) {
if !canceled {
// the filename might or might not be quoted, so unquote first then join the strings.
args, err := shellwords.Split(resp)
filename := strings.Join(args, " ")
if err != nil {
InfoBar.Error("Error parsing arguments: ", err)
return
}
h.saveBufToFile(filename)
}
})
2018-08-27 22:53:10 +03:00
return false
}
2019-01-17 06:15:11 +03:00
// This function saves the buffer to `filename` and changes the buffer's path and name
// to `filename` if the save is successful
2019-01-19 23:37:59 +03:00
func (h *BufPane) saveBufToFile(filename string) {
2019-01-17 06:15:11 +03:00
err := h.Buf.SaveAs(filename)
if err != nil {
if strings.HasSuffix(err.Error(), "permission denied") {
InfoBar.YNPrompt("Permission denied. Do you want to save this file using sudo? (y,n)", func(yes, canceled bool) {
if yes && !canceled {
err = h.Buf.SaveAsWithSudo(filename)
if err != nil {
InfoBar.Error(err)
} else {
h.Buf.Path = filename
h.Buf.SetName(filename)
InfoBar.Message("Saved " + filename)
}
}
})
} else {
InfoBar.Error(err)
}
} else {
h.Buf.Path = filename
h.Buf.SetName(filename)
InfoBar.Message("Saved " + filename)
}
}
2018-08-27 22:53:10 +03:00
// Find opens a prompt and searches forward for the input
2019-01-19 23:37:59 +03:00
func (h *BufPane) Find() bool {
2019-01-04 01:07:28 +03:00
InfoBar.Prompt("Find: ", "", "Find", func(resp string) {
2019-01-03 23:35:24 +03:00
// Event callback
2019-01-16 06:45:28 +03:00
match, found, _ := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.Cursor.Loc, true, true)
2019-01-03 23:27:43 +03:00
if found {
h.Cursor.SetSelectionStart(match[0])
h.Cursor.SetSelectionEnd(match[1])
h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0]
h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
} else {
h.Cursor.ResetSelection()
}
}, func(resp string, canceled bool) {
2019-01-03 23:35:24 +03:00
// Finished callback
2019-01-03 23:27:43 +03:00
if !canceled {
2019-01-16 06:45:28 +03:00
match, found, err := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.Cursor.Loc, true, true)
2019-01-03 23:27:43 +03:00
if err != nil {
InfoBar.Error(err)
}
if found {
h.Cursor.SetSelectionStart(match[0])
h.Cursor.SetSelectionEnd(match[1])
h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0]
h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
h.Cursor.Loc = h.Cursor.CurSelection[1]
2019-01-03 23:35:24 +03:00
h.lastSearch = resp
2019-01-03 23:27:43 +03:00
} else {
h.Cursor.ResetSelection()
2019-01-03 23:35:24 +03:00
InfoBar.Message("No matches found")
2019-01-03 23:27:43 +03:00
}
} else {
h.Cursor.ResetSelection()
}
})
2018-08-27 22:53:10 +03:00
return true
}
// FindNext searches forwards for the last used search term
2019-01-19 23:37:59 +03:00
func (h *BufPane) FindNext() bool {
2019-01-03 23:35:24 +03:00
// If the cursor is at the start of a selection and we search we want
// to search from the end of the selection in the case that
// the selection is a search result in which case we wouldn't move at
// at all which would be bad
searchLoc := h.Cursor.Loc
if h.Cursor.HasSelection() {
searchLoc = h.Cursor.CurSelection[1]
}
2019-01-16 06:45:28 +03:00
match, found, err := h.Buf.FindNext(h.lastSearch, h.Buf.Start(), h.Buf.End(), searchLoc, true, true)
2019-01-03 23:35:24 +03:00
if err != nil {
InfoBar.Error(err)
}
if found {
h.Cursor.SetSelectionStart(match[0])
h.Cursor.SetSelectionEnd(match[1])
h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0]
h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
h.Cursor.Loc = h.Cursor.CurSelection[1]
} else {
h.Cursor.ResetSelection()
}
2018-08-27 22:53:10 +03:00
return true
}
// FindPrevious searches backwards for the last used search term
2019-01-19 23:37:59 +03:00
func (h *BufPane) FindPrevious() bool {
2019-01-03 23:35:24 +03:00
// If the cursor is at the end of a selection and we search we want
// to search from the beginning of the selection in the case that
// the selection is a search result in which case we wouldn't move at
// at all which would be bad
searchLoc := h.Cursor.Loc
if h.Cursor.HasSelection() {
searchLoc = h.Cursor.CurSelection[0]
}
2019-01-16 06:45:28 +03:00
match, found, err := h.Buf.FindNext(h.lastSearch, h.Buf.Start(), h.Buf.End(), searchLoc, false, true)
2019-01-03 23:35:24 +03:00
if err != nil {
InfoBar.Error(err)
}
if found {
h.Cursor.SetSelectionStart(match[0])
h.Cursor.SetSelectionEnd(match[1])
h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0]
h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
h.Cursor.Loc = h.Cursor.CurSelection[1]
} else {
h.Cursor.ResetSelection()
}
2018-08-27 22:53:10 +03:00
return true
}
// Undo undoes the last action
2019-01-19 23:37:59 +03:00
func (h *BufPane) Undo() bool {
2019-01-01 00:36:54 +03:00
h.Buf.Undo()
2019-01-03 23:59:26 +03:00
InfoBar.Message("Undid action")
2018-08-27 22:53:10 +03:00
return true
}
// Redo redoes the last action
2019-01-19 23:37:59 +03:00
func (h *BufPane) Redo() bool {
2019-01-01 00:36:54 +03:00
h.Buf.Redo()
2019-01-03 23:59:26 +03:00
InfoBar.Message("Redid action")
2018-08-27 22:53:10 +03:00
return true
}
// Copy the selection to the system clipboard
2019-01-19 23:37:59 +03:00
func (h *BufPane) Copy() bool {
2019-01-01 00:36:54 +03:00
if h.Cursor.HasSelection() {
h.Cursor.CopySelection("clipboard")
h.freshClip = true
2019-01-03 00:27:27 +03:00
InfoBar.Message("Copied selection")
2019-01-01 00:36:54 +03:00
}
2018-08-27 22:53:10 +03:00
return true
}
// CutLine cuts the current line to the clipboard
2019-01-19 23:37:59 +03:00
func (h *BufPane) CutLine() bool {
2019-01-01 00:36:54 +03:00
h.Cursor.SelectLine()
if !h.Cursor.HasSelection() {
return false
}
if h.freshClip == true {
if h.Cursor.HasSelection() {
if clip, err := clipboard.ReadAll("clipboard"); err != nil {
// messenger.Error(err)
} else {
clipboard.WriteAll(clip+string(h.Cursor.GetSelection()), "clipboard")
}
}
} else if time.Since(h.lastCutTime)/time.Second > 10*time.Second || h.freshClip == false {
h.Copy()
}
h.freshClip = true
h.lastCutTime = time.Now()
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
2019-01-02 06:36:12 +03:00
InfoBar.Message("Cut line")
2018-08-27 22:53:10 +03:00
return true
}
// Cut the selection to the system clipboard
2019-01-19 23:37:59 +03:00
func (h *BufPane) Cut() bool {
2019-01-01 00:36:54 +03:00
if h.Cursor.HasSelection() {
h.Cursor.CopySelection("clipboard")
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
h.freshClip = true
2019-01-02 06:36:12 +03:00
InfoBar.Message("Cut selection")
2019-01-01 00:36:54 +03:00
return true
} else {
return h.CutLine()
}
2018-08-27 22:53:10 +03:00
}
// DuplicateLine duplicates the current line or selection
2019-01-19 23:37:59 +03:00
func (h *BufPane) DuplicateLine() bool {
2019-01-01 00:36:54 +03:00
if h.Cursor.HasSelection() {
h.Buf.Insert(h.Cursor.CurSelection[1], string(h.Cursor.GetSelection()))
2019-01-01 00:36:54 +03:00
} else {
h.Cursor.End()
h.Buf.Insert(h.Cursor.Loc, "\n"+string(h.Buf.LineBytes(h.Cursor.Y)))
2019-01-01 00:36:54 +03:00
// h.Cursor.Right()
}
2019-01-02 06:36:12 +03:00
InfoBar.Message("Duplicated line")
2018-08-27 22:53:10 +03:00
return true
}
// DeleteLine deletes the current line
2019-01-19 23:37:59 +03:00
func (h *BufPane) DeleteLine() bool {
2019-01-01 00:36:54 +03:00
h.Cursor.SelectLine()
if !h.Cursor.HasSelection() {
return false
}
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
2019-01-02 06:36:12 +03:00
InfoBar.Message("Deleted line")
2018-08-27 22:53:10 +03:00
return true
}
// MoveLinesUp moves up the current line or selected lines if any
2019-01-19 23:37:59 +03:00
func (h *BufPane) MoveLinesUp() bool {
2019-01-15 06:38:59 +03:00
if h.Cursor.HasSelection() {
if h.Cursor.CurSelection[0].Y == 0 {
InfoBar.Message("Can not move further up")
return true
}
start := h.Cursor.CurSelection[0].Y
end := h.Cursor.CurSelection[1].Y
if start > end {
end, start = start, end
}
h.Buf.MoveLinesUp(
start,
end,
)
h.Cursor.CurSelection[1].Y -= 1
} else {
if h.Cursor.Loc.Y == 0 {
InfoBar.Message("Can not move further up")
return true
}
h.Buf.MoveLinesUp(
h.Cursor.Loc.Y,
h.Cursor.Loc.Y+1,
)
}
2018-08-27 22:53:10 +03:00
return true
}
// MoveLinesDown moves down the current line or selected lines if any
2019-01-19 23:37:59 +03:00
func (h *BufPane) MoveLinesDown() bool {
2019-01-15 06:38:59 +03:00
if h.Cursor.HasSelection() {
if h.Cursor.CurSelection[1].Y >= h.Buf.LinesNum() {
InfoBar.Message("Can not move further down")
return true
}
start := h.Cursor.CurSelection[0].Y
end := h.Cursor.CurSelection[1].Y
if start > end {
end, start = start, end
}
h.Buf.MoveLinesDown(
start,
end,
)
} else {
if h.Cursor.Loc.Y >= h.Buf.LinesNum()-1 {
InfoBar.Message("Can not move further down")
return true
}
h.Buf.MoveLinesDown(
h.Cursor.Loc.Y,
h.Cursor.Loc.Y+1,
)
}
2018-08-27 22:53:10 +03:00
return true
}
// Paste whatever is in the system clipboard into the buffer
// Delete and paste if the user has a selection
2019-01-19 23:37:59 +03:00
func (h *BufPane) Paste() bool {
2019-01-03 00:27:27 +03:00
clip, _ := clipboard.ReadAll("clipboard")
h.paste(clip)
2018-08-27 22:53:10 +03:00
return true
}
// PastePrimary pastes from the primary clipboard (only use on linux)
2019-01-19 23:37:59 +03:00
func (h *BufPane) PastePrimary() bool {
2019-01-03 00:27:27 +03:00
clip, _ := clipboard.ReadAll("primary")
h.paste(clip)
2018-08-27 22:53:10 +03:00
return true
}
2019-01-19 23:37:59 +03:00
func (h *BufPane) paste(clip string) {
2019-01-03 00:27:27 +03:00
if h.Buf.Settings["smartpaste"].(bool) {
if h.Cursor.X > 0 && len(util.GetLeadingWhitespace([]byte(strings.TrimLeft(clip, "\r\n")))) == 0 {
leadingWS := util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y))
clip = strings.Replace(clip, "\n", "\n"+string(leadingWS), -1)
}
}
if h.Cursor.HasSelection() {
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
}
h.Buf.Insert(h.Cursor.Loc, clip)
2019-01-03 00:27:27 +03:00
// h.Cursor.Loc = h.Cursor.Loc.Move(Count(clip), h.Buf)
h.freshClip = false
InfoBar.Message("Pasted clipboard")
}
2018-08-27 22:53:10 +03:00
// JumpToMatchingBrace moves the cursor to the matching brace if it is
// currently on a brace
2019-01-19 23:37:59 +03:00
func (h *BufPane) JumpToMatchingBrace() bool {
2019-01-15 06:38:59 +03:00
for _, bp := range buffer.BracePairs {
r := h.Cursor.RuneUnder(h.Cursor.X)
if r == bp[0] || r == bp[1] {
matchingBrace := h.Buf.FindMatchingBrace(bp, h.Cursor.Loc)
h.Cursor.GotoLoc(matchingBrace)
}
}
2018-08-27 22:53:10 +03:00
return true
}
// SelectAll selects the entire buffer
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectAll() bool {
2019-01-01 00:36:54 +03:00
h.Cursor.SetSelectionStart(h.Buf.Start())
h.Cursor.SetSelectionEnd(h.Buf.End())
// Put the cursor at the beginning
h.Cursor.X = 0
h.Cursor.Y = 0
2018-08-27 22:53:10 +03:00
return true
}
// OpenFile opens a new file in the buffer
2019-01-19 23:37:59 +03:00
func (h *BufPane) OpenFile() bool {
2019-01-04 01:07:28 +03:00
InfoBar.Prompt("> ", "open ", "Open", nil, func(resp string, canceled bool) {
2019-01-01 07:47:24 +03:00
if !canceled {
2019-01-11 22:49:22 +03:00
h.HandleCommand(resp)
2019-01-01 07:47:24 +03:00
}
2019-01-03 00:27:27 +03:00
})
2018-08-27 22:53:10 +03:00
return false
}
// Start moves the viewport to the start of the buffer
2019-01-19 23:37:59 +03:00
func (h *BufPane) Start() bool {
2019-01-11 05:26:58 +03:00
v := h.GetView()
2019-01-01 06:07:01 +03:00
v.StartLine = 0
2019-01-11 05:26:58 +03:00
h.SetView(v)
2018-08-27 22:53:10 +03:00
return false
}
// End moves the viewport to the end of the buffer
2019-01-19 23:37:59 +03:00
func (h *BufPane) End() bool {
2019-01-01 00:36:54 +03:00
// TODO: softwrap problems?
2019-01-11 05:26:58 +03:00
v := h.GetView()
2019-01-01 06:07:01 +03:00
if v.Height > h.Buf.LinesNum() {
v.StartLine = 0
2019-01-11 05:26:58 +03:00
h.SetView(v)
2019-01-01 00:36:54 +03:00
} else {
2019-01-01 06:07:01 +03:00
h.StartLine = h.Buf.LinesNum() - v.Height
2019-01-01 00:36:54 +03:00
}
2018-08-27 22:53:10 +03:00
return false
}
// PageUp scrolls the view up a page
2019-01-19 23:37:59 +03:00
func (h *BufPane) PageUp() bool {
2019-01-11 05:26:58 +03:00
v := h.GetView()
2019-01-03 00:27:27 +03:00
if v.StartLine > v.Height {
h.ScrollUp(v.Height)
} else {
v.StartLine = 0
}
2019-01-11 05:26:58 +03:00
h.SetView(v)
2018-08-27 22:53:10 +03:00
return false
}
// PageDown scrolls the view down a page
2019-01-19 23:37:59 +03:00
func (h *BufPane) PageDown() bool {
2019-01-11 05:26:58 +03:00
v := h.GetView()
2019-01-03 00:27:27 +03:00
if h.Buf.LinesNum()-(v.StartLine+v.Height) > v.Height {
h.ScrollDown(v.Height)
} else if h.Buf.LinesNum() >= v.Height {
v.StartLine = h.Buf.LinesNum() - v.Height
}
2018-08-27 22:53:10 +03:00
return false
}
// SelectPageUp selects up one page
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectPageUp() bool {
2019-01-03 00:27:27 +03:00
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
2019-01-11 05:26:58 +03:00
h.Cursor.UpN(h.GetView().Height)
2019-01-03 00:27:27 +03:00
h.Cursor.SelectTo(h.Cursor.Loc)
2018-08-27 22:53:10 +03:00
return true
}
// SelectPageDown selects down one page
2019-01-19 23:37:59 +03:00
func (h *BufPane) SelectPageDown() bool {
2019-01-03 00:27:27 +03:00
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
2019-01-11 05:26:58 +03:00
h.Cursor.DownN(h.GetView().Height)
2019-01-03 00:27:27 +03:00
h.Cursor.SelectTo(h.Cursor.Loc)
2018-08-27 22:53:10 +03:00
return true
}
// CursorPageUp places the cursor a page up
2019-01-19 23:37:59 +03:00
func (h *BufPane) CursorPageUp() bool {
2019-01-03 00:27:27 +03:00
h.Cursor.Deselect(true)
if h.Cursor.HasSelection() {
h.Cursor.Loc = h.Cursor.CurSelection[0]
h.Cursor.ResetSelection()
h.Cursor.StoreVisualX()
}
2019-01-11 05:26:58 +03:00
h.Cursor.UpN(h.GetView().Height)
2018-08-27 22:53:10 +03:00
return true
}
// CursorPageDown places the cursor a page up
2019-01-19 23:37:59 +03:00
func (h *BufPane) CursorPageDown() bool {
2019-01-03 00:27:27 +03:00
h.Cursor.Deselect(false)
if h.Cursor.HasSelection() {
h.Cursor.Loc = h.Cursor.CurSelection[1]
h.Cursor.ResetSelection()
h.Cursor.StoreVisualX()
}
2019-01-11 05:26:58 +03:00
h.Cursor.DownN(h.GetView().Height)
2018-08-27 22:53:10 +03:00
return true
}
// HalfPageUp scrolls the view up half a page
2019-01-19 23:37:59 +03:00
func (h *BufPane) HalfPageUp() bool {
2019-01-11 05:26:58 +03:00
v := h.GetView()
2019-01-03 00:27:27 +03:00
if v.StartLine > v.Height/2 {
h.ScrollUp(v.Height / 2)
} else {
v.StartLine = 0
}
2019-01-11 05:26:58 +03:00
h.SetView(v)
2018-08-27 22:53:10 +03:00
return false
}
// HalfPageDown scrolls the view down half a page
2019-01-19 23:37:59 +03:00
func (h *BufPane) HalfPageDown() bool {
2019-01-11 05:26:58 +03:00
v := h.GetView()
2019-01-03 00:27:27 +03:00
if h.Buf.LinesNum()-(v.StartLine+v.Height) > v.Height/2 {
h.ScrollDown(v.Height / 2)
} else {
if h.Buf.LinesNum() >= v.Height {
v.StartLine = h.Buf.LinesNum() - v.Height
}
}
2019-01-11 05:26:58 +03:00
h.SetView(v)
2018-08-27 22:53:10 +03:00
return false
}
// ToggleRuler turns line numbers off and on
2019-01-19 23:37:59 +03:00
func (h *BufPane) ToggleRuler() bool {
2019-01-01 00:36:54 +03:00
if !h.Buf.Settings["ruler"].(bool) {
h.Buf.Settings["ruler"] = true
2019-01-02 06:36:12 +03:00
InfoBar.Message("Enabled ruler")
2019-01-01 00:36:54 +03:00
} else {
h.Buf.Settings["ruler"] = false
2019-01-02 06:36:12 +03:00
InfoBar.Message("Disabled ruler")
2019-01-01 00:36:54 +03:00
}
2018-08-27 22:53:10 +03:00
return false
}
// JumpLine jumps to a line and moves the view accordingly.
2019-01-19 23:37:59 +03:00
func (h *BufPane) JumpLine() bool {
2018-08-27 22:53:10 +03:00
return false
}
// ClearStatus clears the messenger bar
2019-01-19 23:37:59 +03:00
func (h *BufPane) ClearStatus() bool {
2019-01-03 00:27:27 +03:00
InfoBar.Message("")
2018-08-27 22:53:10 +03:00
return false
}
// ToggleHelp toggles the help screen
2019-01-19 23:37:59 +03:00
func (h *BufPane) ToggleHelp() bool {
2019-01-16 01:10:13 +03:00
if h.Buf.Type == buffer.BTHelp {
h.Quit()
} else {
h.openHelp("help")
}
return false
2018-08-27 22:53:10 +03:00
}
// ToggleKeyMenu toggles the keymenu option and resizes all tabs
2019-01-19 23:37:59 +03:00
func (h *BufPane) ToggleKeyMenu() bool {
2019-01-17 02:37:45 +03:00
config.GlobalSettings["keymenu"] = !config.GetGlobalOption("keymenu").(bool)
Tabs.Resize()
2019-01-16 01:10:13 +03:00
return false
2018-08-27 22:53:10 +03:00
}
// ShellMode opens a terminal to run a shell command
2019-01-19 23:37:59 +03:00
func (h *BufPane) ShellMode() bool {
2019-01-11 00:37:05 +03:00
InfoBar.Prompt("$ ", "", "Shell", nil, func(resp string, canceled bool) {
if !canceled {
// The true here is for openTerm to make the command interactive
shell.RunInteractiveShell(resp, true, false)
}
})
2018-08-27 22:53:10 +03:00
return false
}
// CommandMode lets the user enter a command
2019-01-19 23:37:59 +03:00
func (h *BufPane) CommandMode() bool {
2019-01-04 01:07:28 +03:00
InfoBar.Prompt("> ", "", "Command", nil, func(resp string, canceled bool) {
2019-01-02 07:29:25 +03:00
if !canceled {
2019-01-11 22:49:22 +03:00
h.HandleCommand(resp)
2019-01-02 07:29:25 +03:00
}
})
2018-08-27 22:53:10 +03:00
return false
}
// ToggleOverwriteMode lets the user toggle the text overwrite mode
2019-01-19 23:37:59 +03:00
func (h *BufPane) ToggleOverwriteMode() bool {
2019-01-03 00:27:27 +03:00
h.isOverwriteMode = !h.isOverwriteMode
2018-08-27 22:53:10 +03:00
return false
}
// Escape leaves current mode
2019-01-19 23:37:59 +03:00
func (h *BufPane) Escape() bool {
2018-08-27 22:53:10 +03:00
return false
}
// Quit this will close the current tab or view that is open
2019-01-19 23:37:59 +03:00
func (h *BufPane) Quit() bool {
2019-01-10 00:55:00 +03:00
quit := func() {
2019-01-14 05:06:58 +03:00
h.Buf.Close()
2019-01-10 04:07:18 +03:00
if len(MainTab().Panes) > 1 {
2019-01-10 00:55:00 +03:00
h.Unsplit()
2019-01-10 07:44:53 +03:00
} else if len(Tabs.List) > 1 {
Tabs.RemoveTab(h.splitID)
2019-01-10 00:55:00 +03:00
} else {
screen.Screen.Fini()
2019-01-10 07:44:53 +03:00
InfoBar.Close()
2019-01-10 00:55:00 +03:00
os.Exit(0)
}
}
2019-01-05 05:48:19 +03:00
if h.Buf.Modified() {
InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) {
if !canceled && !yes {
2019-01-10 00:55:00 +03:00
quit()
2019-01-11 22:49:22 +03:00
} else if !canceled && yes {
h.Save()
quit()
2019-01-05 05:48:19 +03:00
}
})
} else {
2019-01-10 00:55:00 +03:00
quit()
2019-01-05 05:48:19 +03:00
}
2018-08-27 22:53:10 +03:00
return false
}
// QuitAll quits the whole editor; all splits and tabs
2019-01-19 23:37:59 +03:00
func (h *BufPane) QuitAll() bool {
2018-08-27 22:53:10 +03:00
return false
}
// AddTab adds a new tab with an empty buffer
2019-01-19 23:37:59 +03:00
func (h *BufPane) AddTab() bool {
2019-01-10 07:44:53 +03:00
width, height := screen.Screen.Size()
2019-01-15 06:16:44 +03:00
iOffset := config.GetInfoBarOffset()
2019-01-10 07:44:53 +03:00
b := buffer.NewBufferFromString("", "", buffer.BTDefault)
2019-01-15 06:16:44 +03:00
tp := NewTabFromBuffer(0, 0, width, height-iOffset, b)
2019-01-10 07:44:53 +03:00
Tabs.AddTab(tp)
Tabs.SetActive(len(Tabs.List) - 1)
2019-01-11 23:04:55 +03:00
return false
2018-08-27 22:53:10 +03:00
}
// PreviousTab switches to the previous tab in the tab list
2019-01-19 23:37:59 +03:00
func (h *BufPane) PreviousTab() bool {
2019-01-10 07:44:53 +03:00
a := Tabs.Active()
Tabs.SetActive(util.Clamp(a-1, 0, len(Tabs.List)-1))
2018-08-27 22:53:10 +03:00
return false
}
// NextTab switches to the next tab in the tab list
2019-01-19 23:37:59 +03:00
func (h *BufPane) NextTab() bool {
2019-01-10 07:44:53 +03:00
a := Tabs.Active()
Tabs.SetActive(util.Clamp(a+1, 0, len(Tabs.List)-1))
2018-08-27 22:53:10 +03:00
return false
}
2019-01-14 02:18:23 +03:00
// VSplitAction opens an empty vertical split
2019-01-19 23:37:59 +03:00
func (h *BufPane) VSplitAction() bool {
2019-01-14 02:18:23 +03:00
h.VSplitBuf(buffer.NewBufferFromString("", "", buffer.BTDefault))
2019-01-10 07:44:53 +03:00
2018-08-27 22:53:10 +03:00
return false
}
2019-01-14 02:18:23 +03:00
// HSplitAction opens an empty horizontal split
2019-01-19 23:37:59 +03:00
func (h *BufPane) HSplitAction() bool {
2019-01-14 02:18:23 +03:00
h.HSplitBuf(buffer.NewBufferFromString("", "", buffer.BTDefault))
2019-01-10 07:44:53 +03:00
2018-08-27 22:53:10 +03:00
return false
}
// Unsplit closes all splits in the current tab except the active one
2019-01-19 23:37:59 +03:00
func (h *BufPane) Unsplit() bool {
2019-01-10 04:07:18 +03:00
n := MainTab().GetNode(h.splitID)
2019-01-10 00:55:00 +03:00
n.Unsplit()
2019-01-10 04:07:18 +03:00
MainTab().RemovePane(MainTab().GetPane(h.splitID))
MainTab().Resize()
MainTab().SetActive(len(MainTab().Panes) - 1)
2018-08-27 22:53:10 +03:00
return false
}
// NextSplit changes the view to the next split
2019-01-19 23:37:59 +03:00
func (h *BufPane) NextSplit() bool {
2019-01-10 07:44:53 +03:00
a := MainTab().active
if a < len(MainTab().Panes)-1 {
a++
} else {
a = 0
}
2019-01-10 07:44:53 +03:00
MainTab().SetActive(a)
2018-08-27 22:53:10 +03:00
return false
}
// PreviousSplit changes the view to the previous split
2019-01-19 23:37:59 +03:00
func (h *BufPane) PreviousSplit() bool {
2019-01-10 07:44:53 +03:00
a := MainTab().active
if a > 0 {
a--
} else {
a = len(MainTab().Panes) - 1
}
2019-01-10 07:44:53 +03:00
MainTab().SetActive(a)
2018-08-27 22:53:10 +03:00
return false
}
var curMacro []interface{}
var recordingMacro bool
// ToggleMacro toggles recording of a macro
2019-01-19 23:37:59 +03:00
func (h *BufPane) ToggleMacro() bool {
2018-08-27 22:53:10 +03:00
return true
}
// PlayMacro plays back the most recently recorded macro
2019-01-19 23:37:59 +03:00
func (h *BufPane) PlayMacro() bool {
2018-08-27 22:53:10 +03:00
return true
}
// SpawnMultiCursor creates a new multiple cursor at the next occurrence of the current selection or current word
2019-01-19 23:37:59 +03:00
func (h *BufPane) SpawnMultiCursor() bool {
2019-01-03 23:59:26 +03:00
spawner := h.Buf.GetCursor(h.Buf.NumCursors() - 1)
if !spawner.HasSelection() {
spawner.SelectWord()
2019-01-05 05:48:19 +03:00
h.multiWord = true
return true
}
2019-01-03 23:59:26 +03:00
2019-01-05 05:48:19 +03:00
sel := spawner.GetSelection()
searchStart := spawner.CurSelection[1]
2019-01-03 23:59:26 +03:00
2019-01-05 05:48:19 +03:00
search := string(sel)
2019-01-17 01:52:30 +03:00
search = regexp.QuoteMeta(search)
2019-01-05 05:48:19 +03:00
if h.multiWord {
search = "\\b" + search + "\\b"
}
2019-01-17 01:52:30 +03:00
match, found, err := h.Buf.FindNext(search, h.Buf.Start(), h.Buf.End(), searchStart, true, true)
2019-01-05 05:48:19 +03:00
if err != nil {
InfoBar.Error(err)
}
if found {
c := buffer.NewCursor(h.Buf, buffer.Loc{})
c.SetSelectionStart(match[0])
c.SetSelectionEnd(match[1])
c.OrigSelection[0] = c.CurSelection[0]
c.OrigSelection[1] = c.CurSelection[1]
c.Loc = c.CurSelection[1]
h.Buf.AddCursor(c)
2019-01-17 01:52:30 +03:00
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
2019-01-05 05:48:19 +03:00
h.Buf.MergeCursors()
} else {
InfoBar.Message("No matches found")
2019-01-03 23:59:26 +03:00
}
2019-01-05 05:48:19 +03:00
return true
2018-08-27 22:53:10 +03:00
}
// SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection
2019-01-19 23:37:59 +03:00
func (h *BufPane) SpawnMultiCursorSelect() bool {
2019-01-03 23:59:26 +03:00
// Avoid cases where multiple cursors already exist, that would create problems
if h.Buf.NumCursors() > 1 {
return false
}
var startLine int
var endLine int
a, b := h.Cursor.CurSelection[0].Y, h.Cursor.CurSelection[1].Y
if a > b {
startLine, endLine = b, a
} else {
startLine, endLine = a, b
}
if h.Cursor.HasSelection() {
h.Cursor.ResetSelection()
h.Cursor.GotoLoc(buffer.Loc{0, startLine})
for i := startLine; i <= endLine; i++ {
c := buffer.NewCursor(h.Buf, buffer.Loc{0, i})
c.StoreVisualX()
h.Buf.AddCursor(c)
}
h.Buf.MergeCursors()
} else {
return false
}
InfoBar.Message("Added cursors from selection")
2018-08-27 22:53:10 +03:00
return false
}
// MouseMultiCursor is a mouse action which puts a new cursor at the mouse position
2019-01-19 23:37:59 +03:00
func (h *BufPane) MouseMultiCursor(e *tcell.EventMouse) bool {
2019-01-03 07:26:40 +03:00
b := h.Buf
mx, my := e.Position()
2019-01-11 05:26:58 +03:00
mouseLoc := h.GetMouseLoc(buffer.Loc{X: mx, Y: my})
2019-01-03 07:26:40 +03:00
c := buffer.NewCursor(b, mouseLoc)
b.AddCursor(c)
b.MergeCursors()
2018-08-27 22:53:10 +03:00
return false
}
// SkipMultiCursor moves the current multiple cursor to the next available position
2019-01-19 23:37:59 +03:00
func (h *BufPane) SkipMultiCursor() bool {
2019-01-03 23:59:26 +03:00
lastC := h.Buf.GetCursor(h.Buf.NumCursors() - 1)
sel := lastC.GetSelection()
searchStart := lastC.CurSelection[1]
2019-01-17 01:52:30 +03:00
search := string(sel)
search = regexp.QuoteMeta(search)
if h.multiWord {
search = "\\b" + search + "\\b"
}
match, found, err := h.Buf.FindNext(search, h.Buf.Start(), h.Buf.End(), searchStart, true, true)
2019-01-03 23:59:26 +03:00
if err != nil {
InfoBar.Error(err)
}
if found {
lastC.SetSelectionStart(match[0])
lastC.SetSelectionEnd(match[1])
lastC.OrigSelection[0] = lastC.CurSelection[0]
lastC.OrigSelection[1] = lastC.CurSelection[1]
lastC.Loc = lastC.CurSelection[1]
h.Buf.MergeCursors()
2019-01-17 01:52:30 +03:00
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
2019-01-03 23:59:26 +03:00
} else {
InfoBar.Message("No matches found")
}
2019-01-17 01:52:30 +03:00
return true
2018-08-27 22:53:10 +03:00
}
// RemoveMultiCursor removes the latest multiple cursor
2019-01-19 23:37:59 +03:00
func (h *BufPane) RemoveMultiCursor() bool {
2019-01-03 07:26:40 +03:00
if h.Buf.NumCursors() > 1 {
h.Buf.RemoveCursor(h.Buf.NumCursors() - 1)
2019-01-17 01:52:30 +03:00
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
2019-01-03 07:26:40 +03:00
h.Buf.UpdateCursors()
2019-01-05 05:48:19 +03:00
} else {
h.multiWord = false
2019-01-03 07:26:40 +03:00
}
2019-01-17 01:52:30 +03:00
return true
2018-08-27 22:53:10 +03:00
}
// RemoveAllMultiCursors removes all cursors except the base cursor
2019-01-19 23:37:59 +03:00
func (h *BufPane) RemoveAllMultiCursors() bool {
2019-01-03 07:26:40 +03:00
h.Buf.ClearCursors()
2019-01-05 05:48:19 +03:00
h.multiWord = false
2019-01-03 07:26:40 +03:00
return true
2018-08-27 22:53:10 +03:00
}