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"
os.Exit(1)
}
+ m := clipboard.SetMethod(config.GetGlobalOption("clipboard").(string))
+ clipErr := clipboard.Initialize(m)
+
defer func() {
if err := recover(); err != nil {
screen.Screen.Fini()
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
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
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=
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=
"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"
h.doubleClick = false
h.Cursor.SelectLine()
- h.Cursor.CopySelection("primary")
+ h.Cursor.CopySelection(clipboard.PrimaryReg)
} else {
// Double click
h.lastClickTime = time.Now()
h.tripleClick = false
h.Cursor.SelectWord()
- h.Cursor.CopySelection("primary")
+ h.Cursor.CopySelection(clipboard.PrimaryReg)
}
} else {
h.doubleClick = false
// 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
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()
}
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 {
// 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
// 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
// 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
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
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"
// h.Cursor.SetSelectionEnd(h.Cursor.Loc)
// }
if h.Cursor.HasSelection() {
- h.Cursor.CopySelection("primary")
+ h.Cursor.CopySelection(clipboard.PrimaryReg)
}
h.mouseReleased = true
}
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"
}
} 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 {
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)
"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"
}
}
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())
package buffer
import (
- "github.com/zyedidia/clipboard"
+ "github.com/zyedidia/micro/v2/internal/clipboard"
"github.com/zyedidia/micro/v2/internal/util"
)
// 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)
}
}
}
--- /dev/null
+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
+}
--- /dev/null
+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
+}
--- /dev/null
+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)
+}
--- /dev/null
+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)
+}
// Options with validators
var optionValidators = map[string]optionValidator{
"autosave": validateNonNegativeValue,
+ "clipboard": validateClipboard,
"tabsize": validatePositiveValue,
"scrollmargin": validateNonNegativeValue,
"scrollspeed": validateNonNegativeValue,
// default values
var DefaultGlobalOnlySettings = map[string]interface{}{
"autosave": float64(0),
+ "clipboard": "external",
"colorscheme": "default",
"divchars": "|-",
"divreverse": true,
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)
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
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 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.
+* 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.
-# Copying
+* 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).
Copying follows a similar discussion to the one above about pasting.
The primary difference is before performing a copy, the application
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).
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.