Merge
This commit is contained in:
commit
b473fe458d
12 changed files with 114 additions and 44 deletions
|
@ -56,10 +56,10 @@ You can also check out the website for Micro at https://micro-editor.github.io.
|
|||
- Note that while Windows is supported Mingw/Cygwin is not (see below)
|
||||
- Plugin system (plugins are written in Lua).
|
||||
- micro has a built-in plugin manager to automatically install, remove, and update plugins.
|
||||
- Built-in diff gutter
|
||||
- Simple autocompletion
|
||||
- Built-in diff gutter.
|
||||
- Simple autocompletion.
|
||||
- Persistent undo.
|
||||
- Automatic linting and error notifications
|
||||
- Automatic linting and error notifications.
|
||||
- Syntax highlighting for over [130 languages](runtime/syntax).
|
||||
- Color scheme support.
|
||||
- By default, micro comes with 16, 256, and true color themes.
|
||||
|
@ -132,6 +132,8 @@ for other operating systems. These packages are not guaranteed to be up-to-date.
|
|||
* `scoop install micro`.
|
||||
* OpenBSD: Available in the ports tree and also available as a binary package.
|
||||
* `pkd_add -v micro`.
|
||||
* NetBSD, macOS, Linux, Illumos, etc. with [pkgsrc](http://www.pkgsrc.org/)-current:
|
||||
* `pkg_add micro`
|
||||
|
||||
### Building from source
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
|
@ -44,7 +45,7 @@ func InitFlags() {
|
|||
fmt.Println(" \tCleans the configuration directory")
|
||||
fmt.Println("-config-dir dir")
|
||||
fmt.Println(" \tSpecify a custom location for the configuration directory")
|
||||
fmt.Println("[FILE]:LINE:COL")
|
||||
fmt.Println("[FILE]:LINE:COL (if the `parsecursor` option is enabled)")
|
||||
fmt.Println("+LINE:COL")
|
||||
fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
|
||||
fmt.Println("-options")
|
||||
|
@ -155,18 +156,31 @@ func LoadInput() []*buffer.Buffer {
|
|||
}
|
||||
|
||||
files := make([]string, 0, len(args))
|
||||
flagStartPos := ""
|
||||
flagr := regexp.MustCompile(`^\+\d+(:\d+)?$`)
|
||||
flagStartPos := buffer.Loc{-1, -1}
|
||||
flagr := regexp.MustCompile(`^\+(\d+)(?::(\d+))?$`)
|
||||
for _, a := range args {
|
||||
if flagr.MatchString(a) {
|
||||
flagStartPos = a[1:]
|
||||
} else {
|
||||
if flagStartPos != "" {
|
||||
files = append(files, a+":"+flagStartPos)
|
||||
flagStartPos = ""
|
||||
} else {
|
||||
files = append(files, a)
|
||||
match := flagr.FindStringSubmatch(a)
|
||||
if len(match) == 3 && match[2] != "" {
|
||||
line, err := strconv.Atoi(match[1])
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
continue
|
||||
}
|
||||
col, err := strconv.Atoi(match[2])
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
continue
|
||||
}
|
||||
flagStartPos = buffer.Loc{col - 1, line - 1}
|
||||
} else if len(match) == 3 && match[2] == "" {
|
||||
line, err := strconv.Atoi(match[1])
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
continue
|
||||
}
|
||||
flagStartPos = buffer.Loc{0, line - 1}
|
||||
} else {
|
||||
files = append(files, a)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +188,7 @@ func LoadInput() []*buffer.Buffer {
|
|||
// Option 1
|
||||
// We go through each file and load it
|
||||
for i := 0; i < len(files); i++ {
|
||||
buf, err := buffer.NewBufferFromFile(files[i], btype)
|
||||
buf, err := buffer.NewBufferFromFileAtLoc(files[i], btype, flagStartPos)
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
continue
|
||||
|
@ -191,10 +205,10 @@ func LoadInput() []*buffer.Buffer {
|
|||
screen.TermMessage("Error reading from stdin: ", err)
|
||||
input = []byte{}
|
||||
}
|
||||
buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, btype))
|
||||
buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
|
||||
} else {
|
||||
// Option 3, just open an empty buffer
|
||||
buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, btype))
|
||||
buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
|
||||
}
|
||||
|
||||
return buffers
|
||||
|
|
|
@ -727,6 +727,7 @@ func (h *BufPane) Save() bool {
|
|||
}
|
||||
|
||||
// SaveAsCB performs a save as and does a callback at the very end (after all prompts have been resolved)
|
||||
// The callback is only called if the save was successful
|
||||
func (h *BufPane) SaveAsCB(action string, callback func()) bool {
|
||||
InfoBar.Prompt("Filename: ", "", "Save", nil, func(resp string, canceled bool) {
|
||||
if !canceled {
|
||||
|
@ -757,6 +758,7 @@ func (h *BufPane) SaveAs() bool {
|
|||
|
||||
// This function saves the buffer to `filename` and changes the buffer's path and name
|
||||
// to `filename` if the save is successful
|
||||
// The callback is only called if the save was successful
|
||||
func (h *BufPane) saveBufToFile(filename string, action string, callback func()) bool {
|
||||
err := h.Buf.SaveAs(filename)
|
||||
if err != nil {
|
||||
|
@ -769,6 +771,9 @@ func (h *BufPane) saveBufToFile(filename string, action string, callback func())
|
|||
h.Buf.Path = filename
|
||||
h.Buf.SetName(filename)
|
||||
InfoBar.Message("Saved " + filename)
|
||||
if callback != nil {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
if h.Buf.Settings["autosu"].(bool) {
|
||||
|
@ -779,9 +784,6 @@ func (h *BufPane) saveBufToFile(filename string, action string, callback func())
|
|||
saveWithSudo()
|
||||
h.completeAction(action)
|
||||
}
|
||||
if callback != nil {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
@ -792,9 +794,9 @@ func (h *BufPane) saveBufToFile(filename string, action string, callback func())
|
|||
h.Buf.Path = filename
|
||||
h.Buf.SetName(filename)
|
||||
InfoBar.Message("Saved " + filename)
|
||||
}
|
||||
if callback != nil {
|
||||
callback()
|
||||
if callback != nil {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -124,6 +124,8 @@ func BufMapKey(k Event, action string) {
|
|||
break
|
||||
}
|
||||
}
|
||||
// if the action changed the current pane, update the reference
|
||||
h = MainTab().CurPane()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -701,6 +701,9 @@ func (h *BufPane) GotoCmd(args []string) {
|
|||
InfoBar.Error(err)
|
||||
return
|
||||
}
|
||||
if line < 0 {
|
||||
line = h.Buf.LinesNum() + 1 + line
|
||||
}
|
||||
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
|
||||
col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line)))
|
||||
h.Cursor.GotoLoc(buffer.Loc{col, line})
|
||||
|
@ -710,6 +713,9 @@ func (h *BufPane) GotoCmd(args []string) {
|
|||
InfoBar.Error(err)
|
||||
return
|
||||
}
|
||||
if line < 0 {
|
||||
line = h.Buf.LinesNum() + 1 + line
|
||||
}
|
||||
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
|
||||
h.Cursor.GotoLoc(buffer.Loc{0, line})
|
||||
}
|
||||
|
|
|
@ -191,13 +191,22 @@ type Buffer struct {
|
|||
StartCursor Loc
|
||||
}
|
||||
|
||||
// NewBufferFromFile opens a new buffer using the given path
|
||||
// It will also automatically handle `~`, and line/column with filename:l:c
|
||||
// It will return an empty buffer if the path does not exist
|
||||
// and an error if the file is a directory
|
||||
func NewBufferFromFile(path string, btype BufType) (*Buffer, error) {
|
||||
// NewBufferFromFileAtLoc opens a new buffer with a given cursor location
|
||||
// If cursorLoc is {-1, -1} the location does not overwrite what the cursor location
|
||||
// would otherwise be (start of file, or saved cursor position if `savecursor` is
|
||||
// enabled)
|
||||
func NewBufferFromFileAtLoc(path string, btype BufType, cursorLoc Loc) (*Buffer, error) {
|
||||
var err error
|
||||
filename, cursorPos := util.GetPathAndCursorPosition(path)
|
||||
filename := path
|
||||
if config.GetGlobalOption("parsecursor").(bool) && cursorLoc.X == -1 && cursorLoc.Y == -1 {
|
||||
var cursorPos []string
|
||||
filename, cursorPos = util.GetPathAndCursorPosition(filename)
|
||||
cursorLoc, err = ParseCursorLocation(cursorPos)
|
||||
if err != nil {
|
||||
cursorLoc = Loc{-1, -1}
|
||||
}
|
||||
}
|
||||
|
||||
filename, err = util.ReplaceHome(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -212,11 +221,6 @@ func NewBufferFromFile(path string, btype BufType) (*Buffer, error) {
|
|||
|
||||
defer file.Close()
|
||||
|
||||
cursorLoc, cursorerr := ParseCursorLocation(cursorPos)
|
||||
if cursorerr != nil {
|
||||
cursorLoc = Loc{-1, -1}
|
||||
}
|
||||
|
||||
var buf *Buffer
|
||||
if err != nil {
|
||||
// File does not exist -- create an empty buffer with that name
|
||||
|
@ -228,6 +232,19 @@ func NewBufferFromFile(path string, btype BufType) (*Buffer, error) {
|
|||
return buf, nil
|
||||
}
|
||||
|
||||
// NewBufferFromFile opens a new buffer using the given path
|
||||
// It will also automatically handle `~`, and line/column with filename:l:c
|
||||
// It will return an empty buffer if the path does not exist
|
||||
// and an error if the file is a directory
|
||||
func NewBufferFromFile(path string, btype BufType) (*Buffer, error) {
|
||||
return NewBufferFromFileAtLoc(path, btype, Loc{-1, -1})
|
||||
}
|
||||
|
||||
// NewBufferFromStringAtLoc creates a new buffer containing the given string with a cursor loc
|
||||
func NewBufferFromStringAtLoc(text, path string, btype BufType, cursorLoc Loc) *Buffer {
|
||||
return NewBuffer(strings.NewReader(text), int64(len(text)), path, cursorLoc, btype)
|
||||
}
|
||||
|
||||
// NewBufferFromString creates a new buffer containing the given string
|
||||
func NewBufferFromString(text, path string, btype BufType) *Buffer {
|
||||
return NewBuffer(strings.NewReader(text), int64(len(text)), path, Loc{-1, -1}, btype)
|
||||
|
|
|
@ -74,9 +74,6 @@ func (c *Cursor) GetVisualX() int {
|
|||
|
||||
bytes := c.buf.LineBytes(c.Y)
|
||||
tabsize := int(c.buf.Settings["tabsize"].(float64))
|
||||
if c.X > util.CharacterCount(bytes) {
|
||||
c.X = util.CharacterCount(bytes) - 1
|
||||
}
|
||||
|
||||
return util.StringWidth(bytes, c.X, tabsize)
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -203,6 +203,7 @@ var defaultCommonSettings = map[string]interface{}{
|
|||
"readonly": false,
|
||||
"rmtrailingws": false,
|
||||
"ruler": true,
|
||||
"relativeruler": false,
|
||||
"savecursor": false,
|
||||
"saveundo": false,
|
||||
"scrollbar": false,
|
||||
|
@ -253,6 +254,7 @@ var DefaultGlobalOnlySettings = map[string]interface{}{
|
|||
"infobar": true,
|
||||
"keymenu": false,
|
||||
"mouse": true,
|
||||
"parsecursor": false,
|
||||
"paste": false,
|
||||
"savehistory": true,
|
||||
"sucmd": "sudo",
|
||||
|
|
|
@ -335,7 +335,14 @@ func (w *BufWindow) drawDiffGutter(backgroundStyle tcell.Style, softwrapped bool
|
|||
}
|
||||
|
||||
func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxLineNumLength int, vloc *buffer.Loc, bloc *buffer.Loc) {
|
||||
lineNum := strconv.Itoa(bloc.Y + 1)
|
||||
cursorLine := w.Buf.GetActiveCursor().Loc.Y
|
||||
var lineInt int
|
||||
if w.Buf.Settings["relativeruler"] == false || cursorLine == bloc.Y {
|
||||
lineInt = bloc.Y + 1
|
||||
} else {
|
||||
lineInt = bloc.Y - cursorLine
|
||||
}
|
||||
lineNum := strconv.Itoa(util.Abs(lineInt))
|
||||
|
||||
// Write the spaces before the line number if necessary
|
||||
for i := 0; i < maxLineNumLength-len(lineNum); i++ {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
"github.com/zyedidia/micro/v2/internal/config"
|
||||
"github.com/zyedidia/micro/v2/internal/util"
|
||||
|
@ -97,6 +98,10 @@ func ShowCursor(x, y int) {
|
|||
// SetContent sets a cell at a point on the screen and makes sure that it is
|
||||
// synced with the last cursor location
|
||||
func SetContent(x, y int, mainc rune, combc []rune, style tcell.Style) {
|
||||
if !unicode.IsPrint(mainc) {
|
||||
mainc = '<27>'
|
||||
}
|
||||
|
||||
Screen.SetContent(x, y, mainc, combc, style)
|
||||
if util.FakeCursor && lastCursor.x == x && lastCursor.y == y {
|
||||
lastCursor.r = mainc
|
||||
|
|
|
@ -178,7 +178,7 @@ Here are the available options:
|
|||
|
||||
default value: `true`
|
||||
|
||||
* `paste`: Treat characters sent from the terminal in a single chunk as a paste
|
||||
* `paste`: treat characters sent from the terminal in a single chunk as a paste
|
||||
event rather than a series of manual key presses. If you are pasting using
|
||||
the terminal keybinding (not Ctrl-v, which is micro's default paste
|
||||
keybinding) then it is a good idea to enable this option during the paste
|
||||
|
@ -187,6 +187,16 @@ Here are the available options:
|
|||
|
||||
default value: `false`
|
||||
|
||||
* `parsecursor`: if enabled, this will cause micro to parse filenames such as
|
||||
file.txt:10:5 as requesting to open `file.txt` with the cursor at line 10
|
||||
and column 5. The column number can also be dropped to open the file at a
|
||||
given line and column 0. Note that with this option enabled it is not possible
|
||||
to open a file such as `file.txt:10:5`, where `:10:5` is part of the filename.
|
||||
It is also possible to open a file with a certain cursor location by using the
|
||||
`+LINE,COL` flag syntax. See `micro -help` for the command line options.
|
||||
|
||||
default value: `false`
|
||||
|
||||
* `pluginchannels`: list of URLs pointing to plugin channels for downloading and
|
||||
installing plugins. A plugin channel consists of a json file with links to
|
||||
plugin repos, which store information about plugin versions and download URLs.
|
||||
|
@ -214,6 +224,12 @@ Here are the available options:
|
|||
|
||||
default value: `true`
|
||||
|
||||
* `relativeruler`: make line numbers display relatively. If set to true, all lines except
|
||||
for the line that the cursor is located will display the distance from the
|
||||
cursor's line.
|
||||
|
||||
default value: `false`
|
||||
|
||||
* `savecursor`: remember where the cursor was last time the file was opened and
|
||||
put it there when you open the file again. Information is saved to
|
||||
`~/.config/micro/buffers/`
|
||||
|
|
Loading…
Reference in a new issue