Skip to content

Commit

Permalink
fix incomplete handling of use statements (relative to namespaces):
Browse files Browse the repository at this point in the history
* (sub)keywords `function` and `const` were tokenized as Name.Namespace
* enumerated names in non-grouped `use` were improperly highlighted
(eg `use Foo, Bar;`, anything after the first name to the final `;`
or `{` was tokenized as Error)
* a namespaced name triggered Error on `\\` in grouped `use` statement
(eg `use some\name\{const Foo\BAR};`)

And add support to comments in this `use` statement.

Also fixes:
* #1353
* #1361
  • Loading branch information
julp committed Apr 6, 2020
1 parent 9675b04 commit 826b516
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 13 deletions.
32 changes: 20 additions & 12 deletions lib/rouge/lexers/php.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ def self.detect?(text)
rule(//) { push :template; push :php }
end

state :ignorables do
rule %r/\s+/, Text
rule %r/#.*?$/, Comment::Single
rule %r(//.*?$), Comment::Single
rule %r(/\*\*(?!/).*?\*/)m, Comment::Doc
rule %r(/\*.*?\*/)m, Comment::Multiline
end

state :template do
rule %r/<\?(php|=)?/, Comment::Preproc, :php
rule(/.*?(?=<\?)|.*/m) { delegate parent }
Expand All @@ -99,11 +107,7 @@ def self.detect?(text)
rule %r/\?>/, Comment::Preproc, :pop!
# heredocs
rule %r/<<<('?)(#{id})\1\n.*?\n\s*\2;?/im, Str::Heredoc
rule %r/\s+/, Text
rule %r/#.*?$/, Comment::Single
rule %r(//.*?$), Comment::Single
rule %r(/\*\*(?!/).*?\*/)m, Comment::Doc
rule %r(/\*.*?\*/)m, Comment::Multiline
mixin :ignorables

rule %r/(->|::)(\s*)(#{id})/ do
groups Operator, Text, Name::Attribute
Expand All @@ -114,8 +118,8 @@ def self.detect?(text)
rule %r/(class|interface|trait)(\s+)(#{nsid})/ do
groups Keyword::Declaration, Text, Name::Class
end
rule %r/(use)(\s+)(function|const|)(\s*)(#{nsid})/ do
groups Keyword::Namespace, Text, Keyword::Namespace, Text, Name::Namespace
rule %r/(use)(\s+)(function|const|)(\s*)(#{nsid})/i do
groups Keyword::Namespace, Text, Keyword, Text, Name::Namespace
push :use
end
rule %r/(namespace)(\s+)(#{nsid})/ do
Expand Down Expand Up @@ -164,22 +168,26 @@ def self.detect?(text)
end

state :use do
rule %r/(\s+)(as)(\s+)(#{id})/ do
rule %r/(\s+)(as)(\s+)(#{id})/i do
groups Text, Keyword, Text, Name
:pop!
end
mixin :ignorables
rule %r/#{nsid}/, Name::Namespace
rule %r/,/, Punctuation
rule %r/\\\{/, Operator, :uselist
rule %r/;/, Punctuation, :pop!
rule %r/[;{]/, Punctuation, :pop!
end

state :uselist do
rule %r/\s+/, Text
mixin :ignorables
rule %r/(function|const)\b/i, Keyword
rule %r/,/, Operator
rule %r/\}/, Operator, :pop!
rule %r/(as)(\s+)(#{id})/ do
rule %r/(as)(\s+)(#{id})/i do
groups Keyword, Text, Name
end
rule %r/#{id}/, Name::Namespace
rule %r/#{nsid}/, Name::Namespace
end

state :funcname do
Expand Down
34 changes: 34 additions & 0 deletions spec/lexers/php_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,39 @@
it 'recognizes trait definition' do
assert_tokens_equal 'trait A {}', ["Keyword.Declaration", "trait"], ["Text", " "], ["Name.Class", "A"], ["Text", " "], ["Punctuation", "{}"]
end

it 'lexes correctly use statements' do
# issue #1353
assert_tokens_equal 'Use Class1, Class2;', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Name.Namespace', 'Class1'], ['Punctuation', ','], ['Text', ' '], ['Name.Namespace', 'Class2'], ['Punctuation', ';']

# issue #1361
assert_tokens_equal 'Use TraitA, TraitB {', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Name.Namespace', 'TraitA'], ['Punctuation', ','], ['Text', ' '], ['Name.Namespace', 'TraitB'], ['Text', ' '], ['Punctuation', '{']

assert_tokens_equal 'Use My\Full\Classname As Another;', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Name.Namespace', 'My\Full\Classname'], ['Text', ' '], ['Keyword', 'As'], ['Text', ' '], ['Name', 'Another'], ['Punctuation', ';']
assert_tokens_equal 'Use My\Full\NSname;', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Name.Namespace', 'My\Full\NSname'], ['Punctuation', ';']
assert_tokens_equal 'Use ArrayObject;', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Name.Namespace', 'ArrayObject'], ['Punctuation', ';']
assert_tokens_equal 'Use Function My\Full\functionName;', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Keyword', 'Function'], ['Text', ' '], ['Name.Namespace', 'My\Full\functionName'], ['Punctuation', ';']
assert_tokens_equal 'Use Function My\Full\functionName As func;', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Keyword', 'Function'], ['Text', ' '], ['Name.Namespace', 'My\Full\functionName'], ['Text', ' '], ['Keyword', 'As'], ['Text', ' '], ['Name', 'func'], ['Punctuation', ';']
assert_tokens_equal 'Use Const My\Full\CONSTANT;', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Keyword', 'Const'], ['Text', ' '], ['Name.Namespace', 'My\Full\CONSTANT'], ['Punctuation', ';']
assert_tokens_equal 'Use My\Full\Classname As Another, My\Full\NSname;', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Name.Namespace', 'My\Full\Classname'], ['Text', ' '], ['Keyword', 'As'], ['Text', ' '], ['Name', 'Another'], ['Punctuation', ','], ['Text', ' '], ['Name.Namespace', 'My\Full\NSname'], ['Punctuation', ';']

assert_tokens_equal 'Use some\name\{ClassA, ClassB, ClassC As C};', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Name.Namespace', 'some\name'], ['Operator', '\{'],
['Name.Namespace', 'ClassA'], ['Operator', ','],
['Text', ' '], ['Name.Namespace', 'ClassB'],
['Operator', ','], ['Text', ' '],
['Name.Namespace', 'ClassC'], ['Text', ' '], ['Keyword', 'As'], ['Text', ' '], ['Name', 'C'], ['Operator', '}'], ['Punctuation', ';']
assert_tokens_equal 'Use Function some\name\{fn_a, fn_b, fn_c};', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Keyword', 'Function'], ['Text', ' '], ['Name.Namespace', 'some\name'], ['Operator', '\{'],
['Name.Namespace', 'fn_a'], ['Operator', ','],
['Text', ' '], ['Name.Namespace', 'fn_b'], ['Operator', ','],
['Text', ' '], ['Name.Namespace', 'fn_c'], ['Operator', '}'], ['Punctuation', ';']
assert_tokens_equal 'Use Const some\name\{ConstA, ConstB, ConstC};', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Keyword', 'Const'], ['Text', ' '], ['Name.Namespace', 'some\name'], ['Operator', '\{'],
['Name.Namespace', 'ConstA'], ['Operator', ','],
['Text', ' '], ['Name.Namespace', 'ConstB'], ['Operator', ','], ['Text', ' '],
['Name.Namespace', 'ConstC'], ['Operator', '}'], ['Punctuation', ';']
assert_tokens_equal 'Use some\name\{Function some_fn, Const Foo\BAR, SomeClass};', ['Keyword.Namespace', 'Use'], ['Text', ' '], ['Name.Namespace', 'some\name'], ['Operator', '\{'],
['Keyword', 'Function'], ['Text', ' '], ['Name.Namespace', 'some_fn'], ['Operator', ','],
['Text', ' '], ['Keyword', 'Const'], ['Text', ' '], ['Name.Namespace', 'Foo\BAR'], ['Operator', ','],
['Text', ' '], ['Name.Namespace', 'SomeClass'], ['Operator', '}'], ['Punctuation', ';']
end
end
end
17 changes: 16 additions & 1 deletion spec/visual/samples/php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,22 @@ use const some\namespace\ConstC;
// Grouped uses
use some\namespace\{ClassA, ClassB, ClassC as C};
use function some\namespace\{fn_a, fn_b, fn_c};
use const some\namespace\{ConstA, ConstB, ConstC};
use const some\namespace\{
ConstA,
#ConstB,
ConstC
};

use some\name\{function some_fn, const Foo\BAR, SomeClass};

use Class1, /*Class2, */Class3;

class ClassB
{
use TraitA, TraitB, TraitC {
TraitB::foo insteadof TraitA;
}
}

namespace some\namespace;

Expand Down

0 comments on commit 826b516

Please sign in to comment.