public NodeSnapshot(ITextSnapshot snapshot, INode node) { int offset = 0; if (node.Values.GetEnumerator().MoveNext()) { ITextSnapshotLine line = snapshot.GetLineFromPosition(node.Position); // if the Value list is not empty, expand the snapshotSpan // to include leading whitespaces, so that when a user // types smth in this space he will get the dropdown for (; node.Position - offset > line.Extent.Start.Position; offset++) { if (snapshot[node.Position - offset] == ' ') continue; if (snapshot[node.Position - offset] == '\t') continue; if ( snapshot[node.Position - offset] == '%' || snapshot[node.Position - offset] == '{' || snapshot[node.Position - offset] == '|' ) offset++; break; } } this.snapshotSpan = new SnapshotSpan(snapshot, node.Position - offset, node.Length + offset); this.node = node; foreach (IEnumerable<INode> list in node.Nodes.Values) foreach (INode child in list) children.Add(new NodeSnapshot(snapshot, child)); }
public static bool IsFirstTokenOnLine(this SyntaxToken token, ITextSnapshot snapshot) { Contract.ThrowIfNull(snapshot); var baseLine = snapshot.GetLineFromPosition(token.SpanStart); return baseLine.GetFirstNonWhitespacePosition() == token.SpanStart; }
private LinePreservingCodeReplacer(ITextView view, string newCode, Span range) { _view = view; _snapshot = view.TextBuffer.CurrentSnapshot; _oldCode = _snapshot.GetText(range); _newCode = newCode; _newLines = newCode.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); _startingReplacementLine = _snapshot.GetLineFromPosition(range.Start).LineNumber; }
public static bool IsInNamespace(ITextSnapshot snapshot, int position) { if (position > 0) { ITextSnapshotLine line = snapshot.GetLineFromPosition(position); if (line.Length > 2 && position - line.Start > 2) { return snapshot[position - 1] == ':'; } } return false; }
public ReverseExpressionParser(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) { _snapshot = snapshot; _buffer = buffer; _span = span; var loc = span.GetSpan(snapshot); var line = _curLine = snapshot.GetLineFromPosition(loc.Start); var targetSpan = new Span(line.Start.Position, span.GetEndPoint(snapshot).Position - line.Start.Position); _tokens = Classifier.GetClassificationSpans(new SnapshotSpan(snapshot, targetSpan)); }
public static TextSpan GetFormattingSpan(ITextSnapshot snapshot, SnapshotSpan selectedSpan) { var currentLine = snapshot.GetLineFromPosition(selectedSpan.Start); var endPosition = selectedSpan.IsEmpty ? currentLine.End : selectedSpan.End; var previousLine = GetNonEmptyPreviousLine(snapshot, currentLine); // first line on screen if (currentLine == previousLine) { return TextSpan.FromBounds(currentLine.Start, endPosition); } var lastNonNoisyCharPosition = previousLine.GetLastNonWhitespacePosition().Value; return TextSpan.FromBounds(lastNonNoisyCharPosition, endPosition); }
private static Span? GetRoxygenBlockPosition(ITextSnapshot snapshot, int definitionStart) { var line = snapshot.GetLineFromPosition(definitionStart); for (int i = line.LineNumber - 1; i >= 0; i--) { var currentLine = snapshot.GetLineFromLineNumber(i); string lineText = currentLine.GetText().TrimStart(); if (lineText.Length > 0) { if (lineText.EqualsOrdinal("##")) { return new Span(currentLine.Start, currentLine.Length); } else if (lineText.EqualsOrdinal("#'")) { return null; } break; } } return new Span(line.Start, 0); }
public void AddError(ITextSnapshot snapshot, Diagnostic diagnostic, TextSpan span) { var line = snapshot.GetLineFromPosition(span.Start); var task = new ErrorTask { Text = diagnostic.Message, Line = line.LineNumber, Column = span.Start - line.Start.Position, Category = TaskCategory.CodeSense, ErrorCategory = TaskErrorCategory.Error, Priority = TaskPriority.Normal, Document = span.Filename }; task.Navigate += OnTaskNavigate; _errorListProvider.Tasks.Add(task); }
/// <summary> /// Creates the designer (proxy) node over the real syntax node passed in as a parameter /// Also recursively creates child nodes for all 'real' node children /// </summary> /// <param name="parent"></param> /// <param name="snapshot"></param> /// <param name="node"></param> public DesignerNode(NodeProvider provider, DesignerNode parent, ITextSnapshot snapshot, INode node) { this.provider = provider; Parent = parent; this.node = node; if (node.NodeType == NodeType.ParsingContext) { snapshotSpan = new SnapshotSpan(snapshot, node.Position + node.Length, 0); extensionSpan = new SnapshotSpan(snapshot, node.Position, node.Length); } else { snapshotSpan = new SnapshotSpan(snapshot, node.Position, node.Length); int offset = 0; if (node.Values.GetEnumerator().MoveNext()) { ITextSnapshotLine line = snapshot.GetLineFromPosition(node.Position); // if the Value list is not empty, expand the snapshotSpan // to include leading whitespaces, so that when a user // types smth in this space he will get the dropdown for (; node.Position - offset > line.Extent.Start.Position; offset++) { switch (snapshot[node.Position - offset - 1]) { case ' ': case '\t': continue; default: break; } break; } } extensionSpan = new SnapshotSpan(snapshot, node.Position - offset, offset); } foreach (IEnumerable<INode> list in node.Nodes.Values) foreach (INode child in list) children.Add(new DesignerNode(provider, this, snapshot, child)); }
static bool IsWord(ITextSnapshot snapshot, int position, int length) { bool valid = position >= 0 && length >= 0 && (uint)(position + length) <= (uint)snapshot.Length; Debug.Assert(valid); if (!valid) return false; var line = snapshot.GetLineFromPosition(position); return UnicodeUtilities.IsWord(line, position - line.Start.Position, length); }
/// <summary> /// Returns the applicable span at the provided position. /// </summary> /// <returns>A tracking span, or null if there is no token at the /// provided position.</returns> internal static ITrackingSpan GetApplicableSpan(this ITextSnapshot snapshot, int position, bool completeWord) { var classifier = snapshot.TextBuffer.GetPythonClassifier(); var line = snapshot.GetLineFromPosition(position); if (classifier == null || line == null) { return(null); } var spanLength = position - line.Start.Position; if (completeWord) { // Increase position by one to include 'fob' in: "abc.|fob" if (spanLength < line.Length) { spanLength += 1; } } var classifications = classifier.GetClassificationSpans(new SnapshotSpan(line.Start, spanLength)); // Handle "|" if (classifications == null || classifications.Count == 0) { return(null); } var lastToken = classifications[classifications.Count - 1]; // Handle "fob |" if (lastToken == null || position > lastToken.Span.End) { return(null); } if (position > lastToken.Span.Start) { if (lastToken.ClassificationType.IsOfType(PredefinedClassificationTypeNames.String)) { // Handle "'contents of strin|g" var text = lastToken.Span.GetText(); var span = GetStringContentSpan(text, lastToken.Span.Start) ?? lastToken.Span; return(snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeInclusive)); } if (lastToken.CanComplete()) { // Handle "fo|o" : when it is 'show member' or 'complete word' use complete token. // When it is autocompletion (as when typing in front of the existing contruct), take left part only. var span = completeWord ? lastToken.Span : Span.FromBounds(lastToken.Span.Start, position); return(snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeInclusive)); } // Handle "<|=" return(null); } var secondLastToken = classifications.Count >= 2 ? classifications[classifications.Count - 2] : null; if (lastToken.Span.Start == position && lastToken.CanComplete() && (secondLastToken == null || // Handle "|fob" position > secondLastToken.Span.End || // Handle "if |fob" !secondLastToken.CanComplete())) // Handle "abc.|fob" { return(snapshot.CreateTrackingSpan(lastToken.Span, SpanTrackingMode.EdgeInclusive)); } // Handle "abc|." // ("ab|c." would have been treated as "ab|c") if (secondLastToken != null && secondLastToken.Span.End == position && secondLastToken.CanComplete()) { return(snapshot.CreateTrackingSpan(secondLastToken.Span, SpanTrackingMode.EdgeInclusive)); } return(null); }
IEnumerable<DeleteHorizontalWhitespaceInfo> GetHorizontalWhiteSpaceSpans(ITextSnapshot snapshot, Span span) { // Make sure no newline character is considered whitespace Debug.Assert(!IsWhitespace('\r')); Debug.Assert(!IsWhitespace('\n')); Debug.Assert(!IsWhitespace('\u0085')); Debug.Assert(!IsWhitespace('\u2028')); Debug.Assert(!IsWhitespace('\u2029')); int spanEnd = span.End; if (spanEnd > snapshot.Length) throw new ArgumentOutOfRangeException(nameof(span)); int pos = span.Start; ITextSnapshotLine line = null; while (pos < spanEnd) { while (pos < spanEnd && !IsWhitespace(snapshot[pos])) pos++; int start = pos; while (pos < spanEnd && IsWhitespace(snapshot[pos])) pos++; int end = pos; if (start == end) { Debug.Assert(end == spanEnd); break; } if (line == null || start >= line.EndIncludingLineBreak.Position) line = snapshot.GetLineFromPosition(start); Debug.Assert(start >= line.Start.Position && end <= line.End); bool addSpace; if (start == line.Start.Position || end == line.End.Position) addSpace = false; else if (IsWhitespace(snapshot[start - 1])) addSpace = false; else if (IsWhitespace(snapshot[end])) addSpace = false; else { //TODO: sometimes all the spaces are removed "// xxx = int i 123;" // Select "xxx = int i 123;", and all spaces are removed ("xxx=inti123;") // Select the full string "// xxx = int i 123;" and whitespaces // are kept ("// xxx = int i 123;"). Execute it again, and all spaces // are removed ("//xxx=inti123;") addSpace = true; } yield return new DeleteHorizontalWhitespaceInfo(Span.FromBounds(start, end), addSpace); } Debug.Assert(pos == spanEnd); }
protected virtual bool TokenEndsAtEndOfLine(ITextSnapshot snapshot, ITokenSource lexer, IToken token) { Lexer lexerLexer = lexer as Lexer; if (lexerLexer != null) { int c = lexerLexer.CharStream.LA(1); return c == '\r' || c == '\n'; } ITextSnapshotLine line = snapshot.GetLineFromPosition(token.StopIndex + 1); return line.End <= token.StopIndex + 1 && line.EndIncludingLineBreak >= token.StopIndex + 1; }
/////////////////////////////////////////////////////////////////////////////////// // // Validate the mardown directive syntax according to the ruleset definitions // // Copyright (c) 2014 Microsoft Corporation. // Author: Junyi Yi ([email protected]) - Initial version // /////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Validate the whole document according to the specified ruleset. /// </summary> /// <param name="snapshot">The whole document snapshot.</param> /// <param name="errorTagger">The tagger used to generate error squiggles.</param> /// <param name="ruleset">The specified ruleset.</param> public static void ValidateDirectiveSyntax(ITextSnapshot snapshot, DirectiveRuleset ruleset, SimpleTagger<ErrorTag> errorTagger) { // Remove all current error squiggles errorTagger.RemoveTagSpans(errorTagSpan => true); // Get the full document text and clear all HTML tags string text = snapshot.GetText(); text = MarkdownParser.DestroyHtmlTags(text); // Three cases: // 0123456789 01234567 8 01234567 8 // [ WA ab ] [ WA ab \n [ WA ab EOT // | |-endIndex=9 | |-endIndex=8 | |-endIndex=8 // |-startIndex=0 |-startIndex=0 |-startIndex=0 // Greedily search for the pair of '[...]' (supports nested pair '[... [...] ...]') // Here 'Greedily' means if we have a string '[...[...]', it would also treat the latter '[...]' as the pair for (int startIndex = text.IndexOf('['); startIndex >= 0; startIndex = text.IndexOf('[', startIndex)) { int endIndex = MarkdownParser.FindCorrespondingEndBracket(text, startIndex + 1); // Get the directive content string ITrackingSpan overallDirective = snapshot.CreateTrackingSpan(startIndex + 1, endIndex - startIndex - 1, SpanTrackingMode.EdgeInclusive); string directive = overallDirective.GetText(snapshot); var directiveMatches = Regex.Matches(directive, string.Concat(@"^\s*(", ValidationUtilities.DirectiveNameRegularPattern, @")(.*)$")); if (directiveMatches.Count != 1 || !directiveMatches[0].Success || directiveMatches[0].Groups.Count != 3 || directiveMatches[0].Value != directive) { startIndex++; continue; } string directiveName = directiveMatches[0].Groups[1].Value; string directiveContent = directiveMatches[0].Groups[2].Value; var rule = ruleset.TryGetDirectiveRule(directiveName); if (rule != null) { // Get the preceding and following directive string of the same line ITextSnapshotLine line = snapshot.GetLineFromPosition(startIndex); string precedingText = snapshot.GetText(line.Start, startIndex - line.Start); string followingText = endIndex < line.End ? snapshot.GetText(endIndex + 1, line.End - endIndex - 1) : string.Empty; // If we found a exactly-matched rule, just validate it string message = rule.Validate(directiveContent, precedingText, followingText); if (message != null) { ITrackingSpan squiggleSpan = overallDirective; if (rule.SquiggleWholeLine) { squiggleSpan = snapshot.CreateTrackingSpan(line.Start, line.Length, SpanTrackingMode.EdgeInclusive); } errorTagger.CreateTagSpan(squiggleSpan, new ErrorTag(PredefinedErrorTypeNames.SyntaxError, message)); } // If we miss the closing bracket, give out the prompt message if (endIndex >= text.Length || text[endIndex] != ']') { errorTagger.CreateTagSpan(snapshot.CreateTrackingSpan(line.End, 0, SpanTrackingMode.EdgePositive), new ErrorTag(PredefinedErrorTypeNames.CompilerError, "Missing the closing bracket")); } } else { // Otherwise we may take a look at the suspects var suspects = ruleset.GetSuspects(directive); if (suspects.Count() > 0) { StringBuilder suspectPrompt = new StringBuilder(); suspectPrompt.AppendLine("Are you trying to enter one of the following directives?"); foreach (var suspect in suspects) { suspectPrompt.AppendLine(string.Format(" \u2022 {0} - {1}", suspect.ParentRule.DirectiveName, suspect.SuggestionMessage)); } errorTagger.CreateTagSpan(overallDirective, new ErrorTag(PredefinedErrorTypeNames.Warning, suspectPrompt.ToString().TrimEnd())); } } startIndex = endIndex; } }
public override async void DoCommand(object sender, EventArgs args) { var activeView = CommonPackage.GetActiveTextView(_serviceProvider); var project = activeView.GetProjectAtCaret(_serviceProvider); var configuration = activeView.GetInterpreterConfigurationAtCaret(_serviceProvider); ITextSelection selection = activeView.Selection; ITextSnapshot snapshot = activeView.TextBuffer.CurrentSnapshot; var workspace = _serviceProvider.GetWorkspace(); IVsInteractiveWindow repl; try { repl = ExecuteInReplCommand.EnsureReplWindow(_serviceProvider, configuration, project, workspace); } catch (MissingInterpreterException ex) { MessageBox.Show(ex.Message, Strings.ProductTitle); return; } string input; bool focusRepl = false, alwaysSubmit = false; if (selection.StreamSelectionSpan.Length > 0) { // Easy, just send the selection to the interactive window. input = activeView.Selection.StreamSelectionSpan.GetText(); if (!input.EndsWithOrdinal("\n") && !input.EndsWithOrdinal("\r")) { input += activeView.Options.GetNewLineCharacter(); } focusRepl = true; } else if (!activeView.Properties.ContainsProperty(_executedLastLine)) { // No selection, and we haven't hit the end of the file in line-by-line mode. // Send the current line, and then move the caret to the next non-blank line. ITextSnapshotLine targetLine = snapshot.GetLineFromPosition(selection.Start.Position); var targetSpan = targetLine.Extent; // If the line is inside a code cell, expand the target span to // contain the entire cell. var cellStart = CodeCellAnalysis.FindStartOfCell(targetLine); if (cellStart != null) { var cellEnd = CodeCellAnalysis.FindEndOfCell(cellStart, targetLine); targetSpan = new SnapshotSpan(cellStart.Start, cellEnd.End); targetLine = CodeCellAnalysis.FindEndOfCell(cellEnd, targetLine, includeWhitespace: true); alwaysSubmit = true; } input = targetSpan.GetText(); bool moved = false; while (targetLine.LineNumber < snapshot.LineCount - 1) { targetLine = snapshot.GetLineFromLineNumber(targetLine.LineNumber + 1); // skip over blank lines, unless it's the last line, in which case we want to land on it no matter what if (!string.IsNullOrWhiteSpace(targetLine.GetText()) || targetLine.LineNumber == snapshot.LineCount - 1) { activeView.Caret.MoveTo(new SnapshotPoint(snapshot, targetLine.Start)); activeView.Caret.EnsureVisible(); moved = true; break; } } if (!moved) { // There's no where for the caret to go, don't execute the line if // we've already executed it. activeView.Caret.PositionChanged += Caret_PositionChanged; activeView.Properties[_executedLastLine] = _executedLastLine; } } else if ((repl.InteractiveWindow.CurrentLanguageBuffer?.CurrentSnapshot.Length ?? 0) != 0) { // We reached the end of the file but have some text buffered. Execute it now. input = activeView.Options.GetNewLineCharacter(); } else { // We've hit the end of the current text view and executed everything input = null; } if (input != null) { repl.Show(focusRepl); var inputs = repl.InteractiveWindow.Properties.GetOrCreateSingletonProperty( () => new InteractiveInputs(repl.InteractiveWindow, _serviceProvider, alwaysSubmit) ); await inputs.EnqueueAsync(input); } // Take focus back if REPL window has stolen it and we're in line-by-line mode. if (!focusRepl && !activeView.HasAggregateFocus) { var adapterService = _serviceProvider.GetComponentModel().GetService <VisualStudio.Editor.IVsEditorAdaptersFactoryService>(); var tv = adapterService.GetViewAdapter(activeView); tv.SendExplicitFocus(); } }
public SourcePos IndexToLine(int index) { var line = _ss.GetLineFromPosition(index); return(new SourcePos("", line.LineNumber + 1, index - line.Start.Position + 1)); }
public static string GetLineTextFromPosition(int position, ITextSnapshot snapshot) { return snapshot.GetLineFromPosition(position - 1).GetText(); }
static int GetLineNumber(ITextSnapshot snapshot, int position) { Debug.Assert((uint)position <= (uint)snapshot.Length); if ((uint)position > (uint)snapshot.Length) return int.MaxValue; return snapshot.GetLineFromPosition(position).LineNumber; }
// This function could use some refinement as it will return some false positives, // but hopefully they are rare enough to not cause any major issues. I would much // rather return a couple false positives than false negatives. private static int GetTypeScriptFunctionLine(ITextSnapshot capture, int lineNumber, bool isAboveFunction, string lineText) { if (!isAboveFunction) { while (!lineText.Contains('(') && !typeScriptFnRegex.IsMatch(lineText)) { lineNumber--; lineText = capture.GetLineFromLineNumber(lineNumber).Extent.GetText(); //There is no function declaration associated with the curly brace. if (lineText.Contains('{')) return -1; } return lineNumber; } else { if (typeScriptFnRegex.IsMatch(lineText)) { return lineNumber; } if (keywordWithParen.IsMatch(lineText)) { return -1; } var line = capture.GetLineFromLineNumber(lineNumber); var parenOpen = lineText.IndexOf("("); var parenBlock = GetCompleteParenBlock(capture, lineNumber, line.Start + lineText.IndexOf("(")); if (parenBlock == null) { return -1; } var endParamsPosition = line.Start.Position + parenOpen + parenBlock.Length; var lineEnd = capture.GetLineFromPosition(endParamsPosition); var startTextAfterParams = endParamsPosition + 1; var textAfterParams = capture.GetText(startTextAfterParams, lineEnd.End.Position - startTextAfterParams); var lineCounter = lineEnd.LineNumber; while (!lineText.Contains('{') && lineCounter < capture.LineCount) { lineCounter++; lineText = capture.GetLineFromLineNumber(lineCounter).Extent.GetText(); textAfterParams += lineText; } if (!lineText.Contains('{')) { return -1; } textAfterParams = textAfterParams.Substring(0, textAfterParams.LastIndexOf('{')); // If there is no text between the ) and {, then we know it is a valid function header. if (String.IsNullOrWhiteSpace(textAfterParams)) { return lineNumber; } // If there is text between ) {, check if it is a return type declaration. if (textAfterParams.Trim().StartsWith(":")) { return lineNumber; } return -1; } }
/// <summary> /// Returns the applicable span at the provided position. /// </summary> /// <returns>A tracking span, or null if there is no token at the /// provided position.</returns> public static ITrackingSpan GetApplicableSpan(this ITextSnapshot snapshot, int position) { //var classifier = snapshot.TextBuffer.GetPythonClassifier(); IClassifier classifier = null; var line = snapshot.GetLineFromPosition(position); if (classifier == null || line == null) { return(null); } var spanLength = position - line.Start.Position; // Increase position by one to include 'fob' in: "abc.|fob" if (spanLength < line.Length) { spanLength += 1; } var classifications = classifier.GetClassificationSpans(new SnapshotSpan(line.Start, spanLength)); // Handle "|" if (classifications == null || classifications.Count == 0) { return(null); } var lastToken = classifications[classifications.Count - 1]; // Handle "fob |" if (lastToken == null || position > lastToken.Span.End) { return(null); } if (position > lastToken.Span.Start) { if (lastToken.CanComplete()) { // Handle "fo|o" return(snapshot.CreateTrackingSpan(lastToken.Span, SpanTrackingMode.EdgeInclusive)); } else { // Handle "<|=" return(null); } } var secondLastToken = classifications.Count >= 2 ? classifications[classifications.Count - 2] : null; if (lastToken.Span.Start == position && lastToken.CanComplete() && (secondLastToken == null || // Handle "|fob" position > secondLastToken.Span.End || // Handle "if |fob" !secondLastToken.CanComplete())) { // Handle "abc.|fob" return(snapshot.CreateTrackingSpan(lastToken.Span, SpanTrackingMode.EdgeInclusive)); } // Handle "abc|." // ("ab|c." would have been treated as "ab|c") if (secondLastToken != null && secondLastToken.Span.End == position && secondLastToken.CanComplete()) { return(snapshot.CreateTrackingSpan(secondLastToken.Span, SpanTrackingMode.EdgeInclusive)); } return(null); }
public IEditorLine GetLineFromPosition(int position) => new EditorLine(_snapshot.GetLineFromPosition(position));
internal void OnVisualBufferChanged(object sender, TextContentChangedEventArgs e) { if (textSnapshot != null) { foreach (ITextChange textChange in e.Changes) { Span textChangeCurrentSpan; if (e.Before == textSnapshot) { textChangeCurrentSpan = textChange.OldSpan; } else if (e.After == textSnapshot) { textChangeCurrentSpan = textChange.NewSpan; } else { ITrackingSpan textChangeOldSpan = e.Before.CreateTrackingSpan(textChange.OldSpan, SpanTrackingMode.EdgeInclusive); textChangeCurrentSpan = textChangeOldSpan.GetSpan(textSnapshot); } var startLine = textSnapshot.GetLineFromPosition(textChangeCurrentSpan.Start); var endLine = startLine; if (startLine.EndIncludingLineBreak.Position < textChangeCurrentSpan.End) { endLine = textSnapshot.GetLineFromPosition(textChangeCurrentSpan.End); } for (int i = startLine.LineNumber; i <= endLine.LineNumber; i++) { modifiedLinesCache.Add(i); } } } // Recreate MdTextViewLine for the current snapshot for all lines except those // modified as those will get recreated during render foreach (MdTextViewLine line in this) { int lineNumber = line.LineNumber; if (!modifiedLinesCache.Contains(lineNumber - 1)) { reusedLinesCache.Add(line); } } this.Clear(); var newSnapshot = e.After; foreach (MdTextViewLine line in reusedLinesCache) { var snapshotLine = textSnapshot.GetLineFromLineNumber(line.LineNumber - 1); var lineStart = textSnapshot.CreateTrackingPoint(snapshotLine.Start, PointTrackingMode.Negative); var newLineStart = lineStart.GetPosition(newSnapshot); var newSnapshotLine = newSnapshot.GetLineFromPosition(newLineStart); int lineNumber = newSnapshotLine.LineNumber + 1; var documentLine = textEditor.Document.GetLine(lineNumber); var newLine = new MdTextViewLine(this, textEditor, documentLine, lineNumber, textEditor.TextViewMargin.GetLayout(documentLine)); Add(newLine); } modifiedLinesCache.Clear(); reusedLinesCache.Clear(); textSnapshot = newSnapshot; // we need this to synchronize MultiSelectionBroker to the new snapshot this.textEditor.TextArea.RaiseLayoutChanged(); }
private static SnapshotPoint Scan(string txt, SnapshotPoint start, List <TokenRegion> newRegions, ITextSnapshot newSnapshot) { int longCommentDepth = 0; SnapshotPoint commentStart = new SnapshotPoint(); SnapshotPoint commentEndAsWeKnowIt = new SnapshotPoint(); // used only when longCommentDepth != 0 int N = txt.Length; bool done = false; while (!done) { N = txt.Length; // length of the current buffer int cur = 0; // offset into the current buffer if (longCommentDepth != 0) { ScanForEndOfComment(txt, ref longCommentDepth, ref cur); if (longCommentDepth == 0) { // we just finished parsing a long comment newRegions.Add(new TokenRegion(commentStart, new SnapshotPoint(newSnapshot, start + cur), DafnyTokenKind.Comment)); } else { // we're still parsing the long comment Contract.Assert(cur == txt.Length); commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, start + cur); goto OUTER_CONTINUE; } } // repeatedly get the remaining tokens from this buffer int end; // offset into the current buffer for (; ; cur = end) { // advance to the first character of a keyword or token DafnyTokenKind ty = DafnyTokenKind.Keyword; for (; ; cur++) { if (N <= cur) { // we've looked at everything in this buffer goto OUTER_CONTINUE; } char ch = txt[cur]; if ('a' <= ch && ch <= 'z') { break; } if ('A' <= ch && ch <= 'Z') { break; } if ('0' <= ch && ch <= '9') { ty = DafnyTokenKind.Number; break; } if (ch == '_' || ch == '?' || ch == '\\') { break; // parts of identifiers } if (ch == '\'') { ty = DafnyTokenKind.Char; break; } // part character literal or identifier if (ch == '"') { ty = DafnyTokenKind.String; break; } if (ch == '/') { ty = DafnyTokenKind.Comment; break; } } // advance to the end of the token end = cur + 1; // offset into the current buffer // first investigate if this is really a character literal if (ty == DafnyTokenKind.Char) { ty = DafnyTokenKind.Keyword; // we've seen a starting single-quote already if (cur + 3 <= N && txt[cur + 2] == '\'') { // Look for a simple character literal, like 'a' char cx = txt[cur + 1]; if (cx != '\'' && cx != '\\' && cx != '\n' && cx != '\r') { if (cur + 3 == N) { ty = DafnyTokenKind.Char; end = cur + 3; } else { // check if the next character is an identifier character, because then what we've seen was // really just part of that identifier cx = txt[cur + 3]; if ('a' <= cx && cx <= 'z') { } else if ('A' <= cx && cx <= 'Z') { } else if ('0' <= cx && cx <= '9') { } else if (cx == '\'' || cx == '_' || cx == '?' || cx == '\\') { } else { ty = DafnyTokenKind.Char; end = cur + 3; } } } } else if (cur + 4 <= N && txt[cur + 1] == '\\' && txt[cur + 3] == '\'') { // Look for an escaped character literal, like '\n' (note, a \ cannot be part of an identifier) char cx = txt[cur + 2]; if (cx == '\'' || cx == '\"' || cx == '\\' || cx == '0' || cx == 'n' || cx == 'r' || cx == 't') { ty = DafnyTokenKind.Char; end = cur + 4; } } else if (cur + 8 <= N && txt[cur + 1] == '\\' && txt[cur + 2] == 'u' && txt[cur + 7] == '\'') { // Look for a unicode character literal, like '\u40fE' (note, a \ cannot be part of an identifier) var numberOfHexDigits = 0; for (int i = 3; i < 7; i++) { char cx = txt[cur + i]; if (('0' <= cx && cx <= '9') || ('a' <= cx && cx <= 'f') || ('A' <= cx && cx <= 'F')) { numberOfHexDigits++; } } if (numberOfHexDigits == 4) { ty = DafnyTokenKind.Char; end = cur + 8; } } } if (ty == DafnyTokenKind.Number) { // scan the rest of this number for (; end < N; end++) { char ch = txt[end]; if ('0' <= ch && ch <= '9') { } else { break; } } } else if (ty == DafnyTokenKind.Char) { // we already did the work above } else if (ty == DafnyTokenKind.String) { // scan the rest of this string, but not past the end-of-buffer for (; end < N; end++) { char ch = txt[end]; if (ch == '"') { end++; break; } else if (ch == '\\') { // escape sequence end++; if (end == N) { break; } ch = txt[end]; if (ch == 'u') { end += 4; if (N <= end) { end = N; break; } } } } } else if (ty == DafnyTokenKind.Comment) { if (end == N) { continue; // this was not the start of a comment; it was just a single "/" and we don't care to color it } char ch = txt[end]; if (ch == '/') { // a short comment, to the end of the line. end = newSnapshot.GetLineFromPosition(start + end).End.Position - start; } else if (ch == '*') { // a long comment; find the matching "*/" end++; commentStart = new SnapshotPoint(newSnapshot, start + cur); Contract.Assert(longCommentDepth == 0); longCommentDepth = 1; ScanForEndOfComment(txt, ref longCommentDepth, ref end); if (longCommentDepth == 0) { // we finished scanning a long comment, and "end" is set to right after it newRegions.Add(new TokenRegion(commentStart, new SnapshotPoint(newSnapshot, start + end), DafnyTokenKind.Comment)); } else { commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, start + end); } continue; } else { // not a comment; it was just a single "/" and we don't care to color it continue; } } else { int trailingDigits = 0; for (; end < N; end++) { char ch = txt[end]; if ('a' <= ch && ch <= 'z') { trailingDigits = 0; } else if ('A' <= ch && ch <= 'Z') { trailingDigits = 0; } else if ('0' <= ch && ch <= '9') { trailingDigits++; } else if (ch == '\'' || ch == '_' || ch == '?' || ch == '\\') { trailingDigits = 0; } else { break; } } // we have a keyword or an identifier string s = txt.Substring(cur, end - cur); if (0 < trailingDigits && s.Length == 5 + trailingDigits && s.StartsWith("array") && s[5] != '0' && (trailingDigits != 1 || s[5] != '1')) { // this is a keyword for a built-in type (array2, array3, ...) ty = DafnyTokenKind.BuiltInType; } else if (0 < trailingDigits && s.Length == 2 + trailingDigits && s.StartsWith("bv") && (s[2] != '0' || trailingDigits == 1)) { // this is a keyword for a built-in type (bv0, bv1, ...) ty = DafnyTokenKind.BuiltInType; } else { switch (s) { #region keywords case "abstract": case "allocated": case "as": case "assert": case "assume": case "break": case "by": case "calc": case "case": case "class": case "const": case "trait": case "extends": case "codatatype": case "colemma": case "constructor": case "copredicate": case "datatype": case "else": case "exists": case "export": case "false": case "forall": case "fresh": case "function": case "ghost": case "if": case "import": case "in": case "include": case "inductive": case "iterator": case "label": case "lemma": case "match": case "method": case "modify": case "module": case "new": case "newtype": case "null": case "old": case "opened": case "predicate": case "print": case "protected": case "refines": case "return": case "returns": case "static": case "then": case "this": case "true": case "twostate": case "type": case "unchanged": case "var": case "where": case "while": case "yield": case "yields": #endregion break; #region keywords in specification clauses case "decreases": case "ensures": case "invariant": case "modifies": case "provides": case "reads": case "requires": case "reveals": case "witness": // "yields" plays a dual role #endregion ty = DafnyTokenKind.SpecificationClause; break; #region keywords for built-in types case "array": case "bool": case "char": case "imap": case "int": case "iset": case "map": case "multiset": case "nat": case "object": case "real": case "seq": case "set": case "string": #endregion ty = DafnyTokenKind.BuiltInType; break; default: continue; // it was an identifier, so we don't color it } } } newRegions.Add(new TokenRegion(new SnapshotPoint(newSnapshot, start + cur), new SnapshotPoint(newSnapshot, start + end), ty)); } OUTER_CONTINUE: done = true; if (longCommentDepth != 0) { // we need to look into the next line ITextSnapshotLine currLine = newSnapshot.GetLineFromPosition(start + N); if ((currLine.LineNumber + 1) < newSnapshot.LineCount) { ITextSnapshotLine nextLine = newSnapshot.GetLineFromLineNumber(currLine.LineNumber + 1); txt = nextLine.GetText(); start = nextLine.Start; // we are done scanning the current buffer, but not the whole file yet. // we need to continue to find the enclosing "*/", or until the end of the file. done = false; } else { // This was a malformed comment, running to the end of the buffer. Above, we let "commentEndAsWeKnowIt" be the end of the // last line, so we can use it here. newRegions.Add(new TokenRegion(commentStart, commentEndAsWeKnowIt, DafnyTokenKind.Comment)); } } } return(new SnapshotPoint(newSnapshot, start + N)); }
/// <summary> /// Find all of the tag regions in the document (snapshot) and notify /// listeners of any that changed /// </summary> void ReparseFile(object sender, TextContentChangedEventArgs args) { ITextSnapshot snapshot = _buffer.CurrentSnapshot; if (snapshot == _snapshot) { return; // we've already computed the regions for this snapshot } NormalizedSnapshotSpanCollection difference = new NormalizedSnapshotSpanCollection(); ScanResult result; if (_buffer.Properties.TryGetProperty(bufferTokenTaggerKey, out result) && (result._oldSnapshot == _snapshot) && (result._newSnapshot == snapshot)) { difference = result._difference; // save the new baseline _regions = result._regions; _snapshot = snapshot; } else { List <TokenRegion> regions = new List <TokenRegion>(); List <SnapshotSpan> rescannedRegions = new List <SnapshotSpan>(); // loop through the changes and check for changes in comments first. If // the change is in a comments, we need to rescan starting from the // beginning of the comments (which in multi-lined comments, it can // be a line that the changes are not on), otherwise, we can just rescan the lines // that the changes are on. bool done; SnapshotPoint start, end; for (int i = 0; i < args.Changes.Count; i++) { done = false; // get the span of the lines that the change is on. int cStart = args.Changes[i].NewSpan.Start; int cEnd = args.Changes[i].NewSpan.End; start = snapshot.GetLineFromPosition(cStart).Start; end = snapshot.GetLineFromPosition(cEnd).End; SnapshotSpan newSpan = new SnapshotSpan(start, end); foreach (TokenRegion r in _regions) { if (r.Kind == DafnyTokenKind.Comment) { // if the change is in the comments, we want to start scanning from the // the beginning of the comments instead. SnapshotSpan span = r.Span.TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive); if (span.IntersectsWith(newSpan)) { start = span.Start.Position < newSpan.Start.Position ? span.Start : newSpan.Start; end = span.End.Position > newSpan.End.Position ? span.End : newSpan.End; end = Scan(snapshot.GetText(new SnapshotSpan(start, end)), start, regions, snapshot); // record the regions that we rescanned. rescannedRegions.Add(new SnapshotSpan(start, end)); done = true; break; } } } if (!done) { // scan the lines that the change is on to generate the new regions. end = Scan(snapshot.GetText(new SnapshotSpan(start, end)), start, regions, snapshot); // record the span that we rescanned. rescannedRegions.Add(new SnapshotSpan(start, end)); } } List <SnapshotSpan> oldSpans = new List <SnapshotSpan>(); List <SnapshotSpan> newSpans = new List <SnapshotSpan>(); // record the newly created spans. foreach (TokenRegion r in regions) { newSpans.Add(r.Span); } // loop through the old scan results and remove the ones that // are in the regions that are rescanned. foreach (TokenRegion r in _regions) { SnapshotSpan origSpan = r.Span.TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive); bool obsolete = false; foreach (SnapshotSpan span in rescannedRegions) { if (origSpan.IntersectsWith(span)) { oldSpans.Add(span); obsolete = true; break; } } if (!obsolete) { TokenRegion region = new TokenRegion(origSpan.Start, origSpan.End, r.Kind); regions.Add(region); } } NormalizedSnapshotSpanCollection oldSpanCollection = new NormalizedSnapshotSpanCollection(oldSpans); NormalizedSnapshotSpanCollection newSpanCollection = new NormalizedSnapshotSpanCollection(newSpans); difference = SymmetricDifference(oldSpanCollection, newSpanCollection); // save the scan result _buffer.Properties[bufferTokenTaggerKey] = new ScanResult(_snapshot, snapshot, regions, difference); // save the new baseline _snapshot = snapshot; _regions = regions; } var chng = TagsChanged; if (chng != null) { foreach (var span in difference) { chng(this, new SnapshotSpanEventArgs(span)); } } }
public static string GetLineTextFromPosition(int position, ITextSnapshot snapshot) { return(snapshot.GetLineFromPosition(position - 1).GetText()); }
private static int GetFirstArgumentIndent(ITextSnapshot snapshot, IFunction fc) { var line = snapshot.GetLineFromPosition(fc.OpenBrace.End); return(fc.OpenBrace.End - line.Start); }
public static int GetLineColumnFromPosition(this ITextSnapshot snapshot, int caretPosition) { var snapshotLine = snapshot.GetLineFromPosition(caretPosition); return(caretPosition - snapshotLine.Start.Position); }
/// <summary> /// Returns the applicable span at the provided position. /// </summary> /// <returns>A tracking span, or null if there is no token at the /// provided position.</returns> internal static ITrackingSpan GetApplicableSpan(ITextSnapshot snapshot, int position) { var classifier = snapshot.TextBuffer.GetNodejsClassifier(); var line = snapshot.GetLineFromPosition(position); if (classifier == null || line == null) { return null; } var spanLength = position - line.Start.Position; // Increase position by one to include 'fob' in: "abc.|fob" if (spanLength < line.Length) { spanLength += 1; } var classifications = classifier.GetClassificationSpans(new SnapshotSpan(line.Start, spanLength)); // Handle "|" if (classifications == null || classifications.Count == 0) { return null; } var lastToken = classifications[classifications.Count - 1]; // Handle "fob |" if (lastToken == null || position > lastToken.Span.End) { return null; } if (position > lastToken.Span.Start) { if (lastToken.CanComplete()) { // Handle "fo|o" return snapshot.CreateTrackingSpan(lastToken.Span, SpanTrackingMode.EdgeInclusive); } else { // Handle "<|=" return null; } } var secondLastToken = classifications.Count >= 2 ? classifications[classifications.Count - 2] : null; if (lastToken.Span.Start == position && lastToken.CanComplete() && (secondLastToken == null || // Handle "|fob" position > secondLastToken.Span.End || // Handle "if |fob" !secondLastToken.CanComplete())) { // Handle "abc.|fob" return snapshot.CreateTrackingSpan(lastToken.Span, SpanTrackingMode.EdgeInclusive); } // Handle "abc|." // ("ab|c." would have been treated as "ab|c") if (secondLastToken != null && secondLastToken.Span.End == position && secondLastToken.CanComplete()) { return snapshot.CreateTrackingSpan(secondLastToken.Span, SpanTrackingMode.EdgeInclusive); } return null; }
private void GetTrackingSpan(ITextSnapshot snapshot, int triggerPoint) { ITextSnapshotLine line = snapshot.GetLineFromPosition(triggerPoint); string lineString = line.GetText(); var stopChars = new char[] {' ', '\t', '{', '}', '.', '"', ':'}; int start = lineString.Substring(0, triggerPoint - line.Start.Position).LastIndexOfAny(stopChars) + line.Start.Position + 1; int length = lineString.Substring(triggerPoint - line.Start.Position).IndexOfAny(stopChars) + triggerPoint - start; _trackingSpan = snapshot.CreateTrackingSpan(start, length < 0 ? 0 : length, SpanTrackingMode.EdgeInclusive); }
private bool TryAsSwitchSection(SyntaxNode childnode, ITextSnapshot snapshot, ref BlockType type, ref int startPosition, ref int endPosition) { var child = childnode as SwitchSectionSyntax; if (child != null) { type = BlockType.Conditional; startPosition = child.Labels.FullSpan.End; ITextSnapshotLine line = snapshot.GetLineFromPosition(startPosition); if ((startPosition == line.Start.Position) && (line.LineNumber > 0)) { startPosition = snapshot.GetLineFromLineNumber(line.LineNumber - 1).End; } endPosition = child.Span.End; return true; } return false; }
private static int GetFirstArgumentIndent(ITextSnapshot snapshot, IFunction fc) { var line = snapshot.GetLineFromPosition(fc.OpenBrace.End); return fc.OpenBrace.End - line.Start; }
private void AddAllGuids(ITextSnapshot snapshot, List<Intel.Completion> list) { var guidSpans = _classifier.GetClassificationSpans(new SnapshotSpan(snapshot, 0, snapshot.Length)).Where(g => g.ClassificationType.IsOfType(PkgdefClassificationTypes.Guid)); Dictionary<string, Tuple<string, ImageSource>> dic = new Dictionary<string, Tuple<string, ImageSource>>(); var unknown = _glyphService.GetGlyph(StandardGlyphGroup.GlyphGroupModule, StandardGlyphItem.TotalGlyphItems); foreach (var cspan in guidSpans) { string guid = cspan.Span.GetText(); ITextSnapshotLine line = snapshot.GetLineFromPosition(cspan.Span.Start.Position); string lineText = line.GetText(); Guid check = Guid.Empty; if (!dic.ContainsKey(guid) && Guid.TryParse(guid, out check)) { dic[guid] = null; } if (check != Guid.Empty && (!dic.ContainsKey(guid) || dic[guid] == null || !dic[guid].Item1.Contains("\\"))) { string before = lineText.Substring(0, lineText.IndexOf(guid)).Trim(); string text = before; var glyph = unknown; Match entryKey = Regex.Match(text, "\"(?<key>[^\"]+)\""); if (entryKey.Success) { text = entryKey.Value + "="; } string name; glyph = GetGlyph(lineText, glyph, out name); if (glyph == null || text == "\"") continue; if (!string.IsNullOrEmpty(name)) name += Environment.NewLine; dic[guid] = Tuple.Create(name + text + Environment.NewLine + "Line: " + line.LineNumber, glyph); } } List<Completion> entries = new List<Completion>(); foreach (string guid in dic.Keys) { if (dic[guid] != null) entries.Add(CreateCompletion(guid, guid, dic[guid].Item2, dic[guid].Item1)); } list.AddRange(entries.OrderByDescending(e => e.Description)); }
// This function could use some refinement as it will return some false positives, // but hopefully they are rare enough to not cause any major issues. I would much // rather return a couple false positives than false negatives. private static int GetTypeScriptFunctionLine(ITextSnapshot capture, int lineNumber, bool isAboveFunction, string lineText) { if (!isAboveFunction) { while (!lineText.Contains('(') && !typeScriptFnRegex.IsMatch(lineText)) { lineNumber--; lineText = capture.GetLineFromLineNumber(lineNumber).Extent.GetText(); //There is no function declaration associated with the curly brace. if (lineText.Contains('{')) { return(-1); } } return(lineNumber); } else { if (typeScriptFnRegex.IsMatch(lineText)) { return(lineNumber); } if (keywordWithParen.IsMatch(lineText)) { return(-1); } var line = capture.GetLineFromLineNumber(lineNumber); var parenOpen = lineText.IndexOf("("); var parenBlock = GetCompleteParenBlock(capture, lineNumber, line.Start + lineText.IndexOf("(")); if (parenBlock == null) { return(-1); } var endParamsPosition = line.Start.Position + parenOpen + parenBlock.Length; var lineEnd = capture.GetLineFromPosition(endParamsPosition); var startTextAfterParams = endParamsPosition + 1; var textAfterParams = capture.GetText(startTextAfterParams, lineEnd.End.Position - startTextAfterParams); var lineCounter = lineEnd.LineNumber; while (!lineText.Contains('{') && lineCounter < capture.LineCount) { lineCounter++; lineText = capture.GetLineFromLineNumber(lineCounter).Extent.GetText(); textAfterParams += lineText; } if (!lineText.Contains('{')) { return(-1); } textAfterParams = textAfterParams.Substring(0, textAfterParams.LastIndexOf('{')); // If there is no text between the ) and {, then we know it is a valid function header. if (String.IsNullOrWhiteSpace(textAfterParams)) { return(lineNumber); } // If there is text between ) {, check if it is a return type declaration. if (textAfterParams.Trim().StartsWith(":")) { return(lineNumber); } return(-1); } }