-
Notifications
You must be signed in to change notification settings - Fork 634
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve the autocomplete feature of the builtin python IDE #9402
Conversation
Looks like there are some test failures on the self-serve, looking into them now |
if (string.IsNullOrEmpty(dynamoCoreDir)) | ||
{ | ||
// Gather executing assembly location | ||
string nodePath = Assembly.GetExecutingAssembly().Location; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems odd - I can see this returning odd results on some systems, could it be worth trying to get access to the pathManager or pathResolver which has all of these paths?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested in Sandbox and Revit without issues but I agree getting a path and stepping back is a bit strange. I assumed the Nodes folder was always relative to the Root directory. Wouldn't I need access to the Dynamo model to use the pathManager?
If we do make a change we should probably do it here as well, although there is no navigating in this scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class is initialized in ScriptEditor, you can pass in dynamoViewModel.Model.PathManager.DynamoCoreDirectory
when initializing instead of getting this info in the getter, it is a dup of what we have in PathManager
.
Adding a new constructor will do the job for you
public IronPythonCompletionProvider(string dynamoCoreDir )
{
pythonLibDir= [you logic of getting the dir from dynamoCoreDir ]
IronPythonCompletionProvider();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^awesome, thanks @QilongTang!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to duplicate the logic from the original IronPythonCompletionProvider()
constructor and mark the original Obsolete
correct? I didn't think you could call a constructor from within a constructor of the same class like that without constructor chaining
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alfarok you don't NEED to obsolete the other constructor if it should be called, I don't really see why you need to chain the constructors at all but you can if thats useful.
I would also consider adding the std library path as a parameter, (maybe optional?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but I dont think you can call one constructor from inside another to any great effect since you dont actually explicitly return from a constructor in c#
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mjkkirschner I was thinking that's why I would need to duplicate everything from the original since it can't be called from within the new constructor but I wasn't sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahh, I see, it's a big constructor, yeah you can use chaining to call the other constructor with the :this()
syntax.
// Try to load Python Standard Library Type for autocomplete | ||
try | ||
{ | ||
var pyLibImports = String.Format("import sys\nsys.path.append(r'{0}')\n", pythonStdLib); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this still need to be done?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes it is required to load the autocomplete for the python std modules. It is also being done for ProtoGeometry which is included in the template by default.
return paramMatches; | ||
} | ||
|
||
public List<string> findClrReferences(string code) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs a summary tag
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this along with one other method above can actually be private, will replace them now
After pulling in the latest changes from master, down to 7 test failures that need to be addressed
|
/// <summary> | ||
/// Cache storing a reference to the Dynamo Core directory | ||
/// </summary> | ||
private static string dynamoCoreDir { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove this property, see comments below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alfarok I agree with @QilongTang - that seems like a good place to pass the references you need.
/// Class constructor | ||
/// </summary> | ||
[Obsolete("Use additional constructor passing the Dynamo Core Directory to enable Python Std Lib autocomplete.")] | ||
public IronPythonCompletionProvider() : this("") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mjkkirschner @QilongTang is this an API breaking change? The original constructor calls the new constructor passing an empty string as the dynamoCoreDir
param. If this is null or empty the standard lib autocomplete is not loaded
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure! It doesn't change the signature, so it's not immediately obvious that it is a break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think it breaks,
- Function signature does not change
- from your code, if it is called without a param, the behavior is still same with before
RegexToType.Add(arrayRegex, typeof(List)); | ||
RegexToType.Add(dictRegex, typeof(PythonDictionary)); | ||
// Special case for python variables defined as null | ||
ImportedTypes["None"] = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mjkkirschner @QilongTang This causes 3 old tests to fail because a new ImportedType
is being added so completionProvider.ImportedTypes.Count
has stepped by 1 for:
CanImportLibrary()
CanImportSystemLibraryAndGetCompletionData()
DuplicateCallsToImportShouldBeFine()
Still working through the remaining 4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mjkkirschner this is the other type that is added by default which is why several tests stepped by 1
@@ -47,6 +44,11 @@ public ScriptScope Scope | |||
set { scope = value; } | |||
} | |||
|
|||
/// <summary> | |||
/// Cache storing a reference to the Dynamo Core directory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this comment correct? seems odd.
// Determine if the Python Standard Library is available in the given context | ||
if(!String.IsNullOrEmpty(dynamoCoreDir)) | ||
{ | ||
pythonLibDir = dynamoCoreDir + @"\IronPython.StdLib.2.7.8"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alfarok don't do this - instead use System.Path.Combine - it will work cross platform.
@@ -237,7 +237,7 @@ public void CanImportLibrary() | |||
var completionProvider = new IronPythonCompletionProvider(); | |||
completionProvider.UpdateImportedTypes(str); | |||
|
|||
Assert.AreEqual(1, completionProvider.ImportedTypes.Count); | |||
Assert.AreEqual(2, completionProvider.ImportedTypes.Count); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what are the other types?
@alfarok looks good so far, will review again when you fix the other tests. Two comments:
|
@alfarok thanks for finishing this up, the changes look great! My only comment is on the
I recommend the following minor change:
At this point I'm stating to think it's probably better to just replace this whole method with some regex as well. |
@@ -205,7 +205,7 @@ public IronPythonCompletionProvider(string dynamoCoreDir) | |||
// Determine if the Python Standard Library is available in the given context | |||
if(!String.IsNullOrEmpty(dynamoCoreDir)) | |||
{ | |||
pythonLibDir = dynamoCoreDir + @"\IronPython.StdLib.2.7.8"; | |||
pythonLibDir = Path.Combine(dynamoCoreDir + @"\IronPython.StdLib.2.7.8"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alfarok this is not right - it should be: Path.Combine(dynamoCoreDir, "IronPython.StdLib.2.7.8");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mjkkirschner nice catch, fixing now
var previousTries = 0; | ||
badStatements.TryGetValue(statement, out previousTries); | ||
// TODO - why is this 3? Should this be a constant? | ||
if (previousTries > 3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dimven Thanks for taking a look, I think we are pretty close! I will take a closer look at the GetName
method as it may be related to some of the remaining 4 test failures. It is also possible the tests are using old methods that aren't updated with the new logic.
Do you happen to remember the significance of this 3
above? Possibly related to the length of these known assemblies?
private static string[] knownAssemblies = {
"mscorlib",
"RevitAPI",
"RevitAPIUI",
"ProtoGeometry"
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also don't see any references to GetName
in the current implementation which is why I marked it Obsolete
, but I may be missing something
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah true. Didn't notice you split it in two methods.
The knownAssemblies
is used in TryGetTypeFromFullName
method, which acts as a short-circuit around firing up the python interpreter for any full name types that were already preloaded.
Down to 1 failure but I am skeptical about a couple others that are simply checking the imported types count, doing a little more digging.
|
@@ -293,6 +292,7 @@ public void CanMatchImportSystemLibraryWithComment() | |||
var matches = IronPythonCompletionProvider.FindBasicImportStatements(str); | |||
|
|||
Assert.AreEqual(1, matches.Count); | |||
Assert.IsTrue(matches.ContainsKey("System")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could use sequence equal here to be more specific with the test results
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the only location we strictly checked the count without verifying the matching key didn't change. Should I update this for all instances?
@mjkkirschner I think this is ready for a final review, all tests should be passing. We now handle the scenario in which an import statement ends in |
All unit tests are now passing on the self-serve CI |
…l expected defaults
"None" | ||
}; | ||
|
||
foreach (var type in importedTypes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems to reimplement sequence equals:
https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.sequenceequal?view=netframework-4.7.2 ?
any reason not to just use it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be good now
@alfarok would it be possible to fit in one more usability update for the 2.1 release? If so, I added a secondary generic autocomplete that pops up automatically after every 2nd character and suggest the built-in python methods and the most common "words" used in the script above: That helps me greatly reduces the chance for spelling mistakes and speeds up writing. |
@dimven I want to get this work merged since the original PR has been open for while and already has quite a few changes. If you want to open a new PR with those changes we can take a look (much faster than the last PR 😁), it definitely seems like a nice to have feature. Unfortunately, these changes will all be going into 2.2 because the 2.1 branch has already been cut and gone through preliminary testing. I am not 100% sure on this but I think the plan moving forward is to release updates to Dynamo Core more frequently so there should be less time between these releases. Thanks again for all your efforts, these are some really nice enhancements and I am sure the community will be very pleased! |
got the LGTM from @mjkkirschner. @dimven Any chance you have a semi-long python script that doesn't touch the Revit API that you could share? Doesn't have to do anything fancy but after speaking with Mike we thought it would be a good to add a test catching any performance degradation in the future with parsing long scripts. |
@alfarok , sorry for not getting back to you in time. Unfortunately, I didn't have anything non-revit related to test this but maybe one of the sample from the primers will be perfect for this: http://primer.dynamobim.org/13_Best-Practice/datasets/13-2/RoofDrainageSim.zip It has a semi-long python script at the end of the graph. |
1 similar comment
@alfarok , sorry for not getting back to you in time. Unfortunately, I didn't have anything non-revit related to test this but maybe one of the sample from the primers will be perfect for this: http://primer.dynamobim.org/13_Best-Practice/datasets/13-2/RoofDrainageSim.zip It has a semi-long python script at the end of the graph. |
Background
Picking up the awesome work originally completed by @dimven in #8420
The type guessing should be relatively accurate for simple statements. It is difficult to guarantee 100% coverage for complicated variable definitions as it would require the luxury of running in the same scope as the actual script or pre-executing the entire script.
Purpose
This PR does the following:
Obsolete
while making new properties/methodsprivate/internal
Testing
CodeCompletionTests.cs
)Examples
The examples in #8420 are all still valid.
ProtoGeometry
Python Standard Library
Declarations
Check these if you believe they are true
*.resx
filesReviewers
@dimven @mjkkirschner @QilongTang
FYIs
@Racel