diff --git a/README.md b/README.md index 69c501e..20e74d6 100644 --- a/README.md +++ b/README.md @@ -175,13 +175,19 @@ a tab separated format: * definition: function or variable definition string or close approximation for information display purpose * symbol location: in which file (or `stdin`) & byte offset this symbol is defined. Separated with a space. * documentation: escaped documentation string of this symbol +* typeOf: resolved type name of this symbol: + + * For variables, fields, globals, constants: resolved type or empty if unresolved. + * For functions: resolved return type or empty if unresolved. + * For constructors: may be struct/class name or empty in any case. + * Otherwise (probably) empty. #### Example `--extended` output identifiers libraryFunction f Tuple!long libraryFunction(string s, string s2) stdin 190 foobar - libraryFunction f int* libraryFunction(string s) stdin 99 Hello\nWorld - libraryVariable v int libraryVariable stdin 56 My variable + libraryFunction f int* libraryFunction(string s) stdin 99 Hello\nWorld int* + libraryVariable v int libraryVariable stdin 56 My variable int libreTypes g stdin 298 #### Note @@ -190,6 +196,9 @@ DCD's output will start with "identifiers" when completing at a left paren character if the keywords *pragma*, *scope*, *__traits*, *extern*, or *version* were just before the paren. +Types in the calltips and typeOf column may not be complete, e.g. missing +template parameters or typeof expressions, etc. + ### Parenthesis completion When the first line of output is "calltips", the editor should display a function diff --git a/common/src/dcd/common/messages.d b/common/src/dcd/common/messages.d index a2aff4a..e0e7a8f 100644 --- a/common/src/dcd/common/messages.d +++ b/common/src/dcd/common/messages.d @@ -152,6 +152,14 @@ struct AutocompleteResponse * Documentation associated with this symbol. */ string documentation; + // when changing the behavior here, update README.md + /** + * For variables, fields, globals, constants: resolved type or empty if unresolved. + * For functions: resolved return type or empty if unresolved. + * For constructors: may be struct/class name or empty in any case. + * Otherwise (probably) empty. + */ + string typeOf; } /** diff --git a/dsymbol/src/dsymbol/symbol.d b/dsymbol/src/dsymbol/symbol.d index 3b6a9c6..3d740c7 100644 --- a/dsymbol/src/dsymbol/symbol.d +++ b/dsymbol/src/dsymbol/symbol.d @@ -445,6 +445,43 @@ struct DSymbol /// Protection level for this symbol IdType protection; + string formatType(string suffix = "") const + { + if (kind == CompletionKind.functionName) + { + if (type) // try to give return type symbol + return type.formatType; + else // null if unresolved, user can manually pick .name or .callTip if needed + return null; + } + else if (name == POINTER_SYMBOL_NAME) + { + if (!type) + return suffix ~ "*"; + else + return type.formatType(suffix ~ "*"); + } + else if (name == ARRAY_SYMBOL_NAME) + { + if (!type) + return suffix ~ "[]"; + else + return type.formatType(suffix ~ "[]"); + } + else if (name == ASSOC_ARRAY_SYMBOL_NAME) + { + // TODO: include AA key type + if (!type) + return suffix ~ "[...]"; + else + return type.formatType(suffix ~ "[...]"); + } + else + { + // TODO: include template parameters + return name ~ suffix; + } + } } /** diff --git a/dsymbol/src/dsymbol/tests.d b/dsymbol/src/dsymbol/tests.d index ffb6e35..0ff9d46 100644 --- a/dsymbol/src/dsymbol/tests.d +++ b/dsymbol/src/dsymbol/tests.d @@ -217,8 +217,8 @@ unittest }; ScopeSymbolPair pair = generateAutocompleteTrees(source, cache); DSymbol* meaningOfLife = pair.symbol.getFirstPartNamed(istring("meaningOfLife")); - writeln(meaningOfLife.type.name); - assert(meaningOfLife.type.name == "int"); + writeln(meaningOfLife.type.formatType); + assert(meaningOfLife.type.formatType == "int*"); } diff --git a/src/dcd/client/client.d b/src/dcd/client/client.d index f1c196f..e0be61d 100644 --- a/src/dcd/client/client.d +++ b/src/dcd/client/client.d @@ -396,7 +396,8 @@ void printCompletionResponse(ref const AutocompleteResponse response, bool exten completion.kind == char.init ? "" : "" ~ completion.kind, completion.definition, completion.symbolFilePath.length ? completion.symbolFilePath ~ " " ~ completion.symbolLocation.to!string : "", - completion.documentation + completion.documentation, + completion.typeOf )); else app.put(makeTabSeparated(completion.identifier, "" ~ completion.kind)); diff --git a/src/dcd/server/autocomplete/complete.d b/src/dcd/server/autocomplete/complete.d index 17b8fb3..4363bf0 100644 --- a/src/dcd/server/autocomplete/complete.d +++ b/src/dcd/server/autocomplete/complete.d @@ -701,5 +701,6 @@ do auto completion = makeSymbolCompletionInfo(symbol, char.init); completion.identifier = "this"; completion.definition = generatedStructConstructorCalltip; + completion.typeOf = symbol.name; return completion; } diff --git a/src/dcd/server/autocomplete/util.d b/src/dcd/server/autocomplete/util.d index ba22733..81dffbc 100644 --- a/src/dcd/server/autocomplete/util.d +++ b/src/dcd/server/autocomplete/util.d @@ -614,16 +614,26 @@ bool isUdaExpression(T)(ref T tokens) AutocompleteResponse.Completion makeSymbolCompletionInfo(const DSymbol* symbol, char kind) { - string definition; - if ((kind == CompletionKind.variableName || kind == CompletionKind.memberVariableName) && symbol.type) - definition = symbol.type.name ~ ' ' ~ symbol.name; - else if (kind == CompletionKind.enumMember) - definition = symbol.name; // TODO: add enum value to definition string - else - definition = symbol.callTip; - // TODO: definition strings could include more information, like on classes inheritance - return AutocompleteResponse.Completion(symbol.name, kind, definition, + auto ret = AutocompleteResponse.Completion(symbol.name, kind, null, symbol.symbolFile, symbol.location, symbol.doc); + + if (symbol.type) + ret.typeOf = symbol.type.formatType; + + if ((kind == CompletionKind.variableName || kind == CompletionKind.memberVariableName) && symbol.type) + { + if (symbol.type.kind == CompletionKind.functionName && !ret.typeOf.length) + ret.definition = symbol.type.name ~ ' ' ~ symbol.name; + else + ret.definition = ret.typeOf ~ ' ' ~ symbol.name; + } + else if (kind == CompletionKind.enumMember) + ret.definition = symbol.name; // TODO: add enum value to definition string + else + ret.definition = symbol.callTip; + + // TODO: extend completion with more info such as class inheritance + return ret; } bool doUFCSSearch(string beforeToken, string lastToken) diff --git a/tests/tc059/expected1.txt b/tests/tc059/expected1.txt index a000d6f..092c0f7 100644 --- a/tests/tc059/expected1.txt +++ b/tests/tc059/expected1.txt @@ -1,4 +1,5 @@ identifiers -libraryFunction f Tuple!long libraryFunction(string s, string s2) stdin 190 foobar -libraryFunction f int* libraryFunction(string s) stdin 99 Hello\nWorld -libraryVariable v int libraryVariable stdin 56 My variable +libraryFunction f Tuple!long libraryFunction(string s, string s2) stdin 223 foobar +libraryFunction f int* libraryFunction(string s) stdin 132 Hello\nWorld int* +libraryVariable v int libraryVariable stdin 56 My variable int +libraryVariable2 v int* libraryVariable2 stdin 88 My variable int* diff --git a/tests/tc059/file.d b/tests/tc059/file.d index d5acbb7..61d7fe6 100644 --- a/tests/tc059/file.d +++ b/tests/tc059/file.d @@ -5,6 +5,8 @@ void main(string[] args) /// My variable int libraryVariable; +/// ditto +int* libraryVariable2; /// Hello /// World diff --git a/tests/tc061/expected1.txt b/tests/tc061/expected1.txt index a223a51..9155960 100644 --- a/tests/tc061/expected1.txt +++ b/tests/tc061/expected1.txt @@ -1,3 +1,3 @@ calltips -libraryFunction Tuple!long libraryFunction(string s, string s2) stdin 166 foobar -libraryFunction int* libraryFunction(string s) stdin 75 Hello\nWorld +libraryFunction Tuple!long libraryFunction(string s, string s2) stdin 166 foobar +libraryFunction int* libraryFunction(string s) stdin 75 Hello\nWorld int* diff --git a/tests/tc_extended_ditto/expected1.txt b/tests/tc_extended_ditto/expected1.txt index 8574d4a..df0cd3d 100644 --- a/tests/tc_extended_ditto/expected1.txt +++ b/tests/tc_extended_ditto/expected1.txt @@ -1,3 +1,3 @@ identifiers -foo f void foo() stdin 26 my documentation -foo f void foo(int i) stdin 49 my documentation +foo f void foo() stdin 26 my documentation void +foo f void foo(int i) stdin 49 my documentation void diff --git a/tests/tc_extended_types/expected1.txt b/tests/tc_extended_types/expected1.txt new file mode 100644 index 0000000..fd102a5 --- /dev/null +++ b/tests/tc_extended_types/expected1.txt @@ -0,0 +1,2 @@ +identifiers +bar v foo bar stdin 92 diff --git a/tests/tc_extended_types/file.d b/tests/tc_extended_types/file.d new file mode 100644 index 0000000..9a8e905 --- /dev/null +++ b/tests/tc_extended_types/file.d @@ -0,0 +1,12 @@ +/// my documentation +struct S +{ + T foo(T)() { return T.init; } +} + +void test() +{ + S s; + auto bar = s.foo!int(); + bar +} diff --git a/tests/tc_extended_types/run.sh b/tests/tc_extended_types/run.sh new file mode 100755 index 0000000..c6f7154 --- /dev/null +++ b/tests/tc_extended_types/run.sh @@ -0,0 +1,5 @@ +set -e +set -u + +../../bin/dcd-client $1 file.d -x -c115 > actual1.txt +diff actual1.txt expected1.txt --strip-trailing-cr diff --git a/tests/tc_import_symbol_list/run.sh b/tests/tc_import_symbol_list/run.sh index 7152821..e11da68 100755 --- a/tests/tc_import_symbol_list/run.sh +++ b/tests/tc_import_symbol_list/run.sh @@ -2,5 +2,5 @@ set -e set -u ../../bin/dcd-client $1 file.d --extended -I"$PWD"/newpackage -c$(wc -c < file.d) > actual1.txt -echo -e "identifiers\nSomeStruct\ts\t\t$PWD/newpackage/newmodule.d 26\t" > expected1.txt +echo -e "identifiers\nSomeStruct\ts\t\t$PWD/newpackage/newmodule.d 26\t\t" > expected1.txt diff actual1.txt expected1.txt --strip-trailing-cr diff --git a/tests/tc_pointer_type_printing/expected1.txt b/tests/tc_pointer_type_printing/expected1.txt new file mode 100644 index 0000000..36d01b0 --- /dev/null +++ b/tests/tc_pointer_type_printing/expected1.txt @@ -0,0 +1,14 @@ +identifiers +itemA v S itemA stdin 44 S +itemB v S* itemB stdin 55 S* +itemC v S[]* itemC stdin 68 S[]* +itemD v S[][]* itemD stdin 83 S[][]* +itemE v S[][]*[] itemE stdin 100 S[][]*[] +itemF v S[][]*[][] itemF stdin 119 S[][]*[][] +itemG v S[][...]*[][] itemG stdin 141 S[][...]*[][] +itemH v S[][]*[...][] itemH stdin 163 S[][]*[...][] +itemI v S[...][]*[...][] itemI stdin 188 S[...][]*[...][] +itemJ v S[...]*[]*[...][] itemJ stdin 214 S[...]*[]*[...][] +itemK v S[...]*[]**[...][] itemK stdin 241 S[...]*[]**[...][] +itemL v S[...]*[]*[...]*[] itemL stdin 268 S[...]*[]*[...]*[] +itemM v S[...]*[]*[...]*[]* itemM stdin 296 S[...]*[]*[...]*[]* diff --git a/tests/tc_pointer_type_printing/file.d b/tests/tc_pointer_type_printing/file.d new file mode 100644 index 0000000..f56fa56 --- /dev/null +++ b/tests/tc_pointer_type_printing/file.d @@ -0,0 +1,23 @@ +struct S +{ + int member; +} + +void test() +{ + S itemA; + S* itemB; + S[]* itemC; + S[][]* itemD; + S[][]*[] itemE; + S[][]*[][] itemF; + S[][int]*[][] itemG; + S[][]*[int][] itemH; + S[int][]*[int][] itemI; + S[int]*[]*[int][] itemJ; + S[int]*[]**[int][] itemK; + S[int]*[]*[int]*[] itemL; + S[int]*[]*[int]*[]* itemM; + + item +} diff --git a/tests/tc_pointer_type_printing/run.sh b/tests/tc_pointer_type_printing/run.sh new file mode 100755 index 0000000..9cb30cd --- /dev/null +++ b/tests/tc_pointer_type_printing/run.sh @@ -0,0 +1,5 @@ +set -e +set -u + +../../bin/dcd-client $1 file.d -x -c309 > actual1.txt +diff actual1.txt expected1.txt --strip-trailing-cr diff --git a/tests/tc_recursive_public_import/run.sh b/tests/tc_recursive_public_import/run.sh index d1afc35..972c452 100755 --- a/tests/tc_recursive_public_import/run.sh +++ b/tests/tc_recursive_public_import/run.sh @@ -4,5 +4,5 @@ set -u echo "import: $PWD/testing" ../../bin/dcd-client $1 app.d --extended -I $PWD/ -c50 > actual.txt -echo -e "identifiers\nworld\tv\tWorld world\t$PWD/testing/a.d 77\t" > expected.txt +echo -e "identifiers\nworld\tv\tWorld world\t$PWD/testing/a.d 77\t\tWorld" > expected.txt diff actual.txt expected.txt --strip-trailing-cr