%PDF- %PDF-
| Direktori : /home/q/g/b/qgbqkvz/www/wp-content/plugins/wp-scss/scssphp/src/Ast/Selector/ |
| Current File : /home/q/g/b/qgbqkvz/www/wp-content/plugins/wp-scss/scssphp/src/Ast/Selector/ComplexSelector.php |
<?php
/**
* SCSSPHP
*
* @copyright 2012-2020 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://scssphp.github.io/scssphp
*/
namespace ScssPhp\ScssPhp\Ast\Selector;
use ScssPhp\ScssPhp\Exception\SassFormatException;
use ScssPhp\ScssPhp\Extend\ExtendUtil;
use ScssPhp\ScssPhp\Logger\LoggerInterface;
use ScssPhp\ScssPhp\Parser\SelectorParser;
use ScssPhp\ScssPhp\Util\EquatableUtil;
use ScssPhp\ScssPhp\Util\ListUtil;
use ScssPhp\ScssPhp\Visitor\SelectorVisitor;
/**
* A complex selector.
*
* A complex selector is composed of {@see CompoundSelector}s separated by
* {@see Combinator}s. It selects elements based on their parent selectors.
*/
final class ComplexSelector extends Selector
{
/**
* This selector's leading combinators.
*
* If this is empty, that indicates that it has no leading combinator. If
* it's more than one element, that means it's invalid CSS; however, we still
* support this for backwards-compatibility purposes.
*
* @var list<string>
* @phpstan-var list<Combinator::*>
* @readonly
*/
private $leadingCombinators;
/**
* The components of this selector.
*
* This is only empty if {@see $leadingCombinators} is not empty.
*
* Descendant combinators aren't explicitly represented here. If two
* {@see CompoundSelector}s are adjacent to one another, there's an implicit
* descendant combinator between them.
*
* It's possible for multiple {@see Combinator}s to be adjacent to one another.
* This isn't valid CSS, but Sass supports it for CSS hack purposes.
*
* @var list<ComplexSelectorComponent>
* @readonly
*/
private $components;
/**
* Whether a line break should be emitted *before* this selector.
*
* @var bool
* @readonly
*/
private $lineBreak;
/**
* @var int|null
*/
private $specificity;
/**
* @param list<string> $leadingCombinators
* @param list<ComplexSelectorComponent> $components
* @param bool $lineBreak
*
* @phpstan-param list<Combinator::*> $leadingCombinators
*/
public function __construct(array $leadingCombinators, array $components, bool $lineBreak = false)
{
if ($leadingCombinators === [] && $components === []) {
throw new \InvalidArgumentException('leadingCombinators and components may not both be empty.');
}
$this->leadingCombinators = $leadingCombinators;
$this->components = $components;
$this->lineBreak = $lineBreak;
}
/**
* Parses a complex selector from $contents.
*
* If passed, $url is the name of the file from which $contents comes.
* $allowParent controls whether a {@see ParentSelector} is allowed in this
* selector.
*
* @throws SassFormatException if parsing fails.
*/
public static function parse(string $contents, ?LoggerInterface $logger = null, ?string $url = null, bool $allowParent = true): ComplexSelector
{
return (new SelectorParser($contents, $logger, $url, $allowParent))->parseComplexSelector();
}
/**
* @return list<string>
* @phpstan-return list<Combinator::*>
*/
public function getLeadingCombinators(): array
{
return $this->leadingCombinators;
}
/**
* @return list<ComplexSelectorComponent>
*/
public function getComponents(): array
{
return $this->components;
}
/**
* If this compound selector is composed of a single compound selector with
* no combinators, returns it.
*
* Otherwise, returns null.
*
* @return CompoundSelector|null
*/
public function getSingleCompound(): ?CompoundSelector
{
if (\count($this->leadingCombinators) === 0 && \count($this->components) === 1 && \count($this->components[0]->getCombinators()) === 0) {
return $this->components[0]->getSelector();
}
return null;
}
public function getLastComponent(): ComplexSelectorComponent
{
if (\count($this->components) === 0) {
throw new \OutOfBoundsException('Cannot get the last component of an empty list.');
}
return $this->components[\count($this->components) - 1];
}
public function getLineBreak(): bool
{
return $this->lineBreak;
}
/**
* This selector's specificity.
*
* Specificity is represented in base 1000. The spec says this should be
* "sufficiently high"; it's extremely unlikely that any single selector
* sequence will contain 1000 simple selectors.
*/
public function getSpecificity(): int
{
if ($this->specificity === null) {
$specificity = 0;
foreach ($this->components as $component) {
$specificity += $component->getSelector()->getSpecificity();
}
$this->specificity = $specificity;
}
return $this->specificity;
}
public function accept(SelectorVisitor $visitor)
{
return $visitor->visitComplexSelector($this);
}
/**
* Whether this is a superselector of $other.
*
* That is, whether this matches every element that $other matches, as well
* as possibly additional elements.
*/
public function isSuperselector(ComplexSelector $other): bool
{
return \count($this->leadingCombinators) === 0 && \count($other->leadingCombinators) ===0 && ExtendUtil::complexIsSuperselector($this->components, $other->components);
}
public function equals(object $other): bool
{
return $other instanceof ComplexSelector && $this->leadingCombinators === $other->leadingCombinators && EquatableUtil::listEquals($this->components, $other->components);
}
/**
* Returns a copy of `$this` with $combinators added to the end of the final
* component in {@see components}.
*
* If $forceLineBreak is `true`, this will mark the new complex selector as
* having a line break.
*
* @param list<string> $combinators
* @param bool $forceLineBreak
*
* @return ComplexSelector
*
* @phpstan-param list<Combinator::*> $combinators
*/
public function withAdditionalCombinators(array $combinators, bool $forceLineBreak = false): ComplexSelector
{
if ($combinators === []) {
return $this;
}
if ($this->components === []) {
return new ComplexSelector(array_merge($this->leadingCombinators, $combinators), [], $this->lineBreak || $forceLineBreak);
}
return new ComplexSelector(
$this->leadingCombinators,
array_merge(
ListUtil::exceptLast($this->components),
[ListUtil::last($this->components)->withAdditionalCombinators($combinators)]
),
$this->lineBreak || $forceLineBreak
);
}
/**
* Returns a copy of `$this` with an additional $component added to the end.
*
* If $forceLineBreak is `true`, this will mark the new complex selector as
* having a line break.
*
* @param ComplexSelectorComponent $component
* @param bool $forceLineBreak
*
* @return ComplexSelector
*/
public function withAdditionalComponent(ComplexSelectorComponent $component, bool $forceLineBreak = false): ComplexSelector
{
return new ComplexSelector($this->leadingCombinators, array_merge($this->components, [$component]), $this->lineBreak || $forceLineBreak);
}
/**
* Returns a copy of `this` with $child's combinators added to the end.
*
* If $child has {@see leadingCombinators}, they're appended to `this`'s last
* combinator. This does _not_ resolve parent selectors.
*
* If $forceLineBreak is `true`, this will mark the new complex selector as
* having a line break.
*
* @param ComplexSelector $child
* @param bool $forceLineBreak
*
* @return ComplexSelector
*/
public function concatenate(ComplexSelector $child, bool $forceLineBreak = false): ComplexSelector
{
if (\count($child->leadingCombinators) === 0) {
return new ComplexSelector(
$this->leadingCombinators,
array_merge($this->components, $child->components),
$this->lineBreak || $child->lineBreak || $forceLineBreak
);
}
if (\count($this->components) === 0) {
return new ComplexSelector(
array_merge($this->leadingCombinators, $child->leadingCombinators),
$child->components,
$this->lineBreak || $child->lineBreak || $forceLineBreak
);
}
return new ComplexSelector(
$this->leadingCombinators,
array_merge(
ListUtil::exceptLast($this->components),
[ListUtil::last($this->components)->withAdditionalCombinators($child->leadingCombinators)],
$child->components
),
$this->lineBreak || $child->lineBreak || $forceLineBreak
);
}
}