D-Scanner/autocomplete.d

379 lines
12 KiB
D

// Copyright Brian Schott (Sir Alaran) 2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
module autocomplete;
import std.range;
import std.algorithm;
import std.array;
import std.conv;
import std.stdio;
import std.typecons;
import std.path;
import std.file;
import std.d.lexer;
import parser;
import langutils;
import types;
immutable string[] versions = ["AIX", "all", "Alpha", "ARM", "BigEndian", "BSD",
"Cygwin", "D_Coverage", "D_Ddoc", "DigitalMars", "D_InlineAsm_X86",
"D_InlineAsm_X86_64", "D_LP64", "D_NET", "D_PIC", "D_Version2",
"FreeBSD", "GNU", "HPPA", "HPPA64", "Hurd", "IA64", "LDC", "linux",
"LittleEndian", "MinGW", "MIPS", "MIPS64", "none", "OpenBSD", "OSX",
"Posix", "PPC", "PPC64", "S390", "S390X", "SDC", "SH", "SH64", "SkyOS",
"Solaris", "SPARC", "SPARC64", "SysV3", "SysV4", "unittest", "Win32",
"Win64", "Windows", "X86", "X86_64"
];
immutable string[] scopes = ["exit", "failure", "success"];
///**
// * Returns: indicies into the token array
// */
//size_t findEndOfExpression(const Token[] tokens, const size_t index)
//out (result)
//{
// assert (result < tokens.length);
// assert (result >= index);
//}
//body
//{
// size_t i = index;
// loop: while (i < tokens.length)
// {
// switch (tokens[i].type)
// {
// case TokenType.Return:
// case TokenType.New:
// case TokenType.Delete:
// case TokenType.Comma:
// case TokenType.RBrace:
// case TokenType.RParen:
// case TokenType.RBracket:
// case TokenType.Semicolon:
// break loop;
// case TokenType.LParen:
// skipParens(tokens, i);
// break;
// case TokenType.LBrace:
// skipBraces(tokens, i);
// break;
// case TokenType.LBracket:
// skipBrackets(tokens, i);
// break;
// default:
// ++i;
// break;
// }
// }
// return i;
//}
//
//size_t findBeginningOfExpression(const Token[] tokens, const size_t index)
//in
//{
// assert (index < tokens.length);
// assert (tokens.length > 0);
//}
//out (result)
//{
// import std.string;
// assert (result < tokens.length);
// assert (result <= index, format("findBeginningOfExpression %d, %d", result, index));
//}
//body
//{
// size_t i = index;
// loop: while (i < tokens.length)
// {
// switch (tokens[i].type)
// {
// case TokenType.Assign: case TokenType.BitAnd: case TokenType.BitAndEquals:
// case TokenType.BitOr: case TokenType.BitOrEquals: case TokenType.CatEquals:
// case TokenType.Colon: case TokenType.Comma: case TokenType.Decrement:
// case TokenType.Div: case TokenType.DivEquals: case TokenType.Dollar:
// case TokenType.Equals: case TokenType.GoesTo:
// case TokenType.Greater: case TokenType.GreaterEqual: case TokenType.Hash:
// case TokenType.Increment: case TokenType.LBrace: case TokenType.LBracket:
// case TokenType.Less: case TokenType.LessEqual: case TokenType.LessEqualGreater:
// case TokenType.LessOrGreater: case TokenType.LogicAnd: case TokenType.LogicOr:
// case TokenType.LParen: case TokenType.Minus: case TokenType.MinusEquals:
// case TokenType.Mod: case TokenType.ModEquals: case TokenType.MulEquals:
// case TokenType.Not: case TokenType.NotEquals: case TokenType.NotGreater:
// case TokenType.NotGreaterEqual: case TokenType.NotLess: case TokenType.NotLessEqual:
// case TokenType.NotLessEqualGreater: case TokenType.Plus: case TokenType.PlusEquals:
// case TokenType.Pow: case TokenType.PowEquals: case TokenType.RBrace:
// case TokenType.Semicolon: case TokenType.ShiftLeft: case TokenType.ShiftLeftEqual:
// case TokenType.ShiftRight: case TokenType.ShiftRightEqual: case TokenType.Slice:
// case TokenType.Star: case TokenType.Ternary: case TokenType.Tilde:
// case TokenType.Unordered: case TokenType.UnsignedShiftRight: case TokenType.UnsignedShiftRightEqual:
// case TokenType.Vararg: case TokenType.Xor: case TokenType.XorEquals:
// case TokenType.KEYWORDS_BEGIN: .. case TokenType.KEYWORDS_END:
// return i + 1;
// case TokenType.RParen:
// if (i == 0)
// break loop;
// skipParens(tokens, i);
// break;
// case TokenType.RBracket:
// if (i == 0)
// break loop;
// skipBrackets(tokens, i);
// break;
// default:
// if (i == 0)
// break loop;
// i--;
// break;
// }
// }
// return i + 1;
//}
//
//const(Token)[] splitCallChain(const(Token)[] tokens)
//{
// auto app = appender!(Token[])();
// size_t i = 0;
// while (i < tokens.length)
// {
// app.put(tokens[i++]);
// while (i < tokens.length && tokens[i] == TokenType.LParen) skipParens(tokens, i);
// while (i < tokens.length && tokens[i] == TokenType.LBracket) skipBrackets(tokens, i);
// while (i < tokens.length && tokens[i] == TokenType.Dot) ++i;
// }
// return app.data;
//}
//
//unittest
//{
// auto code = `a.b[10].c("grcl").x`;
// auto tokens = tokenize(code);
// assert (splitCallChain(tokens) == ["a", "b", "c", "x"]);
//}
//
//struct AutoComplete
//{
// this(const (Token)[] tokens, CompletionContext context)
// {
// this.tokens = tokens;
// this.context = context;
// }
//
// string getTypeOfExpression(const(Token)[] expression, const Token[] tokens, size_t cursor)
// {
// stderr.writeln("getting type of ", expression);
// if (expression.length == 0)
// return "void";
// auto type = typeOfVariable(expression[0], cursor);
// if (type is null)
// return "void";
// size_t index = 1;
// while (index < expression.length)
// {
// const Tuple!(string, string)[string] typeMap = context.getMembersOfType(
// type);
// const Tuple!(string, string)* memberType = expression[index].value in typeMap;
// if (memberType is null)
// return "void";
// else
// type = (*memberType)[0];
// index++;
// }
// return type;
// }
//
// string typeOfVariable(Token symbol, size_t cursor)
// {
// // int is of type int, double of type double, and so on
// if (symbol.value in typeProperties)
// return symbol.value;
//
// string tokenType = getTypeFromToken(symbol);
// if (tokenType !is null)
// return tokenType;
//
// if (context.getMembersOfType(symbol.value))
// return symbol.value;
//
// // Arbitrarily define the depth of the cursor position as zero
// // iterate backwards through the code to try to find the variable
// int depth = 0;
// auto preceedingTokens = assumeSorted(tokens).lowerBound(cursor);
// auto index = preceedingTokens.length - 1;
// while (true)
// {
// if (preceedingTokens[index] == TokenType.LBrace)
// --depth;
// else if (preceedingTokens[index] == TokenType.RBrace)
// ++depth;
// else if (depth <= 0 && preceedingTokens[index].value == symbol)
// {
// // Found the symbol, now determine if it was declared here.
// auto p = preceedingTokens[index - 1];
//
//
// if ((p == TokenType.Auto || p == TokenType.Immutable
// || p == TokenType.Const)
// && preceedingTokens[index + 1] == TokenType.Assign)
// {
// // Try to determine the type of a variable declared as "auto"
// return getTypeOfExpression(
// tokens[index + 2 .. findEndOfExpression(tokens, index + 2)],
// tokens, cursor);
// }
// else if (p == TokenType.Identifier
// || (p.type > TokenType.TYPES_BEGIN
// && p.type < TokenType.TYPES_END))
// {
// // Handle simple cases like "int a;" or "Someclass instance;"
// return p.value;
// }
// else if (p == TokenType.RBracket || p == TokenType.RParen)
// {
// return combineTokens(tokens[findBeginningOfExpression(tokens, index) .. index]);
// }
// }
// if (index == 0)
// break;
// else
// --index;
// }
//
// // Find all struct or class bodies that we're in.
// // Check for the symbol in those class/struct/interface bodies
// // if match is found, return it
// auto structs = context.getStructsContaining(cursor);
// if (symbol == "this" && structs.length > 0)
// {
// return minCount!("a.bodyStart > b.bodyStart")(structs)[0].name;
// }
//
// foreach (s; structs)
// {
// auto t = s.getMemberType(symbol.value);
// if (t !is null)
// return t;
// }
// return "void";
// }
//
// string symbolAt(size_t cursor) const
// {
// auto r = assumeSorted(tokens).lowerBound(cursor)[$ - 1];
// if (r.value.length + r.startIndex > cursor)
// return r.value;
// else
// return null;
// }
//
// string parenComplete(size_t cursor)
// {
// auto index = assumeSorted(tokens).lowerBound(cursor).length - 2;
// Token t = tokens[index];
// switch (tokens[index].type)
// {
// case TokenType.Version:
// return "completions\n" ~ to!string(join(map!`a ~ " k"`(versions), "\n").array());
// case TokenType.Scope:
// return "completions\n" ~ to!string(join(map!`a ~ " k"`(scopes), "\n").array());
// case TokenType.If:
// case TokenType.Cast:
// case TokenType.While:
// case TokenType.For:
// case TokenType.Foreach:
// case TokenType.Switch:
// return "";
// default:
// size_t startIndex = findBeginningOfExpression(tokens, index);
// auto callChain = splitCallChain(tokens[startIndex .. index + 1]);
// auto expressionType = getTypeOfExpression(
// callChain[0 .. $ - 1], tokens, cursor);
// return "calltips\n" ~ to!string(context.getCallTipsFor(expressionType,
// callChain[$ - 1].value, cursor).join("\n").array());
// }
// }
//
// string dotComplete(size_t cursor)
// {
// stderr.writeln("dotComplete");
// auto index = assumeSorted(tokens).lowerBound(cursor).length - 1;
// Token t = tokens[index];
//
// // If the last character entered before the cursor isn't a dot, give up.
// // The user was probably in the middle of typing the slice or vararg
// // operators
// if (t != TokenType.Dot)
// return null;
//
// size_t startIndex = findBeginningOfExpression(tokens, index);
// if (startIndex - 1 < tokens.length && tokens[startIndex - 1] == TokenType.Import)
// {
// return importComplete(splitCallChain(tokens[startIndex .. index]));
// }
//
// auto expressionType = getTypeOfExpression(
// splitCallChain(tokens[startIndex .. index]), tokens, cursor);
//
// stderr.writeln("expression type is ", expressionType);
//
// // Complete pointers and references the same way
// if (expressionType[$ - 1] == '*')
// expressionType = expressionType[0 .. $ - 1];
//
// const Tuple!(string, string)[string] typeMap = context.getMembersOfType(
// expressionType);
// if (typeMap is null)
// return "";
// auto app = appender!(string[])();
// foreach (k, t; typeMap)
// app.put(k ~ " " ~ t[1]);
// return to!string(array(join(sort!("a.toLower() < b.toLower()")(app.data), "\n")));
// }
//
// string importComplete(const(Token)[] tokens)
// {
// stderr.writeln("importComplete");
// auto app = appender!(string[])();
// string part = to!string(map!"a.value.dup"(tokens).join("/").array());
// foreach (path; context.importDirectories)
// {
// stderr.writeln("Searching for ", path, "/", part);
// if (!exists(buildPath(path, part)))
// continue;
// stderr.writeln("found it");
// foreach (DirEntry dirEntry; dirEntries(buildPath(path, part),
// SpanMode.shallow))
// {
// if (dirEntry.isDir)
// app.put(baseName(dirEntry.name) ~ " P");
// else if (dirEntry.name.endsWith(".d", ".di"))
// app.put(stripExtension(baseName(dirEntry.name)) ~ " M");
// }
// }
// return to!string(sort!("a.toLower() < b.toLower()")(app.data).join("\n").array());
// }
//
// const(Token)[] tokens;
// CompletionContext context;
//}
//
//unittest
//{
// auto code = q{
//struct TestStruct { int a; int b; }
//TestStruct ts;
//ts.a.
// };
//
// auto tokens = tokenize(code);
// auto mod = parseModule(tokens);
// auto context = new CompletionContext(mod);
// auto completion = AutoComplete(tokens, context);
// assert (completion.getTypeOfExpression(splitCallChain(tokens[13 .. 16]),
// tokens, 56) == "int");
//}