Autocompletion improvements
This commit is contained in:
parent
99dfd0973a
commit
cb719a9c36
23
README.md
23
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.
|
||||
|
|
121
autocomplete.d
121
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,10 +61,59 @@ 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
|
||||
{
|
||||
|
@ -74,12 +125,24 @@ struct AutoComplete
|
|||
|
||||
string getTypeOfExpression(const(Token)[] expression, const Token[] tokens, size_t cursor)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
|
2
build.sh
2
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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
6
types.d
6
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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue