mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-24 17:50:15 +09:00
Tab bar and support for opening multiple files
This commit is contained in:
@@ -73,6 +73,8 @@ var bindingActions = map[string]func(*View) bool{
|
||||
"ShellMode": (*View).ShellMode,
|
||||
"CommandMode": (*View).CommandMode,
|
||||
"Quit": (*View).Quit,
|
||||
"LastView": (*View).LastView,
|
||||
"NextView": (*View).NextView,
|
||||
}
|
||||
|
||||
var bindingKeys = map[string]tcell.Key{
|
||||
@@ -902,7 +904,7 @@ func (v *View) OpenFile() bool {
|
||||
if v.CanClose("Continue? (yes, no, save) ") {
|
||||
filename, canceled := messenger.Prompt("File to open: ", "Open")
|
||||
if canceled {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
@@ -910,12 +912,19 @@ func (v *View) OpenFile() bool {
|
||||
|
||||
if err != nil {
|
||||
messenger.Error(err.Error())
|
||||
return true
|
||||
return false
|
||||
}
|
||||
buf := NewBuffer(file, filename)
|
||||
v.OpenBuffer(buf)
|
||||
return true
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *View) openInNewView(buf *Buffer) {
|
||||
views = append(views, NewView(buf))
|
||||
mainView++
|
||||
views[mainView].Num = mainView
|
||||
}
|
||||
|
||||
// Start moves the viewport to the start of the buffer
|
||||
@@ -1082,12 +1091,38 @@ func (v *View) Quit() bool {
|
||||
// Make sure not to quit if there are unsaved changes
|
||||
if views[mainView].CanClose("Quit anyway? (yes, no, save) ") {
|
||||
views[mainView].CloseBuffer()
|
||||
screen.Fini()
|
||||
os.Exit(0)
|
||||
if len(views) > 1 {
|
||||
views = views[:v.Num+copy(views[v.Num:], views[v.Num+1:])]
|
||||
for i, v := range views {
|
||||
v.Num = i
|
||||
}
|
||||
if v.Num <= mainView {
|
||||
if !(v.Num == mainView && mainView == 0) {
|
||||
mainView--
|
||||
}
|
||||
}
|
||||
} else {
|
||||
screen.Fini()
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *View) LastView() bool {
|
||||
if mainView > 0 {
|
||||
mainView--
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *View) NextView() bool {
|
||||
if mainView < len(views)-1 {
|
||||
mainView++
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// None is no action
|
||||
func None() bool {
|
||||
return false
|
||||
|
||||
@@ -55,7 +55,7 @@ var (
|
||||
)
|
||||
|
||||
// LoadInput loads the file input for the editor
|
||||
func LoadInput() (string, []byte, error) {
|
||||
func LoadInput() []*Buffer {
|
||||
// There are a number of ways micro should start given its input
|
||||
|
||||
// 1. If it is given a file in os.Args, it should open that
|
||||
@@ -72,23 +72,34 @@ func LoadInput() (string, []byte, error) {
|
||||
var filename string
|
||||
var input []byte
|
||||
var err error
|
||||
var buffers []*Buffer
|
||||
|
||||
if len(os.Args) > 1 {
|
||||
// Option 1
|
||||
filename = os.Args[1]
|
||||
// Check that the file exists
|
||||
if _, e := os.Stat(filename); e == nil {
|
||||
input, err = ioutil.ReadFile(filename)
|
||||
for i := 1; i < len(os.Args); i++ {
|
||||
filename = os.Args[i]
|
||||
// Check that the file exists
|
||||
if _, e := os.Stat(filename); e == nil {
|
||||
input, err = ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
TermMessage(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
buffers = append(buffers, NewBuffer(input, filename))
|
||||
}
|
||||
} 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)
|
||||
buffers = append(buffers, NewBuffer(input, filename))
|
||||
} else {
|
||||
// Option 3, just open an empty buffer
|
||||
buffers = append(buffers, NewBuffer(input, filename))
|
||||
}
|
||||
|
||||
// Option 3, or just return whatever we got
|
||||
return filename, input, err
|
||||
return buffers
|
||||
}
|
||||
|
||||
// InitConfigDir finds the configuration directory for micro according to the XDG spec.
|
||||
@@ -170,9 +181,10 @@ func InitScreen() {
|
||||
// RedrawAll redraws everything -- all the views and the messenger
|
||||
func RedrawAll() {
|
||||
messenger.Clear()
|
||||
for _, v := range views {
|
||||
v.Display()
|
||||
}
|
||||
// for _, v := range views {
|
||||
views[mainView].Display()
|
||||
// }
|
||||
DisplayTabBar()
|
||||
messenger.Display()
|
||||
screen.Show()
|
||||
}
|
||||
@@ -186,12 +198,6 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
filename, input, err := LoadInput()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
L = lua.NewState()
|
||||
defer L.Close()
|
||||
|
||||
@@ -210,8 +216,6 @@ func main() {
|
||||
// Load the help files
|
||||
LoadHelp()
|
||||
|
||||
buf := NewBuffer(input, filename)
|
||||
|
||||
InitScreen()
|
||||
|
||||
// This is just so if we have an error, we can exit cleanly and not completely
|
||||
@@ -229,8 +233,12 @@ func main() {
|
||||
|
||||
messenger = new(Messenger)
|
||||
messenger.history = make(map[string][]string)
|
||||
views = make([]*View, 1)
|
||||
views[0] = NewView(buf)
|
||||
// views = make([]*View, 1)
|
||||
buffers := LoadInput()
|
||||
for i, buf := range buffers {
|
||||
views = append(views, NewView(buf))
|
||||
views[i].Num = i
|
||||
}
|
||||
|
||||
L.SetGlobal("OS", luar.New(L, runtime.GOOS))
|
||||
L.SetGlobal("views", luar.New(L, views))
|
||||
|
||||
@@ -15,7 +15,7 @@ type Statusline struct {
|
||||
// Display draws the statusline to the screen
|
||||
func (sline *Statusline) Display() {
|
||||
// We'll draw the line at the lowest line in the view
|
||||
y := sline.view.height
|
||||
y := sline.view.height + sline.view.y
|
||||
|
||||
file := sline.view.Buf.Name
|
||||
// If the name is empty, use 'No name'
|
||||
|
||||
35
cmd/micro/tabbar.go
Normal file
35
cmd/micro/tabbar.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
func DisplayTabBar() {
|
||||
str := ""
|
||||
for i, v := range views {
|
||||
if i == mainView {
|
||||
str += "["
|
||||
} else {
|
||||
str += " "
|
||||
}
|
||||
str += v.Buf.Name
|
||||
if i == mainView {
|
||||
str += "]"
|
||||
} else {
|
||||
str += " "
|
||||
}
|
||||
str += " "
|
||||
}
|
||||
|
||||
tabBarStyle := defStyle.Reverse(true)
|
||||
if style, ok := colorscheme["tabbar"]; ok {
|
||||
tabBarStyle = style
|
||||
}
|
||||
|
||||
// Maybe there is a unicode filename?
|
||||
fileRunes := []rune(str)
|
||||
w, _ := screen.Size()
|
||||
for x := 0; x < w; x++ {
|
||||
if x < len(fileRunes) {
|
||||
screen.SetContent(x, 0, fileRunes[x], nil, tabBarStyle)
|
||||
} else {
|
||||
screen.SetContent(x, 0, ' ', nil, tabBarStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,9 @@ type View struct {
|
||||
width int
|
||||
height int
|
||||
|
||||
// Where this view is located
|
||||
x, y int
|
||||
|
||||
// How much to offset because of line numbers
|
||||
lineNumOffset int
|
||||
|
||||
@@ -40,6 +43,9 @@ type View struct {
|
||||
// Is the help text opened in this view
|
||||
helpOpen bool
|
||||
|
||||
// This is the index of this view in the views array
|
||||
Num int
|
||||
|
||||
// Is this view modifiable?
|
||||
Modifiable bool
|
||||
|
||||
@@ -91,6 +97,8 @@ func NewView(buf *Buffer) *View {
|
||||
func NewViewWidthHeight(buf *Buffer, w, h int) *View {
|
||||
v := new(View)
|
||||
|
||||
v.x, v.y = 0, 1
|
||||
|
||||
v.widthPercent = w
|
||||
v.heightPercent = h
|
||||
v.Resize(screen.Size())
|
||||
@@ -113,6 +121,8 @@ func NewViewWidthHeight(buf *Buffer, w, h int) *View {
|
||||
func (v *View) Resize(w, h int) {
|
||||
// Always include 1 line for the command line at the bottom
|
||||
h--
|
||||
// Include one line for the tab bar at the top
|
||||
h--
|
||||
v.width = int(float32(w) * float32(v.widthPercent) / 100)
|
||||
// We subtract 1 for the statusline
|
||||
v.height = int(float32(h) * float32(v.heightPercent) / 100)
|
||||
@@ -173,6 +183,7 @@ func (v *View) OpenBuffer(buf *Buffer) {
|
||||
v.Topline = 0
|
||||
v.leftCol = 0
|
||||
v.Cursor.ResetSelection()
|
||||
v.Relocate()
|
||||
v.messages = make(map[string][]GutterMessage)
|
||||
|
||||
v.matches = Match(v)
|
||||
@@ -459,12 +470,12 @@ func (v *View) DisplayView() {
|
||||
}
|
||||
|
||||
for lineN := 0; lineN < v.height; lineN++ {
|
||||
var x int
|
||||
x := v.x
|
||||
// If the buffer is smaller than the view height
|
||||
if lineN+v.Topline >= v.Buf.NumLines {
|
||||
// We have to clear all this space
|
||||
for i := 0; i < v.width; i++ {
|
||||
screen.SetContent(i, lineN, ' ', nil, defStyle)
|
||||
screen.SetContent(i, lineN+v.y, ' ', nil, defStyle)
|
||||
}
|
||||
|
||||
continue
|
||||
@@ -492,9 +503,9 @@ func (v *View) DisplayView() {
|
||||
gutterStyle = style
|
||||
}
|
||||
}
|
||||
screen.SetContent(x, lineN, '>', nil, gutterStyle)
|
||||
screen.SetContent(x, lineN+v.y, '>', nil, gutterStyle)
|
||||
x++
|
||||
screen.SetContent(x, lineN, '>', nil, gutterStyle)
|
||||
screen.SetContent(x, lineN+v.y, '>', nil, gutterStyle)
|
||||
x++
|
||||
if v.Cursor.Y == lineN+v.Topline {
|
||||
messenger.Message(msg.msg)
|
||||
@@ -504,9 +515,9 @@ func (v *View) DisplayView() {
|
||||
}
|
||||
}
|
||||
if !msgOnLine {
|
||||
screen.SetContent(x, lineN, ' ', nil, tcell.StyleDefault)
|
||||
screen.SetContent(x, lineN+v.y, ' ', nil, tcell.StyleDefault)
|
||||
x++
|
||||
screen.SetContent(x, lineN, ' ', nil, tcell.StyleDefault)
|
||||
screen.SetContent(x, lineN+v.y, ' ', nil, tcell.StyleDefault)
|
||||
x++
|
||||
if v.Cursor.Y == lineN+v.Topline && messenger.gutterMessage {
|
||||
messenger.Reset()
|
||||
@@ -525,18 +536,18 @@ func (v *View) DisplayView() {
|
||||
if settings["ruler"] == true {
|
||||
lineNum = strconv.Itoa(lineN + v.Topline + 1)
|
||||
for i := 0; i < maxLineLength-len(lineNum); i++ {
|
||||
screen.SetContent(x, lineN, ' ', nil, lineNumStyle)
|
||||
screen.SetContent(x, lineN+v.y, ' ', nil, lineNumStyle)
|
||||
x++
|
||||
}
|
||||
// Write the actual line number
|
||||
for _, ch := range lineNum {
|
||||
screen.SetContent(x, lineN, ch, nil, lineNumStyle)
|
||||
screen.SetContent(x, lineN+v.y, ch, nil, lineNumStyle)
|
||||
x++
|
||||
}
|
||||
|
||||
if settings["ruler"] == true {
|
||||
// Write the extra space
|
||||
screen.SetContent(x, lineN, ' ', nil, lineNumStyle)
|
||||
screen.SetContent(x, lineN+v.y, ' ', nil, lineNumStyle)
|
||||
x++
|
||||
}
|
||||
}
|
||||
@@ -594,11 +605,14 @@ func (v *View) DisplayView() {
|
||||
if x-v.leftCol >= v.lineNumOffset {
|
||||
screen.SetContent(x-v.leftCol, lineN, indentChar[0], nil, lineIndentStyle)
|
||||
}
|
||||
if x-v.leftCol >= v.lineNumOffset {
|
||||
screen.SetContent(x-v.leftCol, lineN+v.y, indentChar[0], nil, lineIndentStyle)
|
||||
}
|
||||
tabSize := int(settings["tabsize"].(float64))
|
||||
for i := 0; i < tabSize-1; i++ {
|
||||
x++
|
||||
if x-v.leftCol >= v.lineNumOffset {
|
||||
screen.SetContent(x-v.leftCol, lineN, ' ', nil, lineStyle)
|
||||
screen.SetContent(x-v.leftCol, lineN+v.y, ' ', nil, lineStyle)
|
||||
}
|
||||
}
|
||||
} else if runewidth.RuneWidth(ch) > 1 {
|
||||
@@ -613,7 +627,7 @@ func (v *View) DisplayView() {
|
||||
}
|
||||
} else {
|
||||
if x-v.leftCol >= v.lineNumOffset {
|
||||
screen.SetContent(x-v.leftCol, lineN, ch, nil, lineStyle)
|
||||
screen.SetContent(x-v.leftCol, lineN+v.y, ch, nil, lineStyle)
|
||||
}
|
||||
}
|
||||
charNum = charNum.Move(1, v.Buf)
|
||||
@@ -632,7 +646,7 @@ func (v *View) DisplayView() {
|
||||
if style, ok := colorscheme["selection"]; ok {
|
||||
selectStyle = style
|
||||
}
|
||||
screen.SetContent(x-v.leftCol, lineN, ' ', nil, selectStyle)
|
||||
screen.SetContent(x-v.leftCol, lineN+v.y, ' ', nil, selectStyle)
|
||||
x++
|
||||
}
|
||||
|
||||
@@ -647,7 +661,7 @@ func (v *View) DisplayView() {
|
||||
}
|
||||
}
|
||||
if !(x-v.leftCol < v.lineNumOffset) {
|
||||
screen.SetContent(x-v.leftCol+i, lineN, ' ', nil, lineStyle)
|
||||
screen.SetContent(x+i, lineN+v.y, ' ', nil, lineStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -659,7 +673,7 @@ func (v *View) DisplayCursor() {
|
||||
if (v.Cursor.Y-v.Topline < 0 || v.Cursor.Y-v.Topline > v.height-1) || v.Cursor.HasSelection() {
|
||||
screen.HideCursor()
|
||||
} else {
|
||||
screen.ShowCursor(v.Cursor.GetVisualX()+v.lineNumOffset-v.leftCol, v.Cursor.Y-v.Topline)
|
||||
screen.ShowCursor(v.x+v.Cursor.GetVisualX()+v.lineNumOffset-v.leftCol, v.Cursor.Y-v.Topline+v.y)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user