diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index a4d22b70..723e11b7 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -16,6 +16,7 @@ import ( lua "github.com/yuin/gopher-lua" "github.com/zyedidia/micro/v2/internal/action" "github.com/zyedidia/micro/v2/internal/buffer" + "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/config" ulua "github.com/zyedidia/micro/v2/internal/lua" "github.com/zyedidia/micro/v2/internal/screen" @@ -269,6 +270,9 @@ func main() { os.Exit(1) } + m := clipboard.SetMethod(config.GetGlobalOption("clipboard").(string)) + clipErr := clipboard.Initialize(m) + defer func() { if err := recover(); err != nil { screen.Screen.Fini() @@ -313,6 +317,10 @@ func main() { screen.TermMessage(err) } + if clipErr != nil { + action.InfoBar.Error(clipErr, " or change 'clipboard' option") + } + events = make(chan tcell.Event) // Here is the event loop which runs in a separate thread diff --git a/go.mod b/go.mod index 3a025637..c66f4fbd 100644 --- a/go.mod +++ b/go.mod @@ -12,12 +12,12 @@ require ( github.com/sergi/go-diff v1.1.0 github.com/stretchr/testify v1.4.0 github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb - github.com/zyedidia/clipboard v1.0.1 + github.com/zyedidia/clipboard v1.0.3 github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 github.com/zyedidia/highlight v0.0.0-20170330143449-201131ce5cf5 github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d github.com/zyedidia/pty v2.0.0+incompatible // indirect - github.com/zyedidia/tcell v1.4.8 + github.com/zyedidia/tcell v1.4.9 github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415 golang.org/x/text v0.3.2 gopkg.in/sourcemap.v1 v1.0.5 // indirect diff --git a/go.sum b/go.sum index cc537641..bbdcca78 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,10 @@ github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/zyedidia/clipboard v1.0.1 h1:DNsDMRXdptfdp4f6gSnSNJpWadAgI8UCm26elADCYak= github.com/zyedidia/clipboard v1.0.1/go.mod h1:zykFnZUXX0ErxqvYLUFEq7QDJKId8rmh2FgD0/Y8cjA= +github.com/zyedidia/clipboard v1.0.2 h1:Mh5t+hjEf9I5pcveVxVAFJsuOhd2ByavDgIkflK3Qps= +github.com/zyedidia/clipboard v1.0.2/go.mod h1:zykFnZUXX0ErxqvYLUFEq7QDJKId8rmh2FgD0/Y8cjA= +github.com/zyedidia/clipboard v1.0.3 h1:F/nCDVYMdbDWTmY8s8cJl0tnwX32q96IF09JHM14bUI= +github.com/zyedidia/clipboard v1.0.3/go.mod h1:zykFnZUXX0ErxqvYLUFEq7QDJKId8rmh2FgD0/Y8cjA= github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 h1:oMHjjTLfGXVuyOQBYj5/td9WC0mw4g1xDBPovIqmHew= github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3/go.mod h1:YKbIYP//Eln8eDgAJGI3IDvR3s4Tv9Z9TGIOumiyQ5c= github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655 h1:Z3RhH6hvcSx7eX6Q/pP6YVsgea/1eMDG99vtWwi3nK4= @@ -54,6 +58,8 @@ github.com/zyedidia/pty v2.0.0+incompatible h1:Ou5vXL6tvjst+RV8sUFISbuKDnUJPhnpy github.com/zyedidia/pty v2.0.0+incompatible/go.mod h1:4y9l9yJZNxRa7GB/fB+mmDmGkG3CqmzLf4vUxGGotEA= github.com/zyedidia/tcell v1.4.8 h1:s4zYGOyCNDK4cdrgNVME0SxGizuT/oKY3OyB4Ls2Qpg= github.com/zyedidia/tcell v1.4.8/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA= +github.com/zyedidia/tcell v1.4.9 h1:DYUrd8gCmSxUlbFd280sT6tzEqhxfyWMbZNNnup6iQ0= +github.com/zyedidia/tcell v1.4.9/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA= github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415 h1:752dTQ5OatJ9M5ULK2+9lor+nzyZz+LYDo3WGngg3Rc= github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415/go.mod h1:8leT8G0Cm8NoJHdrrKHyR9MirWoF4YW7pZh06B6H+1E= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/internal/action/actions.go b/internal/action/actions.go index ce2d86c7..5d7b31e0 100644 --- a/internal/action/actions.go +++ b/internal/action/actions.go @@ -7,8 +7,8 @@ import ( "time" shellquote "github.com/kballard/go-shellquote" - "github.com/zyedidia/clipboard" "github.com/zyedidia/micro/v2/internal/buffer" + "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/shell" @@ -59,7 +59,7 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool { h.doubleClick = false h.Cursor.SelectLine() - h.Cursor.CopySelection("primary") + h.Cursor.CopySelection(clipboard.PrimaryReg) } else { // Double click h.lastClickTime = time.Now() @@ -68,7 +68,7 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool { h.tripleClick = false h.Cursor.SelectWord() - h.Cursor.CopySelection("primary") + h.Cursor.CopySelection(clipboard.PrimaryReg) } } else { h.doubleClick = false @@ -961,13 +961,9 @@ func (h *BufPane) Redo() bool { // Copy the selection to the system clipboard func (h *BufPane) Copy() bool { if h.Cursor.HasSelection() { - h.Cursor.CopySelection("clipboard") + h.Cursor.CopySelection(clipboard.ClipboardReg) h.freshClip = true - if clipboard.Unsupported { - InfoBar.Message("Copied selection (install xclip for external clipboard)") - } else { - InfoBar.Message("Copied selection") - } + InfoBar.Message("Copied selection") } h.Relocate() return true @@ -979,13 +975,9 @@ func (h *BufPane) CopyLine() bool { return false } else { h.Cursor.SelectLine() - h.Cursor.CopySelection("clipboard") + h.Cursor.CopySelection(clipboard.ClipboardReg) h.freshClip = true - if clipboard.Unsupported { - InfoBar.Message("Copied line (install xclip for external clipboard)") - } else { - InfoBar.Message("Copied line") - } + InfoBar.Message("Copied line") } h.Cursor.Deselect(true) h.Relocate() @@ -1000,10 +992,10 @@ func (h *BufPane) CutLine() bool { } if h.freshClip == true { if h.Cursor.HasSelection() { - if clip, err := clipboard.ReadAll("clipboard"); err != nil { - // messenger.Error(err) + if clip, err := clipboard.Read(clipboard.ClipboardReg); err != nil { + InfoBar.Error(err) } else { - clipboard.WriteAll(clip+string(h.Cursor.GetSelection()), "clipboard") + clipboard.Write(clip+string(h.Cursor.GetSelection()), clipboard.ClipboardReg) } } } else if time.Since(h.lastCutTime)/time.Second > 10*time.Second || h.freshClip == false { @@ -1021,7 +1013,7 @@ func (h *BufPane) CutLine() bool { // Cut the selection to the system clipboard func (h *BufPane) Cut() bool { if h.Cursor.HasSelection() { - h.Cursor.CopySelection("clipboard") + h.Cursor.CopySelection(clipboard.ClipboardReg) h.Cursor.DeleteSelection() h.Cursor.ResetSelection() h.freshClip = true @@ -1147,7 +1139,10 @@ func (h *BufPane) MoveLinesDown() bool { // Paste whatever is in the system clipboard into the buffer // Delete and paste if the user has a selection func (h *BufPane) Paste() bool { - clip, _ := clipboard.ReadAll("clipboard") + clip, err := clipboard.Read(clipboard.ClipboardReg) + if err != nil { + InfoBar.Error(err) + } h.paste(clip) h.Relocate() return true @@ -1155,7 +1150,10 @@ func (h *BufPane) Paste() bool { // PastePrimary pastes from the primary clipboard (only use on linux) func (h *BufPane) PastePrimary() bool { - clip, _ := clipboard.ReadAll("primary") + clip, err := clipboard.Read(clipboard.PrimaryReg) + if err != nil { + InfoBar.Error(err) + } h.paste(clip) h.Relocate() return true @@ -1177,11 +1175,7 @@ func (h *BufPane) paste(clip string) { h.Buf.Insert(h.Cursor.Loc, clip) // h.Cursor.Loc = h.Cursor.Loc.Move(Count(clip), h.Buf) h.freshClip = false - if clipboard.Unsupported { - InfoBar.Message("Pasted clipboard (install xclip for external clipboard)") - } else { - InfoBar.Message("Pasted clipboard") - } + InfoBar.Message("Pasted clipboard") } // JumpToMatchingBrace moves the cursor to the matching brace if it is diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index cc365d0a..10280abf 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -8,6 +8,7 @@ import ( lua "github.com/yuin/gopher-lua" "github.com/zyedidia/micro/v2/internal/buffer" + "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/display" ulua "github.com/zyedidia/micro/v2/internal/lua" @@ -360,7 +361,7 @@ func (h *BufPane) HandleEvent(event tcell.Event) { // h.Cursor.SetSelectionEnd(h.Cursor.Loc) // } if h.Cursor.HasSelection() { - h.Cursor.CopySelection("primary") + h.Cursor.CopySelection(clipboard.PrimaryReg) } h.mouseReleased = true } diff --git a/internal/action/command.go b/internal/action/command.go index cff83374..f3f5db48 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -13,6 +13,7 @@ import ( shellquote "github.com/kballard/go-shellquote" "github.com/zyedidia/micro/v2/internal/buffer" + "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/shell" @@ -505,6 +506,12 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error { } } else if option == "paste" { screen.Screen.SetPaste(nativeValue.(bool)) + } else if option == "clipboard" { + m := clipboard.SetMethod(nativeValue.(string)) + err := clipboard.Initialize(m) + if err != nil { + return err + } } else { for _, pl := range config.Plugins { if option == pl.Name { diff --git a/internal/action/infocomplete.go b/internal/action/infocomplete.go index f39dfbda..12f7844b 100644 --- a/internal/action/infocomplete.go +++ b/internal/action/infocomplete.go @@ -186,6 +186,16 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) { if strings.HasPrefix("doas", input) { suggestions = append(suggestions, "doas") } + case "clipboard": + if strings.HasPrefix("external", input) { + suggestions = append(suggestions, "external") + } + if strings.HasPrefix("internal", input) { + suggestions = append(suggestions, "internal") + } + if strings.HasPrefix("terminal", input) { + suggestions = append(suggestions, "terminal") + } } } sort.Strings(suggestions) diff --git a/internal/action/termpane.go b/internal/action/termpane.go index 8c75d04b..860dc192 100644 --- a/internal/action/termpane.go +++ b/internal/action/termpane.go @@ -4,7 +4,7 @@ import ( "errors" "runtime" - "github.com/zyedidia/clipboard" + "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/display" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/shell" @@ -90,7 +90,7 @@ func (t *TermPane) HandleEvent(event tcell.Event) { } } if e.Key() == tcell.KeyCtrlC && t.HasSelection() { - clipboard.WriteAll(t.GetSelection(t.GetView().Width), "clipboard") + clipboard.Write(t.GetSelection(t.GetView().Width), clipboard.ClipboardReg) InfoBar.Message("Copied selection to clipboard") } else if t.Status != shell.TTDone { t.WriteString(event.EscSeq()) diff --git a/internal/buffer/cursor.go b/internal/buffer/cursor.go index 488a14c4..0521ebc7 100644 --- a/internal/buffer/cursor.go +++ b/internal/buffer/cursor.go @@ -1,7 +1,7 @@ package buffer import ( - "github.com/zyedidia/clipboard" + "github.com/zyedidia/micro/v2/internal/clipboard" "github.com/zyedidia/micro/v2/internal/util" ) @@ -125,10 +125,10 @@ func (c *Cursor) End() { // CopySelection copies the user's selection to either "primary" // or "clipboard" -func (c *Cursor) CopySelection(target string) { +func (c *Cursor) CopySelection(target clipboard.Register) { if c.HasSelection() { - if target != "primary" || c.buf.Settings["useprimary"].(bool) { - clipboard.WriteAll(string(c.GetSelection()), target) + if target != clipboard.PrimaryReg || c.buf.Settings["useprimary"].(bool) { + clipboard.Write(string(c.GetSelection()), target) } } } diff --git a/internal/clipboard/clipboard.go b/internal/clipboard/clipboard.go new file mode 100644 index 00000000..3a7dd004 --- /dev/null +++ b/internal/clipboard/clipboard.go @@ -0,0 +1,151 @@ +package clipboard + +import ( + "errors" + + "github.com/zyedidia/clipboard" +) + +type Method int + +const ( + // External relies on external tools for accessing the clipboard + // These include xclip, xsel, wl-clipboard for linux, pbcopy/pbpaste on Mac, + // and Syscalls on Windows. + External Method = iota + // Terminal uses the terminal to manage the clipboard via OSC 52. Many + // terminals do not support OSC 52, in which case this method won't work. + Terminal + // Internal just manages the clipboard with an internal buffer and doesn't + // attempt to interface with the system clipboard + Internal +) + +// CurrentMethod is the method used to store clipboard information +var CurrentMethod Method = Internal + +// A Register is a buffer used to store text. The system clipboard has the 'clipboard' +// and 'primary' (linux-only) registers, but other registers may be used internal to micro. +type Register int + +const ( + // ClipboardReg is the main system clipboard + ClipboardReg Register = -1 + // PrimaryReg is the system primary clipboard (linux only) + PrimaryReg = -2 +) + +// Initialize attempts to initialize the clipboard using the given method +func Initialize(m Method) error { + var err error + switch m { + case External: + err = clipboard.Initialize() + } + return err +} + +// SetMethod changes the clipboard access method +func SetMethod(m string) Method { + switch m { + case "internal": + CurrentMethod = Internal + case "external": + CurrentMethod = External + case "terminal": + CurrentMethod = Terminal + } + return CurrentMethod +} + +// Read reads from a clipboard register +func Read(r Register) (string, error) { + return read(r, CurrentMethod) +} + +// Write writes text to a clipboard register +func Write(text string, r Register) error { + return write(text, r, CurrentMethod) +} + +// ReadMulti reads text from a clipboard register for a certain multi-cursor +func ReadMulti(r Register, num int) (string, error) { + s := multi.getText(r, num) + return s, nil +} + +// WriteMulti writes text to a clipboard register for a certain multi-cursor +func WriteMulti(text string, r Register, num int) error { + return writeMulti(text, r, num, CurrentMethod) +} + +// ValidMulti checks if the internal multi-clipboard is valid and up-to-date +// with the system clipboard +func ValidMulti(r Register, ncursors int) bool { + clip, err := Read(r) + if err != nil { + return false + } + return multi.isValid(r, ncursors, clip) +} + +func writeMulti(text string, r Register, num int, m Method) error { + multi.writeText(text, r, num) + return write(multi.getAllText(r), r, m) +} + +func read(r Register, m Method) (string, error) { + switch m { + case External: + switch r { + case ClipboardReg: + return clipboard.ReadAll("clipboard") + case PrimaryReg: + return clipboard.ReadAll("primary") + default: + return internal.read(r), nil + } + case Internal: + return internal.read(r), nil + case Terminal: + switch r { + case ClipboardReg: + // terminal paste works by sending an esc sequence to the + // terminal to trigger a paste event + err := terminal.read("clipboard") + return "", err + case PrimaryReg: + err := terminal.read("primary") + return "", err + default: + return internal.read(r), nil + } + } + return "", errors.New("Invalid clipboard method") +} + +func write(text string, r Register, m Method) error { + switch m { + case External: + switch r { + case ClipboardReg: + return clipboard.WriteAll(text, "clipboard") + case PrimaryReg: + return clipboard.WriteAll(text, "primary") + default: + internal.write(text, r) + } + case Internal: + internal.write(text, r) + case Terminal: + switch r { + case ClipboardReg: + return terminal.write(text, "c") + case PrimaryReg: + return terminal.write(text, "p") + default: + internal.write(text, r) + } + } + return nil +} diff --git a/internal/clipboard/internal.go b/internal/clipboard/internal.go new file mode 100644 index 00000000..b178b3cb --- /dev/null +++ b/internal/clipboard/internal.go @@ -0,0 +1,17 @@ +package clipboard + +type internalClipboard map[Register]string + +var internal internalClipboard + +func init() { + internal = make(internalClipboard) +} + +func (c internalClipboard) read(r Register) string { + return c[r] +} + +func (c internalClipboard) write(text string, r Register) { + c[r] = text +} diff --git a/internal/clipboard/multi.go b/internal/clipboard/multi.go new file mode 100644 index 00000000..25a19321 --- /dev/null +++ b/internal/clipboard/multi.go @@ -0,0 +1,66 @@ +package clipboard + +import ( + "bytes" + "hash/fnv" +) + +// For storing multi cursor clipboard contents +type multiClipboard map[Register][]string + +var multi multiClipboard + +func (c multiClipboard) getAllText(r Register) string { + content := c[r] + if content == nil { + return "" + } + + buf := &bytes.Buffer{} + for _, s := range content { + buf.WriteString(s) + buf.WriteByte('\n') + } + return buf.String() +} + +func (c multiClipboard) getText(r Register, num int) string { + content := c[r] + if content == nil || len(content) <= num { + return "" + } + + return content[num] +} + +func hash(s string) uint32 { + h := fnv.New32a() + h.Write([]byte(s)) + return h.Sum32() +} + +// isValid checks if the text stored in this multi-clipboard is the same as the +// text stored in the system clipboard (provided as an argument), and therefore +// if it is safe to use the multi-clipboard for pasting instead of the system +// clipboard. +func (c multiClipboard) isValid(r Register, ncursors int, clipboard string) bool { + content := c[r] + if content == nil || len(content) != ncursors { + return false + } + + return hash(clipboard) == hash(c.getAllText(r)) +} + +func (c multiClipboard) writeText(text string, r Register, num int) { + content := c[r] + if content == nil || num >= cap(content) { + content = make([]string, num+1, num+1) + } + + content[num] = text +} + +func init() { + multi = make(multiClipboard) +} diff --git a/internal/clipboard/terminal.go b/internal/clipboard/terminal.go new file mode 100644 index 00000000..37b37dc3 --- /dev/null +++ b/internal/clipboard/terminal.go @@ -0,0 +1,15 @@ +package clipboard + +import "github.com/zyedidia/micro/v2/internal/screen" + +type terminalClipboard struct{} + +var terminal terminalClipboard + +func (t terminalClipboard) read(reg string) error { + return screen.Screen.GetClipboard(reg) +} + +func (t terminalClipboard) write(text, reg string) error { + return screen.Screen.SetClipboard(text, reg) +} diff --git a/internal/config/settings.go b/internal/config/settings.go index f298ae04..efbc671a 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -43,6 +43,7 @@ func init() { // Options with validators var optionValidators = map[string]optionValidator{ "autosave": validateNonNegativeValue, + "clipboard": validateClipboard, "tabsize": validatePositiveValue, "scrollmargin": validateNonNegativeValue, "scrollspeed": validateNonNegativeValue, @@ -322,6 +323,7 @@ func DefaultCommonSettings() map[string]interface{} { // default values var DefaultGlobalOnlySettings = map[string]interface{}{ "autosave": float64(0), + "clipboard": "external", "colorscheme": "default", "divchars": "|-", "divreverse": true, @@ -450,6 +452,22 @@ func validateColorscheme(option string, value interface{}) error { return nil } +func validateClipboard(option string, value interface{}) error { + val, ok := value.(string) + + if !ok { + return errors.New("Expected string type for clipboard") + } + + switch val { + case "internal", "external", "terminal": + default: + return errors.New(option + " must be 'internal', 'external', or 'terminal'") + } + + return nil +} + func validateLineEnding(option string, value interface{}) error { endingType, ok := value.(string) diff --git a/runtime/help/copypaste.md b/runtime/help/copypaste.md index 57e6a7cd..1fbf52a5 100644 --- a/runtime/help/copypaste.md +++ b/runtime/help/copypaste.md @@ -4,8 +4,51 @@ because there are multiple methods. This help document will explain the various methods for copying and pasting, how they work, and the best methods for doing so over SSH. +# OSC 52 (terminal clipboard) + +If possible, setting the `clipboard` option to `terminal` will give +best results because it will work over SSH and locally. However, there +is limited support among terminal emulators for the terminal clipboard +(which uses the OSC 52 protocol to communicate clipboard contents). +Here is a list of terminal emulators and their status: + +* Kitty: supported, but only writing is enabled by default. To enable + reading, add `read-primary` and `read-clipboard` to the + `clipboard_control` option. + +* iTerm2: supported, but must be enabled in + `Preferences->General-> Selection->Applications in terminal may access clipboard`. + +* `st`: supported. + +* `rxvt-unicode`: not natively supported, but there is a Perl extension + [here](http://anti.teamidiot.de/static/nei/*/Code/urxvt/). + +* `xterm`: supported, but disabled by default. It can be enabled by putting + the following in `.Xresources` or `.Xdefaults`: + `XTerm*disallowedWindowOps: 20,21,SetXprop`. + +* `gnome-terminal`: does not support OSC 52. + +**Summary:** If you want copy and paste to work over SSH, then you +should set `clipboard` to `terminal`, and make sure your terminal +supports OSC 52. + # Pasting +## Recommendations (TL;DR) + +The recommended method of pasting is the following: + +* If you are not working over SSH, use the micro keybinding (Ctrl-v + by default) to perform pastes. If on Linux, install `xclip` or + `xsel` beforehand. + +* If you are working over SSH, use the terminal keybinding + (Ctrl-Shift-v or Command-v) to perform pastes. If your terminal + does not support bracketed paste, when performing a paste first + enable the `paste` option, and when finished disable the option. + ## Micro paste events Micro is an application that runs within the terminal. This means @@ -56,20 +99,23 @@ machine's clipboard. On the other hand, the terminal keybinding for paste will access your local clipboard and send the text over the network as a paste event, which is what you want. -## Recommendations +# Copying -The recommended method of pasting is the following: +# Recommendations (TL;DR) -* If you are not working over SSH, use the micro keybinding (Ctrl-v - by default) to perform pastes. If on Linux, install `xclip` or - `xsel` beforehand. +The recommended method of copying is the following: + +* If you are not working over SSH, use the micro keybinding (Ctrl-c by + default) to perform copies. If on Linux, install `xclip` or `xsel` + beforehand. * If you are working over SSH, use the terminal keybinding - (Ctrl-Shift-v or Command-v) to perform pastes. If your terminal - does not support bracketed paste, when performing a paste first - enable the `paste` option, and when finished disable the option. - -# Copying + (Ctrl-Shift-c or Command-c) to perform copies. You must first disable + the `mouse` option to perform a terminal selection, and you may wish + to disable line numbers and diff indicators (`ruler` and `diffgutter` + options) and close other splits. This method will only be able to copy + characters that are displayed on the screen (you will not be able to + copy more than one page's worth of characters). Copying follows a similar discussion to the one above about pasting. The primary difference is before performing a copy, the application @@ -92,19 +138,3 @@ means that for copying multiple lines using the terminal selection, you should first disable line numbers and diff indicators (turn off the `ruler` and `diffgutter` options), otherwise they might be part of your selection and copied. - -## Recommendations - -The recommended method of copying is the following: - -* If you are not working over SSH, use the micro keybinding (Ctrl-c by - default) to perform copies. If on Linux, install `xclip` or `xsel` - beforehand. - -* If you are working over SSH, use the terminal keybinding - (Ctrl-Shift-c or Command-c) to perform copies. You must first disable - the `mouse` option to perform a terminal selection, and you may wish - to disable line numbers and diff indicators (`ruler` and `diffgutter` - options) and close other splits. This method will only be able to copy - characters that are displayed on the screen (you will not be able to - copy more than one page's worth of characters). diff --git a/runtime/help/options.md b/runtime/help/options.md index c8b20d07..3fb187a6 100644 --- a/runtime/help/options.md +++ b/runtime/help/options.md @@ -54,6 +54,25 @@ Here are the available options: default value: `false` +* `clipboard`: specifies how micro should access the system clipboard. + Possible values are: + * `external`: accesses clipboard via an external tool, such as xclip/xsel + or wl-clipboard on Linux, pbcopy/pbpaste on MacOS, and system calls on + Windows. On Linux, if you do not have one of the tools installed, or if + they are not working, micro will throw an error and use an internal + clipboard. + * `terminal`: accesses the clipboard via your terminal emulator. Note that + there is limited support among terminal emulators for this feature + (called OSC 52). Terminals that are known to work are Kitty (enable + reading with `clipboard_control` setting), iTerm2 (enable in prefs), + st, rxvt-unicode and xterm if enabled (see `> help copypaste` for + details). Note that Gnome-terminal does not support this feature. With + this setting, copy-paste **will** work over ssh. See `> help copypaste` + for details. + * `internal`: micro will use an internal clipboard. + + default value: `external` + * `colorcolumn`: if this is not set to 0, it will display a column at the specified column. This is useful if you want column 80 to be highlighted special for example.