mirror of
https://github.com/zyedidia/micro.git
synced 2026-02-07 23:50:18 +09:00
Improve the search functionality and improve selection
This commit is contained in:
@@ -76,22 +76,22 @@ func (c *Cursor) Loc() int {
|
||||
|
||||
// ResetSelection resets the user's selection
|
||||
func (c *Cursor) ResetSelection() {
|
||||
c.curSelection[0] = -1
|
||||
c.curSelection[1] = -1
|
||||
c.curSelection[0] = 0
|
||||
c.curSelection[1] = 0
|
||||
}
|
||||
|
||||
// HasSelection returns whether or not the user has selected anything
|
||||
func (c *Cursor) HasSelection() bool {
|
||||
return c.curSelection[1] != -1 || c.curSelection[0] != -1
|
||||
return c.curSelection[0] != c.curSelection[1]
|
||||
}
|
||||
|
||||
// DeleteSelection deletes the currently selected text
|
||||
func (c *Cursor) DeleteSelection() {
|
||||
if c.curSelection[0] > c.curSelection[1] {
|
||||
c.v.eh.Remove(c.curSelection[1], c.curSelection[0]+1)
|
||||
c.v.eh.Remove(c.curSelection[1], c.curSelection[0])
|
||||
c.SetLoc(c.curSelection[1])
|
||||
} else {
|
||||
c.v.eh.Remove(c.curSelection[0], c.curSelection[1]+1)
|
||||
c.v.eh.Remove(c.curSelection[0], c.curSelection[1])
|
||||
c.SetLoc(c.curSelection[0])
|
||||
}
|
||||
}
|
||||
@@ -99,9 +99,9 @@ func (c *Cursor) DeleteSelection() {
|
||||
// GetSelection returns the cursor's selection
|
||||
func (c *Cursor) GetSelection() string {
|
||||
if c.curSelection[0] > c.curSelection[1] {
|
||||
return string([]rune(c.v.buf.text)[c.curSelection[1] : c.curSelection[0]+1])
|
||||
return string([]rune(c.v.buf.text)[c.curSelection[1]:c.curSelection[0]])
|
||||
}
|
||||
return string([]rune(c.v.buf.text)[c.curSelection[0] : c.curSelection[1]+1])
|
||||
return string([]rune(c.v.buf.text)[c.curSelection[0]:c.curSelection[1]])
|
||||
}
|
||||
|
||||
// SelectLine selects the current line
|
||||
@@ -143,7 +143,7 @@ func (c *Cursor) SelectWord() {
|
||||
if !IsWordChar(string(c.RuneUnder(c.x))) {
|
||||
loc := c.Loc()
|
||||
c.curSelection[0] = loc
|
||||
c.curSelection[1] = loc
|
||||
c.curSelection[1] = loc + 1
|
||||
c.origSelection = c.curSelection
|
||||
return
|
||||
}
|
||||
@@ -161,7 +161,7 @@ func (c *Cursor) SelectWord() {
|
||||
forward++
|
||||
}
|
||||
|
||||
c.curSelection[1] = ToCharPos(forward, c.y, c.v.buf)
|
||||
c.curSelection[1] = ToCharPos(forward, c.y, c.v.buf) + 1
|
||||
c.origSelection[1] = c.curSelection[1]
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ func (c *Cursor) AddWordToSelection() {
|
||||
forward++
|
||||
}
|
||||
|
||||
c.curSelection[1] = ToCharPos(forward, c.y, c.v.buf)
|
||||
c.curSelection[1] = ToCharPos(forward, c.y, c.v.buf) + 1
|
||||
c.curSelection[0] = c.origSelection[0]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,6 @@ type Messenger struct {
|
||||
// style to use when drawing the message
|
||||
style tcell.Style
|
||||
|
||||
realtimePrompt bool
|
||||
|
||||
// We have to keep track of the cursor for prompting
|
||||
cursorx int
|
||||
}
|
||||
@@ -112,16 +110,11 @@ func (m *Messenger) Prompt(prompt string) (string, bool) {
|
||||
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
if e.Key() == tcell.KeyEscape {
|
||||
switch e.Key() {
|
||||
case tcell.KeyCtrlQ, tcell.KeyCtrlC, tcell.KeyEscape:
|
||||
// Cancel
|
||||
m.hasPrompt = false
|
||||
} else if e.Key() == tcell.KeyCtrlC {
|
||||
// Cancel
|
||||
m.hasPrompt = false
|
||||
} else if e.Key() == tcell.KeyCtrlQ {
|
||||
// Cancel
|
||||
m.hasPrompt = false
|
||||
} else if e.Key() == tcell.KeyEnter {
|
||||
case tcell.KeyEnter:
|
||||
// User is done entering their response
|
||||
m.hasPrompt = false
|
||||
response, canceled = m.response, false
|
||||
|
||||
63
src/micro.go
63
src/micro.go
@@ -16,13 +16,16 @@ const (
|
||||
undoThreshold = 500 // If two events are less than n milliseconds apart, undo both of them
|
||||
)
|
||||
|
||||
// The main screen
|
||||
var screen tcell.Screen
|
||||
var (
|
||||
// The main screen
|
||||
screen tcell.Screen
|
||||
|
||||
// Object to send messages and prompts to the user
|
||||
var messenger *Messenger
|
||||
// Object to send messages and prompts to the user
|
||||
messenger *Messenger
|
||||
|
||||
var redrawStatus int
|
||||
redrawStatus int
|
||||
searching bool
|
||||
)
|
||||
|
||||
// LoadInput loads the file input for the editor
|
||||
func LoadInput() (string, []byte, error) {
|
||||
@@ -138,48 +141,8 @@ func main() {
|
||||
// Wait for the user's action
|
||||
event := screen.PollEvent()
|
||||
|
||||
if messenger.realtimePrompt {
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
if e.Key() == tcell.KeyEscape {
|
||||
// Cancel
|
||||
messenger.hasPrompt = false
|
||||
messenger.realtimePrompt = false
|
||||
messenger.Clear()
|
||||
messenger.Reset()
|
||||
continue
|
||||
} else if e.Key() == tcell.KeyCtrlC {
|
||||
// Cancel
|
||||
messenger.hasPrompt = false
|
||||
messenger.realtimePrompt = false
|
||||
messenger.Clear()
|
||||
messenger.Reset()
|
||||
continue
|
||||
} else if e.Key() == tcell.KeyCtrlQ {
|
||||
// Cancel
|
||||
messenger.hasPrompt = false
|
||||
messenger.realtimePrompt = false
|
||||
messenger.Clear()
|
||||
messenger.Reset()
|
||||
continue
|
||||
} else if e.Key() == tcell.KeyEnter {
|
||||
// User is done entering their response
|
||||
messenger.hasPrompt = false
|
||||
messenger.realtimePrompt = false
|
||||
messenger.Clear()
|
||||
messenger.Reset()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if messenger.cursorx < 0 {
|
||||
// Cancel
|
||||
messenger.realtimePrompt = false
|
||||
messenger.hasPrompt = false
|
||||
messenger.Clear()
|
||||
messenger.Reset()
|
||||
continue
|
||||
}
|
||||
messenger.HandleEvent(event)
|
||||
if searching {
|
||||
HandleSearchEvent(event, view)
|
||||
} else {
|
||||
// Check if we should quit
|
||||
switch e := event.(type) {
|
||||
@@ -202,9 +165,9 @@ func main() {
|
||||
view.Resize(screen.Size())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send it to the view
|
||||
view.HandleEvent(event)
|
||||
// Send it to the view
|
||||
view.HandleEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
70
src/search.go
Normal file
70
src/search.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gdamore/tcell"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func BeginSearch() {
|
||||
searching = true
|
||||
messenger.hasPrompt = true
|
||||
messenger.Message("Find: ")
|
||||
}
|
||||
|
||||
func EndSearch() {
|
||||
searching = false
|
||||
messenger.hasPrompt = false
|
||||
messenger.Clear()
|
||||
messenger.Reset()
|
||||
}
|
||||
|
||||
func HandleSearchEvent(event tcell.Event, v *View) {
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
switch e.Key() {
|
||||
case tcell.KeyCtrlQ, tcell.KeyCtrlC, tcell.KeyEscape, tcell.KeyEnter:
|
||||
// Done
|
||||
EndSearch()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
messenger.HandleEvent(event)
|
||||
|
||||
if messenger.cursorx < 0 {
|
||||
// Done
|
||||
EndSearch()
|
||||
return
|
||||
}
|
||||
|
||||
if messenger.response == "" {
|
||||
v.cursor.ResetSelection()
|
||||
// We don't end the search though
|
||||
return
|
||||
}
|
||||
|
||||
str := strings.Join(v.buf.lines[v.cursor.y:], "\n")
|
||||
charPos := ToCharPos(0, v.cursor.y, v.buf)
|
||||
r, err := regexp.Compile(messenger.response)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
match := r.FindStringIndex(str)
|
||||
if match == nil {
|
||||
str = strings.Join(v.buf.lines[:v.cursor.y], "\n")
|
||||
match = r.FindStringIndex(str)
|
||||
charPos = 0
|
||||
if match == nil {
|
||||
v.cursor.ResetSelection()
|
||||
return
|
||||
}
|
||||
}
|
||||
v.cursor.curSelection[0] = charPos + match[0]
|
||||
v.cursor.curSelection[1] = charPos + match[1]
|
||||
v.cursor.x, v.cursor.y = FromCharPos(charPos+match[1]-1, v.buf)
|
||||
if v.Relocate() {
|
||||
v.matches = Match(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
43
src/view.go
43
src/view.go
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/atotto/clipboard"
|
||||
"github.com/gdamore/tcell"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -89,6 +88,7 @@ func NewViewWidthHeight(buf *Buffer, w, h int) *View {
|
||||
y: 0,
|
||||
v: v,
|
||||
}
|
||||
v.cursor.ResetSelection()
|
||||
|
||||
v.eh = NewEventHandler(v)
|
||||
|
||||
@@ -293,22 +293,28 @@ func (v *View) OpenFile() {
|
||||
|
||||
// Relocate moves the view window so that the cursor is in view
|
||||
// This is useful if the user has scrolled far away, and then starts typing
|
||||
func (v *View) Relocate() {
|
||||
func (v *View) Relocate() bool {
|
||||
ret := false
|
||||
cy := v.cursor.y
|
||||
if cy < v.topline {
|
||||
v.topline = cy
|
||||
ret = true
|
||||
}
|
||||
if cy > v.topline+v.height-1 {
|
||||
v.topline = cy - v.height + 1
|
||||
ret = true
|
||||
}
|
||||
|
||||
cx := v.cursor.GetVisualX()
|
||||
if cx < v.leftCol {
|
||||
v.leftCol = cx
|
||||
ret = true
|
||||
}
|
||||
if cx+v.lineNumOffset+1 > v.leftCol+v.width {
|
||||
v.leftCol = cx - v.width + v.lineNumOffset + 1
|
||||
ret = true
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// MoveToMouseClick moves the cursor to location x, y assuming x, y were given
|
||||
@@ -340,25 +346,6 @@ func (v *View) HandleEvent(event tcell.Event) {
|
||||
// By default it's true because most events should cause a relocate
|
||||
relocate := true
|
||||
|
||||
if messenger.realtimePrompt {
|
||||
str := strings.Join(v.buf.lines[v.cursor.y:], "\n")
|
||||
charPos := ToCharPos(0, v.cursor.y, v.buf)
|
||||
r, err := regexp.Compile(messenger.response)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
match := r.FindStringIndex(str)
|
||||
if match == nil {
|
||||
v.cursor.ResetSelection()
|
||||
return
|
||||
}
|
||||
v.cursor.curSelection[0] = charPos + match[0]
|
||||
v.cursor.curSelection[1] = charPos + match[1] - 1
|
||||
v.cursor.x, v.cursor.y = FromCharPos(charPos+match[1]-1, v.buf)
|
||||
v.Relocate()
|
||||
return
|
||||
}
|
||||
|
||||
// By default we don't update and syntax highlighting
|
||||
v.UpdateLines(-2, 0)
|
||||
switch e := event.(type) {
|
||||
@@ -424,9 +411,7 @@ func (v *View) HandleEvent(event tcell.Event) {
|
||||
case tcell.KeyCtrlS:
|
||||
v.Save()
|
||||
case tcell.KeyCtrlF:
|
||||
messenger.realtimePrompt = true
|
||||
messenger.hasPrompt = true
|
||||
messenger.Message("Find: ")
|
||||
BeginSearch()
|
||||
case tcell.KeyCtrlZ:
|
||||
v.eh.Undo()
|
||||
// Rehighlight the entire buffer
|
||||
@@ -529,7 +514,7 @@ func (v *View) HandleEvent(event tcell.Event) {
|
||||
} else if v.doubleClick {
|
||||
v.cursor.AddWordToSelection()
|
||||
} else {
|
||||
v.cursor.curSelection[1] = v.cursor.Loc() - 1
|
||||
v.cursor.curSelection[1] = v.cursor.Loc()
|
||||
}
|
||||
}
|
||||
v.mouseReleased = false
|
||||
@@ -656,8 +641,8 @@ func (v *View) DisplayView() {
|
||||
// }
|
||||
|
||||
if v.cursor.HasSelection() &&
|
||||
(charNum >= v.cursor.curSelection[0] && charNum <= v.cursor.curSelection[1] ||
|
||||
charNum <= v.cursor.curSelection[0] && charNum >= v.cursor.curSelection[1]) {
|
||||
(charNum >= v.cursor.curSelection[0] && charNum < v.cursor.curSelection[1] ||
|
||||
charNum < v.cursor.curSelection[0] && charNum >= v.cursor.curSelection[1]) {
|
||||
|
||||
lineStyle = tcell.StyleDefault.Reverse(true)
|
||||
|
||||
@@ -691,8 +676,8 @@ func (v *View) DisplayView() {
|
||||
// The newline may be selected, in which case we should draw the selection style
|
||||
// with a space to represent it
|
||||
if v.cursor.HasSelection() &&
|
||||
(charNum >= v.cursor.curSelection[0] && charNum <= v.cursor.curSelection[1] ||
|
||||
charNum <= v.cursor.curSelection[0] && charNum >= v.cursor.curSelection[1]) {
|
||||
(charNum >= v.cursor.curSelection[0] && charNum < v.cursor.curSelection[1] ||
|
||||
charNum < v.cursor.curSelection[0] && charNum >= v.cursor.curSelection[1]) {
|
||||
|
||||
selectStyle := tcell.StyleDefault.Reverse(true)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user