Skip to content

Commit

Permalink
Recognize GraphQL strings passed to JavaScript functions (#109)
Browse files Browse the repository at this point in the history
Some JavaScript GraphQL frameworks use well-known functions rather than
tagged template literals. This change introduces basic support for these
cases via a new `g:graphql_javascript_functions` variable (["graphql"]).

This is implemented as a bit of a hack on top of the existing tagged
template support to avoid introducing new syntax regions:

    Tagged template:    graphql`
    Function call:      graphql(`
    Syntax pattern:     (graphql|graphql\()`

This is a good-enough place to start, and the overall approach gives us
room to iterate later if we want to revisit the implementation.
  • Loading branch information
jparise authored Aug 6, 2024
1 parent 73fbf5f commit 9caa247
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 6 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ The list of recognized tag names is defined by the `g:graphql_javascript_tags`
variable, which defaults to `["gql", "graphql", "Relay.QL"]`. This can also
be set on a per-buffer basis using the `b:graphql_javascript_tags` variable.

Untagged template literals passed as the first argument to specific functions
can also be recognized. `g:graphql_javascript_functions` defines this list of
functions, which defaults to `["graphql"]`. This list can also be set on a
per-buffer basis using the `b:graphql_javascript_functions` variable.

```javascript
const query = graphql(`
{
user(id: ${uid}) {
firstName
lastName
}
}
`;
```
You can also add a `# gql` or `# graphql` comment at the start of a template
string to indicate that its contents should be considered GraphQL syntax.
Expand Down
3 changes: 2 additions & 1 deletion after/syntax/javascript/graphql.vim
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

call graphql#embed_syntax('GraphQLSyntax')

let s:tags = '\%(' . join(graphql#javascript_tags(), '\|') . '\)'
let s:functions = map(copy(graphql#javascript_functions()), 'v:val .. "("')
let s:tags = '\%(' . join(graphql#javascript_tags() + s:functions, '\|') . '\)'

if graphql#has_syntax_group('jsTemplateExpression')
" pangloss/vim-javascript
Expand Down
3 changes: 2 additions & 1 deletion after/syntax/typescript/graphql.vim
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

call graphql#embed_syntax('GraphQLSyntax')

let s:tags = '\%(' . join(graphql#javascript_tags(), '\|') . '\)'
let s:functions = map(copy(graphql#javascript_functions()), 'v:val .. "("')
let s:tags = '\%(' . join(graphql#javascript_tags() + s:functions, '\|') . '\)'

exec 'syntax region graphqlTemplateString matchgroup=typescriptTemplate start=+' . s:tags . '\@20<=`+ skip=+\\`+ end=+`+ contains=@GraphQLSyntax,typescriptTemplateSubstitution extend'
exec 'syntax match graphqlTaggedTemplate +' . s:tags . '\ze`+ nextgroup=graphqlTemplateString'
Expand Down
4 changes: 4 additions & 0 deletions autoload/graphql.vim
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ function! graphql#has_syntax_group(group) abort
return v:true
endfunction

function! graphql#javascript_functions() abort
return graphql#var('graphql_javascript_functions', ['graphql'])
endfunction

function! graphql#javascript_tags() abort
return graphql#var('graphql_javascript_tags', ['gql', 'graphql', 'Relay.QL'])
endfunction
9 changes: 9 additions & 0 deletions doc/graphql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ supported.

*graphql-javascript-options*

*g:graphql_javascript_functions*
*b:graphql_javascript_functions*
|g:graphql_javascript_functions| list of strings

Default: `["graphql"]`

This variable lists the JavaScript function names that will be recognized as
receiving GraphQL template literal strings as their first argument.

*g:graphql_javascript_tags*
*b:graphql_javascript_tags*
|g:graphql_javascript_tags| list of strings
Expand Down
35 changes: 33 additions & 2 deletions test/javascript/default.vader
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Before:
Save g:graphql_javascript_tags
Save b:graphql_javascript_functions
Save b:graphql_javascript_tags

setlocal shiftwidth=2
source ../after/indent/javascript.vim
Expand Down Expand Up @@ -69,16 +70,46 @@ Execute (Syntax assertions):
AssertEqual 'javascript', b:current_syntax
AssertEqual 'graphqlName', SyntaxOf('user')

Given javascript (Template literal with a graphql() function):
const query = graphql(`
{
user(id: ${uid}) {
firstName
lastName
}
}
`;

Execute (Syntax assertions):
AssertEqual 'javascript', b:current_syntax
AssertEqual 'graphqlName', SyntaxOf('user')

Given javascript (Template literal):
const s = `text`;

Execute (Untagged template literals aren't matched ):
AssertNotEqual 'graphqlTemplateString', SyntaxOf('`')

Given javascript (Custom function):
const query = gql(`
{
user(id: ${uid}) {
firstName
lastName
}
}
`;

Execute (Function names can be customized):
let b:graphql_javascript_functions = ['gql']
source ../after/syntax/javascript/graphql.vim
AssertEqual 'javascript', b:current_syntax
AssertEqual 'graphqlName', SyntaxOf('user')

Given javascript (Custom tag):
const query = GraphQL.Tag`{}`;

Execute (Tag names can be customized):
let g:graphql_javascript_tags = ['GraphQL.Tag']
let b:graphql_javascript_tags = ['GraphQL.Tag']
source ../after/syntax/javascript/graphql.vim
AssertEqual 'graphqlTaggedTemplate', SyntaxOf('GraphQL.Tag')
19 changes: 17 additions & 2 deletions test/typescript/default.vader
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Before:
Save g:graphql_javascript_tags
Save b:graphql_javascript_functions
Save b:graphql_javascript_tags

setlocal shiftwidth=2
source ../after/indent/typescript.vim
Expand Down Expand Up @@ -48,7 +49,7 @@ Given typescript (Custom tag):
const query = GraphQL.Tag`{}`;

Execute (Tag names can be customized):
let g:graphql_javascript_tags = ['GraphQL.Tag']
let b:graphql_javascript_tags = ['GraphQL.Tag']
source ../after/syntax/typescript/graphql.vim
AssertEqual 'graphqlTaggedTemplate', SyntaxOf('GraphQL.Tag')

Expand Down Expand Up @@ -85,3 +86,17 @@ Given typescript (Template literal with `# graphql` comment):
Execute (Syntax assertions):
AssertEqual 'typescript', b:current_syntax
AssertEqual 'graphqlName', SyntaxOf('user')

Given typescript (Template literal with a graphql() function):
const query = graphql(`
{
user(id: ${uid}) {
firstName
lastName
}
}
`;

Execute (Syntax assertions):
AssertEqual 'typescript', b:current_syntax
AssertEqual 'graphqlName', SyntaxOf('user')

0 comments on commit 9caa247

Please sign in to comment.