Skip to content

Commit 31d6124

Browse files
committed
fix: support namespace-qualified types in extends/implements (issue #50)
- Update extends-class pattern to handle namespace-qualified types - Update implements-class pattern to handle namespace-qualified types - Patterns now correctly tokenize System.Exception, Database.Batchable, etc. - Use lookahead to distinguish namespace-qualified from simple types - Fix type-builtin to support both 'Id' and 'ID' (Apex is case-insensitive) - Add test cases for namespace-qualified extends and implements Closes #50
1 parent c9e5baa commit 31d6124

File tree

5 files changed

+76
-7
lines changed

5 files changed

+76
-7
lines changed

src/apex.tmLanguage.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,21 +420,35 @@ repository:
420420
- include: '#comment'
421421

422422
extends-class:
423-
begin: (extends)\b\s+([_[:alpha:]][_[:alnum:]]*)
423+
begin: (extends)\b\s+
424424
beginCaptures:
425425
'1': { name: keyword.other.extends.apex }
426-
'2': { name: entity.name.type.extends.apex }
427426
end: '(?={|implements)'
427+
patterns:
428+
- begin: (?=[_[:alpha:]][_[:alnum:]]*\s*\.)
429+
end: '(?={|implements)'
430+
patterns:
431+
- include: '#support-type'
432+
- include: '#type'
433+
- match: ([_[:alpha:]][_[:alnum:]]*)
434+
captures:
435+
'1': { name: entity.name.type.extends.apex }
428436

429437
implements-class:
430438
begin: (implements)\b
431439
beginCaptures:
432440
'1': { name: keyword.other.implements.apex }
433441
patterns:
442+
- begin: (?=[_[:alpha:]][_[:alnum:]]*\s*\.)
443+
end: '(?={|extends|,)'
444+
patterns:
445+
- include: '#support-type'
446+
- include: '#type'
434447
- match: ([_[:alpha:]][_[:alnum:]]*)\b\s*(,)?
435448
captures:
436449
'1': { name: entity.name.type.implements.apex }
437450
'2': { name: punctuation.separator.comma.apex }
451+
- include: '#punctuation-comma'
438452
end: '(?={|extends)'
439453

440454
soql-query-expression:
@@ -1654,7 +1668,7 @@ repository:
16541668
- include: '#type-nullable-suffix'
16551669

16561670
type-builtin:
1657-
match: \b(Blob|Boolean|byte|Date|Datetime|Decimal|Double|ID|Integer|Long|Object|String|Time|void)\b
1671+
match: \b(Blob|Boolean|byte|Date|Datetime|Decimal|Double|Id|Integer|Long|Object|String|Time|void)\b
16581672
captures:
16591673
'1': { name: keyword.type.apex }
16601674

test/annotation.tests.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public class MyTestClass { }`);
104104
it('annotation with multiple parameters on field', async () => {
105105
const input =
106106
Input.InClass(`@InvocableMethod(label='Insert Accounts' description='Inserts new accounts.' required=false)
107-
global ID leadId;
107+
global Id leadId;
108108
`);
109109
const tokens = await tokenize(input);
110110

@@ -133,7 +133,9 @@ public class MyTestClass { }`);
133133
});
134134

135135
it('annotation on same line as method declaration (issue #44)', async () => {
136-
const input = Input.InClass(`@Future(callout=true) public static void method() {}`);
136+
const input = Input.InClass(
137+
`@Future(callout=true) public static void method() {}`
138+
);
137139
const tokens = await tokenize(input);
138140

139141
tokens.should.deep.equal([

test/class.tests.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,5 +216,42 @@ public abstract class PublicAbstractClass { }
216216
Token.Punctuation.CloseBrace,
217217
]);
218218
});
219+
220+
it('class extends namespace-qualified type (issue #50)', async () => {
221+
const input = Input.FromText(`class MyClass extends System.Exception {}`);
222+
const tokens = await tokenize(input);
223+
224+
tokens.should.deep.equal([
225+
Token.Keywords.Class,
226+
Token.Identifiers.ClassName('MyClass'),
227+
Token.Keywords.Extends,
228+
Token.Support.Class.System,
229+
Token.Punctuation.Accessor,
230+
Token.Support.Class.TypeText('Exception'),
231+
Token.Punctuation.OpenBrace,
232+
Token.Punctuation.CloseBrace,
233+
]);
234+
});
235+
236+
it('class implements namespace-qualified type (issue #50)', async () => {
237+
const input = Input.FromText(
238+
`class MyClass implements Database.Batchable<Account> {}`
239+
);
240+
const tokens = await tokenize(input);
241+
242+
tokens.should.deep.equal([
243+
Token.Keywords.Class,
244+
Token.Identifiers.ClassName('MyClass'),
245+
Token.Keywords.Implements,
246+
Token.Support.Class.Database,
247+
Token.Punctuation.Accessor,
248+
Token.Support.Class.TypeText('Batchable'),
249+
Token.Punctuation.TypeParameters.Begin,
250+
Token.Type('Account'),
251+
Token.Punctuation.TypeParameters.End,
252+
Token.Punctuation.OpenBrace,
253+
Token.Punctuation.CloseBrace,
254+
]);
255+
});
219256
});
220257
});

test/interface.tests.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*--------------------------------------------------------------------------------------------*/
66

77
import { should } from 'chai';
8-
import { tokenize, Token } from './utils/tokenize';
8+
import { tokenize, Input, Token } from './utils/tokenize';
99

1010
describe('Grammar', () => {
1111
before(() => {
@@ -78,5 +78,21 @@ interface IBar extends IFoo { }
7878
Token.Punctuation.CloseBrace,
7979
]);
8080
});
81+
82+
it('interface extends namespace-qualified type (issue #50)', async () => {
83+
const input = Input.FromText(`interface MyInterface extends System.IComparable {}`);
84+
const tokens = await tokenize(input);
85+
86+
tokens.should.deep.equal([
87+
Token.Keywords.Interface,
88+
Token.Identifiers.InterfaceName('MyInterface'),
89+
Token.Keywords.Extends,
90+
Token.Support.Class.System,
91+
Token.Punctuation.Accessor,
92+
Token.Support.Class.TypeText('IComparable'),
93+
Token.Punctuation.OpenBrace,
94+
Token.Punctuation.CloseBrace,
95+
]);
96+
});
8197
});
8298
});

test/utils/tokenize.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ export namespace Token {
646646
export const Datetime = createToken('Datetime', 'keyword.type.apex');
647647
export const Decimal = createToken('Decimal', 'keyword.type.apex');
648648
export const Double = createToken('Double', 'keyword.type.apex');
649-
export const ID = createToken('ID', 'keyword.type.apex');
649+
export const ID = createToken('Id', 'keyword.type.apex');
650650
export const Integer = createToken('Integer', 'keyword.type.apex');
651651
export const Long = createToken('Long', 'keyword.type.apex');
652652
export const Object = createToken('Object', 'keyword.type.apex');

0 commit comments

Comments
 (0)