diff --git a/CHANGES b/CHANGES index 011eb8a4..62159c3c 100644 --- a/CHANGES +++ b/CHANGES @@ -1280,4 +1280,13 @@ Version history * mago debugger expression evaluator: - fixed wrong values displayed eventually when switching stack frames * installer: - - full installer now bundled with DMD 2.091.1 and LDC 1.23.0 + - full installer now bundled with DMD 2.093.1 and LDC 1.23.0 + +unreleased version 1.1.0 + * dmdserver: + - updated to DMD 2.095.0-beta.1 + - experimental: show parameter storage ref/out/lazy at call site + * editor: + - bugzilla 21239: navigation bar now sorted, symbols starting with "__" moved to the bottom + * installer: + - full installer now bundled with DMD 2.095.0-beta.1 and LDC 1.24.0 diff --git a/VERSION b/VERSION index 8c887726..c9ec3bc4 100644 --- a/VERSION +++ b/VERSION @@ -1,5 +1,5 @@ #define VERSION_MAJOR 1 -#define VERSION_MINOR 0 -#define VERSION_REVISION 1 -#define VERSION_BETA -#define VERSION_BUILD 0 +#define VERSION_MINOR 1 +#define VERSION_REVISION 0 +#define VERSION_BETA -beta +#define VERSION_BUILD 1 diff --git a/c2d/idl2d.d b/c2d/idl2d.d index 157c0f69..e99b8343 100644 --- a/c2d/idl2d.d +++ b/c2d/idl2d.d @@ -1628,6 +1628,8 @@ version(none) version(vsi) if(currentModule == "msdbg") replaceTokenSequence(tokens, "const DWORD S_UNKNOWN = 0x3;", "denum DWORD S_UNKNOWN = 0x3;", true); + if(currentModule == "msdbg167") + replaceTokenSequence(tokens, "__uuidof($data)", "$data.iid", true); if(currentModule == "activdbg") replaceTokenSequence(tokens, "const THREAD_STATE", "denum THREAD_STATE", true); diff --git a/c2d/vsi2d.visualdproj b/c2d/vsi2d.visualdproj index e713ed51..c499fec5 100644 --- a/c2d/vsi2d.visualdproj +++ b/c2d/vsi2d.visualdproj @@ -40,7 +40,6 @@ 0 0 0 - 0 0 0 0 @@ -164,7 +163,6 @@ 0 0 0 - 0 0 0 0 @@ -288,7 +286,6 @@ 0 0 0 - 0 0 0 0 @@ -412,7 +409,6 @@ 0 0 0 - 0 0 0 0 @@ -536,7 +532,6 @@ 0 0 0 - 0 0 0 0 @@ -660,7 +655,6 @@ 0 0 0 - 0 0 0 0 @@ -784,7 +778,6 @@ 0 0 0 - 0 0 0 0 @@ -908,7 +901,6 @@ 0 0 0 - 0 0 0 0 @@ -1032,7 +1024,6 @@ 0 0 0 - 0 0 0 0 @@ -1156,7 +1147,6 @@ 0 0 0 - 0 0 0 0 @@ -1280,7 +1270,6 @@ 0 0 0 - 0 0 0 0 @@ -1404,7 +1393,6 @@ 0 0 0 - 0 0 0 0 @@ -1528,7 +1516,6 @@ 0 0 0 - 0 0 0 0 @@ -1652,7 +1639,6 @@ 0 0 0 - 0 0 0 0 @@ -1776,7 +1762,6 @@ 0 0 0 - 0 0 0 0 @@ -1900,7 +1885,6 @@ 0 0 0 - 0 0 0 0 @@ -1986,6 +1970,7 @@ + diff --git a/doc/StartPage.dd b/doc/StartPage.dd index 62a6781a..f8c9d0d1 100644 --- a/doc/StartPage.dd +++ b/doc/StartPage.dd @@ -84,6 +84,13 @@ $(H2 News) $(P $(LINK2 VersionHistory.html, Full version history and complete details...) ) +2020-08-23 Version 1.0.1 + $(UL + $(LI dmd based semantic engine: updated to DMD 2.093.1) + $(LI editor: now extra mouse buttons to navigate backward/forward supported) + $(LI installer: full installer now bundled with DMD 2.093.1 and LDC 1.23.0) + ) + 2020-07-04 Version 1.0.0 $(UL $(LI DMD based semantic engine: enabled by default, updated to front end version 2.092) @@ -92,36 +99,13 @@ $(UL $(LI goto definition by clicking identifier in tool tip) ) -2020-23-03 Version 0.52.0 +2020-03-23 Version 0.52.0 $(UL $(LI mago: added support for tuples and simpler display of globals) $(LI DMD based semantic engine: updated to 2.091, improvements to code-completions) $(LI projects: updated support for all language options) ) -2020-18-01 Version 0.51.0 -$(UL - $(LI experimental: semantic server built from DMD frontend) - $(LI VS2017+: Ctrl+Click goto definition added) - $(LI VS2017+: syntax highlighted tool tips) -) - -2019-09-03 Version 0.50.1 -$(UL - $(LI Fixes some integration issues with VS 2019 16.2) - $(LI mago: improve function call in watch window) - $(LI better version highlighting for files not in project) -) - -2019-06-23 Version 0.50.0 -$(UL - $(LI Now checks for updates of Visual D, DMD and LDC and assists download and install) - $(LI Setup including DMD and LDC now available) - $(LI mago debugger: better support for dynamic type of classes, show exception messages, - conditional breakpoints) - $(LI highlight references to symbol at caret) -) - $(LINK2 VersionHistory.html, more...) $(H2 Download) diff --git a/doc/VersionHistory.dd b/doc/VersionHistory.dd index 9192af80..735bd904 100644 --- a/doc/VersionHistory.dd +++ b/doc/VersionHistory.dd @@ -1,5 +1,22 @@ Ddoc +$(H2 2020-08-23 Version 1.0.1) + $(UL + $(LI dmd based semantic engine: + $(UL + $(LI updated to DMD 2.093.1) + $(LI fixed showing documentation for template functions) + )) + $(LI editor: now extra mouse buttons to navigate backward/forward supported) + $(LI project management: + $(UL + $(LI bugzilla 21024: VS2017/2019 new project: fixed unrelated files listed in New Project Dialog) + $(LI bugzilla 21028: project templates not visible when VS started as standard/restricted user) + )) + $(LI mago debugger expression evaluator: fixed wrong values displayed eventually when switching stack frames) + $(LI installer: full installer now bundled with DMD 2.093.1 and LDC 1.23.0) + ) + $(H2 2020-07-04 Version 1.0.0) $(UL $(LI project management: diff --git a/doc/visuald.ddoc b/doc/visuald.ddoc index 91c98948..436fc200 100644 --- a/doc/visuald.ddoc +++ b/doc/visuald.ddoc @@ -1,6 +1,6 @@ -VERSION = 1.0.0 -DMD_VERSION = 2.092.1 -LDC_VERSION = 1.22.0 +VERSION = 1.0.1 +DMD_VERSION = 2.093.1 +LDC_VERSION = 1.23.0 ROOT_DIR = https://www.dlang.org/ ROOT = https://www.dlang.org BODYCLASS = visuald diff --git a/msbuild/dbuild/dbuild.csproj b/msbuild/dbuild/dbuild.csproj index 0319cd26..6cf744c1 100644 --- a/msbuild/dbuild/dbuild.csproj +++ b/msbuild/dbuild/dbuild.csproj @@ -106,6 +106,7 @@ false AnyCPU 12.0 + false pdbonly @@ -161,7 +162,6 @@ - @@ -225,7 +225,6 @@ c:\Windows\assembly\GAC\Microsoft.VisualStudio.VCProjectEngine\12.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.VCProjectEngine.dll - @@ -291,7 +290,6 @@ c:\Windows\assembly\GAC\Microsoft.VisualStudio.VCProjectEngine\14.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.VCProjectEngine.dll - Local @@ -333,7 +331,6 @@ c:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.VCProjectEngine.dll - @@ -358,7 +355,6 @@ assemblies\v16\Microsoft.VisualStudio.VCProjectEngine.dll - assemblies\v16\Microsoft.Build.CPPTasks.Common.dll @@ -369,7 +365,6 @@ assemblies\v16_1\Microsoft.Build.CPPTasks.Common.dll - Designer diff --git a/nsis/visuald.nsi b/nsis/visuald.nsi index 4f1edbcd..d34c4d5b 100644 --- a/nsis/visuald.nsi +++ b/nsis/visuald.nsi @@ -29,12 +29,12 @@ ; define DMD source path to include dmd installation ; !define DMD -!define DMD_VERSION "2.093.1" +!define DMD_VERSION "2.095.0-beta.1" !define DMD_SRC c:\d\dmd-${DMD_VERSION} ; define LDC to include ldc installation ; !define LDC -!define LDC_VERSION "1.23.0" +!define LDC_VERSION "1.24.0" !define LDC_SRC c:\d\ldc2-${LDC_VERSION}-windows-multilib ; define VS2019 to include VS2019 support diff --git a/sdk/vsi.visualdproj b/sdk/vsi.visualdproj index e1e77d4f..005e68af 100644 --- a/sdk/vsi.visualdproj +++ b/sdk/vsi.visualdproj @@ -2186,6 +2186,8 @@ + + @@ -2254,6 +2256,7 @@ + diff --git a/vdc/abothe/comserver/DParserCOMServer.csproj b/vdc/abothe/comserver/DParserCOMServer.csproj index 24e47158..d39272c3 100644 --- a/vdc/abothe/comserver/DParserCOMServer.csproj +++ b/vdc/abothe/comserver/DParserCOMServer.csproj @@ -25,6 +25,7 @@ 4 x86 false + false pdbonly diff --git a/vdc/abothe/comserver/IVDServer.cs b/vdc/abothe/comserver/IVDServer.cs index 2ad8bb48..34cba41e 100644 --- a/vdc/abothe/comserver/IVDServer.cs +++ b/vdc/abothe/comserver/IVDServer.cs @@ -38,6 +38,7 @@ public interface IVDServer void GetReferencesResult (out string stringList); void ConfigureCommentTasks (string tasks); void GetCommentTasks (string filename, out string tasks); + void GetParameterStorageLocs(string filename, out object locs); // array of triplets of DWORD } } diff --git a/vdc/abothe/comserver/VDServer.cs b/vdc/abothe/comserver/VDServer.cs index 9ead348d..65d73d92 100644 --- a/vdc/abothe/comserver/VDServer.cs +++ b/vdc/abothe/comserver/VDServer.cs @@ -341,7 +341,12 @@ public void GetReferencesResult(out string stringList) break; } } - + + public void GetParameterStorageLocs(string filename, out object locs) + { + throw new NotImplementedException(); + } + #if false [EditorBrowsable(EditorBrowsableState.Never)] [ComRegisterFunction()] diff --git a/vdc/dmdserver/dmd b/vdc/dmdserver/dmd index 43c6d688..6afbd059 160000 --- a/vdc/dmdserver/dmd +++ b/vdc/dmdserver/dmd @@ -1 +1 @@ -Subproject commit 43c6d688268b659317d8ea9a238642ee4067126b +Subproject commit 6afbd059a93e5c85f7577f880d604b0a2853aaf4 diff --git a/vdc/dmdserver/dmdinit.d b/vdc/dmdserver/dmdinit.d index eb75fec3..00aaec55 100644 --- a/vdc/dmdserver/dmdinit.d +++ b/vdc/dmdserver/dmdinit.d @@ -67,15 +67,15 @@ enum string[2][] dmdStatics = //["_D3dmd10expression10IntegerExp__T7literalVii1ZQnRZ11theConstantCQCkQCjQCa", "IntegerExp"], //["_D3dmd10expression10IntegerExp__T7literalViN1ZQnRZ11theConstantCQCkQCjQCa", "IntegerExp"], ["_D3dmd10identifier10Identifier17generateIdWithLocFNbAyaKxSQCe7globals3LocZ8countersHSQDfQDeQCvQCmFNbQBwKxQBwZ3Keyk", "countersType"], - ["_D3dmd10identifier10Identifier10generateIdFNbAxaZ1ik", "size_t"], + ["_D3dmd10identifier10Identifier9newSuffixFNbZ1ik", "size_t"], ]; string cmangled(string s) { version (Win64) { - if (s == "_D3dmd10identifier10Identifier10generateIdFNbAxaZ1ik") - return "_D3dmd10identifier10Identifier10generateIdFNbAxaZ1im"; // size_t + if (s == "_D3dmd10identifier10Identifier9newSuffixFNbZ1ik") + return "_D3dmd10identifier10Identifier9newSuffixFNbZ1im"; // size_t if (s == "_D3dmd6dmacro10MacroTable6expandMFKSQBi4root9outbuffer9OutBufferkKkAxaZ4nesti") return "_D3dmd6dmacro10MacroTable6expandMFKSQBi4root9outbuffer9OutBuffermKmAxaZ4nesti"; } @@ -151,7 +151,7 @@ void dmdInit() version(CRuntime_Microsoft) initFPU(); - global.params.isWindows = true; + global.params.targetOS = TargetOS.Windows; global._init(); //Token._init(); Id.initialize(); @@ -238,7 +238,7 @@ void dmdSetupParams(const ref Options opts) global = global.init; global._init(); - global.params.isWindows = true; + global.params.targetOS = TargetOS.Windows; global.params.errorLimit = 0; global.params.color = false; global.params.link = true; @@ -403,7 +403,7 @@ void dmdReinit() void addDefaultVersionIdentifiers(const ref Param params) { VersionCondition.addPredefinedGlobalIdent("DigitalMars"); - if (params.isWindows) + if (params.targetOS == TargetOS.Windows) { VersionCondition.addPredefinedGlobalIdent("Windows"); if (global.params.mscoff) @@ -417,7 +417,7 @@ void addDefaultVersionIdentifiers(const ref Param params) VersionCondition.addPredefinedGlobalIdent("CppRuntime_DigitalMars"); } } - else if (params.isLinux) + else if (params.targetOS == TargetOS.linux) { VersionCondition.addPredefinedGlobalIdent("Posix"); VersionCondition.addPredefinedGlobalIdent("linux"); @@ -425,7 +425,7 @@ void addDefaultVersionIdentifiers(const ref Param params) VersionCondition.addPredefinedGlobalIdent("CRuntime_Glibc"); VersionCondition.addPredefinedGlobalIdent("CppRuntime_Gcc"); } - else if (params.isOSX) + else if (params.targetOS == TargetOS.OSX) { VersionCondition.addPredefinedGlobalIdent("Posix"); VersionCondition.addPredefinedGlobalIdent("OSX"); @@ -433,28 +433,28 @@ void addDefaultVersionIdentifiers(const ref Param params) // For legacy compatibility VersionCondition.addPredefinedGlobalIdent("darwin"); } - else if (params.isFreeBSD) + else if (params.targetOS == TargetOS.FreeBSD) { VersionCondition.addPredefinedGlobalIdent("Posix"); VersionCondition.addPredefinedGlobalIdent("FreeBSD"); VersionCondition.addPredefinedGlobalIdent("ELFv1"); VersionCondition.addPredefinedGlobalIdent("CppRuntime_Clang"); } - else if (params.isOpenBSD) + else if (params.targetOS == TargetOS.OpenBSD) { VersionCondition.addPredefinedGlobalIdent("Posix"); VersionCondition.addPredefinedGlobalIdent("OpenBSD"); VersionCondition.addPredefinedGlobalIdent("ELFv1"); VersionCondition.addPredefinedGlobalIdent("CppRuntime_Gcc"); } - else if (params.isDragonFlyBSD) + else if (params.targetOS == TargetOS.DragonFlyBSD) { VersionCondition.addPredefinedGlobalIdent("Posix"); VersionCondition.addPredefinedGlobalIdent("DragonFlyBSD"); VersionCondition.addPredefinedGlobalIdent("ELFv1"); VersionCondition.addPredefinedGlobalIdent("CppRuntime_Gcc"); } - else if (params.isSolaris) + else if (params.targetOS == TargetOS.Solaris) { VersionCondition.addPredefinedGlobalIdent("Posix"); VersionCondition.addPredefinedGlobalIdent("Solaris"); @@ -482,7 +482,7 @@ void addDefaultVersionIdentifiers(const ref Param params) { VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); VersionCondition.addPredefinedGlobalIdent("X86_64"); - if (params.isWindows) + if (params.targetOS == TargetOS.Windows) { VersionCondition.addPredefinedGlobalIdent("Win64"); } @@ -492,7 +492,7 @@ void addDefaultVersionIdentifiers(const ref Param params) VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); VersionCondition.addPredefinedGlobalIdent("X86"); - if (params.isWindows) + if (params.targetOS == TargetOS.Windows) { VersionCondition.addPredefinedGlobalIdent("Win32"); } diff --git a/vdc/dmdserver/dmdserver.d b/vdc/dmdserver/dmdserver.d index afbe5209..4a6f129a 100644 --- a/vdc/dmdserver/dmdserver.d +++ b/vdc/dmdserver/dmdserver.d @@ -823,6 +823,43 @@ class DMDServer : ComObject, IVDServer return S_OK; } + override HRESULT GetParameterStorageLocs(in BSTR filename, VARIANT* locs) + { + // array of pairs of DWORD + string fname = makeFilenameCanonical(to_string(filename), null); + + ModuleData* md; + synchronized(gErrorSync) + { + md = findModule(fname, false); + if (!md || !md.analyzedModule) + return S_FALSE; + } + + auto stcLoc = findParameterStorageClass(md.analyzedModule); + + SAFEARRAY *sa = SafeArrayCreateVector(VT_INT, 0, 3 * cast(ULONG) stcLoc.length); + if(!sa) + return E_OUTOFMEMORY; + + for(LONG index = 0; index < stcLoc.length; index++) + { + LONG idx = index * 3; + LONG value = stcLoc[index].type; + SafeArrayPutElement(sa, &idx, &value); + idx++; + value = stcLoc[index].line; + SafeArrayPutElement(sa, &idx, &value); + idx++; + value = stcLoc[index].col - 1; + SafeArrayPutElement(sa, &idx, &value); + } + + locs.vt = VT_ARRAY | VT_INT; + locs.parray = sa; + return S_OK; + } + /////////////////////////////////////////////////////////////// // create our own task pool to be able to destroy it (it keeps a the // arguments to the last task, so they are never collected) @@ -1120,6 +1157,15 @@ string findIdentifierTypesResultToString(FindIdentifierTypesResult res) return s; } +//////////////////////////////////////////////////////////////// +void dummy_instantiation_of_cas() +{ + // workaround missing symbol starting with dmd 2.093 + import core.atomic; + LONG Target, old, Value; + cas( cast(shared(LONG)*)Target, old, Value ); +} + //////////////////////////////////////////////////////////////// extern(C) int _CrtDumpMemoryLeaks(); diff --git a/vdc/dmdserver/dmdserver.visualdproj b/vdc/dmdserver/dmdserver.visualdproj index dc8916c2..79c98da4 100644 --- a/vdc/dmdserver/dmdserver.visualdproj +++ b/vdc/dmdserver/dmdserver.visualdproj @@ -562,7 +562,7 @@ 0 c:\l\d\dmd2\windows\bin\dmd.exe dmd\src ..\.. - dmd dmd\res + dmd dmd\src\dmd\res ..\..\bin\$(ConfigurationName) $(OutDir)\$(PlatformName)\$(ProjectName) @@ -1749,7 +1749,9 @@ - + + + diff --git a/vdc/dmdserver/semanalysis.d b/vdc/dmdserver/semanalysis.d index 6c54f6cd..2ac3cc38 100644 --- a/vdc/dmdserver/semanalysis.d +++ b/vdc/dmdserver/semanalysis.d @@ -1774,6 +1774,35 @@ void do_unittests() // TODO: checkTip(m, 3, 18, "(enum) `tok.TOK`"); checkTip(m, 5, 19, "(constant global) `const(uint) shell.VSITEMID_NIL`"); + + /////////////////////////////////////////////////////////// + // parameter storage class + lastContext = null; + filename = "source.d"; + source = q{ + void funIn(in int p) {} + void funRef(ref int p) {} + void funOut(out int p) {} + void funLazy(lazy int p) {} // Line 5 + + void foo(int[] arr) + { + int x; + funIn(x); // Line 10 + funRef(x); + funOut(x); + funLazy(x); + funRef(arr[3]); + } + }; + m = checkErrors(source, ""); + + auto stcpos = findParameterStorageClass(m); + assert_equal(stcpos.length, 4); + assert_equal(stcpos[0], ParameterStorageClassPos(0, 11, 11)); + assert_equal(stcpos[1], ParameterStorageClassPos(1, 12, 11)); + assert_equal(stcpos[2], ParameterStorageClassPos(2, 13, 12)); + assert_equal(stcpos[3], ParameterStorageClassPos(0, 14, 11)); } unittest @@ -1867,7 +1896,8 @@ unittest opts.msvcrt = true; opts.warnings = true; opts.importDirs = guessImportPaths() ~ srcdir ~ dirName(dirName(thisdir)); - opts.stringImportDirs ~= srcdir ~ "/dmd/res"; + opts.stringImportDirs ~= srcdir ~ "/dmd/res"; // for default_ddoc_theme.ddoc + opts.stringImportDirs ~= srcdir ~ "/.."; // for VERSION opts.versionIds ~= "MARS"; //opts.versionIds ~= "NoBackend"; diff --git a/vdc/dmdserver/semvisitor.d b/vdc/dmdserver/semvisitor.d index 34d918e0..7db20e48 100644 --- a/vdc/dmdserver/semvisitor.d +++ b/vdc/dmdserver/semvisitor.d @@ -341,7 +341,7 @@ extern(C++) class ASTVisitor : StoppableVisitor // function declaration only if (auto tf = decl.type ? decl.type.isTypeFunction() : null) { - if (tf.parameterList && tf.parameterList.parameters) + if (tf.parameterList.parameters) foreach(i, p; *tf.parameterList.parameters) if (!stop) { @@ -1135,7 +1135,7 @@ TipData tipForDeclaration(Declaration decl) if (auto td = fntype && decl.parent ? decl.parent.isTemplateDeclaration() : null) functionToBufferFull(fntype, &buf, decl.getIdent(), &hgs, td); else if (fntype) - functionToBufferWithIdent(fntype, &buf, decl.toPrettyChars(true), &hgs); + functionToBufferWithIdent(fntype, &buf, decl.toPrettyChars(true), &hgs, func.isStatic); else buf.writestring(decl.toPrettyChars(true)); auto res = buf.extractSlice(); // take ownership @@ -1875,6 +1875,88 @@ FindIdentifierTypesResult findIdentifierTypes(Module mod) return itv.idTypes; } +//////////////////////////////////////////////////////////////////////////////// +struct ParameterStorageClassPos +{ + int type; // ref, out, lazy + int line; + int col; +} + +ParameterStorageClassPos[] findParameterStorageClass(Module mod) +{ + extern(C++) class ParameterStorageClassVisitor : ASTVisitor + { + ParameterStorageClassPos[] stcPos; + const(char)* filename; + + alias visit = ASTVisitor.visit; + + final void addParamPos(int type, int line, int col) + { + auto psp = ParameterStorageClassPos(type, line, col); + stcPos ~= psp; + } + final void addParamPos(int type, Expression expr) + { + if (expr.loc.filename is filename) + addParamPos(type, expr.loc.linnum, expr.loc.charnum); + } + final void addLazyParamPos(int type, Expression expr) + { + if (!expr.loc.filename) + // drill into generated function for lazy parameter + if (expr.op == TOK.function_) + if (auto fd = (cast(FuncExp)expr).fd) + if (fd.fbody) + if (auto cs = fd.fbody.isCompoundStatement()) + if (cs.statements && cs.statements.length) + if (auto rs = (*cs.statements)[0].isReturnStatement()) + expr = rs.exp; + + if (expr.loc.filename is filename) + addParamPos(type, expr.loc.linnum, expr.loc.charnum); + } + + override void visit(CallExp expr) + { + if (expr.arguments && expr.arguments.length) + { + if (auto tf = expr.f ? expr.f.type.isTypeFunction() : null) + { + if (auto params = tf.parameterList.parameters) + { + size_t cnt = min(expr.arguments.length, params.length); + for (size_t p = 0; p < cnt; p++) + { + auto stc = (*params)[p].storageClass; + if (stc & STC.ref_) + { + if (stc & (STC.in_ | STC.const_)) + continue; + if((*params)[p].type && !(*params)[p].type.isMutable()) + continue; + addParamPos(0, (*expr.arguments)[p]); + } + else if (stc & STC.out_) + addParamPos(1, (*expr.arguments)[p]); + else if (stc & STC.lazy_) + addLazyParamPos(2, (*expr.arguments)[p]); + } + } + } + } + super.visit(expr); + } + } + + scope psv = new ParameterStorageClassVisitor; + psv.filename = mod.srcfile.toChars(); + mod.accept(psv); + + return psv.stcPos; +} + //////////////////////////////////////////////////////////////////////////////// struct Reference { diff --git a/vdc/ivdserver.d b/vdc/ivdserver.d index 54b4d85e..13ee55ab 100644 --- a/vdc/ivdserver.d +++ b/vdc/ivdserver.d @@ -149,7 +149,7 @@ public: // this method is called once after GetParseErrors returned successfully HRESULT GetBinaryIsInLocations(in BSTR filename, VARIANT* locs); - // return the locations where "in" and "is" are used as binary operators + // return the document outline, i.e. a simple description of the AST // // filename: file name // @@ -233,6 +233,15 @@ public: // // return S_FALSE as long as the parsing is still running HRESULT GetCommentTasks(in BSTR filename, BSTR* tasks); + + // return the locations of call arguments with parameter storage class ref, out or lazy + // + // filename: file name + // locs: an array of triplets of DWORDs type,line,index that gives the text + // location of the argument expression. type is 0/1/2 for ref/out/lazy + // + // this method is called once after GetParseErrors returned successfully + HRESULT GetParameterStorageLocs(in BSTR filename, VARIANT* locs); } /////////////////////////////////////////////////////////////////////// @@ -293,3 +302,17 @@ enum TypeReferenceKind : uint DebugIdentifier, VersionIdentifier, } + +enum ParameterStorageClass +{ + Ref, + Out, + Lazy +} + +struct ParameterStorageLoc +{ + int type; + int line; + int col; +} diff --git a/vdc/ivdserver.idl b/vdc/ivdserver.idl index 5bd5ea44..c45be43f 100644 --- a/vdc/ivdserver.idl +++ b/vdc/ivdserver.idl @@ -39,5 +39,6 @@ interface IVDServer : IUnknown HRESULT GetReferencesResult([out,retval] BSTR* stringList); HRESULT ConfigureCommentTasks([in] BSTR tasks); HRESULT GetCommentTasks([in] BSTR filename, [out,retval] BSTR* tasks); + HRESULT GetParameterStorageLocs([in] BSTR filename, [out, retval] VARIANT* locs); // array of triplets of DWORD }; diff --git a/vdc/vdserver.d b/vdc/vdserver.d index 32756850..07e4c506 100644 --- a/vdc/vdserver.d +++ b/vdc/vdserver.d @@ -644,6 +644,11 @@ class VDServer : ComObject, IVDServer return E_NOTIMPL; } + override HRESULT GetParameterStorageLocs(in BSTR filename, VARIANT* locs) + { + return E_NOTIMPL; + } + /////////////////////////////////////////////////////////////// // create our own task pool to be able to destroy it (it keeps a the // arguments to the last task, so they are never collected) diff --git a/vdextensions/ParamStorageAdornmentTagger.cs b/vdextensions/ParamStorageAdornmentTagger.cs new file mode 100644 index 00000000..1dadb0ff --- /dev/null +++ b/vdextensions/ParamStorageAdornmentTagger.cs @@ -0,0 +1,623 @@ +// adapted from +// https://github.com/microsoft/VSSDK-Extensibility-Samples/blob/master/Intra-text_Adornment/C%23/Support/IntraTextAdornmentTagger.cs +//*************************************************************************** + +using System; +using System.ComponentModel.Composition; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Media; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Globalization; +using System.Runtime.InteropServices; // DllImport + +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace vdext15 +{ + /// + /// Helper class for interspersing adornments into text. + /// + /// + /// To avoid an issue around intra-text adornment support and its interaction with text buffer changes, + /// this tagger reacts to text and paramStorage tag changes with a delay. It waits to send out its own TagsChanged + /// event until the WPF Dispatcher is running again and it takes care to report adornments + /// that are consistent with the latest sent TagsChanged event by storing that particular snapshot + /// and using it to query for the data tags. + /// + internal abstract class IntraTextAdornmentTagger + : ITagger + where TAdornment : UIElement + { + protected readonly IWpfTextView view; + protected Dictionary adornmentCache = new Dictionary(); + protected ITextSnapshot snapshot { get; private set; } + private readonly List invalidatedSpans = new List(); + + protected IntraTextAdornmentTagger(IWpfTextView view) + { + this.view = view; + snapshot = view.TextBuffer.CurrentSnapshot; + + this.view.LayoutChanged += HandleLayoutChanged; + this.view.TextBuffer.Changed += HandleBufferChanged; + } + + /// The span of text that this adornment will elide. + /// Adornment corresponding to given data. May be null. + protected abstract TAdornment CreateAdornment(TData data, SnapshotSpan span); + + /// True if the adornment was updated and should be kept. False to have the adornment removed from the view. + protected abstract bool UpdateAdornment(TAdornment adornment, TData data); + + /// Spans to provide adornment data for. These spans do not necessarily correspond to text lines. + /// + /// If adornments need to be updated, call or . + /// This will, indirectly, cause to be called. + /// + /// + /// A sequence of: + /// * adornment data for each adornment to be displayed + /// * the span of text that should be elided for that adornment (zero length spans are acceptable) + /// * and affinity of the adornment (this should be null if and only if the elided span has a length greater than zero) + /// + protected abstract IEnumerable> GetAdornmentData(NormalizedSnapshotSpanCollection spans); + + private void HandleBufferChanged(object sender, TextContentChangedEventArgs args) + { + var editedSpans = args.Changes.Select(change => new SnapshotSpan(args.After, change.NewSpan)).ToList(); + InvalidateSpans(editedSpans); + } + + /// + /// Causes intra-text adornments to be updated asynchronously. + /// + protected void InvalidateSpans(IList spans) + { + lock (invalidatedSpans) + { + bool wasEmpty = invalidatedSpans.Count == 0; + invalidatedSpans.AddRange(spans); + + if (wasEmpty && this.invalidatedSpans.Count > 0) + view.VisualElement.Dispatcher.BeginInvoke(new Action(AsyncUpdate)); + } + } + + private void AsyncUpdate() + { + // Store the snapshot that we're now current with and send an event + // for the text that has changed. + if (snapshot != view.TextBuffer.CurrentSnapshot) + { + snapshot = view.TextBuffer.CurrentSnapshot; + + Dictionary translatedAdornmentCache = new Dictionary(); + + foreach (var keyValuePair in adornmentCache) + translatedAdornmentCache.Add(keyValuePair.Key.TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive), keyValuePair.Value); + + adornmentCache = translatedAdornmentCache; + } + + List translatedSpans; + lock (invalidatedSpans) + { + translatedSpans = invalidatedSpans.Select(s => s.TranslateTo(snapshot, SpanTrackingMode.EdgeInclusive)).ToList(); + invalidatedSpans.Clear(); + } + + if (translatedSpans.Count == 0) + return; + + var start = translatedSpans.Select(span => span.Start).Min(); + var end = translatedSpans.Select(span => span.End).Max(); + + RaiseTagsChanged(new SnapshotSpan(start, end)); + } + + /// + /// Causes intra-text adornments to be updated synchronously. + /// + protected void RaiseTagsChanged(SnapshotSpan span) + { + var handler = TagsChanged; + if (handler != null) + handler(this, new SnapshotSpanEventArgs(span)); + } + + private void HandleLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) + { + SnapshotSpan visibleSpan = view.TextViewLines.FormattedSpan; + + // Filter out the adornments that are no longer visible. + List toRemove = new List( + from keyValuePair + in adornmentCache + where !keyValuePair.Key.TranslateTo(visibleSpan.Snapshot, SpanTrackingMode.EdgeExclusive).IntersectsWith(visibleSpan) + select keyValuePair.Key); + + foreach (var span in toRemove) + adornmentCache.Remove(span); + } + + + // Produces tags on the snapshot that the tag consumer asked for. + public virtual IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) + { + if (spans == null || spans.Count == 0) + yield break; + + // Translate the request to the snapshot that this tagger is current with. + + ITextSnapshot requestedSnapshot = spans[0].Snapshot; + + var translatedSpans = new NormalizedSnapshotSpanCollection(spans.Select(span => span.TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive))); + + // Grab the adornments. + foreach (var tagSpan in GetAdornmentTagsOnSnapshot(translatedSpans)) + { + // Translate each adornment to the snapshot that the tagger was asked about. + SnapshotSpan span = tagSpan.Span.TranslateTo(requestedSnapshot, SpanTrackingMode.EdgeExclusive); + + IntraTextAdornmentTag tag = new IntraTextAdornmentTag(tagSpan.Tag.Adornment, tagSpan.Tag.RemovalCallback, tagSpan.Tag.Affinity); + yield return new TagSpan(span, tag); + } + } + + // Produces tags on the snapshot that this tagger is current with. + private IEnumerable> GetAdornmentTagsOnSnapshot(NormalizedSnapshotSpanCollection spans) + { + if (spans.Count == 0) + yield break; + + ITextSnapshot snapshot = spans[0].Snapshot; + + System.Diagnostics.Debug.Assert(snapshot == this.snapshot); + + // Since WPF UI objects have state (like mouse hover or animation) and are relatively expensive to create and lay out, + // this code tries to reuse controls as much as possible. + // The controls are stored in this.adornmentCache between the calls. + + // Mark which adornments fall inside the requested spans with Keep=false + // so that they can be removed from the cache if they no longer correspond to data tags. + HashSet toRemove = new HashSet(); + foreach (var ar in adornmentCache) + if (spans.IntersectsWith(new NormalizedSnapshotSpanCollection(ar.Key))) + toRemove.Add(ar.Key); + + foreach (var spanDataPair in GetAdornmentData(spans).Distinct(new Comparer())) + { + // Look up the corresponding adornment or create one if it's new. + TAdornment adornment; + SnapshotSpan snapshotSpan = spanDataPair.Item1; + PositionAffinity? affinity = spanDataPair.Item2; + TData adornmentData = spanDataPair.Item3; + if (adornmentCache.TryGetValue(snapshotSpan, out adornment)) + { + if (UpdateAdornment(adornment, adornmentData)) + toRemove.Remove(snapshotSpan); + } + else + { + adornment = CreateAdornment(adornmentData, snapshotSpan); + + if (adornment == null) + continue; + + // Get the adornment to measure itself. Its DesiredSize property is used to determine + // how much space to leave between text for this adornment. + // Note: If the size of the adornment changes, the line will be reformatted to accommodate it. + // Note: Some adornments may change size when added to the view's visual tree due to inherited + // dependency properties that affect layout. Such options can include SnapsToDevicePixels, + // UseLayoutRounding, TextRenderingMode, TextHintingMode, and TextFormattingMode. Making sure + // that these properties on the adornment match the view's values before calling Measure here + // can help avoid the size change and the resulting unnecessary re-format. + adornment.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + adornmentCache.Add(snapshotSpan, adornment); + } + var tag = new IntraTextAdornmentTag(adornment, null, null, 4, null, null, affinity); + yield return new TagSpan(snapshotSpan, tag); + } + + foreach (var snapshotSpan in toRemove) + adornmentCache.Remove(snapshotSpan); + } + + public event EventHandler TagsChanged; + + private class Comparer : IEqualityComparer> + { + public bool Equals(Tuple x, Tuple y) + { + if (x == null && y == null) + return true; + if (x == null || y == null) + return false; + return x.Item1.Equals(y.Item1); + } + + public int GetHashCode(Tuple obj) + { + return obj.Item1.GetHashCode(); + } + } + + } + + /// Provides paramStorage adornments in place of paramStorage constants. + internal sealed class ParamStorageAdornmentTagger + : IntraTextAdornmentTagger + { + [DllImport("visuald.dll")] + public static extern bool GetParameterStorageLocs(string fname, + [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_INT)] out int[] locs, + out bool pending, out int changeCount); + + public static bool GetParameterStorageLocations(string fname, out int[] data, out bool pending, out int changeCount) + { + try + { + // grab triplets of (type, line, col) + if (!GetParameterStorageLocs(fname, out data, out pending, out changeCount)) + return false; + return true; + } + catch + { + data = new int[0]; + pending = true; + changeCount = -1; + return false; + } + } + + public SolidColorBrush storageColor = new SolidColorBrush(Colors.Green); + IEditorFormatMap formatMap; + + private static SolidColorBrush toBrush(object obj) + { + if (obj == null) + return new SolidColorBrush(Colors.Red); + return (SolidColorBrush)obj; + } + + public void UpdateColors() + { + try + { + ResourceDictionary rd = formatMap.GetProperties("ParamStorageClassificationFormatVisualD"); + storageColor = toBrush(rd[EditorFormatDefinition.ForegroundBrushId]); + + foreach (var tag in adornmentCache) + tag.Value.Foreground = storageColor; + } + catch { } + } + + internal static ITagger GetTagger(IWpfTextView view, IEditorFormatMap formatMap, + Lazy> paramStorageTagger) + { + return view.Properties.GetOrCreateSingletonProperty( + () => new ParamStorageAdornmentTagger(view, formatMap, paramStorageTagger.Value)); + } + + private ITagAggregator paramStorageTagger; + + private ParamStorageAdornmentTagger(IWpfTextView view, IEditorFormatMap formatMap, + ITagAggregator paramStorageTagger) + : base(view) + { + this.paramStorageTagger = paramStorageTagger; + this.formatMap = formatMap; + this.formatMap.FormatMappingChanged += (sender, args) => UpdateColors(); + UpdateColors(); + } + + public void Dispose() + { + paramStorageTagger.Dispose(); + + view.Properties.RemoveProperty(typeof(ParamStorageAdornmentTagger)); + } + + // To produce adornments that don't obscure the text, the adornment tags + // should have zero length spans. Overriding this method allows control + // over the tag spans. + protected override IEnumerable> + GetAdornmentData(NormalizedSnapshotSpanCollection spans) + { + if (spans.Count == 0) + yield break; + + ITextSnapshot snapshot = spans[0].Snapshot; + + var paramStorageTags = paramStorageTagger.GetTags(spans); + + foreach (IMappingTagSpan dataTagSpan in paramStorageTags) + { + NormalizedSnapshotSpanCollection paramStorageTagSpans = dataTagSpan.Span.GetSpans(snapshot); + + // Ignore data tags that are split by projection. + // This is theoretically possible but unlikely in current scenarios. + if (paramStorageTagSpans.Count != 1) + continue; + + SnapshotSpan adornmentSpan = new SnapshotSpan(paramStorageTagSpans[0].End, 0); + + yield return Tuple.Create(adornmentSpan, (PositionAffinity?)PositionAffinity.Predecessor, dataTagSpan.Tag); + } + } + + protected override ParamStorageAdornment CreateAdornment(ParamStorageTag dataTag, SnapshotSpan span) + { + return new ParamStorageAdornment(dataTag, storageColor); + } + + protected override bool UpdateAdornment(ParamStorageAdornment adornment, ParamStorageTag dataTag) + { + adornment.Update(dataTag); + return true; + } + } + + /// + /// Determines which spans of text likely refer to paramStorage values. + /// + /// + /// + /// This is a data-only component. The tagging system is a good fit for presenting data-about-text. + /// The takes paramStorage tags produced by this tagger and creates corresponding UI for this data. + /// + /// + internal class ParamStorageTagger : ITagger + { + private string _fileName; + int[] _stcLocs; + ITextSnapshot _pendingSnapshot; + ITextSnapshot _currentSnapshot; + int _changeCount; + + public ParamStorageTagger(ITextBuffer buffer) + { + IVsTextBuffer vsbuffer; + buffer.Properties.TryGetProperty(typeof(IVsTextBuffer), out vsbuffer); + _fileName = ""; + if (vsbuffer != null) + { + IPersistFileFormat fileFormat = vsbuffer as IPersistFileFormat; + if (fileFormat != null) + { + UInt32 format; + fileFormat.GetCurFile(out _fileName, out format); + } + } + + buffer.Changed += (sender, args) => HandleBufferChanged(args); + _pendingSnapshot = _currentSnapshot = buffer.CurrentSnapshot; + } + + #region ITagger implementation + + public void UpdateStcSpans() + { + int[] stcLocs; + bool pending; + int changeCount; + ParamStorageAdornmentTagger.GetParameterStorageLocations(_fileName, out stcLocs, out pending, out changeCount); + if (pending && _stcLocs != null) + return; + if (_pendingSnapshot == null && _changeCount == changeCount) + return; + + if (stcLocs != null) + { + int j = 0; + for (int i = 0; i + 2 < stcLocs.Length; i += 3) + { + if (stcLocs[i] >= 0 && stcLocs[i] <= 2) + { + if (stcLocs[i + 1] >= _currentSnapshot.LineCount) + continue; + var line = _currentSnapshot.GetLineFromLineNumber(stcLocs[i + 1] - 1); + stcLocs[j] = stcLocs[i]; + stcLocs[j + 1] = line.Start.Position + stcLocs[i + 2]; + j += 2; + } + } + Array.Resize(ref stcLocs, j); + } + _stcLocs = stcLocs; + _changeCount = changeCount; + if (!pending) + _pendingSnapshot = null; + } + + public virtual IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) + { + UpdateStcSpans(); + + // Note that the spans argument can contain spans that are sub-spans of lines or intersect multiple lines. + if (_stcLocs != null) + for (int i = 0; i + 1 < _stcLocs.Length; i += 2) + { + // need to compare manually as info is on different snapshots + int pos = _stcLocs[i + 1]; + bool intersects = false; + foreach (var span in spans) + if (span.Start.Position <= pos && pos <= span.End.Position) + intersects = true; + + if (intersects) + { + var point = new SnapshotPoint(spans.First().Snapshot, pos); + SnapshotSpan span = new SnapshotSpan(point, 1); + yield return new TagSpan(span, new ParamStorageTag(_stcLocs[i])); + } + } + } + public event EventHandler TagsChanged; + + #endregion + + /// + /// Handle buffer changes. The default implementation expands changes to full lines and sends out + /// a event for these lines. + /// + /// The buffer change arguments. + protected virtual void HandleBufferChanged(TextContentChangedEventArgs args) + { + if (args.Changes.Count == 0) + return; + + var temp = TagsChanged; + if (temp == null) + return; + + // adapt stcSpans, so any unmodified tags get moved with changes + if (_stcLocs != null) + foreach (var change in args.Changes) + for (int i = 0; i + 1 < _stcLocs.Length; i += 2) + if (_stcLocs[i + 1] >= change.OldPosition) + _stcLocs[i + 1] += change.Delta; + + // Combine all changes into a single span so that + // the ITagger<>.TagsChanged event can be raised just once for a compound edit + // with many parts. + _pendingSnapshot = _currentSnapshot = args.After; + + int start = args.Changes[0].NewPosition; + int end = args.Changes[args.Changes.Count - 1].NewEnd; + + SnapshotSpan totalAffectedSpan = new SnapshotSpan( + _currentSnapshot.GetLineFromPosition(start).Start, + _currentSnapshot.GetLineFromPosition(end).End); + + temp(this, new SnapshotSpanEventArgs(totalAffectedSpan)); + } + } + + /// + /// Data tag indicating that the tagged text represents a paramStorage. + /// + /// + /// Note that this tag has nothing directly to do with adornments or other UI. + /// This sample's adornments will be produced based on the data provided in these tags. + /// This separation provides the potential for other extensions to consume paramStorage tags + /// and provide alternative UI or other derived functionality over this data. + /// + internal class ParamStorageTag : ITag + { + internal ParamStorageTag(int type) + { + Type = type; + } + + internal readonly int Type; + } + + internal sealed class ParamStorageAdornment : TextBlock //Button + { + internal ParamStorageAdornment(ParamStorageTag paramStorageTag, Brush color) + { + //Text = "ref"; + FontSize = FontSize * 0.75; + + //Background = Brushes.AntiqueWhite; + Foreground = color; + TextAlignment = TextAlignment.Center; + + Update(paramStorageTag); + } + + internal void Update(ParamStorageTag paramStorageTag) + { + var txt = paramStorageTag.Type == 0 ? "ref " : paramStorageTag.Type == 1 ? "out " : "lazy "; + Inlines.Clear(); + Inlines.Add(new Italic(new Run(txt))); + } + } + +//#if STC_ADORNMENT + [Export(typeof(ITaggerProvider))] + [ContentType("d")] + [TagType(typeof(ParamStorageTag))] + internal sealed class ParamStorageTaggerProvider : ITaggerProvider + { + public ITagger CreateTagger(ITextBuffer buffer) where T : ITag + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + + return buffer.Properties.GetOrCreateSingletonProperty(() => new ParamStorageTagger(buffer)) as ITagger; + } + } + + [Export(typeof(IViewTaggerProvider))] + [ContentType("d")] + [ContentType("projection")] + [TagType(typeof(IntraTextAdornmentTag))] + internal sealed class ParamStorageAdornmentTaggerProvider : IViewTaggerProvider + { + [Import] + internal IBufferTagAggregatorFactoryService BufferTagAggregatorFactoryService = null; + + [Import] + internal IClassificationTypeRegistryService ClassificationRegistry = null; + + [Import] + internal IEditorFormatMapService FormatMapService = null; + + [Export(typeof(ClassificationTypeDefinition))] + [Name("ParamStorageClassificationVisualD")] + internal static ClassificationTypeDefinition paramStorageClassificationType = null; + + private static IClassificationType s_paramStorageClassification; + + public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) where T : ITag + { + if (s_paramStorageClassification == null) + s_paramStorageClassification = ClassificationRegistry.GetClassificationType("ParamStorageClassificationVisualD"); + + if (textView == null) + throw new ArgumentNullException("textView"); + + if (buffer == null) + throw new ArgumentNullException("buffer"); + + if (buffer != textView.TextBuffer) + return null; + + return ParamStorageAdornmentTagger.GetTagger( + (IWpfTextView)textView, FormatMapService.GetEditorFormatMap(textView), + new Lazy>( + () => BufferTagAggregatorFactoryService.CreateTagAggregator(textView.TextBuffer))) + as ITagger; + } + } + + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = "ParamStorageClassificationVisualD")] + [Name("ParamStorageClassificationFormatVisualD")] + [UserVisible(true)] + [Order(After = Priority.High)] + internal sealed class ParamStorageFormatDefinition : ClassificationFormatDefinition + { + public ParamStorageFormatDefinition() + { + this.DisplayName = "Visual D Parameter Storage"; + this.ForegroundColor = System.Windows.Media.Colors.LightSkyBlue; + } + } +//#endif +} diff --git a/vdextensions/gotodef.cs b/vdextensions/gotodef.cs index 7680f119..12b44b38 100644 --- a/vdextensions/gotodef.cs +++ b/vdextensions/gotodef.cs @@ -166,16 +166,14 @@ public override void PreviewKeyUp(KeyEventArgs args) [Order(Before = "WordSelection")] internal sealed class GoToDefMouseHandlerProvider : IMouseProcessorProvider { -#pragma warning disable 649 // Field is never assigned to, and will always have its default value null - [Import] - private IClassifierAggregatorService _aggregatorFactory; + private IClassifierAggregatorService _aggregatorFactory = null; [Import] - private ITextStructureNavigatorSelectorService _navigatorService; + private ITextStructureNavigatorSelectorService _navigatorService = null; [Import] - private SVsServiceProvider _globalServiceProvider; + private SVsServiceProvider _globalServiceProvider = null; public IMouseProcessor GetAssociatedProcessor(IWpfTextView view) { @@ -224,11 +222,11 @@ private IVsUIShell GetVsUIShell() #endregion } -/// -/// Handle ctrl+click on valid elements to send GoToDefinition to the shell. Also handle mouse moves -/// (when control is pressed) to highlight references for which GoToDefinition will (likely) be valid. -/// -internal sealed class GoToDefMouseHandler : MouseProcessorBase + /// + /// Handle ctrl+click on valid elements to send GoToDefinition to the shell. Also handle mouse moves + /// (when control is pressed) to highlight references for which GoToDefinition will (likely) be valid. + /// + internal sealed class GoToDefMouseHandler : MouseProcessorBase { private IWpfTextView _view; private CtrlKeyState _state; @@ -350,11 +348,11 @@ public override void PostprocessMouseUp(MouseButtonEventArgs e) } - #endregion + #endregion - #region Private helpers + #region Private helpers - private Point RelativeToView(Point position) + private Point RelativeToView(Point position) { return new Point(position.X + _view.ViewportLeft, position.Y + _view.ViewportTop); } @@ -467,7 +465,7 @@ private bool DispatchGoToDef() #endregion } - #region Classification type/format exports +#region Classification type/format exports [Export(typeof(EditorFormatDefinition))] [ClassificationType(ClassificationTypeNames = "UnderlineClassificationVisualD")] @@ -484,24 +482,24 @@ public UnderlineFormatDefinition() } } - #endregion +#endregion - #region Provider definition +#region Provider definition - [Export(typeof(IViewTaggerProvider))] + [Export(typeof(IViewTaggerProvider))] [ContentType("text")] [TagType(typeof(ClassificationTag))] - internal class UnderlineClassifierProvider : IViewTaggerProvider + internal class UnderlineClassifierProvider : IViewTaggerProvider { [Import] - internal IClassificationTypeRegistryService ClassificationRegistry; + internal IClassificationTypeRegistryService ClassificationRegistry = null; //[Import] //private SVsServiceProvider _serviceProvider; [Export(typeof(ClassificationTypeDefinition))] [Name("UnderlineClassificationVisualD")] - internal static ClassificationTypeDefinition underlineClassificationType; + internal static ClassificationTypeDefinition underlineClassificationType = null; private static IClassificationType s_underlineClassification; public static UnderlineClassifier GetClassifierForView(ITextView view) @@ -533,9 +531,9 @@ public static UnderlineClassifier GetClassifierForView(ITextView view) } } - #endregion +#endregion - internal class UnderlineClassifier : ITagger + internal class UnderlineClassifier : ITagger { private IClassificationType _classificationType; private ITextView _textView; @@ -548,7 +546,7 @@ internal UnderlineClassifier(ITextView textView, IClassificationType classificat _underlineSpan = null; } - #region Private helpers +#region Private helpers private void SendEvent(SnapshotSpan span) { @@ -557,9 +555,9 @@ private void SendEvent(SnapshotSpan span) temp(this, new SnapshotSpanEventArgs(span)); } - #endregion +#endregion - #region UnderlineClassification public members +#region UnderlineClassification public members public SnapshotSpan? CurrentUnderlineSpan { get { return _underlineSpan; } } @@ -590,7 +588,7 @@ public void SetUnderlineSpan(SnapshotSpan? span) } } - #endregion +#endregion public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) { diff --git a/vdextensions/vdext15.csproj b/vdextensions/vdext15.csproj index 6996da58..77e4480b 100644 --- a/vdextensions/vdext15.csproj +++ b/vdextensions/vdext15.csproj @@ -25,6 +25,7 @@ 4 False false + false full @@ -140,6 +141,7 @@ + diff --git a/vdextensions/vdextensions.csproj b/vdextensions/vdextensions.csproj index 97672944..71ace7ca 100644 --- a/vdextensions/vdextensions.csproj +++ b/vdextensions/vdextensions.csproj @@ -22,6 +22,7 @@ prompt 4 False + false full diff --git a/vdwizard/VisualDWizard.csproj b/vdwizard/VisualDWizard.csproj index 2b57ff3d..482d55d1 100644 --- a/vdwizard/VisualDWizard.csproj +++ b/vdwizard/VisualDWizard.csproj @@ -42,6 +42,7 @@ false False False + false pdbonly diff --git a/visuald/dlangsvc.d b/visuald/dlangsvc.d index bb8cea80..18da6519 100644 --- a/visuald/dlangsvc.d +++ b/visuald/dlangsvc.d @@ -895,6 +895,16 @@ class LanguageService : DisposingComObject, return true; } + ParameterStorageLoc[] GetParameterStorageLocs(string filename, out bool pending, out int changeCount) + { + Source src = GetSource(filename); + if(!src) + return null; + pending = src.mHasPendingUpdateModule || src.mModificationCountSemantic != src.mModificationCount; + changeCount = src.mModificationCount; + return src.mParameterStcLocs; + } + // QuickInfo callback from C# /////////////////////////////////// private uint mLastTipIdleTaskScheduled; @@ -1489,6 +1499,19 @@ class NavigationBarClient : DisposingComObject, IVsDropdownBarClient, IVsCoTaskM size_t[] mColumn3; int mCurrentLine; + bool columnLess(const ref size_t col1, const ref size_t col2) + { + auto d1 = mNodes[col1].desc; + auto d2 = mNodes[col2].desc; + bool i1 = d1.startsWith("__"); // move internal symbols to the end of the list + bool i2 = d2.startsWith("__"); + if (i1 && !i2) + return false; + if (!i1 && i2) + return true; + return icmp(d1, d2) < 0; + } + void UpdateFromSource(string outline) { string[] lines = outline.splitLines(); @@ -1535,6 +1558,8 @@ class NavigationBarClient : DisposingComObject, IVsDropdownBarClient, IVsCoTaskM for (size_t n = 0; n < mNodes.length; n++) if (mNodes[n].depth <= 1) mGlobal[cnt++] = n; + + sort!((a, b) => columnLess(a, b))(mGlobal); } int getNodeIndex(int iCombo, int index) @@ -1548,10 +1573,10 @@ class NavigationBarClient : DisposingComObject, IVsDropdownBarClient, IVsCoTaskM return -1; } - int findGlobalIndex(int line) + int findLineIndex(size_t[] column, int line) { - for (size_t g = 0; g < mGlobal.length; g++) - if (mNodes[mGlobal[g]].containsLine(line)) + for (size_t g = 0; g < column.length; g++) + if (mNodes[column[g]].containsLine(line)) return g; return -1; } @@ -1569,7 +1594,7 @@ class NavigationBarClient : DisposingComObject, IVsDropdownBarClient, IVsCoTaskM mCurrentLine = line; mColumn2 = null; mColumn3 = null; - int sel1 = findGlobalIndex(line); + int sel1 = findLineIndex(mGlobal, line); int sel2 = -1; int sel3 = -1; if (sel1 >= 0) @@ -1585,24 +1610,21 @@ class NavigationBarClient : DisposingComObject, IVsDropdownBarClient, IVsCoTaskM mColumn2 ~= h; if (mNodes[h].containsLine(line)) { - sel2 = mColumn2.length - 1; int hdepth = mNodes[h].depth; for (int i = h + 1; i < mNodes.length; i++) { if (mNodes[i].depth <= hdepth) break; if (mNodes[i].depth == hdepth + 1) - { mColumn3 ~= i; - if (mNodes[i].containsLine(line)) - { - sel3 = mColumn3.length - 1; - } - } } } } } + sort!((a, b) => columnLess(a, b))(mColumn2); + sort!((a, b) => columnLess(a, b))(mColumn3); + sel2 = findLineIndex(mColumn2, line); + sel3 = findLineIndex(mColumn3, line); } if (mDropdownBar) { @@ -1956,6 +1978,7 @@ class Source : DisposingComObject, IVsUserDataEvents, IVsTextLinesEvents, IdentifierType[][wstring] mIdentifierTypes; vdc.util.TextPos[] mBinaryIsIn; NewHiddenRegion[] mOutlineRegions; + ParameterStorageLoc[] mParameterStcLocs; int mOutliningState; int mModificationCountOutlining; @@ -4641,13 +4664,14 @@ else } extern(D) void OnUpdateModule(uint request, string filename, string parseErrors, vdc.util.TextPos[] binaryIsIn, - string tasks, string outline) + string tasks, string outline, ParameterStorageLoc[] parameterStcLocs) { mHasPendingUpdateModule = false; if (!Package.GetGlobalOptions().showParseErrors) parseErrors = null; updateParseErrors(parseErrors); mBinaryIsIn = binaryIsIn; + mParameterStcLocs = parameterStcLocs; if(IVsTextColorState colorState = qi_cast!IVsTextColorState(mBuffer)) { scope(exit) release(colorState); diff --git a/visuald/dllmain.d b/visuald/dllmain.d index 1b395357..2ba320b7 100644 --- a/visuald/dllmain.d +++ b/visuald/dllmain.d @@ -26,6 +26,7 @@ import threadaux = core.sys.windows.threadaux; import std.conv; import std.array; +import sdk.win32.oleauto; // enable precise scanning of the DATA/TLS sections (dmd 2.075+) and GC (dmd 2.085+) pragma(msg, "DMD VERSION = ", __VERSION__); @@ -219,3 +220,38 @@ BOOL GetTooltipResult(uint request, BSTR* btip, BSTR* bfmt, BSTR* blinks) *blinks = allocwBSTR(links); return rc; } + +extern(Windows) +BOOL GetParameterStorageLocs(const(char)* fname, SAFEARRAY** psa, BOOL* pPending, INT* pChangeCount) +{ + if (!Package.s_instance) + return false; // not yet loaded as a package + + int changeCount; + bool pending; + string filename = to!string(fname); + auto stcLocs = Package.GetLanguageService().GetParameterStorageLocs(filename, pending, changeCount); + + SAFEARRAY *sa = SafeArrayCreateVector(VT_INT, 0, 3 * cast(ULONG) stcLocs.length); + if(!sa) + return E_OUTOFMEMORY; + + LONG idx = 0; + for(LONG index = 0; index < stcLocs.length; index++) + { + LONG value = stcLocs[index].type; + SafeArrayPutElement(sa, &idx, &value); + idx++; + value = stcLocs[index].line; + SafeArrayPutElement(sa, &idx, &value); + idx++; + value = stcLocs[index].col - 1; + SafeArrayPutElement(sa, &idx, &value); + idx++; + } + + *pPending = pending; + *psa = sa; + *pChangeCount = changeCount; + return true; +} diff --git a/visuald/dpackage.d b/visuald/dpackage.d index ea5f26ab..224b3409 100644 --- a/visuald/dpackage.d +++ b/visuald/dpackage.d @@ -1613,6 +1613,7 @@ class GlobalOptions bool useDParser; bool mixinAnalysis; bool UFCSExpansions; + bool showParamStorage; byte sortExpMode; // 0: alphabetically, 1: by type, 2: by declaration and scope bool exactExpMatch; ubyte checkUpdatesVisualD; // CheckFrequency: 0: never, 1: daily, 2: weekly, 3: daily prereleases @@ -1638,6 +1639,7 @@ class GlobalOptions bool lastColorizeCoverage; bool lastColorizeVersions; bool lastShowParseErrors; + bool lastShowParamStorage; bool lastUseDParser; string lastInstallDirs; @@ -1991,6 +1993,7 @@ class GlobalOptions deleteFiles = cast(byte) getIntOpt("deleteFiles", 0); //parseSource = getBoolOpt("parseSource", true); showParseErrors = getBoolOpt("showParseErrors", true); + showParamStorage = getBoolOpt("showParamStorage", true); expandFromSemantics = getBoolOpt("expandFromSemantics", true); //expandFromBuffer = getBoolOpt("expandFromBuffer", true); expandFromJSON = getBoolOpt("expandFromJSON", true); @@ -2176,6 +2179,7 @@ class GlobalOptions lastColorizeVersions = ColorizeVersions; lastUseDParser = useDParser; lastShowParseErrors = showParseErrors; + lastShowParamStorage = showParamStorage; updateDefaultColors(); @@ -2268,6 +2272,7 @@ class GlobalOptions keyToolOpts.Set("deleteFiles", deleteFiles); //keyToolOpts.Set("parseSource", parseSource); keyToolOpts.Set("showParseErrors", showParseErrors); + keyToolOpts.Set("showParamStorage", showParamStorage); keyToolOpts.Set("expandFromSemantics", expandFromSemantics); //keyToolOpts.Set("expandFromBuffer", expandFromBuffer); keyToolOpts.Set("expandFromJSON", expandFromJSON); @@ -2348,7 +2353,13 @@ class GlobalOptions lastShowParseErrors = showParseErrors; updateColorizer = true; } - if(updateColorizer) + if (lastShowParamStorage != showParamStorage) + { + lastShowParamStorage = showParamStorage; + if(auto svc = Package.s_instance.mLangsvc) + svc.RestartParser(); + } + else if(updateColorizer) if(auto svc = Package.s_instance.mLangsvc) svc.UpdateColorizer(true); diff --git a/visuald/propertypage.d b/visuald/propertypage.d index 88ba4934..7d5cdba5 100644 --- a/visuald/propertypage.d +++ b/visuald/propertypage.d @@ -2983,8 +2983,11 @@ class IntellisensePropertyPage : GlobalPropertyPage AddTitleLine("Semantic analysis"); AddControl("", mShowParseErrors = new CheckBox(mCanvas, "Show parsing errors (squiggles and markers)")); version(DParserOption) AddControl("", mUseDmdParser = new CheckBox(mCanvas, "use DMD parsing engine for semantic analysis")); - AddControl("", mMixinAnalysis = new CheckBox(mCanvas, "Enable mixin analysis")); - AddControl("", mUFCSExpansions = new CheckBox(mCanvas, "Enable UFCS expansions")); + auto lineY = mLineY; + AddControl("", mShowParamStorage = new CheckBox(mCanvas, "Show parameter storage class at call site (experimental)")); + mLineY = lineY; // overlay with next + AddTwoControls(mMixinAnalysis = new CheckBox(mCanvas, "Enable mixin analysis"), + mUFCSExpansions = new CheckBox(mCanvas, "Enable UFCS expansions")); //AddControl("", mSemanticGotoDef = new CheckBox(mCanvas, "Use semantic analysis for \"Goto Definition\" (before trying JSON info)")); AddTitleLine("Expansions"); AddControl("", mExpandSemantics = new CheckBox(mCanvas, "Expansions from semantic analysis")); @@ -3010,7 +3013,10 @@ class IntellisensePropertyPage : GlobalPropertyPage else bool useDParser = true; mMixinAnalysis.setEnabled(useDParser); mUFCSExpansions.setEnabled(useDParser); + mMixinAnalysis.setVisible(useDParser); + mUFCSExpansions.setVisible(useDParser); mSortExpMode.setEnabled(useDParser); + mShowParamStorage.setVisible(!useDParser); mShowValueInTooltip.setEnabled(mShowTypeInTooltip.isChecked()); } @@ -3027,6 +3033,7 @@ class IntellisensePropertyPage : GlobalPropertyPage version(DParserOption) mUseDmdParser.setChecked(!opts.useDParser); mMixinAnalysis.setChecked(opts.mixinAnalysis); mUFCSExpansions.setChecked(opts.UFCSExpansions); + mShowParamStorage.setChecked(opts.showParamStorage); mSortExpMode.setSelection(opts.sortExpMode); mExactExpMatch.setChecked(opts.exactExpMatch); @@ -3048,6 +3055,7 @@ class IntellisensePropertyPage : GlobalPropertyPage version(DParserOption) changes += changeOption(!mUseDmdParser.isChecked(), opts.useDParser, refopts.useDParser); changes += changeOption(mMixinAnalysis.isChecked(), opts.mixinAnalysis, refopts.mixinAnalysis); changes += changeOption(mUFCSExpansions.isChecked(), opts.UFCSExpansions, refopts.UFCSExpansions); + changes += changeOption(mShowParamStorage.isChecked(), opts.showParamStorage, refopts.showParamStorage); changes += changeOption(cast(ubyte) mSortExpMode.getSelection(), opts.sortExpMode, refopts.sortExpMode); changes += changeOption(mExactExpMatch.isChecked(), opts.exactExpMatch, refopts.exactExpMatch); return changes; @@ -3062,6 +3070,7 @@ class IntellisensePropertyPage : GlobalPropertyPage CheckBox mShowValueInTooltip; //CheckBox mSemanticGotoDef; version(DParserOption) CheckBox mUseDmdParser; + CheckBox mShowParamStorage; CheckBox mUFCSExpansions; ComboBox mSortExpMode; CheckBox mExactExpMatch; diff --git a/visuald/vdserverclient.d b/visuald/vdserverclient.d index f3108f84..93b895f3 100644 --- a/visuald/vdserverclient.d +++ b/visuald/vdserverclient.d @@ -8,6 +8,7 @@ module visuald.vdserverclient; +import visuald.dpackage; import visuald.pkgutil; import visuald.logutil; @@ -453,7 +454,7 @@ class GetDefinitionCommand : FileCommand ////////////////////////////////////// alias void delegate(uint request, string filename, string parseErrors, - TextPos[] binaryIsIn, string tasks, string outline) UpdateModuleCallBack; + TextPos[] binaryIsIn, string tasks, string outline, ParameterStorageLoc[] stcLocs) UpdateModuleCallBack; class UpdateModuleCommand : FileCommand { @@ -494,32 +495,38 @@ class UpdateModuleCommand : FileCommand mErrors = detachBSTR(errors); - VARIANT locs; - - if(gVDServer.GetBinaryIsInLocations(fname, &locs) == S_OK && - (locs.vt == (VT_ARRAY | VT_INT) || locs.vt == (VT_ARRAY | VT_I4))) + void variantToArray(size_t function(size_t) reorder = n => n, A)(ref VARIANT locs, ref A a) { - SAFEARRAY* sa = locs.parray; - assert(SafeArrayGetDim(sa) == 1); - LONG lbound, ubound; - SafeArrayGetLBound(sa, 1, &lbound); - SafeArrayGetUBound(sa, 1, &ubound); - - size_t cnt = (ubound - lbound + 1) / 2; - mBinaryIsIn.length = cnt; - for(size_t i = 0; i < cnt; i++) + if (locs.vt == (VT_ARRAY | VT_INT) || locs.vt == (VT_ARRAY | VT_I4)) { - LONG index = lbound + 2 * i; - int line, col; - SafeArrayGetElement(sa, &index, &line); - mBinaryIsIn[i].line = line; - index++; - SafeArrayGetElement(sa, &index, &col); - mBinaryIsIn[i].index = col; + SAFEARRAY* sa = locs.parray; + assert(SafeArrayGetDim(sa) == 1); + LONG lbound, ubound; + SafeArrayGetLBound(sa, 1, &lbound); + SafeArrayGetUBound(sa, 1, &ubound); + + enum Alen = a[0].tupleof.length; + size_t cnt = (ubound - lbound + 1) / Alen; + a.length = cnt; + LONG index = lbound; + int val; + for(size_t i = 0; i < cnt; i++) + { + static foreach(f; 0..Alen) + { + SafeArrayGetElement(sa, &index, &val); + a[i].tupleof[reorder(f)] = val; + index++; + } + } + SafeArrayDestroy(sa); } - SafeArrayDestroy(sa); } + VARIANT locs; + if(gVDServer.GetBinaryIsInLocations(fname, &locs) == S_OK) + variantToArray!(n => 1 - n)(locs, mBinaryIsIn); // swap line and column + BSTR tasks; if(gVDServer.GetCommentTasks(fname, &tasks) == S_OK) mTasks = detachBSTR(tasks); @@ -528,6 +535,13 @@ class UpdateModuleCommand : FileCommand if(gVDServer.GetDocumentOutline(fname, &outline) == S_OK) mOutline = detachBSTR(outline); + if (Package.GetGlobalOptions().showParamStorage) + { + VARIANT stclocs; + if(gVDServer.GetParameterStorageLocs(fname, &stclocs) == S_OK) + variantToArray(stclocs, mParameterStcLocs); + } + send(gUITid); return S_OK; } @@ -537,7 +551,7 @@ class UpdateModuleCommand : FileCommand version(DebugCmd) dbglog(to!string(mRequest) ~ " forward: " ~ mCommand ~ " " ~ ": " ~ mErrors); if(mCallback) - mCallback(mRequest, mFilename, mErrors, cast(TextPos[])mBinaryIsIn, mTasks, mOutline); + mCallback(mRequest, mFilename, mErrors, cast(TextPos[])mBinaryIsIn, mTasks, mOutline, mParameterStcLocs); return true; } @@ -548,6 +562,7 @@ class UpdateModuleCommand : FileCommand string mOutline; bool mVerbose; TextPos[] mBinaryIsIn; + ParameterStorageLoc[] mParameterStcLocs; } ////////////////////////////////////// diff --git a/visuald/visuald.def b/visuald/visuald.def index d6f017e2..c87fd279 100644 --- a/visuald/visuald.def +++ b/visuald/visuald.def @@ -25,3 +25,4 @@ EXPORTS GenerateGeneralXMLW = GenerateGeneralXMLW PRIVATE GetTooltip = GetTooltip PRIVATE GetTooltipResult = GetTooltipResult PRIVATE + GetParameterStorageLocs = GetParameterStorageLocs PRIVATE