How do i retain capture group in replace by regex in init.coffee?


#1

I’m trying to make a custom command to apply regex replaces in sequence. Some regex I have are to be replaced by their capture groups:

regs[1] = /;"([A-Z])/g
res[1]=";$1"
editor.scan(regs[1], (match) -> match.replace(res[1]))

Supposed to replace ;"Bug to ;Bug but what I get is ;$1ug since capture groups are not retained. Wrapping replace into regex as /;$1/g results in an error. So, is there a way to reference a capture group inside the scan method?


#2

Throw a console.log in there to drop match into the console.

editor.scan regs[1], (match) ->
        console.log(match)

You’ll find that the capture groups are remembered in an array. It’s easy to get access to them and add them to the inserted text.


#3

Hello.

How about an example (read: possibility) without giving away the exact answer?


orignial text…

89099
54755
11556
34158
87687

changed text…

8909.9
5475.5
1155.6
3415.8
8768.7

init.coffee…

atom.commands.add 'atom-text-editor', 'test command', ->
  _search = /(\d{4})(\d)/g
  _replace = '$1.$2'
  _editor = atom.workspace.getActiveTextEditor()
  _buffer = _editor.getText() # done only once
  # ++++ 
  # __using a normal built-in string 'replace' command__
  _buffer = _buffer.replace(_search, _replace)
  # ++++
  _editor.setText(_buffer)  # done only once

The above is one possibility. The line between the + is where the WHILE / FOR may be.

Disadvantages include…

  1. absorb the whole text into a singular string
    – lines are not split from one another (but it can be done)
    – memory consumption
  2. loose cursor position when all text replace with setText()

#4

Nice example!

Does the Atom API allow replacement in place
or
should the “match” object be used to point to the text position
and replace by something like editor.setTextInBufferRange()?


#5

Yes. That’s what the replace() option is for in scan().


#6

Okay… I see now. I have this now for my own example:

atom.commands.add 'atom-text-editor', 'test command', ->
  # Create an object for the active editor
  _editor = atom.workspace.getActiveTextEditor()
  # Mark start position for 'undo'
  _checkpoint = _editor.createCheckpoint()
  # Search term
  _search = /(\d{4})(\d)/g
  _editor.scan _search,  (_grab) ->
    _grab.replace("#{_grab.match[1]}.#{_grab.match[2]}") # <- how to set this in a programmed way?
  # Mark end position for 'undo'
  _editor.groupChangesSinceCheckpoint(_checkpoint)

To make the replace() configurable before entering the scan, does seem to be a challenge. Do we have something like a Python function of lambda?
:neutral_face:…{ See next post. }

@ephemeris: Something you might need… look at what _checkpoint is doing in the above example. It will allow you to ‘bundle’ all the actions on the file; when doing undo, all actions will be undone. Without it you will undo step-by-step.

Working with the buffer string (the example before) does not have this issue.


#7

Next iteration ->

atom.commands.add 'atom-text-editor', 'test command', ->
    # Create an object for the active editor
    _editor = atom.workspace.getActiveTextEditor()
    # Mark start position for 'undo'
    _checkpoint = _editor.createCheckpoint()

    # Search & replace strings
    _search = /(\d{4})(\d)/g
    _replace = "$1.$2"
  
    #Search & replace actions
    _editor.scan _search,  (_grab) ->
        _grab.replace(_grab.match[0].replace(_search,_replace))
  
    # Mark end position for 'undo'
    _editor.groupChangesSinceCheckpoint(_checkpoint)

…combining examples 1 and 2.


#9

that’s the one I’ve settled with - the only suggestion I completely understand what’s happening inside

Update: had to change a regex from /(^[A-Za-z\s]+;)/g to /(\n[A-Za-z\s]+;)/g to account for new lines in the _buffer variable. Otherwise everything seems to work.


#10

Thank you for your positive feedback.
If your files are too big or you need to revisit the solution,
I suggest you ask about my 3rd example.

Good catch!

Cheers.
dP