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 }