367 lines
8.3 KiB
D
367 lines
8.3 KiB
D
/**
|
|
* This file is part of DCD, a development tool for the D programming language.
|
|
* Copyright (C) 2013 Brian Schott
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
module actypes;
|
|
|
|
import stdx.d.lexer;
|
|
import stdx.d.ast;
|
|
import std.algorithm;
|
|
import std.stdio;
|
|
import std.array;
|
|
import messages;
|
|
import autocomplete;
|
|
import std.array;
|
|
|
|
/**
|
|
* Any special information about a variable declaration symbol.
|
|
*/
|
|
enum SymbolQualifier
|
|
{
|
|
/// _none
|
|
none,
|
|
/// the symbol is an _array
|
|
array,
|
|
/// the symbol is a associative array
|
|
assocArray,
|
|
/// the symbol is a function or delegate pointer
|
|
func
|
|
}
|
|
|
|
/**
|
|
* Autocompletion symbol
|
|
*/
|
|
class ACSymbol
|
|
{
|
|
public:
|
|
|
|
this() {}
|
|
|
|
/**
|
|
* Params:
|
|
* name = the symbol's name
|
|
*/
|
|
this(string name)
|
|
{
|
|
this.name = name;
|
|
}
|
|
|
|
/**
|
|
* Params:
|
|
* name = the symbol's name
|
|
* kind = the symbol's completion kind
|
|
*/
|
|
this(string name, CompletionKind kind)
|
|
{
|
|
this.name = name;
|
|
this.kind = kind;
|
|
}
|
|
|
|
/**
|
|
* Params:
|
|
* name = the symbol's name
|
|
* kind = the symbol's completion kind
|
|
* resolvedType = the resolved type of the symbol
|
|
*/
|
|
this(string name, CompletionKind kind, ACSymbol resolvedType)
|
|
{
|
|
this.name = name;
|
|
this.kind = kind;
|
|
this.resolvedType = resolvedType;
|
|
}
|
|
|
|
/**
|
|
* Symbols that compose this symbol, such as enum members, class variables,
|
|
* methods, etc.
|
|
*/
|
|
ACSymbol[] parts;
|
|
|
|
/**
|
|
* Listing of superclasses
|
|
*/
|
|
string[] superClasses;
|
|
|
|
/**
|
|
* Symbol's name
|
|
*/
|
|
string name;
|
|
|
|
/**
|
|
* Symbol's location in bytes
|
|
*/
|
|
size_t location;
|
|
|
|
/**
|
|
* Any special information about this symbol
|
|
*/
|
|
SymbolQualifier qualifier;
|
|
|
|
/**
|
|
* The kind of symbol
|
|
*/
|
|
CompletionKind kind;
|
|
|
|
/**
|
|
* The return type if this is a function, or the element type if this is an
|
|
* array or associative array, or the variable type if this is a variable.
|
|
* This field is null if this symbol is a class
|
|
*/
|
|
Type type;
|
|
|
|
/**
|
|
* The symbol that represents the type. _resolvedType is an autocomplete
|
|
* class, type is an AST class, so after a module is parsed the symbols
|
|
* need to be post-processed to tie variable declarations to the symbols
|
|
* that actually contain the correct autocomplete information.
|
|
*/
|
|
ACSymbol resolvedType;
|
|
|
|
/**
|
|
* Calltip to display if this is a function
|
|
*/
|
|
string calltip;
|
|
|
|
/**
|
|
* Finds symbol parts by name
|
|
*/
|
|
ACSymbol[] getPartsByName(string name)
|
|
{
|
|
return parts.filter!(a => a.name == name).array;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scope such as a block statement, struct body, etc.
|
|
*/
|
|
class Scope
|
|
{
|
|
public:
|
|
|
|
/**
|
|
* Params:
|
|
* start = the index of the opening brace
|
|
* end = the index of the closing brace
|
|
*/
|
|
this(size_t start, size_t end)
|
|
{
|
|
this.start = start;
|
|
this.end = end;
|
|
}
|
|
|
|
/**
|
|
* Gets all symbols in the scope that contains the cursor as well as its
|
|
* parent scopes.
|
|
*/
|
|
ACSymbol[] getSymbolsInCurrentScope(size_t cursorPosition)
|
|
{
|
|
Scope s = findCurrentScope(cursorPosition);
|
|
if (s is null)
|
|
return [];
|
|
else
|
|
return s.getSymbols();
|
|
}
|
|
|
|
/**
|
|
* Gets all symbols in this scope and its parent scopes.
|
|
*/
|
|
ACSymbol[] getSymbols()
|
|
{
|
|
ACSymbol[] rVal;
|
|
rVal ~= symbols;
|
|
if (parent !is null)
|
|
rVal ~= parent.getSymbols();
|
|
return rVal;
|
|
}
|
|
|
|
/**
|
|
* Finds the scope containing the cursor position, then searches for a
|
|
* symbol with the given name.
|
|
*/
|
|
ACSymbol[] findSymbolsInCurrentScope(size_t cursorPosition, string name)
|
|
{
|
|
auto s = findCurrentScope(cursorPosition);
|
|
if (s is null)
|
|
{
|
|
writeln("Could not find scope");
|
|
return [];
|
|
}
|
|
else
|
|
return s.findSymbolsInScope(name);
|
|
}
|
|
|
|
/**
|
|
* Returns: the innermost Scope that contains the given cursor position.
|
|
*/
|
|
Scope findCurrentScope(size_t cursorPosition)
|
|
{
|
|
if (start != size_t.max && (cursorPosition < start || cursorPosition > end))
|
|
return null;
|
|
foreach (sc; children)
|
|
{
|
|
auto s = sc.findCurrentScope(cursorPosition);
|
|
if (s is null)
|
|
continue;
|
|
else
|
|
return s;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Finds a symbol with the given name in this scope or one of its parent
|
|
* scopes.
|
|
*/
|
|
ACSymbol[] findSymbolsInScope(string name)
|
|
{
|
|
ACSymbol[] currentMatches = symbols.filter!(a => a.name == name)().array();
|
|
if (currentMatches.length == 0 && parent !is null)
|
|
return parent.findSymbolsInScope(name);
|
|
return currentMatches;
|
|
}
|
|
|
|
/**
|
|
* Fills in the $(D resolvedType) fields of the symbols in this scope and
|
|
* all child scopes.
|
|
*/
|
|
void resolveSymbolTypes()
|
|
{
|
|
// We only care about resolving types of variables, all other symbols
|
|
// don't have any indirection
|
|
foreach (ref s; symbols.filter!(a => (a.kind == CompletionKind.variableName
|
|
|| a.kind == CompletionKind.functionName || a.kind == CompletionKind.memberVariableName
|
|
|| a.kind == CompletionKind.enumMember || a.kind == CompletionKind.aliasName)
|
|
&& a.resolvedType is null)())
|
|
{
|
|
// writeln("Resolving type of symbol ", s.name);
|
|
Type type = s.type;
|
|
if (type is null)
|
|
{
|
|
//writeln("Could not find it due to null type");
|
|
continue;
|
|
}
|
|
|
|
if (type.type2.builtinType != TokenType.invalid)
|
|
{
|
|
//writeln("It was a built-in type");
|
|
// This part is easy. Autocomplete properties of built-in types
|
|
s.resolvedType = findSymbolsInScope(getTokenValue(type.type2.builtinType))[0];
|
|
}
|
|
else if (type.type2.symbol !is null)
|
|
{
|
|
// Look up a type by its name for cases like class, enum,
|
|
// interface, struct, or union members.
|
|
|
|
// TODO: Does not work with qualified names or template instances
|
|
Symbol sym = type.type2.symbol;
|
|
if (sym.identifierOrTemplateChain.identifiersOrTemplateInstances.length != 1)
|
|
{
|
|
writeln("Could not resolve type");
|
|
continue;
|
|
}
|
|
ACSymbol[] resolvedType = findSymbolsInCurrentScope(s.location,
|
|
sym.identifierOrTemplateChain.identifiersOrTemplateInstances[0].identifier.value);
|
|
if (resolvedType.length > 0 && (resolvedType[0].kind == CompletionKind.interfaceName
|
|
|| resolvedType[0].kind == CompletionKind.className
|
|
|| resolvedType[0].kind == CompletionKind.aliasName
|
|
|| resolvedType[0].kind == CompletionKind.unionName
|
|
|| resolvedType[0].kind == CompletionKind.structName))
|
|
{
|
|
// writeln("Type resolved to ", resolvedType[0].name, " which has kind ",
|
|
// resolvedType[0].kind, " and call tip ", resolvedType[0].calltip);
|
|
s.resolvedType = resolvedType[0];
|
|
}
|
|
}
|
|
|
|
foreach (suffix; type.typeSuffixes)
|
|
{
|
|
//writeln("Handling type suffix");
|
|
// Handle type suffixes for declarations, e.g.:
|
|
// int[] a;
|
|
// SomeClass[string] b;
|
|
// double function(double, double) c;
|
|
auto sym = s.resolvedType;
|
|
s.resolvedType = new ACSymbol;
|
|
s.resolvedType.resolvedType = sym;
|
|
if (suffix.array)
|
|
{
|
|
if (suffix.type !is null)
|
|
{
|
|
// assocative array
|
|
s.resolvedType.qualifier = SymbolQualifier.assocArray;
|
|
s.resolvedType.parts ~= assocArraySymbols;
|
|
}
|
|
else
|
|
{
|
|
// normal array
|
|
s.resolvedType.qualifier = SymbolQualifier.array;
|
|
s.resolvedType.parts ~= arraySymbols;
|
|
}
|
|
}
|
|
else if (suffix.delegateOrFunction.type != TokenType.invalid)
|
|
{
|
|
s.resolvedType.qualifier = SymbolQualifier.func;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (c; children)
|
|
{
|
|
c.resolveSymbolTypes();
|
|
}
|
|
|
|
foreach (ref ACSymbol c; symbols.filter!(a => a.kind == CompletionKind.className
|
|
|| a.kind == CompletionKind.interfaceName))
|
|
{
|
|
foreach (string sc; c.superClasses)
|
|
{
|
|
//writeln("Adding inherited fields from ", sc);
|
|
ACSymbol[] s = findSymbolsInScope(sc);
|
|
if (s.length > 0)
|
|
{
|
|
foreach (part; s[0].parts)
|
|
c.parts ~= part;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Index of the opening brace
|
|
*/
|
|
size_t start = size_t.max;
|
|
|
|
/**
|
|
* Index of the closing brace
|
|
*/
|
|
size_t end = size_t.max;
|
|
|
|
/**
|
|
* Symbols contained in this scope
|
|
*/
|
|
ACSymbol[] symbols;
|
|
|
|
/**
|
|
* The parent scope
|
|
*/
|
|
Scope parent;
|
|
|
|
/**
|
|
* Child scopes
|
|
*/
|
|
Scope[] children;
|
|
}
|