Tab bar and support for opening multiple files

This commit is contained in:
Zachary Yedidia
2016-06-03 16:41:09 -04:00
parent 3080e32a8f
commit 5f19f69681
5 changed files with 132 additions and 40 deletions

View File

@@ -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

View File

@@ -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))

View File

@@ -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
View 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)
}
}
}

View File

@@ -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)
}
}