-
Notifications
You must be signed in to change notification settings - Fork 76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(label): add ZGLabel and ZGDocumentLabelManager implementations #83
Conversation
660abbb
to
b9e58df
Compare
Thank you for the PR. Left some small comments for now but will take a closer look and think about this more. |
I don't see any comments? Or am I stupid. |
They should show up as inline comments here on GitHub's review. |
I don't see any review, maybe it's still pending? As in, did you confirm the review? Not sure if that's required or not. But I just did a test review, and I do see those comments. |
@@ -201,12 +203,13 @@ - (BOOL)updateDynamicVariableAddress:(ZGVariable *)variable | |||
{ | |||
BOOL needsToReload = NO; | |||
ZGDocumentWindowController *windowController = _windowController; | |||
if (windowController != nil && variable.usesDynamicAddress && !variable.finishedEvaluatingDynamicAddress) | |||
if (windowController != nil && (variable.usesDynamicLabelAddress || variable.usesDynamicAddress) && !variable.finishedEvaluatingDynamicAddress) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is an edge case. If a label uses a dynamic pointer address, a variable that references this label is basically also potentially-changing, but the variable might think its address can't change and won't update it..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yes. That's definitely an interesting case. That can be solved if we assume that usesDynamicAddress
also included the usage of label('')
. Which makes sense, as it is dynamic.
EDIT:
Wait, come to think of it. I don't think that case is valid. Currently all labels are updated before any variable is updated. So once the variables that are actually pointing to this label are being updated, they already have access to the most recent address.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The potential problem is if the label's address changes and the finishedEvaluatingDynamicAddress
bit here. Once a variable's address is computed it's cached, unless this code detects it uses a pointer in the address formula. One (easy) way of fixing it (although not as 'efficient') is making any variable that references a label not use a cache so the address would have to be computed each time. Also the cache of all variables may need to be invalidated if a label is mutated or a label is removed or added or what not (if a cache is used).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe forbid a label from using a pointer.. but I don't know if that is a good idea. Relatedly I'm not sure if labels should be allowed to reference other labels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should forbid a label from using pointers. I also don't think we should forbid labels referencing other labels. That's a killer feature in my opinion.
I suggest we start tracking the dependant variables for each label. (e.g. add NSMutableArray * dependants
), and use this information to invalidate the dependant variable cache when the label has changed. Depending on whether or not the variable who was invalidated is actually visible, it'll be updated. If it's not visible, then the cache is still invalid but it won't be calculated until it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now if you want to make things easy I think we can disable caching when a variable uses a label; it's an optimization problem, and maybe very few variables will use labels in practice. But we can improve it later.
I'm not sure I'm following the idea of storing more state for dependents..
But another idea: when we create a new label we should be able to know if a pointer is referenced or if it references another label that references a pointer. When we parse a variable's address formula, we should be able to know what labels are referenced. If any of those labels reference pointers directly/indirectly we need to disable caching. The only additional state on a label is "does it use pointers?" But again, this may not be a problem we have to solve right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think it might be best to solve this problem when it stops being a hypothetical one. I’ll change it so variable caching is disabled once a label is referenced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to change:
if (error == nil && !variable.usesDynamicPointerAddress)
to:
if (error == nil && !variable.usesDynamicPointerAddress && !variable.usesDynamicLabelAddress)
I think.
Also in currentProcessChangedWithOldProcess:newProcess:
need to set .finishedEvaluatingDynamicAddress = NO
for all label variables.
f0c6c20
to
492e076
Compare
492e076
to
740ba28
Compare
ZGLabel *label = [documentLabelManager.labels objectForKey:labelString]; | ||
|
||
labelAddressNumber = @(label.address); | ||
if (labelAddressNumber == nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if label is nil, no need to assign labelAddressNumber (leave it zero). Otherwise if it's not nil, assign to @(label.address)
but no need to check the result is nil (this shouldn't happen).
I suppose you could return an error instead if the label doesn't exist.. but I don't think this is really an "error", just waiting until the label gets assigned.. (edit: I'm trying to think if it makes any difference from a caching perspective but I don't think so at the moment.. 🤔)
@@ -186,6 +193,16 @@ - (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)__un | |||
_data.variables = [NSArray array]; | |||
} | |||
|
|||
NSArray<ZGLabel *> *newLabels = [keyedUnarchiver decodeObjectOfClass:[NSArray class] forKey:ZGLabelsArrayKey]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably wrong for specifying the decoded class but so is the code above it saving the variables. I can look at this later independently from this pull request.
|
||
for (ZGLabel *label in documentData.labels) | ||
{ | ||
if(label.name != nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't be nil.
NSMutableArray<ZGLabel *> *temporaryArray = [[NSMutableArray alloc] initWithArray:_documentData.labels]; | ||
[temporaryArray addObject:label]; | ||
|
||
_documentData.labels = [NSArray arrayWithArray:temporaryArray]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
_documentData.labels = [_documentData.labels arrayByAddingObject:label];
ZGVariable *_variable = [coder decodeObjectOfClass:[ZGVariable class] forKey:ZGVariableKey]; | ||
|
||
if(_name == NULL || _variable == NULL) { | ||
return NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should use nil
not NULL
for obj-c types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also these local variables should not be prefixed with _ and you need to assign them to the ivars.
{ | ||
_name = name; | ||
_variable = [[ZGVariable alloc] initWithValue:NULL size:pointerSize address:address type:ZGPointer qualifier:qualifier pointerSize:pointerSize]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing return self.
_variable = [[ZGVariable alloc] initWithValue:NULL size:pointerSize address:variable.address type:ZGPointer qualifier:variable.qualifier pointerSize:pointerSize]; | ||
|
||
[_variable setAddressFormula:variable.addressFormula]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing return self.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also unused method for now (I rather add methods when needed than keep them around).
ZGMemoryAddress memoryAddress = 0x0; | ||
char *identifier = NULL; | ||
|
||
if (PyArg_ParseTuple(args, "sK:addLabel", &identifier, &memoryAddress)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should likely allow passing a numerical OR a string which would be creating a label with an address formula. It should be possible from scripting to create such dynamic labels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also not sure if there should be addLabel/removeLabel rather than setLabel with None being to remove the label 🤔
After some thinking, I think a variable needs to have a label in order for that label to be around. Basically a label needs to be attached to some variable in the table. From a user point of view (and not implementation point of view): when a user wants to set a label onto a variable, that should just label the variable with a specific reference-able name. When the user edits the address formula of that variable, the description there (that explains base() and []s) can maybe indicate the reference-able label name of the variable (I believe I had a branch somewhere that actually did something like this). (Edit: the variable description can be updated too if the user hasn’t edited the description themselves yet and it has been so far auto-generated). Also changing the address formula of a variable that is labeled should update the variable/label appropriately. Any other variable can reference the label using label("myLabelName"). When a variable that has a label is removed, that label should also be discarded (unless maybe there's another variable with the same label like a copy but I’m not sure this should be allowed. There is undo support to think about too.). The only labels that need to be updated/managed are labels that are assigned to variables in the table. From scripting, the user should only be able to set/change an existing label, not add or remove labels. I don't want an ordered set of labels view the user needs to manage if I can avoid it. Is it necessary that a label creates another variable? Maybe not. Is it necessary for the document to save another array of labels, perhaps not either. |
Interesting, that was my initial approach (e.g. |
Regarding copies, I don't think two variables currently in the table should be allowed to be assigned to the same label. So copying a variable that is labeled should likely strip the label. Trying to set the same label onto another variable should fail. Probably a |
I merged an implementation for adding labels in #95 (more details there). This PR helped me speed up writing an initial implementation. |
This should at least resolve #66 and adds a starting point for #16.
No tests yet, as I think some discussion on the exact implementation is advisable.
I am not sure what the best approach is for solving #66. I see two possibilities with this PR:
Separate a
ZGLabel
andZGVariable
in the UI, and create separate tables for them to manage them.A
ZGLabel
is referencing aZGVariable
, and as such the table implementation can be re-used forZGLabel
. When assigning a label to a variable, you would actually lift the referenced variable into a label and this means that the variable is moved from the variables list to the labels list. You can still interact with the variable in the same way, but it now is a label for all intents and purposes. (e.g. edit address formula and others)Do not create separate tables for variables and labels, instead augment the variables view in some way that adds a simple but clear distinction between variables and variables with labels.
This would be the least amount of work, but I am not sure what the best way to indicate a label on a variable would be. An additional column is easiest, but is also a waste of space. (Assuming most variables don't have a label.) The creation of a label is the same as for 1. (e.g. lifting the ZGVariable into a ZGLabel). But it'll stick around in the variables array, and still be part of the variables table.