public async Task <bool> ExtractMethod(IExtractMethodInput input) { var analyzer = _view.GetAnalyzerAtCaret(_serviceProvider); if (analyzer == null) { return(false); } var buffer = _view.GetPythonBufferAtCaret(); var snapshot = buffer.CurrentSnapshot; var projectFile = _view.GetAnalysisAtCaret(_serviceProvider); if (projectFile == null) { return(false); } // extract once to validate the selection var extractInfo = await analyzer.ExtractMethodAsync( projectFile, buffer, _view, "method_name", null, null ); if (extractInfo == null) { return(false); } var extract = extractInfo.Data; if (extract.cannotExtractMsg != null) { input.CannotExtract(extract.cannotExtractMsg); return(false); } if (extract.wasExpanded && !input.ShouldExpandSelection()) { return(false); } if (extract.startIndex != null && extract.endIndex != null) { var selectionSpan = _view.BufferGraph.MapUpToBuffer( new SnapshotSpan( snapshot, Span.FromBounds(extract.startIndex.Value, extract.endIndex.Value) ), SpanTrackingMode.EdgeInclusive, _view.TextBuffer ); foreach (var span in selectionSpan) { _view.Selection.Select(span, false); break; } } var info = input.GetExtractionInfo(new ExtractedMethodCreator(analyzer, projectFile, _view, buffer, extract)); if (info == null) { // user cancelled extract method return(false); } // extract again to get the final result... extractInfo = await analyzer.ExtractMethodAsync( projectFile, buffer, _view, info.Name, info.Parameters, info.TargetScope?.Scope.id ); if (extractInfo == null) { return(false); } VsProjectAnalyzer.ApplyChanges( extractInfo.Data.changes, buffer, extractInfo.GetTracker(extractInfo.Data.version) ); return(true); }
public async Task <bool> ExtractMethod(IExtractMethodInput input) { var buffer = _view.GetPythonBufferAtCaret(); var bi = _services.GetBufferInfo(buffer); var entry = bi?.AnalysisEntry; if (entry?.Analyzer == null) { return(false); } var snapshot = buffer.CurrentSnapshot; // extract once to validate the selection var extract = await entry.Analyzer.ExtractMethodAsync( bi, _view, "method_name", null, null ); if (extract == null) { return(false); } if (extract.cannotExtractMsg != null) { input.CannotExtract(extract.cannotExtractMsg); return(false); } if (extract.wasExpanded && !input.ShouldExpandSelection()) { return(false); } if (extract.startLine > 0 && extract.endLine > 0) { var selectionSpan = _view.BufferGraph.MapUpToBuffer( new SourceSpan( new SourceLocation(extract.startLine, extract.startCol), new SourceLocation(extract.endLine, extract.endCol) ).ToSnapshotSpan(snapshot), SpanTrackingMode.EdgeInclusive, _view.TextBuffer ); foreach (var span in selectionSpan) { _view.Selection.Select(span, false); break; } } var info = input.GetExtractionInfo(new ExtractedMethodCreator(bi, _view, extract)); if (info == null) { // user cancelled extract method return(false); } // extract again to get the final result... extract = await entry.Analyzer.ExtractMethodAsync( bi, _view, info.Name, info.Parameters, info.TargetScope?.Scope.id ); if (extract == null) { return(false); } VsProjectAnalyzer.ApplyChanges( extract.changes, buffer, bi.LocationTracker, extract.version ); return(true); }
public bool ExtractMethod(IExtractMethodInput input) { // tighten up the selection so that we don't expand into any enclosing nodes because we overlap w/ their white space... var selectionStart = _view.Selection.Start.Position; while (selectionStart.Position < _view.TextBuffer.CurrentSnapshot.Length && Char.IsWhiteSpace(selectionStart.GetChar())) { selectionStart += 1; } var selectionEnd = _view.Selection.End.Position; if (selectionEnd.Position == _view.TextBuffer.CurrentSnapshot.Length) { selectionEnd -= 1; } while (selectionEnd.Position >= 0 && Char.IsWhiteSpace(selectionEnd.GetChar())) { selectionEnd -= 1; } var walker = new EnclosingNodeWalker(_ast, selectionStart, selectionEnd); _ast.Walk(walker); Debug.Assert(walker.Target != null); if (walker.Target == null) { return false; } // expand the selection if we aren't currently covering a full expression/statement if (!walker.Target.IsValidSelection || (WasSelectionExpanded(walker.Target, selectionStart, selectionEnd) && !input.ShouldExpandSelection())) { return false; } _view.Selection.Select( new SnapshotSpan( _view.TextBuffer.CurrentSnapshot, Span.FromBounds( walker.Target.StartIncludingIndentation, walker.Target.End ) ), false ); // check for things we cannot handle if (!IsValidExtraction(input, walker.Target)) { return false; } // get the variables which are read by the selected statement(s) var varCollector = new InnerVariableWalker(_ast); walker.Target.Walk(varCollector); // Walk the target to understand flow control and definite assignment to further understand // what we need to flow in. For example if a variable is assigned to both in the un-extracted // and extracted code but it's definitely assigned in the extracted code then we don't need // to flow the variable in. // Run flow checker, first on any nested scopes... foreach (ScopeStatement scope in varCollector._scopes) { FlowChecker.Check(scope); } // then on our extracted code var parent = walker.Target.Parents[walker.Target.Parents.Length - 1]; HashSet<PythonVariable> readBeforeInit; FlowChecker extractedChecker = null; if (parent.ScopeVariables != null) { extractedChecker = new FlowChecker(parent); walker.Target.Walk(extractedChecker); readBeforeInit = extractedChecker.ReadBeforeInitializedVariables; } else { readBeforeInit = new HashSet<PythonVariable>(); } // then on code which follows our extracted body var afterStmts = walker.Target.GetStatementsAfter(_ast); HashSet<PythonVariable> readByFollowingCodeBeforeInit = null; var parentNode = walker.Target.Parents[walker.Target.Parents.Length - 1]; var outputVars = new HashSet<PythonVariable>(); if (parentNode.ScopeVariables != null) { var checker = new FlowChecker(parentNode); foreach (var afterStmt in afterStmts) { afterStmt.Walk(checker); } readByFollowingCodeBeforeInit = checker.ReadBeforeInitializedVariables; foreach (var variable in varCollector._allWrittenVariables) { if (variable != null && variable.Scope is PythonAst) { // global variable assigned to in outer scope, and left with // a valid value (not deleted) from the extracted code. We // need to pass the value back out and assign to it in the // global scope. if (!checker.IsInitialized(variable) && extractedChecker.IsAssigned(variable)) { outputVars.Add(variable); } } } } // collect any nested scopes, and see if they read any free variables var scopeCollector = new ScopeCollector(); foreach (var afterStmt in afterStmts) { afterStmt.Walk(scopeCollector); } foreach (var scope in scopeCollector._scopes) { if (scope.FreeVariables != null) { foreach (var freeVar in scope.FreeVariables) { if (varCollector._allWrittenVariables.Contains(freeVar)) { // we've assigned to a variable accessed from an inner // scope, we need to get the value of the variable back out. outputVars.Add(freeVar); } } } } // discover any variables which are consumed and need to be available as outputs... var outputCollector = new OuterVariableWalker(_ast, walker.Target, varCollector, readBeforeInit, readByFollowingCodeBeforeInit, outputVars); _ast.Walk(outputCollector); if (outputCollector._outputVars.Count > 0 && walker.Target.ContainsReturn) { input.CannotExtract("Cannot extract method that assigns to variables and returns"); return false; } var creator = new ExtractedMethodCreator( _ast, walker.Target.Parents, outputCollector._inputVars, outputCollector._outputVars, walker.Target, _view.Options.GetIndentSize(), !_view.Options.IsConvertTabsToSpacesEnabled(), _view.Options.GetNewLineCharacter() ); var info = input.GetExtractionInfo(creator); if (info == null) { // user cancelled extract method return false; } // get the new method body... var newMethod = creator.GetExtractionResult(info); // generate the call site. using (var edit = _view.TextBuffer.CreateEdit()) { // delete the selected code int start = _view.Selection.Start.Position; edit.Delete(Span.FromBounds(_view.Selection.Start.Position, _view.Selection.End.Position)); // insert the newly generated method edit.Insert(walker.Target.InsertLocations[info.TargetScope], newMethod.Method); // insert the call to the new method edit.Insert(start, newMethod.Call); edit.Apply(); } return true; }
public async Task <bool> ExtractMethod(IExtractMethodInput input) { var analyzer = _view.GetAnalyzer(_serviceProvider); var projectFile = _view.TextBuffer.GetAnalysisEntry(); // extract once to validate the selection var extractInfo = await analyzer.ExtractMethodAsync( projectFile, _view.TextBuffer, _view, "method_name", null, null ); if (extractInfo == null) { return(false); } var extract = extractInfo.Data; if (extract.cannotExtractMsg != null) { input.CannotExtract(extract.cannotExtractMsg); return(false); } if (extract.wasExpanded && !input.ShouldExpandSelection()) { return(false); } if (extract.startIndex != null && extract.endIndex != null) { _view.Selection.Select( new SnapshotSpan( _view.TextBuffer.CurrentSnapshot, Span.FromBounds(extract.startIndex.Value, extract.endIndex.Value) ), false ); } var info = input.GetExtractionInfo(new ExtractedMethodCreator(analyzer, projectFile, _view, extract)); if (info == null) { // user cancelled extract method return(false); } // extract again to get the final result... extractInfo = await analyzer.ExtractMethodAsync( projectFile, _view.TextBuffer, _view, info.Name, info.Parameters, info.TargetScope?.Scope.id ); if (extractInfo == null) { return(false); } VsProjectAnalyzer.ApplyChanges( extractInfo.Data.changes, _view.TextBuffer, extractInfo.GetTracker(extractInfo.Data.version) ); return(true); }
public bool ExtractMethod(IExtractMethodInput input) { // tighten up the selection so that we don't expand into any enclosing nodes because we overlap w/ their white space... var selectionStart = _view.Selection.Start.Position; while (selectionStart.Position < _view.TextBuffer.CurrentSnapshot.Length && Char.IsWhiteSpace(selectionStart.GetChar())) { selectionStart += 1; } var selectionEnd = _view.Selection.End.Position; if (selectionEnd.Position == _view.TextBuffer.CurrentSnapshot.Length) { selectionEnd -= 1; } while (selectionEnd.Position >= 0 && Char.IsWhiteSpace(selectionEnd.GetChar())) { selectionEnd -= 1; } var walker = new EnclosingNodeWalker(_ast, selectionStart, selectionEnd); _ast.Walk(walker); Debug.Assert(walker.Target != null); if (walker.Target == null) { return(false); } // expand the selection if we aren't currently covering a full expression/statement if (!walker.Target.IsValidSelection || (WasSelectionExpanded(walker.Target, selectionStart, selectionEnd) && !input.ShouldExpandSelection())) { return(false); } _view.Selection.Select( new SnapshotSpan( _view.TextBuffer.CurrentSnapshot, Span.FromBounds( walker.Target.StartIncludingIndentation, walker.Target.End ) ), false ); // check for things we cannot handle if (!IsValidExtraction(input, walker.Target)) { return(false); } // get the variables which are read by the selected statement(s) var varCollector = new InnerVariableWalker(_ast); walker.Target.Walk(varCollector); // Walk the target to understand flow control and definite assignment to further understand // what we need to flow in. For example if a variable is assigned to both in the un-extracted // and extracted code but it's definitely assigned in the extracted code then we don't need // to flow the variable in. // Run flow checker, first on any nested scopes... foreach (ScopeStatement scope in varCollector._scopes) { FlowChecker.Check(scope); } // then on our extracted code var parent = walker.Target.Parents[walker.Target.Parents.Length - 1]; HashSet <PythonVariable> readBeforeInit; FlowChecker extractedChecker = null; if (parent.ScopeVariables != null) { extractedChecker = new FlowChecker(parent); walker.Target.Walk(extractedChecker); readBeforeInit = extractedChecker.ReadBeforeInitializedVariables; } else { readBeforeInit = new HashSet <PythonVariable>(); } // then on code which follows our extracted body var afterStmts = walker.Target.GetStatementsAfter(_ast); HashSet <PythonVariable> readByFollowingCodeBeforeInit = null; var parentNode = walker.Target.Parents[walker.Target.Parents.Length - 1]; var outputVars = new HashSet <PythonVariable>(); if (parentNode.ScopeVariables != null) { var checker = new FlowChecker(parentNode); foreach (var afterStmt in afterStmts) { afterStmt.Walk(checker); } readByFollowingCodeBeforeInit = checker.ReadBeforeInitializedVariables; foreach (var variable in varCollector._allWrittenVariables) { if (variable != null && variable.Scope is PythonAst) { // global variable assigned to in outer scope, and left with // a valid value (not deleted) from the extracted code. We // need to pass the value back out and assign to it in the // global scope. if (!checker.IsInitialized(variable) && extractedChecker.IsAssigned(variable)) { outputVars.Add(variable); } } } } // collect any nested scopes, and see if they read any free variables var scopeCollector = new ScopeCollector(); foreach (var afterStmt in afterStmts) { afterStmt.Walk(scopeCollector); } foreach (var scope in scopeCollector._scopes) { if (scope.FreeVariables != null) { foreach (var freeVar in scope.FreeVariables) { if (varCollector._allWrittenVariables.Contains(freeVar)) { // we've assigned to a variable accessed from an inner // scope, we need to get the value of the variable back out. outputVars.Add(freeVar); } } } } // discover any variables which are consumed and need to be available as outputs... var outputCollector = new OuterVariableWalker(_ast, walker.Target, varCollector, readBeforeInit, readByFollowingCodeBeforeInit, outputVars); _ast.Walk(outputCollector); if (outputCollector._outputVars.Count > 0 && walker.Target.ContainsReturn) { input.CannotExtract("Cannot extract method that assigns to variables and returns"); return(false); } var creator = new ExtractedMethodCreator( _ast, walker.Target.Parents, outputCollector._inputVars, outputCollector._outputVars, walker.Target, _view.Options.GetIndentSize(), !_view.Options.IsConvertTabsToSpacesEnabled(), _view.Options.GetNewLineCharacter() ); var info = input.GetExtractionInfo(creator); if (info == null) { // user cancelled extract method return(false); } // get the new method body... var newMethod = creator.GetExtractionResult(info); // generate the call site. using (var edit = _view.TextBuffer.CreateEdit()) { // delete the selected code int start = _view.Selection.Start.Position; edit.Delete(Span.FromBounds(_view.Selection.Start.Position, _view.Selection.End.Position)); // insert the newly generated method edit.Insert(walker.Target.InsertLocations[info.TargetScope], newMethod.Method); // insert the call to the new method edit.Insert(start, newMethod.Call); edit.Apply(); } return(true); }
public async Task<bool> ExtractMethod(IExtractMethodInput input) { var analyzer = _view.GetAnalyzerAtCaret(_serviceProvider); if (analyzer == null) { return false; } var buffer = _view.GetPythonBufferAtCaret(); var snapshot = buffer.CurrentSnapshot; var projectFile = _view.GetAnalysisAtCaret(_serviceProvider); if (projectFile == null) { return false; } // extract once to validate the selection var extractInfo = await analyzer.ExtractMethodAsync( projectFile, buffer, _view, "method_name", null, null ); if (extractInfo == null) { return false; } var extract = extractInfo.Data; if (extract.cannotExtractMsg != null) { input.CannotExtract(extract.cannotExtractMsg); return false; } if (extract.wasExpanded && !input.ShouldExpandSelection()) { return false; } if (extract.startIndex != null && extract.endIndex != null) { var selectionSpan = _view.BufferGraph.MapUpToBuffer( new SnapshotSpan( snapshot, Span.FromBounds(extract.startIndex.Value, extract.endIndex.Value) ), SpanTrackingMode.EdgeInclusive, _view.TextBuffer ); foreach (var span in selectionSpan) { _view.Selection.Select(span, false); break; } } var info = input.GetExtractionInfo(new ExtractedMethodCreator(analyzer, projectFile, _view, buffer, extract)); if (info == null) { // user cancelled extract method return false; } // extract again to get the final result... extractInfo = await analyzer.ExtractMethodAsync( projectFile, buffer, _view, info.Name, info.Parameters, info.TargetScope?.Scope.id ); if (extractInfo == null) { return false; } VsProjectAnalyzer.ApplyChanges( extractInfo.Data.changes, buffer, extractInfo.GetTracker(extractInfo.Data.version) ); return true; }