Tinderbox Forum

Recursively linkTo?

Hi, I know I can create links to child notes by expanding a note, selecting all descendants and using a linkTo(child) stamp.

I’d like to do that without first expanding the note, i.e. select only the parent note and use code that would recursively drill down and apply linkTo(child) to each note that got child notes.

Is this possible at all?

linkTo(child). The’ child’ designator implies the first child (by sibling order) of the current note. Do you perhaps mean to use ‘children’ to make a discrete link to each/every child?

Not as such, but if the notes shared a prototype, then the they could inherit an edict to run linkTo(chilren). BU, I’d remove that code once done as there’s no point in repating a task already done (which is why a stamp was a good idea in the original case).

Or, make an agent. In the agent, make $MyString a Displayed Attributes. The query is:

descendedFrom($MyString(agent))

and the action is:

linkToOriginal(children)

Now place the $Path of to top level note of the outline branch in the agent’s MyString. You will still neen to stamp the top level container to link to its children but all the deeper levels will be linked. (Tested in v8.9.2).

I seems you are making a mindmap of your TBX (imagine a an outline with one root item and it’s children drawn radialy instead of as a vertical list). What is the use case here?

Ah, yes what I want is children. Didn’t notice the difference as I just tried and it worked, so I didn’t look up child / children .

Interestingly both, linkTo(child) and linkTo(children), yield the same result over here which then isn’t correct I guess?

I’d like to view a note and its descendants in Hyperbolic view.

However, if a child is an alias I sometimes want to also include the original’s links, sometimes not. In the former case the note’s Hyperbolic view would stop at the alias, in the latter it would include links from the alias’ original.

Not necessarily.

I think that won’t work in this case because I’d like to have the two versions mentioned above that treat an alias differently. Should have mentioned that in my first post.

There’s no specific use case, I upgraded from a quite old Tinderbox version and now build a new starter file. My question was more of “am I missing a way to do it recursively”, using stamps works fine so I’ll just continue use them.

Thanks for showing me the alternatives!

While experimenting with temporarily setting Rule or Edict to linkTo(children) I found some unexpected behaviour. Code below is not complete but it shows what’s happening.

Using a stamp with code below on the parent note “notes”

  • this works as expected:

    $MyList=collect(descendants,$Path);
    
    $MyList.each(X) {
    	$MyString(X)=$Rule(X);
    	$MyBoolean(X)=$RuleDisabled(X);
    	$Rule(X)='linkTo(children)';
    	$RuleDisabled(X)=false
    };
    
  • but this sets the selected “notes” note’s MyList to executedRule:

    $MyList=collect(descendants,$Path);
    
    $MyList.each(X) {
    	$MyString(X)=$Rule(X);
    	$MyBoolean(X)=$RuleDisabled(X);
    	$MyList(X)=;
    	$Rule(X)='linkTo(children); $MyList="executedRule"';
    	$RuleDisabled(X)=false
    };
    

Don’t understand why a note’s Rule would act on another note. Any idea what’s going on here?

(I used MyList as I didn’t want to create a user attribute)

The second occurs because each note processed via the loop is instructed, via its rule, to set $MyList to the value “executedRule”. So it is doing what you asked it to.

Run on the “notes” note this

$MyList=collect(descendants,$Path);

sets the “notes” note’s $MyList to the paths of all its descendants:

/code und test/notes/a;/code und test/notes/a/a1;/code und test/notes/a/a2;/code und test/notes/a/a3;/code und test/notes/b;/code und test/notes/b/b1;/code und test/notes/b/b1/b1.1;/code und test/notes/b/b2;/code und test/notes/b/b2/b2.1;/code und test/notes/b/b3;/code und test/notes/b/b3/b2.2

Now, in my understanding this

$MyList.each(X)

would only iterate over the collected descendants paths, i.e. it won’t iterate over the selected "notes"note itself (as it is not included in the “notes” note’s $MyList).

But what I see is that this (the code from my last post):

$MyList=collect(descendants,$Path);

$MyList.each(X) {
	$MyString(X)=$Rule(X);
	$MyBoolean(X)=$RuleDisabled(X);
	$MyList(X)=;
	$Rule(X)='linkTo(children); $MyList="executedRule"';
	$RuleDisabled(X)=false
};

sets the “notes” note’s $MyList to executedRule

… although the “notes” note’s path isn’t included in the the “notes” note’s $MyList at the time $MyList.each is used.

So either I’m misunderstanding how .each works, or there’s something going wrong.

How can this code change the “notes” note’s $MyList if it doesn’t iterate over it (as it’s not included in the “notes” note’s $MyList)?

I’m setting each descendant’s $Rule, why does this rule act on another note without telling it explicitly to do so (by using e.g. $MyList(parent))?

(I apologize for not choosing a better name for “notes”, things like "notes" note's are probably a bit confusing)

Ah, you are setting $MyList, i.e. attribute MyList in “notes” when perhaps you meant to set $MyList(X)—being attribute MyList in note X?

Not sure :slight_smile:

I thought setting an attribute via a rule always sets the attribute of the same note that the rule belongs to.

If that’s true then there‘s something wrong with the code‘s result, or am I missing something?

Ah, I missed the single quotes in the rule-setting code. Do you have a small test doc that replicates the issue (and includes a note with $Text describing the problem)? It would make it easier to de-bug the issue.

In this line:

$Rule(X)='linkTo(children); $MyList="executedRule"';

are you wanting the $Rule of note X to be set to this code, containing two discrete actionx:

linkTo(children); $MyList="executedRule"

Or, split out for clarity:

linkTo(children);
$MyList="executedRule";

Separately, I’m confused as to the use of a rule here. You only need to to this task once. an edict would be a far less intrusive approach.

I’m also unclear as to the use of the ‘executedRule’ in a list. If you are wanting to make the rule as run then a boolean would make more sense, e.g. $MyBoolean. But, i’d not worry over adding user attributes, they are far less intrusive to the general doc than running unnecessary rules.

Assuming for a moment (pending a test doc) that your code works a better approach would be:

$MyList=collect(descendants,$Path);

$MyList.each(X) {
	$MyString(X)=$Rule(X);
	$MyBoolean(X)=$RuleDisabled(X);
	$MyList(X)=;
	$Edict(X)="if($MyBoolean==false){linkTo(children); $MyBoolean==true}"};
	$RuleDisabled(X)=false
};

So now each note X gets the $Edict (again split out into linres for clarity):

if($MyBoolean==false){
   linkTo(children);
   $MyBoolean==true
}

Once set the edict runs the linkTo() command and sets that note’s MyBoolean to true (i.e. ‘ticked’ when seen in Displayed Attributes an other parts of the UI). On second running of the edict, no action occurs as MyBoolean, having been set, no longer evaluates as false and so the code inside the if conditional is not run.

Also, if free, there is a Tinderbox meet-up today where more eyes can look at this.

Yes.

The (complete) idea was to temporarily set each note’s $Rule to linkTo(children) and to restore each note’s $Rule and $RuleDisabled afterwards. A rule because I thought if it’s possible at all then with a rule, not with an edict.

I wanted to use $MyList="executedRule" as a marker to tell whether the rule already fired and couldn’t use $MyBoolean as that was already used to hold the original $RuleDisabled state. Don’t want to create a user attribute just for this task.

However, it seems the behaviour of rules has changed since my previous Tinderbox version (8.0.?). If I recall correctly then changing a rule made the rule run immediately. Now, with Tinderbox 8.9.2 it seems rules behave more like edicts, i.e. they don’t fire immediately but one needs to select the note first. I know rules and edicts run on schedule, but if a rule doesn’t also fire immediately after changing it then my idea won’t work.

But I’m not sure whether that works or not - sometimes the links are visible immediately which is what I want, sometimes I need to select the deeper descendants first (which would destroy the idea of only selecting the parent note).

Here’s a test file

link stamp.tbx (88.1 KB)

PS what I still don’t get is why the selected “notes” note’s $MyList is set to executedRule - I just don’t see why this happens.

Sadly your Tinderbox opens as an empty file and opening in BBEdit it looks like you’ve possible saved it in a different format/encoding to the original. so still no reference file against which to work. So your P.S. can’t be investigated.

Meanwhile…

Why? There is no real overhead and if this task is important enough to need to work correctly, this seems like a shot in the foot. Adding an extra attribute has as-close-to-zero-as-nothing impact on your document. You are right that have one attribute do two different thing in two different contexts is storing trouble for later.

I don’t think it is a rule vs edict thing, but rather that you can’t set a rule, run it and void it all in one action code cycle as the last voids the action before it gets to runs.

But, Tinderbox need to know that rule as been set. I don’t believe it has ever been set/run in the same cycle. it may just be the speed of the execution of the action cycle that implies otherewise.

Without a test file it’s hard to replicate the scenario. Please check your test file opens in Tinderbox before it uploads. If it does, then we’ve gremlins attached to your upload method. :slight_smile:

Ahh, sorry! Updated the file above.

It’s not important, just would be nice if it would be possible. It’s more of an experiment whether temporarily using a rule or an edict would allow to act on nested notes. Meanwhile I think that won’t work at the moment, see the updated file and this post.

I hesitate to create a user attribute for this as it would probably irritate me in every new file - but I now figured I should simply prefix such “setup” user attributes with e.g. “sys” which would make it clearer.

Probably. I already tried this some years ago but can’t recall whether it worked or not. Maybe it’s simply not possible.

Meanwhile I’ve found the reason why the stamp below sets the selected “notes” note’s $MyList

$MyList=collect(descendants,$Path);

$MyList.each(X) {
	$MyString(X)=$Rule(X);
	$MyBoolean(X)=$RuleDisabled(X);
	$MyList(X)=;
	$Rule(X)='linkTo(children); $MyList="executedRule"';
	$RuleDisabled(X)=false
};

This line

$Rule(X)='linkTo(children); $MyList="executedRule"';

isn’t used as a whole to set the rule, instead Tinderbox stops after linkTo(children). So what the stamp actually does is setting the X's $Rule to linkTo(children) and then the following $MyList="executedRule" sets the “notes” notes $MyList to executedRule.

Rewritting it with a technique I learned from you works:

$Rule(X)="linkTo(children);"+" $MyList='executedRule'";

But that won’t help if the rule doesn’t fire immediately (or stops to work).

I’m curious whether the rule also stops to run automatically with your setup.

Jumping in super late here, but could this not be done much more simply with an agent? I’ve not tested this, but you could search inside the parent note, have the parent note name bit a variable for the linkTo action and have the action link all the results of the agent to the parent. Again, I’ve not tested this, but this could be simpler, no?

That was my thought too, having expended some time trying to find a rule /edict-based method.

In truth, given the outcome is for use in hyperbolic view, a cleaner approach—albeit via a new feature, would be modify Hyperbolic view to have an option to generate hierarchy-based links perhaps styled differently form others.

1 Like

Sometimes I’d like to restrict the Hyperbolic view to only include any aliases, but sometimes I’d like also include the aliases’ original links, so an agent won’t work. Thanks!

1 Like

That would be great. Ideally with a toggle that includes aliases or also the aliases’ original links.

1 Like