// 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 parser; import langutils; import types; import tokenizer; 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" ]; /** * Returns: indicies into the token array */ size_t findEndOfExpression(const Token[] tokens, size_t index) { return index; } size_t findBeginningOfExpression(const Token[] tokens, size_t index) { return index; } 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) { return "void"; } /** * This is where the magic happens */ 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; switch (symbol.type) { case TokenType.floatLiteral: return "float"; case TokenType.doubleLiteral: return "double"; case TokenType.realLiteral: return "real"; case TokenType.intLiteral: return "int"; case TokenType.unsignedIntLiteral: return "uint"; case TokenType.longLiteral: return "long"; case TokenType.unsignedLongLiteral: return "ulong"; default: break; } 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.tAuto || p == TokenType.tImmutable || p == TokenType.tConst) && preceedingTokens[index + 1] == TokenType.assign) { return null; } else if (p == TokenType.identifier || (p.type > TokenType.TYPES_BEGIN && p.type < TokenType.TYPES_END)) { return p.value; } } 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) { stderr.writeln("parenComplete"); auto index = assumeSorted(tokens).lowerBound(cursor).length - 2; Token t = tokens[index]; stderr.writeln(t); if (t.startIndex + t.value.length + 1 != cursor) return ""; switch (tokens[index].type) { case TokenType.tVersion: return to!string(join(map!`a ~ "?1"`(versions), " ").array()); case TokenType.tIf: case TokenType.tCast: case TokenType.tWhile: case TokenType.tFor: case TokenType.tForeach: case TokenType.tSwitch: return ""; default: return ""; } } string dotComplete(size_t cursor) { auto index = assumeSorted(tokens).lowerBound(cursor).length - 2; Token t = tokens[index]; if (t.startIndex + t.value.length + 1 != cursor) return ""; auto type = typeOfVariable(t, cursor); const Tuple!(string, string)[string] typeMap = context.getMembersOfType(type); 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), " "))); } const(Token)[] tokens; CompletionContext context; }