Making custom command to replace regex match in entire file or selection


#1

I want a custom command that replaces all matches of hard-coded regex - specifically list formatting.

* a
# b
1.  c

to

a
b
c

Here’s what I’m trying with init.coffee:

atom.commands.add 'atom-text-editor', 'custom:clear-list', ->
  editor = atom.workspace.getActiveTextEditor()
  text=editor.getText()
  regMatch="^(\*|\#|1.)\s*"
  editor.scan(regMatch,text.replace(""))

And I get Uncaught TypeError: callback is not a function error when I execute the command

Apparently, I can’t get the buffer object right, the api docs have syntax but no examples so I’m not sure where to look.


#2

Try this:

atom.commands.add 'atom-text-editor', 'custom:clear-list', ->
  editor = atom.workspace.getActiveTextEditor()
  regMatch = /^(\*|#|1\.)\s*/
  editor.scan(regMatch, (match) -> match.replace(""))

I spotted a few things wrong with your code, and I’ll try to explain them below.

  1. You were attempting to create a regex using strings. That’ll work, but you would need to double-escape everything. It’s much cleaner to just create it using the regex syntax anyway.
  2. 1. in regex means “1, followed by anything except a newline”. Meaning 18 would match - probably not what you intended. 1\. properly escapes the dot.
  3. editor.scan requires a function callback, but you were providing it with a statement (namely, the empty string returned by text.replace("")).

I haven’t tested my version, but hopefully if it doesn’t work it’ll be close enough that you’ll be able to fix it.


Error newText.replace is not a function (in a command)
#3

Sort of works… It removes 1st list element it encounters and… that’s it. I still don’t understand how the buffer object works so I don’t get what counts as the “buffer” when ::scan us called. When I switched to

editor.backwardsScanInBufferRange(regMatch, [[0, 1], [5, 3]], (match) -> match.replace(""))

with explicitly stated range it removes just the last list marker. I can’t even make a loop because I don’t know the object I’m to iterate over.


#4

Try regMatch = /^(\*|#|1\.)\s*/g To make the regex match a global search
Or if you want it to match any number, use regMatch = /^(\*|#|\d+\.)\s*/g


#5

I would like to plug Regex101 here as a great tool for rapid development of regular expressions.


#6

thanks to Wliu I found the regex syntax which is:

findThat= /my-regex-here/
editor.scan(findThat, (match) -> match.replace("")


#7

Thanks to your answer I wrote this command which replaces the double blank lines by a single one.

But it triggers: TypeError: newText.replace is not a function any idea why?

atom.commands.add 'atom-text-editor', 'custom:double-blank-line', ->
  editor = atom.workspace.getActiveTextEditor()
  regMatch=/^(\s*\n){2,}/  #regex must be enclosed /btw/
  regReplace=/\n/
  editor.scan(regMatch, (match) -> match.replace(regReplace))
  alert('done!')

ps: To run it from the context menu, I also added

atom.contextMenu.add {
    "atom-text-editor": [{
      label: "test"
      submenu: [
        {label: 'remove double blank line'
        command:'custom:double-blank-line'}
      ]
    }]
  }

#8

Try {replace} -> replace("\\n") instead.


#9

Thanks Aerijo but I’m not sure to understand. I tried the code below but it doesn’t work. It replaces the double blank lines by \n (literally)

atom.commands.add 'atom-text-editor', 'custom:double-blank-line', ->
  editor = atom.workspace.getActiveTextEditor()
  regMatch=/^(\s*\n){2,}/  #regex must be enclosed /btw/
  editor.scan(regMatch, (match) -> match.replace("\\n"))

ps: (and another issue, it doesn’t replace all instances, but only the first it finds)


#10

OK, I actually tested this one* :slight_smile:. No more wrong escapes.

atom.commands.add 'atom-text-editor', 'custom:double-blank-line', ->
  buffer = atom.workspace.getActiveTextEditor().getBuffer()
  regMatch = /^(?:\s*\n){2,}/g  #regex must be enclosed /btw/
  buffer.backwardsScan(regMatch, (match) -> match.replace("\n"))

Basically, the replace part was tripping up the rest of the search. Doing it backwards solves everything, and is actually hinted at in the docs.

*well, I tested a JavaScript equivalent. No promises I haven’t made a CoffeeScript typo.


Error newText.replace is not a function (in a command)
#11

It works thanks a lot for your patience! (And for the g flag!)


#12

I should point out we’re using the TextBuffer now, instead of the TextEditor. It makes no functional difference to the code above, but they have different properties. Using the variable name editor might be misleading, so I’ve changed it to buffer in my solution.