1 /**
2  * This file is part of DCD, a development tool for the D programming language.
3  * Copyright (C) 2014 Brian Schott
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 module server.autocomplete;
20 
21 import std.algorithm;
22 import std.experimental.allocator;
23 import std.experimental.logger;
24 import std.array;
25 import std.conv;
26 import std.experimental.logger;
27 import std.file;
28 import std.path;
29 import std.range;
30 import std.stdio;
31 import std.string;
32 import std.typecons;
33 import std.uni;
34 
35 import dparse.ast;
36 import dparse.lexer;
37 import dparse.parser;
38 import dparse.rollback_allocator;
39 
40 import dsymbol.conversion;
41 import dsymbol.modulecache;
42 import dsymbol.string_interning;
43 import dsymbol.symbol;
44 import dsymbol.scope_;
45 import dsymbol.builtin.names;
46 import dsymbol.builtin.symbols;
47 
48 import common.constants;
49 import common.messages;
50 
51 import containers.hashset;
52 
53 /**
54  * Finds the uses of the symbol at the cursor position within a single document.
55  * Params:
56  *     request = the autocompletion request.
57  * Returns:
58  *     the autocompletion response.
59  */
60 public AutocompleteResponse findLocalUse(AutocompleteRequest request,
61     ref ModuleCache moduleCache)
62 {
63     AutocompleteResponse response;
64     RollbackAllocator rba;
65     auto allocator = scoped!(ASTAllocator)();
66     auto cache = StringCache(StringCache.defaultBucketCount);
67 
68     // patchs the original request for the subsequent requests
69     request.kind = RequestKind.symbolLocation;
70 
71     // getSymbolsForCompletion() copy to avoid repetitive parsing
72     LexerConfig config;
73     config.fileName = "";
74     const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode,
75             config, &cache);
76     SymbolStuff getSymbolsAtCursor(size_t cursorPosition)
77     {
78         auto sortedTokens = assumeSorted(tokenArray);
79         auto beforeTokens = sortedTokens.lowerBound(cursorPosition);
80         ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
81             &rba, request.cursorPosition, moduleCache);
82         auto expression = getExpression(beforeTokens);
83         return SymbolStuff(getSymbolsByTokenChain(pair.scope_, expression,
84             cursorPosition, CompletionType.location), pair.symbol, pair.scope_);
85     }
86 
87     // gets the symbol matching to cursor pos
88     SymbolStuff stuff = getSymbolsAtCursor(cast(size_t)request.cursorPosition);
89     scope(exit) stuff.destroy();
90 
91     // starts searching only if no ambiguity with the symbol
92     if (stuff.symbols.length == 1)
93     {
94         const(DSymbol*) sourceSymbol = stuff.symbols[0];
95         response.symbolLocation = sourceSymbol.location;
96         response.symbolFilePath = sourceSymbol.symbolFile.idup;
97 
98         // gets the source token to avoid too much getSymbolsAtCursor()
99         const(Token)* sourceToken;
100         foreach(i, t; tokenArray)
101         {
102             if (t.type != tok!"identifier")
103                 continue;
104             if (request.cursorPosition >= t.index &&
105                 request.cursorPosition < t.index + t.text.length)
106             {
107                 sourceToken = tokenArray.ptr + i;
108                 break;
109             }
110         }
111 
112         // finds the tokens that match to the source symbol
113         if (sourceToken != null) foreach (t; tokenArray)
114         {
115             if (t.type == tok!"identifier" && t.text == sourceToken.text)
116             {
117                 size_t pos = cast(size_t) t.index + 1; // place cursor inside the token
118                 SymbolStuff candidate = getSymbolsAtCursor(pos);
119                 scope(exit) candidate.destroy();
120                 if (candidate.symbols.length == 1 &&
121                     candidate.symbols[0].location == sourceSymbol.location &&
122                     candidate.symbols[0].symbolFile == sourceSymbol.symbolFile)
123                 {
124                     response.locations ~= t.index;
125                 }
126             }
127         }
128         else
129         {
130             warning("The source token is not an identifier");
131         }
132     }
133     else
134     {
135         warning("No or ambiguous symbol for the identifier at cursor");
136     }
137     return response;
138 }
139 
140 /**
141  * Gets documentation for the symbol at the cursor
142  * Params:
143  *     request = the autocompletion request
144  * Returns:
145  *     the autocompletion response
146  */
147 public AutocompleteResponse getDoc(const AutocompleteRequest request,
148 	ref ModuleCache moduleCache)
149 {
150 //	trace("Getting doc comments");
151 	AutocompleteResponse response;
152 	RollbackAllocator rba;
153 	auto allocator = scoped!(ASTAllocator)();
154 	auto cache = StringCache(StringCache.defaultBucketCount);
155 	SymbolStuff stuff = getSymbolsForCompletion(request, CompletionType.ddoc,
156 		allocator, &rba, cache, moduleCache);
157 	if (stuff.symbols.length == 0)
158 		warning("Could not find symbol");
159 	else
160 	{
161 		Appender!(char[]) app;
162 
163 		bool isDitto(string s)
164 		{
165 			import std.uni : icmp;
166 			if (s.length > 5)
167 				return false;
168 			else
169 				return s.icmp("ditto") == 0;
170 		}
171 
172 		void putDDocChar(char c)
173 		{
174 			switch (c)
175 			{
176 			case '\\':
177 				app.put('\\');
178 				app.put('\\');
179 				break;
180 			case '\n':
181 				app.put('\\');
182 				app.put('n');
183 				break;
184 			default:
185 				app.put(c);
186 				break;
187 			}
188 		}
189 
190 		void putDDocString(string s)
191 		{
192 			foreach (char c; s)
193 				putDDocChar(c);
194 		}
195 
196 		foreach(ref symbol; stuff.symbols.filter!(a => !a.doc.empty && !isDitto(a.doc)))
197 		{
198 			app.clear;
199 			putDDocString(symbol.doc);
200 			response.docComments ~= app.data.idup;
201 		}
202 	}
203 	return response;
204 }
205 
206 /**
207  * Finds the declaration of the symbol at the cursor position.
208  * Params:
209  *     request = the autocompletion request
210  * Returns:
211  *     the autocompletion response
212  */
213 public AutocompleteResponse findDeclaration(const AutocompleteRequest request,
214 	ref ModuleCache moduleCache)
215 {
216 	AutocompleteResponse response;
217 	RollbackAllocator rba;
218 	auto allocator = scoped!(ASTAllocator)();
219 	auto cache = StringCache(StringCache.defaultBucketCount);
220 	SymbolStuff stuff = getSymbolsForCompletion(request,
221 		CompletionType.location, allocator, &rba, cache, moduleCache);
222 	scope(exit) stuff.destroy();
223 	if (stuff.symbols.length > 0)
224 	{
225 		response.symbolLocation = stuff.symbols[0].location;
226 		response.symbolFilePath = stuff.symbols[0].symbolFile.idup;
227 	}
228 	else
229 		warning("Could not find symbol declaration");
230 	return response;
231 }
232 
233 /**
234  * Handles autocompletion
235  * Params:
236  *     request = the autocompletion request
237  * Returns:
238  *     the autocompletion response
239  */
240 public AutocompleteResponse complete(const AutocompleteRequest request,
241 	ref ModuleCache moduleCache)
242 {
243 	const(Token)[] tokenArray;
244 	auto stringCache = StringCache(StringCache.defaultBucketCount);
245 	auto beforeTokens = getTokensBeforeCursor(request.sourceCode,
246 		request.cursorPosition, stringCache, tokenArray);
247 	if (beforeTokens.length >= 2)
248 	{
249 		if (beforeTokens[$ - 1] == tok!"(" || beforeTokens[$ - 1] == tok!"[")
250 		{
251 			return parenCompletion(beforeTokens, tokenArray, request.cursorPosition,
252 				moduleCache);
253 		}
254 		else if (beforeTokens[$ - 1] == tok!",")
255 		{
256 			immutable size_t end = goBackToOpenParen(beforeTokens);
257 			if (end != size_t.max)
258 				return parenCompletion(beforeTokens[0 .. end], tokenArray,
259 					request.cursorPosition, moduleCache);
260 		}
261 		else
262 		{
263 			ImportKind kind = determineImportKind(beforeTokens);
264 			if (kind == ImportKind.neither)
265 			{
266 				if (beforeTokens.isUdaExpression)
267 					beforeTokens = beforeTokens[$-1 .. $];
268 				return dotCompletion(beforeTokens, tokenArray, request.cursorPosition,
269 					moduleCache);
270             }
271 			else
272 				return importCompletion(beforeTokens, kind, moduleCache);
273 		}
274 	}
275 	return dotCompletion(beforeTokens, tokenArray, request.cursorPosition, moduleCache);
276 }
277 
278 /**
279  *
280  */
281 public AutocompleteResponse symbolSearch(const AutocompleteRequest request,
282 	ref ModuleCache moduleCache)
283 {
284 	import containers.ttree : TTree;
285 
286 	LexerConfig config;
287 	config.fileName = "";
288 	auto cache = StringCache(StringCache.defaultBucketCount);
289 	const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode,
290 		config, &cache);
291 	auto allocator = scoped!(ASTAllocator)();
292 	RollbackAllocator rba;
293 	ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
294 		&rba, request.cursorPosition, moduleCache);
295 	scope(exit) pair.destroy();
296 
297 	static struct SearchResults
298 	{
299 		void put(const(DSymbol)* symbol)
300 		{
301 			tree.insert(SearchResult(symbol));
302 		}
303 
304 		static struct SearchResult
305 		{
306 			const(DSymbol)* symbol;
307 
308 			int opCmp(ref const SearchResult other) const pure nothrow
309 			{
310 				if (other.symbol.symbolFile < symbol.symbolFile)
311 					return -1;
312 				if (other.symbol.symbolFile > symbol.symbolFile)
313 					return 1;
314 				if (other.symbol.location < symbol.location)
315 					return -1;
316 				return other.symbol.location > symbol.location;
317 			}
318 		}
319 
320 		TTree!(SearchResult) tree;
321 	}
322 
323 	SearchResults results;
324 	HashSet!size_t visited;
325 	foreach (symbol; pair.scope_.symbols)
326 		symbol.getParts!SearchResults(internString(request.searchName), results, visited);
327 	foreach (s; moduleCache.getAllSymbols())
328 		s.symbol.getParts!SearchResults(internString(request.searchName), results, visited);
329 
330 	AutocompleteResponse response;
331 	foreach (result; results.tree[])
332 	{
333 		response.locations ~= result.symbol.location;
334 		response.completionKinds ~= result.symbol.kind;
335 		response.completions ~= result.symbol.symbolFile;
336 	}
337 
338 	return response;
339 }
340 
341 /******************************************************************************/
342 private:
343 
344 enum ImportKind : ubyte
345 {
346 	selective,
347 	normal,
348 	neither
349 }
350 
351 /**
352  * Handles dot completion for identifiers and types.
353  * Params:
354  *     beforeTokens = the tokens before the cursor
355  *     tokenArray = all tokens in the file
356  *     cursorPosition = the cursor position in bytes
357  * Returns:
358  *     the autocompletion response
359  */
360 AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
361 	size_t cursorPosition, ref ModuleCache moduleCache)
362 {
363 	AutocompleteResponse response;
364 
365 	// Partial symbol name appearing after the dot character and before the
366 	// cursor.
367 	string partial;
368 
369 	// Type of the token before the dot, or identifier if the cursor was at
370 	// an identifier.
371 	IdType significantTokenType;
372 
373 	if (beforeTokens.length >= 1 && beforeTokens[$ - 1] == tok!"identifier")
374 	{
375 		// Set partial to the slice of the identifier between the beginning
376 		// of the identifier and the cursor. This improves the completion
377 		// responses when the cursor is in the middle of an identifier instead
378 		// of at the end
379 		auto t = beforeTokens[$ - 1];
380 		if (cursorPosition - t.index >= 0 && cursorPosition - t.index <= t.text.length)
381 			partial = t.text[0 .. cursorPosition - t.index];
382 		significantTokenType = tok!"identifier";
383 		beforeTokens = beforeTokens[0 .. $ - 1];
384 	}
385 	else if (beforeTokens.length >= 2 && beforeTokens[$ - 1] ==  tok!".")
386 		significantTokenType = beforeTokens[$ - 2].type;
387 	else
388 		return response;
389 
390 	switch (significantTokenType)
391 	{
392 	case tok!"stringLiteral":
393 	case tok!"wstringLiteral":
394 	case tok!"dstringLiteral":
395 		foreach (symbol; arraySymbols)
396 		{
397 			response.completionKinds ~= symbol.kind;
398 			response.completions ~= symbol.name.dup;
399 		}
400 		response.completionType = CompletionType.identifiers;
401 		break;
402 	case tok!"int":
403 	case tok!"uint":
404 	case tok!"long":
405 	case tok!"ulong":
406 	case tok!"char":
407 	case tok!"wchar":
408 	case tok!"dchar":
409 	case tok!"bool":
410 	case tok!"byte":
411 	case tok!"ubyte":
412 	case tok!"short":
413 	case tok!"ushort":
414 	case tok!"cent":
415 	case tok!"ucent":
416 	case tok!"float":
417 	case tok!"ifloat":
418 	case tok!"cfloat":
419 	case tok!"idouble":
420 	case tok!"cdouble":
421 	case tok!"double":
422 	case tok!"real":
423 	case tok!"ireal":
424 	case tok!"creal":
425 	case tok!"identifier":
426 	case tok!")":
427 	case tok!"]":
428 	case tok!"this":
429 	case tok!"super":
430 		auto allocator = scoped!(ASTAllocator)();
431 		RollbackAllocator rba;
432 		ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
433 			&rba, cursorPosition, moduleCache);
434 		scope(exit) pair.destroy();
435 		response.setCompletions(pair.scope_, getExpression(beforeTokens),
436 			cursorPosition, CompletionType.identifiers, false, partial);
437 		break;
438 	case tok!"(":
439 	case tok!"{":
440 	case tok!"[":
441 	case tok!";":
442 	case tok!":":
443 		break;
444 	default:
445 		break;
446 	}
447 	return response;
448 }
449 
450 /**
451  * Params:
452  *     sourceCode = the source code of the file being edited
453  *     cursorPosition = the cursor position in bytes
454  * Returns:
455  *     a sorted range of tokens before the cursor position
456  */
457 auto getTokensBeforeCursor(const(ubyte[]) sourceCode, size_t cursorPosition,
458 	ref StringCache cache, out const(Token)[] tokenArray)
459 {
460 	LexerConfig config;
461 	config.fileName = "";
462 	tokenArray = getTokensForParser(cast(ubyte[]) sourceCode, config, &cache);
463 	auto sortedTokens = assumeSorted(tokenArray);
464 	return sortedTokens.lowerBound(cast(size_t) cursorPosition);
465 }
466 
467 /**
468  * Params:
469  *     request = the autocompletion request
470  *     type = type the autocompletion type
471  * Returns:
472  *     all symbols that should be considered for the autocomplete list based on
473  *     the request's source code, cursor position, and completion type.
474  */
475 SymbolStuff getSymbolsForCompletion(const AutocompleteRequest request,
476 	const CompletionType type, IAllocator allocator, RollbackAllocator* rba,
477 	ref StringCache cache, ref ModuleCache moduleCache)
478 {
479 	const(Token)[] tokenArray;
480 	auto beforeTokens = getTokensBeforeCursor(request.sourceCode,
481 		request.cursorPosition, cache, tokenArray);
482 	ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
483 		rba, request.cursorPosition, moduleCache);
484 	auto expression = getExpression(beforeTokens);
485 	return SymbolStuff(getSymbolsByTokenChain(pair.scope_, expression,
486 		request.cursorPosition, type), pair.symbol, pair.scope_);
487 }
488 
489 struct SymbolStuff
490 {
491 	void destroy()
492 	{
493 		typeid(DSymbol).destroy(symbol);
494 		typeid(Scope).destroy(scope_);
495 	}
496 
497 	DSymbol*[] symbols;
498 	DSymbol* symbol;
499 	Scope* scope_;
500 }
501 
502 /**
503  * Handles paren completion for function calls and some keywords
504  * Params:
505  *     beforeTokens = the tokens before the cursor
506  *     tokenArray = all tokens in the file
507  *     cursorPosition = the cursor position in bytes
508  * Returns:
509  *     the autocompletion response
510  */
511 AutocompleteResponse parenCompletion(T)(T beforeTokens,
512 	const(Token)[] tokenArray, size_t cursorPosition, ref ModuleCache moduleCache)
513 {
514 	AutocompleteResponse response;
515 	immutable(string)[] completions;
516 	switch (beforeTokens[$ - 2].type)
517 	{
518 	case tok!"__traits":
519 		completions = traits;
520 		goto fillResponse;
521 	case tok!"scope":
522 		completions = scopes;
523 		goto fillResponse;
524 	case tok!"version":
525 		completions = predefinedVersions;
526 		goto fillResponse;
527 	case tok!"extern":
528 		completions = linkages;
529 		goto fillResponse;
530 	case tok!"pragma":
531 		completions = pragmas;
532 	fillResponse:
533 		response.completionType = CompletionType.identifiers;
534 		foreach (completion; completions)
535 		{
536 			response.completions ~= completion;
537 			response.completionKinds ~= CompletionKind.keyword;
538 		}
539 		break;
540 	case tok!"characterLiteral":
541 	case tok!"doubleLiteral":
542 	case tok!"dstringLiteral":
543 	case tok!"floatLiteral":
544 	case tok!"identifier":
545 	case tok!"idoubleLiteral":
546 	case tok!"ifloatLiteral":
547 	case tok!"intLiteral":
548 	case tok!"irealLiteral":
549 	case tok!"longLiteral":
550 	case tok!"realLiteral":
551 	case tok!"stringLiteral":
552 	case tok!"uintLiteral":
553 	case tok!"ulongLiteral":
554 	case tok!"wstringLiteral":
555 	case tok!"this":
556 	case tok!"super":
557 	case tok!")":
558 	case tok!"]":
559 		auto allocator = scoped!(ASTAllocator)();
560 		RollbackAllocator rba;
561 		ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
562 			&rba, cursorPosition, moduleCache);
563 		scope(exit) pair.destroy();
564 		auto expression = getExpression(beforeTokens[0 .. $ - 1]);
565 		response.setCompletions(pair.scope_, expression,
566 			cursorPosition, CompletionType.calltips, beforeTokens[$ - 1] == tok!"[");
567 		break;
568 	default:
569 		break;
570 	}
571 	return response;
572 }
573 
574 /**
575  * Determines if an import is selective, whole-module, or neither.
576  */
577 ImportKind determineImportKind(T)(T tokens)
578 {
579 	assert (tokens.length > 1);
580 	size_t i = tokens.length - 1;
581 	if (!(tokens[i] == tok!":" || tokens[i] == tok!"," || tokens[i] == tok!"."
582 			|| tokens[i] == tok!"identifier"))
583 		return ImportKind.neither;
584 	bool foundColon = false;
585 	while (true) switch (tokens[i].type)
586 	{
587 	case tok!":":
588 		foundColon = true;
589 		goto case;
590 	case tok!"identifier":
591 	case tok!"=":
592 	case tok!".":
593 	case tok!",":
594 		if (i == 0)
595 			return ImportKind.neither;
596 		else
597 			i--;
598 		break;
599 	case tok!"import":
600 		return foundColon ? ImportKind.selective : ImportKind.normal;
601 	default:
602 		return ImportKind.neither;
603 	}
604 	return ImportKind.neither;
605 }
606 
607 unittest
608 {
609 	import std.stdio : writeln;
610 
611 	Token[] t = [
612 		Token(tok!"import"), Token(tok!"identifier"), Token(tok!"."),
613 		Token(tok!"identifier"), Token(tok!":"), Token(tok!"identifier"), Token(tok!",")
614 	];
615 	assert(determineImportKind(t) == ImportKind.selective);
616 	Token[] t2;
617 	t2 ~= Token(tok!"else");
618 	t2 ~= Token(tok!":");
619 	assert(determineImportKind(t2) == ImportKind.neither);
620 	writeln("Unittest for determineImportKind() passed");
621 }
622 
623 /**
624  * Provides autocomplete for selective imports, e.g.:
625  * ---
626  * import std.algorithm: balancedParens;
627  * ---
628  */
629 AutocompleteResponse importCompletion(T)(T beforeTokens, ImportKind kind,
630 	ref ModuleCache moduleCache)
631 in
632 {
633 	assert (beforeTokens.length >= 2);
634 }
635 body
636 {
637 	AutocompleteResponse response;
638 	if (beforeTokens.length <= 2)
639 		return response;
640 
641 	size_t i = beforeTokens.length - 1;
642 
643 	if (kind == ImportKind.normal)
644 	{
645 
646 		while (beforeTokens[i].type != tok!"," && beforeTokens[i].type != tok!"import"
647 				&& beforeTokens[i].type != tok!"=" )
648 			i--;
649 		setImportCompletions(beforeTokens[i .. $], response, moduleCache);
650 		return response;
651 	}
652 
653 	loop: while (true) switch (beforeTokens[i].type)
654 	{
655 	case tok!"identifier":
656 	case tok!"=":
657 	case tok!",":
658 	case tok!".":
659 		i--;
660 		break;
661 	case tok!":":
662 		i--;
663 		while (beforeTokens[i].type == tok!"identifier" || beforeTokens[i].type == tok!".")
664 			i--;
665 		break loop;
666 	default:
667 		break loop;
668 	}
669 
670 	size_t j = i;
671 	loop2: while (j <= beforeTokens.length) switch (beforeTokens[j].type)
672 	{
673 	case tok!":": break loop2;
674 	default: j++; break;
675 	}
676 
677 	if (i >= j)
678 	{
679 		warning("Malformed import statement");
680 		return response;
681 	}
682 
683 	immutable string path = beforeTokens[i + 1 .. j]
684 		.filter!(token => token.type == tok!"identifier")
685 		.map!(token => cast() token.text)
686 		.joiner(dirSeparator)
687 		.text();
688 
689 	string resolvedLocation = moduleCache.resolveImportLocation(path);
690 	if (resolvedLocation is null)
691 	{
692 		warning("Could not resolve location of ", path);
693 		return response;
694 	}
695 	auto symbols = moduleCache.getModuleSymbol(internString(resolvedLocation));
696 
697 	import containers.hashset : HashSet;
698 	HashSet!string h;
699 
700 	void addSymbolToResponses(const(DSymbol)* sy)
701 	{
702 		auto a = DSymbol(sy.name);
703 		if (!builtinSymbols.contains(&a) && sy.name !is null && !h.contains(sy.name)
704 				&& !sy.skipOver && sy.name != CONSTRUCTOR_SYMBOL_NAME
705 				&& isPublicCompletionKind(sy.kind))
706 		{
707 			response.completionKinds ~= sy.kind;
708 			response.completions ~= sy.name;
709 			h.insert(sy.name);
710 		}
711 	}
712 
713 	foreach (s; symbols.opSlice().filter!(a => !a.skipOver))
714 	{
715 		if (s.kind == CompletionKind.importSymbol && s.type !is null)
716 			foreach (sy; s.type.opSlice().filter!(a => !a.skipOver))
717 				addSymbolToResponses(sy);
718 		else
719 			addSymbolToResponses(s);
720 	}
721 	response.completionType = CompletionType.identifiers;
722 	return response;
723 }
724 
725 /**
726  * Populates the response with completion information for an import statement
727  * Params:
728  *     tokens = the tokens after the "import" keyword and before the cursor
729  *     response = the response that should be populated
730  */
731 void setImportCompletions(T)(T tokens, ref AutocompleteResponse response,
732 	ref ModuleCache cache)
733 {
734 	response.completionType = CompletionType.identifiers;
735 	string partial = null;
736 	if (tokens[$ - 1].type == tok!"identifier")
737 	{
738 		partial = tokens[$ - 1].text;
739 		tokens = tokens[0 .. $ - 1];
740 	}
741 	auto moduleParts = tokens.filter!(a => a.type == tok!"identifier").map!("a.text").array();
742 	string path = buildPath(moduleParts);
743 
744 	bool found = false;
745 
746 	foreach (importPath; cache.getImportPaths())
747 	{
748 		if (importPath.isFile)
749 		{
750 			if (!exists(importPath))
751 				continue;
752 
753 			found = true;
754 
755 			auto n = importPath.baseName(".d").baseName(".di");
756 			if (isFile(importPath) && (importPath.endsWith(".d") || importPath.endsWith(".di"))
757 					&& (partial is null || n.startsWith(partial)))
758 			{
759 				response.completions ~= n;
760 				response.completionKinds ~= CompletionKind.moduleName;
761 			}
762 		}
763 		else
764 		{
765 			string p = buildPath(importPath, path);
766 			if (!exists(p))
767 				continue;
768 
769 			found = true;
770 
771 			try foreach (string name; dirEntries(p, SpanMode.shallow))
772 			{
773 				import std.path: baseName;
774 				if (name.baseName.startsWith(".#"))
775 					continue;
776 
777 				auto n = name.baseName(".d").baseName(".di");
778 				if (isFile(name) && (name.endsWith(".d") || name.endsWith(".di"))
779 					&& (partial is null || n.startsWith(partial)))
780 				{
781 					response.completions ~= n;
782 					response.completionKinds ~= CompletionKind.moduleName;
783 				}
784 				else if (isDir(name))
785 				{
786 					if (n[0] != '.' && (partial is null || n.startsWith(partial)))
787 					{
788 						response.completions ~= n;
789 						response.completionKinds ~=
790 							exists(buildPath(name, "package.d")) || exists(buildPath(name, "package.di"))
791 							? CompletionKind.moduleName : CompletionKind.packageName;
792 					}
793 				}
794 			}
795 			catch(FileException)
796 			{
797 				warning("Cannot access import path: ", importPath);
798 			}
799 		}
800 	}
801 	if (!found)
802 		warning("Could not find ", moduleParts);
803 }
804 
805 static void skip(alias O, alias C, T)(T t, ref size_t i)
806 {
807 	int depth = 1;
808 	while (i < t.length) switch (t[i].type)
809 	{
810 	case O:
811 		i++;
812 		depth++;
813 		break;
814 	case C:
815 		i++;
816 		depth--;
817 		if (depth <= 0)
818 			return;
819 		break;
820 	default:
821 		i++;
822 		break;
823 	}
824 }
825 
826 bool isSliceExpression(T)(T tokens, size_t index)
827 {
828 	while (index < tokens.length) switch (tokens[index].type)
829 	{
830 	case tok!"[":
831 		skip!(tok!"[", tok!"]")(tokens, index);
832 		break;
833 	case tok!"(":
834 		skip!(tok!"(", tok!")")(tokens, index);
835 		break;
836 	case tok!"]":
837 	case tok!"}":
838 		return false;
839 	case tok!"..":
840 		return true;
841 	default:
842 		index++;
843 		break;
844 	}
845 	return false;
846 }
847 
848 /**
849  *
850  */
851 DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
852 	T tokens, size_t cursorPosition, CompletionType completionType)
853 {
854 	//writeln(">>>");
855 	//dumpTokens(tokens.release);
856 	//writeln(">>>");
857 
858 	static size_t skipEnd(T tokenSlice, size_t i, IdType open, IdType close)
859 	{
860 		size_t j = i + 1;
861 		for (int depth = 1; depth > 0 && j < tokenSlice.length; j++)
862 		{
863 			if (tokenSlice[j].type == open)
864 				depth++;
865 			else if (tokenSlice[j].type == close)
866 			{
867 				depth--;
868 				if (depth == 0) break;
869 			}
870 		}
871 		return j;
872 	}
873 
874 	// Find the symbol corresponding to the beginning of the chain
875 	DSymbol*[] symbols;
876 	if (tokens.length == 0)
877 		return [];
878 	// Recurse in case the symbol chain starts with an expression in parens
879 	// e.g. (a.b!c).d
880 	if (tokens[0] == tok!"(")
881 	{
882 		immutable j = skipEnd(tokens, 0, tok!"(", tok!")");
883 		symbols = getSymbolsByTokenChain(completionScope, tokens[1 .. j],
884 				cursorPosition, completionType);
885 		tokens = tokens[j + 1 .. $];
886 		//writeln("<<<");
887 		//dumpTokens(tokens.release);
888 		//writeln("<<<");
889 		if (tokens.length == 0) // workaround (#371)
890 			return [];
891 	}
892 	else if (tokens[0] == tok!"." && tokens.length > 1)
893 	{
894 		tokens = tokens[1 .. $];
895 		if (tokens.length == 0)	// workaround (#371)
896 			return [];
897 		symbols = completionScope.getSymbolsAtGlobalScope(stringToken(tokens[0]));
898 	}
899 	else
900 		symbols = completionScope.getSymbolsByNameAndCursor(stringToken(tokens[0]), cursorPosition);
901 
902 	if (symbols.length == 0)
903 	{
904 		//TODO: better bugfix for issue #368, see test case 52 or pull #371
905 		if (tokens.length)
906 			warning("Could not find declaration of ", stringToken(tokens[0]),
907 				" from position ", cursorPosition);
908 		else assert(0, "internal error");
909 		return [];
910 	}
911 
912 	// If the `symbols` array contains functions, and one of them returns
913 	// void and the others do not, this is a property function. For the
914 	// purposes of chaining auto-complete we want to ignore the one that
915 	// returns void. This is a no-op if we are getting doc comments.
916 	void filterProperties() @nogc @safe
917 	{
918 		if (symbols.length == 0 || completionType == CompletionType.ddoc)
919 			return;
920 		if (symbols[0].kind == CompletionKind.functionName
921 			|| symbols[0].qualifier == SymbolQualifier.func)
922 		{
923 			int voidRets = 0;
924 			int nonVoidRets = 0;
925 			size_t firstNonVoidIndex = size_t.max;
926 			foreach (i, sym; symbols)
927 			{
928 				if (sym.type is null)
929 					return;
930 				if (&sym.type.name[0] == &getBuiltinTypeName(tok!"void")[0])
931 					voidRets++;
932 				else
933 				{
934 					nonVoidRets++;
935 					firstNonVoidIndex = min(firstNonVoidIndex, i);
936 				}
937 			}
938 			if (voidRets > 0 && nonVoidRets > 0)
939 				symbols = symbols[firstNonVoidIndex .. $];
940 		}
941 	}
942 
943 	filterProperties();
944 
945 	if (shouldSwapWithType(completionType, symbols[0].kind, 0, tokens.length - 1))
946 	{
947 		//trace("Swapping types");
948 		if (symbols.length == 0 || symbols[0].type is null || symbols[0].type is symbols[0])
949 			return [];
950 		else if (symbols[0].type.kind == CompletionKind.functionName)
951 		{
952 			if (symbols[0].type.type is null)
953 				symbols = [];
954 			else
955 				symbols = [symbols[0].type.type];
956 		}
957 		else
958 			symbols = [symbols[0].type];
959 	}
960 
961 	loop: for (size_t i = 1; i < tokens.length; i++)
962 	{
963 		void skip(IdType open, IdType close)
964 		{
965 			i = skipEnd(tokens, i, open, close);
966 		}
967 
968 		switch (tokens[i].type)
969 		{
970 		case tok!"int":
971 		case tok!"uint":
972 		case tok!"long":
973 		case tok!"ulong":
974 		case tok!"char":
975 		case tok!"wchar":
976 		case tok!"dchar":
977 		case tok!"bool":
978 		case tok!"byte":
979 		case tok!"ubyte":
980 		case tok!"short":
981 		case tok!"ushort":
982 		case tok!"cent":
983 		case tok!"ucent":
984 		case tok!"float":
985 		case tok!"ifloat":
986 		case tok!"cfloat":
987 		case tok!"idouble":
988 		case tok!"cdouble":
989 		case tok!"double":
990 		case tok!"real":
991 		case tok!"ireal":
992 		case tok!"creal":
993 		case tok!"this":
994 		case tok!"super":
995 			symbols = symbols[0].getPartsByName(internString(str(tokens[i].type)));
996 			if (symbols.length == 0)
997 				break loop;
998 			break;
999 		case tok!"identifier":
1000 			//trace(symbols[0].qualifier, " ", symbols[0].kind);
1001 			filterProperties();
1002 
1003 			if (symbols.length == 0)
1004 				break loop;
1005 
1006 			// Use type instead of the symbol itself for certain symbol kinds
1007 			while (symbols[0].qualifier == SymbolQualifier.func
1008 				|| symbols[0].kind == CompletionKind.functionName
1009 				|| (symbols[0].kind == CompletionKind.moduleName
1010 					&& symbols[0].type !is null && symbols[0].type.kind == CompletionKind.importSymbol)
1011 				|| symbols[0].kind == CompletionKind.importSymbol
1012 				|| symbols[0].kind == CompletionKind.aliasName)
1013 			{
1014 				symbols = symbols[0].type is null || symbols[0].type is symbols[0] ? [] : [symbols[0].type];
1015 				if (symbols.length == 0)
1016 					break loop;
1017 			}
1018 
1019 			//trace("looking for ", tokens[i].text, " in ", symbols[0].name);
1020 			symbols = symbols[0].getPartsByName(internString(tokens[i].text));
1021 			//trace("symbols: ", symbols.map!(a => a.name));
1022 			filterProperties();
1023 			if (symbols.length == 0)
1024 			{
1025 				//trace("Couldn't find it.");
1026 				break loop;
1027 			}
1028 			if (shouldSwapWithType(completionType, symbols[0].kind, i, tokens.length - 1))
1029 			{
1030 				symbols = symbols[0].type is null || symbols[0].type is symbols[0] ? [] : [symbols[0].type];
1031 				if (symbols.length == 0)
1032 					break loop;
1033 			}
1034 			if ((symbols[0].kind == CompletionKind.aliasName
1035 				|| symbols[0].kind == CompletionKind.moduleName)
1036 				&& (completionType == CompletionType.identifiers
1037 				|| i + 1 < tokens.length))
1038 			{
1039 				symbols = symbols[0].type is null || symbols[0].type is symbols[0] ? [] : [symbols[0].type];
1040 			}
1041 			if (symbols.length == 0)
1042 				break loop;
1043 			if (tokens[i].type == tok!"!")
1044 			{
1045 				i++;
1046 				if (tokens[i].type == tok!"(")
1047 					goto case;
1048 				else
1049 					i++;
1050 			}
1051 			break;
1052 		case tok!"(":
1053 			skip(tok!"(", tok!")");
1054 			break;
1055 		case tok!"[":
1056 			if (symbols[0].qualifier == SymbolQualifier.array)
1057 			{
1058 				skip(tok!"[", tok!"]");
1059 				if (!isSliceExpression(tokens, i))
1060 				{
1061 					symbols = symbols[0].type is null || symbols[0].type is symbols[0] ? [] : [symbols[0].type];
1062 					if (symbols.length == 0)
1063 						break loop;
1064 				}
1065 			}
1066 			else if (symbols[0].qualifier == SymbolQualifier.assocArray)
1067 			{
1068 				symbols = symbols[0].type is null || symbols[0].type is symbols[0] ? [] : [symbols[0].type];
1069 				skip(tok!"[", tok!"]");
1070 			}
1071 			else
1072 			{
1073 				skip(tok!"[", tok!"]");
1074 				DSymbol*[] overloads;
1075 				if (isSliceExpression(tokens, i))
1076 					overloads = symbols[0].getPartsByName(internString("opSlice"));
1077 				else
1078 					overloads = symbols[0].getPartsByName(internString("opIndex"));
1079 				if (overloads.length > 0)
1080 				{
1081 					symbols = overloads[0].type is null ? [] : [overloads[0].type];
1082 				}
1083 				else
1084 					return [];
1085 			}
1086 			break;
1087 		case tok!".":
1088 			break;
1089 		default:
1090 			break loop;
1091 		}
1092 	}
1093 	return symbols;
1094 }
1095 
1096 /**
1097  *
1098  */
1099 void setCompletions(T)(ref AutocompleteResponse response,
1100 	Scope* completionScope, T tokens, size_t cursorPosition,
1101 	CompletionType completionType, bool isBracket = false, string partial = null)
1102 {
1103 	static void addSymToResponse(const(DSymbol)* s, ref AutocompleteResponse r, string p,
1104 		size_t[] circularGuard = [])
1105 	{
1106 		if (circularGuard.canFind(cast(size_t) s))
1107 			return;
1108 		foreach (sym; s.opSlice())
1109 		{
1110 			if (sym.name !is null && sym.name.length > 0 && isPublicCompletionKind(sym.kind)
1111 				&& (p is null ? true : toUpper(sym.name.data).startsWith(toUpper(p)))
1112 				&& !r.completions.canFind(sym.name)
1113 				&& sym.name[0] != '*')
1114 			{
1115 				r.completionKinds ~= sym.kind;
1116 				r.completions ~= sym.name.dup;
1117 			}
1118 			if (sym.kind == CompletionKind.importSymbol && !sym.skipOver && sym.type !is null)
1119 				addSymToResponse(sym.type, r, p, circularGuard ~ (cast(size_t) s));
1120 		}
1121 	}
1122 
1123 	// Handle the simple case where we get all symbols in scope and filter it
1124 	// based on the currently entered text.
1125 	if (partial !is null && tokens.length == 0)
1126 	{
1127 		auto currentSymbols = completionScope.getSymbolsInCursorScope(cursorPosition);
1128 		foreach (s; currentSymbols.filter!(a => isPublicCompletionKind(a.kind)
1129 				&& toUpper(a.name.data).startsWith(toUpper(partial))))
1130 		{
1131 			response.completionKinds ~= s.kind;
1132 			response.completions ~= s.name.dup;
1133 		}
1134 		response.completionType = CompletionType.identifiers;
1135 		return;
1136 	}
1137 
1138 	if (tokens.length == 0)
1139 		return;
1140 
1141 	DSymbol*[] symbols = getSymbolsByTokenChain(completionScope, tokens,
1142 		cursorPosition, completionType);
1143 
1144 	if (symbols.length == 0)
1145 		return;
1146 
1147 	if (completionType == CompletionType.identifiers)
1148 	{
1149 		while (symbols[0].qualifier == SymbolQualifier.func
1150 				|| symbols[0].kind == CompletionKind.functionName
1151 				|| symbols[0].kind == CompletionKind.importSymbol
1152 				|| symbols[0].kind == CompletionKind.aliasName)
1153 		{
1154 			symbols = symbols[0].type is null || symbols[0].type is symbols[0] ? []
1155 				: [symbols[0].type];
1156 			if (symbols.length == 0)
1157 				return;
1158 		}
1159 		addSymToResponse(symbols[0], response, partial);
1160 		response.completionType = CompletionType.identifiers;
1161 	}
1162 	else if (completionType == CompletionType.calltips)
1163 	{
1164 		//trace("Showing call tips for ", symbols[0].name, " of kind ", symbols[0].kind);
1165 		if (symbols[0].kind != CompletionKind.functionName
1166 			&& symbols[0].callTip is null)
1167 		{
1168 			if (symbols[0].kind == CompletionKind.aliasName)
1169 			{
1170 				if (symbols[0].type is null || symbols[0].type is symbols[0])
1171 					return;
1172 				symbols = [symbols[0].type];
1173 			}
1174 			if (symbols[0].kind == CompletionKind.variableName)
1175 			{
1176 				auto dumb = symbols[0].type;
1177 				if (dumb !is null)
1178 				{
1179 					if (dumb.kind == CompletionKind.functionName)
1180 					{
1181 						symbols = [dumb];
1182 						goto setCallTips;
1183 					}
1184 					if (isBracket)
1185 					{
1186 						auto index = dumb.getPartsByName(internString("opIndex"));
1187 						if (index.length > 0)
1188 						{
1189 							symbols = index;
1190 							goto setCallTips;
1191 						}
1192 					}
1193 					auto call = dumb.getPartsByName(internString("opCall"));
1194 					if (call.length > 0)
1195 					{
1196 						symbols = call;
1197 						goto setCallTips;
1198 					}
1199 				}
1200 			}
1201 			if (symbols[0].kind == CompletionKind.structName
1202 				|| symbols[0].kind == CompletionKind.className)
1203 			{
1204 				auto constructor = symbols[0].getPartsByName(CONSTRUCTOR_SYMBOL_NAME);
1205 				if (constructor.length == 0)
1206 				{
1207 					// Build a call tip out of the struct fields
1208 					if (symbols[0].kind == CompletionKind.structName)
1209 					{
1210 						response.completionType = CompletionType.calltips;
1211 						response.completions = [generateStructConstructorCalltip(symbols[0])];
1212 						return;
1213 					}
1214 				}
1215 				else
1216 				{
1217 					symbols = constructor;
1218 					goto setCallTips;
1219 				}
1220 			}
1221 		}
1222 	setCallTips:
1223 		response.completionType = CompletionType.calltips;
1224 		foreach (symbol; symbols)
1225 		{
1226 			if (symbol.kind != CompletionKind.aliasName && symbol.callTip !is null)
1227 				response.completions ~= symbol.callTip;
1228 		}
1229 	}
1230 }
1231 
1232 string generateStructConstructorCalltip(const DSymbol* symbol)
1233 in
1234 {
1235 	assert(symbol.kind == CompletionKind.structName);
1236 }
1237 body
1238 {
1239 	string generatedStructConstructorCalltip = "this(";
1240 	const(DSymbol)*[] fields = symbol.opSlice().filter!(
1241 		a => a.kind == CompletionKind.variableName).map!(a => cast(const(DSymbol)*) a).array();
1242 	fields.sort!((a, b) => a.location < b.location);
1243 	foreach (i, field; fields)
1244 	{
1245 		if (field.kind != CompletionKind.variableName)
1246 			continue;
1247 		i++;
1248 		if (field.type !is null)
1249 		{
1250 			generatedStructConstructorCalltip ~= field.type.name;
1251 			generatedStructConstructorCalltip ~= " ";
1252 		}
1253 		generatedStructConstructorCalltip ~= field.name;
1254 		if (i < fields.length)
1255 			generatedStructConstructorCalltip ~= ", ";
1256 	}
1257 	generatedStructConstructorCalltip ~= ")";
1258 	return generatedStructConstructorCalltip;
1259 }
1260 
1261 private enum TYPE_IDENT_AND_LITERAL_CASES = q{
1262 	case tok!"int":
1263 	case tok!"uint":
1264 	case tok!"long":
1265 	case tok!"ulong":
1266 	case tok!"char":
1267 	case tok!"wchar":
1268 	case tok!"dchar":
1269 	case tok!"bool":
1270 	case tok!"byte":
1271 	case tok!"ubyte":
1272 	case tok!"short":
1273 	case tok!"ushort":
1274 	case tok!"cent":
1275 	case tok!"ucent":
1276 	case tok!"float":
1277 	case tok!"ifloat":
1278 	case tok!"cfloat":
1279 	case tok!"idouble":
1280 	case tok!"cdouble":
1281 	case tok!"double":
1282 	case tok!"real":
1283 	case tok!"ireal":
1284 	case tok!"creal":
1285 	case tok!"this":
1286 	case tok!"super":
1287 	case tok!"identifier":
1288 	case tok!"stringLiteral":
1289 	case tok!"wstringLiteral":
1290 	case tok!"dstringLiteral":
1291 };
1292 
1293 bool isUdaExpression(T)(ref T tokens)
1294 {
1295 	bool result;
1296 	ptrdiff_t skip;
1297 	ptrdiff_t i = tokens.length - 2;
1298 	
1299 	if (i < 1)
1300 		return result;
1301 	
1302 	// skips the UDA ctor
1303 	if (tokens[i].type == tok!")")
1304 	{
1305 		++skip;
1306 		--i;
1307 		while (i >= 2)
1308 		{
1309 			skip += tokens[i].type == tok!")";
1310 			skip -= tokens[i].type == tok!"(";
1311 			--i;
1312 			if (skip == 0)
1313 			{
1314 				// @UDA!(TemplateParameters)(FunctionParameters)
1315 				if (i > 3 && tokens[i].type == tok!"!" && tokens[i-1].type == tok!")")
1316 				{
1317 					skip = 1;
1318 					i -= 2;
1319 					continue;
1320 				}
1321 				else break;
1322 			}
1323 		}
1324 	}
1325 	
1326 	if (skip == 0)
1327 	{
1328 		// @UDA!SingleTemplateParameter
1329 		if (i > 2 && tokens[i].type == tok!"identifier" && tokens[i-1].type == tok!"!")
1330 		{
1331 			i -= 2;
1332 		}
1333 
1334 		// @UDA
1335 		if (i > 0 && tokens[i].type == tok!"identifier" && tokens[i-1].type == tok!"@")
1336 		{
1337 			result = true;
1338 		}
1339 	}
1340     
1341 	return result;
1342 }
1343 
1344 /**
1345  *
1346  */
1347 T getExpression(T)(T beforeTokens)
1348 {
1349 	enum EXPRESSION_LOOP_BREAK = q{
1350 		if (i + 1 < beforeTokens.length) switch (beforeTokens[i + 1].type)
1351 		{
1352 		mixin (TYPE_IDENT_AND_LITERAL_CASES);
1353 			i++;
1354 			break expressionLoop;
1355 		default:
1356 			break;
1357 		}
1358 	};
1359 
1360 	if (beforeTokens.length == 0)
1361 		return beforeTokens[0 .. 0];
1362 	size_t i = beforeTokens.length - 1;
1363 	size_t sliceEnd = beforeTokens.length;
1364 	IdType open;
1365 	IdType close;
1366 	uint skipCount = 0;
1367 
1368 	expressionLoop: while (true)
1369 	{
1370 		switch (beforeTokens[i].type)
1371 		{
1372 		case tok!"import":
1373 			i++;
1374 			break expressionLoop;
1375 		mixin (TYPE_IDENT_AND_LITERAL_CASES);
1376 			mixin (EXPRESSION_LOOP_BREAK);
1377 			break;
1378 		case tok!".":
1379 			break;
1380 		case tok!")":
1381 			open = tok!")";
1382 			close = tok!"(";
1383 			goto skip;
1384 		case tok!"]":
1385 			open = tok!"]";
1386 			close = tok!"[";
1387 		skip:
1388 			mixin (EXPRESSION_LOOP_BREAK);
1389 			immutable bookmark = i;
1390 			int depth = 1;
1391 			do
1392 			{
1393 				if (depth == 0 || i == 0)
1394 					break;
1395 				else
1396 					i--;
1397 				if (beforeTokens[i].type == open)
1398 					depth++;
1399 				else if (beforeTokens[i].type == close)
1400 					depth--;
1401 			} while (true);
1402 
1403 			skipCount++;
1404 
1405 			// check the current token after skipping parens to the left.
1406 			// if it's a loop keyword, pretend we never skipped the parens.
1407 			if (i > 0) switch (beforeTokens[i - 1].type)
1408 			{
1409 				case tok!"scope":
1410 				case tok!"if":
1411 				case tok!"while":
1412 				case tok!"for":
1413 				case tok!"foreach":
1414 				case tok!"foreach_reverse":
1415 				case tok!"do":
1416 				case tok!"cast":
1417 				case tok!"catch":
1418 					i = bookmark + 1;
1419 					break expressionLoop;
1420 				case tok!"!":
1421 					if (skipCount == 1)
1422 					{
1423 						sliceEnd = i - 1;
1424 						i -= 2;
1425 					}
1426 					break expressionLoop;
1427 				default:
1428 					break;
1429 			}
1430 			break;
1431 		default:
1432 			i++;
1433 			break expressionLoop;
1434 		}
1435 		if (i == 0)
1436 			break;
1437 		else
1438 			i--;
1439 	}
1440 	return beforeTokens[i .. sliceEnd];
1441 }
1442 
1443 size_t goBackToOpenParen(T)(T beforeTokens)
1444 in
1445 {
1446 	assert (beforeTokens.length > 0);
1447 }
1448 body
1449 {
1450 	size_t i = beforeTokens.length - 1;
1451 	IdType open;
1452 	IdType close;
1453 	while (true) switch (beforeTokens[i].type)
1454 	{
1455 	case tok!",":
1456 	case tok!".":
1457 	case tok!"*":
1458 	case tok!"&":
1459 	case tok!"doubleLiteral":
1460 	case tok!"floatLiteral":
1461 	case tok!"idoubleLiteral":
1462 	case tok!"ifloatLiteral":
1463 	case tok!"intLiteral":
1464 	case tok!"longLiteral":
1465 	case tok!"realLiteral":
1466 	case tok!"irealLiteral":
1467 	case tok!"uintLiteral":
1468 	case tok!"ulongLiteral":
1469 	case tok!"characterLiteral":
1470 	mixin(TYPE_IDENT_AND_LITERAL_CASES);
1471 		if (i == 0)
1472 			return size_t.max;
1473 		else
1474 			i--;
1475 		break;
1476 	case tok!"(":
1477 	case tok!"[":
1478 		return i + 1;
1479 	case tok!")":
1480 		open = tok!")";
1481 		close = tok!"(";
1482 		goto skip;
1483 	case tok!"}":
1484 		open = tok!"}";
1485 		close = tok!"{";
1486 		goto skip;
1487 	case tok!"]":
1488 		open = tok!"]";
1489 		close = tok!"[";
1490 	skip:
1491 		if (i == 0)
1492 			return size_t.max;
1493 		else
1494 			i--;
1495 		int depth = 1;
1496 		do
1497 		{
1498 			if (depth == 0 || i == 0)
1499 				break;
1500 			else
1501 				i--;
1502 			if (beforeTokens[i].type == open)
1503 				depth++;
1504 			else if (beforeTokens[i].type == close)
1505 				depth--;
1506 		} while (true);
1507 		break;
1508 	default:
1509 		return size_t.max;
1510 	}
1511 	return size_t.max;
1512 }
1513 
1514 /**
1515  * Params:
1516  *     completionType = the completion type being requested
1517  *     kind = the kind of the current item in the completion chain
1518  *     current = the index of the current item in the symbol chain
1519  *     max = the number of items in the symbol chain
1520  * Returns:
1521  *     true if the symbol should be swapped with its type field
1522  */
1523 bool shouldSwapWithType(CompletionType completionType, CompletionKind kind,
1524 	size_t current, size_t max) pure nothrow @safe
1525 {
1526 	// packages never have types, so always return false
1527 	if (kind == CompletionKind.packageName
1528 		|| kind == CompletionKind.className
1529 		|| kind == CompletionKind.structName
1530 		|| kind == CompletionKind.interfaceName
1531 		|| kind == CompletionKind.enumName
1532 		|| kind == CompletionKind.unionName
1533 		|| kind == CompletionKind.templateName
1534 		|| kind == CompletionKind.keyword)
1535 	{
1536 		return false;
1537 	}
1538 	// Swap out every part of a chain with its type except the last part
1539 	if (current < max)
1540 		return true;
1541 	// Only swap out types for these kinds
1542 	immutable bool isInteresting =
1543 		kind == CompletionKind.variableName
1544 		|| kind == CompletionKind.memberVariableName
1545 		|| kind == CompletionKind.importSymbol
1546 		|| kind == CompletionKind.aliasName
1547 		|| kind == CompletionKind.enumMember
1548 		|| kind == CompletionKind.functionName;
1549 	return isInteresting && (completionType == CompletionType.identifiers
1550 		|| (completionType == completionType.calltips && kind == CompletionKind.variableName)) ;
1551 }
1552 
1553 istring stringToken()(auto ref const Token a)
1554 {
1555 	return internString(a.text is null ? str(a.type) : a.text);
1556 }
1557 
1558 //void dumpTokens(const Token[] tokens)
1559 //{
1560 	//foreach (t; tokens)
1561 		//writeln(t.line, ":", t.column, " ", stringToken(t));
1562 //}