From cb719a9c36277262c1659ff59ae73b085eba9854 Mon Sep 17 00:00:00 2001 From: Hackerpilot Date: Tue, 22 May 2012 14:48:09 -0700 Subject: [PATCH] Autocompletion improvements --- README.md | 23 +++++++++ autocomplete.d | 123 ++++++++++++++++++++++++++++++++++++++++++------- build.sh | 2 +- codegen.d | 6 +-- tokenizer.d | 1 + types.d | 6 +-- 6 files changed, 137 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 77a1e54..d84d92a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,29 @@ code file. # Dot Completion This is currently under development. +### Output format +The output of the --dotComplete option is a list of valid completions at the +given cursor position. The completions are printed one per line. Lines are ended +by a single line feed character (0x0a). Each line consists of the symbol name +followed by a single space character (0x20), followed by one character indicating +what the symbol is. Symbol definitions are taken from the list of recommended +"kind" values from the CTAGS standard unless there was no relevant recommendaton +present. + +##### Example output: + foo v + bar f + +##### Supported kinds +* c -- class names +* i -- interface names +* s -- structure names +* v -- variable +* m -- member variable +* k -- keyword, built-in version, scope statement +* f -- function or method +* g -- enum name + # Paren Completion This is currently under development. diff --git a/autocomplete.d b/autocomplete.d index f57339f..26ba6dd 100644 --- a/autocomplete.d +++ b/autocomplete.d @@ -27,13 +27,15 @@ immutable string[] versions = ["AIX", "all", "Alpha", "ARM", "BigEndian", "BSD", "Win64", "Windows", "X86", "X86_64" ]; +immutable string[] scopes = ["exit", "failure", "success"]; + /** * Returns: indicies into the token array */ -size_t findEndOfExpression(const Token[] tokens, size_t index) +size_t findEndOfExpression(const Token[] tokens, const size_t index) { size_t i = index; - while (i < tokens.length) + loop: while (i < tokens.length) { switch (tokens[i].type) { @@ -41,15 +43,15 @@ size_t findEndOfExpression(const Token[] tokens, size_t index) case TokenType.RParen: case TokenType.RBracket: case TokenType.Semicolon: - break; + break loop; case TokenType.LParen: - skipParens(tokens, index); + skipParens(tokens, i); break; case TokenType.LBrace: - skipBraces(tokens, index); + skipBraces(tokens, i); break; case TokenType.LBracket: - skipBrackets(tokens, index); + skipBrackets(tokens, i); break; default: ++i; @@ -59,11 +61,60 @@ size_t findEndOfExpression(const Token[] tokens, size_t index) return i; } -size_t findBeginningOfExpression(const Token[] tokens, size_t index) +size_t findBeginningOfExpression(const Token[] tokens, const size_t index) { - return index; + size_t i = index; + loop: while (i < tokens.length) + { + switch (tokens[i].type) + { + case TokenType.RBrace: + case TokenType.RParen: + case TokenType.RBracket: + case TokenType.Semicolon: + break loop; + case TokenType.LBrace: + skipBraces(tokens, i); + break; + case TokenType.LParen: + skipParens(tokens, i); + break; + case TokenType.LBracket: + skipBrackets(tokens, i); + break; + default: + if (i == 0) + break loop; + i--; + break; + } + } + return i; } +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; + } + writeln(app.data); + 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) @@ -74,12 +125,24 @@ struct AutoComplete string getTypeOfExpression(const(Token)[] expression, const Token[] tokens, size_t cursor) { - return "void"; + auto type = typeOfVariable(expression[0], cursor); + if (type is null) + return null; + 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; } - /** - * 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 @@ -171,10 +234,14 @@ struct AutoComplete { auto index = assumeSorted(tokens).lowerBound(cursor).length - 2; Token t = tokens[index]; + if (t.startIndex + t.value.length + 1 != cursor) + return ""; switch (tokens[index].type) { case TokenType.Version: - return to!string(join(map!`a ~ "?5"`(versions), " ").array()); + return to!string(join(map!`a ~ " k"`(versions), "\n").array()); + case TokenType.Scope: + return to!string(join(map!`a ~ " k"`(scopes), "\n").array()); case TokenType.If: case TokenType.Cast: case TokenType.While: @@ -183,6 +250,9 @@ struct AutoComplete case TokenType.Switch: return ""; default: + size_t startIndex = findBeginningOfExpression(tokens, index); + auto expressionType = getTypeOfExpression(tokens[startIndex .. index], + tokens, index); return ""; } } @@ -193,17 +263,36 @@ struct AutoComplete Token t = tokens[index]; if (t.startIndex + t.value.length + 1 != cursor) return ""; - auto type = typeOfVariable(t, cursor); + size_t startIndex = findBeginningOfExpression(tokens, index); + auto expressionType = getTypeOfExpression( + splitCallChain(tokens[startIndex .. index]), tokens, index); - const Tuple!(string, string)[string] typeMap = context.getMembersOfType(type); + 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), " "))); + app.put(k ~ " " ~ t[1]); + return to!string(array(join(sort!"a.toLower() < b.toLower()"(app.data), "\n"))); } 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"); +} diff --git a/build.sh b/build.sh index 96031ad..8a65715 100755 --- a/build.sh +++ b/build.sh @@ -1,2 +1,2 @@ -#dmd *.d -release -noboundscheck -O -w -wi -m64 -property -ofdscanner +#dmd *.d -release -noboundscheck -O -w -wi -m64 -property -ofdscanner #-inline dmd *.d -g -unittest -m64 -w -wi -property -ofdscanner diff --git a/codegen.d b/codegen.d index 8fd54c7..029a1d8 100644 --- a/codegen.d +++ b/codegen.d @@ -54,7 +54,7 @@ string printCaseStatements(K, V)(TrieNode!(K,V) node, string indentString) caseStatement ~= k; caseStatement ~= "';\n"; caseStatement ~= indentString; - caseStatement ~= "\tcurrentToken.lineNumber = lineNumber;"; + caseStatement ~= "\tcurrentToken.lineNumber = lineNumber;\n"; caseStatement ~= indentString; caseStatement ~= "\t++endIndex;\n"; if (v.children.length > 0) @@ -133,5 +133,5 @@ private: // Line feed (EoL) CR = '\u000D', // CR character - LF = '\u000A', // LF character - } \ No newline at end of file + LF = '\u000A', // LF character + } diff --git a/tokenizer.d b/tokenizer.d index 4a61f07..7d7121a 100644 --- a/tokenizer.d +++ b/tokenizer.d @@ -637,6 +637,7 @@ Token[] tokenize(S)(S inputString, IterationStyle iterationStyle = IterationStyl outerSwitch: switch(inputString[endIndex]) { + // TODO: Re-enable code generator when DMD bug 7900 is fixed mixin(generateCaseTrie( "=", "TokenType.Assign", "&", "TokenType.BitAnd", diff --git a/types.d b/types.d index 5588438..2fbb393 100644 --- a/types.d +++ b/types.d @@ -605,9 +605,9 @@ public: continue; Tuple!(string, string)[string] typeMap; foreach(var; s.variables) - typeMap[var.name] = Tuple!(string, string)(var.type, "?1"); + typeMap[var.name] = Tuple!(string, string)(var.type, "m"); foreach(fun; s.functions) - typeMap[fun.name] = Tuple!(string, string)(fun.returnType, "?2"); + typeMap[fun.name] = Tuple!(string, string)(fun.returnType, "f"); return typeMap; } foreach (Enum e; m.enums) @@ -616,7 +616,7 @@ public: continue; Tuple!(string, string)[string] typeMap; foreach (member; e.members) - typeMap[member.name] = Tuple!(string, string)(e.type, "?1"); + typeMap[member.name] = Tuple!(string, string)(e.type, "e"); return typeMap; } }