From 3f4f34fed458465655438f45b06402d2e1c5fb18 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sat, 12 Sep 2020 09:38:11 +0200 Subject: [PATCH 1/7] adapt to dmd 2.094.0-beta1 --- vdc/dmdserver/dmd | 2 +- vdc/dmdserver/dmdinit.d | 6 +- vdc/dmdserver/dmdrmem.d | 95 +++++++++++++++++++++++++++++ vdc/dmdserver/dmdserver.visualdproj | 4 +- vdc/dmdserver/semanalysis.d | 3 +- vdc/dmdserver/semvisitor.d | 2 +- 6 files changed, 105 insertions(+), 7 deletions(-) diff --git a/vdc/dmdserver/dmd b/vdc/dmdserver/dmd index 43c6d688..a56c1ddf 160000 --- a/vdc/dmdserver/dmd +++ b/vdc/dmdserver/dmd @@ -1 +1 @@ -Subproject commit 43c6d688268b659317d8ea9a238642ee4067126b +Subproject commit a56c1ddf67ccbc23f6e83849b26fb5f7dad9ec04 diff --git a/vdc/dmdserver/dmdinit.d b/vdc/dmdserver/dmdinit.d index eb75fec3..98bee8a4 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"; } diff --git a/vdc/dmdserver/dmdrmem.d b/vdc/dmdserver/dmdrmem.d index 539fc424..e242ed6b 100644 --- a/vdc/dmdserver/dmdrmem.d +++ b/vdc/dmdserver/dmdrmem.d @@ -111,3 +111,98 @@ extern (D) T[] arraydup(T)(const scope T[] s) nothrow p[] = s; return p; } + +import core.stdc.string; + +// Define this to have Pool emit traces of objects allocated and disposed +//debug = Pool; +// Define this in addition to Pool to emit per-call traces (otherwise summaries are printed at the end). +//debug = PoolVerbose; + +/** +Defines a pool for class objects. Objects can be fetched from the pool with make() and returned to the pool with +dispose(). Using a reference that has been dispose()d has undefined behavior. make() may return memory that has been +previously dispose()d. + +Currently the pool has effect only if the GC is NOT used (i.e. either `version(GC)` or `mem.isGCEnabled` is false). +Otherwise `make` just forwards to `new` and `dispose` does nothing. + +Internally the implementation uses a singly-linked freelist with a global root. The "next" pointer is stored in the +first word of each disposed object. +*/ +struct Pool(T) +if (is(T == class)) +{ + /// The freelist's root + private static T root; + + private static void trace(string fun, string f, uint l)() + { + debug(Pool) + { + debug(PoolVerbose) + { + fprintf(stderr, "%.*s(%u): bytes: %lu Pool!(%.*s)."~fun~"()\n", + cast(int) f.length, f.ptr, l, T.classinfo.initializer.length, + cast(int) T.stringof.length, T.stringof.ptr); + } + else + { + static ulong calls; + if (calls == 0) + { + // Plant summary printer + static extern(C) void summarize() + { + fprintf(stderr, "%.*s(%u): bytes: %lu calls: %lu Pool!(%.*s)."~fun~"()\n", + cast(int) f.length, f.ptr, l, ((T.classinfo.initializer.length + 15) & ~15) * calls, + calls, cast(int) T.stringof.length, T.stringof.ptr); + } + atexit(&summarize); + } + ++calls; + } + } + } + + /** + Returns a reference to a new object in the same state as if created with new T(args). + */ + static T make(string f = __FILE__, uint l = __LINE__, A...)(auto ref A args) + { + if (!root) + { + trace!("makeNew", f, l)(); + return new T(args); + } + else + { + trace!("makeReuse", f, l)(); + auto result = root; + root = *(cast(T*) root); + memcpy(cast(void*) result, T.classinfo.initializer.ptr, T.classinfo.initializer.length); + result.__ctor(args); + return result; + } + } + + /** + Signals to the pool that this object is no longer used, so it can recycle its memory. + */ + static void dispose(string f = __FILE__, uint l = __LINE__, A...)(T goner) + { + version(GC) + { + if (mem.isGCEnabled) return; + } + trace!("dispose", f, l)(); + debug + { + // Stomp the memory so as to maximize the chance of quick failure if used after dispose(). + auto p = cast(ulong*) goner; + p[0 .. T.classinfo.initializer.length / ulong.sizeof] = 0xdeadbeef; + } + *(cast(T*) goner) = root; + root = goner; + } +} diff --git a/vdc/dmdserver/dmdserver.visualdproj b/vdc/dmdserver/dmdserver.visualdproj index dc8916c2..71baa993 100644 --- a/vdc/dmdserver/dmdserver.visualdproj +++ b/vdc/dmdserver/dmdserver.visualdproj @@ -1749,7 +1749,9 @@ - + + + diff --git a/vdc/dmdserver/semanalysis.d b/vdc/dmdserver/semanalysis.d index 6c54f6cd..d83589fc 100644 --- a/vdc/dmdserver/semanalysis.d +++ b/vdc/dmdserver/semanalysis.d @@ -1867,7 +1867,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..a615c27f 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) { From b6a8ca246cd9341e524a8e2ee39c394c404b5166 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sun, 11 Oct 2020 09:52:08 +0200 Subject: [PATCH 2/7] doc for 1.0.1 --- doc/StartPage.dd | 32 ++++++++------------------------ doc/VersionHistory.dd | 17 +++++++++++++++++ doc/visuald.ddoc | 6 +++--- 3 files changed, 28 insertions(+), 27 deletions(-) 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 From 8255580807cf6be8767ed978b9c5ed23cc6046df Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Thu, 24 Dec 2020 11:02:10 +0100 Subject: [PATCH 3/7] updated to dmd 2.094 show parameter storage class at call site --- CHANGES | 8 +- VERSION | 2 +- c2d/vsi2d.visualdproj | 17 +- msbuild/dbuild/dbuild.csproj | 7 +- nsis/visuald.nsi | 4 +- sdk/vsi.visualdproj | 3 + vdc/abothe/comserver/DParserCOMServer.csproj | 1 + vdc/abothe/comserver/IVDServer.cs | 1 + vdc/abothe/comserver/VDServer.cs | 7 +- vdc/dmdserver/dmd | 2 +- vdc/dmdserver/dmdserver.d | 46 ++ vdc/dmdserver/dmdserver.visualdproj | 2 +- vdc/dmdserver/semanalysis.d | 27 + vdc/dmdserver/semvisitor.d | 82 +++ vdc/ivdserver.d | 25 +- vdc/ivdserver.idl | 1 + vdc/vdserver.d | 5 + vdextensions/ParamStorageAdornmentTagger.cs | 615 +++++++++++++++++++ vdextensions/gotodef.cs | 50 +- vdextensions/vdext15.csproj | 2 + vdextensions/vdextensions.csproj | 1 + vdwizard/VisualDWizard.csproj | 1 + visuald/dlangsvc.d | 48 +- visuald/dllmain.d | 35 ++ visuald/vdserverclient.d | 57 +- visuald/visuald.def | 1 + 26 files changed, 959 insertions(+), 91 deletions(-) create mode 100644 vdextensions/ParamStorageAdornmentTagger.cs diff --git a/CHANGES b/CHANGES index 011eb8a4..d1f74fea 100644 --- a/CHANGES +++ b/CHANGES @@ -1280,4 +1280,10 @@ 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.0.2 + * dmdserver: + - updated to DMD 2.094.1 + * editor: + - bugzilla 21239: navigation bar now sorted, symbols starting with "__" moved to the bottom diff --git a/VERSION b/VERSION index 8c887726..fd99020c 100644 --- a/VERSION +++ b/VERSION @@ -1,5 +1,5 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 0 -#define VERSION_REVISION 1 +#define VERSION_REVISION 2 #define VERSION_BETA #define VERSION_BUILD 0 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/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..55d71585 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.094.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 a56c1ddf..8ff4b839 160000 --- a/vdc/dmdserver/dmd +++ b/vdc/dmdserver/dmd @@ -1 +1 @@ -Subproject commit a56c1ddf67ccbc23f6e83849b26fb5f7dad9ec04 +Subproject commit 8ff4b839a3b843b85b1b75c4dca2b1d6c56a8d16 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 71baa993..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) diff --git a/vdc/dmdserver/semanalysis.d b/vdc/dmdserver/semanalysis.d index d83589fc..eefe3717 100644 --- a/vdc/dmdserver/semanalysis.d +++ b/vdc/dmdserver/semanalysis.d @@ -1774,6 +1774,33 @@ 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 x; + funIn(x); // Line 10 + funRef(x); + funOut(x); + funLazy(x); + } + }; + m = checkErrors(source, ""); + + auto stcpos = findParameterStorageClass(m); + assert_equal(stcpos.length, 3); + assert_equal(stcpos[0], ParameterStorageClassPos(0, 11, 11)); + assert_equal(stcpos[1], ParameterStorageClassPos(1, 12, 11)); + assert_equal(stcpos[2], ParameterStorageClassPos(2, 13, 12)); } unittest diff --git a/vdc/dmdserver/semvisitor.d b/vdc/dmdserver/semvisitor.d index a615c27f..89674ce4 100644 --- a/vdc/dmdserver/semvisitor.d +++ b/vdc/dmdserver/semvisitor.d @@ -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..07d43f6c --- /dev/null +++ b/vdextensions/ParamStorageAdornmentTagger.cs @@ -0,0 +1,615 @@ +// 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); + + public static bool GetParameterStorageLocations(string fname, out int[] data, out bool pending) + { + try + { + // grab triplets of (type, line, col) + if (!GetParameterStorageLocs(fname, out data, out pending)) + return false; + return true; + } + catch + { + data = new int[0]; + pending = true; + 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; + + 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 = buffer.CurrentSnapshot; + } + + #region ITagger implementation + + public void UpdateStcSpans() + { + if (_pendingSnapshot == null) + return; + + int[] stcLocs; + bool pending; + ParamStorageAdornmentTagger.GetParameterStorageLocations(_fileName, out stcLocs, out pending); + if (pending && _stcLocs != null) + return; + + int j = 0; + for (int i = 0; i + 2 < stcLocs.Length; i += 3) + { + if (stcLocs[i] >= 0 && stcLocs[i] <= 2) + { + if (stcLocs[i + 1] >= _pendingSnapshot.LineCount) + continue; + var line = _pendingSnapshot.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; + 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. + 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. + ITextSnapshot snapshot = args.After; + _pendingSnapshot = args.After; + + int start = args.Changes[0].NewPosition; + int end = args.Changes[args.Changes.Count - 1].NewEnd; + + SnapshotSpan totalAffectedSpan = new SnapshotSpan( + snapshot.GetLineFromPosition(start).Start, + snapshot.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..15187c47 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,37 @@ 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) +{ + 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; + return true; +} diff --git a/visuald/vdserverclient.d b/visuald/vdserverclient.d index f3108f84..69d1317c 100644 --- a/visuald/vdserverclient.d +++ b/visuald/vdserverclient.d @@ -453,7 +453,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 +494,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 +534,10 @@ class UpdateModuleCommand : FileCommand if(gVDServer.GetDocumentOutline(fname, &outline) == S_OK) mOutline = detachBSTR(outline); + VARIANT stclocs; + if(gVDServer.GetParameterStorageLocs(fname, &stclocs) == S_OK) + variantToArray(stclocs, mParameterStcLocs); + send(gUITid); return S_OK; } @@ -537,7 +547,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 +558,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 From eb20cdc3114f2c05bd04b4353118e96b1be05907 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Thu, 24 Dec 2020 11:35:52 +0100 Subject: [PATCH 4/7] adapt to dmd 2.095 --- vdc/dmdserver/dmd | 2 +- vdc/dmdserver/dmdinit.d | 22 +++++++++++----------- vdc/dmdserver/semvisitor.d | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/vdc/dmdserver/dmd b/vdc/dmdserver/dmd index 8ff4b839..12601bff 160000 --- a/vdc/dmdserver/dmd +++ b/vdc/dmdserver/dmd @@ -1 +1 @@ -Subproject commit 8ff4b839a3b843b85b1b75c4dca2b1d6c56a8d16 +Subproject commit 12601bff1d2687b4225884616a7c20899827d24a diff --git a/vdc/dmdserver/dmdinit.d b/vdc/dmdserver/dmdinit.d index 98bee8a4..00aaec55 100644 --- a/vdc/dmdserver/dmdinit.d +++ b/vdc/dmdserver/dmdinit.d @@ -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/semvisitor.d b/vdc/dmdserver/semvisitor.d index 89674ce4..7db20e48 100644 --- a/vdc/dmdserver/semvisitor.d +++ b/vdc/dmdserver/semvisitor.d @@ -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 From b7badd1a5bab76fff09434db2d209e68829081ea Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sat, 26 Dec 2020 10:34:50 +0100 Subject: [PATCH 5/7] idl2d: adapt to VSSDK 16.7 add option to enable/disable parameter storage display --- c2d/idl2d.d | 2 + vdc/dmdserver/dmd | 2 +- vdc/dmdserver/semanalysis.d | 6 +- vdextensions/ParamStorageAdornmentTagger.cs | 80 +++++++++++---------- visuald/dllmain.d | 3 +- visuald/dpackage.d | 13 +++- visuald/propertypage.d | 13 +++- visuald/vdserverclient.d | 10 ++- 8 files changed, 83 insertions(+), 46 deletions(-) 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/vdc/dmdserver/dmd b/vdc/dmdserver/dmd index 12601bff..6afbd059 160000 --- a/vdc/dmdserver/dmd +++ b/vdc/dmdserver/dmd @@ -1 +1 @@ -Subproject commit 12601bff1d2687b4225884616a7c20899827d24a +Subproject commit 6afbd059a93e5c85f7577f880d604b0a2853aaf4 diff --git a/vdc/dmdserver/semanalysis.d b/vdc/dmdserver/semanalysis.d index eefe3717..2ac3cc38 100644 --- a/vdc/dmdserver/semanalysis.d +++ b/vdc/dmdserver/semanalysis.d @@ -1785,22 +1785,24 @@ void do_unittests() void funOut(out int p) {} void funLazy(lazy int p) {} // Line 5 - void foo() + 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, 3); + 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 diff --git a/vdextensions/ParamStorageAdornmentTagger.cs b/vdextensions/ParamStorageAdornmentTagger.cs index 07d43f6c..1dadb0ff 100644 --- a/vdextensions/ParamStorageAdornmentTagger.cs +++ b/vdextensions/ParamStorageAdornmentTagger.cs @@ -261,14 +261,15 @@ internal sealed class ParamStorageAdornmentTagger { [DllImport("visuald.dll")] public static extern bool GetParameterStorageLocs(string fname, - [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_INT)] out int[] locs, out bool pending); + [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) + 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)) + if (!GetParameterStorageLocs(fname, out data, out pending, out changeCount)) return false; return true; } @@ -276,6 +277,7 @@ public static bool GetParameterStorageLocations(string fname, out int[] data, ou { data = new int[0]; pending = true; + changeCount = -1; return false; } } @@ -383,6 +385,8 @@ internal class ParamStorageTagger : ITagger private string _fileName; int[] _stcLocs; ITextSnapshot _pendingSnapshot; + ITextSnapshot _currentSnapshot; + int _changeCount; public ParamStorageTagger(ITextBuffer buffer) { @@ -400,37 +404,41 @@ public ParamStorageTagger(ITextBuffer buffer) } buffer.Changed += (sender, args) => HandleBufferChanged(args); - _pendingSnapshot = buffer.CurrentSnapshot; + _pendingSnapshot = _currentSnapshot = buffer.CurrentSnapshot; } #region ITagger implementation public void UpdateStcSpans() { - if (_pendingSnapshot == null) - return; - int[] stcLocs; bool pending; - ParamStorageAdornmentTagger.GetParameterStorageLocations(_fileName, out stcLocs, out pending); + int changeCount; + ParamStorageAdornmentTagger.GetParameterStorageLocations(_fileName, out stcLocs, out pending, out changeCount); if (pending && _stcLocs != null) return; + if (_pendingSnapshot == null && _changeCount == changeCount) + return; - int j = 0; - for (int i = 0; i + 2 < stcLocs.Length; i += 3) + if (stcLocs != null) { - if (stcLocs[i] >= 0 && stcLocs[i] <= 2) + int j = 0; + for (int i = 0; i + 2 < stcLocs.Length; i += 3) { - if (stcLocs[i + 1] >= _pendingSnapshot.LineCount) - continue; - var line = _pendingSnapshot.GetLineFromLineNumber(stcLocs[i + 1] - 1); - stcLocs[j] = stcLocs[i]; - stcLocs[j + 1] = line.Start.Position + stcLocs[i + 2]; - j += 2; + 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); } - Array.Resize(ref stcLocs, j); _stcLocs = stcLocs; + _changeCount = changeCount; if (!pending) _pendingSnapshot = null; } @@ -440,22 +448,23 @@ public virtual IEnumerable> GetTags(NormalizedSnapshot UpdateStcSpans(); // Note that the spans argument can contain spans that are sub-spans of lines or intersect multiple lines. - 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) + if (_stcLocs != null) + for (int i = 0; i + 1 < _stcLocs.Length; i += 2) { - var point = new SnapshotPoint(spans.First().Snapshot, pos); - SnapshotSpan span = new SnapshotSpan(point, 1); - yield return new TagSpan(span, new ParamStorageTag(_stcLocs[i])); + // 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; @@ -485,15 +494,14 @@ protected virtual void HandleBufferChanged(TextContentChangedEventArgs args) // 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. - ITextSnapshot snapshot = args.After; - _pendingSnapshot = args.After; + _pendingSnapshot = _currentSnapshot = args.After; int start = args.Changes[0].NewPosition; int end = args.Changes[args.Changes.Count - 1].NewEnd; SnapshotSpan totalAffectedSpan = new SnapshotSpan( - snapshot.GetLineFromPosition(start).Start, - snapshot.GetLineFromPosition(end).End); + _currentSnapshot.GetLineFromPosition(start).Start, + _currentSnapshot.GetLineFromPosition(end).End); temp(this, new SnapshotSpanEventArgs(totalAffectedSpan)); } diff --git a/visuald/dllmain.d b/visuald/dllmain.d index 15187c47..2ba320b7 100644 --- a/visuald/dllmain.d +++ b/visuald/dllmain.d @@ -222,7 +222,7 @@ BOOL GetTooltipResult(uint request, BSTR* btip, BSTR* bfmt, BSTR* blinks) } extern(Windows) -BOOL GetParameterStorageLocs(const(char)* fname, SAFEARRAY** psa, BOOL* pPending) +BOOL GetParameterStorageLocs(const(char)* fname, SAFEARRAY** psa, BOOL* pPending, INT* pChangeCount) { if (!Package.s_instance) return false; // not yet loaded as a package @@ -252,5 +252,6 @@ BOOL GetParameterStorageLocs(const(char)* fname, SAFEARRAY** psa, BOOL* pPending *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 69d1317c..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; @@ -534,9 +535,12 @@ class UpdateModuleCommand : FileCommand if(gVDServer.GetDocumentOutline(fname, &outline) == S_OK) mOutline = detachBSTR(outline); - VARIANT stclocs; - if(gVDServer.GetParameterStorageLocs(fname, &stclocs) == S_OK) - variantToArray(stclocs, mParameterStcLocs); + if (Package.GetGlobalOptions().showParamStorage) + { + VARIANT stclocs; + if(gVDServer.GetParameterStorageLocs(fname, &stclocs) == S_OK) + variantToArray(stclocs, mParameterStcLocs); + } send(gUITid); return S_OK; From c87e566b2107781c58fb0bca319e801fe1d266ff Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sat, 26 Dec 2020 18:41:38 +0100 Subject: [PATCH 6/7] version 1.1.0, bundle dmd 2.095-beta.1 --- CHANGES | 7 +++++-- VERSION | 8 ++++---- nsis/visuald.nsi | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index d1f74fea..62159c3c 100644 --- a/CHANGES +++ b/CHANGES @@ -1282,8 +1282,11 @@ Version history * installer: - full installer now bundled with DMD 2.093.1 and LDC 1.23.0 -unreleased version 1.0.2 +unreleased version 1.1.0 * dmdserver: - - updated to DMD 2.094.1 + - 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 fd99020c..c9ec3bc4 100644 --- a/VERSION +++ b/VERSION @@ -1,5 +1,5 @@ #define VERSION_MAJOR 1 -#define VERSION_MINOR 0 -#define VERSION_REVISION 2 -#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/nsis/visuald.nsi b/nsis/visuald.nsi index 55d71585..d34c4d5b 100644 --- a/nsis/visuald.nsi +++ b/nsis/visuald.nsi @@ -29,7 +29,7 @@ ; define DMD source path to include dmd installation ; !define DMD -!define DMD_VERSION "2.094.1" +!define DMD_VERSION "2.095.0-beta.1" !define DMD_SRC c:\d\dmd-${DMD_VERSION} ; define LDC to include ldc installation From 77513e88f69a5d790377363b4462830a10d9c6c2 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sat, 26 Dec 2020 18:46:30 +0100 Subject: [PATCH 7/7] dmd.root.rmem.Pool no longer necessary --- vdc/dmdserver/dmdrmem.d | 95 ----------------------------------------- 1 file changed, 95 deletions(-) diff --git a/vdc/dmdserver/dmdrmem.d b/vdc/dmdserver/dmdrmem.d index e242ed6b..539fc424 100644 --- a/vdc/dmdserver/dmdrmem.d +++ b/vdc/dmdserver/dmdrmem.d @@ -111,98 +111,3 @@ extern (D) T[] arraydup(T)(const scope T[] s) nothrow p[] = s; return p; } - -import core.stdc.string; - -// Define this to have Pool emit traces of objects allocated and disposed -//debug = Pool; -// Define this in addition to Pool to emit per-call traces (otherwise summaries are printed at the end). -//debug = PoolVerbose; - -/** -Defines a pool for class objects. Objects can be fetched from the pool with make() and returned to the pool with -dispose(). Using a reference that has been dispose()d has undefined behavior. make() may return memory that has been -previously dispose()d. - -Currently the pool has effect only if the GC is NOT used (i.e. either `version(GC)` or `mem.isGCEnabled` is false). -Otherwise `make` just forwards to `new` and `dispose` does nothing. - -Internally the implementation uses a singly-linked freelist with a global root. The "next" pointer is stored in the -first word of each disposed object. -*/ -struct Pool(T) -if (is(T == class)) -{ - /// The freelist's root - private static T root; - - private static void trace(string fun, string f, uint l)() - { - debug(Pool) - { - debug(PoolVerbose) - { - fprintf(stderr, "%.*s(%u): bytes: %lu Pool!(%.*s)."~fun~"()\n", - cast(int) f.length, f.ptr, l, T.classinfo.initializer.length, - cast(int) T.stringof.length, T.stringof.ptr); - } - else - { - static ulong calls; - if (calls == 0) - { - // Plant summary printer - static extern(C) void summarize() - { - fprintf(stderr, "%.*s(%u): bytes: %lu calls: %lu Pool!(%.*s)."~fun~"()\n", - cast(int) f.length, f.ptr, l, ((T.classinfo.initializer.length + 15) & ~15) * calls, - calls, cast(int) T.stringof.length, T.stringof.ptr); - } - atexit(&summarize); - } - ++calls; - } - } - } - - /** - Returns a reference to a new object in the same state as if created with new T(args). - */ - static T make(string f = __FILE__, uint l = __LINE__, A...)(auto ref A args) - { - if (!root) - { - trace!("makeNew", f, l)(); - return new T(args); - } - else - { - trace!("makeReuse", f, l)(); - auto result = root; - root = *(cast(T*) root); - memcpy(cast(void*) result, T.classinfo.initializer.ptr, T.classinfo.initializer.length); - result.__ctor(args); - return result; - } - } - - /** - Signals to the pool that this object is no longer used, so it can recycle its memory. - */ - static void dispose(string f = __FILE__, uint l = __LINE__, A...)(T goner) - { - version(GC) - { - if (mem.isGCEnabled) return; - } - trace!("dispose", f, l)(); - debug - { - // Stomp the memory so as to maximize the chance of quick failure if used after dispose(). - auto p = cast(ulong*) goner; - p[0 .. T.classinfo.initializer.length / ulong.sizeof] = 0xdeadbeef; - } - *(cast(T*) goner) = root; - root = goner; - } -}