undefined docpath

Get help using and writing Nisus Writer Pro macros.
Post Reply
js
Posts: 229
Joined: 2007-04-12 14:59:36

undefined docpath

Post by js » 2015-09-21 09:21:46

I can‘t get to grips with a seemingly simple macro problem:
I want Nisus to check if a certain document myDoc is open, and if not, to open or to activate it. I wrote this:

Code: Select all

$docPath =  "~/Documents/myDoc"
$actDoc = Document.active
$actDocPath = $actDoc.filePath
if $actDoc == undefined
   Document.open $docPath
elsif $actDocPath !=  $docPath
   Document.open $docPath
end
The problem is: This works well, if myDoc is not open, or if it is not active. But if no document at all is open Nisus complains that the filepath property requires an object, but get‘s „undefined“ . But that is just why I want to open it: that it is not undefined anymore.

BTW My if-construction is meant to say: (If A) OR (if B) then do C. Is there another way to this?

User avatar
martin
Official Nisus Person
Posts: 4261
Joined: 2002-07-11 17:14:10
Location: San Diego, CA
Contact:

Re: undefined docpath

Post by martin » 2015-09-21 13:52:23

js wrote:But if no document at all is open Nisus complains that the filepath property requires an object, but get‘s „undefined“ .
You see that error message because (as you say) there's no document open. Without a document object, you cannot access its "filePath" property.

The solution is to check if a document is open before trying to get its file path, eg:

Code: Select all

$actDoc = Document.active
If $actDoc
	$actDocPath = $actDoc.filePath
	...
End
You could omit the temporary path variable, resulting in a macro like this:

Code: Select all

$docPath =  "~/Desktop/test.rtf"
$actDoc = Document.active
If ! $actDoc
	Document.open $docPath
ElseIf $actDoc.filePath != $docPath
	Document.open $docPath
End
Of course, as the (somewhat gruesome) saying goes, there's more than one way to skin a cat. Let's consider your other question:
My if-construction is meant to say: (If A) OR (if B) then do C. Is there another way to this?
Hrm, I was going to say that you could do this, but there's a problem. While you can use an OR operator to include two conditions in a single "if" statement, you run into the same problem with the undefined Document object. For example, this works correctly:

Code: Select all

$number = 999
If ($number < 1) || ($number > 100)
	Prompt "The number $number is not from 1-100."
End
However, in your case this does NOT work properly:

Code: Select all

$docPath =  "~/Desktop/test.rtf"
$actDoc = Document.active
If (! $actDoc) || ($actDoc.filePath != $docPath) # FAILS: possible undefined object
	Document.open $docPath
End
The problem is that the || OR operator isn't doing something that's called short-circuit evaluation. This is a common feature of programming language's logical operators, where subsequent expressions are only evaluated as required based on the value of earlier expressions. I found the lack of this feature in the Nisus macro language somewhat surprising, but I see that the macro reference doesn't make any claims to support it. I'll have to file an enhancement.

If you really want a single code path (a good programming habit) for opening the desired document, you can do so if you contort your code a little:

Code: Select all

$docPath =  "~/Desktop/test.rtf"
$actDoc = Document.active

$actDocPath = ""
If $actDoc
	$actDocPath = $actDoc.filePath
End

If $actDocPath != $docPath
	Document.open $docPath
End
I hope that helps.

js
Posts: 229
Joined: 2007-04-12 14:59:36

Re: undefined docpath

Post by js » 2015-09-22 11:05:22

Thank you for the generous explanations. This works well for me.
Now I had sent only the first half of my macro. But unfortunately I find myself with quite a similar problem with it’s second half, which let's me stuck again. My original macro (with your help) is this:

Code: Select all

$docPath =  "~/Documents/test.rtf"
$actDoc = Document.active
If ! $actDoc
   Document.open $docPath
ElseIf $actDoc.filePath != $docPath
   Document.open $docPath
End
That works fine. It continues like this:

Code: Select all

$sel = $actDoc.textSelection
$parStyle = $sel.paragraphStyle
$parName = $parStyle.name
If $parName == "myPARNAME"
execute myCODE
end
The purpose of this part 2 of the macro is to check the front document just opened, and see if the insertion is in paragraph with the Style myPARNAME, in which case it should execute myCODE.
But if I start out with no document open the macro complains: „The 'textSelection' property requires an object, bot found 'undefined' from '$actDoc'.“ Why is that? Part 1 of the macro was meant to make sure that my document is open and in front? How can it be 'undefined'?

User avatar
phspaelti
Posts: 912
Joined: 2007-02-07 00:58:12
Location: Japan

Re: undefined docpath

Post by phspaelti » 2015-09-22 14:21:00

js wrote: The purpose of this part 2 of the macro is to check the front document just opened, and see if the insertion is in paragraph with the Style myPARNAME, in which case it should execute myCODE.
But if I start out with no document open the macro complains: „The 'textSelection' property requires an object, bot found 'undefined' from '$actDoc'.“ Why is that? Part 1 of the macro was meant to make sure that my document is open and in front? How can it be 'undefined'?
Because you set $actDoc first, before you check anything. The way you have coded it $actDoc 'preserves' the status quo at the start of the macro, i.e., the document that was open when you ran the macro.

Looking at what you're trying to do I'm wondering why you're doing all this. It seems you want your macro to work with "~/Documents/test.rtf" no matter what document is open at the time (if any). If that is true, dispense with all the code Martin sent and just do:

Code: Select all

$actDoc = Document.open "~/Documents/test.rtf"
This one line will:
  1. Guarantee that "~/Documents/test.rtf" is open
  2. Guarantee that "~/Documents/test.rtf" is frontmost and active
  3. Set $actDoc to "~/Documents/test.rtf"
If you also want to keep the previously active document (if any), just precede that one line with:

Code: Select all

$previouslyActiveDoc = Document.active
But remember that this variable will possibly be 'undefined' so you will have to test it before use.
philip

js
Posts: 229
Joined: 2007-04-12 14:59:36

Re: undefined docpath

Post by js » 2015-09-23 00:41:27

Thank you. This sounds very convincing,and finally my complete macro works fine.

js
Posts: 229
Joined: 2007-04-12 14:59:36

Re: undefined docpath

Post by js » 2015-09-23 02:28:14

Hopefully not boring anybody with a supplementary question. Please consider this code:

Code: Select all

$docPath =  "~/Documents/myFile"
$actDoc = Document.active
If ! $actDoc # if no file is active (open) at all ...
   Document.open $docPath # open myFile and exit
   exit
 elseIf $actDoc.filePath != $docPath  # if the active file is not myFile ...
   Document.open $docPath # open myFile and exit
   exit
end

prompt “Who cares for your code! I do what _I_ want!”
The first conditions checks if a file is active at all; the second if the active file is myFile.
In both cases I want the macro to open myFile and exit.

Now myFile is sure to be open and active. Therefore neither condition 1 nor condition 2 is true. Therefore running the macro again I should see the prompt, shouldn't I? But I don't.
Of course the ultimate goal is not to see the prompt, but to do something with my file, in case it is open and active. (But do nothing with it in case it had just to be opened or activated).

User avatar
phspaelti
Posts: 912
Joined: 2007-02-07 00:58:12
Location: Japan

Re: undefined docpath

Post by phspaelti » 2015-09-23 05:49:38

You can test what the problem is by using a prompt statement after the test to see what Nisus is testing:

Code: Select all

elseIf $actDoc.filePath != $docPath  
    prompt $actDoc.filePath & ' != ' & $docPath  
If you do this, you will see that the problem is fairly trivial. $actDoc.filePath will be a full/expanded file path which starts with '/Users/…', while the $docPath is as written in the code. If you compare with $docPath.expandedFilePath instead, the code will work as you expected.
philip

js
Posts: 229
Joined: 2007-04-12 14:59:36

Re: undefined docpath

Post by js » 2015-09-23 08:47:36

Thank you. That works fine now.

Maybe I should add this: After reading your explanation and realizing my mistake, I thought instead of changing the code from '$docPath' into '$docPath.expandedFilePath', I could alternatively enter the full form of the path of myFile. This did not work. I think the reason is the new sandboxing environment. From the complaint of the macro it turned out that myDoc does not in "Users/documents/myDoc", but in "~/Library/Containers/com.nisus.NisusWriter/Data/" followed by "Users/documents/myDoc". The user does not know this, but the macro does, and complains. Pleas correct if I am wrong.

User avatar
phspaelti
Posts: 912
Joined: 2007-02-07 00:58:12
Location: Japan

Re: undefined docpath

Post by phspaelti » 2015-09-23 22:05:26

Actually '$docPath.expandedFilePath' worked fine for me, but it might depend on the actual location of the file. I don't think your problem has anything to do with sandboxing. It looks like some kind of alias issue.

In order to check if two file paths point to the same you might want to use the following command instead:

Code: Select all

File.isEquivalentPaths path, otherPath
However the manual states:
Some other path normalizations are handled, like removing empty path components, but this does not include resolving aliases or symlinks in the path
so this might still not work.
philip

User avatar
martin
Official Nisus Person
Posts: 4261
Joined: 2002-07-11 17:14:10
Location: San Diego, CA
Contact:

Re: undefined docpath

Post by martin » 2015-09-30 10:49:37

js wrote:I think the reason is the new sandboxing environment. From the complaint of the macro it turned out that myDoc does not in "Users/documents/myDoc", but in "~/Library/Containers/com.nisus.NisusWriter/Data/" followed by "Users/documents/myDoc".
That is actually a side effect of sandboxing. I'm not sure why, but when OSX grants NWP access to some file outside its sandbox (eg: a normal user document) the system creates those kind of "shadow" files in NWP's sandbox container. It's probably some implementation detail that lets OSX keep track of which files NWP should have access to.

In any case, Philip's suggestion to use "File.isEquivalentPaths" is the best way to compare paths. It should correctly handle situations like this where one path is inside the sandbox and another is outside, but both resolve to the same file on disk.

Post Reply