Skip to content

Commit 90bdad5

Browse files
committed
Fixed indentation issue with strongly typed placeholders by sending the current GraphQL environment to the language service (#130)
1 parent d9dbc4d commit 90bdad5

File tree

6 files changed

+72
-10
lines changed

6 files changed

+72
-10
lines changed

src/main/com/intellij/lang/jsgraphql/ide/injection/JSGraphQLTemplateFragmentLanguageInjector.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.intellij.lang.javascript.JSTokenTypes;
1515
import com.intellij.lang.javascript.psi.ecma6.JSStringTemplateExpression;
1616
import com.intellij.lang.jsgraphql.JSGraphQLLanguage;
17+
import com.intellij.openapi.util.Ref;
1718
import com.intellij.openapi.util.TextRange;
1819
import com.intellij.psi.PsiElement;
1920
import com.intellij.psi.PsiLanguageInjectionHost;
@@ -26,11 +27,13 @@ public class JSGraphQLTemplateFragmentLanguageInjector implements MultiHostInjec
2627

2728
private static final ArrayList<Class<JSStringTemplateExpression>> INJECTION_CLASSES = Lists.newArrayList(JSStringTemplateExpression.class);
2829

30+
public final static ThreadLocal<String> CURRENT_INJECTION_ENVIRONMENT = new ThreadLocal<>();
31+
2932
@Override
3033
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
3134

32-
33-
if(JSGraphQLLanguageInjectionUtil.isJSGraphQLLanguageInjectionTarget(context)) {
35+
final Ref<String> envRef = new Ref<>(null);
36+
if(JSGraphQLLanguageInjectionUtil.isJSGraphQLLanguageInjectionTarget(context, envRef)) {
3437

3538
final JSStringTemplateExpression template = (JSStringTemplateExpression)context;
3639

@@ -60,7 +63,13 @@ public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull
6063
}
6164
}
6265

63-
registrar.doneInjecting();
66+
try {
67+
CURRENT_INJECTION_ENVIRONMENT.set(envRef.get());
68+
// lexing and parsing occurs inside the next method, allowing us to access the env from the thread local
69+
registrar.doneInjecting();
70+
} finally {
71+
CURRENT_INJECTION_ENVIRONMENT.remove();
72+
}
6473
}
6574
}
6675

src/main/com/intellij/lang/jsgraphql/ide/notifications/JSGraphQLConfigEditorNotificationProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ protected Runnable getConfigureAction() {
151151
FileEditorManager.getInstance(myProject).openFile(config, true, true);
152152
if(setProjectDir) {
153153
// fetch tokens from the client to force it to set the project dir base on the module we picked
154-
JSGraphQLNodeLanguageServiceClient.getTokens("", myProject);
154+
JSGraphQLNodeLanguageServiceClient.getTokens("", myProject, null);
155155
}
156156
Notifications.Bus.notify(new Notification("GraphQL", "Created " + JSGraphQLConfigurationProvider.GRAPHQL_CONFIG_JSON + " and " + JSGraphQLConfigurationProvider.GRAPHQL_DEFAULT_SCHEMA, "Edit to load your own GraphQL schema.", NotificationType.INFORMATION));
157157
}

src/main/com/intellij/lang/jsgraphql/languageservice/JSGraphQLNodeLanguageServiceClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public class JSGraphQLNodeLanguageServiceClient {
2828
private static final Logger log = Logger.getInstance(JSGraphQLNodeLanguageServiceClient.class);
2929
private static Map<Project, JSGraphQLNodeLanguageServiceInstance> languageServiceInstances = Maps.newConcurrentMap();
3030

31-
public static TokensResponse getTokens(String buffer, Project project) {
32-
final BufferRequest request = BufferRequest.getTokens(buffer);
31+
public static TokensResponse getTokens(String buffer, Project project, String environment) {
32+
final BufferRequest request = BufferRequest.getTokens(buffer, environment);
3333
return executeRequest(request, TokensResponse.class, project);
3434
}
3535

src/main/com/intellij/lang/jsgraphql/languageservice/api/BufferRequest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ protected BufferRequest(String command, String environment) {
1717
super(command, environment);
1818
}
1919

20-
public static BufferRequest getTokens(String buffer) {
21-
BufferRequest ret = new BufferRequest("getTokens", null);
20+
public static BufferRequest getTokens(String buffer, String environment) {
21+
BufferRequest ret = new BufferRequest("getTokens", environment);
2222
ret.buffer = buffer;
2323
return ret;
2424
}

src/main/com/intellij/lang/jsgraphql/lexer/JSGraphQLLexer.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.intellij.lang.jsgraphql.JSGraphQLDebugUtil;
1212
import com.intellij.lang.jsgraphql.JSGraphQLKeywords;
1313
import com.intellij.lang.jsgraphql.JSGraphQLTokenTypes;
14+
import com.intellij.lang.jsgraphql.ide.injection.JSGraphQLTemplateFragmentLanguageInjector;
1415
import com.intellij.lang.jsgraphql.languageservice.JSGraphQLNodeLanguageServiceClient;
1516
import com.intellij.lang.jsgraphql.languageservice.api.Token;
1617
import com.intellij.lang.jsgraphql.languageservice.api.TokensResponse;
@@ -41,6 +42,8 @@ public class JSGraphQLLexer extends LexerBase {
4142
private int startOffset;
4243
private int endOffset;
4344

45+
private String environment;
46+
4447
private TokensResponse response;
4548

4649
private final Project project;
@@ -63,6 +66,8 @@ public void start(@NotNull CharSequence buffer, int startOffset, int endOffset,
6366
this.tokens = Lists.newArrayList();
6467
this.response = null;
6568

69+
this.environment = JSGraphQLTemplateFragmentLanguageInjector.CURRENT_INJECTION_ENVIRONMENT.get();
70+
6671
fetchTokensFromLanguageService();
6772
}
6873

@@ -75,7 +80,7 @@ private void fetchTokensFromLanguageService() {
7580
}
7681

7782
// get the response using the client
78-
response = JSGraphQLNodeLanguageServiceClient.getTokens(bufferAsString, project);
83+
response = JSGraphQLNodeLanguageServiceClient.getTokens(bufferAsString, project, environment);
7984

8085
if (response == null) {
8186
// blank

src/main/com/intellij/lang/jsgraphql/parser/JSGraphQLParser.java

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,13 @@ private void endScope(@NotNull PsiBuilder builder, Ref<Integer> tokenIndex, Stac
218218

219219
private List<PropertyScope> getPropertyScopes(List<JSGraphQLToken> tokens) {
220220

221-
final List<JSGraphQLToken> astTokens = tokens.stream()
221+
List<JSGraphQLToken> astTokens = tokens.stream()
222222
.filter((token) -> token.tokenType != JSGraphQLTokenTypes.WHITESPACE && token.tokenType != JSGraphQLTokenTypes.COMMENT)
223223
.collect(Collectors.toList());
224224

225+
// remove ${jsVar} placeholders from consideration
226+
astTokens = removeVariablePlaceholders(astTokens);
227+
225228
final List<PropertyScope> ret = Lists.newArrayList();
226229
final Stack<PropertyScope> scopes = new Stack<>();
227230

@@ -335,6 +338,51 @@ private List<PropertyScope> getPropertyScopes(List<JSGraphQLToken> tokens) {
335338
return ret;
336339
}
337340

341+
/**
342+
* Removes placeholders like ${jsVariable.foo.bar} from the astTokens since the curly braces shouldn't count as selection sets
343+
*/
344+
private List<JSGraphQLToken> removeVariablePlaceholders(List<JSGraphQLToken> astTokens) {
345+
final List<JSGraphQLToken> tokens = Lists.newArrayListWithExpectedSize(astTokens.size());
346+
for (int i = 0; i < astTokens.size(); i++) {
347+
JSGraphQLToken currentToken = astTokens.get(i);
348+
if(currentToken.tokenType == JSGraphQLTokenTypes.VARIABLE) {
349+
// found a '$' so if the next token is a '{' then it's a placeholder
350+
if(i < astTokens.size() - 1 /* there is a next token */) {
351+
final JSGraphQLToken nextToken = astTokens.get(i + 1);
352+
if(nextToken.tokenType == JSGraphQLTokenTypes.LBRACE) {
353+
// found a placeholder, so skip tokens until we on the other side of the placeholder
354+
tokens.add(currentToken);
355+
currentToken = null;
356+
int openBraces = 1;
357+
// start at the token that comes after '$' and '{'
358+
for(int j = i + 2; j < astTokens.size(); j++) {
359+
final JSGraphQLToken token = astTokens.get(j);
360+
i = j;
361+
if(token.tokenType == JSGraphQLTokenTypes.LBRACE) {
362+
openBraces++;
363+
} else if(token.tokenType == JSGraphQLTokenTypes.RBRACE) {
364+
openBraces--;
365+
if(openBraces == 0) {
366+
// found the end of the placeholder
367+
if(j < astTokens.size() - 1) {
368+
// there's a next token which is the one we want to add
369+
i++;
370+
currentToken = astTokens.get(i);
371+
break;
372+
}
373+
}
374+
}
375+
}
376+
}
377+
}
378+
}
379+
if(currentToken != null) {
380+
tokens.add(currentToken);
381+
}
382+
}
383+
return tokens;
384+
}
385+
338386
private boolean isValueForAttribute(List<JSGraphQLToken> astTokens, PropertyScope attributeNameScope, PropertyScope attributeValue) {
339387
for(int i = attributeNameScope.astTokenStartIndex + 1; i < astTokens.size(); i++) {
340388
final JSGraphQLToken token = astTokens.get(i);

0 commit comments

Comments
 (0)