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 dcd.server.autocomplete.localuse;
20 
21 import std.experimental.logger;
22 import std.range;
23 import std.typecons;
24 
25 import dcd.server.autocomplete.util;
26 
27 import dparse.lexer;
28 import dparse.rollback_allocator;
29 
30 import dsymbol.conversion;
31 import dsymbol.modulecache;
32 import dsymbol.symbol;
33 
34 import dcd.common.messages;
35 
36 /**
37  * Finds the uses of the symbol at the cursor position within a single document.
38  * Params:
39  *     request = the autocompletion request.
40  * Returns:
41  *     the autocompletion response.
42  */
43 public AutocompleteResponse findLocalUse(AutocompleteRequest request,
44 	ref ModuleCache moduleCache)
45 {
46 	AutocompleteResponse response;
47 	RollbackAllocator rba;
48 	auto allocator = scoped!(ASTAllocator)();
49 	auto cache = StringCache(StringCache.defaultBucketCount);
50 
51 	// patchs the original request for the subsequent requests
52 	request.kind = RequestKind.symbolLocation;
53 
54 	// getSymbolsForCompletion() copy to avoid repetitive parsing
55 	LexerConfig config;
56 	config.fileName = "";
57 	const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode,
58 			config, &cache);
59 	SymbolStuff getSymbolsAtCursor(size_t cursorPosition)
60 	{
61 		auto sortedTokens = assumeSorted(tokenArray);
62 		auto beforeTokens = sortedTokens.lowerBound(cursorPosition);
63 		ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
64 			&rba, request.cursorPosition, moduleCache);
65 		auto expression = getExpression(beforeTokens);
66 		return SymbolStuff(getSymbolsByTokenChain(pair.scope_, expression,
67 			cursorPosition, CompletionType.location), pair.symbol, pair.scope_);
68 	}
69 
70 	// gets the symbol matching to cursor pos
71 	SymbolStuff stuff = getSymbolsAtCursor(cast(size_t)request.cursorPosition);
72 	scope(exit) stuff.destroy();
73 
74 	// starts searching only if no ambiguity with the symbol
75 	if (stuff.symbols.length == 1)
76 	{
77 		const(DSymbol*) sourceSymbol = stuff.symbols[0];
78 		response.symbolLocation = sourceSymbol.location;
79 		response.symbolFilePath = sourceSymbol.symbolFile.idup;
80 
81 		// gets the source token to avoid too much getSymbolsAtCursor()
82 		const(Token)* sourceToken;
83 		foreach(i, t; tokenArray)
84 		{
85 			if (t.type != tok!"identifier")
86 				continue;
87 			if (request.cursorPosition >= t.index &&
88 				request.cursorPosition < t.index + t.text.length)
89 			{
90 				sourceToken = tokenArray.ptr + i;
91 				break;
92 			}
93 		}
94 
95 		// finds the tokens that match to the source symbol
96 		if (sourceToken != null) foreach (t; tokenArray)
97 		{
98 			if (t.type == tok!"identifier" && t.text == sourceToken.text)
99 			{
100 				size_t pos = cast(size_t) t.index + 1; // place cursor inside the token
101 				SymbolStuff candidate = getSymbolsAtCursor(pos);
102 				scope(exit) candidate.destroy();
103 				if (candidate.symbols.length == 1 &&
104 					candidate.symbols[0].location == sourceSymbol.location &&
105 					candidate.symbols[0].symbolFile == sourceSymbol.symbolFile)
106 				{
107 					AutocompleteResponse.Completion c;
108 					c.symbolLocation = t.index;
109 					response.completions ~= c;
110 				}
111 			}
112 		}
113 		else
114 		{
115 			warning("The source token is not an identifier");
116 		}
117 	}
118 	else
119 	{
120 		warning("No or ambiguous symbol for the identifier at cursor");
121 	}
122 	return response;
123 }