From 172d45ce81e574a9364baef8969c7639e0060e62 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sat, 25 Nov 2017 00:24:39 +0100 Subject: [PATCH] Added --full output mode This will send symbol location & documentation along with completions and calltips partly #20, fix #96, fix #269 --- README.md | 25 + src/dcd/client/client.d | 53 +- src/dcd/common/constants.d | 1119 ++++++++++++++++++++---- src/dcd/common/messages.d | 128 ++- src/dcd/server/autocomplete/complete.d | 60 +- src/dcd/server/autocomplete/doc.d | 32 +- src/dcd/server/autocomplete/localuse.d | 4 +- src/dcd/server/autocomplete/symbols.d | 6 +- src/dcd/server/autocomplete/util.d | 13 + tests/tc059/expected1.txt | 4 + tests/tc059/file.d | 20 + tests/tc059/run.sh | 5 + tests/tc060/file.d | 1 + tests/tc060/run.sh | 16 + tests/tc061/expected1.txt | 3 + tests/tc061/file.d | 17 + tests/tc061/run.sh | 5 + 17 files changed, 1248 insertions(+), 263 deletions(-) create mode 100644 tests/tc059/expected1.txt create mode 100644 tests/tc059/file.d create mode 100755 tests/tc059/run.sh create mode 100644 tests/tc060/file.d create mode 100755 tests/tc060/run.sh create mode 100644 tests/tc061/expected1.txt create mode 100644 tests/tc061/file.d create mode 100755 tests/tc061/run.sh diff --git a/README.md b/README.md index efbc58f..1fc74f9 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,31 @@ tab character, followed by a completion kind calltip v getPartByName f +#### Extended output mode +You can pass `--full` to dcd-client to get more information. Output will now be +escaped (newlines get escaped to `\n`, tabs get escaped to `\t`, backslash gets escaped to `\\`). + +Calltips are slightly different here because they first start with the function name instead of +arguments and the second part will be blank. The actual calltip is now in the third column. + +Columns may be empty, in which case there will be multiple tabs next to each other. + +The following information will be available in every line for completion in this format then in +a tab separated format: +* identifier: raw name of a variable or function, etc +* kind: empty for calltips, see above for rest +* definition: function or variable definition string or close approximation for information display purpose +* symbolFilePath: in which file this symbol is defined or `stdin` +* symbolLocation: the byte offset at which the symbol is located in the file +* documentation: escaped documentation string of this symbol + +#### Example `--full` 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 + libreTypes g stdin 298 + #### Note DCD's output will start with "identifiers" when completing at a left paren character if the keywords *pragma*, *scope*, *__traits*, *extern*, or *version* diff --git a/src/dcd/client/client.d b/src/dcd/client/client.d index 83c7071..315b674 100644 --- a/src/dcd/client/client.d +++ b/src/dcd/client/client.d @@ -51,6 +51,7 @@ int main(string[] args) bool listImports; bool getIdentifier; bool localUse; + bool fullOutput; string search; version(Windows) { @@ -73,7 +74,7 @@ int main(string[] args) "tcp", &useTCP, "socketFile", &socketFile, "getIdentifier", &getIdentifier, "localUsage", &localUse, // TODO:remove this line in Nov. 2017 - "localUse|u", &localUse); + "localUse|u", &localUse, "full|2", &fullOutput); } catch (ConvException e) { @@ -239,7 +240,7 @@ int main(string[] args) else if (localUse) printLocalUse(response); else - printCompletionResponse(response); + printCompletionResponse(response, fullOutput); return 0; } @@ -288,6 +289,10 @@ Options: Searches for all the uses of the symbol at the cursor location in the given filename (or stdin). + --full | -2 + Includes more information with a slightly different format for + calltips when autocompleting. + --query | -q | --status Query the server statis. Returns 0 if the server is running. Returns 1 if the server could not be contacted. @@ -343,16 +348,14 @@ Socket createSocket(string socketFile, ushort port) void printDocResponse(ref const AutocompleteResponse response) { import std.algorithm : each; - response.docComments.each!(writeln); + response.completions.each!(a => a.documentation.escapeTabValue(true).writeln); } void printIdentifierResponse(ref const AutocompleteResponse response) { if (response.completions.length == 0) return; - write(response.completions[0]); - write("\t"); - writeln(response.symbolIdentifier); + writeln(makeTabSeparated(response.completions[0].identifier, response.symbolIdentifier.to!string)); } void printLocationResponse(ref const AutocompleteResponse response) @@ -360,26 +363,35 @@ void printLocationResponse(ref const AutocompleteResponse response) if (response.symbolFilePath is null) writeln("Not found"); else - writefln("%s\t%d", response.symbolFilePath, response.symbolLocation); + writeln(makeTabSeparated(response.symbolFilePath, response.symbolLocation.to!string)); } -void printCompletionResponse(ref const AutocompleteResponse response) +void printCompletionResponse(ref const AutocompleteResponse response, bool full) { if (response.completions.length > 0) { writeln(response.completionType); auto app = appender!(string[])(); - if (response.completionType == CompletionType.identifiers) + if (response.completionType == CompletionType.identifiers || full) { - for (size_t i = 0; i < response.completions.length; i++) - app.put(format("%s\t%s", response.completions[i], response.completionKinds[i])); + foreach (ref completion; response.completions) + { + if (full) + app.put(makeTabSeparated( + completion.identifier, + completion.kind == char.init ? "" : "" ~ completion.kind, + completion.definition, + completion.symbolFilePath.length ? completion.symbolFilePath ~ " " ~ completion.symbolLocation.to!string : "", + completion.documentation + )); + else + app.put(makeTabSeparated(completion.identifier, "" ~ completion.kind)); + } } else { foreach (completion; response.completions) - { - app.put(completion); - } + app.put(completion.definition); } // Deduplicate overloaded methods foreach (line; app.data.sort().uniq) @@ -389,20 +401,17 @@ void printCompletionResponse(ref const AutocompleteResponse response) void printSearchResponse(const AutocompleteResponse response) { - foreach(i; 0 .. response.completions.length) - { - writefln("%s\t%s\t%s", response.completions[i], response.completionKinds[i], - response.locations[i]); - } + foreach(ref completion; response.completions) + writeln(makeTabSeparated(completion.identifier, "" ~ completion.kind, completion.symbolLocation.to!string)); } void printLocalUse(const AutocompleteResponse response) { if (response.symbolFilePath.length) { - writeln(response.symbolFilePath, '\t', response.symbolLocation); - foreach(loc; response.locations) - writeln(loc); + writeln(makeTabSeparated(response.symbolFilePath, response.symbolLocation.to!string)); + foreach(loc; response.completions) + writeln(loc.symbolLocation); } else write("00000"); } diff --git a/src/dcd/common/constants.d b/src/dcd/common/constants.d index 5b37622..9f662e6 100644 --- a/src/dcd/common/constants.d +++ b/src/dcd/common/constants.d @@ -20,187 +20,986 @@ module dcd.common.constants; // The lists in this module should be kept sorted. +struct ConstantCompletion +{ + string identifier; + string ddoc; +} + /** * Pragma arguments */ -immutable string[] pragmas = [ - "inline", - "lib", - "mangle", - "msg", - "startaddress" +immutable ConstantCompletion[] pragmas = [ + // docs from https://github.com/dlang/dlang.org/blob/master/spec/pragma.dd + ConstantCompletion("inline", `$(P Affects whether functions are inlined or not. If at the declaration level, it +affects the functions declared in the block it controls. If inside a function, it +affects the function it is enclosed by. If there are multiple pragma inlines in a function, +the lexically last one takes effect.) + +$(P It takes three forms:) +$(OL + $(LI +--- +pragma(inline) +--- + Sets the behavior to match the default behavior set by the compiler switch + $(DDSUBLINK dmd, switch-inline, $(TT -inline)). + ) + $(LI +--- +pragma(inline, false) +--- + Functions are never inlined. + ) + $(LI +--- +pragma(inline, true) +--- + If a function cannot be inlined with the $(DDSUBLINK dmd, switch-inline, $(TT -inline)) + switch, an error message is issued. This is expected to be improved in the future to causing + functions to always be inlined regardless of compiler switch settings. Whether a compiler can + inline a particular function or not is implementation defined. + ) +) +--- +pragma(inline): +int foo(int x) // foo() is never inlined +{ + pragma(inline, true); + ++x; + pragma(inline, false); // supercedes the others + return x + 3; +} +---`), + ConstantCompletion("lib", `Inserts a directive in the object file to link in the library +specified by the $(ASSIGNEXPRESSION). +The $(ASSIGNEXPRESSION)s must be a string literal: +----------------- +pragma(lib, "foo.lib"); +-----------------`), + ConstantCompletion("mangle", `Overrides the default mangling for a symbol. It's only effective +when the symbol is a function declaration or a variable declaration. +For example this allows linking to a symbol which is a D keyword, which would normally +be disallowed as a symbol name: +----------------- +pragma(mangle, "body") +extern(C) void body_func(); +-----------------`), + ConstantCompletion("msg", `Constructs a message from the arguments and prints to the standard error stream while compiling: +----------------- +pragma(msg, "compiling...", 1, 1.0); +-----------------`), + ConstantCompletion("startaddress", `Puts a directive into the object file saying that the +function specified in the first argument will be the +start address for the program: +----------------- +void foo() { ... } +pragma(startaddress, foo); +----------------- +This is not normally used for application level programming, +but is for specialized systems work. +For applications code, the start address is taken care of +by the runtime library.`) ]; /** * Linkage types */ -immutable string[] linkages = [ - "C", - "C++", - "D", - "Objective-C", - "Pascal", - "System", - "Windows" +immutable ConstantCompletion[] linkages = [ + // https://dlang.org/spec/attribute.html#linkage + ConstantCompletion("C", "Enforces C calling conventions for the function, no mangling"), + ConstantCompletion("C++", "offers limited compatibility with C++"), + ConstantCompletion("D", "Default D mangling and calling conventions"), + ConstantCompletion("Objective-C", + "Objective-C offers limited compatibility with Objective-C, see the Interfacing to Objective-C documentation for more information"), + ConstantCompletion("Pascal"), + ConstantCompletion("System", "`Windows` on Windows platforms, `C` on other platforms."), + ConstantCompletion("Windows", "Enforces Win32/`__stdcall` conventions for the function") ]; +private immutable string isLazyDoc = `$(P Takes one argument. If that argument is a declaration, + $(D true) is returned if it is $(D_KEYWORD ref), $(D_KEYWORD out), + or $(D_KEYWORD lazy), otherwise $(D false). +) + +--- +void fooref(ref int x) +{ + static assert(__traits(isRef, x)); + static assert(!__traits(isOut, x)); + static assert(!__traits(isLazy, x)); +} + +void fooout(out int x) +{ + static assert(!__traits(isRef, x)); + static assert(__traits(isOut, x)); + static assert(!__traits(isLazy, x)); +} + +void foolazy(lazy int x) +{ + static assert(!__traits(isRef, x)); + static assert(!__traits(isOut, x)); + static assert(__traits(isLazy, x)); +} +---`; + /** * Traits arguments */ -immutable string[] traits = [ - "allMembers", - "classInstanceSize", - "compiles", - "derivedMembers", - "getAliasThis", - "getAttributes", - "getFunctionAttributes", - "getFunctionVariadicStyle", - "getLinkage", - "getMember", - "getOverloads", - "getParameterStorageClasses", - "getPointerBitmap", - "getProtection", - "getUnitTests", - "getVirtualFunctions", - "getVirtualIndex", - "getVirtualMethods", - "hasMember", - "identifier", - "isAbstractClass", - "isAbstractFunction", - "isArithmetic", - "isAssociativeArray", - "isFinalClass", - "isFinalFunction", - "isFloating", - "isIntegral", - "isLazy", - "isNested", - "isOut", - "isOverrideFunction", - "isPOD", - "isRef", - "isSame", - "isScalar", - "isStaticArray", - "isStaticFunction", - "isTemplate", - "isUnsigned", - "isVirtualFunction", - "isVirtualMethod", - "parent" +immutable ConstantCompletion[] traits = [ + // https://github.com/dlang/dlang.org/blob/master/spec/traits.dd + ConstantCompletion("allMembers", `Takes a single argument, which must evaluate to either +a type or an expression of type. +A tuple of string literals is returned, each of which +is the name of a member of that type combined with all +of the members of the base classes (if the type is a class). +No name is repeated. +Builtin properties are not included.`), + ConstantCompletion("classInstanceSize", `Takes a single argument, which must evaluate to either +a class type or an expression of class type. +The result +is of type $(CODE size_t), and the value is the number of +bytes in the runtime instance of the class type. +It is based on the static type of a class, not the +polymorphic type.`), + ConstantCompletion("compiles", `$(P Returns a bool $(D true) if all of the arguments +compile (are semantically correct). +The arguments can be symbols, types, or expressions that +are syntactically correct. +The arguments cannot be statements or declarations. +) + +$(P If there are no arguments, the result is $(D false).) + +--- +import std.stdio; + +struct S +{ + static int s1; + int s2; +} + +int foo(); +int bar(); + +void main() +{ + writeln(__traits(compiles)); // false + writeln(__traits(compiles, foo)); // true + writeln(__traits(compiles, foo + 1)); // true + writeln(__traits(compiles, &foo + 1)); // false + writeln(__traits(compiles, typeof(1))); // true + writeln(__traits(compiles, S.s1)); // true + writeln(__traits(compiles, S.s3)); // false + writeln(__traits(compiles, 1,2,3,int,long,std)); // true + writeln(__traits(compiles, 3[1])); // false + writeln(__traits(compiles, 1,2,3,int,long,3[1])); // false +} +--- + +$(P This is useful for:) + +$(UL +$(LI Giving better error messages inside generic code than +the sometimes hard to follow compiler ones.) +$(LI Doing a finer grained specialization than template +partial specialization allows for.) +)`), + ConstantCompletion("derivedMembers", `$(P Takes a single argument, which must evaluate to either +a type or an expression of type. +A tuple of string literals is returned, each of which +is the name of a member of that type. +No name is repeated. +Base class member names are not included. +Builtin properties are not included. +) + +--- +import std.stdio; + +class D +{ + this() { } + ~this() { } + void foo() { } + int foo(int) { return 0; } +} + +void main() +{ + auto a = [__traits(derivedMembers, D)]; + writeln(a); // ["__ctor", "__dtor", "foo"] +} +--- + +$(P The order in which the strings appear in the result +is not defined.)`), + ConstantCompletion("getAliasThis", `Takes one argument, a symbol of aggregate type. +If the given aggregate type has $(D alias this), returns a list of +$(D alias this) names, by a tuple of $(D string)s. +Otherwise returns an empty tuple.`), + ConstantCompletion("getAttributes", `$(P +Takes one argument, a symbol. Returns a tuple of all attached user defined attributes. +If no UDA's exist it will return an empty tuple. +) + +$(P + For more information, see: $(DDSUBLINK spec/attribute, uda, User Defined Attributes) +) + +--- +@(3) int a; +@("string", 7) int b; + +enum Foo; +@Foo int c; + +pragma(msg, __traits(getAttributes, a)); +pragma(msg, __traits(getAttributes, b)); +pragma(msg, __traits(getAttributes, c)); +--- + +Prints: + +$(CONSOLE +tuple(3) +tuple("string", 7) +tuple((Foo)) +)`), + ConstantCompletion("getFunctionAttributes", `$(P + Takes one argument which must either be a function symbol, function literal, + or a function pointer. It returns a string tuple of all the attributes of + that function $(B excluding) any user defined attributes (UDAs can be + retrieved with the $(RELATIVE_LINK2 get-attributes, getAttributes) trait). + If no attributes exist it will return an empty tuple. +) + +$(B Note:) The order of the attributes in the returned tuple is +implementation-defined and should not be relied upon. + +$(P + A list of currently supported attributes are:) + $(UL $(LI $(D pure), $(D nothrow), $(D @nogc), $(D @property), $(D @system), $(D @trusted), $(D @safe), and $(D ref))) + $(B Note:) $(D ref) is a function attribute even though it applies to the return type. + +$(P + Additionally the following attributes are only valid for non-static member functions:) + $(UL $(LI $(D const), $(D immutable), $(D inout), $(D shared))) + +For example: + +--- +int sum(int x, int y) pure nothrow { return x + y; } + +// prints ("pure", "nothrow", "@system") +pragma(msg, __traits(getFunctionAttributes, sum)); + +struct S +{ +void test() const @system { } +} + +// prints ("const", "@system") +pragma(msg, __traits(getFunctionAttributes, S.test)); +--- + +$(P Note that some attributes can be inferred. For example:) + +--- +// prints ("pure", "nothrow", "@nogc", "@trusted") +pragma(msg, __traits(getFunctionAttributes, (int x) @trusted { return x * 2; })); +---`), + ConstantCompletion("getFunctionVariadicStyle", `$(P +Takes one argument which must either be a function symbol, or a type +that is a function, delegate or a function pointer. +It returns a string identifying the kind of +$(LINK2 function.html#variadic, variadic arguments) that are supported. +) + +$(TABLE2 getFunctionVariadicStyle, +$(THEAD string returned, kind, access, example) +$(TROW $(D "none"), not a variadic function,  , $(D void foo();)) +$(TROW $(D "argptr"), D style variadic function, $(D _argptr) and $(D _arguments), $(D void bar(...))) +$(TROW $(D "stdarg"), C style variadic function, $(LINK2 $(ROOT_DIR)phobos/core_stdc_stdarg.html, $(D core.stdc.stdarg)), $(D extern (C) void abc(int, ...))) +$(TROW $(D "typesafe"), typesafe variadic function, array on stack, $(D void def(int[] ...))) +) + +--- +import core.stdc.stdarg; + +void novar() {} +extern(C) void cstyle(int, ...) {} +extern(C++) void cppstyle(int, ...) {} +void dstyle(...) {} +void typesafe(int[]...) {} + +static assert(__traits(getFunctionVariadicStyle, novar) == "none"); +static assert(__traits(getFunctionVariadicStyle, cstyle) == "stdarg"); +static assert(__traits(getFunctionVariadicStyle, cppstyle) == "stdarg"); +static assert(__traits(getFunctionVariadicStyle, dstyle) == "argptr"); +static assert(__traits(getFunctionVariadicStyle, typesafe) == "typesafe"); + +static assert(__traits(getFunctionVariadicStyle, (int[] a...) {}) == "typesafe"); +static assert(__traits(getFunctionVariadicStyle, typeof(cstyle)) == "stdarg"); +---`), + ConstantCompletion("getLinkage", `$(P Takes one argument, which is a declaration symbol, or the type of a function, +delegate, or pointer to function. +Returns a string representing the $(LINK2 attribute.html#LinkageAttribute, LinkageAttribute) +of the declaration. +The string is one of: +) + +$(UL +$(LI $(D "D")) +$(LI $(D "C")) +$(LI $(D "C++")) +$(LI $(D "Windows")) +$(LI $(D "Pascal")) +$(LI $(D "Objective-C")) +$(LI $(D "System")) +) + +--- +extern (C) int fooc(); +alias aliasc = fooc; + +static assert(__traits(getLinkage, fooc) == "C"); +static assert(__traits(getLinkage, aliasc) == "C"); +---`), + ConstantCompletion("getMember", `$(P Takes two arguments, the second must be a string. +The result is an expression formed from the first +argument, followed by a $(SINGLEQUOTE .), followed by the second +argument as an identifier. +) + +--- +import std.stdio; + +struct S +{ + int mx; + static int my; +} + +void main() +{ + S s; + + __traits(getMember, s, "mx") = 1; // same as s.mx=1; + writeln(__traits(getMember, s, "m" ~ "x")); // 1 + + __traits(getMember, S, "mx") = 1; // error, no this for S.mx + __traits(getMember, S, "my") = 2; // ok +} +---`), + ConstantCompletion("getOverloads", `$(P The first argument is an aggregate (e.g. struct/class/module). +The second argument is a string that matches the name of +one of the functions in that aggregate. +The result is a tuple of all the overloads of that function. +) + +--- +import std.stdio; + +class D +{ + this() { } + ~this() { } + void foo() { } + int foo(int) { return 2; } +} + +void main() +{ + D d = new D(); + + foreach (t; __traits(getOverloads, D, "foo")) + writeln(typeid(typeof(t))); + + alias b = typeof(__traits(getOverloads, D, "foo")); + foreach (t; b) + writeln(typeid(t)); + + auto i = __traits(getOverloads, d, "foo")[1](1); + writeln(i); +} +--- + +Prints: + +$(CONSOLE +void() +int() +void() +int() +2 +)`), + ConstantCompletion("getParameterStorageClasses", `$(P + Takes two arguments. + The first must either be a function symbol, or a type + that is a function, delegate or a function pointer. + The second is an integer identifying which parameter, where the first parameter is + 0. + It returns a tuple of strings representing the storage classes of that parameter. +) + +--- +ref int foo(return ref const int* p, scope int* a, out int b, lazy int c); + +static assert(__traits(getParameterStorageClasses, foo, 0)[0] == "return"); +static assert(__traits(getParameterStorageClasses, foo, 0)[1] == "ref"); + +static assert(__traits(getParameterStorageClasses, foo, 1)[0] == "scope"); +static assert(__traits(getParameterStorageClasses, foo, 2)[0] == "out"); +static assert(__traits(getParameterStorageClasses, typeof(&foo), 3)[0] == "lazy"); +---`), + ConstantCompletion("getPointerBitmap", `$(P The argument is a type. + The result is an array of $(D size_t) describing the memory used by an instance of the given type. +) +$(P The first element of the array is the size of the type (for classes it is + the $(GBLINK classInstanceSize)).) +$(P The following elements describe the locations of GC managed pointers within the + memory occupied by an instance of the type. + For type T, there are $(D T.sizeof / size_t.sizeof) possible pointers represented + by the bits of the array values.) +$(P This array can be used by a precise GC to avoid false pointers.) +--- +class C +{ + // implicit virtual function table pointer not marked + // implicit monitor field not marked, usually managed manually + C next; + size_t sz; + void* p; + void function () fn; // not a GC managed pointer +} + +struct S +{ + size_t val1; + void* p; + C c; + byte[] arr; // { length, ptr } + void delegate () dg; // { context, func } +} + +static assert (__traits(getPointerBitmap, C) == [6*size_t.sizeof, 0b010100]); +static assert (__traits(getPointerBitmap, S) == [7*size_t.sizeof, 0b0110110]); +---`), + ConstantCompletion("getProtection", `$(P The argument is a symbol. + The result is a string giving its protection level: "public", "private", "protected", "export", or "package". +) + +--- +import std.stdio; + +class D +{ + export void foo() { } + public int bar; +} + +void main() +{ + D d = new D(); + + auto i = __traits(getProtection, d.foo); + writeln(i); + + auto j = __traits(getProtection, d.bar); + writeln(j); +} +--- + +Prints: + +$(CONSOLE +export +public +)`), + ConstantCompletion("getUnitTests", `$(P + Takes one argument, a symbol of an aggregate (e.g. struct/class/module). + The result is a tuple of all the unit test functions of that aggregate. + The functions returned are like normal nested static functions, + $(DDSUBLINK glossary, ctfe, CTFE) will work and + $(DDSUBLINK spec/attribute, uda, UDA's) will be accessible. +) + +$(H3 Note:) + +$(P + The -unittest flag needs to be passed to the compiler. If the flag + is not passed $(CODE __traits(getUnitTests)) will always return an + empty tuple. +) + +--- +module foo; + +import core.runtime; +import std.stdio; + +struct name { string name; } + +class Foo +{ + unittest + { + writeln("foo.Foo.unittest"); + } +} + +@name("foo") unittest +{ + writeln("foo.unittest"); +} + +template Tuple (T...) +{ + alias Tuple = T; +} + +shared static this() +{ + // Override the default unit test runner to do nothing. After that, "main" will + // be called. + Runtime.moduleUnitTester = { return true; }; +} + +void main() +{ + writeln("start main"); + + alias tests = Tuple!(__traits(getUnitTests, foo)); + static assert(tests.length == 1); + + alias attributes = Tuple!(__traits(getAttributes, tests[0])); + static assert(attributes.length == 1); + + foreach (test; tests) + test(); + + foreach (test; __traits(getUnitTests, Foo)) + test(); +} +--- + +$(P By default, the above will print:) + +$(CONSOLE +start main +foo.unittest +foo.Foo.unittest +)`), + ConstantCompletion("getVirtualFunctions", `The same as $(GLINK getVirtualMethods), except that +final functions that do not override anything are included.`), + ConstantCompletion("getVirtualIndex", `Takes a single argument which must evaluate to a function. +The result is a $(CODE ptrdiff_t) containing the index +of that function within the vtable of the parent type. +If the function passed in is final and does not override +a virtual function, $(D -1) is returned instead.`), + ConstantCompletion("getVirtualMethods", `$(P The first argument is a class type or an expression of + class type. + The second argument is a string that matches the name of + one of the functions of that class. + The result is a tuple of the virtual overloads of that function. + It does not include final functions that do not override anything. +) + +--- +import std.stdio; + +class D +{ + this() { } + ~this() { } + void foo() { } + int foo(int) { return 2; } +} + +void main() +{ + D d = new D(); + + foreach (t; __traits(getVirtualMethods, D, "foo")) + writeln(typeid(typeof(t))); + + alias b = typeof(__traits(getVirtualMethods, D, "foo")); + foreach (t; b) + writeln(typeid(t)); + + auto i = __traits(getVirtualMethods, d, "foo")[1](1); + writeln(i); +} +--- + +Prints: + +$(CONSOLE +void() +int() +void() +int() +2 +)`), + ConstantCompletion("hasMember", `$(P The first argument is a type that has members, or + is an expression of a type that has members. + The second argument is a string. + If the string is a valid property of the type, + $(D true) is returned, otherwise $(D false). +) + +--- +import std.stdio; + +struct S +{ + int m; +} + +void main() +{ + S s; + + writeln(__traits(hasMember, S, "m")); // true + writeln(__traits(hasMember, s, "m")); // true + writeln(__traits(hasMember, S, "y")); // false + writeln(__traits(hasMember, int, "sizeof")); // true +} +---`), + ConstantCompletion("identifier", `$(P Takes one argument, a symbol. Returns the identifier + for that symbol as a string literal. +) +--- +import std.stdio; + +int var = 123; +pragma(msg, typeof(var)); // int +pragma(msg, typeof(__traits(identifier, var))); // string +writeln(var); // 123 +writeln(__traits(identifier, var)); // "var" +---`), + ConstantCompletion("isAbstractClass", `$(P If the arguments are all either types that are abstract classes, + or expressions that are typed as abstract classes, then $(D true) + is returned. + Otherwise, $(D false) is returned. + If there are no arguments, $(D false) is returned.) + +--- +import std.stdio; + +abstract class C { int foo(); } + +void main() +{ + C c; + writeln(__traits(isAbstractClass, C)); + writeln(__traits(isAbstractClass, c, C)); + writeln(__traits(isAbstractClass)); + writeln(__traits(isAbstractClass, int*)); +} +--- + +Prints: + +$(CONSOLE +true +true +false +false +)`), + ConstantCompletion("isAbstractFunction", `$(P Takes one argument. If that argument is an abstract function, + $(D true) is returned, otherwise $(D false). +) + +--- +import std.stdio; + +struct S +{ + void bar() { } +} + +class C +{ + void bar() { } +} + +class AC +{ + abstract void foo(); +} + +void main() +{ + writeln(__traits(isAbstractFunction, C.bar)); // false + writeln(__traits(isAbstractFunction, S.bar)); // false + writeln(__traits(isAbstractFunction, AC.foo)); // true +} +---`), + ConstantCompletion("isArithmetic", `$(P If the arguments are all either types that are arithmetic types, + or expressions that are typed as arithmetic types, then $(D true) + is returned. + Otherwise, $(D false) is returned. + If there are no arguments, $(D false) is returned.) + +--- +import std.stdio; + +void main() +{ + int i; + writeln(__traits(isArithmetic, int)); + writeln(__traits(isArithmetic, i, i+1, int)); + writeln(__traits(isArithmetic)); + writeln(__traits(isArithmetic, int*)); +} +--- + +Prints: + +$(CONSOLE +true +true +false +false +)`), + ConstantCompletion("isAssociativeArray", `Works like $(D isArithmetic), except it's for associative array types.`), + ConstantCompletion("isFinalClass", `Works like $(D isAbstractClass), except it's for final classes.`), + ConstantCompletion("isFinalFunction", `$(P Takes one argument. If that argument is a final function, + $(D true) is returned, otherwise $(D false). +) + +--- +import std.stdio; + +struct S +{ + void bar() { } +} + +class C +{ + void bar() { } + final void foo(); +} + +final class FC +{ + void foo(); +} + +void main() +{ + writeln(__traits(isFinalFunction, C.bar)); // false + writeln(__traits(isFinalFunction, S.bar)); // false + writeln(__traits(isFinalFunction, C.foo)); // true + writeln(__traits(isFinalFunction, FC.foo)); // true +} +---`), + ConstantCompletion("isFloating", `Works like $(D isArithmetic), except it's for floating +point types (including imaginary and complex types).`), + ConstantCompletion("isFuture", "Takes one argument. It returns `true` if the argument is a symbol +marked with the `@future` keyword, otherwise `false`. Currently, only +functions and variable declarations have support for the `@future` keyword."), + ConstantCompletion("isIntegral", `Works like $(D isArithmetic), except it's for integral +types (including character types).`), + ConstantCompletion("isLazy", isLazyDoc), + ConstantCompletion("isNested", `Takes one argument. +It returns $(D true) if the argument is a nested type which internally +stores a context pointer, otherwise it returns $(D false). +Nested types can be $(DDSUBLINK spec/class, nested, classes), +$(DDSUBLINK spec/struct, nested, structs), and +$(DDSUBLINK spec/function, variadicnested, functions).`), + ConstantCompletion("isOut", isLazyDoc), + ConstantCompletion("isOverrideFunction", `$(P Takes one argument. If that argument is a function marked with + $(D_KEYWORD override), $(D true) is returned, otherwise $(D false). +) + +--- +import std.stdio; + +class Base +{ + void foo() { } +} + +class Foo : Base +{ + override void foo() { } + void bar() { } +} + +void main() +{ + writeln(__traits(isOverrideFunction, Base.foo)); // false + writeln(__traits(isOverrideFunction, Foo.foo)); // true + writeln(__traits(isOverrideFunction, Foo.bar)); // false +} +---`), + ConstantCompletion("isPOD", `$(P Takes one argument, which must be a type. It returns +$(D true) if the type is a $(DDSUBLINK glossary, pod, POD) type, otherwise $(D false).)`), + ConstantCompletion("isRef", isLazyDoc), + ConstantCompletion("isSame", `$(P Takes two arguments and returns bool $(D true) if they + are the same symbol, $(D false) if not.) + +--- +import std.stdio; + +struct S { } + +int foo(); +int bar(); + +void main() +{ + writeln(__traits(isSame, foo, foo)); // true + writeln(__traits(isSame, foo, bar)); // false + writeln(__traits(isSame, foo, S)); // false + writeln(__traits(isSame, S, S)); // true + writeln(__traits(isSame, std, S)); // false + writeln(__traits(isSame, std, std)); // true +} +--- + +$(P If the two arguments are expressions made up of literals +or enums that evaluate to the same value, true is returned.)`), + ConstantCompletion("isScalar", `Works like $(D isArithmetic), except it's for scalar types.`), + ConstantCompletion("isStaticArray", `Works like $(D isArithmetic), except it's for static array types.`), + ConstantCompletion("isStaticFunction", `Takes one argument. If that argument is a static function, +meaning it has no context pointer, +$(D true) is returned, otherwise $(D false).`), + ConstantCompletion("isTemplate", `$(P Takes one argument. If that argument is a template then $(D true) is returned, + otherwise $(D false). +) + +--- +void foo(T)(){} +static assert(__traits(isTemplate,foo)); +static assert(!__traits(isTemplate,foo!int())); +static assert(!__traits(isTemplate,"string")); +---`), + ConstantCompletion("isUnsigned", `Works like $(D isArithmetic), except it's for unsigned types.`), + ConstantCompletion("isVirtualFunction", `The same as $(GLINK isVirtualMethod), except +that final functions that don't override anything return true.`), + ConstantCompletion("isVirtualMethod", `Takes one argument. If that argument is a virtual function, +$(D true) is returned, otherwise $(D false). +Final functions that don't override anything return false.`), + ConstantCompletion("parent", `Takes a single argument which must evaluate to a symbol. +The result is the symbol that is the parent of it.`) ]; /** * Scope conditions */ -immutable string[] scopes = [ - "exit", - "failure", - "success" +immutable ConstantCompletion[] scopes = [ + ConstantCompletion("exit", "executes statements when the scope exits normally or due to exception unwinding"), + ConstantCompletion("failure", "executes statements when the scope exits due to exception unwinding"), + ConstantCompletion("success", "executes statements when the scope exits normally") ]; /** * Compiler-defined values for version() conditions. */ -immutable string[] predefinedVersions = [ - "AArch64", - "AIX", - "all", - "Alpha", - "Alpha_HardFloat", - "Alpha_SoftFloat", - "Android", - "ARM", - "ARM_HardFloat", - "ARM_SoftFloat", - "ARM_SoftFP", - "ARM_Thumb", - "assert", - "BigEndian", - "BSD", - "CRuntime_Bionic", - "CRuntime_DigitalMars", - "CRuntime_Glibc", - "CRuntime_Microsoft", - "Cygwin", - "DigitalMars", - "DragonFlyBSD", - "D_Coverage", - "D_Ddoc", - "D_HardFloat", - "D_InlineAsm_X86", - "D_InlineAsm_X86_64", - "D_LP64", - "D_NoBoundsChecks", - "D_PIC", - "D_SIMD", - "D_SoftFloat", - "D_Version2", - "D_X32", - "ELFv1", - "ELFv2", - "Epiphany", - "FreeBSD", - "FreeStanding", - "GNU", - "Haiku", - "HPPA", - "HPPA64", - "Hurd", - "IA64", - "iOS", - "LDC", - "linux", - "LittleEndian", - "MinGW", - "MIPS32", - "MIPS64", - "MIPS_EABI", - "MIPS_HardFloat", - "MIPS_N32", - "MIPS_N64", - "MIPS_O32", - "MIPS_O64", - "MIPS_SoftFloat", - "NetBSD", - "none", - "NVPTX", - "NVPTX64", - "OpenBSD", - "OSX", - "PlayStation", - "PlayStation4", - "Posix", - "PPC", - "PPC64", - "PPC_HardFloat", - "PPC_SoftFloat", - "RISCV32", - "RISCV64", - "S390", - "S390X", - "SDC", - "SH", - "SH64", - "SkyOS", - "Solaris", - "SPARC", - "SPARC64", - "SPARC_HardFloat", - "SPARC_SoftFloat", - "SPARC_V8Plus", - "SystemZ", - "SysV3", - "SysV4", - "TVOS", - "unittest", - "WatchOS", - "Win32", - "Win64", - "Windows", - "X86", - "X86_64" +immutable ConstantCompletion[] predefinedVersions = [ + ConstantCompletion("AArch64"), + ConstantCompletion("AIX"), + ConstantCompletion("all"), + ConstantCompletion("Alpha"), + ConstantCompletion("Alpha_HardFloat"), + ConstantCompletion("Alpha_SoftFloat"), + ConstantCompletion("Android"), + ConstantCompletion("ARM"), + ConstantCompletion("ARM_HardFloat"), + ConstantCompletion("ARM_SoftFloat"), + ConstantCompletion("ARM_SoftFP"), + ConstantCompletion("ARM_Thumb"), + ConstantCompletion("assert"), + ConstantCompletion("BigEndian"), + ConstantCompletion("BSD"), + ConstantCompletion("CRuntime_Bionic"), + ConstantCompletion("CRuntime_DigitalMars"), + ConstantCompletion("CRuntime_Glibc"), + ConstantCompletion("CRuntime_Microsoft"), + ConstantCompletion("Cygwin"), + ConstantCompletion("DigitalMars"), + ConstantCompletion("DragonFlyBSD"), + ConstantCompletion("D_Coverage"), + ConstantCompletion("D_Ddoc"), + ConstantCompletion("D_HardFloat"), + ConstantCompletion("D_InlineAsm_X86"), + ConstantCompletion("D_InlineAsm_X86_64"), + ConstantCompletion("D_LP64"), + ConstantCompletion("D_NoBoundsChecks"), + ConstantCompletion("D_PIC"), + ConstantCompletion("D_SIMD"), + ConstantCompletion("D_SoftFloat"), + ConstantCompletion("D_Version2"), + ConstantCompletion("D_X32"), + ConstantCompletion("ELFv1"), + ConstantCompletion("ELFv2"), + ConstantCompletion("Epiphany"), + ConstantCompletion("FreeBSD"), + ConstantCompletion("FreeStanding"), + ConstantCompletion("GNU"), + ConstantCompletion("Haiku"), + ConstantCompletion("HPPA"), + ConstantCompletion("HPPA64"), + ConstantCompletion("Hurd"), + ConstantCompletion("IA64"), + ConstantCompletion("iOS"), + ConstantCompletion("LDC"), + ConstantCompletion("linux"), + ConstantCompletion("LittleEndian"), + ConstantCompletion("MinGW"), + ConstantCompletion("MIPS32"), + ConstantCompletion("MIPS64"), + ConstantCompletion("MIPS_EABI"), + ConstantCompletion("MIPS_HardFloat"), + ConstantCompletion("MIPS_N32"), + ConstantCompletion("MIPS_N64"), + ConstantCompletion("MIPS_O32"), + ConstantCompletion("MIPS_O64"), + ConstantCompletion("MIPS_SoftFloat"), + ConstantCompletion("NetBSD"), + ConstantCompletion("none"), + ConstantCompletion("NVPTX"), + ConstantCompletion("NVPTX64"), + ConstantCompletion("OpenBSD"), + ConstantCompletion("OSX"), + ConstantCompletion("PlayStation"), + ConstantCompletion("PlayStation4"), + ConstantCompletion("Posix"), + ConstantCompletion("PPC"), + ConstantCompletion("PPC64"), + ConstantCompletion("PPC_HardFloat"), + ConstantCompletion("PPC_SoftFloat"), + ConstantCompletion("RISCV32"), + ConstantCompletion("RISCV64"), + ConstantCompletion("S390"), + ConstantCompletion("S390X"), + ConstantCompletion("SDC"), + ConstantCompletion("SH"), + ConstantCompletion("SH64"), + ConstantCompletion("SkyOS"), + ConstantCompletion("Solaris"), + ConstantCompletion("SPARC"), + ConstantCompletion("SPARC64"), + ConstantCompletion("SPARC_HardFloat"), + ConstantCompletion("SPARC_SoftFloat"), + ConstantCompletion("SPARC_V8Plus"), + ConstantCompletion("SystemZ"), + ConstantCompletion("SysV3"), + ConstantCompletion("SysV4"), + ConstantCompletion("TVOS"), + ConstantCompletion("unittest"), + ConstantCompletion("WatchOS"), + ConstantCompletion("Win32"), + ConstantCompletion("Win64"), + ConstantCompletion("Windows"), + ConstantCompletion("X86"), + ConstantCompletion("X86_64") ]; diff --git a/src/dcd/common/messages.d b/src/dcd/common/messages.d index be47dab..5e7ba37 100644 --- a/src/dcd/common/messages.d +++ b/src/dcd/common/messages.d @@ -120,6 +120,44 @@ struct AutocompleteRequest */ struct AutocompleteResponse { + static struct Completion + { + /** + * The name of the symbol for a completion, for calltips just the function name. + */ + string identifier; + /** + * The kind of the item. Will be char.init for calltips. + */ + char kind; + /** + * Definition for a symbol for a completion including attributes or the arguments for calltips. + */ + string definition; + /** + * The path to the file that contains the symbol. + */ + string symbolFilePath; + /** + * The byte offset at which the symbol is located or symbol location for symbol searches. + */ + size_t symbolLocation; + /** + * Documentation associated with this symbol. + */ + string documentation; + + deprecated("Use identifier (or definition for calltips) instead") string compatibilityContent() const + { + if (kind == char.init) + return definition; + else + return identifier; + } + + alias compatibilityContent this; + } + /** * The autocompletion type. (Parameters or identifier) */ @@ -135,26 +173,10 @@ struct AutocompleteResponse */ size_t symbolLocation; - /** - * The documentation comment - */ - string[] docComments; - /** * The completions */ - string[] completions; - - /** - * The kinds of the items in the completions array. Will be empty if the - * completion type is a function argument list. - */ - char[] completionKinds; - - /** - * Symbol locations for symbol searches. - */ - size_t[] locations; + Completion[] completions; /** * Import paths that are registered by the server. @@ -166,6 +188,30 @@ struct AutocompleteResponse */ ulong symbolIdentifier; + deprecated("use completions[].documentation + escapeTabValue instead") string[] docComments() @property + { + string[] ret; + foreach (ref completion; completions) + ret ~= completion.documentation.escapeTabValue(true); + return ret; + } + + deprecated("use completions[].kind instead") char[] completionKinds() @property + { + char[] ret; + foreach (ref completion; completions) + ret ~= completion.kind; + return ret; + } + + deprecated("use completions[].symbolLocation instead") size_t[] locations() @property + { + size_t[] ret; + foreach (ref completion; completions) + ret ~= completion.symbolLocation; + return ret; + } + /** * Creates an empty acknowledgement response */ @@ -246,3 +292,51 @@ bool serverIsRunning(bool useTCP, string socketFile, ushort port) else return false; } + +/// Escapes \n, \t and \ in the string. If single is true \t won't be escaped. +string escapeTabValue(string s, bool single = false) +{ + import std.array : Appender; + + Appender!(char[]) app; + void putChar(char c) + { + switch (c) + { + case '\\': + app.put('\\'); + app.put('\\'); + break; + case '\n': + app.put('\\'); + app.put('n'); + break; + case '\t': + if (single) + goto default; + else + { + app.put('\\'); + app.put('t'); + break; + } + default: + app.put(c); + break; + } + } + + foreach (char c; s) + putChar(c); + + return app.data.idup; +} + +/// Joins string arguments with tabs and escapes them +string makeTabSeparated(string[] args...) +{ + import std.algorithm : map; + import std.array : join; + + return args.map!(a => a.escapeTabValue).join("\t"); +} diff --git a/src/dcd/server/autocomplete/complete.d b/src/dcd/server/autocomplete/complete.d index 444dc41..a1cade1 100644 --- a/src/dcd/server/autocomplete/complete.d +++ b/src/dcd/server/autocomplete/complete.d @@ -127,10 +127,7 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray, { mixin(STRING_LITERAL_CASES); foreach (symbol; arraySymbols) - { - response.completionKinds ~= symbol.kind; - response.completions ~= symbol.name.dup; - } + response.completions ~= makeSymbolCompletionInfo(symbol, symbol.kind); response.completionType = CompletionType.identifiers; break; mixin(TYPE_IDENT_CASES); @@ -169,7 +166,7 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens, const(Token)[] tokenArray, size_t cursorPosition, ref ModuleCache moduleCache) { AutocompleteResponse response; - immutable(string)[] completions; + immutable(ConstantCompletion)[] completions; switch (beforeTokens[$ - 2].type) { case tok!"__traits": @@ -190,8 +187,12 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens, response.completionType = CompletionType.identifiers; foreach (completion; completions) { - response.completions ~= completion; - response.completionKinds ~= CompletionKind.keyword; + response.completions ~= AutocompleteResponse.Completion( + completion.identifier, + CompletionKind.keyword, + null, null, 0, // definition, symbol path+location + completion.ddoc + ); } break; case tok!"characterLiteral": @@ -310,8 +311,7 @@ body && !sy.skipOver && sy.name != CONSTRUCTOR_SYMBOL_NAME && isPublicCompletionKind(sy.kind)) { - response.completionKinds ~= sy.kind; - response.completions ~= sy.name; + response.completions ~= makeSymbolCompletionInfo(sy, sy.kind); h.insert(sy.name); } } @@ -361,10 +361,7 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response, auto n = importPath.baseName(".d").baseName(".di"); if (isFile(importPath) && (importPath.endsWith(".d") || importPath.endsWith(".di")) && (partial is null || n.startsWith(partial))) - { - response.completions ~= n; - response.completionKinds ~= CompletionKind.moduleName; - } + response.completions ~= AutocompleteResponse.Completion(n, CompletionKind.moduleName, null, importPath, 0); } else { @@ -383,18 +380,18 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response, auto n = name.baseName(".d").baseName(".di"); if (isFile(name) && (name.endsWith(".d") || name.endsWith(".di")) && (partial is null || n.startsWith(partial))) - { - response.completions ~= n; - response.completionKinds ~= CompletionKind.moduleName; - } + response.completions ~= AutocompleteResponse.Completion(n, CompletionKind.moduleName, null, name, 0); else if (isDir(name)) { if (n[0] != '.' && (partial is null || n.startsWith(partial))) { - response.completions ~= n; - response.completionKinds ~= - exists(buildPath(name, "package.d")) || exists(buildPath(name, "package.di")) - ? CompletionKind.moduleName : CompletionKind.packageName; + string packageDPath = buildPath(name, "package.d"); + string packageDIPath = buildPath(name, "package.di"); + bool packageD = exists(packageDPath); + bool packageDI = exists(packageDIPath); + auto kind = packageD || packageDI ? CompletionKind.moduleName : CompletionKind.packageName; + string file = packageD ? packageDPath : packageDI ? packageDIPath : name; + response.completions ~= AutocompleteResponse.Completion(n, kind, null, file, 0); } } } @@ -424,11 +421,10 @@ void setCompletions(T)(ref AutocompleteResponse response, { if (sym.name !is null && sym.name.length > 0 && isPublicCompletionKind(sym.kind) && (p is null ? true : toUpper(sym.name.data).startsWith(toUpper(p))) - && !r.completions.canFind(sym.name) + && !r.completions.canFind!(a => a.identifier == sym.name) && sym.name[0] != '*') { - r.completionKinds ~= sym.kind; - r.completions ~= sym.name.dup; + r.completions ~= makeSymbolCompletionInfo(sym, sym.kind); } if (sym.kind == CompletionKind.importSymbol && !sym.skipOver && sym.type !is null) addSymToResponse(sym.type, r, p, circularGuard ~ (cast(size_t) s)); @@ -443,8 +439,7 @@ void setCompletions(T)(ref AutocompleteResponse response, foreach (s; currentSymbols.filter!(a => isPublicCompletionKind(a.kind) && toUpper(a.name.data).startsWith(toUpper(partial)))) { - response.completionKinds ~= s.kind; - response.completions ~= s.name.dup; + response.completions ~= makeSymbolCompletionInfo(s, s.kind); } response.completionType = CompletionType.identifiers; return; @@ -539,12 +534,16 @@ void setCompletions(T)(ref AutocompleteResponse response, foreach (symbol; symbols) { if (symbol.kind != CompletionKind.aliasName && symbol.callTip !is null) - response.completions ~= symbol.callTip; + { + auto completion = makeSymbolCompletionInfo(symbol, char.init); + // TODO: put return type + response.completions ~= completion; + } } } } -string generateStructConstructorCalltip(const DSymbol* symbol) +AutocompleteResponse.Completion generateStructConstructorCalltip(const DSymbol* symbol) in { assert(symbol.kind == CompletionKind.structName); @@ -570,5 +569,8 @@ body generatedStructConstructorCalltip ~= ", "; } generatedStructConstructorCalltip ~= ")"; - return generatedStructConstructorCalltip; + auto completion = makeSymbolCompletionInfo(symbol, char.init); + completion.identifier = "this"; + completion.definition = generatedStructConstructorCalltip; + return completion; } diff --git a/src/dcd/server/autocomplete/doc.d b/src/dcd/server/autocomplete/doc.d index e2e97ce..567e6c1 100644 --- a/src/dcd/server/autocomplete/doc.d +++ b/src/dcd/server/autocomplete/doc.d @@ -53,8 +53,6 @@ public AutocompleteResponse getDoc(const AutocompleteRequest request, warning("Could not find symbol"); else { - Appender!(char[]) app; - bool isDitto(string s) { import std.uni : icmp; @@ -64,35 +62,11 @@ public AutocompleteResponse getDoc(const AutocompleteRequest request, return s.icmp("ditto") == 0; } - void putDDocChar(char c) - { - switch (c) - { - case '\\': - app.put('\\'); - app.put('\\'); - break; - case '\n': - app.put('\\'); - app.put('n'); - break; - default: - app.put(c); - break; - } - } - - void putDDocString(string s) - { - foreach (char c; s) - putDDocChar(c); - } - foreach(ref symbol; stuff.symbols.filter!(a => !a.doc.empty && !isDitto(a.doc))) { - app.clear; - putDDocString(symbol.doc); - response.docComments ~= app.data.idup; + AutocompleteResponse.Completion c; + c.documentation = symbol.doc; + response.completions ~= c; } } return response; diff --git a/src/dcd/server/autocomplete/localuse.d b/src/dcd/server/autocomplete/localuse.d index 7990498..6b42f17 100644 --- a/src/dcd/server/autocomplete/localuse.d +++ b/src/dcd/server/autocomplete/localuse.d @@ -104,7 +104,9 @@ public AutocompleteResponse findLocalUse(AutocompleteRequest request, candidate.symbols[0].location == sourceSymbol.location && candidate.symbols[0].symbolFile == sourceSymbol.symbolFile) { - response.locations ~= t.index; + AutocompleteResponse.Completion c; + c.symbolLocation = t.index; + response.completions ~= c; } } } diff --git a/src/dcd/server/autocomplete/symbols.d b/src/dcd/server/autocomplete/symbols.d index ead6575..62bacf5 100644 --- a/src/dcd/server/autocomplete/symbols.d +++ b/src/dcd/server/autocomplete/symbols.d @@ -116,11 +116,7 @@ public AutocompleteResponse symbolSearch(const AutocompleteRequest request, AutocompleteResponse response; foreach (result; results.tree[]) - { - response.locations ~= result.symbol.location; - response.completionKinds ~= result.symbol.kind; - response.completions ~= result.symbol.symbolFile; - } + response.completions ~= makeSymbolCompletionInfo(result.symbol, result.symbol.kind); return response; } diff --git a/src/dcd/server/autocomplete/util.d b/src/dcd/server/autocomplete/util.d index 397b77d..0eacd85 100644 --- a/src/dcd/server/autocomplete/util.d +++ b/src/dcd/server/autocomplete/util.d @@ -738,3 +738,16 @@ unittest i = skipParenReverseBefore(t, i, tok!")", tok!"("); assert(i == 1); } + +AutocompleteResponse.Completion makeSymbolCompletionInfo(const DSymbol* symbol, char kind) +{ + string definition; + if ((kind == 'v' || kind == 'm') && symbol.type) + definition = symbol.type.name ~ ' ' ~ symbol.name; + else if (kind == 'e') + definition = symbol.name; + else + definition = symbol.callTip; + return AutocompleteResponse.Completion(symbol.name, kind, definition, + symbol.symbolFile, symbol.location, symbol.doc); +} \ No newline at end of file diff --git a/tests/tc059/expected1.txt b/tests/tc059/expected1.txt new file mode 100644 index 0000000..a000d6f --- /dev/null +++ b/tests/tc059/expected1.txt @@ -0,0 +1,4 @@ +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 diff --git a/tests/tc059/file.d b/tests/tc059/file.d new file mode 100644 index 0000000..d5acbb7 --- /dev/null +++ b/tests/tc059/file.d @@ -0,0 +1,20 @@ +void main(string[] args) +{ + libr +} + +/// My variable +int libraryVariable; + +/// Hello +/// World +int* libraryFunction(string s) pure { + return &libraryVariable; +} + +/** + * foobar + */ +Tuple!long libraryFunction(string s, string s2) pure @nogc { + return tuple(cast(long) (s.length * s2.length)); +} diff --git a/tests/tc059/run.sh b/tests/tc059/run.sh new file mode 100755 index 0000000..d05cb47 --- /dev/null +++ b/tests/tc059/run.sh @@ -0,0 +1,5 @@ +set -e +set -u + +../../bin/dcd-client $1 file.d --full -c32 > actual1.txt +diff actual1.txt expected1.txt diff --git a/tests/tc060/file.d b/tests/tc060/file.d new file mode 100644 index 0000000..e872bd8 --- /dev/null +++ b/tests/tc060/file.d @@ -0,0 +1 @@ +extern() diff --git a/tests/tc060/run.sh b/tests/tc060/run.sh new file mode 100755 index 0000000..3e60dbf --- /dev/null +++ b/tests/tc060/run.sh @@ -0,0 +1,16 @@ +set -e +set -u + +../../bin/dcd-client $1 --full file.d -c7 > actual1.txt + +minimumsize=100 # identifiers + the symbols without documentation + some margin +actualsize=$(wc -c < "actual1.txt") + +# we don't want to unittest the documentation, so we just check if there is something that makes it longer than it would be + +if [ $actualsize -ge $minimumsize ]; then + exit 0 +else + cat actual1.txt + exit 1 +fi diff --git a/tests/tc061/expected1.txt b/tests/tc061/expected1.txt new file mode 100644 index 0000000..a223a51 --- /dev/null +++ b/tests/tc061/expected1.txt @@ -0,0 +1,3 @@ +calltips +libraryFunction Tuple!long libraryFunction(string s, string s2) stdin 166 foobar +libraryFunction int* libraryFunction(string s) stdin 75 Hello\nWorld diff --git a/tests/tc061/file.d b/tests/tc061/file.d new file mode 100644 index 0000000..124a257 --- /dev/null +++ b/tests/tc061/file.d @@ -0,0 +1,17 @@ +void main(string[] args) +{ + libraryFunction(); +} + +/// Hello +/// World +int* libraryFunction(string s) pure { + return &libraryVariable; +} + +/** + * foobar + */ +Tuple!long libraryFunction(string s, string s2) pure @nogc { + return tuple(cast(long) (s.length * s2.length)); +} diff --git a/tests/tc061/run.sh b/tests/tc061/run.sh new file mode 100755 index 0000000..d821d46 --- /dev/null +++ b/tests/tc061/run.sh @@ -0,0 +1,5 @@ +set -e +set -u + +../../bin/dcd-client $1 file.d --full -c44 > actual1.txt +diff actual1.txt expected1.txt