Skip to content

Commit

Permalink
Chnaged: #1965_ Rewrite clipboard
Browse files Browse the repository at this point in the history
  • Loading branch information
fox0430 committed Dec 13, 2023
1 parent 46bd538 commit c759ab0
Show file tree
Hide file tree
Showing 14 changed files with 403 additions and 331 deletions.
4 changes: 2 additions & 2 deletions documents/configfile.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,10 @@ enable
Set clipboard tool for Linux (string)
default is xsel

`xsel` or `xclip` or `wl-clipboard`.
`xsel`, `xclip`, `wl-clipboard`, `wsl-default`, `macOS-default`.

```
toolOnLinux
tool
```

### TabLine table
Expand Down
3 changes: 2 additions & 1 deletion example/moerc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ liveReloadOfFile = false
colorMode = "24bit"

[Clipboard]

enable = true

# toolOnLinux = "xsel"
tool = "xsel"

[BuildOnSave]

Expand Down
119 changes: 72 additions & 47 deletions src/moepkg/clipboard.nim
Original file line number Diff line number Diff line change
Expand Up @@ -19,69 +19,94 @@

import std/[os, osproc, strformat, strutils]
import pkg/results
import independentutils, platform, settings, unicodeext
import independentutils, settings, unicodeext

proc linesToStrings(lines: seq[Runes]): string =
proc linesToString(lines: Runes | seq[Runes]): string =
result = lines.toString
result.stripLineEnd

proc genHereDocument(cmd, delimiterStr, buf: string): string {.inline.} =
cmd &
" <<" &
"'" &
delimiterStr &
"'" &
"\n" &
buf &
"\n" &
delimiterStr &
"\n"

proc xselCopyCommand(delimiterStr, buf: string): string {.inline.} =
genHereDocument("xsel", delimiterStr, buf)

proc xclipCopyCommand(delimiterStr, buf: string): string {.inline.} =
genHereDocument("xclip", delimiterStr, buf)

proc wlClipboardCopyComand(delimiterStr, buf: string): string {.inline.} =
genHereDocument("wl-copy", delimiterStr, buf)

proc wslDefaultCopyCommand(delimiterStr, buf: string): string {.inline.} =
genHereDocument("clip.exe", delimiterStr, buf)

proc macOsDefaultCopyCommand(delimiterStr, buf: string): string {.inline.} =
genHereDocument("pbcopy", delimiterStr, buf)

proc xselPasteCommand(): string {.inline.} = "xsel -o"

proc xclipPasteCommand(): string {.inline.} = "xclip -o"

proc wlClipboardPasteCommand(): string {.inline.} = "wl-paste"

proc wslDefaultPasteCommand(): string {.inline.} =
"powershell.exe -Command Get-Clipboard"

proc macOsDefaultPasteCommand(): string {.inline.} = "pbpaste"

proc sendToClipboard*(
buffer: seq[Runes],
tool: ClipboardTool) =
buffer: Runes | seq[Runes],
tool: ClipboardTool): Result[(), string] =
## Send the buffer to the OS clipboard (xclip, xsel, etc).

if buffer.len < 1: return
if buffer.isEmpty: return

let
str = linesToStrings(buffer)
delimiterStr = genDelimiterStr(str)

case currentPlatform:
of linux:
let cmd =
if tool == ClipboardTool.xclip:
"xclip -r <<" & "'" & delimiterStr & "'" & "\n" & str & "\n" & delimiterStr & "\n"
elif tool == ClipboardTool.xsel:
"xsel <<" & "'" & delimiterStr & "'" & "\n" & str & "\n" & delimiterStr & "\n"
elif tool == ClipboardTool.wlClipboard:
"wl-copy <<" & "'" & delimiterStr & "'" & "\n" & str & "\n" & delimiterStr & "\n"
else:
""

if cmd.len > 0:
discard execShellCmd(cmd)
of wsl:
let cmd = "clip.exe <<" & "'" & delimiterStr & "'" & "\n" & str & "\n" & delimiterStr & "\n"
discard execShellCmd(cmd)
of mac:
let cmd = "pbcopy <<" & "'" & delimiterStr & "'" & "\n" & str & "\n" & delimiterStr & "\n"
discard execShellCmd(cmd)
else:
discard

proc sendToClipboard*(buffer: Runes, tool: ClipboardTool) {.inline.} =
## Send the buffer to the OS clipboard (xclip, xsel, etc).

sendToClipboard(@[buffer], tool)

proc getBufferFromClipboard*(tool: ClipboardTool): Result[Runes, string] =
## Return the buffer from the OS clipboard.
buf = linesToString(buffer)
delimiterStr = genDelimiterStr(buf)
cmd =
case tool:
of xsel: xselCopyCommand(delimiterStr, buf)
of xclip: xclipCopyCommand(delimiterStr, buf)
of wlClipboard: wlClipboardCopyComand(delimiterStr, buf)
of wslDefault: wslDefaultCopyCommand(delimiterStr, buf)
of macOsDefault: macOsDefaultCopyCommand(delimiterStr, buf)

if tool == ClipboardTool.none: return
if execShellCmd(cmd) != 0:
return Result[(), string].err "Error: Clipboard: copy failed"

return Result[(), string].ok ()

proc getFromClipboard*(tool: ClipboardTool): Result[Runes, string] =
## Return the buffer from the OS clipboard.

let cmd =
case tool:
of xsel: "xsel -o"
of xclip: "xclip -o"
of wlClipboard: "wl-paste"
of wslDefault: "powershell.exe -Command Get-Clipboard"
of macOsDefault: "pbpaste"
else: ""
of xsel: xselPasteCommand()
of xclip: xclipPasteCommand()
of wlClipboard: wlClipboardPasteCommand()
of wslDefault: wslDefaultPasteCommand()
of macOsDefault: macOsDefaultPasteCommand()

let cmdResult = execCmdEx(cmd)
if cmdResult.exitCode != 0:
return Result[Runes, string].err fmt"clipboard: Failed to get clipboard buffer: {$cmdResult}"
return Result[Runes, string].err fmt"Error: Clipboard: Failed to get clipboard buffer: {$cmdResult}"

var buf = cmdResult.output
buf.stripLineEnd
case tool:
of wlClipboard, wslDefault:
# Remove two newlines.
for i in 0 .. 1: buf.stripLineEnd
else:
buf.stripLineEnd

return Result[Runes, string].ok buf.toRunes
12 changes: 6 additions & 6 deletions src/moepkg/configmode.nim
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ proc getClipboardTableSettingsValues(
result = @[ru "true", ru "false"]
else:
result = @[ru "false", ru "true"]
of "toolOnLinux":
of "tool":
for toolName in ClipboardTool:
if $toolName == "wlClipboard":
result.add ru "wl-clipboard"
Expand Down Expand Up @@ -776,9 +776,9 @@ proc changeClipBoardTableSettings(
case settingName:
of "enable":
settings.enable = parseBool(settingVal)
of "toolOnLinux":
of "tool":
let name = if settingVal == "wl-clipboard": "wlClipboard" else: settingVal
settings.toolOnLinux = parseEnum[ClipboardTool](name)
settings.tool = parseEnum[ClipboardTool](name)
else:
discard

Expand Down Expand Up @@ -1181,7 +1181,7 @@ proc getSettingType(table, name: string): SettingType =
case name:
of "enable":
result = SettingType.bool
of "toolOnLinux":
of "tool":
result = SettingType.enums
else:
result = SettingType.none
Expand Down Expand Up @@ -1886,8 +1886,8 @@ proc initClipBoardTableBuffer(settings: ClipboardSettings): seq[Runes] =
case $name:
of "enable":
result.add(ru nameStr & space & $settings.enable)
of "toolOnLinux":
result.add(ru nameStr & space & $settings.toolOnLinux)
of "tool":
result.add(ru nameStr & space & $settings.tool)

proc initBuildOnSaveTableBuffer(settings: BuildOnSaveSettings): seq[Runes] =
result.add(ru"BuildOnSave")
Expand Down
3 changes: 2 additions & 1 deletion src/moepkg/init.nim
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ proc initEditor*(): EditorStatus =

result.initSidebar

result.registers.setClipBoardTool(result.settings.clipboard.toolOnLinux)
if result.settings.clipboard.enable:
result.registers.setClipBoardTool(result.settings.clipboard.tool)

disableControlC()
catchTerminalResize()
Expand Down
9 changes: 4 additions & 5 deletions src/moepkg/registers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,8 @@ proc initRegisters*(): Registers =
proc setClipboardTool*(r: var Registers, tool: ClipboardTool) {.inline.} =
## Set the clipboard tool for Linux and init the clipboard register.

if tool != ClipboardTool.none:
r.clipboardTool = some(tool)
r.clipboard = initClipBoardRegister(now())
r.clipboardTool = some(tool)
r.clipboard = initClipBoardRegister(now())

proc isNamedRegisterName*(c: char): bool {.inline.} =
c in Letters
Expand Down Expand Up @@ -167,7 +166,7 @@ proc setNoNamedRegister*(
r.noNamed.set(buffer)

if r.clipboardTool.isSome:
buffer.sendToClipboard(r.clipboardTool.get)
discard buffer.sendToClipboard(r.clipboardTool.get)

proc set(
r: var NumberRegisters,
Expand Down Expand Up @@ -308,7 +307,7 @@ proc trySetClipBoardRegister(r: var Registers): bool =
if r.clipboardTool.isSome:
# Check the OS clipboard and update clipboard and no named registers.

let buf = getBufferFromClipboard(r.clipboardTool.get)
let buf = getFromClipboard(r.clipboardTool.get)
if buf.isOk and buf.get.len > 0:
let lines = buf.get.splitLines
if r.isUpdateClipBoardRegister(lines):
Expand Down
55 changes: 28 additions & 27 deletions src/moepkg/settings.nim
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ type
VSCode

ClipboardTool* = enum
none
xsel
xclip
wlClipboard
Expand Down Expand Up @@ -202,7 +201,7 @@ type

ClipboardSettings* = object
enable*: bool
toolOnLinux*: ClipboardTool
tool*: ClipboardTool

GitSettings* = object
showChangedLine*: bool
Expand Down Expand Up @@ -412,34 +411,35 @@ proc initPersistSettings(): PersistSettings =
result.searchHistoryLimit = 1000
result.cursorPosition = true

# Automatically set the clipboard tool on GNU/Linux
proc autoSetClipboardTool(): ClipboardTool =
result = ClipboardTool.none
proc autoSetClipboardTool(): Option[ClipboardTool] =
## Automatically set the clipboard tool for your environment.

case currentPlatform:
of linux:
# Check if X server is running
if execCmdExNoOutput("xset q") == 0:

if execCmdExNoOutput("xsel --version") == 0:
result = ClipboardTool.xsel
result = some(ClipboardTool.xsel)
elif execCmdExNoOutput("xclip -version") == 0:
let (output, _) = execCmdEx("xclip -version")
# Check xclip version
let
lines = output.splitLines
versionStr = (strutils.splitWhitespace(lines[0]))[2]
if parseFloat(versionStr) >= 0.13:
result = ClipboardTool.xclip
result = some(ClipboardTool.xclip)
elif execCmdExNoOutput("wl-copy -v") == 0:
result = ClipboardTool.wlClipboard
result = some(ClipboardTool.wlClipboard)
of mac:
result = some(ClipboardTool.macOsDefault)
else:
discard
result = none(ClipboardTool)

proc initClipboardSettings(): ClipboardSettings =
result.toolOnLinux = autoSetClipboardTool()

if ClipboardTool.none != result.toolOnLinux:
let tool = autoSetClipboardTool()
if tool.isSome:
result.tool = tool.get
result.enable = true

proc initGitSettings(): GitSettings =
Expand Down Expand Up @@ -1195,17 +1195,19 @@ proc parseClipboardTable(
if clipboardConfigs.contains("enable"):
s.clipboard.enable = clipboardConfigs["enable"].getBool

if clipboardConfigs.contains("toolOnLinux"):
let str = clipboardConfigs["toolOnLinux"].getStr
if clipboardConfigs.contains("tool"):
let str = clipboardConfigs["tool"].getStr
case str:
of "xsel":
s.clipboard.toolOnLinux = ClipboardTool.xsel
of "xclip":
s.clipboard.toolOnLinux = ClipboardTool.xclip
s.clipboard.tool = ClipboardTool.xclip
of "wl-clipboard":
s.clipboard.toolOnLinux = ClipboardTool.wlClipboard
s.clipboard.tool = ClipboardTool.wlClipboard
of "wsl-default":
s.clipboard.tool = ClipboardTool.wslDefault
of "macOS-default":
s.clipboard.tool = ClipboardTool.macOsDefault
else:
s.clipboard.toolOnLinux = ClipboardTool.xsel
s.clipboard.tool = ClipboardTool.xsel

proc parseTabLineTable(s: var EditorSettings, tablineConfigs: TomlValueRef) =
if tablineConfigs.contains("allBuffer"):
Expand Down Expand Up @@ -1892,18 +1894,17 @@ proc validateStandardTable(table: TomlValueRef): Option[InvalidItem] =
return some(InvalidItem(name: $key, val: $val))

proc validateClipboardTable(table: TomlValueRef): Option[InvalidItem] =
template isValidTool(s: string): bool =
s in [ "xclip", "xsel", "wl-clipboard", "wsl-defaut", "macOS-default"]

for key, val in table.getTable:
case key:
of "enable":
if not (val.kind == TomlValueKind.Bool):
return some(InvalidItem(name: $key, val: $val))
of "toolOnLinux":
if not (
(val.kind == TomlValueKind.String) and
(val.getStr == "none" or
val.getStr == "xclip" or
val.getStr == "xsel" or
val.getStr == "wl-clipboard")): return some(InvalidItem(name: $key, val: $val))
of "tool":
if not (val.kind == TomlValueKind.String and val.getStr.isValidTool):
return some(InvalidItem(name: $key, val: $val))
else:
return some(InvalidItem(name: $key, val: $val))

Expand Down Expand Up @@ -2415,7 +2416,7 @@ proc genTomlConfigStr*(settings: EditorSettings): string =

result.addLine fmt "[Clipboard]"
result.addLine fmt "enable = {$settings.clipboard.enable}"
result.addLine fmt "toolOnLinux = \"{$settings.clipboard.toolOnLinux}\""
result.addLine fmt "tool = \"{$settings.clipboard.tool}\""

result.addLine ""

Expand Down
12 changes: 12 additions & 0 deletions src/moepkg/unicodeext.nim
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ proc startsWith*(r1: Runes, r2: Runes | Rune): bool {.inline.} =
proc endsWith*(r1: Runes, r2: Runes | Rune): bool {.inline.} =
endsWith($r1, $r2)

proc toString*(runes: Runes): string {.inline.} = $runes

proc toString*(lines: seq[Runes]): string =
for i, runes in lines: result &= $runes & '\n'

Expand Down Expand Up @@ -625,3 +627,13 @@ proc maxLen*(lines: seq[Runes]): int {.inline.} =

proc maxHigh*(lines: seq[Runes]): int {.inline.} =
lines.mapIt(it.high).max

proc removeNewLineAtEnd*(runes: Runes): Runes =
result = runes

var countNewline = 0
for i in countdown(runes.high, 0):
if runes[i] in [ru'\n', ru'\r']: countNewline.inc
else: break

if countNewline > 0: return result[0 .. countNewline]
Loading

0 comments on commit c759ab0

Please sign in to comment.