Ordered Pair Deletion

Get help using and writing Nisus Writer Pro macros.
Post Reply
adryan
Posts: 603
Joined: 2014-02-08 12:57:03
Location: Australia

Ordered Pair Deletion

Post by adryan »

G'day, all

I thought I would try to devise a Macro for the task described, but I confess I was defeated by the Macro Language. For example, I could determine whether or not a target string was found in the text, but I only ended up with a boolean value: I couldn’t even see how to select the found text or make use of it in further operations. So some guidance would be much appreciated.

I have several paragraphs (not necessarily contiguous) of the following form:–

initial_string {{"stringX1", "stringY1"}, {"stringX2", "stringY2"}, {"stringX3", "stringY3"}}

So I basically have sets of ordered pairs.

The string designations here have no significance: the strings in the actual document could consist of any, and any number of, characters. Furthermore, the set in any given paragraph could contain any number of ordered pairs — not necessarily three as shown above. By way of concrete example, each ordered pair might represent the author and title of a book, and the book sought might be leaving my library in the (probably futile) hope that the light of erudition it represents illuminates some hitherto unenlightened corner of the universe. You get the idea….

I also have a target string.

I want to search a given paragraph of the above form for the target string. I don’t want to search all such paragraphs. I might want to search just the third one, for example. And I want to be able to set this in advance in the Macro (ie, I don’t need a dialog box to get input).

More specifically, I want to search the nominated paragraph to see if the target string is contained in the second string of any of the ordered pairs. If it is, I want to delete that entire ordered pair from the set, while leaving the rest of the paragraph unchanged. The target string will appear at most once in the nominated paragraph.

Ideally, the Macro would work for ordered n-tuples, for any n ≥2, not just ordered pairs. And not necessarily looking at the second (or last) element of an n-tuple. (But I realize I’m being greedy here.)

Depending on where (if at all) the deletion occurs, some tidying up of the ordered pair separators (viz, “, ”) may be required, but I can handle this myself. As for the rest….

Cheers,
Adrian
MacBook Pro (M1 Pro, 2021)
macOS Ventura
Nisus Writer user since 1996
User avatar
phspaelti
Posts: 1345
Joined: 2007-02-07 00:58:12
Location: Japan

Re: Ordered Pair Deletion

Post by phspaelti »

Hi Adryan,
The task you describe seems like it would be (mostly) straightforward to solve with Find and Replace. Basically the task you describe is (in pseudo-code):

Code: Select all

Find and Replace    {  " ... " { n-1 times } " .. [target expression] .. " [any more stuff] }   ,  [nothing] 
Where "n" stands for the n-th element of the tuple.
NB: I have left out the complication of the delimiters for the elements of the tuple (something like "comma - space")
NB2: Note the confusing double use of {} here; one time as delimiter in your expression, and one time as numeric repeater in the find expression

The one special point is that you want to restrict this search to one paragraph (i.e., the "k-th" paragraph of the relevant type).

This last condition is the one thing that suggests use of Macro Language Find instead of Command Line Find. To review the difference:

Code: Select all

Command Line:  Find  and Replace $find_expression, $replace_expression, $options
Macro Language:  $text_object.findAndReplace $find_expression, $replace_expression, $options
The find expression can be restricted with a where expression that consists of a Text Selection object. You can get text selection objects easily using Macro Language Find. So the procedure of the macro will look like this:
  1. Get the text object
  2. Search for all relevant paragraphs and store the resulting selections in an array
  3. Construct a search expression meeting the restrictions described above
  4. Do a Find/Replace using the search expression from step 3, restricted to the (k-1)th element of the array from step 2
Partial code will look like this:

Code: Select all

$k = …
$n = …
$doc = Document.active
$paraSels = $doc.text.findAll … (search expression and options)
$searchExpr = @String% … %
$doc.text.findAndReplace $searchExpr, '', (options), $paraSels[$k - 1]
The rest is left as exercise for the reader :wink:
philip
adryan
Posts: 603
Joined: 2014-02-08 12:57:03
Location: Australia

Re: Ordered Pair Deletion

Post by adryan »

G'day, Phillip et al

Thanks for the prompt response, Phillip. I'll now repair to my ivory tower and study it.

Cheers,
Adrian
MacBook Pro (M1 Pro, 2021)
macOS Ventura
Nisus Writer user since 1996
adryan
Posts: 603
Joined: 2014-02-08 12:57:03
Location: Australia

Re: Ordered Pair Deletion

Post by adryan »

G'day, Phillip et al

I keep getting an error message: "Invalid option (>) given to find/replace command." I don't know why. Same thing whether I use 'E' or 'Ea'.

I'm really flying blind here. Without knowing how to make the result of a find operation stand out in some way (eg, selected, highlighted or text-colored), I don't really know where to start looking for errors. Is there some easy way to debug Macros, analogous to the Events and Replies listings in AppleScript's Script Editor?

Cheers,
Adrian
MacBook Pro (M1 Pro, 2021)
macOS Ventura
Nisus Writer user since 1996
User avatar
phspaelti
Posts: 1345
Joined: 2007-02-07 00:58:12
Location: Japan

Re: Ordered Pair Deletion

Post by phspaelti »

Hello Adrian,

I take it from your writing that you are trying to use the macro type of find and replace. The thing about that is that it will not show you anything, since it works directly on the object. It does not go through the visual interface and change the selection(s) in any way.

I don't think there are any debug commands that would be effective for this type of problem. What I recommend is starting simple.
So first:

Code: Select all

$doc = Document.active
$result = $doc.text.find 'Nisus'    # or any other relevant string
prompt $result
Then add some options

Code: Select all

$doc = Document.active
$result = $doc.text.find 'Nisus .{5}', 'E'
prompt $result.substring
Or

Code: Select all

$doc = Document.active
$results = $doc.text.find 'Nisus', 'Ea'
prompt $results.count
If you still have problems, paste your offending code.
And if any part of what I am writing here is unclear, let me know, and I'll try to explain.

HTH
Philip
philip
User avatar
phspaelti
Posts: 1345
Joined: 2007-02-07 00:58:12
Location: Japan

Re: Ordered Pair Deletion

Post by phspaelti »

adryan wrote: 2024-07-30 22:58:03 I keep getting an error message: "Invalid option (>) given to find/replace command." I don't know why. Same thing whether I use 'E' or 'Ea'.
Actually re-reading this, I think I know what is going on here.
Probably the search expression has some problem in the delimiter(s). For example you are looking for a string with quotes, and have used quotes as the delimiters. So when Nisus finds the quote in the expression it thinks it has reached the end and the rest looks like gibberish to the macro engine.

Here's an example:

Code: Select all

$text.find 'don't do that!', 'e'
Note the quote inside "don't". This will terminate the find expression, and so Nisus won't know what to to with the "t do that!". Your example is probably just more complicated so it's not as obvious.

But Nisus is telling you where the problem is: where in the line does it have a ">" character?
philip
adryan
Posts: 603
Joined: 2014-02-08 12:57:03
Location: Australia

Re: Ordered Pair Deletion

Post by adryan »

G'day, Phillip et al

First, thanks for your assistance with this, Phillip.

I had already discovered the Prompt command, but it needed to be used so often (at least for the way I was constructing the Macro) that it became too awkward. It occurs to me now that Macro development could benefit from the employment of an overarching debugging Macro that executes and reports the results of an actual Macro of interest. You would feed the nasceent Macro to the debugging Macro which would generate a document listing each command as it was executed, together with the result, akin to what is available in AppleScript. I imagine one might use the Type Text command instead of the Prompt command.

There may have been a problem with the syntax of the find expression, but not quite of the sort you mention. I belong to the old school of recommending the use of double (outermost) quotes in general writing, to avoid confusion between apostrophes and quotation marks, so I'm quite aware of the issue of premature string termination. But where things may have come unstuck is in my use of (paired) double quotes within the single quotes. I didn't think it would be a problem, but maybe the great Macro Language Taskmaster thought otherwise.

The only use of the ">" character in a find/replace command was as part of an "@Text<string>" construction, so I'm afraid I was none the wiser for that error message.

I thought I would use this project as a way of gaining more facility with the Macro Language than I currently possess. And already I think I have made some progress in this respect. Since it wasn't quite coming together for me, though, I have reverted to standard (non-object-oriented) Find & Replace techniques, with a view to getting something that works and that I might then recast in the object-oriented manner.

I've now replaced all the delimiters (braces and quotes) with a single delimiter, which simplifies things considerably. I can reconstruct the original syntax later. (It needs to be fed back into an AppleScript script where AppleScript's handling of lists as text proved intractable for my purpose; hence my appealing to NWP's superior text manipulation capability. This explains the origin of the braces.) The braces became particularly problematic because Find & Replace uses them as part of its Repeat N Times syntax. Having said that, I've since decided to use a loop construction instead.

I've got it all working for ordered pairs. Now I'm generalizing it to n-tuples. I'll report back when I'm done.

Cheers,
Adrian
MacBook Pro (M1 Pro, 2021)
macOS Ventura
Nisus Writer user since 1996
User avatar
phspaelti
Posts: 1345
Joined: 2007-02-07 00:58:12
Location: Japan

Re: Ordered Pair Deletion

Post by phspaelti »

Hello Adrian,
adryan wrote: 2024-08-01 15:04:31 There may have been a problem with the syntax of the find expression, but not quite of the sort you mention. I belong to the old school of recommending the use of double (outermost) quotes in general writing, to avoid confusion between apostrophes and quotation marks, so I'm quite aware of the issue of premature string termination. But where things may have come unstuck is in my use of (paired) double quotes within the single quotes. I didn't think it would be a problem, but maybe the great Macro Language Taskmaster thought otherwise.

The only use of the ">" character in a find/replace command was as part of an "@Text<string>" construction, so I'm afraid I was none the wiser for that error message.
Actually the problem is exactly what I meant. The thing about delimiters is, there are 3 kinds:
  • single quotes
  • double quotes
  • using @Text or @String you can use pretty much any character you want
Note that in the last case 'bracket' style characters come in left/right pairs
So in your case the delimiters being used were "<" and ">". Or maybe they were supposed to be, but weren't.

From what you write here—and knowing your contributions to this forum—I suspect you started the expression using macroize. The macroize gave you @Text< … > delimiters. Then you modfied the expression in such a way that a ">" ended up in the part of code that was supposed to be the find options.

Just to return to your thinking about the respective merits of single quotes, vs. double quotes, that is not really the best way to choose the right delimiter. The difference between the two types really depends on interpolation of special characters. With single quote, only single quote is treated in a special way. But with double quotes all matter of characters become imbued with special meaning, and you will have to use 'escapes' to return them back to the litteral meaning. So using double quotes can create all kinds of issues, if you are not careful. If you look at my contributions to this forum, you will see that I overwhelmingly prefer single quotes for that reason.
The second important difference between the two types of quotes is interpolation. Double quotes also allow you to interpolate variables. So the following code:

Code: Select all

$hello = 'goodbye'
Find "$hello"
will search the document for the string "goodbye", rather than "hello" following a dollar sign.

As you get used to things, actually the best option is to use the third type of delimiter, especially if you are trying to write a search expression for string containing lots of 'technical mark-up' (in your case braces, quotes, etc.). You might note that in my earlier comment I used @S% … %, because I thought the percentage symbol might be less likely to clash with other characters used in the search expression.
[By the way "@S" is short for "@String". "@String" is like "@Text" when you don't care about attributes.]
adryan wrote: 2024-08-01 15:04:31 I had already discovered the Prompt command, but it needed to be used so often (at least for the way I was constructing the Macro) that it became too awkward. It occurs to me now that Macro development could benefit from the employment of an overarching debugging Macro that executes and reports the results of an actual Macro of interest.
To which I would say "duh!" :)
Copious use of prompt is the traditional way to debug.

The way I write macros is "bit by bit". First I write a line that creates the $doc variable and I add "prompt $doc". Then I run it. When it reports the correct document object, I quote out the prompt. ("Quote out" means "prefix the line with "#" to make it a comment so it gets ignored by the macro.) Then I write the next bit, say a Find expression that finds the parts of the document I want to work with. I add a prompt and run. Check that it works correctly. Rinse and repeat.
By doing it this way you ensure that you have parts of your code that you know are bug free. If you leave the prompts in (and quote them out) you can easily go back and forth without it becoming too distracting.

Asking Nisus to write a full-on debugger might be a case of diminishing returns. It would probably be a big feature that few users would benefit from. I'm not against it, mind you. But my experience has always been that I find learning to use a debugger is itself a whole 'nother skill.
There are a host of debug related features. The most useful is "Run Selection as Macro" which you can use to test small bits of a macro, as long as you are careful to mind the variables. Aside from the time-honored prompt command, there are also the Debug.log command (and a few others) which allow you to check variables and other things easily.

HTH
philip
User avatar
phspaelti
Posts: 1345
Joined: 2007-02-07 00:58:12
Location: Japan

Re: Ordered Pair Deletion

Post by phspaelti »

Okay, Adrian, my bad :oops:

Having tried to flesh out the macro according to my own instructions, I see now that the $range variable which is supposed to act as limit on the Find triggers the incomprehensible message that you mentioned ("Invalid option (>) given to find/replace command." )

This looks like it might even be some kind of bug. I will have to investigate at how, or what is causing this, and/or how to fix it.

=========== Update ==============
Were you trying to use the $range limit on a regular (command line) Find by any chance?
That doesn't work.
The $range limit method works only on macro language .find
philip
User avatar
phspaelti
Posts: 1345
Joined: 2007-02-07 00:58:12
Location: Japan

Re: Ordered Pair Deletion

Post by phspaelti »

So here is my quick version of the macro I was suggesting.
I also add a test file.
Attachments
Test file for Adrian’s tuple.rtf
(19.84 KiB) Downloaded 383 times
Adrian_s Tuple Macro.nwm
(6.71 KiB) Downloaded 381 times
philip
adryan
Posts: 603
Joined: 2014-02-08 12:57:03
Location: Australia

Re: Ordered Pair Deletion

Post by adryan »

G'day, Phillip et al

Thanks for pointing me in the direction of the Debugging section of the Macro Language Reference, Phillip. But that's perhaps for some other time (says he ruefully). It may be that judicious use of that $doc.setSelection command may help.

Hard to be sure (because I haven't kept every single version I've tried), but I don't think I ever used the $range limit method in an ordinary Find & Replace command.

Thanks heaps for the Macro and test file. The way you've constructed the Macro makes it look so elegant. And, with a bit of work, I think I understand it all! I got a bit confused about the range business until I realized it was the range of an element of an array and not that of the entire array.

In the second find expression, what does "?:" signify? I don't think I've come across it before, and I couldn't find it in either the User Guide or the Macro Language Reference.

I amended your Macro to complete the job of deleting the unwanted tuple and any remaining redundant tuple separators. (See code box.)

Code: Select all

# Modified version of Phillip's Macro

# User variables
$target = '___'
$para_num = 4
$tuple_n = 2

# Macro variables
$n = $tuple_n - 1
$k = $para_num - 1

# Select the document
$doc = Document.active

# Locate the relevant paragraphs
$paraSels = $doc.text.find @S'^Initial {.+}', 'Ea'

# Delete the tuples containing the target string in the nth element
$range = $paraSels[$k].range
$doc.text.findAndReplace @S'{(?:"[^"]+", ){$n}"[^"]*$target[^"]*"[^}]*}', '', 'Ea', $range

# Delete redundant tuple separators
$doc.text.findAndReplace @S'{, ', '{', 'Ea', $range
$doc.text.findAndReplace @S', }', '}', 'Ea', $range
However, when I run it on your test file, it deletes the tuple but baulks at the final two commands, giving an error message such as: "The given character index (452) is out of bounds for the Text object (length 427)." I wondered whether the range of the paragraph needed to be ascertained once more, but the curious thing is that my version of the Macro works perfectly with my own test file whose form is identical with yours. (Which, incidentally, shows you have understood precisely the form of the text I am dealing with.)

Cheers,
Adrian
MacBook Pro (M1 Pro, 2021)
macOS Ventura
Nisus Writer user since 1996
adryan
Posts: 603
Joined: 2014-02-08 12:57:03
Location: Australia

Re: Ordered Pair Deletion

Post by adryan »

G'day, Phillip et al

I think I've figured out why my version of the Macro failed on your test file, Phillip. The difference between your file and the ones I've been using is that the content of my files extends beyond the last paragraph containing the tuples, whereas in yours it doesn't. Once more text is appended to your file, the Macro works. So it appears one needs to reevaluate the range before deleting the separators.

So here's my amended version:–

Code: Select all

# Modified version of Phillip's Macro

# User variables
$target = '___'
$para_num = 4
$tuple_n = 2

# Macro variables
$n = $tuple_n - 1
$k = $para_num - 1

# Select the document
$doc = Document.active

# Locate the relevant paragraphs
$paraSels = $doc.text.find @S'^Initial {.+}', 'Ea'

# Delete the tuples containing the target string in the nth element
$range = $paraSels[$k].range
$doc.text.findAndReplace @S'{(?:"[^"]+", ){$n}"[^"]*$target[^"]*"[^}]*}', '', 'Ea', $range

# Delete redundant tuple separators
# In general, the Replace operation alters the length of the paragraph, so the range needs to be reevaluated each time.

$paraSels = $doc.text.find @S'^Initial {.+}', 'Ea'
$range = $paraSels[$k].range
$doc.text.findAndReplace @S'{, ', '{', 'Ea', $range

$paraSels = $doc.text.find @S'^Initial {.+}', 'Ea'
$range = $paraSels[$k].range
$doc.text.findAndReplace @S', }', '}', 'Ea', $range
This seems to work with all our files.

Thanks again for your help, Phillip. I'll now be able to feed the result of this Macro back into my AppleScript script.

Cheers,
Adrian
MacBook Pro (M1 Pro, 2021)
macOS Ventura
Nisus Writer user since 1996
User avatar
phspaelti
Posts: 1345
Joined: 2007-02-07 00:58:12
Location: Japan

Re: Ordered Pair Deletion

Post by phspaelti »

Hi Adrian,
Glad to hear that you figured it out. It was because I didn't want to deal with the clean-up of the tuple delimiters that I left the macro with the target tuple selected :wink:
adryan wrote: 2024-08-02 03:12:36 In the second find expression, what does "?:" signify? I don't think I've come across it before, and I couldn't find it in either the User Guide or the Macro Language Reference.
The difference is between " ( … ) " and " (?: … ) ". The latter type of parenthesis do not get counted for match purposes. I tend to use them out of habit, and use the first type only when I actually want to match them later. I especially use the non-counted type when they are followed by a repeat symbol, as in this case, where the parentheses are followed by " {$n} ", i.e. "repeated n-times".

Or to put it differently, I use " (?: … ) " when the parentheses are for grouping purposes, rather than matching purposes.
philip
adryan
Posts: 603
Joined: 2014-02-08 12:57:03
Location: Australia

Re: Ordered Pair Deletion

Post by adryan »

G'day, Phillip et al

Gottit. Thanks.

Cheers,
Adrian
MacBook Pro (M1 Pro, 2021)
macOS Ventura
Nisus Writer user since 1996
Post Reply