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
|
# Dot Completion
|
||||||
This is currently under development.
|
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
|
# Paren Completion
|
||||||
This is currently under development.
|
This is currently under development.
|
||||||
|
|
123
autocomplete.d
123
autocomplete.d
|
@ -27,13 +27,15 @@ immutable string[] versions = ["AIX", "all", "Alpha", "ARM", "BigEndian", "BSD",
|
||||||
"Win64", "Windows", "X86", "X86_64"
|
"Win64", "Windows", "X86", "X86_64"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
immutable string[] scopes = ["exit", "failure", "success"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns: indicies into the token array
|
* 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;
|
size_t i = index;
|
||||||
while (i < tokens.length)
|
loop: while (i < tokens.length)
|
||||||
{
|
{
|
||||||
switch (tokens[i].type)
|
switch (tokens[i].type)
|
||||||
{
|
{
|
||||||
|
@ -41,15 +43,15 @@ size_t findEndOfExpression(const Token[] tokens, size_t index)
|
||||||
case TokenType.RParen:
|
case TokenType.RParen:
|
||||||
case TokenType.RBracket:
|
case TokenType.RBracket:
|
||||||
case TokenType.Semicolon:
|
case TokenType.Semicolon:
|
||||||
break;
|
break loop;
|
||||||
case TokenType.LParen:
|
case TokenType.LParen:
|
||||||
skipParens(tokens, index);
|
skipParens(tokens, i);
|
||||||
break;
|
break;
|
||||||
case TokenType.LBrace:
|
case TokenType.LBrace:
|
||||||
skipBraces(tokens, index);
|
skipBraces(tokens, i);
|
||||||
break;
|
break;
|
||||||
case TokenType.LBracket:
|
case TokenType.LBracket:
|
||||||
skipBrackets(tokens, index);
|
skipBrackets(tokens, i);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
++i;
|
++i;
|
||||||
|
@ -59,11 +61,60 @@ size_t findEndOfExpression(const Token[] tokens, size_t index)
|
||||||
return i;
|
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
|
struct AutoComplete
|
||||||
{
|
{
|
||||||
this(const (Token)[] tokens, CompletionContext context)
|
this(const (Token)[] tokens, CompletionContext context)
|
||||||
|
@ -74,12 +125,24 @@ struct AutoComplete
|
||||||
|
|
||||||
string getTypeOfExpression(const(Token)[] expression, const Token[] tokens, size_t cursor)
|
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)
|
string typeOfVariable(Token symbol, size_t cursor)
|
||||||
{
|
{
|
||||||
// int is of type int, double of type double, and so on
|
// 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;
|
auto index = assumeSorted(tokens).lowerBound(cursor).length - 2;
|
||||||
Token t = tokens[index];
|
Token t = tokens[index];
|
||||||
|
if (t.startIndex + t.value.length + 1 != cursor)
|
||||||
|
return "";
|
||||||
switch (tokens[index].type)
|
switch (tokens[index].type)
|
||||||
{
|
{
|
||||||
case TokenType.Version:
|
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.If:
|
||||||
case TokenType.Cast:
|
case TokenType.Cast:
|
||||||
case TokenType.While:
|
case TokenType.While:
|
||||||
|
@ -183,6 +250,9 @@ struct AutoComplete
|
||||||
case TokenType.Switch:
|
case TokenType.Switch:
|
||||||
return "";
|
return "";
|
||||||
default:
|
default:
|
||||||
|
size_t startIndex = findBeginningOfExpression(tokens, index);
|
||||||
|
auto expressionType = getTypeOfExpression(tokens[startIndex .. index],
|
||||||
|
tokens, index);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,17 +263,36 @@ struct AutoComplete
|
||||||
Token t = tokens[index];
|
Token t = tokens[index];
|
||||||
if (t.startIndex + t.value.length + 1 != cursor)
|
if (t.startIndex + t.value.length + 1 != cursor)
|
||||||
return "";
|
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)
|
if (typeMap is null)
|
||||||
return "";
|
return "";
|
||||||
auto app = appender!(string[])();
|
auto app = appender!(string[])();
|
||||||
foreach (k, t; typeMap)
|
foreach (k, t; typeMap)
|
||||||
app.put(k ~ t[1]);
|
app.put(k ~ " " ~ t[1]);
|
||||||
return to!string(array(join(sort!"a.toLower() < b.toLower()"(app.data), " ")));
|
return to!string(array(join(sort!"a.toLower() < b.toLower()"(app.data), "\n")));
|
||||||
}
|
}
|
||||||
|
|
||||||
const(Token)[] tokens;
|
const(Token)[] tokens;
|
||||||
CompletionContext context;
|
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
|
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 ~= k;
|
||||||
caseStatement ~= "';\n";
|
caseStatement ~= "';\n";
|
||||||
caseStatement ~= indentString;
|
caseStatement ~= indentString;
|
||||||
caseStatement ~= "\tcurrentToken.lineNumber = lineNumber;";
|
caseStatement ~= "\tcurrentToken.lineNumber = lineNumber;\n";
|
||||||
caseStatement ~= indentString;
|
caseStatement ~= indentString;
|
||||||
caseStatement ~= "\t++endIndex;\n";
|
caseStatement ~= "\t++endIndex;\n";
|
||||||
if (v.children.length > 0)
|
if (v.children.length > 0)
|
||||||
|
@ -133,5 +133,5 @@ private:
|
||||||
|
|
||||||
// Line feed (EoL)
|
// Line feed (EoL)
|
||||||
CR = '\u000D', // CR character
|
CR = '\u000D', // CR character
|
||||||
LF = '\u000A', // LF character
|
LF = '\u000A', // LF character
|
||||||
}
|
}
|
||||||
|
|
|
@ -637,6 +637,7 @@ Token[] tokenize(S)(S inputString, IterationStyle iterationStyle = IterationStyl
|
||||||
|
|
||||||
outerSwitch: switch(inputString[endIndex])
|
outerSwitch: switch(inputString[endIndex])
|
||||||
{
|
{
|
||||||
|
// TODO: Re-enable code generator when DMD bug 7900 is fixed
|
||||||
mixin(generateCaseTrie(
|
mixin(generateCaseTrie(
|
||||||
"=", "TokenType.Assign",
|
"=", "TokenType.Assign",
|
||||||
"&", "TokenType.BitAnd",
|
"&", "TokenType.BitAnd",
|
||||||
|
|
6
types.d
6
types.d
|
@ -605,9 +605,9 @@ public:
|
||||||
continue;
|
continue;
|
||||||
Tuple!(string, string)[string] typeMap;
|
Tuple!(string, string)[string] typeMap;
|
||||||
foreach(var; s.variables)
|
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)
|
foreach(fun; s.functions)
|
||||||
typeMap[fun.name] = Tuple!(string, string)(fun.returnType, "?2");
|
typeMap[fun.name] = Tuple!(string, string)(fun.returnType, "f");
|
||||||
return typeMap;
|
return typeMap;
|
||||||
}
|
}
|
||||||
foreach (Enum e; m.enums)
|
foreach (Enum e; m.enums)
|
||||||
|
@ -616,7 +616,7 @@ public:
|
||||||
continue;
|
continue;
|
||||||
Tuple!(string, string)[string] typeMap;
|
Tuple!(string, string)[string] typeMap;
|
||||||
foreach (member; e.members)
|
foreach (member; e.members)
|
||||||
typeMap[member.name] = Tuple!(string, string)(e.type, "?1");
|
typeMap[member.name] = Tuple!(string, string)(e.type, "e");
|
||||||
return typeMap;
|
return typeMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue