Skip to content
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

Global mixins and mixins inside namespaces handle scopes differently #1316

Open
SomMeri opened this issue May 8, 2013 · 2 comments
Open

Comments

@SomMeri
Copy link
Member

SomMeri commented May 8, 2013

Scoping of global mixins is handled differently then scoping of mixin inside namespaces:

  • If I call mixin directly, it does not look into callers scope until it searched whole mixins scope. It will go through all parents, including the global scope.
  • If I call mixin from namespace, it searches only local scope and then proceeds to callers scope. Anything defined in definition parents scope is ignored.

Should they behave differently? If no, which version is correct?

Direct mixin call - mixin searches definition space first. It will never hit the caller:

/* define global mixin and variable */
.content() { 
  mixin:global;
}
@content: global;

/* mixin to be called */
.mobile() {
    variable: @content;
    .content();
}

.big-desktop-button {
  /* define local mixin and variable */
  .content() { 
    mixin:caller;
  }
  /* call mixin */
  @content: caller;
  .mobile();
}

compiles into:

.big-desktop-button {
  variable: global;
  mixin: global;
}

If I place exactly the same mixin into a namespace, output changes:

.content() { 
  mixin:global;
}
@content: global;

#namespace() {
  .mobile() {
    variable: @content;
    .content();
  }
}

.big-desktop-button {
  .content() { 
    mixin:caller;
  }
  @content: caller;
  #namespace > .mobile();
}

compiles into:

.big-desktop-button {
  variable: caller;
  mixin: caller;
}

The same effect happens with variables and mixins defined in namespace itself:

#namespace() {

  .content() { 
    mixin:namespace;
  }
  @content: namespace;

  .mobile() {
    variable: @content;
    .content();
  }
}

.big-desktop-button {
  .content() { 
    mixin:caller;
  }
  @content: caller;
  #namespace > .mobile();
}

compiles into:

.big-desktop-button {
  variable: caller;
  mixin: caller;
}

Tested on 1.4.0-beta-b4 with windows node.js.

@seven-phases-max
Copy link
Member

If I place exactly the same mixin into a namespace, output changes:

More over if you remove #namespace's parens (i.e. turn a parametric namespace into non-parametric) the result is global again :)

I was recently trying to make a sort of graphical cheat sheet for LESS 'scope precedence' (because sometimes it may be really complicated to retain which one overrides which other). Well, I did not finish it (because I did not manage to find a satisfactory visual representation - yet I hope), but here's at least a basic crib that covers most of typical scenarios, it's actually a two because parametric and non-parametric parent mixins (aka namespaces) result in a different precedence:

  • non-parametric parent:
    local > callee > parent > global > caller
  • parametric parent:
    local > callee > caller > global > parent

The code template I used (to illustrate what all those local/callee/parent/global/caller refer to):

// @scope: global;

.parent {
    // @scope: parent;

    .mixin-of-interest() {
        // @scope: local;
        overhere: @scope;
        .callee();
    }
}

.callee() {
    // @scope: callee;
}

.caller() {
    // @scope: caller;
    .parent.mixin-of-interest();
}

// ............................................................................

usage {.caller()}

p.s. edit: fixed parametric vs. non-parametric precedence sheet (accidentally swapped).

@seven-phases-max
Copy link
Member

Another curiouse example inspired by #2212 (comment):

.parent() {
    .sub() {var-in-sub: @var}
    .sup() {var-in-sup: @var}
}

.output {
    @var: green;
    .parent();
    .sub();        // -> black
    .parent.sup(); // -> green
}


// somewhere in a far far away galaxy:

.generate-some-classes() {
    @var: black;
    div {
        color: @var;
    }
} 

.generate-some-classes();

Now thinking of it in context of #2212 (comment) it becomes to look even more tricky: the problem is that global scope is the "parent" of the .sub too and it may be not really possible to fix this w/o breaking the rest of things :( I.e. is it possible to have parent > caller > global at all (at least this is what I hoped to get everywhere eventually) since a parent scope is something quite abstract actually and in many cases the parent is the global...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants