265 lines
7.5 KiB
D
265 lines
7.5 KiB
D
/*******************************************************************************
|
|
* Authors: Brian Schott
|
|
* Copyright: Brian Schott
|
|
* Date: Jul 19 2013
|
|
*
|
|
* License:
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
******************************************************************************/
|
|
|
|
module autocomplete;
|
|
|
|
import std.algorithm;
|
|
import std.array;
|
|
import std.conv;
|
|
import std.d.ast;
|
|
import std.d.lexer;
|
|
import std.d.parser;
|
|
import std.range;
|
|
import std.stdio;
|
|
import std.uni;
|
|
|
|
import messages;
|
|
import importutils;
|
|
import constants;
|
|
|
|
AutocompleteResponse complete(AutocompleteRequest request, string[] importPaths)
|
|
{
|
|
writeln("Got a completion request");
|
|
AutocompleteResponse response;
|
|
|
|
LexerConfig config;
|
|
auto tokens = request.sourceCode.byToken(config);
|
|
auto tokenArray = tokens.array();
|
|
auto sortedTokens = assumeSorted(tokenArray);
|
|
|
|
auto beforeTokens = sortedTokens.lowerBound(cast(size_t) request.cursorPosition);
|
|
if (beforeTokens[$ - 1] == TokenType.lParen && beforeTokens.length >= 2)
|
|
{
|
|
switch (beforeTokens[$ - 2].type)
|
|
{
|
|
case TokenType.traits:
|
|
response.completionType = CompletionType.identifiers;
|
|
for (size_t i = 0; i < traits.length; i++)
|
|
{
|
|
response.completions ~= traits[i];
|
|
response.completionKinds ~= CompletionKind.keyword;
|
|
}
|
|
break;
|
|
case TokenType.scope_:
|
|
response.completionType = CompletionType.identifiers;
|
|
for (size_t i = 0; i < scopes.length; i++)
|
|
{
|
|
response.completions ~= scopes[i];
|
|
response.completionKinds ~= CompletionKind.keyword;
|
|
}
|
|
break;
|
|
case TokenType.version_:
|
|
response.completionType = CompletionType.identifiers;
|
|
for (size_t i = 0; i < versions.length; i++)
|
|
{
|
|
response.completions ~= versions[i];
|
|
response.completionKinds ~= CompletionKind.keyword;
|
|
}
|
|
break;
|
|
case TokenType.pragma_:
|
|
response.completionType = CompletionType.identifiers;
|
|
for (size_t i = 0; i < pragmas.length; i++)
|
|
{
|
|
response.completions ~= pragmas[i];
|
|
response.completionKinds ~= CompletionKind.keyword;
|
|
}
|
|
break;
|
|
case TokenType.identifier:
|
|
// TODO
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (beforeTokens[$ - 1] == TokenType.dot && beforeTokens.length >= 2)
|
|
{
|
|
switch (beforeTokens[$ - 2].type)
|
|
{
|
|
case TokenType.int_:
|
|
case TokenType.uint_:
|
|
case TokenType.long_:
|
|
case TokenType.ulong_:
|
|
case TokenType.char_:
|
|
case TokenType.wchar_:
|
|
case TokenType.dchar_:
|
|
case TokenType.bool_:
|
|
case TokenType.byte_:
|
|
case TokenType.ubyte_:
|
|
case TokenType.short_:
|
|
case TokenType.ushort_:
|
|
case TokenType.cent_:
|
|
case TokenType.ucent_:
|
|
response.completionType = CompletionType.identifiers;
|
|
for (size_t i = 0; i < integerProperties.length; i++)
|
|
{
|
|
response.completions ~= integerProperties[i];
|
|
response.completionKinds ~= CompletionKind.keyword;
|
|
}
|
|
break;
|
|
case TokenType.float_:
|
|
case TokenType.ifloat_:
|
|
case TokenType.cfloat_:
|
|
case TokenType.idouble_:
|
|
case TokenType.cdouble_:
|
|
case TokenType.double_:
|
|
case TokenType.real_:
|
|
case TokenType.ireal_:
|
|
case TokenType.creal_:
|
|
response.completionType = CompletionType.identifiers;
|
|
for (size_t i = 0; i < floatProperties.length; i++)
|
|
{
|
|
response.completions ~= floatProperties[i];
|
|
response.completionKinds ~= CompletionKind.keyword;
|
|
}
|
|
break;
|
|
case TokenType.stringLiteral:
|
|
case TokenType.wstringLiteral:
|
|
case TokenType.dstringLiteral:
|
|
response.completionType = CompletionType.identifiers;
|
|
for (size_t i = 0; i < arrayProperties.length; i++)
|
|
{
|
|
response.completions ~= arrayProperties[i];
|
|
response.completionKinds ~= CompletionKind.keyword;
|
|
}
|
|
break;
|
|
case TokenType.identifier:
|
|
// TODO: This is a placeholder
|
|
response.completionType = CompletionType.identifiers;
|
|
for (size_t i = 0; i < allProperties.length; i++)
|
|
{
|
|
response.completions ~= allProperties[i];
|
|
response.completionKinds ~= CompletionKind.keyword;
|
|
}
|
|
break;
|
|
case TokenType.lParen:
|
|
case TokenType.lBrace:
|
|
case TokenType.lBracket:
|
|
case TokenType.semicolon:
|
|
case TokenType.colon:
|
|
// TODO: global scope
|
|
break;
|
|
case TokenType.rParen:
|
|
case TokenType.rBrace:
|
|
case TokenType.rBracket:
|
|
default:
|
|
// TODO
|
|
break;
|
|
}
|
|
}
|
|
else if (beforeTokens[$ - 1] == TokenType.identifier)
|
|
{
|
|
Module mod = parseModule(tokenArray, request.fileName, &messageFunction);
|
|
|
|
writeln("Resolved imports: ", getImportedFiles(mod, importPaths ~ request.importPaths));
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
void messageFunction(string fileName, int line, int column, string message)
|
|
{
|
|
// does nothing
|
|
}
|
|
|
|
string createCamelCaseRegex(string input)
|
|
{
|
|
dstring output;
|
|
uint i;
|
|
foreach (dchar d; input)
|
|
{
|
|
if (isLower(d))
|
|
output ~= d;
|
|
else if (i > 0)
|
|
{
|
|
output ~= ".*";
|
|
output ~= d;
|
|
}
|
|
i++;
|
|
}
|
|
return to!string(output ~ ".*");
|
|
}
|
|
|
|
unittest
|
|
{
|
|
assert("ClNa".createCamelCaseRegex() == "Cl.*Na.*");
|
|
}
|
|
|
|
enum SymbolKind
|
|
{
|
|
className,
|
|
interfaceName,
|
|
enumName,
|
|
variableName,
|
|
structName,
|
|
unionName,
|
|
functionName
|
|
}
|
|
|
|
class Symbol
|
|
{
|
|
Symbol[] parts;
|
|
string name;
|
|
SymbolKind kind;
|
|
Type[string] templateParameters;
|
|
}
|
|
|
|
class Scope
|
|
{
|
|
public:
|
|
/**
|
|
* @return the innermost Scope that contains the given cursor position.
|
|
*/
|
|
const(Scope) findCurrentScope(size_t cursorPosition) const
|
|
{
|
|
if (cursorPosition < start || cursorPosition > end)
|
|
return null;
|
|
foreach (sc; children)
|
|
{
|
|
auto s = sc.findCurrentScope(cursorPosition);
|
|
if (s is null)
|
|
continue;
|
|
else
|
|
return s;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
const(Symbol)[] getSymbolsInScope() const
|
|
{
|
|
return symbols ~ parent.getSymbolsInScope();
|
|
}
|
|
|
|
|
|
Symbol[] getSymbolsInScope(string name)
|
|
{
|
|
Symbol[] results;
|
|
symbols.filter!(x => x.name == name)().copy(results);
|
|
parent.getSymbolsInScope(name).copy(results);
|
|
return results;
|
|
}
|
|
|
|
private:
|
|
size_t start;
|
|
size_t end;
|
|
Symbol[] symbols;
|
|
Scope parent;
|
|
Scope[] children;
|
|
}
|