private static IScriptExtent GetExtentFromConstant(string value, IScriptExtent source) { string startLine = null; int newLineCount = 1; int positionInColumn = 1; int lastNewLineIndex = -1; for (int i = 0; i < value.Length; i++) { switch (value[i]) { case '\n': if (startLine == null) { startLine = value.Substring(0, i + 1); } lastNewLineIndex = i; newLineCount++; positionInColumn = 1; break; case '\r': break; default: positionInColumn++; break; } } if (startLine == null) { startLine = value; } IScriptPosition oldStart = source.StartScriptPosition; string fullScript = oldStart.GetFullScript(); string endLine = lastNewLineIndex > -1 ? value.Substring(lastNewLineIndex + 1, value.Length - lastNewLineIndex) : value; var start = new ScriptPosition( oldStart.File, 1, 1, startLine, fullScript); var end = new ScriptPosition( oldStart.File, newLineCount, positionInColumn, endLine, fullScript); return(new ScriptExtent(start, end)); }
public static Ast GetContainingAstOfPosition(Ast fullAst, IScriptPosition position) { var visitor = new FindAstFromPositionVisitor(position); fullAst.Visit(visitor); return(visitor.GetAst()); }
internal CompletionAnalysis(Ast ast, Token[] tokens, IScriptPosition cursorPosition, Hashtable options) { this._ast = ast; this._tokens = tokens; this._cursorPosition = cursorPosition; this._options = options; }
private StatementAst CreateProcessedMessage(IScriptPosition position, ConsoleColor dashColor, Type astType) { ExpressionAst traceInvocation = InvokeMember( isStatic: true, Type(typeof(SequencePointTracer)), nameof(SequencePointTracer.TraceExpression), Variable("Host"), Member(Type(typeof(ConsoleColor)), dashColor.ToString(), isStatic: true), Type(astType), Variable(StackVariable), Constant(position.LineNumber), Constant(position.ColumnNumber), Index( Array( Pipeline( CommandExpression( ArrayLiteral(Variable(TempVariable))))), Constant(0)), Variable("false")); return(Pipeline(CommandExpression(traceInvocation))); // var prefix = Add(CreateDashString(), String("< {0}")); // var message = Format(prefix, Variable(TempVariable)); // return Pipeline( // Command( // WriteHost, // message, // CommandParameter("ForegroundColor"), // String(dashColor.ToString()))); }
internal CompletionAnalysis(Ast ast, Token[] tokens, IScriptPosition cursorPosition, Hashtable options) { _ast = ast; _tokens = tokens; _cursorPosition = cursorPosition; _options = options; }
private static Tuple <string, int, int> GetInputAndCursorFromAst(IScriptPosition cursorPosition) { string line = cursorPosition.Line; int num = cursorPosition.ColumnNumber - 1; int num2 = cursorPosition.Offset - num; return(Tuple.Create <string, int, int>(line.Substring(0, num), num, num2)); }
private static Collection <CompletionResult> GetCompletions( Ast ast, IReadOnlyList <Token> tokens, IScriptPosition cursorPosition, Hashtable options, out bool clobberCompletions) { KeywordResult?result = GetCurrentKeyword(ast, tokens, cursorPosition); Token lastToken = result?.Frame.ParentContext?.LastToken; if (lastToken is null) { clobberCompletions = false; return(null); } KeywordResult keyword = result.Value; switch (lastToken.Kind) { case TokenKind.NewLine: case TokenKind.Semi: case TokenKind.Pipe: case TokenKind.LParen: case TokenKind.LCurly: case TokenKind.AtParen: case TokenKind.DollarParen: clobberCompletions = false; return(CompleteKeywords(keyword)); case TokenKind.Identifier: case TokenKind.Command: if (keyword.Frame.ParentContext.HasCommandAtPosition(cursorPosition)) { clobberCompletions = false; return(CompleteKeywords(keyword)); } clobberCompletions = true; return(CompleteParameters(keyword, cursorPosition)); case TokenKind.Generic: if (lastToken.Extent.EndOffset == cursorPosition.Offset) { clobberCompletions = true; return(CompleteParameters(keyword, cursorPosition)); } break; case TokenKind.Parameter: clobberCompletions = true; return(CompleteParameters(keyword, cursorPosition)); } clobberCompletions = false; return(null); }
public static KeywordContext BuildFromInput( Ast ast, IReadOnlyList <Token> tokens, IScriptPosition cursorPosition) { // Now find the AST we're in CompletionPositionContext positionContext = GetEffectiveScriptPosition(cursorPosition, tokens); Ast containingAst = FindAstFromPositionVisitor.GetContainingAstOfPosition(ast, positionContext.LastNonNewlineToken.Extent.EndScriptPosition); if (containingAst is null) { return(null); } // Find the command AST that we're in CommandAst containingCommandAst = GetFirstParentCommandAst(containingAst); var context = new KeywordContext { ContainingAst = containingAst, ContainingCommandAst = containingCommandAst, FullAst = ast, LastTokenIndex = positionContext.LastTokenIndex, LastToken = positionContext.LastToken, LastNonNewlineToken = positionContext.LastNonNewlineToken, EffectivePositionToken = positionContext.EffectivePositionToken, Tokens = tokens, Position = cursorPosition }; // Build a list of the keyword ASTs we're in going up Ast currAst = containingAst; var commandAsts = new List <CommandAst>(); do { if (currAst is CommandAst commandAst) { commandAsts.Add(commandAst); } currAst = currAst.Parent; } while (currAst != null); // Then build the context list going back down var keywordStack = new List <KeywordContextFrame>(commandAsts.Count); int frameIndex = 0; for (int i = commandAsts.Count - 1; i >= 0; i--) { keywordStack.Add(new KeywordContextFrame(context, frameIndex, commandAsts[i])); frameIndex++; } context.KeywordStack = keywordStack; return(context); }
private static CommandCompletion CompleteInputImpl(Ast ast, Token[] tokens, IScriptPosition positionOfCursor, Hashtable options) { PowerShell powershell = PowerShell.Create(RunspaceMode.CurrentRunspace); int replacementIndex = -1; int replacementLength = -1; List <CompletionResult> list = null; if (NeedToInvokeLegacyTabExpansion(powershell)) { Tuple <string, int, int> inputAndCursorFromAst = GetInputAndCursorFromAst(positionOfCursor); list = InvokeLegacyTabExpansion(powershell, inputAndCursorFromAst.Item1, inputAndCursorFromAst.Item2, false, out replacementIndex, out replacementLength); replacementIndex += inputAndCursorFromAst.Item3; } if ((list == null) || (list.Count == 0)) { ExecutionContext executionContextFromTLS = LocalPipeline.GetExecutionContextFromTLS(); MutableTuple tuple2 = null; foreach (CallStackFrame frame in executionContextFromTLS.Debugger.GetCallStack()) { dynamic obj2 = PSObject.AsPSObject(frame); var site1 = CallSite <Func <CallSite, object, bool> > .Create(Binder.UnaryOperation(CSharpBinderFlags.None, ExpressionType.IsTrue, typeof(CommandCompletion), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); if (site1.Target(site1, obj2.Command.Equals("TabExpansion2", StringComparison.OrdinalIgnoreCase))) { tuple2 = frame.FunctionContext._localsTuple; break; } } SessionStateScope currentScope = null; if (tuple2 != null) { currentScope = executionContextFromTLS.EngineSessionState.CurrentScope; SessionStateScope parent = executionContextFromTLS.EngineSessionState.CurrentScope; while ((parent != null) && (parent.LocalsTuple != tuple2)) { parent = parent.Parent; } if (parent != null) { executionContextFromTLS.EngineSessionState.CurrentScope = parent.Parent; } } try { list = new CompletionAnalysis(ast, tokens, positionOfCursor, options).GetResults(powershell, out replacementIndex, out replacementLength); } finally { if (currentScope != null) { executionContextFromTLS.EngineSessionState.CurrentScope = currentScope; } } } return(new CommandCompletion(new Collection <CompletionResult>(list ?? EmptyCompletionResult), -1, replacementIndex, replacementLength)); }
/// <summary> /// Gets completions for the symbol found in the Ast at /// the given file offset. /// </summary> /// <param name="scriptAst"> /// The Ast which will be traversed to find a completable symbol. /// </param> /// <param name="currentTokens"> /// The array of tokens corresponding to the scriptAst parameter. /// </param> /// <param name="fileOffset"> /// The 1-based file offset at which a symbol will be located. /// </param> /// <param name="runspace"> /// The Runspace to use for gathering completions. /// </param> /// <returns> /// A CommandCompletion instance that contains completions for the /// symbol at the given offset. /// </returns> static public CommandCompletion GetCompletions( Ast scriptAst, Token[] currentTokens, int fileOffset, Runspace runspace) { var type = scriptAst.Extent.StartScriptPosition.GetType(); var method = #if NanoServer type.GetMethod( "CloneWithNewOffset", BindingFlags.Instance | BindingFlags.NonPublic); #else type.GetMethod( "CloneWithNewOffset", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(int) }, null); #endif IScriptPosition cursorPosition = (IScriptPosition)method.Invoke( scriptAst.Extent.StartScriptPosition, new object[] { fileOffset }); Logger.Write( LogLevel.Verbose, string.Format( "Getting completions at offset {0} (line: {1}, column: {2})", fileOffset, cursorPosition.LineNumber, cursorPosition.ColumnNumber)); CommandCompletion commandCompletion = null; if (runspace.RunspaceAvailability == RunspaceAvailability.Available) { using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create()) { powerShell.Runspace = runspace; commandCompletion = CommandCompletion.CompleteInput( scriptAst, currentTokens, cursorPosition, null, powerShell); } } return(commandCompletion); }
private static CompletionPositionContext GetEffectiveScriptPosition( IScriptPosition cursorPosition, IReadOnlyList <Token> tokens) { // Go backward through the tokens to determine if we're positioned where a new command should be Token lastToken = null; int lastTokenIndex = -1; for (int i = tokens.Count - 1; i >= 0; i--) { Token currToken = tokens[i]; if (currToken.Extent.EndScriptPosition.LineNumber < cursorPosition.LineNumber || (currToken.Extent.EndScriptPosition.LineNumber == cursorPosition.LineNumber && currToken.Extent.EndScriptPosition.ColumnNumber <= cursorPosition.ColumnNumber)) { if (lastToken == null) { lastTokenIndex = i; lastToken = currToken; break; } } } if (lastToken.Kind != TokenKind.NewLine) { return(new CompletionPositionContext(lastToken, lastToken, lastToken, lastTokenIndex)); } // Go through and find the first token before us that isn't a newline. // When the cursor is at the end of an open scriptblock // it falls beyond that scriptblock's extent, // meaning we must backtrack to find the real context for a completion Token lastNonNewlineToken = null; Token firstEndNewlineToken = lastToken; for (int i = lastTokenIndex; i >= 0; i--) { Token currToken = tokens[i]; if (currToken.Kind != TokenKind.NewLine) { lastNonNewlineToken = currToken; break; } // This becomes the last token we saw moving backward // So when we see a non-newline token, this is the one just after that firstEndNewlineToken = currToken; } return(new CompletionPositionContext(lastToken, lastNonNewlineToken, firstEndNewlineToken, lastTokenIndex)); }
public static Tuple <Ast, Token[], IScriptPosition> MapStringInputToParsedInput(string input, int cursorIndex) { Token[] tokenArray; ParseError[] errorArray; if (cursorIndex > input.Length) { throw PSTraceSource.NewArgumentException("cursorIndex"); } ScriptBlockAst ast = Parser.ParseInput(input, out tokenArray, out errorArray); IScriptPosition position = ((InternalScriptPosition)ast.Extent.StartScriptPosition).CloneWithNewOffset(cursorIndex); return(Tuple.Create <Ast, Token[], IScriptPosition>(ast, tokenArray, position)); }
internal static string BriefMessage(IScriptPosition position) { StringBuilder builder = new StringBuilder(position.Line); if (position.ColumnNumber > builder.Length) { builder.Append(" <<<< "); } else { builder.Insert(position.ColumnNumber - 1, " >>>> "); } return StringUtil.Format(ParserStrings.TraceScriptLineMessage, position.LineNumber, builder.ToString()); }
/// <summary> /// Return a message that looks like: /// 12+ $x + <<<< $b /// </summary> internal static string BriefMessage(IScriptPosition position) { StringBuilder message = new StringBuilder(position.Line); if (position.ColumnNumber > message.Length) { message.Append(" <<<< "); } else { message.Insert(position.ColumnNumber - 1, " >>>> "); } return(StringUtil.Format(ParserStrings.TraceScriptLineMessage, position.LineNumber, message.ToString())); }
public static CommandCompletion CompleteInput(Ast ast, Token[] tokens, IScriptPosition positionOfCursor, Hashtable options) { if (ast == null) { throw PSTraceSource.NewArgumentNullException("ast"); } if (tokens == null) { throw PSTraceSource.NewArgumentNullException("tokens"); } if (positionOfCursor == null) { throw PSTraceSource.NewArgumentNullException("positionOfCursor"); } return(CompleteInputImpl(ast, tokens, positionOfCursor, options)); }
/// <summary> /// Get the abstract syntax tree, tokens and the cursor position. /// </summary> /// <param name="script">The active script.</param> /// <param name="caretPosition">The caret position.</param> /// <param name="ast">The AST to get.</param> /// <param name="tokens">The tokens to get.</param> /// <param name="cursorPosition">The cursor position to get.</param> public static void GetCommandCompletionParameters(string script, int caretPosition, out Ast ast, out Token[] tokens, out IScriptPosition cursorPosition) { ParseError[] array; ast = Tokenize(script, out tokens, out array); if (ast != null) { //HACK: Clone with a new offset using private method... var type = ast.Extent.StartScriptPosition.GetType(); var method = type.GetMethod("CloneWithNewOffset", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(int) }, null); cursorPosition = (IScriptPosition)method.Invoke(ast.Extent.StartScriptPosition, new object[] { caretPosition }); return; } cursorPosition = null; }
/// <summary> /// Gets completions for the symbol found in the Ast at /// the given file offset. /// </summary> /// <param name="scriptAst"> /// The Ast which will be traversed to find a completable symbol. /// </param> /// <param name="currentTokens"> /// The array of tokens corresponding to the scriptAst parameter. /// </param> /// <param name="fileOffset"> /// The 1-based file offset at which a symbol will be located. /// </param> /// <param name="executionService"> /// The PowerShellContext to use for gathering completions. /// </param> /// <param name="logger">An ILogger implementation used for writing log messages.</param> /// <param name="cancellationToken"> /// A CancellationToken to cancel completion requests. /// </param> /// <returns> /// A CommandCompletion instance that contains completions for the /// symbol at the given offset. /// </returns> public static async Task <CommandCompletion> GetCompletionsAsync( Ast scriptAst, Token[] currentTokens, int fileOffset, IInternalPowerShellExecutionService executionService, ILogger logger, CancellationToken cancellationToken) { IScriptPosition cursorPosition = s_clonePositionWithNewOffset(scriptAst.Extent.StartScriptPosition, fileOffset); logger.LogTrace( string.Format( "Getting completions at offset {0} (line: {1}, column: {2})", fileOffset, cursorPosition.LineNumber, cursorPosition.ColumnNumber)); Stopwatch stopwatch = new(); CommandCompletion commandCompletion = null; await executionService.ExecuteDelegateAsync( representation : "CompleteInput", new ExecutionOptions { Priority = ExecutionPriority.Next }, (pwsh, _) => { stopwatch.Start(); commandCompletion = CommandCompletion.CompleteInput( scriptAst, currentTokens, cursorPosition, options: null, powershell: pwsh); }, cancellationToken) .ConfigureAwait(false); stopwatch.Stop(); logger.LogTrace($"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms."); return(commandCompletion); }
private static Collection <CompletionResult> CompleteParameters( KeywordResult keyword, IScriptPosition cursorPosition) { if (keyword.Schema.ShouldUseDefaultParameterCompletions) { return(null); } // If we're still on the last token, we need to look further back Token lastToken = keyword.Frame.ParentContext.LastToken; if (cursorPosition.Offset == lastToken.Extent.EndOffset) { lastToken = keyword.Frame.ParentContext.Tokens[keyword.Frame.ParentContext.LastTokenIndex - 1]; } return(lastToken.Kind == TokenKind.Parameter ? CompleteParameterValues(keyword, lastToken) : CompleteParameterNames(keyword)); }
/// <summary> /// Gets completions for the symbol found in the Ast at /// the given file offset. /// </summary> /// <param name="scriptAst"> /// The Ast which will be traversed to find a completable symbol. /// </param> /// <param name="currentTokens"> /// The array of tokens corresponding to the scriptAst parameter. /// </param> /// <param name="fileOffset"> /// The 1-based file offset at which a symbol will be located. /// </param> /// <param name="runspace"> /// The Runspace to use for gathering completions. /// </param> /// <returns> /// A CommandCompletion instance that contains completions for the /// symbol at the given offset. /// </returns> static public CommandCompletion GetCompletions( Ast scriptAst, Token[] currentTokens, int fileOffset, Runspace runspace) { var type = scriptAst.Extent.StartScriptPosition.GetType(); var method = type.GetMethod( "CloneWithNewOffset", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(int) }, null); IScriptPosition cursorPosition = (IScriptPosition)method.Invoke( scriptAst.Extent.StartScriptPosition, new object[] { fileOffset }); CommandCompletion commandCompletion = null; if (runspace.RunspaceAvailability == RunspaceAvailability.Available) { using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create()) { powerShell.Runspace = runspace; commandCompletion = CommandCompletion.CompleteInput( scriptAst, currentTokens, cursorPosition, null, powerShell); } } return(commandCompletion); }
internal CompletionContext CreateCompletionContext(ExecutionContext executionContext) { Func <Token, bool> predicate = null; Func <Token, bool> func2 = null; Token token = null; IScriptPosition positionForAstSearch = this._cursorPosition; bool flag = false; Token token2 = this._tokens.LastOrDefault <Token>(t => IsCursorWithinOrJustAfterExtent(this._cursorPosition, t.Extent) && IsInterestingToken(t)); if (token2 == null) { if (predicate == null) { predicate = t => IsCursorBeforeExtent(this._cursorPosition, t.Extent) && IsInterestingToken(t); } token = this._tokens.LastOrDefault <Token>(predicate); if (token != null) { positionForAstSearch = token.Extent.EndScriptPosition; flag = true; } } else { StringExpandableToken token3 = token2 as StringExpandableToken; if ((token3 != null) && (token3.NestedTokens != null)) { if (func2 == null) { func2 = t => IsCursorWithinOrJustAfterExtent(this._cursorPosition, t.Extent) && IsInterestingToken(t); } token2 = token3.NestedTokens.LastOrDefault <Token>(func2) ?? token3; } } IEnumerable <Ast> source = AstSearcher.FindAll(this._ast, ast => IsCursorWithinOrJustAfterExtent(positionForAstSearch, ast.Extent), true); return(new CompletionContext { TokenAtCursor = token2, TokenBeforeCursor = token, CursorPosition = this._cursorPosition, RelatedAsts = source.ToList <Ast>(), Options = this._options, ExecutionContext = executionContext, ReplacementIndex = flag ? this._cursorPosition.Offset : 0 }); }
public static CommandCompletion CompleteInput(Ast ast, Token[] tokens, IScriptPosition cursorPosition, Hashtable options, PowerShell powershell) { if (ast == null) { throw PSTraceSource.NewArgumentNullException("ast"); } if (tokens == null) { throw PSTraceSource.NewArgumentNullException("tokens"); } if (cursorPosition == null) { throw PSTraceSource.NewArgumentNullException("cursorPosition"); } if (powershell == null) { throw PSTraceSource.NewArgumentNullException("powershell"); } if (!powershell.IsChild) { RemoteRunspace remoteRunspace = powershell.Runspace as RemoteRunspace; if (remoteRunspace != null) { CheckScriptCallOnRemoteRunspace(remoteRunspace); if (remoteRunspace.GetCapabilities().Equals(RunspaceCapability.Default)) { int num; int num2; powershell.Commands.Clear(); Tuple <string, int, int> inputAndCursorFromAst = GetInputAndCursorFromAst(cursorPosition); return(new CommandCompletion(new Collection <CompletionResult>(InvokeLegacyTabExpansion(powershell, inputAndCursorFromAst.Item1, inputAndCursorFromAst.Item2, true, out num, out num2) ?? EmptyCompletionResult), -1, num + inputAndCursorFromAst.Item3, num2)); } string text = ast.Extent.Text; int offset = ((InternalScriptPosition)cursorPosition).Offset; return(CallScriptWithStringParameterSet(text, offset, options, powershell)); } } return(CallScriptWithAstParameterSet(ast, tokens, cursorPosition, options, powershell)); }
/// <summary> /// Add ARM DSL completions to the front of the completion result collection. /// May clobber other results if DSL completions are determined to be of low relevance. /// </summary> /// <param name="completion">The existing command completion object.</param> /// <param name="ast">The AST of the whole input as parsed.</param> /// <param name="tokens">The tokens of the whole input as parsed.</param> /// <param name="cursorPosition">The position of the cursor within the input.</param> /// <param name="options">A completion options hashtable. This is currently ignored.</param> public static void PrependDslCompletions( CommandCompletion completion, Ast ast, IReadOnlyList <Token> tokens, IScriptPosition cursorPosition, Hashtable options) { Collection <CompletionResult> completionResults = GetCompletions(ast, tokens, cursorPosition, options, out bool clobberCompletions); if (completionResults == null) { return; } if (completion.ReplacementIndex < 0) { completion.ReplacementIndex = cursorPosition.Offset; } if (completion.ReplacementLength < 0) { completion.ReplacementLength = 0; } if (clobberCompletions || completion.CompletionMatches == null || completion.CompletionMatches.Count == 0) { completion.CompletionMatches = completionResults; } else { foreach (CompletionResult existingCompletion in completion.CompletionMatches) { completionResults.Add(existingCompletion); } completion.CompletionMatches = completionResults; } }
internal static TypeName FindTypeNameToComplete(ITypeName type, IScriptPosition cursor) { TypeName name = type as TypeName; if (name != null) { if ((cursor.Offset > type.Extent.StartOffset) && (cursor.Offset <= type.Extent.EndOffset)) { return(name); } return(null); } GenericTypeName name2 = type as GenericTypeName; if (name2 != null) { name = FindTypeNameToComplete(name2.TypeName, cursor); if (name != null) { return(name); } foreach (ITypeName name3 in name2.GenericArguments) { name = FindTypeNameToComplete(name3, cursor); if (name != null) { return(name); } } return(null); } ArrayTypeName name4 = type as ArrayTypeName; if (name4 == null) { return(null); } return((TypeName)(FindTypeNameToComplete(name4.ElementType, cursor) ?? null)); }
internal static TypeName FindTypeNameToComplete(ITypeName type, IScriptPosition cursor) { TypeName name = type as TypeName; if (name != null) { if ((cursor.Offset > type.Extent.StartOffset) && (cursor.Offset <= type.Extent.EndOffset)) { return name; } return null; } GenericTypeName name2 = type as GenericTypeName; if (name2 != null) { name = FindTypeNameToComplete(name2.TypeName, cursor); if (name != null) { return name; } foreach (ITypeName name3 in name2.GenericArguments) { name = FindTypeNameToComplete(name3, cursor); if (name != null) { return name; } } return null; } ArrayTypeName name4 = type as ArrayTypeName; if (name4 == null) { return null; } return (TypeName) (FindTypeNameToComplete(name4.ElementType, cursor) ?? null); }
private static Ast GetLastAstAtCursor(ScriptBlockAst scriptBlockAst, IScriptPosition cursorPosition) { return AstSearcher.FindAll(scriptBlockAst, ast => IsCursorRightAfterExtent(cursorPosition, ast.Extent), true).LastOrDefault<Ast>(); }
private static bool IsCursorBeforeExtent(IScriptPosition cursor, IScriptExtent extent) { return (extent.EndOffset < cursor.Offset); }
private static bool PositionIsEqual(IScriptPosition position1, IScriptPosition position2) { return(position1.ColumnNumber == position2.ColumnNumber && position1.LineNumber == position2.LineNumber && position1.File == position2.File); }
private static bool IsCursorWithinOrJustAfterExtent(IScriptPosition cursor, IScriptExtent extent) { return((cursor.Offset > extent.StartOffset) && (cursor.Offset <= extent.EndOffset)); }
private static bool IsCursorBeforeExtent(IScriptPosition cursor, IScriptExtent extent) { return(extent.EndOffset < cursor.Offset); }
/// <summary> /// Gets completions for the symbol found in the Ast at /// the given file offset. /// </summary> /// <param name="scriptAst"> /// The Ast which will be traversed to find a completable symbol. /// </param> /// <param name="currentTokens"> /// The array of tokens corresponding to the scriptAst parameter. /// </param> /// <param name="fileOffset"> /// The 1-based file offset at which a symbol will be located. /// </param> /// <param name="powerShellContext"> /// The PowerShellContext to use for gathering completions. /// </param> /// <param name="logger">An ILogger implementation used for writing log messages.</param> /// <param name="cancellationToken"> /// A CancellationToken to cancel completion requests. /// </param> /// <returns> /// A CommandCompletion instance that contains completions for the /// symbol at the given offset. /// </returns> static public async Task <CommandCompletion> GetCompletions( Ast scriptAst, Token[] currentTokens, int fileOffset, PowerShellContext powerShellContext, ILogger logger, CancellationToken cancellationToken) { var type = scriptAst.Extent.StartScriptPosition.GetType(); var method = #if CoreCLR type.GetMethod( "CloneWithNewOffset", BindingFlags.Instance | BindingFlags.NonPublic); #else type.GetMethod( "CloneWithNewOffset", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(int) }, null); #endif IScriptPosition cursorPosition = (IScriptPosition)method.Invoke( scriptAst.Extent.StartScriptPosition, new object[] { fileOffset }); logger.Write( LogLevel.Verbose, string.Format( "Getting completions at offset {0} (line: {1}, column: {2})", fileOffset, cursorPosition.LineNumber, cursorPosition.ColumnNumber)); CommandCompletion commandCompletion = null; if (powerShellContext.IsDebuggerStopped) { PSCommand command = new PSCommand(); command.AddCommand("TabExpansion2"); command.AddParameter("Ast", scriptAst); command.AddParameter("Tokens", currentTokens); command.AddParameter("PositionOfCursor", cursorPosition); command.AddParameter("Options", null); PSObject outputObject = (await powerShellContext.ExecuteCommand <PSObject>(command, false, false)) .FirstOrDefault(); if (outputObject != null) { ErrorRecord errorRecord = outputObject.BaseObject as ErrorRecord; if (errorRecord != null) { logger.WriteException( "Encountered an error while invoking TabExpansion2 in the debugger", errorRecord.Exception); } else { commandCompletion = outputObject.BaseObject as CommandCompletion; } } } else if (powerShellContext.CurrentRunspace.Runspace.RunspaceAvailability == RunspaceAvailability.Available) { using (RunspaceHandle runspaceHandle = await powerShellContext.GetRunspaceHandle(cancellationToken)) using (PowerShell powerShell = PowerShell.Create()) { powerShell.Runspace = runspaceHandle.Runspace; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); commandCompletion = CommandCompletion.CompleteInput( scriptAst, currentTokens, cursorPosition, null, powerShell); stopwatch.Stop(); logger.Write(LogLevel.Verbose, $"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms."); } } return(commandCompletion); }
private static bool IsCursorOutsideOfExtent(IScriptPosition cursor, IScriptExtent extent) { return cursor.Offset < extent.StartOffset || cursor.Offset > extent.EndOffset; }
/// <summary> /// Find the configuration statement contains current cursor /// </summary> /// <param name="cursorPosition"></param> /// <param name="ast"></param> /// <param name="keywordAst"></param> /// <returns></returns> private ConfigurationDefinitionAst GetAncestorConfigurationAstAndKeywordAst( IScriptPosition cursorPosition, Ast ast, out DynamicKeywordStatementAst keywordAst) { ConfigurationDefinitionAst configureAst = Ast.GetAncestorConfigurationDefinitionAstAndDynamicKeywordStatementAst(ast, out keywordAst); // Find the configuration statement contains current cursor // Note: cursorPosition.Offset < configureAst.Extent.EndOffset means cursor locates inside the configuration // cursorPosition.Offset = configureAst.Extent.EndOffset means cursor locates at the end of the configuration while (configureAst != null && cursorPosition.Offset > configureAst.Extent.EndOffset) { configureAst = Ast.GetAncestorAst<ConfigurationDefinitionAst>(configureAst.Parent); } return configureAst; }
private static Ast GetLastAstAtCursor(ScriptBlockAst scriptBlockAst, IScriptPosition cursorPosition) { var asts = AstSearcher.FindAll(scriptBlockAst, ast => IsCursorRightAfterExtent(cursorPosition, ast.Extent), searchNestedScriptBlocks: true); return asts.LastOrDefault(); }
private static bool IsCursorWithinOrJustAfterExtent(IScriptPosition cursor, IScriptExtent extent) { return cursor.Offset > extent.StartOffset && cursor.Offset <= extent.EndOffset; }
private static KeywordResult?GetCurrentKeyword(Ast ast, IReadOnlyList <Token> tokens, IScriptPosition cursorPosition) { KeywordContext context = KeywordContext.BuildFromInput(ast, tokens, cursorPosition); if (context is null) { return(null); } DslKeywordSchema currentSchema = PSArmSchemaInformation.PSArmSchema; KeywordContextFrame currentFrame = null; for (int i = 0; i < context.KeywordStack.Count; i++) { KeywordContextFrame frame = context.KeywordStack[i]; string commandName = frame.CommandAst.GetCommandName(); if (commandName is null) { continue; } IReadOnlyDictionary <string, DslKeywordSchema> subschemas = currentSchema.GetInnerKeywords(currentFrame); if (subschemas is null || !subschemas.TryGetValue(commandName, out DslKeywordSchema subschema)) { continue; } currentSchema = subschema; currentFrame = frame; } if (currentFrame is null || currentSchema is null) { return(null); } return(new KeywordResult(currentSchema, currentFrame)); }
private static Ast GetLastAstAtCursor(ScriptBlockAst scriptBlockAst, IScriptPosition cursorPosition) { return(AstSearcher.FindAll(scriptBlockAst, ast => IsCursorRightAfterExtent(cursorPosition, ast.Extent), true).LastOrDefault <Ast>()); }
private static CommandCompletion CallScriptWithAstParameterSet(Ast ast, Token[] tokens, IScriptPosition cursorPosition, Hashtable options, PowerShell powershell) { try { powershell.Commands.Clear(); powershell.AddCommand("TabExpansion2").AddArgument(ast).AddArgument(tokens).AddArgument(cursorPosition).AddArgument(options); Collection <PSObject> collection = powershell.Invoke(); if (collection == null) { return(EmptyCommandCompletion); } if (collection.Count == 1) { CommandCompletion completion = PSObject.Base(collection[0]) as CommandCompletion; if (completion != null) { return(completion); } } } catch (Exception exception) { CommandProcessorBase.CheckForSevereException(exception); } finally { powershell.Commands.Clear(); } return(EmptyCommandCompletion); }
private static bool IsCursorRightAfterExtent(IScriptPosition cursor, IScriptExtent extent) { return(cursor.Offset == extent.EndOffset); }
private static bool IsCursorRightAfterExtent(IScriptPosition cursor, IScriptExtent extent) { return (cursor.Offset == extent.EndOffset); }
private static bool IsCursorBeforeExtent(IScriptPosition cursor, IScriptExtent extent) { return cursor.Offset < extent.StartOffset; }
private static bool IsCursorAfterExtentAndInTheSameLine(IScriptPosition cursor, IScriptExtent extent) { return cursor.Offset >= extent.EndOffset && extent.EndLineNumber == cursor.LineNumber; }
internal static TypeName FindTypeNameToComplete(ITypeName type, IScriptPosition cursor) { var typeName = type as TypeName; if (typeName != null) { // If the cursor is at the start offset, it's not really inside, so return null. // If the cursor is at the end offset, it's not really inside, but it's just before the cursor, // we don want to complete it. return (cursor.Offset > type.Extent.StartOffset && cursor.Offset <= type.Extent.EndOffset) ? typeName : null; } var genericTypeName = type as GenericTypeName; if (genericTypeName != null) { typeName = FindTypeNameToComplete(genericTypeName.TypeName, cursor); if (typeName != null) return typeName; foreach (var t in genericTypeName.GenericArguments) { typeName = FindTypeNameToComplete(t, cursor); if (typeName != null) return typeName; } return null; } var arrayTypeName = type as ArrayTypeName; if (arrayTypeName != null) { return FindTypeNameToComplete(arrayTypeName.ElementType, cursor) ?? null; } return null; }
private static bool IsCursorAfterExtent(IScriptPosition cursor, IScriptExtent extent) { return extent.EndOffset < cursor.Offset; }
// TODO: BRING THIS BACK /// <summary> /// Gets completions for the symbol found in the Ast at /// the given file offset. /// </summary> /// <param name="scriptAst"> /// The Ast which will be traversed to find a completable symbol. /// </param> /// <param name="currentTokens"> /// The array of tokens corresponding to the scriptAst parameter. /// </param> /// <param name="fileOffset"> /// The 1-based file offset at which a symbol will be located. /// </param> /// <param name="powerShellContext"> /// The PowerShellContext to use for gathering completions. /// </param> /// <param name="logger">An ILogger implementation used for writing log messages.</param> /// <param name="cancellationToken"> /// A CancellationToken to cancel completion requests. /// </param> /// <returns> /// A CommandCompletion instance that contains completions for the /// symbol at the given offset. /// </returns> public static async Task <CommandCompletion> GetCompletionsAsync( Ast scriptAst, Token[] currentTokens, int fileOffset, PowerShellContextService powerShellContext, ILogger logger, CancellationToken cancellationToken) { if (!s_completionHandle.Wait(0)) { return(null); } try { IScriptPosition cursorPosition = (IScriptPosition)s_extentCloneWithNewOffset.Invoke( scriptAst.Extent.StartScriptPosition, new object[] { fileOffset }); logger.LogTrace( string.Format( "Getting completions at offset {0} (line: {1}, column: {2})", fileOffset, cursorPosition.LineNumber, cursorPosition.ColumnNumber)); if (!powerShellContext.IsAvailable) { return(null); } var stopwatch = new Stopwatch(); // If the current runspace is out of process we can use // CommandCompletion.CompleteInput because PSReadLine won't be taking up the // main runspace. if (powerShellContext.IsCurrentRunspaceOutOfProcess()) { using (RunspaceHandle runspaceHandle = await powerShellContext.GetRunspaceHandleAsync(cancellationToken)) using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create()) { powerShell.Runspace = runspaceHandle.Runspace; stopwatch.Start(); try { return(CommandCompletion.CompleteInput( scriptAst, currentTokens, cursorPosition, options: null, powershell: powerShell)); } finally { stopwatch.Stop(); logger.LogTrace($"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms."); } } } CommandCompletion commandCompletion = null; await powerShellContext.InvokeOnPipelineThreadAsync( pwsh => { stopwatch.Start(); commandCompletion = CommandCompletion.CompleteInput( scriptAst, currentTokens, cursorPosition, options: null, powershell: pwsh); }); stopwatch.Stop(); logger.LogTrace($"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms."); return(commandCompletion); } finally { s_completionHandle.Release(); } }
private Tuple<ExpressionAst, StatementAst> GetHashEntryContainsCursor( IScriptPosition cursor, HashtableAst hashTableAst, bool isCursorInString) { Tuple<ExpressionAst, StatementAst> keyValuePairWithCursor = null; foreach (var kvp in hashTableAst.KeyValuePairs) { if (IsCursorWithinOrJustAfterExtent(cursor, kvp.Item2.Extent)) { keyValuePairWithCursor = kvp; break; } if (!isCursorInString) { // // Handle following case, cursor is after '=' but before next key value pair, // next key value pair will be treated as kvp.Item2 of 'Ensure' key // // configuration foo // { // File foo // { // DestinationPath = "\foo.txt" // Ensure = | // DependsOn =@("[User]x") // } // } // if (kvp.Item2.Extent.StartLineNumber > kvp.Item1.Extent.EndLineNumber && IsCursorAfterExtentAndInTheSameLine(cursor, kvp.Item1.Extent)) { keyValuePairWithCursor = kvp; break; } // // If cursor is not within a string, then handle following two cases, // // #1) cursor is after '=', in the same line of previous key value pair // configuration test{File testfile{DestinationPath='c:\test'; Ensure = | // // #2) cursor is after '=', in the separate line of previous key value pair // configuration test{File testfile{DestinationPath='c:\test'; // Ensure = | // if (!IsCursorBeforeExtent(cursor, kvp.Item1.Extent) && IsCursorAfterExtentAndInTheSameLine(cursor, kvp.Item2.Extent)) { keyValuePairWithCursor = kvp; } } } return keyValuePairWithCursor; }
private void GetCommandCompletionParameters(int caretPosition, out Ast ast, out Token[] tokens, out IScriptPosition cursorPosition) { ITrackingSpan trackingSpan; _textView.TextBuffer.Properties.TryGetProperty(BufferProperties.Ast, out ast); _textView.TextBuffer.Properties.TryGetProperty(BufferProperties.Tokens, out tokens); _textView.TextBuffer.Properties.TryGetProperty(BufferProperties.SpanTokenized, out trackingSpan); // No ast or tokens available on the buffer. Re-tokenize. if (ast == null || tokens == null) { var trackingSpan2 = _textView.TextBuffer.CurrentSnapshot.CreateTrackingSpan(0, _textView.TextBuffer.CurrentSnapshot.Length, SpanTrackingMode.EdgeInclusive); var text = trackingSpan2.GetText(_textView.TextBuffer.CurrentSnapshot); ParseError[] array; ast = Tokenize(text, out tokens, out array); } if (ast != null) { //HACK: Clone with a new offset using private method... var type = ast.Extent.StartScriptPosition.GetType(); var method = type.GetMethod("CloneWithNewOffset", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] {typeof (int)}, null); cursorPosition = (IScriptPosition)method.Invoke(ast.Extent.StartScriptPosition, new object[]{caretPosition}); return; } cursorPosition = null; }