More actions and window organization

This commit is contained in:
Zachary Yedidia
2018-08-28 18:44:52 -04:00
parent 06d596e780
commit 7d87e6db99
11 changed files with 539 additions and 177 deletions

View File

@@ -9,450 +9,553 @@ import (
// MousePress is the event that should happen when a normal click happens
// This is almost always bound to left click
func (a *BufHandler) MousePress(e *tcell.EventMouse) bool {
func (h *BufHandler) MousePress(e *tcell.EventMouse) bool {
return false
}
// ScrollUpAction scrolls the view up
func (a *BufHandler) ScrollUpAction() bool {
func (h *BufHandler) ScrollUpAction() bool {
return false
}
// ScrollDownAction scrolls the view up
func (a *BufHandler) ScrollDownAction() bool {
func (h *BufHandler) ScrollDownAction() bool {
return false
}
// Center centers the view on the cursor
func (a *BufHandler) Center() bool {
func (h *BufHandler) Center() bool {
return true
}
// CursorUp moves the cursor up
func (a *BufHandler) CursorUp() bool {
a.Cursor.Deselect(true)
a.Cursor.Up()
func (h *BufHandler) CursorUp() bool {
h.Cursor.Deselect(true)
h.Cursor.Up()
return true
}
// CursorDown moves the cursor down
func (a *BufHandler) CursorDown() bool {
a.Cursor.Deselect(true)
a.Cursor.Down()
func (h *BufHandler) CursorDown() bool {
h.Cursor.Deselect(true)
h.Cursor.Down()
return true
}
// CursorLeft moves the cursor left
func (a *BufHandler) CursorLeft() bool {
a.Cursor.Deselect(true)
a.Cursor.Left()
func (h *BufHandler) CursorLeft() bool {
h.Cursor.Deselect(true)
h.Cursor.Left()
return true
}
// CursorRight moves the cursor right
func (a *BufHandler) CursorRight() bool {
a.Cursor.Deselect(true)
a.Cursor.Right()
func (h *BufHandler) CursorRight() bool {
h.Cursor.Deselect(true)
h.Cursor.Right()
return true
}
// WordRight moves the cursor one word to the right
func (a *BufHandler) WordRight() bool {
func (h *BufHandler) WordRight() bool {
h.Cursor.Deselect(true)
h.Cursor.WordRight()
return true
}
// WordLeft moves the cursor one word to the left
func (a *BufHandler) WordLeft() bool {
func (h *BufHandler) WordLeft() bool {
h.Cursor.Deselect(true)
h.Cursor.WordLeft()
return true
}
// SelectUp selects up one line
func (a *BufHandler) SelectUp() bool {
func (h *BufHandler) SelectUp() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.Up()
h.Cursor.SelectTo(h.Cursor.Loc)
return true
}
// SelectDown selects down one line
func (a *BufHandler) SelectDown() bool {
func (h *BufHandler) SelectDown() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.Down()
h.Cursor.SelectTo(h.Cursor.Loc)
return true
}
// SelectLeft selects the character to the left of the cursor
func (a *BufHandler) SelectLeft() bool {
func (h *BufHandler) SelectLeft() bool {
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)
return true
}
// SelectRight selects the character to the right of the cursor
func (a *BufHandler) SelectRight() bool {
func (h *BufHandler) SelectRight() bool {
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)
return true
}
// SelectWordRight selects the word to the right of the cursor
func (a *BufHandler) SelectWordRight() bool {
func (h *BufHandler) SelectWordRight() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.WordRight()
h.Cursor.SelectTo(h.Cursor.Loc)
return true
}
// SelectWordLeft selects the word to the left of the cursor
func (a *BufHandler) SelectWordLeft() bool {
func (h *BufHandler) SelectWordLeft() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.WordLeft()
h.Cursor.SelectTo(h.Cursor.Loc)
return true
}
// StartOfLine moves the cursor to the start of the line
func (a *BufHandler) StartOfLine() bool {
func (h *BufHandler) StartOfLine() bool {
h.Cursor.Deselect(true)
if h.Cursor.X != 0 {
h.Cursor.Start()
} else {
h.Cursor.StartOfText()
}
return true
}
// EndOfLine moves the cursor to the end of the line
func (a *BufHandler) EndOfLine() bool {
func (h *BufHandler) EndOfLine() bool {
h.Cursor.Deselect(true)
h.Cursor.End()
return true
}
// SelectLine selects the entire current line
func (a *BufHandler) SelectLine() bool {
func (h *BufHandler) SelectLine() bool {
h.Cursor.SelectLine()
return true
}
// SelectToStartOfLine selects to the start of the current line
func (a *BufHandler) SelectToStartOfLine() bool {
func (h *BufHandler) SelectToStartOfLine() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.Start()
h.Cursor.SelectTo(h.Cursor.Loc)
return true
}
// SelectToEndOfLine selects to the end of the current line
func (a *BufHandler) SelectToEndOfLine() bool {
func (h *BufHandler) SelectToEndOfLine() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.Cursor.End()
h.Cursor.SelectTo(h.Cursor.Loc)
return true
}
// ParagraphPrevious moves the cursor to the previous empty line, or beginning of the buffer if there's none
func (a *BufHandler) ParagraphPrevious() bool {
func (h *BufHandler) ParagraphPrevious() bool {
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()
}
return true
}
// ParagraphNext moves the cursor to the next empty line, or end of the buffer if there's none
func (a *BufHandler) ParagraphNext() bool {
func (h *BufHandler) ParagraphNext() bool {
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()
}
return true
}
// Retab changes all tabs to spaces or all spaces to tabs depending
// on the user's settings
func (a *BufHandler) Retab() bool {
func (h *BufHandler) Retab() bool {
return true
}
// CursorStart moves the cursor to the start of the buffer
func (a *BufHandler) CursorStart() bool {
func (h *BufHandler) CursorStart() bool {
h.Cursor.Deselect(true)
h.Cursor.X = 0
h.Cursor.Y = 0
return true
}
// CursorEnd moves the cursor to the end of the buffer
func (a *BufHandler) CursorEnd() bool {
func (h *BufHandler) CursorEnd() bool {
h.Cursor.Deselect(true)
h.Cursor.Loc = h.Buf.End()
h.Cursor.StoreVisualX()
return true
}
// SelectToStart selects the text from the cursor to the start of the buffer
func (a *BufHandler) SelectToStart() bool {
func (h *BufHandler) SelectToStart() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.CursorStart()
h.Cursor.SelectTo(h.Buf.Start())
return true
}
// SelectToEnd selects the text from the cursor to the end of the buffer
func (a *BufHandler) SelectToEnd() bool {
func (h *BufHandler) SelectToEnd() bool {
if !h.Cursor.HasSelection() {
h.Cursor.OrigSelection[0] = h.Cursor.Loc
}
h.CursorEnd()
h.Cursor.SelectTo(h.Buf.End())
return true
}
// InsertSpace inserts a space
func (a *BufHandler) InsertSpace() bool {
func (h *BufHandler) InsertSpace() bool {
return true
}
// InsertNewline inserts a newline plus possible some whitespace if autoindent is on
func (a *BufHandler) InsertNewline() bool {
func (h *BufHandler) InsertNewline() bool {
return true
}
// Backspace deletes the previous character
func (a *BufHandler) Backspace() bool {
func (h *BufHandler) Backspace() bool {
return true
}
// DeleteWordRight deletes the word to the right of the cursor
func (a *BufHandler) DeleteWordRight() bool {
func (h *BufHandler) DeleteWordRight() bool {
return true
}
// DeleteWordLeft deletes the word to the left of the cursor
func (a *BufHandler) DeleteWordLeft() bool {
func (h *BufHandler) DeleteWordLeft() bool {
return true
}
// Delete deletes the next character
func (a *BufHandler) Delete() bool {
func (h *BufHandler) Delete() bool {
return true
}
// IndentSelection indents the current selection
func (a *BufHandler) IndentSelection() bool {
func (h *BufHandler) IndentSelection() bool {
return false
}
// OutdentLine moves the current line back one indentation
func (a *BufHandler) OutdentLine() bool {
func (h *BufHandler) OutdentLine() bool {
return true
}
// OutdentSelection takes the current selection and moves it back one indent level
func (a *BufHandler) OutdentSelection() bool {
func (h *BufHandler) OutdentSelection() bool {
return false
}
// InsertTab inserts a tab or spaces
func (a *BufHandler) InsertTab() bool {
func (h *BufHandler) InsertTab() bool {
return true
}
// SaveAll saves all open buffers
func (a *BufHandler) SaveAll() bool {
func (h *BufHandler) SaveAll() bool {
return false
}
// Save the buffer to disk
func (a *BufHandler) Save() bool {
func (h *BufHandler) Save() bool {
return false
}
// SaveAs saves the buffer to disk with the given name
func (a *BufHandler) SaveAs() bool {
func (h *BufHandler) SaveAs() bool {
return false
}
// Find opens a prompt and searches forward for the input
func (a *BufHandler) Find() bool {
func (h *BufHandler) Find() bool {
return true
}
// FindNext searches forwards for the last used search term
func (a *BufHandler) FindNext() bool {
func (h *BufHandler) FindNext() bool {
return true
}
// FindPrevious searches backwards for the last used search term
func (a *BufHandler) FindPrevious() bool {
func (h *BufHandler) FindPrevious() bool {
return true
}
// Undo undoes the last action
func (a *BufHandler) Undo() bool {
func (h *BufHandler) Undo() bool {
return true
}
// Redo redoes the last action
func (a *BufHandler) Redo() bool {
func (h *BufHandler) Redo() bool {
return true
}
// Copy the selection to the system clipboard
func (a *BufHandler) Copy() bool {
func (h *BufHandler) Copy() bool {
return true
}
// CutLine cuts the current line to the clipboard
func (a *BufHandler) CutLine() bool {
func (h *BufHandler) CutLine() bool {
return true
}
// Cut the selection to the system clipboard
func (a *BufHandler) Cut() bool {
func (h *BufHandler) Cut() bool {
return true
}
// DuplicateLine duplicates the current line or selection
func (a *BufHandler) DuplicateLine() bool {
func (h *BufHandler) DuplicateLine() bool {
return true
}
// DeleteLine deletes the current line
func (a *BufHandler) DeleteLine() bool {
func (h *BufHandler) DeleteLine() bool {
return true
}
// MoveLinesUp moves up the current line or selected lines if any
func (a *BufHandler) MoveLinesUp() bool {
func (h *BufHandler) MoveLinesUp() bool {
return true
}
// MoveLinesDown moves down the current line or selected lines if any
func (a *BufHandler) MoveLinesDown() bool {
func (h *BufHandler) MoveLinesDown() bool {
return true
}
// Paste whatever is in the system clipboard into the buffer
// Delete and paste if the user has a selection
func (a *BufHandler) Paste() bool {
func (h *BufHandler) Paste() bool {
return true
}
// PastePrimary pastes from the primary clipboard (only use on linux)
func (a *BufHandler) PastePrimary() bool {
func (h *BufHandler) PastePrimary() bool {
return true
}
// JumpToMatchingBrace moves the cursor to the matching brace if it is
// currently on a brace
func (a *BufHandler) JumpToMatchingBrace() bool {
func (h *BufHandler) JumpToMatchingBrace() bool {
return true
}
// SelectAll selects the entire buffer
func (a *BufHandler) SelectAll() bool {
func (h *BufHandler) SelectAll() bool {
return true
}
// OpenFile opens a new file in the buffer
func (a *BufHandler) OpenFile() bool {
func (h *BufHandler) OpenFile() bool {
return false
}
// Start moves the viewport to the start of the buffer
func (a *BufHandler) Start() bool {
func (h *BufHandler) Start() bool {
return false
}
// End moves the viewport to the end of the buffer
func (a *BufHandler) End() bool {
func (h *BufHandler) End() bool {
return false
}
// PageUp scrolls the view up a page
func (a *BufHandler) PageUp() bool {
func (h *BufHandler) PageUp() bool {
return false
}
// PageDown scrolls the view down a page
func (a *BufHandler) PageDown() bool {
func (h *BufHandler) PageDown() bool {
return false
}
// SelectPageUp selects up one page
func (a *BufHandler) SelectPageUp() bool {
func (h *BufHandler) SelectPageUp() bool {
return true
}
// SelectPageDown selects down one page
func (a *BufHandler) SelectPageDown() bool {
func (h *BufHandler) SelectPageDown() bool {
return true
}
// CursorPageUp places the cursor a page up
func (a *BufHandler) CursorPageUp() bool {
func (h *BufHandler) CursorPageUp() bool {
return true
}
// CursorPageDown places the cursor a page up
func (a *BufHandler) CursorPageDown() bool {
func (h *BufHandler) CursorPageDown() bool {
return true
}
// HalfPageUp scrolls the view up half a page
func (a *BufHandler) HalfPageUp() bool {
func (h *BufHandler) HalfPageUp() bool {
return false
}
// HalfPageDown scrolls the view down half a page
func (a *BufHandler) HalfPageDown() bool {
func (h *BufHandler) HalfPageDown() bool {
return false
}
// ToggleRuler turns line numbers off and on
func (a *BufHandler) ToggleRuler() bool {
func (h *BufHandler) ToggleRuler() bool {
return false
}
// JumpLine jumps to a line and moves the view accordingly.
func (a *BufHandler) JumpLine() bool {
func (h *BufHandler) JumpLine() bool {
return false
}
// ClearStatus clears the messenger bar
func (a *BufHandler) ClearStatus() bool {
func (h *BufHandler) ClearStatus() bool {
return false
}
// ToggleHelp toggles the help screen
func (a *BufHandler) ToggleHelp() bool {
func (h *BufHandler) ToggleHelp() bool {
return true
}
// ToggleKeyMenu toggles the keymenu option and resizes all tabs
func (a *BufHandler) ToggleKeyMenu() bool {
func (h *BufHandler) ToggleKeyMenu() bool {
return true
}
// ShellMode opens a terminal to run a shell command
func (a *BufHandler) ShellMode() bool {
func (h *BufHandler) ShellMode() bool {
return false
}
// CommandMode lets the user enter a command
func (a *BufHandler) CommandMode() bool {
func (h *BufHandler) CommandMode() bool {
return false
}
// ToggleOverwriteMode lets the user toggle the text overwrite mode
func (a *BufHandler) ToggleOverwriteMode() bool {
func (h *BufHandler) ToggleOverwriteMode() bool {
return false
}
// Escape leaves current mode
func (a *BufHandler) Escape() bool {
func (h *BufHandler) Escape() bool {
return false
}
// Quit this will close the current tab or view that is open
func (a *BufHandler) Quit() bool {
func (h *BufHandler) Quit() bool {
screen.Screen.Fini()
os.Exit(0)
return false
}
// QuitAll quits the whole editor; all splits and tabs
func (a *BufHandler) QuitAll() bool {
func (h *BufHandler) QuitAll() bool {
return false
}
// AddTab adds a new tab with an empty buffer
func (a *BufHandler) AddTab() bool {
func (h *BufHandler) AddTab() bool {
return true
}
// PreviousTab switches to the previous tab in the tab list
func (a *BufHandler) PreviousTab() bool {
func (h *BufHandler) PreviousTab() bool {
return false
}
// NextTab switches to the next tab in the tab list
func (a *BufHandler) NextTab() bool {
func (h *BufHandler) NextTab() bool {
return false
}
// VSplitBinding opens an empty vertical split
func (a *BufHandler) VSplitBinding() bool {
func (h *BufHandler) VSplitBinding() bool {
return false
}
// HSplitBinding opens an empty horizontal split
func (a *BufHandler) HSplitBinding() bool {
func (h *BufHandler) HSplitBinding() bool {
return false
}
// Unsplit closes all splits in the current tab except the active one
func (a *BufHandler) Unsplit() bool {
func (h *BufHandler) Unsplit() bool {
return false
}
// NextSplit changes the view to the next split
func (a *BufHandler) NextSplit() bool {
func (h *BufHandler) NextSplit() bool {
return false
}
// PreviousSplit changes the view to the previous split
func (a *BufHandler) PreviousSplit() bool {
func (h *BufHandler) PreviousSplit() bool {
return false
}
@@ -460,41 +563,41 @@ var curMacro []interface{}
var recordingMacro bool
// ToggleMacro toggles recording of a macro
func (a *BufHandler) ToggleMacro() bool {
func (h *BufHandler) ToggleMacro() bool {
return true
}
// PlayMacro plays back the most recently recorded macro
func (a *BufHandler) PlayMacro() bool {
func (h *BufHandler) PlayMacro() bool {
return true
}
// SpawnMultiCursor creates a new multiple cursor at the next occurrence of the current selection or current word
func (a *BufHandler) SpawnMultiCursor() bool {
func (h *BufHandler) SpawnMultiCursor() bool {
return false
}
// SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection
func (a *BufHandler) SpawnMultiCursorSelect() bool {
func (h *BufHandler) SpawnMultiCursorSelect() bool {
return false
}
// MouseMultiCursor is a mouse action which puts a new cursor at the mouse position
func (a *BufHandler) MouseMultiCursor(e *tcell.EventMouse) bool {
func (h *BufHandler) MouseMultiCursor(e *tcell.EventMouse) bool {
return false
}
// SkipMultiCursor moves the current multiple cursor to the next available position
func (a *BufHandler) SkipMultiCursor() bool {
func (h *BufHandler) SkipMultiCursor() bool {
return false
}
// RemoveMultiCursor removes the latest multiple cursor
func (a *BufHandler) RemoveMultiCursor() bool {
func (h *BufHandler) RemoveMultiCursor() bool {
return false
}
// RemoveAllMultiCursors removes all cursors except the base cursor
func (a *BufHandler) RemoveAllMultiCursors() bool {
func (h *BufHandler) RemoveAllMultiCursors() bool {
return false
}

View File

@@ -36,6 +36,9 @@ type BufHandler struct {
cursors []*buffer.Cursor
Cursor *buffer.Cursor // the active cursor
StartLine int // Vertical scrolling
StartCol int // Horizontal scrolling
// Since tcell doesn't differentiate between a mouse release event
// and a mouse move event with no keys pressed, we need to keep
// track of whether or not the mouse was pressed (or not released) last event to determine
@@ -65,22 +68,22 @@ type BufHandler struct {
}
func NewBufHandler(buf *buffer.Buffer) *BufHandler {
a := new(BufHandler)
a.Buf = buf
h := new(BufHandler)
h.Buf = buf
a.cursors = []*buffer.Cursor{&buffer.Cursor{
h.cursors = []*buffer.Cursor{&buffer.Cursor{
Buf: buf,
Loc: buf.StartCursor,
}}
a.Cursor = a.cursors[0]
h.Cursor = h.cursors[0]
buf.SetCursors(a.cursors)
return a
buf.SetCursors(h.cursors)
return h
}
// HandleEvent executes the tcell event properly
// TODO: multiple actions bound to one key
func (a *BufHandler) HandleEvent(event tcell.Event) {
func (h *BufHandler) HandleEvent(event tcell.Event) {
switch e := event.(type) {
case *tcell.EventKey:
ke := KeyEvent{
@@ -88,20 +91,32 @@ func (a *BufHandler) HandleEvent(event tcell.Event) {
mod: e.Modifiers(),
r: e.Rune(),
}
if action, ok := BufKeyBindings[ke]; ok {
action(a)
}
h.DoKeyEvent(ke)
case *tcell.EventMouse:
me := MouseEvent{
btn: e.Buttons(),
mod: e.Modifiers(),
}
if action, ok := BufMouseBindings[me]; ok {
action(a, e)
}
h.DoMouseEvent(me, e)
}
}
func (h *BufHandler) DoKeyEvent(e KeyEvent) bool {
if action, ok := BufKeyBindings[e]; ok {
action(h)
return true
}
return false
}
func (h *BufHandler) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
if action, ok := BufMouseBindings[e]; ok {
action(h, te)
return true
}
return false
}
var BufKeyActions = map[string]BufKeyAction{
"CursorUp": (*BufHandler).CursorUp,
"CursorDown": (*BufHandler).CursorDown,

View File

@@ -31,8 +31,13 @@ type MouseEvent struct {
mod tcell.ModMask
}
type KeyAction func(Handler) bool
type MouseAction func(Handler, tcell.EventMouse) bool
// A Handler will take a tcell event and execute it
// appropriately
type Handler interface {
// DoKeyEvent(KeyEvent) bool
// DoMouseEvent(MouseEvent, *tcell.EventMouse) (MouseAction, bool)
HandleEvent(tcell.Event)
}

View File

@@ -107,6 +107,18 @@ func (c *Cursor) Start() {
c.LastVisualX = c.GetVisualX()
}
// StartOfText moves the cursor to the first non-whitespace rune of
// the line it is on
func (c *Cursor) StartOfText() {
c.Start()
for util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == utf8.RuneCount(c.Buf.LineBytes(c.Y)) {
break
}
c.Right()
}
}
// End moves the cursor to the end of the line it is on
func (c *Cursor) End() {
c.X = utf8.RuneCount(c.Buf.LineBytes(c.Y))
@@ -296,6 +308,143 @@ func (c *Cursor) Relocate() {
}
}
// SelectWord selects the word the cursor is currently on
func (c *Cursor) SelectWord() {
if len(c.Buf.LineBytes(c.Y)) == 0 {
return
}
if !util.IsWordChar(c.RuneUnder(c.X)) {
c.SetSelectionStart(c.Loc)
c.SetSelectionEnd(c.Loc.Move(1, c.Buf))
c.OrigSelection = c.CurSelection
return
}
forward, backward := c.X, c.X
for backward > 0 && util.IsWordChar(c.RuneUnder(backward-1)) {
backward--
}
c.SetSelectionStart(Loc{backward, c.Y})
c.OrigSelection[0] = c.CurSelection[0]
lineLen := utf8.RuneCount(c.Buf.LineBytes(c.Y)) - 1
for forward < lineLen && util.IsWordChar(c.RuneUnder(forward+1)) {
forward++
}
c.SetSelectionEnd(Loc{forward, c.Y}.Move(1, c.Buf))
c.OrigSelection[1] = c.CurSelection[1]
c.Loc = c.CurSelection[1]
}
// AddWordToSelection adds the word the cursor is currently on
// to the selection
func (c *Cursor) AddWordToSelection() {
if c.Loc.GreaterThan(c.OrigSelection[0]) && c.Loc.LessThan(c.OrigSelection[1]) {
c.CurSelection = c.OrigSelection
return
}
if c.Loc.LessThan(c.OrigSelection[0]) {
backward := c.X
for backward > 0 && util.IsWordChar(c.RuneUnder(backward-1)) {
backward--
}
c.SetSelectionStart(Loc{backward, c.Y})
c.SetSelectionEnd(c.OrigSelection[1])
}
if c.Loc.GreaterThan(c.OrigSelection[1]) {
forward := c.X
lineLen := utf8.RuneCount(c.Buf.LineBytes(c.Y)) - 1
for forward < lineLen && util.IsWordChar(c.RuneUnder(forward+1)) {
forward++
}
c.SetSelectionEnd(Loc{forward, c.Y}.Move(1, c.Buf))
c.SetSelectionStart(c.OrigSelection[0])
}
c.Loc = c.CurSelection[1]
}
// SelectTo selects from the current cursor location to the given
// location
func (c *Cursor) SelectTo(loc Loc) {
if loc.GreaterThan(c.OrigSelection[0]) {
c.SetSelectionStart(c.OrigSelection[0])
c.SetSelectionEnd(loc)
} else {
c.SetSelectionStart(loc)
c.SetSelectionEnd(c.OrigSelection[0])
}
}
// WordRight moves the cursor one word to the right
func (c *Cursor) WordRight() {
for util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == utf8.RuneCount(c.Buf.LineBytes(c.Y)) {
c.Right()
return
}
c.Right()
}
c.Right()
for util.IsWordChar(c.RuneUnder(c.X)) {
if c.X == utf8.RuneCount(c.Buf.LineBytes(c.Y)) {
return
}
c.Right()
}
}
// WordLeft moves the cursor one word to the left
func (c *Cursor) WordLeft() {
c.Left()
for util.IsWhitespace(c.RuneUnder(c.X)) {
if c.X == 0 {
return
}
c.Left()
}
c.Left()
for util.IsWordChar(c.RuneUnder(c.X)) {
if c.X == 0 {
return
}
c.Left()
}
c.Right()
}
// RuneUnder returns the rune under the given x position
func (c *Cursor) RuneUnder(x int) rune {
line := c.Buf.LineBytes(c.Y)
if len(line) == 0 || x >= utf8.RuneCount(line) {
return '\n'
} else if x < 0 {
x = 0
}
i := 0
for len(line) > 0 {
r, size := utf8.DecodeRune(line)
line = line[size:]
if i == x {
return r
}
i++
}
return '\n'
}
func (c *Cursor) StoreVisualX() {
c.LastVisualX = c.GetVisualX()
}

View File

@@ -1,4 +1,4 @@
package main
package display
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package main
package display
import (
"bytes"
@@ -23,13 +23,13 @@ type StatusLine struct {
FormatRight string
Info map[string]func(*buffer.Buffer) string
win *Window
win *BufWindow
}
// TODO: plugin modify status line formatter
// NewStatusLine returns a statusline bound to a window
func NewStatusLine(win *Window) *StatusLine {
func NewStatusLine(win *BufWindow) *StatusLine {
s := new(StatusLine)
s.FormatLeft = "$(filename) $(modified)($(line),$(col)) $(opt:filetype) $(opt:fileformat)"
// s.FormatLeft = "$(filename) $(modified)(line,col) $(opt:filetype) $(opt:fileformat)"

View File

@@ -1,4 +1,4 @@
package main
package display
import (
"strconv"
@@ -12,7 +12,12 @@ import (
"github.com/zyedidia/tcell"
)
type Window struct {
type Window interface {
Display()
Clear()
}
type BufWindow struct {
// X and Y coordinates for the top left of the window
X int
Y int
@@ -32,8 +37,8 @@ type Window struct {
sline *StatusLine
}
func NewWindow(x, y, width, height int, buf *buffer.Buffer) *Window {
w := new(Window)
func NewBufWindow(x, y, width, height int, buf *buffer.Buffer) *BufWindow {
w := new(BufWindow)
w.X, w.Y, w.Width, w.Height, w.Buf = x, y, width, height, buf
w.sline = NewStatusLine(w)
@@ -41,7 +46,7 @@ func NewWindow(x, y, width, height int, buf *buffer.Buffer) *Window {
return w
}
func (w *Window) Clear() {
func (w *BufWindow) Clear() {
for y := 0; y < w.Height; y++ {
for x := 0; x < w.Width; x++ {
screen.Screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle)
@@ -49,7 +54,7 @@ func (w *Window) Clear() {
}
}
func (w *Window) DrawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxLineNumLength int, vloc *buffer.Loc, bloc *buffer.Loc) {
func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxLineNumLength int, vloc *buffer.Loc, bloc *buffer.Loc) {
lineNum := strconv.Itoa(bloc.Y + 1)
// Write the spaces before the line number if necessary
@@ -72,9 +77,9 @@ func (w *Window) DrawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxLine
vloc.X++
}
// GetStyle returns the highlight style for the given character position
// getStyle returns the highlight style for the given character position
// If there is no change to the current highlight style it just returns that
func (w *Window) GetStyle(style tcell.Style, bloc buffer.Loc, r rune) tcell.Style {
func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc, r rune) tcell.Style {
if group, ok := w.Buf.Match(bloc.Y)[bloc.X]; ok {
s := config.GetColor(group.String())
return s
@@ -82,7 +87,7 @@ func (w *Window) GetStyle(style tcell.Style, bloc buffer.Loc, r rune) tcell.Styl
return style
}
func (w *Window) ShowCursor(x, y int, main bool) {
func (w *BufWindow) showCursor(x, y int, main bool) {
if main {
screen.Screen.ShowCursor(x, y)
} else {
@@ -91,8 +96,8 @@ func (w *Window) ShowCursor(x, y int, main bool) {
}
}
// DisplayBuffer draws the buffer being shown in this window on the screen.Screen
func (w *Window) DisplayBuffer() {
// displayBuffer draws the buffer being shown in this window on the screen.Screen
func (w *BufWindow) displayBuffer() {
b := w.Buf
bufHeight := w.Height
@@ -132,30 +137,48 @@ func (w *Window) DisplayBuffer() {
// this represents the current draw position in the buffer (char positions)
bloc := buffer.Loc{w.StartCol, w.StartLine}
activeC := w.Buf.GetActiveCursor()
curStyle := config.DefStyle
for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ {
vloc.X = 0
if b.Settings["ruler"].(bool) {
w.DrawLineNum(lineNumStyle, false, maxLineNumLength, &vloc, &bloc)
w.drawLineNum(lineNumStyle, false, maxLineNumLength, &vloc, &bloc)
}
line := b.LineBytes(bloc.Y)
line, nColsBeforeStart := util.SliceVisualEnd(line, bloc.X, tabsize)
totalwidth := bloc.X - nColsBeforeStart
for len(line) > 0 {
if w.Buf.GetActiveCursor().X == bloc.X && w.Buf.GetActiveCursor().Y == bloc.Y {
w.ShowCursor(vloc.X, vloc.Y, true)
}
r, size := utf8.DecodeRune(line)
curStyle = w.GetStyle(curStyle, bloc, r)
draw := func(r rune, style tcell.Style) {
if nColsBeforeStart <= 0 {
screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, curStyle)
if activeC.HasSelection() &&
(bloc.GreaterEqual(activeC.CurSelection[0]) && bloc.LessThan(activeC.CurSelection[1]) ||
bloc.LessThan(activeC.CurSelection[0]) && bloc.GreaterEqual(activeC.CurSelection[1])) {
// The current character is selected
style = config.DefStyle.Reverse(true)
if s, ok := config.Colorscheme["selection"]; ok {
style = s
}
}
screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style)
vloc.X++
}
nColsBeforeStart--
}
totalwidth := bloc.X - 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)
width := 0
@@ -175,11 +198,7 @@ func (w *Window) DisplayBuffer() {
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
if width > 1 {
for i := 1; i < width; i++ {
if nColsBeforeStart <= 0 {
screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, curStyle)
vloc.X++
}
nColsBeforeStart--
draw(char, curStyle)
}
}
totalwidth += width
@@ -195,12 +214,12 @@ func (w *Window) DisplayBuffer() {
}
vloc.X = 0
// This will draw an empty line number because the current line is wrapped
w.DrawLineNum(lineNumStyle, true, maxLineNumLength, &vloc, &bloc)
w.drawLineNum(lineNumStyle, true, maxLineNumLength, &vloc, &bloc)
}
}
}
if w.Buf.GetActiveCursor().X == bloc.X && w.Buf.GetActiveCursor().Y == bloc.Y {
w.ShowCursor(vloc.X, vloc.Y, true)
if activeC.X == bloc.X && activeC.Y == bloc.Y {
w.showCursor(vloc.X, vloc.Y, true)
}
bloc.X = w.StartCol
bloc.Y++
@@ -210,6 +229,11 @@ func (w *Window) DisplayBuffer() {
}
}
func (w *Window) DisplayStatusLine() {
func (w *BufWindow) displayStatusLine() {
w.sline.Display()
}
func (w *BufWindow) Display() {
w.displayBuffer()
w.displayStatusLine()
}

20
cmd/micro/editpane.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"github.com/zyedidia/micro/cmd/micro/action"
"github.com/zyedidia/micro/cmd/micro/buffer"
"github.com/zyedidia/micro/cmd/micro/display"
)
type EditPane struct {
display.Window
action.Handler
}
func NewBufEditPane(x, y, width, height int, b *buffer.Buffer) *EditPane {
e := new(EditPane)
e.Window = display.NewBufWindow(x, y, width, height, b)
e.Handler = action.NewBufHandler(b)
return e
}

View File

@@ -78,7 +78,7 @@ func Import(pkg string) *lua.LTable {
func importFmt() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "tErrorf", luar.New(L, fmt.Errorf))
L.SetField(pkg, "Errorf", luar.New(L, fmt.Errorf))
L.SetField(pkg, "Fprint", luar.New(L, fmt.Fprint))
L.SetField(pkg, "Fprintf", luar.New(L, fmt.Fprintf))
L.SetField(pkg, "Fprintln", luar.New(L, fmt.Fprintln))

View File

@@ -3,9 +3,12 @@ package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/go-errors/errors"
isatty "github.com/mattn/go-isatty"
"github.com/zyedidia/micro/cmd/micro/action"
"github.com/zyedidia/micro/cmd/micro/buffer"
"github.com/zyedidia/micro/cmd/micro/config"
@@ -20,8 +23,9 @@ const (
)
var (
// Version is the version number or commit hash
// These variables should be set by the linker when compiling
// Version is the version number or commit hash
Version = "0.0.0-unknown"
// CommitHash is the commit this version was built on
CommitHash = "Unknown"
@@ -34,9 +38,6 @@ var (
events chan tcell.Event
autosave chan bool
// How many redraws have happened
numRedraw uint
// Command line flags
flagVersion = flag.Bool("version", false, "Show the version number and information")
flagStartPos = flag.String("startpos", "", "LINE,COL to start the cursor at when opening a buffer.")
@@ -91,6 +92,66 @@ func InitFlags() {
}
}
// LoadInput determines which files should be loaded into buffers
// based on the input stored in flag.Args()
func LoadInput() []*buffer.Buffer {
// There are a number of ways micro should start given its input
// 1. If it is given a files in flag.Args(), it should open those
// 2. If there is no input file and the input is not a terminal, that means
// something is being piped in and the stdin should be opened in an
// empty buffer
// 3. If there is no input file and the input is a terminal, an empty buffer
// should be opened
var filename string
var input []byte
var err error
args := flag.Args()
buffers := make([]*buffer.Buffer, 0, len(args))
if len(args) > 0 {
// Option 1
// We go through each file and load it
for i := 0; i < len(args); i++ {
if strings.HasPrefix(args[i], "+") {
if strings.Contains(args[i], ":") {
split := strings.Split(args[i], ":")
*flagStartPos = split[0][1:] + "," + split[1]
} else {
*flagStartPos = args[i][1:] + ",0"
}
continue
}
buf, err := buffer.NewBufferFromFile(args[i])
if err != nil {
util.TermMessage(err)
continue
}
// If the file didn't exist, input will be empty, and we'll open an empty buffer
buffers = append(buffers, buf)
}
} else if !isatty.IsTerminal(os.Stdin.Fd()) {
// Option 2
// The input is not a terminal, so something is being piped in
// and we should read from stdin
input, err = ioutil.ReadAll(os.Stdin)
if err != nil {
util.TermMessage("Error reading from stdin: ", err)
input = []byte{}
}
buffers = append(buffers, buffer.NewBufferFromString(string(input), filename))
} else {
// Option 3, just open an empty buffer
buffers = append(buffers, buffer.NewBufferFromString(string(input), filename))
}
return buffers
}
func main() {
var err error
@@ -127,18 +188,9 @@ func main() {
}
}()
action.TryBindKey("Ctrl-z", "Undo", true)
b, err := buffer.NewBufferFromFile(os.Args[1])
if err != nil {
util.TermMessage(err)
}
b := LoadInput()[0]
width, height := screen.Screen.Size()
w := NewWindow(0, 0, width, height-1, b)
a := action.NewBufHandler(b)
ep := NewBufEditPane(0, 0, width, height-1, b)
// Here is the event loop which runs in a separate thread
go func() {
@@ -153,8 +205,7 @@ func main() {
for {
// Display everything
screen.Screen.Fill(' ', config.DefStyle)
w.DisplayBuffer()
w.DisplayStatusLine()
ep.Display()
screen.Screen.Show()
var event tcell.Event
@@ -165,7 +216,7 @@ func main() {
}
if event != nil {
a.HandleEvent(event)
ep.HandleEvent(event)
}
}

View File

@@ -135,13 +135,8 @@ func FSize(f *os.File) int64 {
// IsWordChar returns whether or not the string is a 'word character'
// If it is a unicode character, then it does not match
// Word characters are defined as [A-Za-z0-9_]
func IsWordChar(str string) bool {
if len(str) > 1 {
// Unicode
return true
}
c := str[0]
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_')
func IsWordChar(r rune) bool {
return (r >= '0' && r <= '9') || (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r == '_')
}
// IsWhitespace returns true if the given rune is a space, tab, or newline