slint icon indicating copy to clipboard operation
slint copied to clipboard

Lookup of unqualified names and enums

Open ogoffart opened this issue 3 years ago • 1 comments

Currently (as defined in lookup.rs) when we have a unqualified name in a expression (for example, right in horizontal-alignment: right;) the lookup rules goes as follow:

  1. arguments of a callback (bar in foo(bar) => { /* ... */ })
  2. id of elements, including self, root or parent
  3. properties in the self element (example: background is referring to self.background
  4. Scope lookup: for each element part of a for or if construct: i. index or data (foo or idx in for foo[idx] in /* ... */) ii. any properties in that element
  5. properties in the root (eg, title would likely be the root.title if the root is a Window)
  6. A type, eg: a global or an enum (eg: HorizontalAlignment or SomeGlobal) these are usually uppercase
  7. If we are in an expression for a property on a enum type, or a callback that returns an enum type, any enum value (eg: right for the horizontal-alignment property would be the same as HorizontalAlignment.right)
  8. Likewise, for a expression of a property on a color, all the CSS color names are in scope
  9. builtin functions (min, max, rgb, floor, debug, ...)

Now, there is the following things are not ideal:

A. Enum value in scope.

horizontal-alignment: right;

Works well, unless we add a right property in the scope (which we actually want to add for the anchors) To solve the problem we have the following options

α. Don't lookup the enum value at all and force user to always write

  horizontal-alignment: HorizontalAlignment.right;

This is more verbose but at least we always know what it is and it is consistant (does not depends on the type of the property)

β. Move the enum lookup before property (but after id). We can still access the properties themselves by using the self.right or root.right if we want.

γ. Keep it as is, but then it is a breaking change to add a property of the same name of an enum value and is unfortinate for left, right, bottom, top

The problem is the same with colours. should colours be considered as an enum anyway so we can do Color.blue anyhere?

B. how to access builtin function if they are overriden by a property?

If there is a property max in scope, it is not possible to call the max() function anymore. We also want to add a property debug to all elements. The option include

α. Do the lookup of functions before the lookup of properties β. Add a way to get the function fully quallified... Std.max(..) or .max(..) or Global.max(..)? γ. Different lookup rules when there is a parentheses, so max would be the property, but max(...) would be the function. But what to do if it is a callback or if the property is callable?

C. Does it makes sense to have the properties of a repeater element in the scope?

ogoffart avatar Jun 25 '21 15:06 ogoffart

I think we need to reconsider something regarding unqualified lookup: we want that adding properties to elements is not a breaking change. Same for enum values or types.

Also a piece of data: i added some debug to look for counts of what the unqualified lookup are: printer demo (native): Stats { arg: 18, root: 84, root_explicit: 190, self_: 67, self_explicit: 10, parent: 54, id: 145, type_: 117, enum_: 54, model: 12, index: 14, scope: 0, function: 21, } gallery(fluent) : Stats { arg: 30, root: 89, root_explicit: 218, self_: 114, self_explicit: 7, parent: 57, id: 156, type_: 129, enum_: 51, model: 3, index: 4, scope: 0, function: 24, }, (note that implicit self is counted as root within the root item itself)

So here is what i suggest should be the new lookup rules: (This assume, we will change the definition of the property so that we can no longer define property on inner elements (#191))


Start with local things:

  1. arguments of callback
  2. id of elements
  3. index or data of model (walk up the scopes)
  4. local properties declared in the current component with property (what is currently usually in root) but walk up all scopes.

Global things:

  1. Type enum name or namespace, or global
  2. builtin functions

Type dependent

  1. enum values or CSS colors.

In particular, this proposal no longer resolve unqualified self. base properties at all. Because having a lookup on self means that adding property in element would break if the user has used a declared property or a type. According to the number, we query root properties more often, but actually we still often use self implicitly. We could add the self properties in the list anyway: between 4. and 5. Because types usually starts with uppercase that might be ok, but we'd still have conflict with enum values. Unless we change our policy and put enum value in camelcase too. We can also have another syntax to access self property. instead of self.foo we could write .foo or something like that.

We still have a possible conflict between type, builtin function and enum values when we allow user to declare their own enum value or if we want to add a builtin function that overlap with an enum value. We might want to reorder 7. and 8. but that is also problematic.

Maybe we could also make it more robust by having namespace and enum use :: instead of ., so if we have Foo::bar we know that Foo is an enum type or namespace and not a global or a property Alternatively could also enforce lower case for properties and uppercase for type so that we will not have conflict.

There is also the topic looking up types. Should builtin elements and builtin enum always be in scope, or should they be imported too? Why do we need to import our widget (which, from the user point of view, are also builtin) but not the builtin elements and struct.

ogoffart avatar May 31 '22 08:05 ogoffart

The new syntax has the new lookup rules

ogoffart avatar Jan 25 '23 11:01 ogoffart