Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

implement AddImport #1656

Merged
merged 47 commits into from
Oct 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
3193a93
first draft of add import on python.
heejaechang Oct 8, 2019
4fa2440
filter out unnecessary imports
heejaechang Oct 9, 2019
8d5c968
removed unused usings
heejaechang Oct 9, 2019
9989184
more refactoring
heejaechang Oct 9, 2019
c90dfc0
addressed PR feedbacks
heejaechang Oct 9, 2019
2868a1a
added initial tests
heejaechang Oct 10, 2019
46dd915
Merge branch 'master' of https://github.com/microsoft/python-language…
heejaechang Oct 10, 2019
615f905
adding more tests
heejaechang Oct 10, 2019
7d0325c
rename
heejaechang Oct 10, 2019
f409d47
simplify test a bit more
heejaechang Oct 10, 2019
7915241
added more tests
heejaechang Oct 10, 2019
3881d24
changed codeaction source to use diagnostic service which should have…
heejaechang Oct 10, 2019
f94461a
removed unused usings
heejaechang Oct 10, 2019
3d18131
add ability to index library to IndexManager
heejaechang Oct 14, 2019
8af626e
trying GetAnalysisAsync
heejaechang Oct 14, 2019
5e0c5bf
add closed module support for add imports.
heejaechang Oct 14, 2019
8e73bbb
removed assert that keep crashing debug bit LS
heejaechang Oct 14, 2019
282ffc0
add closed module test.
heejaechang Oct 14, 2019
062596f
fixed merge conflicts
heejaechang Oct 14, 2019
18735af
use explicit type rather than tuple since it has many members
heejaechang Oct 14, 2019
7b681c1
add readonly
heejaechang Oct 14, 2019
6823f7e
use existing formatUI to format localizable string
heejaechang Oct 15, 2019
50055f5
disabled locally imported code action for now
heejaechang Oct 16, 2019
87e8eef
fixed a bug where it didn't handle circular import properly, and adde…
heejaechang Oct 17, 2019
c1a8eec
handle circular imports better. some refactorings and etc.
heejaechang Oct 18, 2019
bc854e1
tweaked code action ordering a bit
heejaechang Oct 18, 2019
f5d3b25
fix push/pop mismatch due to class decl being duplicated issue
heejaechang Oct 18, 2019
6aa8c94
added test for symbol ordering
heejaechang Oct 18, 2019
ade67a4
added abbreviation for well known module
heejaechang Oct 18, 2019
79fa68f
added some comments
heejaechang Oct 18, 2019
df49da7
added context based filtering
heejaechang Oct 18, 2019
14ae66c
clean up
heejaechang Oct 18, 2019
bf14ff6
make sure we don't suggest imports that can be used in import statement
heejaechang Oct 18, 2019
6535f2c
added support for quick fix in hover
heejaechang Oct 19, 2019
26c1dba
added one more test and fixed one bug
heejaechang Oct 21, 2019
3e7a014
addressed easy feedback first
heejaechang Oct 22, 2019
883dfd4
made add import not to load modules implicitly and only use modules a…
heejaechang Oct 24, 2019
f964c4a
added more abbreviation supports
heejaechang Oct 24, 2019
52a2e5a
removed unnecessary time out
heejaechang Oct 24, 2019
c0abc41
support reverse abbreviation case where user types "np" and we sugges…
heejaechang Oct 24, 2019
a5da0d3
made abbreviation to check existing symbol names to use unique name f…
heejaechang Oct 24, 2019
e71c803
change order of where we show special one (abbreviation one).
heejaechang Oct 24, 2019
4a44e76
add unit test for new ordering
heejaechang Oct 24, 2019
4710421
Merge branch 'master' of https://github.com/microsoft/python-language…
heejaechang Oct 24, 2019
4fdbba7
addressed PR feedbacks
heejaechang Oct 25, 2019
4ce017a
more refactoring
heejaechang Oct 25, 2019
719db2a
fixed merge conflicts
heejaechang Oct 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions src/Analysis/Ast/Impl/Analyzer/Symbols/SymbolCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ public override bool Walk(ClassDefinition cd) {

if (!string.IsNullOrEmpty(cd.NameExpression?.Name)) {
var classInfo = CreateClass(cd);
if (classInfo == null) {
// we can't create class info for this node.
// don't walk down
return false;
}

// The variable is transient (non-user declared) hence it does not have location.
// Class type is tracking locations for references and renaming.
_eval.DeclareVariable(cd.Name, classInfo, VariableSource.Declaration);
Expand All @@ -68,7 +74,9 @@ public override bool Walk(ClassDefinition cd) {
}

public override void PostWalk(ClassDefinition cd) {
if (!IsDeprecated(cd) && !string.IsNullOrEmpty(cd.NameExpression?.Name)) {
if (!IsDeprecated(cd) &&
!string.IsNullOrEmpty(cd.NameExpression?.Name) &&
_typeMap.ContainsKey(cd)) {
_scopes.Pop().Dispose();
}
base.PostWalk(cd);
Expand All @@ -95,9 +103,14 @@ public override void PostWalk(FunctionDefinition fd) {

private PythonClassType CreateClass(ClassDefinition cd) {
PythonType declaringType = null;
if(!(cd.Parent is PythonAst)) {
Debug.Assert(_typeMap.ContainsKey(cd.Parent));
_typeMap.TryGetValue(cd.Parent, out declaringType);
if (!(cd.Parent is PythonAst)) {
if (!_typeMap.TryGetValue(cd.Parent, out declaringType)) {
// we can get into this situation if parent is defined twice and we preserve
// only one of them.
// for example, code has function definition with exact same signature
// and class is defined under one of that function
return null;
}
}
var cls = new PythonClassType(cd, declaringType, _eval.GetLocationOfName(cd),
_eval.SuppressBuiltinLookup ? BuiltinTypeId.Unknown : BuiltinTypeId.Type);
Expand All @@ -120,6 +133,10 @@ private void AddFunction(FunctionDefinition fd, PythonType declaringType) {
f = new PythonFunctionType(fd, declaringType, _eval.GetLocationOfName(fd));
// The variable is transient (non-user declared) hence it does not have location.
// Function type is tracking locations for references and renaming.

// if there are multiple functions with same name exist, only the very first one will be
// maintained in the scope. we should improve this if possible.
// https://github.com/microsoft/python-language-server/issues/1693
_eval.DeclareVariable(fd.Name, f, VariableSource.Declaration);
_typeMap[fd] = f;
declaringType?.AddMember(f.Name, f, overwrite: true);
Expand Down
15 changes: 13 additions & 2 deletions src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

namespace Microsoft.Python.Analysis.Dependencies {
internal sealed class DependencyResolver<TKey, TValue> : IDependencyResolver<TKey, TValue> {
// optimization to only analyze one that is reachable from root
private readonly bool _checkVertexReachability = true;

private readonly Dictionary<TKey, int> _keys = new Dictionary<TKey, int>();
private readonly List<DependencyVertex<TKey, TValue>> _vertices = new List<DependencyVertex<TKey, TValue>>();
private readonly object _syncObj = new object();
Expand Down Expand Up @@ -287,7 +290,7 @@ private bool TryCreateWalkingGraph(in ImmutableArray<DependencyVertex<TKey, TVal

for (var index = 0; index < vertices.Count; index++) {
var vertex = vertices[index];
if (vertex == null || vertex.IsWalked || depths[index] == -1) {
if (vertex == null || vertex.IsWalked || !ReachableFromRoot(depths, index)) {
continue;
}

Expand All @@ -311,7 +314,7 @@ private bool TryCreateWalkingGraph(in ImmutableArray<DependencyVertex<TKey, TVal
foreach (var outgoingIndex in node.DependencyVertex.Outgoing) {
if (!nodesByVertexIndex.TryGetValue(outgoingIndex, out var outgoingNode)) {
var vertex = vertices[outgoingIndex];
if (vertex == null || depths[vertex.Index] == -1) {
if (vertex == null || !ReachableFromRoot(depths, vertex.Index)) {
continue;
}

Expand All @@ -326,6 +329,14 @@ private bool TryCreateWalkingGraph(in ImmutableArray<DependencyVertex<TKey, TVal

analysisGraph = ImmutableArray<WalkingVertex<TKey, TValue>>.Create(nodesByVertexIndex.Values);
return true;

bool ReachableFromRoot(ImmutableArray<int> reachable, int index) {
const int inaccessibleFromRoot = -1;

// one of usage case for this optimization is not analyzing module that is not reachable
// from user code
return _checkVertexReachability && reachable[index] != inaccessibleFromRoot;
}
}

private static ImmutableArray<int> CalculateDepths(in ImmutableArray<DependencyVertex<TKey, TValue>> vertices) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// permissions and limitations under the License.

using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Python.Analysis.Caching;
using Microsoft.Python.Analysis.Core.Interpreter;
using Microsoft.Python.Analysis.Types;
Expand Down Expand Up @@ -86,5 +88,7 @@ public interface IModuleManagement : IModuleResolution {
ImmutableArray<PythonLibraryPath> LibraryPaths { get; }

bool SetUserConfiguredPaths(ImmutableArray<string> paths);

IEnumerable<IPythonModule> GetImportedModules(CancellationToken cancellationToken);
}
}
14 changes: 14 additions & 0 deletions src/Analysis/Ast/Impl/Modules/Resolution/MainModuleResolution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ public MainModuleResolution(string root, IServiceContainer services, ImmutableAr

public IBuiltinsPythonModule BuiltinsModule { get; private set; }

public IEnumerable<IPythonModule> GetImportedModules(CancellationToken cancellationToken) {
foreach (var module in _specialized.Values) {
cancellationToken.ThrowIfCancellationRequested();
yield return module;
}

foreach (var moduleRef in Modules.Values) {
cancellationToken.ThrowIfCancellationRequested();
if (moduleRef.Value != null) {
yield return moduleRef.Value;
}
}
}

protected override IPythonModule CreateModule(string name) {
var moduleImport = CurrentPathResolver.GetModuleImportFromModuleName(name);
if (moduleImport == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,6 @@ public IPythonModule GetOrLoadModule(string name) {
return module;
}

module = Interpreter.ModuleResolution.GetSpecializedModule(name);
if (module != null) {
return module;
}

// Now try regular case.
if (Modules.TryGetValue(name, out var moduleRef)) {
return moduleRef.GetOrCreate(name, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,27 @@ private PathResolverSnapshot(PythonLanguageVersion pythonLanguageVersion, string
}

public ImmutableArray<string> GetAllImportableModuleNames(bool includeImplicitPackages = true) {
return GetAllImportableModuleInfo(n => !string.IsNullOrEmpty(n.FullModuleName), n => n.FullModuleName, includeImplicitPackages);
}

public ImmutableArray<string> GetAllImportableModulesByName(string name, bool includeImplicitPackages = true) {
return GetAllImportableModuleInfo(n => string.Equals(n.Name, name), n => n.FullModuleName, includeImplicitPackages);
}

public ImmutableArray<string> GetAllImportableModuleFilePaths(bool includeImplicitPackages = true) {
return GetAllImportableModuleInfo(n => !string.IsNullOrEmpty(n.ModulePath), n => n.ModulePath, includeImplicitPackages);
}

private ImmutableArray<T> GetAllImportableModuleInfo<T>(Func<Node, bool> predicate, Func<Node, T> valueGetter, bool includeImplicitPackages = true) {
var roots = _roots.Prepend(_nonRooted);
var items = new Queue<Node>(roots);
var names = ImmutableArray<string>.Empty;
var stringValues = ImmutableArray<T>.Empty;

while (items.Count > 0) {
var item = items.Dequeue();
if (item != null) {
if (!string.IsNullOrEmpty(item.FullModuleName) && (item.IsModule || includeImplicitPackages)) {
names = names.Add(item.FullModuleName);
if (predicate(item) && (item.IsModule || includeImplicitPackages)) {
stringValues = stringValues.Add(valueGetter(item));
}

foreach (var child in item.Children.ExcludeDefault()) {
Expand All @@ -99,13 +111,17 @@ public ImmutableArray<string> GetAllImportableModuleNames(bool includeImplicitPa
}
}

return names.AddRange(
return stringValues.AddRange(
_builtins.Children
.Where(b => !string.IsNullOrEmpty(b.FullModuleName))
.Select(b => b.FullModuleName)
.Where(b => predicate(b))
.Select(b => valueGetter(b))
);
}

public string GetModuleNameByPath(string modulePath) {
return TryFindModule(modulePath, out var edge, out _) ? edge.End.FullModuleName : null;
}

public ModuleImport GetModuleImportFromModuleName(in string fullModuleName) {
for (var rootIndex = 0; rootIndex < _roots.Count; rootIndex++) {
if (TryFindModuleByName(rootIndex, fullModuleName, out var lastEdge) && TryCreateModuleImport(lastEdge, out var moduleImports)) {
Expand Down
22 changes: 22 additions & 0 deletions src/Core/Impl/Extensions/ArrayExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// permissions and limitations under the License.

using System;
using System.Collections.Generic;

namespace Microsoft.Python.Core {
public static class ArrayExtensions {
Expand All @@ -36,5 +37,26 @@ public static int IndexOf<T, TValue>(this T[] array, TValue value, Func<T, TValu

return -1;
}

public static TCollection AddIfNotNull<TCollection, TItem>(this TCollection list, TItem item)
where TCollection : ICollection<TItem>
where TItem : class {
if (item == null) {
return list;
}

list.Add(item);
return list;
}

public static TCollection AddIfNotNull<TCollection, TItem>(this TCollection list, params TItem[] items)
where TCollection : ICollection<TItem>
where TItem : class {
foreach (var item in items) {
list.AddIfNotNull(item);
}

return list;
}
}
}
9 changes: 8 additions & 1 deletion src/Core/Impl/Text/Position.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

namespace Microsoft.Python.Core.Text {
[Serializable]
public struct Position {
public struct Position : IEquatable<Position> {
/// <summary>
/// Line position in a document (zero-based).
/// </summary>
Expand All @@ -39,7 +39,14 @@ public struct Position {

public static bool operator >(Position p1, Position p2) => p1.line > p2.line || p1.line == p2.line && p1.character > p2.character;
public static bool operator <(Position p1, Position p2) => p1.line < p2.line || p1.line == p2.line && p1.character < p2.character;
public static bool operator ==(Position p1, Position p2) => p1.Equals(p2);
public static bool operator !=(Position p1, Position p2) => !p1.Equals(p2);

public bool Equals(Position other) => line == other.line && character == other.character;

public override bool Equals(object obj) => obj is Position other ? Equals(other) : false;

public override int GetHashCode() => 0;
public override string ToString() => $"({line}, {character})";
}
}
Loading