mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-15 21:37:09 +09:00
Add multi cursor support
This commit is contained in:
@@ -985,6 +985,13 @@ func (h *BufHandler) SpawnMultiCursorSelect() bool {
|
||||
|
||||
// MouseMultiCursor is a mouse action which puts a new cursor at the mouse position
|
||||
func (h *BufHandler) MouseMultiCursor(e *tcell.EventMouse) bool {
|
||||
b := h.Buf
|
||||
mx, my := e.Position()
|
||||
mouseLoc := h.Win.GetMouseLoc(buffer.Loc{mx, my})
|
||||
c := buffer.NewCursor(b, mouseLoc)
|
||||
b.AddCursor(c)
|
||||
b.MergeCursors()
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -995,10 +1002,15 @@ func (h *BufHandler) SkipMultiCursor() bool {
|
||||
|
||||
// RemoveMultiCursor removes the latest multiple cursor
|
||||
func (h *BufHandler) RemoveMultiCursor() bool {
|
||||
if h.Buf.NumCursors() > 1 {
|
||||
h.Buf.RemoveCursor(h.Buf.NumCursors() - 1)
|
||||
h.Buf.UpdateCursors()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveAllMultiCursors removes all cursors except the base cursor
|
||||
func (h *BufHandler) RemoveAllMultiCursors() bool {
|
||||
return false
|
||||
h.Buf.ClearCursors()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -13,15 +13,18 @@ type BufKeyAction func(*BufHandler) bool
|
||||
type BufMouseAction func(*BufHandler, *tcell.EventMouse) bool
|
||||
|
||||
var BufKeyBindings map[KeyEvent]BufKeyAction
|
||||
var BufKeyStrings map[KeyEvent]string
|
||||
var BufMouseBindings map[MouseEvent]BufMouseAction
|
||||
|
||||
func init() {
|
||||
BufKeyBindings = make(map[KeyEvent]BufKeyAction)
|
||||
BufKeyStrings = make(map[KeyEvent]string)
|
||||
BufMouseBindings = make(map[MouseEvent]BufMouseAction)
|
||||
}
|
||||
|
||||
func BufMapKey(k KeyEvent, action string) {
|
||||
if f, ok := BufKeyActions[action]; ok {
|
||||
BufKeyStrings[k] = action
|
||||
BufKeyBindings[k] = f
|
||||
} else {
|
||||
util.TermMessage("Error:", action, "does not exist")
|
||||
@@ -106,16 +109,11 @@ func (h *BufHandler) HandleEvent(event tcell.Event) {
|
||||
mod: e.Modifiers(),
|
||||
r: e.Rune(),
|
||||
}
|
||||
cursors := h.Buf.GetCursors()
|
||||
for _, c := range cursors {
|
||||
h.Buf.SetCurCursor(c.Num)
|
||||
h.Cursor = c
|
||||
|
||||
done := h.DoKeyEvent(ke)
|
||||
if !done && e.Key() == tcell.KeyRune {
|
||||
h.DoRuneInsert(e.Rune())
|
||||
}
|
||||
}
|
||||
// TODO: maybe reset curcursor to 0
|
||||
case *tcell.EventMouse:
|
||||
switch e.Buttons() {
|
||||
case tcell.ButtonNone:
|
||||
@@ -145,17 +143,25 @@ func (h *BufHandler) HandleEvent(event tcell.Event) {
|
||||
btn: e.Buttons(),
|
||||
mod: e.Modifiers(),
|
||||
}
|
||||
cursors := h.Buf.GetCursors()
|
||||
for _, c := range cursors {
|
||||
h.Buf.SetCurCursor(c.Num)
|
||||
h.Cursor = c
|
||||
h.DoMouseEvent(me, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *BufHandler) DoKeyEvent(e KeyEvent) bool {
|
||||
if action, ok := BufKeyBindings[e]; ok {
|
||||
for _, a := range MultiActions {
|
||||
if a == BufKeyStrings[e] {
|
||||
cursors := h.Buf.GetCursors()
|
||||
for _, c := range cursors {
|
||||
h.Buf.SetCurCursor(c.Num)
|
||||
h.Cursor = c
|
||||
if action(h) {
|
||||
h.Win.Relocate()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if action(h) {
|
||||
h.Win.Relocate()
|
||||
}
|
||||
@@ -175,18 +181,21 @@ func (h *BufHandler) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
|
||||
}
|
||||
|
||||
func (h *BufHandler) DoRuneInsert(r rune) {
|
||||
cursors := h.Buf.GetCursors()
|
||||
for _, c := range cursors {
|
||||
// Insert a character
|
||||
if h.Cursor.HasSelection() {
|
||||
h.Cursor.DeleteSelection()
|
||||
h.Cursor.ResetSelection()
|
||||
if c.HasSelection() {
|
||||
c.DeleteSelection()
|
||||
c.ResetSelection()
|
||||
}
|
||||
|
||||
if h.isOverwriteMode {
|
||||
next := h.Cursor.Loc
|
||||
next := c.Loc
|
||||
next.X++
|
||||
h.Buf.Replace(h.Cursor.Loc, next, string(r))
|
||||
h.Buf.Replace(c.Loc, next, string(r))
|
||||
} else {
|
||||
h.Buf.Insert(h.Cursor.Loc, string(r))
|
||||
h.Buf.Insert(c.Loc, string(r))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,3 +301,60 @@ var BufMouseActions = map[string]BufMouseAction{
|
||||
"MousePress": (*BufHandler).MousePress,
|
||||
"MouseMultiCursor": (*BufHandler).MouseMultiCursor,
|
||||
}
|
||||
|
||||
const funcPrefixLen = 21 // length of "action.(*BufHandler)."
|
||||
|
||||
// MultiActions is a list of actions that should be executed multiple
|
||||
// times if there are multiple cursors (one per cursor)
|
||||
// Generally actions that modify global editor state like quitting or
|
||||
// saving should not be included in this list
|
||||
var MultiActions = []string{
|
||||
"CursorUp",
|
||||
"CursorDown",
|
||||
"CursorPageUp",
|
||||
"CursorPageDown",
|
||||
"CursorLeft",
|
||||
"CursorRight",
|
||||
"CursorStart",
|
||||
"CursorEnd",
|
||||
"SelectToStart",
|
||||
"SelectToEnd",
|
||||
"SelectUp",
|
||||
"SelectDown",
|
||||
"SelectLeft",
|
||||
"SelectRight",
|
||||
"WordRight",
|
||||
"WordLeft",
|
||||
"SelectWordRight",
|
||||
"SelectWordLeft",
|
||||
"DeleteWordRight",
|
||||
"DeleteWordLeft",
|
||||
"SelectLine",
|
||||
"SelectToStartOfLine",
|
||||
"SelectToEndOfLine",
|
||||
"ParagraphPrevious",
|
||||
"ParagraphNext",
|
||||
"InsertNewline",
|
||||
"InsertSpace",
|
||||
"Backspace",
|
||||
"Delete",
|
||||
"InsertTab",
|
||||
"FindNext",
|
||||
"FindPrevious",
|
||||
"Cut",
|
||||
"CutLine",
|
||||
"DuplicateLine",
|
||||
"DeleteLine",
|
||||
"MoveLinesUp",
|
||||
"MoveLinesDown",
|
||||
"IndentSelection",
|
||||
"OutdentSelection",
|
||||
"OutdentLine",
|
||||
"Paste",
|
||||
"PastePrimary",
|
||||
"SelectPageUp",
|
||||
"SelectPageDown",
|
||||
"StartOfLine",
|
||||
"EndOfLine",
|
||||
"JumpToMatchingBrace",
|
||||
}
|
||||
|
||||
@@ -236,36 +236,6 @@ func (b *Buffer) ReOpen() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// SetCursors resets this buffer's cursors to a new list
|
||||
func (b *Buffer) SetCursors(c []*Cursor) {
|
||||
b.cursors = c
|
||||
}
|
||||
|
||||
// SetCurCursor sets the current cursor
|
||||
func (b *Buffer) SetCurCursor(n int) {
|
||||
b.curCursor = n
|
||||
}
|
||||
|
||||
// GetActiveCursor returns the main cursor in this buffer
|
||||
func (b *Buffer) GetActiveCursor() *Cursor {
|
||||
return b.cursors[b.curCursor]
|
||||
}
|
||||
|
||||
// GetCursor returns the nth cursor
|
||||
func (b *Buffer) GetCursor(n int) *Cursor {
|
||||
return b.cursors[n]
|
||||
}
|
||||
|
||||
// GetCursors returns the list of cursors in this buffer
|
||||
func (b *Buffer) GetCursors() []*Cursor {
|
||||
return b.cursors
|
||||
}
|
||||
|
||||
// NumCursors returns the number of cursors
|
||||
func (b *Buffer) NumCursors() int {
|
||||
return len(b.cursors)
|
||||
}
|
||||
|
||||
// LineBytes returns line n as an array of bytes
|
||||
func (b *Buffer) LineBytes(n int) []byte {
|
||||
if n >= len(b.lines) || n < 0 {
|
||||
@@ -429,6 +399,42 @@ func (b *Buffer) IndentString(tabsize int) string {
|
||||
return "\t"
|
||||
}
|
||||
|
||||
// SetCursors resets this buffer's cursors to a new list
|
||||
func (b *Buffer) SetCursors(c []*Cursor) {
|
||||
b.cursors = c
|
||||
}
|
||||
|
||||
// AddCursor adds a new cursor to the list
|
||||
func (b *Buffer) AddCursor(c *Cursor) {
|
||||
b.cursors = append(b.cursors, c)
|
||||
b.UpdateCursors()
|
||||
}
|
||||
|
||||
// SetCurCursor sets the current cursor
|
||||
func (b *Buffer) SetCurCursor(n int) {
|
||||
b.curCursor = n
|
||||
}
|
||||
|
||||
// GetActiveCursor returns the main cursor in this buffer
|
||||
func (b *Buffer) GetActiveCursor() *Cursor {
|
||||
return b.cursors[b.curCursor]
|
||||
}
|
||||
|
||||
// GetCursor returns the nth cursor
|
||||
func (b *Buffer) GetCursor(n int) *Cursor {
|
||||
return b.cursors[n]
|
||||
}
|
||||
|
||||
// GetCursors returns the list of cursors in this buffer
|
||||
func (b *Buffer) GetCursors() []*Cursor {
|
||||
return b.cursors
|
||||
}
|
||||
|
||||
// NumCursors returns the number of cursors
|
||||
func (b *Buffer) NumCursors() int {
|
||||
return len(b.cursors)
|
||||
}
|
||||
|
||||
// MergeCursors merges any cursors that are at the same position
|
||||
// into one cursor
|
||||
func (b *Buffer) MergeCursors() {
|
||||
@@ -464,6 +470,12 @@ func (b *Buffer) UpdateCursors() {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) RemoveCursor(i int) {
|
||||
copy(b.cursors[i:], b.cursors[i+1:])
|
||||
b.cursors[len(b.cursors)-1] = nil
|
||||
b.cursors = b.cursors[:len(b.cursors)-1]
|
||||
}
|
||||
|
||||
// ClearCursors removes all extra cursors
|
||||
func (b *Buffer) ClearCursors() {
|
||||
for i := 1; i < len(b.cursors); i++ {
|
||||
|
||||
@@ -317,7 +317,7 @@ func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc, r rune) (tcell.
|
||||
}
|
||||
|
||||
func (w *BufWindow) showCursor(x, y int, main bool) {
|
||||
if main {
|
||||
if !main {
|
||||
screen.Screen.ShowCursor(x, y)
|
||||
} else {
|
||||
r, _, _, _ := screen.Screen.GetContent(x, y)
|
||||
@@ -370,15 +370,18 @@ func (w *BufWindow) displayBuffer() {
|
||||
// this represents the current draw position in the buffer (char positions)
|
||||
bloc := buffer.Loc{X: -1, Y: w.StartLine}
|
||||
|
||||
activeC := b.GetActiveCursor()
|
||||
cursors := b.GetCursors()
|
||||
|
||||
curStyle := config.DefStyle
|
||||
for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ {
|
||||
vloc.X = 0
|
||||
if b.Settings["ruler"].(bool) {
|
||||
s := lineNumStyle
|
||||
if bloc.Y == activeC.Y {
|
||||
for _, c := range cursors {
|
||||
if bloc.Y == c.Y {
|
||||
s = curNumStyle
|
||||
break
|
||||
}
|
||||
}
|
||||
w.drawLineNum(s, false, maxLineNumLength, &vloc, &bloc)
|
||||
}
|
||||
@@ -391,11 +394,12 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
bloc.X = bslice
|
||||
|
||||
draw := func(r rune, style tcell.Style) {
|
||||
draw := func(r rune, style tcell.Style, showcursor bool) {
|
||||
if nColsBeforeStart <= 0 {
|
||||
if activeC.HasSelection() &&
|
||||
(bloc.GreaterEqual(activeC.CurSelection[0]) && bloc.LessThan(activeC.CurSelection[1]) ||
|
||||
bloc.LessThan(activeC.CurSelection[0]) && bloc.GreaterEqual(activeC.CurSelection[1])) {
|
||||
for _, c := range cursors {
|
||||
if c.HasSelection() &&
|
||||
(bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) ||
|
||||
bloc.LessThan(c.CurSelection[0]) && bloc.GreaterEqual(c.CurSelection[1])) {
|
||||
// The current character is selected
|
||||
style = config.DefStyle.Reverse(true)
|
||||
|
||||
@@ -405,14 +409,23 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
|
||||
if b.Settings["cursorline"].(bool) &&
|
||||
!activeC.HasSelection() && activeC.Y == bloc.Y {
|
||||
!c.HasSelection() && c.Y == bloc.Y {
|
||||
if s, ok := config.Colorscheme["cursor-line"]; ok {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Background(fg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style)
|
||||
|
||||
if showcursor {
|
||||
for _, c := range cursors {
|
||||
if c.X == bloc.X && c.Y == bloc.Y {
|
||||
w.showCursor(w.X+vloc.X, w.Y+vloc.Y, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
vloc.X++
|
||||
}
|
||||
nColsBeforeStart--
|
||||
@@ -422,14 +435,10 @@ func (w *BufWindow) displayBuffer() {
|
||||
|
||||
totalwidth := w.StartCol - nColsBeforeStart
|
||||
for len(line) > 0 {
|
||||
if activeC.X == bloc.X && activeC.Y == bloc.Y {
|
||||
w.showCursor(vloc.X, vloc.Y, true)
|
||||
}
|
||||
|
||||
r, size := utf8.DecodeRune(line)
|
||||
curStyle, _ = w.getStyle(curStyle, bloc, r)
|
||||
|
||||
draw(r, curStyle)
|
||||
draw(r, curStyle, true)
|
||||
|
||||
width := 0
|
||||
|
||||
@@ -443,15 +452,15 @@ func (w *BufWindow) displayBuffer() {
|
||||
char = '@'
|
||||
}
|
||||
|
||||
bloc.X++
|
||||
line = line[size:]
|
||||
|
||||
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
|
||||
if width > 1 {
|
||||
for i := 1; i < width; i++ {
|
||||
draw(char, curStyle)
|
||||
draw(char, curStyle, false)
|
||||
}
|
||||
}
|
||||
bloc.X++
|
||||
line = line[size:]
|
||||
|
||||
totalwidth += width
|
||||
|
||||
// If we reach the end of the window then we either stop or we wrap for softwrap
|
||||
@@ -470,12 +479,10 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if activeC.X == bloc.X && activeC.Y == bloc.Y {
|
||||
w.showCursor(vloc.X, vloc.Y, true)
|
||||
}
|
||||
|
||||
for _, c := range cursors {
|
||||
if b.Settings["cursorline"].(bool) &&
|
||||
!activeC.HasSelection() && activeC.Y == bloc.Y {
|
||||
!c.HasSelection() && c.Y == bloc.Y {
|
||||
style := config.DefStyle
|
||||
if s, ok := config.Colorscheme["cursor-line"]; ok {
|
||||
fg, _, _ := s.Decompose()
|
||||
@@ -485,6 +492,13 @@ func (w *BufWindow) displayBuffer() {
|
||||
screen.Screen.SetContent(i, vloc.Y, ' ', nil, style)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range cursors {
|
||||
if c.X == bloc.X && c.Y == bloc.Y {
|
||||
w.showCursor(w.X+vloc.X, w.Y+vloc.Y, true)
|
||||
}
|
||||
}
|
||||
|
||||
bloc.X = w.StartCol
|
||||
bloc.Y++
|
||||
|
||||
Reference in New Issue
Block a user