hluk / CopyQ

Clipboard manager with advanced features
GNU General Public License v3.0
8.72k stars 441 forks source link

Help replace string (Commands) #656

Closed Qwash closed 7 years ago

Qwash commented 7 years ago

[1]: When I copy http://example.com/example.rar I want copq to auto replace it with http://test.org/example.rar I want to replace http://example.com/*.rarwith http://test.org/(the previous name).rar

[2]: I want CopyQ to check if clipboard contains a string like "TEST" then replace "TEST" with "ANYSTRING" (I will save this for feature needs as an example and try to edit it to fit my needs)

I couldn't understand the scripting language please help

PS: When adding new commands you can see a list of premade commands, this is very helpful, maybe adding "replace string" command to the this list could be a good idea

hluk commented 7 years ago

It's true that commands are not very user-friendly. When it comes to customizing them you need to change the code (visible only if "Show Advanced" is checked).

Copy/paste following code to command list in Comand dialog. The newly added command replaces text in clipboard.

[Command]
Automatic=true
Command="
    copyq:
    // Replace this text ...
    var replaceWhat = 'http://example.com/'
    // .. with this text.
    var replaceWith = 'http://test.org/'

    var text = str(input())
    if ( text.indexOf(replaceWhat) != -1 ) {
      var newText = text.replace(replaceWhat, replaceWith)

      // Copy replaced text back to clipboard.
      copy(newText)

      // Change data to save in item list.
      setData(mimeText, newText)
      removeData(mimeHtml)

      // Notify user.
      popup(
        'Clipboard Replaced',
        'Replaced \"' + text + '\" with \"' + newText + '\"')
    }"
Icon=\xf0d0
Input=text/plain
Name=Replace Clipboard

Here is bit more complicated but more powerful version which uses regexp to match text content and replace it.

[Command]
Automatic=true
Command="
    copyq:
    // Replace this regexp ...
    var replaceWhat = /^http:\\/\\/example\\.com\\/(.*\\.rar)/
    // .. with this text.
    var replaceWith = 'http://test.org/$1'

    var text = str(input())
    if ( text.match(replaceWhat) ) {
      var newText = text.replace(replaceWhat, replaceWith)

      // Copy replaced text back to clipboard.
      copy(newText)

      // Change data to save in item list.
      setData(mimeText, newText)
      removeData(mimeHtml)

      // Notify user.
      popup(
        'Clipboard Replaced',
        'Replaced \"' + text + '\" with \"' + newText + '\"')
    }"
Icon=\xf0d0
Input=text/plain
Name=Replace Clipboard
Qwash commented 7 years ago

[1]: Is it possible to replace many strings in one command using your first example (without using regexp) something like replace('http://example.com/' or 'test1' or 'test2') with 'NewString'

[2]: to remove the string I set var replaceWith = '' Is thats the best way to achieve that or there is a better way to remove specific string from the clipboard

hluk commented 7 years ago

ad 1. AFAIK this is not easy in JavaScript without using regexps. You usually replace all occurrences with regex in form /.../g (g flag is required).

ad 2. Yes, that's the way to remove sub-strings.

Qwash commented 7 years ago

[*] Is its a bad idea to have this command (the first example) repeated 3 or 4 times every command with different vars?

[*]

if ( text.indexOf(replaceWhat) != -1 )

What is the purpose of this line? would I still need it if I set

^https?://

for the command "content"

[*] CQ is using JavaScript for commands or what is this language?

hluk commented 7 years ago

The language is Qt Script -- it's ECMAScript dialect, very similar to JavaScript. In most cases it shouldn't a problem if you use Mozilla's reference.

The purpose of the conditional is to change clipboard content only when needed. It's same as:

var newText = text.replace(replaceWhat, replaceWith)
if (text != newText) {
    // Change clipboard ...
}

Repeating the code can be bad. To modify the code you would need to go through all commands that use it. But currently there is no easy way to re-use a code e.g. from a function defined outside commands. It might be a good idea to add this as feature but sharing commands could become problematic.

You can move the big part of the code to a file:

// File: c:/copyq/replace_clipboard_text.js
var text = str(input())
if ( text.indexOf(replaceWhat) != -1 ) {
  var newText = text.replace(replaceWhat, replaceWith)

  // Copy replaced text back to clipboard.
  copy(newText)

  // Change data to save in item list.
  setData(mimeText, newText)
  removeData(mimeHtml)

  // Notify user.
  popup(
    'Clipboard Replaced',
    'Replaced "' + text + '" with "' + newText + '"')
}

and use only following code in command.

var replaceWhat = 'http://test.org/'
var replaceWith = 'http://test.org/'

var script = File('c:/copyq/replace_clipboard_text.js')
if ( script.openReadOnly() )
  throw 'Failed to open script ' + script.fileName() + ': ' script.errorString()
eval(script.readAll())

I'll try add a function in CopyQ to simplify this.

Qwash commented 7 years ago

Your solution saved my time, I'm very thankful for your time and your help :heart: last thing, I wonder if it's possible to run an app with the item as args after replacing it something like (using your first solution)

copy(newText) run('C:\Test.exe newText')

I'm currently achieving this using an external tool, it would be great to drop it and depend only on your app :)

hluk commented 7 years ago

Here is the way to run external applications from script:

// Variable result stores information about the app after it finishes.
var result = execute('C:/Test.exe', newText)

// Check exit code.
if (result.exit_code != 0)
  popup('Test.exe exited with non-zero code: ' + result.exit_code)

// Check standard error output.
if (result.stderr)
  popup('Test.exe error output: ' + str(result.stderr))

// Check standard output.
if (result.stdout)
  popup('Test.exe output: ' + str(result.stdout))

Alternatively you can change the whole command (remove the copyq: ...) and have only:

C:/Test.exe %1

(%1 is clipboard text or text of selected items if the command is run from context menu or app shortcut)

Qwash commented 7 years ago

I have a problem with the args I want to pass quotation marks in the args but it always launched with wrong args (additional slash) as example, if you add an "in menu" command as the following

C:/0.exe %1

and selected the following item in the tab

/A "TEST" /B

When execute 0.exe it always launch with the following args

C:\0.exe "/A \"TEST\" /B"

instead of

C:\0.exe /A "TEST" /B

hluk commented 7 years ago

Yes, the item text is passed as single argument. If you want to handle it differently, try using:

cmd /c "C:/Test.exe %1"
Qwash commented 7 years ago

I forgot to add "Insert code" mark in my last comment, please check it again.

hluk commented 7 years ago

I'm not sure what you mean now. Doesn't the command from my previous comment already solve this?

Qwash commented 7 years ago

No, the problem isn't the text being passed as a single argument, I think I can workaround this by closing the quotation marks that the app pass it automatically in the start and in the end.

The problem is the slashes that appears when the var have quotation marks and spaces,

Define a var as '/A" "testing" "/B"' and try to launch an app with this var as an arg, instead of launched with "/A "testing" "/B" additional slashes will be added >> "/A \"Testing\" /B"

(I temporary solved this by passing the clipboard to external launcher that add the /A and /B in the start and in the and of the args automaticaly and then launch a predefined app with those args) I will stick to this solution if there is no simple way to do that

hluk commented 7 years ago

AFAIK, applications on Windows can parse arguments in different ways.

Probably the safest way is to pass the item content, not as an argument, but on standard input so application can just read the data.

Qwash commented 7 years ago

You helped me so much! Many thanks to you :)

hluk commented 7 years ago

@Qwash No problem :). Thanks for using the app.

govvin commented 7 months ago

I'll try add a function in CopyQ to simplify this.

@hluk , I'm wondering if you ever got around to adding this as a CopyQ function? Sorry for resurrecting this old thread.

hluk commented 7 months ago

I'll try add a function in CopyQ to simplify this.

@hluk , I'm wondering if you ever got around to adding this as a CopyQ function? Sorry for resurrecting this old thread.

Honestly, I'm not sure. This was long time ago. :grin:

The API in CopyQ and the underlying scripting language improved since then.

Can you be more specific which functionality is required?

pa-0 commented 2 months ago

The language is Qt Script -- it's ECMAScript dialect, very similar to JavaScript. In most cases it shouldn't a problem if you use Mozilla's reference.

The purpose of the conditional is to change clipboard content only when needed. It's same as:

var newText = text.replace(replaceWhat, replaceWith)
if (text != newText) {
    // Change clipboard ...
}

Repeating the code can be bad. To modify the code you would need to go through all commands that use it. But currently there is no easy way to re-use a code e.g. from a function defined outside commands. It might be a good idea to add this as feature but sharing commands could become problematic.

You can move the big part of the code to a file:

// File: c:/copyq/replace_clipboard_text.js
var text = str(input())
if ( text.indexOf(replaceWhat) != -1 ) {
  var newText = text.replace(replaceWhat, replaceWith)

  // Copy replaced text back to clipboard.
  copy(newText)

  // Change data to save in item list.
  setData(mimeText, newText)
  removeData(mimeHtml)

  // Notify user.
  popup(
    'Clipboard Replaced',
    'Replaced "' + text + '" with "' + newText + '"')
}

and use only following code in command.

var replaceWhat = 'http://test.org/'
var replaceWith = 'http://test.org/'

var script = File('c:/copyq/replace_clipboard_text.js')
if ( script.openReadOnly() )
  throw 'Failed to open script ' + script.fileName() + ': ' script.errorString()
eval(script.readAll())

I'll try add a function in CopyQ to simplify this.

I think they were referring to this.


Quick question: what would a command look like that could wrap the selected text in quotation marks? Actually, I think I have that part figured about, what I'm having trouble with is turning it into a 'toggle' function, meaning remove the quotes if they are there.