Added documentation. Removed duplicated code from autocompletion module
This commit is contained in:
parent
e6c03b6610
commit
f52cb6ad2a
|
@ -5,8 +5,10 @@
|
||||||
# *nix binaries
|
# *nix binaries
|
||||||
dcd-client
|
dcd-client
|
||||||
dcd-server
|
dcd-server
|
||||||
dcd-client.o
|
*.o
|
||||||
dcd-server.o
|
|
||||||
|
|
||||||
# Perf reports
|
# Perf reports
|
||||||
perf.data
|
perf.data
|
||||||
|
|
||||||
|
# Valgrind reports
|
||||||
|
callgrind.*
|
||||||
|
|
87
actypes.d
87
actypes.d
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of DCD, a development tool for the D programming language.
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
* Copyright (C) 2013 Brian Schott
|
* Copyright (C) 2014 Brian Schott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -28,6 +28,9 @@ import std.array;
|
||||||
import std.typecons;
|
import std.typecons;
|
||||||
import std.container;
|
import std.container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares symbols by their name
|
||||||
|
*/
|
||||||
bool comparitor(const(ACSymbol)* a, const(ACSymbol)* b) pure nothrow
|
bool comparitor(const(ACSymbol)* a, const(ACSymbol)* b) pure nothrow
|
||||||
{
|
{
|
||||||
return a.name < b.name;
|
return a.name < b.name;
|
||||||
|
@ -150,8 +153,16 @@ public:
|
||||||
SymbolQualifier qualifier;
|
SymbolQualifier qualifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains symbols and supports lookup of symbols by cursor position.
|
||||||
|
*/
|
||||||
struct Scope
|
struct Scope
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* begin = the beginning byte index
|
||||||
|
* end = the ending byte index
|
||||||
|
*/
|
||||||
this (size_t begin, size_t end)
|
this (size_t begin, size_t end)
|
||||||
{
|
{
|
||||||
this.startLocation = begin;
|
this.startLocation = begin;
|
||||||
|
@ -159,6 +170,12 @@ struct Scope
|
||||||
this.symbols = new RedBlackTree!(ACSymbol*, comparitor, true);
|
this.symbols = new RedBlackTree!(ACSymbol*, comparitor, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* cursorPosition = the cursor position in bytes
|
||||||
|
* Returns:
|
||||||
|
* the innermost scope that contains the given cursor position
|
||||||
|
*/
|
||||||
Scope* getScopeByCursor(size_t cursorPosition) const
|
Scope* getScopeByCursor(size_t cursorPosition) const
|
||||||
{
|
{
|
||||||
if (cursorPosition < startLocation) return null;
|
if (cursorPosition < startLocation) return null;
|
||||||
|
@ -172,6 +189,13 @@ struct Scope
|
||||||
return cast(typeof(return)) &this;
|
return cast(typeof(return)) &this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* cursorPosition = the cursor position in bytes
|
||||||
|
* Returns:
|
||||||
|
* all symbols in the scope containing the cursor position, as well as
|
||||||
|
* the symbols in parent scopes of that scope.
|
||||||
|
*/
|
||||||
ACSymbol*[] getSymbolsInCursorScope(size_t cursorPosition) const
|
ACSymbol*[] getSymbolsInCursorScope(size_t cursorPosition) const
|
||||||
{
|
{
|
||||||
auto s = getScopeByCursor(cursorPosition);
|
auto s = getScopeByCursor(cursorPosition);
|
||||||
|
@ -188,14 +212,18 @@ struct Scope
|
||||||
return symbols.array();
|
return symbols.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* name = the symbol name to search for
|
||||||
|
* Returns:
|
||||||
|
* all symbols in this scope or parent scopes with the given name
|
||||||
|
*/
|
||||||
ACSymbol*[] getSymbolsByName(string name) const
|
ACSymbol*[] getSymbolsByName(string name) const
|
||||||
{
|
{
|
||||||
import std.range;
|
import std.range;
|
||||||
ACSymbol s = ACSymbol(name);
|
ACSymbol s = ACSymbol(name);
|
||||||
RedBlackTree!(ACSymbol*, comparitor, true) t = cast() symbols;
|
RedBlackTree!(ACSymbol*, comparitor, true) t = cast() symbols;
|
||||||
auto r = t.equalRange(&s).array();
|
auto r = t.equalRange(&s).array();
|
||||||
version(assert) foreach (n; r)
|
|
||||||
assert (n.name == name, name);
|
|
||||||
if (r.length > 0)
|
if (r.length > 0)
|
||||||
return cast(typeof(return)) r;
|
return cast(typeof(return)) r;
|
||||||
if (parent is null)
|
if (parent is null)
|
||||||
|
@ -203,6 +231,14 @@ struct Scope
|
||||||
return parent.getSymbolsByName(name);
|
return parent.getSymbolsByName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* name = the symbol name to search for
|
||||||
|
* cursorPosition = the cursor position in bytes
|
||||||
|
* Returns:
|
||||||
|
* all symbols with the given name in the scope containing the cursor
|
||||||
|
* and its parent scopes
|
||||||
|
*/
|
||||||
ACSymbol*[] getSymbolsByNameAndCursor(string name, size_t cursorPosition) const
|
ACSymbol*[] getSymbolsByNameAndCursor(string name, size_t cursorPosition) const
|
||||||
{
|
{
|
||||||
auto s = getScopeByCursor(cursorPosition);
|
auto s = getScopeByCursor(cursorPosition);
|
||||||
|
@ -230,6 +266,9 @@ struct Scope
|
||||||
RedBlackTree!(ACSymbol*, comparitor, true) symbols;
|
RedBlackTree!(ACSymbol*, comparitor, true) symbols;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import information
|
||||||
|
*/
|
||||||
struct ImportInformation
|
struct ImportInformation
|
||||||
{
|
{
|
||||||
/// Import statement parts
|
/// Import statement parts
|
||||||
|
@ -243,6 +282,41 @@ struct ImportInformation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Symbols for the built in types
|
||||||
|
*/
|
||||||
|
RedBlackTree!(ACSymbol*, comparitor, true) builtinSymbols;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array properties
|
||||||
|
*/
|
||||||
|
RedBlackTree!(ACSymbol*, comparitor, true) arraySymbols;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associative array properties
|
||||||
|
*/
|
||||||
|
RedBlackTree!(ACSymbol*, comparitor, true) assocArraySymbols;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum, union, class, and interface properties
|
||||||
|
*/
|
||||||
|
RedBlackTree!(ACSymbol*, comparitor, true) aggregateSymbols;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class properties
|
||||||
|
*/
|
||||||
|
RedBlackTree!(ACSymbol*, comparitor, true) classSymbols;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of the _argptr variable
|
||||||
|
*/
|
||||||
|
Type argptrType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of _arguments
|
||||||
|
*/
|
||||||
|
Type argumentsType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes builtin types and the various properties of builtin types
|
* Initializes builtin types and the various properties of builtin types
|
||||||
*/
|
*/
|
||||||
|
@ -406,10 +480,3 @@ static this()
|
||||||
classSymbols = clSym;
|
classSymbols = clSym;
|
||||||
}
|
}
|
||||||
|
|
||||||
RedBlackTree!(ACSymbol*, comparitor, true) builtinSymbols;
|
|
||||||
RedBlackTree!(ACSymbol*, comparitor, true) arraySymbols;
|
|
||||||
RedBlackTree!(ACSymbol*, comparitor, true) assocArraySymbols;
|
|
||||||
RedBlackTree!(ACSymbol*, comparitor, true) aggregateSymbols;
|
|
||||||
RedBlackTree!(ACSymbol*, comparitor, true) classSymbols;
|
|
||||||
Type argptrType;
|
|
||||||
Type argumentsType;
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
/*******************************************************************************
|
/**
|
||||||
* Authors: Brian Schott
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
* Copyright: Brian Schott
|
* Copyright (C) 2014 Brian Schott
|
||||||
* Date: Sep 21 2013
|
|
||||||
*
|
*
|
||||||
* License:
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
@ -16,7 +14,7 @@
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
******************************************************************************/
|
*/
|
||||||
|
|
||||||
module astconverter;
|
module astconverter;
|
||||||
|
|
||||||
|
|
467
autocomplete.d
467
autocomplete.d
|
@ -1,9 +1,7 @@
|
||||||
/*******************************************************************************
|
/**
|
||||||
* Authors: Brian Schott
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
* Copyright: Brian Schott
|
* Copyright (C) 2014 Brian Schott
|
||||||
* Date: Jul 19 2013
|
|
||||||
*
|
*
|
||||||
* License:
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
@ -16,7 +14,7 @@
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
******************************************************************************/
|
*/
|
||||||
|
|
||||||
module autocomplete;
|
module autocomplete;
|
||||||
|
|
||||||
|
@ -40,35 +38,18 @@ import modulecache;
|
||||||
import astconverter;
|
import astconverter;
|
||||||
import stupidlog;
|
import stupidlog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets documentation for the symbol at the cursor
|
||||||
|
* Params:
|
||||||
|
* request = the autocompletion request
|
||||||
|
* Returns:
|
||||||
|
* the autocompletion response
|
||||||
|
*/
|
||||||
AutocompleteResponse getDoc(const AutocompleteRequest request)
|
AutocompleteResponse getDoc(const AutocompleteRequest request)
|
||||||
{
|
{
|
||||||
Log.trace("Getting doc comments");
|
Log.trace("Getting doc comments");
|
||||||
|
|
||||||
AutocompleteResponse response;
|
AutocompleteResponse response;
|
||||||
LexerConfig config;
|
ACSymbol*[] symbols = getSymbolsForCompletion(request, CompletionType.ddoc);
|
||||||
config.fileName = "stdin";
|
|
||||||
StringCache* cache = new StringCache(StringCache.defaultBucketCount);
|
|
||||||
auto tokens = byToken(cast(ubyte[]) request.sourceCode, config, cache);
|
|
||||||
const(Token)[] tokenArray = void;
|
|
||||||
try {
|
|
||||||
tokenArray = tokens.array();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.error("Could not provide autocomplete due to lexing exception: ", e.msg);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
auto sortedTokens = assumeSorted(tokenArray);
|
|
||||||
string partial;
|
|
||||||
|
|
||||||
auto beforeTokens = sortedTokens.lowerBound(cast(size_t) request.cursorPosition);
|
|
||||||
|
|
||||||
Log.trace("Token at cursor: ", beforeTokens[$ - 1].text);
|
|
||||||
|
|
||||||
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray, "stdin");
|
|
||||||
auto expression = getExpression(beforeTokens);
|
|
||||||
|
|
||||||
ACSymbol*[] symbols = getSymbolsByTokenChain(completionScope, expression,
|
|
||||||
request.cursorPosition, CompletionType.ddoc);
|
|
||||||
|
|
||||||
if (symbols.length == 0)
|
if (symbols.length == 0)
|
||||||
Log.error("Could not find symbol");
|
Log.error("Could not find symbol");
|
||||||
else foreach (symbol; symbols)
|
else foreach (symbol; symbols)
|
||||||
|
@ -86,76 +67,213 @@ AutocompleteResponse getDoc(const AutocompleteRequest request)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the declaration of the symbol at the cursor position.
|
* Finds the declaration of the symbol at the cursor position.
|
||||||
|
* Params:
|
||||||
|
* request = the autocompletion request
|
||||||
|
* Returns:
|
||||||
|
* the autocompletion response
|
||||||
*/
|
*/
|
||||||
AutocompleteResponse findDeclaration(const AutocompleteRequest request)
|
AutocompleteResponse findDeclaration(const AutocompleteRequest request)
|
||||||
{
|
{
|
||||||
Log.trace("Finding declaration");
|
Log.trace("Finding declaration");
|
||||||
AutocompleteResponse response;
|
AutocompleteResponse response;
|
||||||
LexerConfig config;
|
ACSymbol*[] symbols = getSymbolsForCompletion(request, CompletionType.location);
|
||||||
config.fileName = "stdin";
|
|
||||||
StringCache* cache = new StringCache(StringCache.defaultBucketCount);
|
|
||||||
auto tokens = byToken(cast(ubyte[]) request.sourceCode, config, cache);
|
|
||||||
const(Token)[] tokenArray = void;
|
|
||||||
try {
|
|
||||||
tokenArray = tokens.array();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.error("Could not provide autocomplete due to lexing exception: ", e.msg);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
auto sortedTokens = assumeSorted(tokenArray);
|
|
||||||
string partial;
|
|
||||||
|
|
||||||
auto beforeTokens = sortedTokens.lowerBound(cast(size_t) request.cursorPosition);
|
|
||||||
|
|
||||||
Log.trace("Token at cursor: ", beforeTokens[$ - 1].text);
|
|
||||||
|
|
||||||
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray, "stdin");
|
|
||||||
auto expression = getExpression(beforeTokens);
|
|
||||||
|
|
||||||
ACSymbol*[] symbols = getSymbolsByTokenChain(completionScope, expression,
|
|
||||||
request.cursorPosition, CompletionType.location);
|
|
||||||
|
|
||||||
if (symbols.length > 0)
|
if (symbols.length > 0)
|
||||||
{
|
{
|
||||||
response.symbolLocation = symbols[0].location;
|
response.symbolLocation = symbols[0].location;
|
||||||
response.symbolFilePath = symbols[0].symbolFile;
|
response.symbolFilePath = symbols[0].symbolFile;
|
||||||
Log.info(beforeTokens[$ - 1].text, " declared in ",
|
Log.info(symbols[0].name, " declared in ",
|
||||||
response.symbolFilePath, " at ", response.symbolLocation);
|
response.symbolFilePath, " at ", response.symbolLocation);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
Log.error("Could not find symbol");
|
Log.error("Could not find symbol");
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldSwapWithType(CompletionType completionType, CompletionKind kind,
|
/**
|
||||||
size_t current, size_t max) pure nothrow @safe
|
* Handles autocompletion
|
||||||
|
* Params:
|
||||||
|
* request = the autocompletion request
|
||||||
|
* Returns:
|
||||||
|
* the autocompletion response
|
||||||
|
*/
|
||||||
|
AutocompleteResponse complete(const AutocompleteRequest request)
|
||||||
{
|
{
|
||||||
// Modules and packages never have types, so always return false
|
Log.info("Got a completion request");
|
||||||
if (kind == CompletionKind.moduleName
|
|
||||||
|| kind == CompletionKind.packageName
|
const(Token)[] tokenArray;
|
||||||
|| kind == CompletionKind.className
|
auto beforeTokens = getTokensBeforeCursor(request.sourceCode,
|
||||||
|| kind == CompletionKind.structName
|
request.cursorPosition, tokenArray);
|
||||||
|| kind == CompletionKind.interfaceName
|
string partial;
|
||||||
|| kind == CompletionKind.enumName
|
IdType tokenType;
|
||||||
|| kind == CompletionKind.unionName)
|
|
||||||
|
if (beforeTokens.length >= 2 && beforeTokens[$ - 1] == tok!"(")
|
||||||
|
return parenCompletion(beforeTokens, tokenArray, request.cursorPosition);
|
||||||
|
|
||||||
|
AutocompleteResponse response;
|
||||||
|
if (beforeTokens.length >= 1 && beforeTokens[$ - 1] == tok!"identifier")
|
||||||
{
|
{
|
||||||
return false;
|
partial = beforeTokens[$ - 1].text;
|
||||||
|
tokenType = beforeTokens[$ - 1].type;
|
||||||
|
beforeTokens = beforeTokens[0 .. $ - 1];
|
||||||
}
|
}
|
||||||
// Swap out every part of a chain with its type except the last part
|
else if (beforeTokens.length >= 2 && beforeTokens[$ - 1] == tok!".")
|
||||||
if (current < max)
|
tokenType = beforeTokens[$ - 2].type;
|
||||||
return true;
|
else
|
||||||
// Only swap out types for these kinds
|
return response;
|
||||||
immutable bool isInteresting =
|
|
||||||
kind == CompletionKind.variableName
|
switch (tokenType)
|
||||||
|| kind == CompletionKind.memberVariableName
|
{
|
||||||
|| kind == CompletionKind.enumMember
|
case tok!"stringLiteral":
|
||||||
|| kind == CompletionKind.functionName;
|
case tok!"wstringLiteral":
|
||||||
return completionType == CompletionType.identifiers && isInteresting;
|
case tok!"dstringLiteral":
|
||||||
|
foreach (symbol; (cast() arraySymbols)[])
|
||||||
|
{
|
||||||
|
response.completionKinds ~= symbol.kind;
|
||||||
|
response.completions ~= symbol.name;
|
||||||
|
}
|
||||||
|
response.completionType = CompletionType.identifiers;
|
||||||
|
break;
|
||||||
|
case tok!"int":
|
||||||
|
case tok!"uint":
|
||||||
|
case tok!"long":
|
||||||
|
case tok!"ulong":
|
||||||
|
case tok!"char":
|
||||||
|
case tok!"wchar":
|
||||||
|
case tok!"dchar":
|
||||||
|
case tok!"bool":
|
||||||
|
case tok!"byte":
|
||||||
|
case tok!"ubyte":
|
||||||
|
case tok!"short":
|
||||||
|
case tok!"ushort":
|
||||||
|
case tok!"cent":
|
||||||
|
case tok!"ucent":
|
||||||
|
case tok!"float":
|
||||||
|
case tok!"ifloat":
|
||||||
|
case tok!"cfloat":
|
||||||
|
case tok!"idouble":
|
||||||
|
case tok!"cdouble":
|
||||||
|
case tok!"double":
|
||||||
|
case tok!"real":
|
||||||
|
case tok!"ireal":
|
||||||
|
case tok!"creal":
|
||||||
|
case tok!"identifier":
|
||||||
|
case tok!")":
|
||||||
|
case tok!"]":
|
||||||
|
case tok!"this":
|
||||||
|
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray,
|
||||||
|
"stdin");
|
||||||
|
auto expression = getExpression(beforeTokens);
|
||||||
|
response.setCompletions(completionScope, expression,
|
||||||
|
request.cursorPosition, CompletionType.identifiers, partial);
|
||||||
|
break;
|
||||||
|
case tok!"(":
|
||||||
|
case tok!"{":
|
||||||
|
case tok!"[":
|
||||||
|
case tok!";":
|
||||||
|
case tok!":":
|
||||||
|
// TODO: global scope
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* sourceCode = the source code of the file being edited
|
||||||
|
* cursorPosition = the cursor position in bytes
|
||||||
|
* Returns:
|
||||||
|
* a sorted range of tokens before the cursor position
|
||||||
|
*/
|
||||||
|
auto getTokensBeforeCursor(const(ubyte[]) sourceCode, size_t cursorPosition,
|
||||||
|
out const(Token)[] tokenArray)
|
||||||
|
{
|
||||||
|
LexerConfig config;
|
||||||
|
config.fileName = "stdin";
|
||||||
|
StringCache* cache = new StringCache(StringCache.defaultBucketCount);
|
||||||
|
auto tokens = byToken(cast(ubyte[]) sourceCode, config, cache);
|
||||||
|
tokenArray = tokens.array();
|
||||||
|
auto sortedTokens = assumeSorted(tokenArray);
|
||||||
|
return sortedTokens.lowerBound(cast(size_t) cursorPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* request = the autocompletion request
|
||||||
|
* type = type the autocompletion type
|
||||||
|
* Returns:
|
||||||
|
* all symbols that should be considered for the autocomplete list based on
|
||||||
|
* the request's source code, cursor position, and completion type.
|
||||||
|
*/
|
||||||
|
ACSymbol*[] getSymbolsForCompletion(const AutocompleteRequest request,
|
||||||
|
const CompletionType type)
|
||||||
|
{
|
||||||
|
const(Token)[] tokenArray;
|
||||||
|
auto beforeTokens = getTokensBeforeCursor(request.sourceCode,
|
||||||
|
request.cursorPosition, tokenArray);
|
||||||
|
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray, "stdin");
|
||||||
|
auto expression = getExpression(beforeTokens);
|
||||||
|
return getSymbolsByTokenChain(completionScope, expression,
|
||||||
|
request.cursorPosition, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles paren completion for function calls and some keywords
|
||||||
|
* Params:
|
||||||
|
* beforeTokens = the tokens before the cursor
|
||||||
|
* tokenArray = all tokens in the file
|
||||||
|
* cursorPosition = the cursor position in bytes
|
||||||
|
* Returns:
|
||||||
|
* the autocompletion response
|
||||||
|
*/
|
||||||
|
AutocompleteResponse parenCompletion(T)(T beforeTokens,
|
||||||
|
const(Token)[] tokenArray, size_t cursorPosition)
|
||||||
|
{
|
||||||
|
AutocompleteResponse response;
|
||||||
|
immutable(string)[] completions;
|
||||||
|
switch (beforeTokens[$ - 2].type)
|
||||||
|
{
|
||||||
|
case tok!"__traits":
|
||||||
|
completions = traits;
|
||||||
|
goto fillResponse;
|
||||||
|
case tok!"scope":
|
||||||
|
completions = scopes;
|
||||||
|
goto fillResponse;
|
||||||
|
case tok!"version":
|
||||||
|
completions = versions;
|
||||||
|
goto fillResponse;
|
||||||
|
case tok!"extern":
|
||||||
|
completions = linkages;
|
||||||
|
goto fillResponse;
|
||||||
|
case tok!"pragma":
|
||||||
|
completions = pragmas;
|
||||||
|
fillResponse:
|
||||||
|
response.completionType = CompletionType.identifiers;
|
||||||
|
for (size_t i = 0; i < completions.length; i++)
|
||||||
|
{
|
||||||
|
response.completions ~= completions[i];
|
||||||
|
response.completionKinds ~= CompletionKind.keyword;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case tok!"identifier":
|
||||||
|
case tok!")":
|
||||||
|
case tok!"]":
|
||||||
|
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray,
|
||||||
|
"stdin");
|
||||||
|
auto expression = getExpression(beforeTokens[0 .. $ - 1]);
|
||||||
|
response.setCompletions(completionScope, expression,
|
||||||
|
cursorPosition, CompletionType.calltips);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
ACSymbol*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope,
|
ACSymbol*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope,
|
||||||
T tokens, size_t cursorPosition, CompletionType completionType)
|
T tokens, size_t cursorPosition, CompletionType completionType)
|
||||||
{
|
{
|
||||||
|
@ -315,140 +433,9 @@ ACSymbol*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope,
|
||||||
return symbols;
|
return symbols;
|
||||||
}
|
}
|
||||||
|
|
||||||
AutocompleteResponse complete(const AutocompleteRequest request)
|
/**
|
||||||
{
|
*
|
||||||
Log.info("Got a completion request");
|
*/
|
||||||
AutocompleteResponse response;
|
|
||||||
|
|
||||||
LexerConfig config;
|
|
||||||
config.fileName = "stdin";
|
|
||||||
StringCache* cache = new StringCache(StringCache.defaultBucketCount);
|
|
||||||
auto tokens = byToken(cast(ubyte[]) request.sourceCode, config,
|
|
||||||
cache);
|
|
||||||
const(Token)[] tokenArray = void;
|
|
||||||
try {
|
|
||||||
tokenArray = tokens.array();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.error("Could not provide autocomplete due to lexing exception: ", e.msg);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
auto sortedTokens = assumeSorted(tokenArray);
|
|
||||||
string partial;
|
|
||||||
|
|
||||||
auto beforeTokens = sortedTokens.lowerBound(cast(size_t) request.cursorPosition);
|
|
||||||
|
|
||||||
IdType tokenType;
|
|
||||||
|
|
||||||
if (beforeTokens.length >= 1 && beforeTokens[$ - 1] == tok!"identifier")
|
|
||||||
{
|
|
||||||
partial = beforeTokens[$ - 1].text;
|
|
||||||
tokenType = beforeTokens[$ - 1].type;
|
|
||||||
beforeTokens = beforeTokens[0 .. $ - 1];
|
|
||||||
goto dotCompletion;
|
|
||||||
}
|
|
||||||
else if (beforeTokens.length >= 2 && beforeTokens[$ - 1] == tok!"(")
|
|
||||||
{
|
|
||||||
immutable(string)[] completions;
|
|
||||||
switch (beforeTokens[$ - 2].type)
|
|
||||||
{
|
|
||||||
case tok!"__traits":
|
|
||||||
completions = traits;
|
|
||||||
goto fillResponse;
|
|
||||||
case tok!"scope":
|
|
||||||
completions = scopes;
|
|
||||||
goto fillResponse;
|
|
||||||
case tok!"version":
|
|
||||||
completions = versions;
|
|
||||||
goto fillResponse;
|
|
||||||
case tok!"extern":
|
|
||||||
completions = linkages;
|
|
||||||
goto fillResponse;
|
|
||||||
case tok!"pragma":
|
|
||||||
completions = pragmas;
|
|
||||||
fillResponse:
|
|
||||||
response.completionType = CompletionType.identifiers;
|
|
||||||
for (size_t i = 0; i < completions.length; i++)
|
|
||||||
{
|
|
||||||
response.completions ~= completions[i];
|
|
||||||
response.completionKinds ~= CompletionKind.keyword;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case tok!"identifier":
|
|
||||||
case tok!")":
|
|
||||||
case tok!"]":
|
|
||||||
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray,
|
|
||||||
"stdin");
|
|
||||||
auto expression = getExpression(beforeTokens[0 .. $ - 1]);
|
|
||||||
response.setCompletions(completionScope, expression,
|
|
||||||
request.cursorPosition, CompletionType.calltips);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (beforeTokens.length >= 2 && beforeTokens[$ - 1] == tok!".")
|
|
||||||
{
|
|
||||||
tokenType = beforeTokens[$ - 2].type;
|
|
||||||
dotCompletion:
|
|
||||||
switch (tokenType)
|
|
||||||
{
|
|
||||||
case tok!"stringLiteral":
|
|
||||||
case tok!"wstringLiteral":
|
|
||||||
case tok!"dstringLiteral":
|
|
||||||
foreach (symbol; (cast() arraySymbols)[])
|
|
||||||
{
|
|
||||||
response.completionKinds ~= symbol.kind;
|
|
||||||
response.completions ~= symbol.name;
|
|
||||||
}
|
|
||||||
response.completionType = CompletionType.identifiers;
|
|
||||||
break;
|
|
||||||
case tok!"int":
|
|
||||||
case tok!"uint":
|
|
||||||
case tok!"long":
|
|
||||||
case tok!"ulong":
|
|
||||||
case tok!"char":
|
|
||||||
case tok!"wchar":
|
|
||||||
case tok!"dchar":
|
|
||||||
case tok!"bool":
|
|
||||||
case tok!"byte":
|
|
||||||
case tok!"ubyte":
|
|
||||||
case tok!"short":
|
|
||||||
case tok!"ushort":
|
|
||||||
case tok!"cent":
|
|
||||||
case tok!"ucent":
|
|
||||||
case tok!"float":
|
|
||||||
case tok!"ifloat":
|
|
||||||
case tok!"cfloat":
|
|
||||||
case tok!"idouble":
|
|
||||||
case tok!"cdouble":
|
|
||||||
case tok!"double":
|
|
||||||
case tok!"real":
|
|
||||||
case tok!"ireal":
|
|
||||||
case tok!"creal":
|
|
||||||
case tok!"identifier":
|
|
||||||
case tok!")":
|
|
||||||
case tok!"]":
|
|
||||||
case tok!"this":
|
|
||||||
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray,
|
|
||||||
"stdin");
|
|
||||||
auto expression = getExpression(beforeTokens);
|
|
||||||
response.setCompletions(completionScope, expression,
|
|
||||||
request.cursorPosition, CompletionType.identifiers, partial);
|
|
||||||
break;
|
|
||||||
case tok!"(":
|
|
||||||
case tok!"{":
|
|
||||||
case tok!"[":
|
|
||||||
case tok!";":
|
|
||||||
case tok!":":
|
|
||||||
// TODO: global scope
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCompletions(T)(ref AutocompleteResponse response,
|
void setCompletions(T)(ref AutocompleteResponse response,
|
||||||
const(Scope)* completionScope, T tokens, size_t cursorPosition,
|
const(Scope)* completionScope, T tokens, size_t cursorPosition,
|
||||||
CompletionType completionType, string partial = null)
|
CompletionType completionType, string partial = null)
|
||||||
|
@ -538,6 +525,9 @@ void setCompletions(T)(ref AutocompleteResponse response,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
T getExpression(T)(T beforeTokens)
|
T getExpression(T)(T beforeTokens)
|
||||||
{
|
{
|
||||||
if (beforeTokens.length == 0)
|
if (beforeTokens.length == 0)
|
||||||
|
@ -641,6 +631,12 @@ T getExpression(T)(T beforeTokens)
|
||||||
return beforeTokens[i .. $];
|
return beforeTokens[i .. $];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates the response with completion information for an import statement
|
||||||
|
* Params:
|
||||||
|
* tokens = the tokens after the "import" keyword and before the cursor
|
||||||
|
* response = the response that should be populated
|
||||||
|
*/
|
||||||
void setImportCompletions(T)(T tokens, ref AutocompleteResponse response)
|
void setImportCompletions(T)(T tokens, ref AutocompleteResponse response)
|
||||||
{
|
{
|
||||||
response.completionType = CompletionType.identifiers;
|
response.completionType = CompletionType.identifiers;
|
||||||
|
@ -650,7 +646,7 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response)
|
||||||
string path = buildPath(moduleParts);
|
string path = buildPath(moduleParts);
|
||||||
foreach (importDirectory; ModuleCache.getImportPaths())
|
foreach (importDirectory; ModuleCache.getImportPaths())
|
||||||
{
|
{
|
||||||
string p = format("%s%s%s", importDirectory, dirSeparator, path);
|
string p = buildPath(importDirectory, path);
|
||||||
Log.trace("Checking for ", p);
|
Log.trace("Checking for ", p);
|
||||||
if (!exists(p))
|
if (!exists(p))
|
||||||
continue;
|
continue;
|
||||||
|
@ -672,6 +668,47 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* completionType = the completion type being requested
|
||||||
|
* kind = the kind of the current item in the completion chain
|
||||||
|
* current = the index of the current item in the symbol chain
|
||||||
|
* max = the number of items in the symbol chain
|
||||||
|
* Returns:
|
||||||
|
* true if the symbol should be swapped with its type field
|
||||||
|
*/
|
||||||
|
bool shouldSwapWithType(CompletionType completionType, CompletionKind kind,
|
||||||
|
size_t current, size_t max) pure nothrow @safe
|
||||||
|
{
|
||||||
|
// Modules and packages never have types, so always return false
|
||||||
|
if (kind == CompletionKind.moduleName
|
||||||
|
|| kind == CompletionKind.packageName
|
||||||
|
|| kind == CompletionKind.className
|
||||||
|
|| kind == CompletionKind.structName
|
||||||
|
|| kind == CompletionKind.interfaceName
|
||||||
|
|| kind == CompletionKind.enumName
|
||||||
|
|| kind == CompletionKind.unionName)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Swap out every part of a chain with its type except the last part
|
||||||
|
if (current < max)
|
||||||
|
return true;
|
||||||
|
// Only swap out types for these kinds
|
||||||
|
immutable bool isInteresting =
|
||||||
|
kind == CompletionKind.variableName
|
||||||
|
|| kind == CompletionKind.memberVariableName
|
||||||
|
|| kind == CompletionKind.enumMember
|
||||||
|
|| kind == CompletionKind.functionName;
|
||||||
|
return completionType == CompletionType.identifiers && isInteresting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* comment = the comment to format
|
||||||
|
* Returns
|
||||||
|
* the comment with the comment characters removed
|
||||||
|
*/
|
||||||
string formatComment(string comment)
|
string formatComment(string comment)
|
||||||
{
|
{
|
||||||
import std.string;
|
import std.string;
|
||||||
|
|
2
client.d
2
client.d
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of DCD, a development tool for the D programming language.
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
* Copyright (C) 2013 Brian Schott
|
* Copyright (C) 2014 Brian Schott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of DCD, a development tool for the D programming language.
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
* Copyright (C) 2013 Brian Schott
|
* Copyright (C) 2014 Brian Schott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of DCD, a development tool for the D programming language.
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
* Copyright (C) 2013 Brian Schott
|
* Copyright (C) 2014 Brian Schott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of DCD, a development tool for the D programming language.
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
* Copyright (C) 2013 Brian Schott
|
* Copyright (C) 2014 Brian Schott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
14
semantic.d
14
semantic.d
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of DCD, a development tool for the D programming language.
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
* Copyright (C) 2013 Brian Schott
|
* Copyright (C) 2014 Brian Schott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -34,6 +34,13 @@ public:
|
||||||
|
|
||||||
@disable this();
|
@disable this();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* name = the name
|
||||||
|
* kind = the completion kind
|
||||||
|
* symbolFile = the file name for this symbol
|
||||||
|
* location = the location of this symbol
|
||||||
|
*/
|
||||||
this(string name, CompletionKind kind, string symbolFile,
|
this(string name, CompletionKind kind, string symbolFile,
|
||||||
size_t location = size_t.max)
|
size_t location = size_t.max)
|
||||||
{
|
{
|
||||||
|
@ -42,6 +49,9 @@ public:
|
||||||
acSymbol.symbolFile = symbolFile;
|
acSymbol.symbolFile = symbolFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a child to the children field and updates the acSymbol's parts field
|
||||||
|
*/
|
||||||
void addChild(SemanticSymbol* child)
|
void addChild(SemanticSymbol* child)
|
||||||
{
|
{
|
||||||
children ~= child;
|
children ~= child;
|
||||||
|
@ -66,7 +76,9 @@ public:
|
||||||
/// Protection level for this symobol
|
/// Protection level for this symobol
|
||||||
IdType protection;
|
IdType protection;
|
||||||
|
|
||||||
|
/// Parent symbol
|
||||||
SemanticSymbol* parent;
|
SemanticSymbol* parent;
|
||||||
|
|
||||||
|
/// Child symbols
|
||||||
SemanticSymbol*[] children;
|
SemanticSymbol*[] children;
|
||||||
}
|
}
|
||||||
|
|
49
server.d
49
server.d
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of DCD, a development tool for the D programming language.
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
* Copyright (C) 2013 Brian Schott
|
* Copyright (C) 2014 Brian Schott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -151,19 +151,42 @@ int main(string[] args)
|
||||||
Log.info("Shutting down.");
|
Log.info("Shutting down.");
|
||||||
break serverLoop;
|
break serverLoop;
|
||||||
case RequestKind.autocomplete:
|
case RequestKind.autocomplete:
|
||||||
AutocompleteResponse response = complete(request);
|
try
|
||||||
ubyte[] responseBytes = msgpack.pack(response);
|
{
|
||||||
s.send(responseBytes);
|
AutocompleteResponse response = complete(request);
|
||||||
|
ubyte[] responseBytes = msgpack.pack(response);
|
||||||
|
s.send(responseBytes);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.error("Could not handle autocomplete request due to an exception:",
|
||||||
|
e.msg);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RequestKind.doc:
|
case RequestKind.doc:
|
||||||
AutocompleteResponse response = getDoc(request);
|
try
|
||||||
ubyte[] responseBytes = msgpack.pack(response);
|
{
|
||||||
s.send(responseBytes);
|
AutocompleteResponse response = getDoc(request);
|
||||||
|
ubyte[] responseBytes = msgpack.pack(response);
|
||||||
|
s.send(responseBytes);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.error("Could not get DDoc information", e.msg);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case RequestKind.symbolLocation:
|
case RequestKind.symbolLocation:
|
||||||
AutocompleteResponse response = findDeclaration(request);
|
try
|
||||||
ubyte[] responseBytes = msgpack.pack(response);
|
{
|
||||||
s.send(responseBytes);
|
AutocompleteResponse response = findDeclaration(request);
|
||||||
|
ubyte[] responseBytes = msgpack.pack(response);
|
||||||
|
s.send(responseBytes);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.error("Could not get symbol location", e.msg);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Log.info("Request processed in ", requestWatch.peek().to!("msecs", float), " milliseconds");
|
Log.info("Request processed in ", requestWatch.peek().to!("msecs", float), " milliseconds");
|
||||||
|
@ -171,6 +194,9 @@ int main(string[] args)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates the configuration file
|
||||||
|
*/
|
||||||
string getConfigurationLocation()
|
string getConfigurationLocation()
|
||||||
{
|
{
|
||||||
version (useXDG)
|
version (useXDG)
|
||||||
|
@ -208,6 +234,9 @@ void warnAboutOldConfigLocation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads import directories from the configuration file
|
||||||
|
*/
|
||||||
string[] loadConfiguredImportDirs()
|
string[] loadConfiguredImportDirs()
|
||||||
{
|
{
|
||||||
warnAboutOldConfigLocation();
|
warnAboutOldConfigLocation();
|
||||||
|
|
10
stupidlog.d
10
stupidlog.d
|
@ -1,9 +1,7 @@
|
||||||
/*******************************************************************************
|
/**
|
||||||
* Authors: Brian Schott
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
* Copyright: Brian Schott
|
* Copyright (C) 2014 Brian Schott
|
||||||
* Date: Oct 5 2013
|
|
||||||
*
|
*
|
||||||
* License:
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
@ -16,7 +14,7 @@
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
******************************************************************************/
|
*/
|
||||||
|
|
||||||
module stupidlog;
|
module stupidlog;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue