1

I am using gVim on Windows. I have an XML file with content (extracted) like this:

      <ExtendedAttributes />
    </Activity>
    <Activity Id="ded54c70-1ef4-4aeb-852e-3740882c36ff" Name="Activity 1">
      <Performers>
        <Performer>2212646c-2674-4329-9ddc-1f8376e952e1</Performer>
      </Performers>
    </Activity>
    <Activity Id="cf70ec72-4d49-434a-abf3-aa3e8dc000b5" Name="Activity 2">
      <Description>This is a dummy description</Description>
      <Implementation>
        <Task />

I want to add a ID before the name of each activity, i.e.

<Activity Id="ded54c70-1ef4-4aeb-852e-3740882c36ff" Name="Activity 1">

will become

<Activity Id="ded54c70-1ef4-4aeb-852e-3740882c36ff" Name="10 Activity 1">

and

<Activity Id="cf70ec72-4d49-434a-abf3-aa3e8dc000b5" Name="Activity 2">

will become

<Activity Id="cf70ec72-4d49-434a-abf3-aa3e8dc000b5" Name="20 Activity 2">

and so on. I tried \(<Activity Id="[0-9a-f-]*" Name="\)\@<=[A-Za-z ,]\+ which can get the value of the "Name" attribute. But when I try to use the command :let i=0 | g/.... | let i=i+1 mentioned in http://vim.wikia.com/wiki/Making_a_list_of_numbers , I can never make it works. All I can do is to replace the names with the serial number (while I want the [serial number] [name] instead )

Could anyone shed some light?
Thank you very much.

Oliver

[edit]

I tried the followings:

:let @a=1 | %s/\(<Activity Id="[0-9a-f-]*" Name="\)\@<=\([A-Za-z() ,&?;/]\+\)/\=(@a+setreg('a',@a+1))/g

but it replaced the name with number only,

:let @a=1 | %s/\(<Activity Id="[0-9a-f-]*" Name="\)\@<=\([A-Za-z() ,&?;/]\+\)/\=(@a+setreg('a',@a+1)).\2/g

but I get "invalid expression",

:let @a=1 | %s/\(<Activity Id="[0-9a-f-]*" Name="\)\@<=\([A-Za-z() ,&?;/]\+\)/\=(@a+setreg('a',@a+1)).' '.\2/g

but I get "invalid expression",

:let @a=1 | %s/\(<Activity Id="[0-9a-f-]*" Name="\)\@<=\([A-Za-z() ,&?;/]\+\)/\=join((@a+setreg('a',@a+1)),' ',\2)/g

but I get "invalid argument for join()",

:let @a=1 | %s/\(<Activity Id="[0-9a-f-]*" Name="\)\@<=\([A-Za-z() ,&?;/]\+\)/\=(@a+setreg('a',@a+1))+\2/g

but I get "invalid expression".

romainl
  • 186,200
  • 21
  • 280
  • 313
Oliver Leung
  • 740
  • 5
  • 12
  • you should record a macro for one manipulation and the use it for all related lines (maybe by global where you can define the related lines per regex (for multiple files you could use something like bufdo in addition)) – snap Jan 29 '18 at 07:09
  • I added the commands I tried. Thank you. – Oliver Leung Jan 29 '18 at 08:06

3 Answers3

2

Assuming your desired ID is the number following Activity multiplied by 10 (that's what i understand from your example), this could be done with:

:%s/Activity \(\d\+\)/\=submatch(1) * 10 . " " . submatch(0)

Breakdown…

  • Perform the substitution on every line:

    :%s/...
    
  • Search for Activity, followed by a space, followed by one or more numbers, capture the numbers:

    .../Activity \(\d\+\)/...
    
  • Use the captured number and the whole match in a "sub-replace expression":

    .../\=submatch(1)*10 . " " . submatch(0)
    

    where we concatenate the captured number by 10, a space, and the whole match.

    See :help sub-replace.

romainl
  • 186,200
  • 21
  • 280
  • 313
  • Sorry. No, "Activity 1", "Activity 2" is just an example of activity names. It could be any ASCII character (which has no relation with the ID). Anyway, thank you. – Oliver Leung Jan 29 '18 at 08:55
  • 1
    How about giving us a real-life example? – romainl Jan 29 '18 at 09:00
  • the file extract is indeed the real file I am working on (except that I change the activity names to be shorter). It is a process document in xpdl format. Since my process modeler does not able to display the activity ID in the diagram, I'd want to change the activity name to include the ID (so to match with the process documentation). A complete file is quite long (~420 line for a process with 3 tasks only) and it is not a good idea to me to include here. – Oliver Leung Jan 31 '18 at 02:07
  • Oh! By combining submatch() with my "Let @a=1 |...", I managed to solve the problem in one step. `:let @a=1 | %s/\( – Oliver Leung Jan 31 '18 at 04:13
0

I finally figured out a way to do the task in 2 steps:

1) Insert a static string that are not likely to appear in the content (e.g. ##%%##%%) by the command:

:%s/\(<Activity Id="[0-9a-f-]*" Name="\)\@<=\([A-Za-z() ,&?;/]\+\)/##%%##%% \2/g

2) Replace the static string with number sequence:

:let @a=1 | %s/##%%##%%/\=(@a+setreg('a',@a+1))/g

It is not a perfect solution, indeed. Any solution for 1 step only are welcomed. Thank you very much.

Oliver Leung
  • 740
  • 5
  • 12
0

This one changes only at Activity that is at the end of the line. First, we try the search separately

/Name="\zs[^"]*\ze"

Then ask our beloved vim

let @a=10 | %s//\=(@a+setreg('a', @a+10)) . " " . submatch(0)
SergioAraujo
  • 11,069
  • 3
  • 50
  • 40