Reply to topic  [ 14 posts ] 
Move Comments to Marked Text (in one swell foop)—Solved 
Author Message

Joined: 2007-11-09 15:27:25
Posts: 86
Hi,
I have a simple macro like this:

Code:
Go to Next Comment
Copy
Go to Marked Text
Paste


This works fine, but of course it works for only one Comment at a time.

Edit: I had asked how to make this work for all Comments at once.

By stealing from http://www.nisus.com/forum/viewtopic.php?f=17&t=3955 I got what I was after.

This is what I ended up with:

Code:
Select Document Start
$count = 0

While true
   # move to the next comment, detecting if no more comments
   $previousSelection = TextSelection.active
   Menu 'Go to Next Comment'
   $selection = TextSelection.active
   If $selection.range == $previousSelection.range
      Break
   End
   
Menu 'Copy'
Menu 'Go to Marked Text’
Menu ‘Paste’
End


It does what I wanted, but perhaps it's stupid. If so, do let me know.

Thanks.


2013-09-29 16:29:26
Profile
User avatar

Joined: 2007-02-07 00:58:12
Posts: 876
Location: Japan
jb wrote:
Hi,
I’ve got a simple macro like this:

Code:
Go to Next Comment
Copy
Go to Marked Text
Paste


This works fine, but of course it works for only one Comment at a time.
How can I turn this into a macro that will replace each instance of marked text with the contents of the Comment linked to it?
I guess it has to do with ‘While’ or ‘For’ or something, but I’ve never quite grasped how those work.

I suppose it's pretty simple ;-)


I guess, it depends on how you define simple…

One way to make a loop is indeed to use:
Code:
while (condition)

end

So you need a condition that will be true until you are finished. Looking in the Nisus Macro Reference (in the Help menu) you will find the following under Comment Object.
Document.allComments which gives you an array with all the comments in the document. You could use this like this:

Code:
$doc = Document.active
while $doc.allComments.count  > 0

end


So combining this with your code you get:

Code:
$doc = Document.active
while $doc.allComments.count > 0
Go to Next Comment
Copy
Go to Marked Text
Paste
end

And this works.

There are more "elegant" ways to solve this problem, but elegance is in the eye of the beholder.

_________________
philip


2013-09-29 19:00:34
Profile
User avatar

Joined: 2007-02-07 00:58:12
Posts: 876
Location: Japan
For what it's worth here is my more terse version:

Code:
$doc = Document.active
$comments = $doc.allComments

$docText = $doc.text
foreach $comment in reversed $comments
$docText.replaceInRange $comment.markedTextRange, $comment.text
end

_________________
philip


2013-09-29 19:03:53
Profile

Joined: 2007-11-09 15:27:25
Posts: 86
Thank you two times, Philip.

It was this that I was trying to arrive at:

while $doc.allComments.count > 0

I'll see if I can figure out your more terse version, particularly 'in reversed'

Thanks again for your help.


2013-09-30 03:46:57
Profile
User avatar

Joined: 2007-02-07 00:58:12
Posts: 876
Location: Japan
jb wrote:
I'll see if I can figure out your more terse version, particularly 'in reversed'


A selection is basically a pair of numbers measuring the offset from the beginning of the file. If you edit an array of selections, then editing a previous one brings with it the danger of changing the location of following selections. To avoid this problem, it's best to work "back-to-front", so in reversed order.

Hope that helps.

_________________
philip


2013-09-30 07:08:42
Profile

Joined: 2007-11-09 15:27:25
Posts: 86
Hi Philip,
Thanks for the explanation.
‘Reversed’ is very clever.

Not so long ago someone asked on the forum how to learn the Nisus macro language, and Martin replied that there’s not really a good way, aside from asking (!).
So I'm hoping that you—or Martin—will indulge me in asking a couple of basic questions.

As for the terse macro, I’m wondering about two things.

1) Are these two assignments necessary or do they just make things easier to read? Perhaps one can have too many periods?

$comments = $doc.allComments
$docText = $doc.text


2) It seems that there is a convention for the use of singular and plural, and I’m wondering just how far it goes. Your terse macro defines ‘$comments’ (pl) but then uses ‘$comment’ (sg) in the foreach statement. Of course this is perfectly understandable in English and it's not hard to see that we do want to distinguish singular and plural. But I didn't realize that the macro language was so intelligent. Can the same be done with, say, Notes, Bookmarks, CrossReferences, etc.? Perhaps there’s something about this in the Macro Reference, but I don’t find it.

Enough for now.
Thanks again.


2013-09-30 15:19:44
Profile
User avatar

Joined: 2007-02-07 00:58:12
Posts: 876
Location: Japan
jb wrote:
1) Are these two assignments necessary or do they just make things easier to read? Perhaps one can have too many periods?

$comments = $doc.allComments
$docText = $doc.text


Yes, you could make the terse version even terser:
Code:
$doc = Document.active

foreach $comment in reversed $doc.allComments
$doc.text.replaceInRange $comment.markedTextRange, $comment.text
end


The basic rule seems to be that the first assignment--the one that instantiates the main object--is necessary, but other assignments are for convenience. That's why all macros start with:
Code:
$doc = Document.active


I tend to like to create as few variables as possible (and use periods instead), while the example macros from Nisussoft often have lots of assignments. There is something to be said for either approach.


jb wrote:
2) It seems that there is a convention for the use of singular and plural, and I’m wondering just how far it goes. Your terse macro defines ‘$comments’ (pl) but then uses ‘$comment’ (sg) in the foreach statement. Of course this is perfectly understandable in English and it's not hard to see that we do want to distinguish singular and plural. But I didn't realize that the macro language was so intelligent. Can the same be done with, say, Notes, Bookmarks, CrossReferences, etc.? Perhaps there’s something about this in the Macro Reference, but I don’t find it.

Well in fact the macro language is not that intelligent. I could have written:

Code:
$doc = Document.active
$comment = $doc.allComments
foreach $comments in reversed $comment
$doc.text.replaceInRange $comments.markedTextRange, $comments.text
end


and that would work just as well. But it would be harder to read.

The crucial thing about foreach is that the thing after in be an array variable. The name before the in is a loop variable; it exists only inside the loop construct, and you can use any name you like, but sensibly you want to use something that makes clear you are using one item from the array. The singular/plural scheme (and generally using plurals for array variables) is a pretty common set up, that is also used by Java programmers, etc.

And you can use this construct with any of the many array (of object) variables that the Nisus macro language has. So

Code:
$doc = Document.active
$notes = $doc.allNotes
foreach $note in $notes


Code:
$doc = Document.active
$bookmarks = $doc.allBookmarks
foreach $bookmark in $bookmarks


And in fact you can use the period method if you prefer:

Code:
$doc = Document.active
foreach $bookmark in $doc.allBookmarks


Best
Philip

_________________
philip


2013-09-30 17:01:02
Profile

Joined: 2007-11-09 15:27:25
Posts: 86
Philip,
This is really helpful.
I made a silly experiment concerning sing/pl to see that, as you wrote, you can use any name you like for the loop variable.

I changed ‘$comment’ to ‘$pear’ and it works just as well:

Code:
$doc = Document.active
$comments = $doc.allComments
$docText = $doc.text
foreach $pear in reversed $comments
$docText.replaceInRange $pear.markedTextRange, $pear.text
end


It’s hard (for me) to switch off the familiar semantics (which is why I really shouldn’t use ‘pear’)—especially since sometimes (Document.active, allComments, text) ordinary meanings are used.

Next I’ll see if I can understand Range.

Thanks again.
James


2013-10-01 05:01:12
Profile
Official Nisus Person
User avatar

Joined: 2002-07-11 17:14:10
Posts: 4251
Location: San Diego, CA
jb wrote:
It’s hard (for me) to switch off the familiar semantics (which is why I really shouldn’t use ‘pear’)—especially since sometimes (Document.active, allComments, text) ordinary meanings are used.

I think using appropriately named variables is very important, even if the macro language itself is oblivious to those you choose. It's hard to read and understand code that uses poor or illogical variable names.

Quote:
Next I’ll see if I can understand Range.

It's a pretty simple object. It really only keeps track of two things: a location and a length, both stored as regular numbers. These numbers behave just as you'd expect, holding whatever value they were assigned upon creation, regardless of what happens in the future to the elements being described.

For example, in the text "Hi James" let's say we want to describe where the word "James" resides using a Range. If we take the location (index) of the first character "H" to be zero, then the letter "J" has a location of 3. The word "James" is 5 character long, so the range is: location=3, length=5, abbreviated for convenience as (3,5). You can get such a range in the macro language in a variety of ways:
Code:
# construct range explicitly, using hard-coded literal values
$range = Range.new(3, 5)

# make the same range, but using a command that searches text:
$text = "Hi James!"
$range = $text.rangeOfString("James")

Either of those give you a range (3, 5).

The thing to remember is that the range's values do not change unless you explicitly tell them to. So even if we replace the word in the text, the range is still the same, eg:
Code:
$text = "Hi James!"
$range = $text.rangeOfString("James")   # range is (3,5)
$text.findAndReplace("Hi", "Bye")       # word "James" is now at location of 4
# the variable $range is still (3,5) even though the word "James" has shifted location

So we can see that a Range is just a pair of plain-old numbers understood to have a specific meaning. Remember that ranges can describe any kind of elements: characters in text, rows in a table, people in a line, etc. From the macro language reference:
Quote:
A Range object describes a contiguous block of elements in some larger context. For example, a range of characters is a block of characters that are all adjacent to one another in the same document. However, such a range only describes the location of these character, it does not contain the actual characters.

A range can describe any kind of element, such as indexes in an array or rows in a table. The type of element being described by a range is irrelevant, the only properties a range maintains are the indexes (integers) of the elements in the larger context. This means that you can use the exact same range object to access different types of elements if you like.

Hopefully that makes some sense.


2013-10-01 14:14:22
Profile WWW

Joined: 2007-11-09 15:27:25
Posts: 86
Thank you, Martin. That’s very helpful. And yes it makes sense!

I saw the .markedText property for Comment Objects, so I thought that I should be able simply to ‘replace’ $comment.markedText with $comment.text. But apparently the only way to do that is with the .replaceInRange command, since there is no .replace command available for a Text object. I’m sure there’s a very good reason for that.
Also, I didn’t realize that there are no commands for Comment Objects.
Which objects do take commands, I wonder. I’ll go see if I can figure it out.
Thanks,
James


2013-10-02 15:22:59
Profile
Official Nisus Person
User avatar

Joined: 2002-07-11 17:14:10
Posts: 4251
Location: San Diego, CA
jb wrote:
But apparently the only way to do that is with the .replaceInRange command, since there is no .replace command available for a Text object. I’m sure there’s a very good reason for that.

Not necessarily! I mean, which commands are available isn't arbitrary, but they are added based on priority/need. Most of the time with text you're going to be replacing some part of a text object, hence the "replaceInRange" command. This hypothetical "replace" command would (I assume) replace the contents of the entire text object, which is needed much less often. That's still possible using the "replaceInRange" command, just a little more work, so the need for the wholesale replacement command is low.

If you ever feel there's a gap, or something that's too inconvenient to code, please let us know and we can evaluate the need for an additional macro command(s) to make some task easier for macro authors.

Quote:
Also, I didn’t realize that there are no commands for Comment Objects.
Which objects do take commands, I wonder. I’ll go see if I can figure it out.

While it's true that the Comment object does not technically have any commands, there are plenty of properties to play with that allow a macro to manipulate the comment. What commands would you want a comment object to respond to? Or perhaps the better question: what would you want a macro to be able to do with comments that is currently not possible?


2013-10-02 15:44:18
Profile WWW
User avatar

Joined: 2007-02-07 00:58:12
Posts: 876
Location: Japan
jb wrote:
I saw the .markedText property for Comment Objects, so I thought that I should be able simply to ‘replace’ $comment.markedText with $comment.text.

The thing here is you have to understand what is meant by a text object. $comment.markedText refers to the text object to which the text is attached. And text object means the whole text object. Try this:
Code:
$doc = Document.active
$comment = $doc.allComments[0]
prompt $comment.markedText

As you'll see, what you get is the whole text of the entire document. In fact it's really the whole object--all the footnotes, tables, styles, comments, bookmarks, etc. If you replaced that with $comment.text you wouldn't have much of a document left.

This takes some getting used to, but a selection object in Nisus macro language consists of two things (1) a range and (2) a text object. The text object is always the whole thing and the range is in relation to that object and describes the part out of it that is selected.
Anyhow, that is why you have to "replaceInRange".

_________________
philip


2013-10-02 17:26:41
Profile
User avatar

Joined: 2007-02-07 00:58:12
Posts: 876
Location: Japan
And something Martin said earlier:
martin wrote:
jb wrote:
It’s hard (for me) to switch off the familiar semantics (which is why I really shouldn’t use ‘pear’)—especially since sometimes (Document.active, allComments, text) ordinary meanings are used.

I think using appropriately named variables is very important, even if the macro language itself is oblivious to those you choose. It's hard to read and understand code that uses poor or illogical variable names.

I completely agree. But it is also a worthwhile experiment to sometimes use different variable names, just to at least confirm to yourself what is crucial. The object types and property names are determined by Nisus and have to be used as given, but the variable names are determined by you. They should be chosen in a sensible manner that helps you keep track of what you are doing.

_________________
philip


2013-10-02 17:35:36
Profile

Joined: 2007-11-09 15:27:25
Posts: 86
@Martin
Thanks for this, too.
But you give me too much credit ;-)
I intended to indicate only my ignorance. My presumption that I could invoke a command to act on the Comment Object ‘directly’ was just a sign of how little I understand. Approaching the Comment Object as part of the text makes perfect sense. And at the moment there’s nothing I want/need that the macro language can’t do.
Now that I see that — as Philip’s macro makes clear — the text object is the whole text, it’s easy to see that the ‘replace’ command would replace the entire text. Not so handy.
I’m looking into which objects take commands and which don’t. It's not so simple (for me) to figure out/understand. More on that later.

@Philip
Thanks a lot for this macro. It's great for a simpleton like me to have this kind of demonstration.

James


2013-10-03 06:07:40
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 14 posts ] 

Who is online

Users browsing this forum: Bing [Bot] and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software