mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-17 22:37:10 +09:00
@@ -76,6 +76,8 @@ var bindingActions = map[string]func(*View) bool{
|
||||
"AddTab": (*View).AddTab,
|
||||
"PreviousTab": (*View).PreviousTab,
|
||||
"NextTab": (*View).NextTab,
|
||||
"NextSplit": (*View).NextSplit,
|
||||
"PreviousSplit": (*View).PreviousSplit,
|
||||
}
|
||||
|
||||
var bindingKeys = map[string]tcell.Key{
|
||||
@@ -398,6 +400,7 @@ func DefaultBindings() map[string]string {
|
||||
"CtrlB": "ShellMode",
|
||||
"CtrlQ": "Quit",
|
||||
"CtrlE": "CommandMode",
|
||||
"CtrlW": "NextSplit",
|
||||
|
||||
// Emacs-style keybindings
|
||||
"Alt-f": "WordRight",
|
||||
@@ -915,11 +918,13 @@ func (v *View) OpenFile() bool {
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
|
||||
var buf *Buffer
|
||||
if err != nil {
|
||||
messenger.Error(err.Error())
|
||||
return false
|
||||
// File does not exist -- create an empty buffer with that name
|
||||
buf = NewBuffer([]byte{}, filename)
|
||||
} else {
|
||||
buf = NewBuffer(file, filename)
|
||||
}
|
||||
buf := NewBuffer(file, filename)
|
||||
v.OpenBuffer(buf)
|
||||
return true
|
||||
}
|
||||
@@ -1090,7 +1095,27 @@ func (v *View) Quit() bool {
|
||||
// Make sure not to quit if there are unsaved changes
|
||||
if v.CanClose("Quit anyway? (yes, no, save) ") {
|
||||
v.CloseBuffer()
|
||||
if len(tabs) > 1 {
|
||||
if len(tabs[curTab].views) > 1 {
|
||||
var view *View
|
||||
if v.splitChild != nil {
|
||||
view = v.splitChild
|
||||
view.splitParent = v.splitParent
|
||||
} else if v.splitParent != nil {
|
||||
view = v.splitParent
|
||||
v.splitParent.splitChild = nil
|
||||
}
|
||||
view.x, view.y = view.splitOrigPos[0], view.splitOrigPos[1]
|
||||
view.widthPercent, view.heightPercent = view.splitOrigDimensions[0], view.splitOrigDimensions[1]
|
||||
view.Resize(screen.Size())
|
||||
if settings["syntax"].(bool) {
|
||||
view.matches = Match(view)
|
||||
}
|
||||
tabs[curTab].views = tabs[curTab].views[:v.Num+copy(tabs[curTab].views[v.Num:], tabs[curTab].views[v.Num+1:])]
|
||||
for i, v := range tabs[curTab].views {
|
||||
v.Num = i
|
||||
}
|
||||
tabs[curTab].curView = view.Num
|
||||
} else if len(tabs) > 1 {
|
||||
if len(tabs[v.TabNum].views) == 1 {
|
||||
tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])]
|
||||
for i, t := range tabs {
|
||||
@@ -1148,6 +1173,28 @@ func (v *View) NextTab() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Changes the view to the next split
|
||||
func (v *View) NextSplit() bool {
|
||||
tab := tabs[curTab]
|
||||
if tab.curView < len(tab.views)-1 {
|
||||
tab.curView++
|
||||
} else {
|
||||
tab.curView = 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Changes the view to the previous split
|
||||
func (v *View) PreviousSplit() bool {
|
||||
tab := tabs[curTab]
|
||||
if tab.curView > 0 {
|
||||
tab.curView--
|
||||
} else {
|
||||
tab.curView = len(tab.views) - 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// None is no action
|
||||
func None() bool {
|
||||
return false
|
||||
|
||||
@@ -2,11 +2,14 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
var commands map[string]func([]string)
|
||||
@@ -18,6 +21,8 @@ var commandActions = map[string]func([]string){
|
||||
"Quit": Quit,
|
||||
"Save": Save,
|
||||
"Replace": Replace,
|
||||
"VSplit": VSplit,
|
||||
"HSplit": HSplit,
|
||||
}
|
||||
|
||||
// InitCommands initializes the default commands
|
||||
@@ -56,6 +61,52 @@ func DefaultCommands() map[string]string {
|
||||
"quit": "Quit",
|
||||
"save": "Save",
|
||||
"replace": "Replace",
|
||||
"vsplit": "VSplit",
|
||||
"hsplit": "HSplit",
|
||||
}
|
||||
}
|
||||
|
||||
// VSplit opens a vertical split with file given in the first argument
|
||||
// If no file is given, it opens an empty buffer in a new split
|
||||
func VSplit(args []string) {
|
||||
if len(args) == 0 {
|
||||
CurView().VSplit(NewBuffer([]byte{}, ""))
|
||||
} else {
|
||||
filename := args[0]
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
|
||||
var buf *Buffer
|
||||
if err != nil {
|
||||
// File does not exist -- create an empty buffer with that name
|
||||
buf = NewBuffer([]byte{}, filename)
|
||||
} else {
|
||||
buf = NewBuffer(file, filename)
|
||||
}
|
||||
CurView().VSplit(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// HSplit opens a horizontal split with file given in the first argument
|
||||
// If no file is given, it opens an empty buffer in a new split
|
||||
func HSplit(args []string) {
|
||||
if len(args) == 0 {
|
||||
CurView().HSplit(NewBuffer([]byte{}, ""))
|
||||
} else {
|
||||
filename := args[0]
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
|
||||
var buf *Buffer
|
||||
if err != nil {
|
||||
// File does not exist -- create an empty buffer with that name
|
||||
buf = NewBuffer([]byte{}, filename)
|
||||
} else {
|
||||
buf = NewBuffer(file, filename)
|
||||
}
|
||||
CurView().HSplit(buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,9 @@ func LoadSyntaxFilesFromDir(dir string) {
|
||||
if style, ok := colorscheme["default"]; ok {
|
||||
defStyle = style
|
||||
}
|
||||
if screen != nil {
|
||||
screen.SetStyle(defStyle)
|
||||
}
|
||||
|
||||
syntaxFiles = make(map[[2]*regexp.Regexp]FileTypeRules)
|
||||
files, _ := ioutil.ReadDir(dir)
|
||||
|
||||
@@ -174,11 +174,11 @@ func InitScreen() {
|
||||
// RedrawAll redraws everything -- all the views and the messenger
|
||||
func RedrawAll() {
|
||||
messenger.Clear()
|
||||
DisplayTabs()
|
||||
messenger.Display()
|
||||
for _, v := range tabs[curTab].views {
|
||||
v.Display()
|
||||
}
|
||||
DisplayTabs()
|
||||
messenger.Display()
|
||||
screen.Show()
|
||||
}
|
||||
|
||||
@@ -281,11 +281,17 @@ func main() {
|
||||
case *tcell.EventMouse:
|
||||
if e.Buttons() == tcell.Button1 {
|
||||
_, h := screen.Size()
|
||||
_, y := e.Position()
|
||||
x, y := e.Position()
|
||||
if y == h-1 && messenger.message != "" {
|
||||
clipboard.WriteAll(messenger.message)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, v := range tabs[curTab].views {
|
||||
if x >= v.x && x < v.x+v.width && y >= v.y && y < v.y+v.height {
|
||||
tabs[curTab].curView = v.Num
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,13 +48,18 @@ func (sline *Statusline) Display() {
|
||||
|
||||
// Maybe there is a unicode filename?
|
||||
fileRunes := []rune(file)
|
||||
viewX := sline.view.x
|
||||
if viewX != 0 {
|
||||
screen.SetContent(viewX, y, ' ', nil, statusLineStyle)
|
||||
viewX++
|
||||
}
|
||||
for x := 0; x < sline.view.width; x++ {
|
||||
if x < len(fileRunes) {
|
||||
screen.SetContent(x, y, fileRunes[x], nil, statusLineStyle)
|
||||
screen.SetContent(viewX+x, y, fileRunes[x], nil, statusLineStyle)
|
||||
} else if x >= sline.view.width-len(rightText) && x < len(rightText)+sline.view.width-len(rightText) {
|
||||
screen.SetContent(x, y, []rune(rightText)[x-sline.view.width+len(rightText)], nil, statusLineStyle)
|
||||
screen.SetContent(viewX+x, y, []rune(rightText)[x-sline.view.width+len(rightText)], nil, statusLineStyle)
|
||||
} else {
|
||||
screen.SetContent(x, y, ' ', nil, statusLineStyle)
|
||||
screen.SetContent(viewX+x, y, ' ', nil, statusLineStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,11 @@ type View struct {
|
||||
matches SyntaxMatches
|
||||
// The matches from the last frame
|
||||
lastMatches SyntaxMatches
|
||||
|
||||
splitParent *View
|
||||
splitChild *View
|
||||
splitOrigDimensions [2]int
|
||||
splitOrigPos [2]int
|
||||
}
|
||||
|
||||
// NewView returns a new fullscreen view
|
||||
@@ -124,15 +129,26 @@ func (v *View) Resize(w, h int) {
|
||||
// Always include 1 line for the command line at the bottom
|
||||
h--
|
||||
if len(tabs) > 1 {
|
||||
// Include one line for the tab bar at the top
|
||||
h--
|
||||
v.y = 1
|
||||
if v.y == 0 {
|
||||
// Include one line for the tab bar at the top
|
||||
h--
|
||||
v.y = 1
|
||||
}
|
||||
} else {
|
||||
v.y = 0
|
||||
if v.y == 1 {
|
||||
v.y = 0
|
||||
}
|
||||
}
|
||||
v.width = int(float32(w) * float32(v.widthPercent) / 100)
|
||||
// We subtract 1 for the statusline
|
||||
v.height = int(float32(h) * float32(v.heightPercent) / 100)
|
||||
if w%2 == 0 && v.x > 1 && v.widthPercent < 100 {
|
||||
v.width++
|
||||
}
|
||||
|
||||
if h%2 == 1 && v.y > 1 && v.heightPercent < 100 {
|
||||
v.height++
|
||||
}
|
||||
if settings["statusline"].(bool) {
|
||||
// Make room for the status line if it is enabled
|
||||
v.height--
|
||||
@@ -218,6 +234,62 @@ func (v *View) ReOpen() {
|
||||
}
|
||||
}
|
||||
|
||||
// HSplit opens a horizontal split with the given buffer
|
||||
func (v *View) HSplit(buf *Buffer) bool {
|
||||
origDimensions := [2]int{v.widthPercent, v.heightPercent}
|
||||
origPos := [2]int{v.x, v.y}
|
||||
|
||||
v.heightPercent /= 2
|
||||
v.Resize(screen.Size())
|
||||
|
||||
newView := NewViewWidthHeight(buf, v.widthPercent, v.heightPercent)
|
||||
|
||||
v.splitOrigDimensions = origDimensions
|
||||
v.splitOrigPos = origPos
|
||||
newView.splitOrigDimensions = origDimensions
|
||||
newView.splitOrigPos = origPos
|
||||
|
||||
newView.TabNum = v.TabNum
|
||||
newView.y = v.y + v.height + 1
|
||||
newView.x = v.x
|
||||
tab := tabs[v.TabNum]
|
||||
tab.curView++
|
||||
newView.Num = len(tab.views)
|
||||
newView.splitParent = v
|
||||
v.splitChild = newView
|
||||
tab.views = append(tab.views, newView)
|
||||
newView.Resize(screen.Size())
|
||||
return false
|
||||
}
|
||||
|
||||
// VSplit opens a vertical split with the given buffer
|
||||
func (v *View) VSplit(buf *Buffer) bool {
|
||||
origDimensions := [2]int{v.widthPercent, v.heightPercent}
|
||||
origPos := [2]int{v.x, v.y}
|
||||
|
||||
v.widthPercent /= 2
|
||||
v.Resize(screen.Size())
|
||||
|
||||
newView := NewViewWidthHeight(buf, v.widthPercent, v.heightPercent)
|
||||
|
||||
v.splitOrigDimensions = origDimensions
|
||||
v.splitOrigPos = origPos
|
||||
newView.splitOrigDimensions = origDimensions
|
||||
newView.splitOrigPos = origPos
|
||||
|
||||
newView.TabNum = v.TabNum
|
||||
newView.y = v.y
|
||||
newView.x = v.x + v.width
|
||||
tab := tabs[v.TabNum]
|
||||
tab.curView++
|
||||
newView.Num = len(tab.views)
|
||||
newView.splitParent = v
|
||||
v.splitChild = newView
|
||||
tab.views = append(tab.views, newView)
|
||||
newView.Resize(screen.Size())
|
||||
return false
|
||||
}
|
||||
|
||||
// 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() bool {
|
||||
@@ -340,7 +412,7 @@ func (v *View) HandleEvent(event tcell.Event) {
|
||||
v.freshClip = false
|
||||
case *tcell.EventMouse:
|
||||
x, y := e.Position()
|
||||
x -= v.lineNumOffset - v.leftCol
|
||||
x -= v.lineNumOffset - v.leftCol + v.x
|
||||
y += v.Topline - v.y
|
||||
// Don't relocate for mouse events
|
||||
relocate = false
|
||||
@@ -457,8 +529,15 @@ func (v *View) ClearAllGutterMessages() {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *View) drawCell(x, y int, ch rune, combc []rune, style tcell.Style) {
|
||||
if x >= v.x && x < v.x+v.width && y >= v.y && y < v.y+v.height {
|
||||
screen.SetContent(x, y, ch, combc, style)
|
||||
}
|
||||
}
|
||||
|
||||
// DisplayView renders the view to the screen
|
||||
func (v *View) DisplayView() {
|
||||
|
||||
// The character number of the character in the top left of the screen
|
||||
charNum := Loc{0, v.Topline}
|
||||
|
||||
@@ -483,13 +562,23 @@ func (v *View) DisplayView() {
|
||||
v.lineNumOffset += 2
|
||||
}
|
||||
|
||||
if v.x != 0 {
|
||||
// One space for the extra split divider
|
||||
v.lineNumOffset++
|
||||
}
|
||||
|
||||
for lineN := 0; lineN < v.height; lineN++ {
|
||||
x := v.x
|
||||
if v.x != 0 {
|
||||
// Draw the split divider
|
||||
v.drawCell(x, lineN+v.y, ' ', nil, defStyle.Reverse(true))
|
||||
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+v.y, ' ', nil, defStyle)
|
||||
for i := x; i < v.x+v.width; i++ {
|
||||
v.drawCell(i, lineN+v.y, ' ', nil, defStyle)
|
||||
}
|
||||
|
||||
continue
|
||||
@@ -517,9 +606,9 @@ func (v *View) DisplayView() {
|
||||
gutterStyle = style
|
||||
}
|
||||
}
|
||||
screen.SetContent(x, lineN+v.y, '>', nil, gutterStyle)
|
||||
v.drawCell(x, lineN+v.y, '>', nil, gutterStyle)
|
||||
x++
|
||||
screen.SetContent(x, lineN+v.y, '>', nil, gutterStyle)
|
||||
v.drawCell(x, lineN+v.y, '>', nil, gutterStyle)
|
||||
x++
|
||||
if v.Cursor.Y == lineN+v.Topline {
|
||||
messenger.Message(msg.msg)
|
||||
@@ -529,9 +618,9 @@ func (v *View) DisplayView() {
|
||||
}
|
||||
}
|
||||
if !msgOnLine {
|
||||
screen.SetContent(x, lineN+v.y, ' ', nil, defStyle)
|
||||
v.drawCell(x, lineN+v.y, ' ', nil, defStyle)
|
||||
x++
|
||||
screen.SetContent(x, lineN+v.y, ' ', nil, defStyle)
|
||||
v.drawCell(x, lineN+v.y, ' ', nil, defStyle)
|
||||
x++
|
||||
if v.Cursor.Y == lineN+v.Topline && messenger.gutterMessage {
|
||||
messenger.Reset()
|
||||
@@ -550,18 +639,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+v.y, ' ', nil, lineNumStyle)
|
||||
v.drawCell(x, lineN+v.y, ' ', nil, lineNumStyle)
|
||||
x++
|
||||
}
|
||||
// Write the actual line number
|
||||
for _, ch := range lineNum {
|
||||
screen.SetContent(x, lineN+v.y, ch, nil, lineNumStyle)
|
||||
v.drawCell(x, lineN+v.y, ch, nil, lineNumStyle)
|
||||
x++
|
||||
}
|
||||
|
||||
if settings["ruler"] == true {
|
||||
// Write the extra space
|
||||
screen.SetContent(x, lineN+v.y, ' ', nil, lineNumStyle)
|
||||
v.drawCell(x, lineN+v.y, ' ', nil, lineNumStyle)
|
||||
x++
|
||||
}
|
||||
}
|
||||
@@ -587,7 +676,7 @@ func (v *View) DisplayView() {
|
||||
lineStyle = highlightStyle
|
||||
}
|
||||
|
||||
if settings["cursorline"].(bool) && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline {
|
||||
if settings["cursorline"].(bool) && tabs[curTab].curView == v.Num && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline {
|
||||
if style, ok := colorscheme["cursor-line"]; ok {
|
||||
fg, _, _ := style.Decompose()
|
||||
lineStyle = lineStyle.Background(fg)
|
||||
@@ -609,7 +698,7 @@ func (v *View) DisplayView() {
|
||||
lineIndentStyle = style
|
||||
}
|
||||
}
|
||||
if settings["cursorline"].(bool) && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline {
|
||||
if settings["cursorline"].(bool) && tabs[curTab].curView == v.Num && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline {
|
||||
if style, ok := colorscheme["cursor-line"]; ok {
|
||||
fg, _, _ := style.Decompose()
|
||||
lineIndentStyle = lineIndentStyle.Background(fg)
|
||||
@@ -617,28 +706,28 @@ func (v *View) DisplayView() {
|
||||
}
|
||||
indentChar := []rune(settings["indentchar"].(string))
|
||||
if x-v.leftCol >= v.lineNumOffset {
|
||||
screen.SetContent(x-v.leftCol, lineN+v.y, indentChar[0], nil, lineIndentStyle)
|
||||
v.drawCell(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+v.y, ' ', nil, lineStyle)
|
||||
v.drawCell(x-v.leftCol, lineN+v.y, ' ', nil, lineStyle)
|
||||
}
|
||||
}
|
||||
} else if runewidth.RuneWidth(ch) > 1 {
|
||||
if x-v.leftCol >= v.lineNumOffset {
|
||||
screen.SetContent(x-v.leftCol, lineN, ch, nil, lineStyle)
|
||||
v.drawCell(x-v.leftCol, lineN+v.y, ch, nil, lineStyle)
|
||||
}
|
||||
for i := 0; i < runewidth.RuneWidth(ch)-1; i++ {
|
||||
x++
|
||||
if x-v.leftCol >= v.lineNumOffset {
|
||||
screen.SetContent(x-v.leftCol, lineN, ' ', nil, lineStyle)
|
||||
v.drawCell(x-v.leftCol, lineN+v.y, ' ', nil, lineStyle)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if x-v.leftCol >= v.lineNumOffset {
|
||||
screen.SetContent(x-v.leftCol, lineN+v.y, ch, nil, lineStyle)
|
||||
v.drawCell(x-v.leftCol, lineN+v.y, ch, nil, lineStyle)
|
||||
}
|
||||
}
|
||||
charNum = charNum.Move(1, v.Buf)
|
||||
@@ -657,22 +746,22 @@ func (v *View) DisplayView() {
|
||||
if style, ok := colorscheme["selection"]; ok {
|
||||
selectStyle = style
|
||||
}
|
||||
screen.SetContent(x-v.leftCol, lineN+v.y, ' ', nil, selectStyle)
|
||||
v.drawCell(x-v.leftCol, lineN+v.y, ' ', nil, selectStyle)
|
||||
x++
|
||||
}
|
||||
|
||||
charNum = charNum.Move(1, v.Buf)
|
||||
|
||||
for i := 0; i < v.width-(x-v.leftCol); i++ {
|
||||
for i := 0; i < v.width-((x-v.x)-v.leftCol); i++ {
|
||||
lineStyle := defStyle
|
||||
if settings["cursorline"].(bool) && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline {
|
||||
if settings["cursorline"].(bool) && tabs[curTab].curView == v.Num && !v.Cursor.HasSelection() && v.Cursor.Y == lineN+v.Topline {
|
||||
if style, ok := colorscheme["cursor-line"]; ok {
|
||||
fg, _, _ := style.Decompose()
|
||||
lineStyle = lineStyle.Background(fg)
|
||||
}
|
||||
}
|
||||
if !(x-v.leftCol < v.lineNumOffset) {
|
||||
screen.SetContent(x+i, lineN+v.y, ' ', nil, lineStyle)
|
||||
v.drawCell(x+i, lineN+v.y, ' ', nil, lineStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -691,7 +780,9 @@ func (v *View) DisplayCursor() {
|
||||
// Display renders the view, the cursor, and statusline
|
||||
func (v *View) Display() {
|
||||
v.DisplayView()
|
||||
v.DisplayCursor()
|
||||
if v.Num == tabs[curTab].curView {
|
||||
v.DisplayCursor()
|
||||
}
|
||||
if settings["statusline"].(bool) {
|
||||
v.sline.Display()
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ you can rebind them to your liking.
|
||||
"CtrlB": "ShellMode",
|
||||
"CtrlQ": "Quit",
|
||||
"CtrlE": "CommandMode",
|
||||
"CtrlW": "NextSplit",
|
||||
|
||||
// Emacs-style keybindings
|
||||
"Alt-f": "WordRight",
|
||||
@@ -114,6 +115,7 @@ You can execute an editor command by pressing `Ctrl-e` followed by the command.
|
||||
Here are the possible commands that you can use.
|
||||
|
||||
* `quit`: Quits micro.
|
||||
|
||||
* `save`: Saves the current buffer.
|
||||
|
||||
* `replace "search" "value" flags`: This will replace `search` with `value`.
|
||||
@@ -133,6 +135,12 @@ Here are the possible commands that you can use.
|
||||
* `bind key action`: creates a keybinding from key to action. See the sections on
|
||||
keybindings above for more info about what keys and actions are available.
|
||||
|
||||
* `vsplit filename`: opens a vertical split with `filename`. If no filename is
|
||||
provided, a vertical split is opened with an empty buffer
|
||||
|
||||
* `hsplit filename`: same as `vsplit` but opens a horizontal split instead of
|
||||
a vertical split
|
||||
|
||||
### Options
|
||||
|
||||
Micro stores all of the user configuration in its configuration directory.
|
||||
|
||||
Reference in New Issue
Block a user