public override bool InsertIndent() { int tabSize = _editorOptions.GetTabSize(); int spacesToInsert = tabSize - (Column % tabSize); string indentToInsert = _editorOptions.IsConvertTabsToSpacesEnabled() ? new string(' ', spacesToInsert) : "\t"; return(InsertText(indentToInsert)); }
private string GetIndentationString(IEditorOptions options, int indentationLength) { if (indentationLength <= 0) { return(string.Empty); } StringBuilder builder = new StringBuilder(); int num = 0; int num2 = indentationLength; if (!options.IsConvertTabsToSpacesEnabled()) { int tabSize = options.GetTabSize(); num = indentationLength / tabSize; num2 -= num * tabSize; } for (int i = 0; i < num; i++) { builder.Append('\t'); } for (int j = 0; j < num2; j++) { builder.Append(' '); } return(builder.ToString()); }
public void SubjectBuffersConnected(IWpfTextView textView, ConnectionReason reason, Collection <ITextBuffer> subjectBuffers) { ITextDocument textDocument; if (_textDocumentFactoryService.TryGetTextDocument(textView.TextBuffer, out textDocument)) { textDocument.FileActionOccurred += delegate(object sender, TextDocumentFileActionEventArgs e) { if (e.FileActionType != FileActionTypes.ContentSavedToDisk) { return; } ITextDocument document = (ITextDocument)sender; ITextBuffer buffer = document.TextBuffer; string filePath = e.FilePath; FixBufferOnSave(buffer, textView.FormattedLineSource.TabSize, filePath); }; } foreach (var buffer in subjectBuffers) { IEditorOptions options = textView.Properties.GetProperty <IEditorOptions>(typeof(IEditorOptions)); int tabSize = options.GetTabSize(); FixBufferOnLoad(buffer, tabSize); } if (textDocument != null) { textDocument.UpdateDirtyState(false, DateTime.Now); } }
//FIXME: make this smarter, it's very simple right now public virtual int?GetDesiredIndentation(ITextSnapshotLine line) { if (!XmlBackgroundParser.TryGetParser(textView.TextBuffer, out var parser)) { return(null); } var indentSize = options.GetIndentSize(); var tabSize = options.GetTabSize(); //calculate the delta between the previous line's expected and actual indent int?previousIndentDelta = null; // find a preceding non-empty line so we don't get confused by blank lines with virtual indents var previousLine = GetPreviousNonEmptyLine(line); if (previousLine != null) { var previousExpectedIndent = GetLineExpectedIndent(previousLine, parser, indentSize); var previousActualIndent = GetLineActualIndent(previousLine, tabSize); previousIndentDelta = previousActualIndent - previousExpectedIndent; } var indent = GetLineExpectedIndent(line, parser, indentSize); // if the previous non-blank line was in the same state and had a different indent than // expected, the user has manually corrected it, so re-apply the same delta to this line. if (previousIndentDelta.HasValue) { indent = Math.Max(0, indent + previousIndentDelta.Value); } return(indent); }
/// <summary> Adds edits to comment out each non-blank line, at the given column. </summary> void ApplyCommentToNonBlankLines(IEditorOptions options, ITextEdit textEdit, Tuple <ITextSnapshotLine, ITextSnapshotLine> firstAndLastLine, int indentToColumn) { for (int lineNumber = firstAndLastLine.Item1.LineNumber; lineNumber <= firstAndLastLine.Item2.LineNumber; ++lineNumber) { var line = firstAndLastLine.Item1.Snapshot.GetLineFromLineNumber(lineNumber); if (!line.IsEmptyOrWhitespace()) { var offset = line.GetOffsetForColumn(indentToColumn, options.GetTabSize()); textEdit.Insert(line.Start + offset, SyntaxFacts.SingleLineComment); } } }
private int GetOffsetFromIndentation(int indentation, IEditorOptions option) { int numberOfTabs = 0; int numberOfSpaces = Math.Max(0, indentation); if (!option.IsConvertTabsToSpacesEnabled()) { var tabSize = option.GetTabSize(); numberOfTabs = indentation / tabSize; numberOfSpaces -= numberOfTabs * tabSize; } return(numberOfTabs + numberOfSpaces); }
/// <summary> Given a set of lines, find the minimum indent of all of the non-blank, non-whitespace lines.</summary> static int DetermineSmallestSignificantColumn(IEditorOptions options, SnapshotSpan span, Tuple <ITextSnapshotLine, ITextSnapshotLine> firstAndLastLine) { var tabSize = options.GetTabSize(); var indentToCommentAt = int.MaxValue; for (int lineNumber = firstAndLastLine.Item1.LineNumber; lineNumber <= firstAndLastLine.Item2.LineNumber; ++lineNumber) { var line = span.Snapshot.GetLineFromLineNumber(lineNumber); var significantColumn = line.GetIndentationColumn(tabSize) ?? Int32.MaxValue; indentToCommentAt = Math.Min(indentToCommentAt, significantColumn); } return(indentToCommentAt); }
static string GetIndentString(int indent, IEditorOptions options) { if (options.IsConvertTabsToSpacesEnabled()) { return(new string (' ', indent)); } var tabSize = options.GetTabSize(); int tabs = indent / tabSize; int spaces = indent - tabs * tabSize; var indentStr = new string ('\t', tabs); if (spaces > 0) { indentStr += new string (' ', spaces); } return(indentStr); }
public bool TryGetDocumentOption(Document document, OptionKey option, OptionSet underlyingOptions, out object value) { // From the document, look up the ITextBuffer, and find its IEditorOptions. // Note that IEditorOptions are also available from an ITextView. Not sure if those options can differ from the ones on the buffer. var workspace = PrimaryWorkspace.Workspace as VisualStudioWorkspace; var textBuffer = workspace?.GetTextBufferForDocument(document.Id); IEditorOptions editorOptions = textBuffer?.Properties[typeof(IEditorOptions)] as IEditorOptions; if (editorOptions == null) { value = null; return(false); } // Check if the OptionKey is one of the ones we want to override with values from the IEditorOptions if (option.Option == FormattingOptions.UseTabs) { value = !editorOptions.IsConvertTabsToSpacesEnabled(); return(true); } else if (option.Option == FormattingOptions.TabSize) { value = editorOptions.GetTabSize(); return(true); } else if (option.Option == FormattingOptions.IndentationSize) { value = editorOptions.GetIndentSize(); return(true); } else if (option.Option == FormattingOptions.NewLine) { value = editorOptions.GetNewLineCharacter(); return(true); } else { value = null; return(false); } }
public CSharpOutliningTagger(ITextBuffer buffer, IClassifier classifier, IEditorOptions editorOptions) { this.Buffer = buffer; this.Snapshot = buffer.CurrentSnapshot; this.Classifier = classifier; this.Buffer.Changed += BufferChanged; this.EditorOptions = editorOptions; // need Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods namespace to work this.TabSize = editorOptions.GetTabSize(); //this.Classifier.ClassificationChanged += BufferChanged; //timer that will trigger outlining update after some period of no buffer changes UpdateTimer = new DispatcherTimer(DispatcherPriority.ApplicationIdle); UpdateTimer.Interval = TimeSpan.FromMilliseconds(2500); UpdateTimer.Tick += (sender, args) => { UpdateTimer.Stop(); this.Outline(); }; this.Outline(); // Force an initial full parse }
private static int DetermineIndentationColumn( IEditorOptions editorOptions, IEnumerable <SnapshotSpan> spans) { int?indentationColumn = null; foreach (SnapshotSpan span in spans) { ITextSnapshot snapshot = span.Snapshot; int startLineNumber = snapshot.GetLineNumberFromPosition(span.Start); int endLineNumber = snapshot.GetLineNumberFromPosition(span.End); // If the span starts after the first non-whitespace of the first line, we'll // exclude that line to avoid throwing off the calculation. Otherwise, the // incorrect indentation will be returned for lambda cases like so: // // void M() // { // Func<int> f = () => // { // return 1; // }; // } // // Without throwing out the first line in the example above, the indentation column // used will be 4, rather than 8. int?startLineFirstNonWhitespace = snapshot.GetLineFromLineNumber(startLineNumber).GetFirstNonWhitespacePosition(); if (startLineFirstNonWhitespace.HasValue && startLineFirstNonWhitespace.Value < span.Start) { startLineNumber++; } for (int lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { ITextSnapshotLine line = snapshot.GetLineFromLineNumber(lineNumber); if (string.IsNullOrWhiteSpace(line.GetText())) { continue; } indentationColumn = indentationColumn.HasValue ? Math.Min(indentationColumn.Value, line.GetText().GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(editorOptions.GetTabSize())) : line.GetText().GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(editorOptions.GetTabSize()); } } return(indentationColumn ?? 0); }
public static int GetLineOffsetFromColumn(this ITextSnapshotLine line, int column, IEditorOptions editorOptions) { return(line.GetText().GetLineOffsetFromColumn(column, editorOptions.GetTabSize())); }
public static int GetColumnFromLineOffset(this ITextSnapshotLine line, int lineOffset, IEditorOptions editorOptions) { return(line.GetText().GetColumnFromLineOffset(lineOffset, editorOptions.GetTabSize())); }
public static int GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(this ITextSnapshotLine line, IEditorOptions editorOptions) { return(line.GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(editorOptions.GetTabSize())); }
private static int CalculateIndentation(string baseline, ITextSnapshotLine line, IEditorOptions options, IClassifier classifier, ITextView textView) { int indentation = GetIndentation(baseline, options.GetTabSize()); int tabSize = options.GetIndentSize(); var tokens = classifier.GetClassificationSpans(line.Extent); if (tokens.Count > 0 && !IsUnterminatedStringToken(tokens[tokens.Count - 1])) { int tokenIndex = tokens.Count - 1; while (tokenIndex >= 0 && (tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment) || tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.WhiteSpace))) { tokenIndex--; } if (tokenIndex < 0) { return(indentation); } if (Genero4glReverseParser.IsExplicitLineJoin(tokens[tokenIndex])) { // explicit line continuation, we indent 1 level for the continued line unless // we're already indented because of multiple line continuation characters. indentation = GetIndentation(line.GetText(), options.GetTabSize()); var joinedLine = tokens[tokenIndex].Span.Start.GetContainingLine(); if (joinedLine.LineNumber > 0) { var prevLineSpans = classifier.GetClassificationSpans(tokens[tokenIndex].Span.Snapshot.GetLineFromLineNumber(joinedLine.LineNumber - 1).Extent); if (prevLineSpans.Count == 0 || !Genero4glReverseParser.IsExplicitLineJoin(prevLineSpans[prevLineSpans.Count - 1])) { indentation += tabSize; } } else { indentation += tabSize; } return(indentation); } string sline = tokens[tokenIndex].Span.GetText(); var lastChar = sline.Length == 0 ? '\0' : sline[sline.Length - 1]; // use the expression parser to figure out if we're in a grouping... var spans = textView.BufferGraph.MapDownToFirstMatch( tokens[tokenIndex].Span, SpanTrackingMode.EdgePositive, PythonContentTypePrediciate ); if (spans.Count == 0) { return(indentation); } var revParser = new Genero4glReverseParser( spans[0].Snapshot, spans[0].Snapshot.TextBuffer, spans[0].Snapshot.CreateTrackingSpan( spans[0].Span, SpanTrackingMode.EdgePositive ) ); var tokenStack = new List <ClassificationSpan>(); tokenStack.Insert(0, null); bool endAtNextNull = false; foreach (var token in revParser) { tokenStack.Insert(0, token); if (token == null && endAtNextNull) { break; } else if (token != null && token.ClassificationType == Genero4glClassifierProvider.Keyword) { var tok = Tokens.GetToken(token.Span.GetText()); if (tok != null && Genero4glAst.ValidStatementKeywords.Contains(tok.Kind)) { switch (tok.Kind) { // Handle any tokens that are valid statement keywords in the autocomplete context but not in the "statement start" context case TokenKind.EndKeyword: continue; default: endAtNextNull = true; break; } } } } var indentStack = new System.Collections.Generic.Stack <LineInfo>(); var current = LineInfo.Empty; List <CancelIndent> cancelIndent = null; int cancelIndentStartingAt = -1; TokenKind firstStatement = TokenKind.EndOfFile; TokenKind latestIndentChangeToken = TokenKind.EndOfFile; ClassificationSpan firstToken = null; for (int i = 0; i < tokenStack.Count; i++) { var token = tokenStack[i]; if (token != null && firstToken == null) { firstToken = token; } if (token == null) { current.NeedsUpdate = true; } else if (token.IsOpenGrouping()) { indentStack.Push(current); var start = token.Span.Start; var line2 = start.GetContainingLine(); current = new LineInfo { Indentation = start.Position - line2.Start.Position + 1 }; } else if (token.IsCloseGrouping()) { if (indentStack.Count > 0) { current = indentStack.Pop(); } else { current.NeedsUpdate = true; } } else if (Genero4glReverseParser.IsExplicitLineJoin(token)) { while (token != null && i + 1 < tokenStack.Count) { i++; token = tokenStack[i]; } } else if (current.NeedsUpdate == true) { var tok = Tokens.GetToken(token.Span.GetText()); if (tok == null || !Genero4glAst.ValidStatementKeywords.Contains(tok.Kind)) { current.NeedsUpdate = false; } else { switch (tok.Kind) { // Handle any tokens that are valid statement keywords in the autocomplete context but not in the "statement start" context case TokenKind.EndKeyword: if (firstStatement != TokenKind.EndOfFile) { current.NeedsUpdate = false; } else { latestIndentChangeToken = tok.Kind; } break; default: { if (firstStatement == TokenKind.EndOfFile) { firstStatement = tok.Kind; } var line2 = token.Span.Start.GetContainingLine(); current = new LineInfo { Indentation = GetIndentation(line2.GetText(), tabSize) }; break; } } } } if (token != null && current.ShouldIndentAfter && cancelIndent != null) { // Check to see if we have following tokens that would cancel the current indent. var tok = Tokens.GetToken(token.Span.GetText()); var tokenCategory = token.ClassificationType; bool allPast = true; bool cancel = false; foreach (var ci in cancelIndent) { if (ci.TokensAhead < (i - cancelIndentStartingAt)) { continue; } else { allPast = false; if (ci.TokensAhead == (i - cancelIndentStartingAt)) { if (ci.UseCategory && ci.CancelCategory != null) { cancel = tokenCategory == ci.CancelCategory; } else if (tok != null) { cancel = tok.Kind == ci.CancelToken; } if (cancel) { break; } } } } if (cancel) { current.ShouldIndentAfter = false; } if (cancel || allPast) { cancelIndent = null; cancelIndentStartingAt = -1; latestIndentChangeToken = TokenKind.EndOfFile; } } if (token != null && ShouldDedentAfterKeyword(token)) { // dedent after some statements current.ShouldDedentAfter = true; } TokenKind tempChangeToken; if (token != null && indentStack.Count == 0 && firstToken == token && ShouldIndentAfterKeyword(token, out tempChangeToken, out cancelIndent)) { // except in a grouping if (latestIndentChangeToken != TokenKind.EndKeyword) { current.ShouldIndentAfter = true; } latestIndentChangeToken = tempChangeToken; if (cancelIndent != null) { cancelIndentStartingAt = i; } } } if (tokenStack.Count > 2 && tokenStack[tokenStack.Count - 2] != null) { if (latestIndentChangeToken != TokenKind.EndOfFile && _customIndentingRules.ContainsKey(latestIndentChangeToken)) { var potentialIndent = _customIndentingRules[latestIndentChangeToken](tokenStack, tabSize); if (potentialIndent != 0) { return(potentialIndent); } } // see if we have specific alignment rules if (firstStatement != TokenKind.EndOfFile && _customIndentingRules.ContainsKey(firstStatement)) { var potentialIndent = _customIndentingRules[firstStatement](tokenStack, tabSize); if (potentialIndent != 0) { return(potentialIndent); } } } indentation = current.Indentation + (current.ShouldIndentAfter ? tabSize : 0) - (current.ShouldDedentAfter ? tabSize : 0); } return(indentation); }
public static int GetLineOffsetFromColumn(this ITextSnapshotLine line, int column, IEditorOptions editorOptions) { return line.GetText().GetLineOffsetFromColumn(column, editorOptions.GetTabSize()); }
public static int GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(this ITextSnapshotLine line, IEditorOptions editorOptions) { return line.GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(editorOptions.GetTabSize()); }
// Code taken from https://github.com/dotnet/roslyn/blob/master/src/EditorFeatures/Core/Shared/Extensions/IProjectionBufferFactoryServiceExtensions.cs public static IProjectionBuffer CreateProjectionBufferWithoutIndentation( this IProjectionBufferFactoryService projectionBufferFactoryService, IEditorOptions editorOptions, ITextSnapshot textSnapshot, string separator, params LineSpan[] exposedLineSpans) { var spans = new List <object>(); if (exposedLineSpans.Length > 0) { if (exposedLineSpans[0].Start > 0 && !string.IsNullOrEmpty(separator)) { spans.Add(separator); spans.Add(editorOptions.GetNewLineCharacter()); } IList <IList <SnapshotSpan> > snapshotSpanRanges = CreateSnapshotSpanRanges(textSnapshot, exposedLineSpans); int indentColumn = DetermineIndentationColumn(editorOptions, snapshotSpanRanges.SelectMany(s => s)); foreach (IList <SnapshotSpan> snapshotSpanRange in snapshotSpanRanges) { foreach (SnapshotSpan snapshotSpan in snapshotSpanRange) { ITextSnapshotLine line = snapshotSpan.Snapshot.GetLineFromPosition(snapshotSpan.Start); int indentPosition = line.GetText().GetLineOffsetFromColumn(indentColumn, editorOptions.GetTabSize()) + line.Start; var mappedSpan = new SnapshotSpan(snapshotSpan.Snapshot, Span.FromBounds(indentPosition, snapshotSpan.End)); ITrackingSpan trackingSpan = mappedSpan.Snapshot.CreateTrackingSpan(mappedSpan, SpanTrackingMode.EdgeExclusive); spans.Add(trackingSpan); // Add a newline between every line. if (snapshotSpan != snapshotSpanRange.Last()) { spans.Add(editorOptions.GetNewLineCharacter()); } } // Add a separator between every set of lines. if (snapshotSpanRange != snapshotSpanRanges.Last()) { spans.Add(editorOptions.GetNewLineCharacter()); spans.Add(separator); spans.Add(editorOptions.GetNewLineCharacter()); } } if (textSnapshot.GetLineNumberFromPosition(snapshotSpanRanges.Last().Last().End) < textSnapshot.LineCount - 1) { spans.Add(editorOptions.GetNewLineCharacter()); spans.Add(separator); } } return(projectionBufferFactoryService.CreateProjectionBuffer( projectionEditResolver: null, sourceSpans: spans, options: ProjectionBufferOptions.None, contentType: textSnapshot.ContentType)); }
public SmartIndent(ITextView textView, IEditorOptions options) : this(textView) { _options = options; _tabSize = _options.GetTabSize(); }
public override bool Indent() { bool singleLineSelection = (GetStartPoint().LineNumber == GetEndPoint().LineNumber); bool entireLastLineSelected = (GetStartPoint().CurrentPosition != GetEndPoint().CurrentPosition&& GetStartPoint().CurrentPosition == TextBuffer.GetEndPoint().StartOfLine&& GetEndPoint().CurrentPosition == TextBuffer.GetEndPoint().EndOfLine); if (singleLineSelection && !entireLastLineSelected) { TextPoint endPoint = GetEndPoint(); if (!Delete()) { return(false); } if (!endPoint.InsertIndent()) { return(false); } TextView.AdvancedTextView.Caret.MoveTo(endPoint.AdvancedTextPoint); } else // indent the selected lines { VirtualSnapshotPoint oldStartPoint = TextSelection.Start; VirtualSnapshotPoint oldEndPoint = TextSelection.End; bool isReversed = TextSelection.IsReversed; ITextSnapshotLine startLine = AdvancedTextRange.Snapshot.GetLineFromPosition(oldStartPoint.Position); ITextSnapshotLine endLine = AdvancedTextRange.Snapshot.GetLineFromPosition(oldEndPoint.Position); // If the selection span initially starts at the whitespace at the beginning of the line in the startLine or // ends at the whitespace at the beginning of the line in the endLine, restore selection and caret position, // *unless* the selection was in box mode. bool startAtStartLineWhitespace = oldStartPoint.Position <= _textView.GetTextPoint(startLine.Start).GetFirstNonWhiteSpaceCharacterOnLine().CurrentPosition; bool endAtEndLineWhitespace = oldEndPoint.Position < _textView.GetTextPoint(endLine.Start).GetFirstNonWhiteSpaceCharacterOnLine().CurrentPosition; bool isBoxSelection = AdvancedSelection.Mode == TextSelectionMode.Box; if (isBoxSelection) { if (!this.BoxIndent()) { return(false); } } else { if (!TextRange.Indent()) { return(false); } } // Computing the new selection and caret position VirtualSnapshotPoint newStartPoint = TextSelection.Start; VirtualSnapshotPoint newEndPoint = TextSelection.End; if (!isBoxSelection && (startAtStartLineWhitespace || endAtEndLineWhitespace)) { // After indent selection span should start at the start of startLine and end at the start of endLine if (startAtStartLineWhitespace) { newStartPoint = new VirtualSnapshotPoint(AdvancedTextRange.Snapshot, oldStartPoint.Position.Position); } if (endAtEndLineWhitespace && oldEndPoint.Position.Position != endLine.Start && endLine.Length != 0) { int insertedTextSize = _editorOptions.IsConvertTabsToSpacesEnabled() ? _editorOptions.GetTabSize() : 1; newEndPoint = new VirtualSnapshotPoint(AdvancedTextRange.Snapshot, newEndPoint.Position.Position - insertedTextSize); } if (!isReversed) { TextSelection.Select(newStartPoint, newEndPoint); } else { TextSelection.Select(newEndPoint, newStartPoint); } TextView.AdvancedTextView.Caret.MoveTo(TextSelection.ActivePoint, PositionAffinity.Successor); } } TextView.AdvancedTextView.Caret.EnsureVisible(); return(true); }
private int?DoSmartIndent(ITextSnapshotLine line) { if (line.LineNumber == 0) { return(null); } int?indentation = GetLeadingWhiteSpace(line.GetText()); var classifications = EnumerateClassificationsInReverse( _classifier, line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1).End ); if (classifications.MoveNext()) { var starting = classifications.Current; // first check to see if we're in an unterminated multiline token if (starting != null) { if (starting.Tag.ClassificationType.Classification == "comment" && starting.Span.GetText().StartsWith("/*") && (!starting.Span.GetText().EndsWith("*/") || starting.Span.End.GetContainingLine() == line)) { // smart indent in comment, dont indent return(null); } else if (starting.Tag.ClassificationType.Classification == "string") { var text = starting.Span.GetText(); if (!text.EndsWith("\"") && !text.EndsWith("'")) { // smart indent in multiline string, no indent return(null); } } } // walk backwards and collect all of the possible tokens that could // be useful here... var tokenStack = new System.Collections.Generic.Stack <ITagSpan <ClassificationTag> >(); tokenStack.Push(null); // end with an implicit newline bool endAtNextNull = false; do { var token = classifications.Current; tokenStack.Push(token); if (token == null && endAtNextNull) { break; } else if (token != null && token.Span.GetText() == "{") { endAtNextNull = true; } } while (classifications.MoveNext()); var indentStack = new System.Collections.Generic.Stack <LineInfo>(); var current = LineInfo.Empty; while (tokenStack.Count > 0) { var token = tokenStack.Pop(); bool didDedent = false; if (token == null) { current.NeedsUpdate = true; } else if (IsOpenGrouping(token)) { if (current.WasIndentKeyword && token.Span.GetText() == "{") { // the indentation statement is followed by braces, go ahead // and remove the level of indentation now current.WasIndentKeyword = false; current.Indentation -= _editorOptions.GetTabSize(); } indentStack.Push(current); var start = token.Span.Start; current = new LineInfo { Indentation = GetLeadingWhiteSpace(start.GetContainingLine().GetText()) + _editorOptions.GetTabSize() }; } else if (_indentKeywords.Contains(token.Span.GetText()) && !current.WasDoKeyword) { // if (foo) // console.log('hi') // We should indent here var start = token.Span.Start; int dedentTo = GetLeadingWhiteSpace(start.GetContainingLine().GetText()); if (current.DedentTo != null && token.Span.GetText() != "if") { // https://nodejstools.codeplex.com/workitem/1176 // if (true) // while (true) // ; // // We should dedent to the if (true) // But for: // if (true) // if (true) // ; // We still want to dedent to our current level for the else dedentTo = current.DedentTo.Value; } current = new LineInfo { Indentation = GetLeadingWhiteSpace(start.GetContainingLine().GetText()) + _editorOptions.GetTabSize(), DedentTo = dedentTo, WasIndentKeyword = true, WasDoKeyword = token.Span.GetText() == "do" }; } else if (IsCloseGrouping(token)) { if (indentStack.Count > 0) { current = indentStack.Pop(); } else { current = new LineInfo { Indentation = GetLeadingWhiteSpace(token.Span.Start.GetContainingLine().GetText()) }; } } else if (IsMultilineStringOrComment(token)) { while (token != null && tokenStack.Count > 0) { token = tokenStack.Pop(); } } else if (current.WasIndentKeyword) { // we've encountered a token after the opening of the indented // statement, go ahead and decrement our indentation level now. current = new LineInfo { Indentation = current.DedentTo, DedentTo = current.DedentTo - _editorOptions.GetTabSize(), WasDoKeyword = current.WasDoKeyword }; didDedent = true; } else if (current.NeedsUpdate) { var line2 = token.Span.Start.GetContainingLine(); current = new LineInfo { Indentation = GetLeadingWhiteSpace(line2.GetText()) }; } if (!didDedent && token != null && _dedentKeywords.Contains(token.Span.GetText())) // dedent after some statements { current.ShouldDedentAfter = true; } } return(current.Indentation - (current.ShouldDedentAfter ? _editorOptions.GetTabSize() : 0)); } return(null); }
private static int CalculateIndentation(string baseline, ITextSnapshotLine line, IEditorOptions options, IClassifier classifier, ITextView textView) { int indentation = GetIndentation(baseline, options.GetTabSize()); int tabSize = options.GetIndentSize(); var tokens = classifier.GetClassificationSpans(line.Extent); if (tokens.Count > 0 && !IsUnterminatedStringToken(tokens[tokens.Count - 1])) { int tokenIndex = tokens.Count - 1; while (tokenIndex >= 0 && (tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment) || tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.WhiteSpace))) { tokenIndex--; } if (tokenIndex < 0) { return indentation; } if (ReverseExpressionParser.IsExplicitLineJoin(tokens[tokenIndex])) { // explicit line continuation, we indent 1 level for the continued line unless // we're already indented because of multiple line continuation characters. indentation = GetIndentation(line.GetText(), options.GetTabSize()); var joinedLine = tokens[tokenIndex].Span.Start.GetContainingLine(); if (joinedLine.LineNumber > 0) { var prevLineSpans = classifier.GetClassificationSpans(tokens[tokenIndex].Span.Snapshot.GetLineFromLineNumber(joinedLine.LineNumber - 1).Extent); if (prevLineSpans.Count == 0 || !ReverseExpressionParser.IsExplicitLineJoin(prevLineSpans[prevLineSpans.Count - 1])) { indentation += tabSize; } } else { indentation += tabSize; } return indentation; } string sline = tokens[tokenIndex].Span.GetText(); var lastChar = sline.Length == 0 ? '\0' : sline[sline.Length - 1]; // use the expression parser to figure out if we're in a grouping... var spans = textView.BufferGraph.MapDownToFirstMatch( tokens[tokenIndex].Span, SpanTrackingMode.EdgePositive, PythonContentTypePrediciate ); if (spans.Count == 0) { return indentation; } var revParser = new ReverseExpressionParser( spans[0].Snapshot, spans[0].Snapshot.TextBuffer, spans[0].Snapshot.CreateTrackingSpan( spans[0].Span, SpanTrackingMode.EdgePositive ) ); var tokenStack = new System.Collections.Generic.Stack<ClassificationSpan>(); tokenStack.Push(null); // end with an implicit newline bool endAtNextNull = false; foreach (var token in revParser) { tokenStack.Push(token); if (token == null && endAtNextNull) { break; } else if (token != null && token.ClassificationType == revParser.Classifier.Provider.Keyword && PythonKeywords.IsOnlyStatementKeyword(token.Span.GetText())) { endAtNextNull = true; } } var indentStack = new System.Collections.Generic.Stack<LineInfo>(); var current = LineInfo.Empty; while (tokenStack.Count > 0) { var token = tokenStack.Pop(); if (token == null) { current.NeedsUpdate = true; } else if (token.IsOpenGrouping()) { indentStack.Push(current); var start = token.Span.Start; var line2 = start.GetContainingLine(); var next = tokenStack.Count > 0 ? tokenStack.Peek() : null; if (next != null && next.Span.End <= line2.End) { current = new LineInfo { Indentation = start.Position - line2.Start.Position + 1 }; } else { current = new LineInfo { Indentation = GetIndentation(line2.GetText(), tabSize) + tabSize }; } } else if (token.IsCloseGrouping()) { if (indentStack.Count > 0) { current = indentStack.Pop(); } else { current.NeedsUpdate = true; } } else if (ReverseExpressionParser.IsExplicitLineJoin(token)) { while (token != null && tokenStack.Count > 0) { token = tokenStack.Pop(); } } else if (current.NeedsUpdate == true) { var line2 = token.Span.Start.GetContainingLine(); current = new LineInfo { Indentation = GetIndentation(line2.GetText(), tabSize) }; } if (token != null && ShouldDedentAfterKeyword(token)) { // dedent after some statements current.ShouldDedentAfter = true; } if (token != null && token.Span.GetText() == ":" && // indent after a colon indentStack.Count == 0) { // except in a grouping current.ShouldIndentAfter = true; // If the colon isn't at the end of the line, cancel it out. // If the following is a ShouldDedentAfterKeyword, only one dedent will occur. current.ShouldDedentAfter = (tokenStack.Count != 0 && tokenStack.Peek() != null); } } indentation = current.Indentation + (current.ShouldIndentAfter ? tabSize : 0) - (current.ShouldDedentAfter ? tabSize : 0); } // Map indentation back to the view's text buffer. int offset = 0; var viewLineStart = textView.BufferGraph.MapUpToSnapshot(line.Start, PointTrackingMode.Positive, PositionAffinity.Successor, textView.TextSnapshot); if (viewLineStart.HasValue) { offset = viewLineStart.Value.Position - viewLineStart.Value.GetContainingLine().Start.Position; } return offset + indentation; }
private static int CalculateIndentation( string baseline, ITextSnapshotLine line, IEditorOptions options, PythonTextBufferInfo buffer ) { var snapshot = line.Snapshot; if (snapshot.TextBuffer != buffer.Buffer) { throw new ArgumentException("buffer mismatch"); } int indentation = GetIndentation(baseline, options.GetTabSize()); int tabSize = options.GetIndentSize(); var tokens = buffer.GetTokens(line).ToList(); while (tokens.Count > 0 && IsWhitespace(tokens[tokens.Count - 1].Category)) { tokens.RemoveAt(tokens.Count - 1); } if (tokens.Count == 0 || IsUnterminatedStringToken(tokens[tokens.Count - 1], snapshot)) { return(indentation); } if (HasExplicitLineJoin(tokens, snapshot)) { // explicit line continuation, we indent 1 level for the continued line unless // we're already indented because of multiple line continuation characters. indentation = GetIndentation(line.GetText(), options.GetTabSize()); var joinedLine = line.LineNumber - 1; if (joinedLine >= 0) { var prevLineTokens = buffer.GetTokens(snapshot.GetLineFromLineNumber(joinedLine)).ToList(); if (prevLineTokens.Count == 0 || !HasExplicitLineJoin(prevLineTokens, snapshot)) { indentation += tabSize; } } else { indentation += tabSize; } return(indentation); } var tokenStack = new Stack <TrackingTokenInfo?>(); tokenStack.Push(null); // end with an implicit newline int endAtLine = -1, currentLine = tokens.Last().LineNumber; foreach (var t in buffer.GetTokensInReverseFromPoint(tokens.Last().ToSnapshotSpan(snapshot).Start)) { if (t.LineNumber == currentLine) { tokenStack.Push(t); } else { tokenStack.Push(null); } if (t.LineNumber == endAtLine) { break; } else if (t.Category == TokenCategory.Keyword && PythonKeywords.IsOnlyStatementKeyword(t.GetText(snapshot), buffer.LanguageVersion)) { endAtLine = t.LineNumber - 1; } if (t.LineNumber != currentLine) { currentLine = t.LineNumber; if (t.Category != TokenCategory.WhiteSpace && t.Category != TokenCategory.Comment && t.Category != TokenCategory.LineComment) { tokenStack.Push(t); } } } var indentStack = new Stack <LineInfo>(); var current = LineInfo.Empty; while (tokenStack.Count > 0) { var t = tokenStack.Pop(); if (t == null) { current.NeedsUpdate = true; continue; } var tline = new Lazy <string>(() => snapshot.GetLineFromLineNumber(t.Value.LineNumber).GetText()); if (IsOpenGrouping(t.Value, snapshot)) { indentStack.Push(current); var next = tokenStack.Count > 0 ? tokenStack.Peek() : null; if (next != null && next.Value.LineNumber == t.Value.LineNumber) { // Put indent at same depth as grouping current = new LineInfo { Indentation = t.Value.ToSourceSpan().End.Column - 1 }; } else { // Put indent at one indent deeper than this line current = new LineInfo { Indentation = GetIndentation(tline.Value, tabSize) + tabSize }; } } else if (IsCloseGrouping(t.Value, snapshot)) { if (indentStack.Count > 0) { current = indentStack.Pop(); } else { current.NeedsUpdate = true; } } else if (IsExplicitLineJoin(t.Value, snapshot)) { while (t != null && tokenStack.Count > 0) { t = tokenStack.Pop(); } if (!t.HasValue) { continue; } } else if (current.NeedsUpdate == true) { current = new LineInfo { Indentation = GetIndentation(tline.Value, tabSize) }; } if (ShouldDedentAfterKeyword(t.Value, snapshot)) // dedent after some statements { current.ShouldDedentAfter = true; } if (IsColon(t.Value, snapshot) && // indent after a colon indentStack.Count == 0) // except in a grouping { current.ShouldIndentAfter = true; // If the colon isn't at the end of the line, cancel it out. // If the following is a ShouldDedentAfterKeyword, only one dedent will occur. current.ShouldDedentAfter = (tokenStack.Count != 0 && tokenStack.Peek() != null); } } indentation = current.Indentation + (current.ShouldIndentAfter ? tabSize : 0) - (current.ShouldDedentAfter ? tabSize : 0); return(indentation); }
public static int GetColumnFromLineOffset(this ITextSnapshotLine line, int lineOffset, IEditorOptions editorOptions) { return line.GetText().GetColumnFromLineOffset(lineOffset, editorOptions.GetTabSize()); }
private static int CalculateIndentation(string baseline, ITextSnapshotLine line, IEditorOptions options, IClassifier classifier, ITextView textView) { int indentation = GetIndentation(baseline, options.GetTabSize()); int tabSize = options.GetIndentSize(); var tokens = classifier.GetClassificationSpans(line.Extent); if (tokens.Count > 0 && !IsUnterminatedStringToken(tokens[tokens.Count - 1])) { int tokenIndex = tokens.Count - 1; while (tokenIndex >= 0 && (tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment) || tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.WhiteSpace))) { tokenIndex--; } if (tokenIndex < 0) { return(indentation); } if (ReverseExpressionParser.IsExplicitLineJoin(tokens[tokenIndex])) { // explicit line continuation, we indent 1 level for the continued line unless // we're already indented because of multiple line continuation characters. indentation = GetIndentation(line.GetText(), options.GetTabSize()); var joinedLine = tokens[tokenIndex].Span.Start.GetContainingLine(); if (joinedLine.LineNumber > 0) { var prevLineSpans = classifier.GetClassificationSpans(tokens[tokenIndex].Span.Snapshot.GetLineFromLineNumber(joinedLine.LineNumber - 1).Extent); if (prevLineSpans.Count == 0 || !ReverseExpressionParser.IsExplicitLineJoin(prevLineSpans[prevLineSpans.Count - 1])) { indentation += tabSize; } } else { indentation += tabSize; } return(indentation); } string sline = tokens[tokenIndex].Span.GetText(); var lastChar = sline.Length == 0 ? '\0' : sline[sline.Length - 1]; // use the expression parser to figure out if we're in a grouping... var spans = textView.BufferGraph.MapDownToFirstMatch( tokens[tokenIndex].Span, SpanTrackingMode.EdgePositive, PythonContentTypePrediciate ); if (spans.Count == 0) { return(indentation); } var revParser = new ReverseExpressionParser( spans[0].Snapshot, spans[0].Snapshot.TextBuffer, spans[0].Snapshot.CreateTrackingSpan( spans[0].Span, SpanTrackingMode.EdgePositive ) ); var tokenStack = new System.Collections.Generic.Stack <ClassificationSpan>(); tokenStack.Push(null); // end with an implicit newline bool endAtNextNull = false; foreach (var token in revParser) { tokenStack.Push(token); if (token == null && endAtNextNull) { break; } else if (token != null && token.ClassificationType == revParser.Classifier.Provider.Keyword && PythonKeywords.IsOnlyStatementKeyword(token.Span.GetText())) { endAtNextNull = true; } } var indentStack = new System.Collections.Generic.Stack <LineInfo>(); var current = LineInfo.Empty; while (tokenStack.Count > 0) { var token = tokenStack.Pop(); if (token == null) { current.NeedsUpdate = true; } else if (token.IsOpenGrouping()) { indentStack.Push(current); var start = token.Span.Start; var line2 = start.GetContainingLine(); var next = tokenStack.Count > 0 ? tokenStack.Peek() : null; if (next != null && next.Span.End <= line2.End) { current = new LineInfo { Indentation = start.Position - line2.Start.Position + 1 }; } else { current = new LineInfo { Indentation = GetIndentation(line2.GetText(), tabSize) + tabSize }; } } else if (token.IsCloseGrouping()) { if (indentStack.Count > 0) { current = indentStack.Pop(); } else { current.NeedsUpdate = true; } } else if (ReverseExpressionParser.IsExplicitLineJoin(token)) { while (token != null && tokenStack.Count > 0) { token = tokenStack.Pop(); } } else if (current.NeedsUpdate == true) { var line2 = token.Span.Start.GetContainingLine(); current = new LineInfo { Indentation = GetIndentation(line2.GetText(), tabSize) }; } if (token != null && ShouldDedentAfterKeyword(token)) // dedent after some statements { current.ShouldDedentAfter = true; } if (token != null && token.Span.GetText() == ":" && // indent after a colon indentStack.Count == 0) // except in a grouping { current.ShouldIndentAfter = true; // If the colon isn't at the end of the line, cancel it out. // If the following is a ShouldDedentAfterKeyword, only one dedent will occur. current.ShouldDedentAfter = (tokenStack.Count != 0 && tokenStack.Peek() != null); } } indentation = current.Indentation + (current.ShouldIndentAfter ? tabSize : 0) - (current.ShouldDedentAfter ? tabSize : 0); } // Map indentation back to the view's text buffer. int offset = 0; var viewLineStart = textView.BufferGraph.MapUpToSnapshot(line.Start, PointTrackingMode.Positive, PositionAffinity.Successor, textView.TextSnapshot); if (viewLineStart.HasValue) { offset = viewLineStart.Value.Position - viewLineStart.Value.GetContainingLine().Start.Position; } return(offset + indentation); }
private LineCommentInfo(ITextSnapshotLine line, IList<ClassificationSpan> spans, SnapshotSpan commentSpan, IEditorOptions options) { this.Line = line; this.CommentSpan = commentSpan; this.CommentOnly = spans.Count == 1; // what about leading whitespace? var commentStart = this.CommentSpan.Start; // Is there a reliable way to detect we're inside a multi-line comment? // for now, we assume single-line... var text = this.CommentSpan.GetText(); if (text.StartsWith("////") || text.StartsWith("''''")) { // A code-comment, probably *shouldn't* be wrapped! this.Style = CommentStyle.CodeComment; this.MarkerSpan = new SnapshotSpan(commentStart, 4); } else if (text.StartsWith("///") || text.StartsWith("'''")) { this.Style = CommentStyle.DocComment; this.MarkerSpan = new SnapshotSpan(commentStart, 3); } else if (text.StartsWith("//") || text.StartsWith("''")) { this.Style = CommentStyle.SingleLine; // Look for special leading characters (+, -, !, ?) commonly // used in other comment-formatters, and consider them a part // of the marker... var extra = text.Skip(2).TakeWhile(c => FormatMarkers.Contains(c)).Count(); this.MarkerSpan = new SnapshotSpan(commentStart, 2 + extra); } else if (text.StartsWith("/*")) { this.Style = CommentStyle.MultiLineStart; // Look for special leading characters (+, -, !, ?) commonly // used in other comment-formatters, and consider them a part // of the marker... var extra = text.Skip(2).TakeWhile(c => FormatMarkers.Contains(c)).Count(); this.MarkerSpan = new SnapshotSpan(commentStart, 2 + extra); } else if ((text.StartsWith("/") || text.StartsWith("'")) && (commentStart > line.Start) && ((commentStart - 1).GetChar() == commentStart.GetChar())) { // Weird case where the classifier gives a bogus result when typing // the "//" at the beginning of the line... perhaps we should *not* // wrap in this case? // Treat like "//" or "''" comment... but fix the span up! this.CommentSpan = new SnapshotSpan(commentStart - 1, this.CommentSpan.End); commentSpan = this.CommentSpan; commentStart = this.CommentSpan.Start; text = this.CommentSpan.GetText(); this.Style = CommentStyle.SingleLine; // Look for special leading characters (+, -, !, ?) commonly // used in other comment-formatters, and consider them a part // of the marker... var extra = text.Skip(2).TakeWhile(c => FormatMarkers.Contains(c)).Count(); this.MarkerSpan = new SnapshotSpan(commentStart, 2 + extra); } else { // We must be a continuation? this.Style = CommentStyle.MultiLineContinuation; this.MarkerSpan = new SnapshotSpan(commentStart, 0); } // Determine the post-marker whitespace (if any). var space = text.Skip(this.MarkerSpan.Length).TakeWhile(c => Whitespace.Contains(c)).Count(); this.ContentSpan = new SnapshotSpan(commentStart + this.MarkerSpan.Length + space, this.CommentSpan.End); // Determine the effective column of the comment, taking into // account that tabs may be more than one space. Sadly, there // doesn't seem to be a way to handle this other than counting every // character in the line! var lineText = this.Line.Extent.GetText(); var currentColumn = 0; var currentPos = this.Line.Start.Position; this.ContentColumnStart = this.ContentSpan.Start.Position - currentPos; int tabSize = options.GetTabSize(); foreach (var c in lineText) { if (currentPos == this.MarkerSpan.Start.Position) { this.MarkerColumnStart = currentColumn; } if (currentPos == this.ContentSpan.Start.Position) { this.ContentColumnStart = currentColumn; break; } // TODO: track effective comment start/end/length? if (c == '\t') { // round up to the next multiple of the tab-stop size! currentColumn += tabSize - (currentColumn % tabSize); } else { currentColumn++; } currentPos++; } }
public override bool Indent() { string textToInsert = _editorOptions.IsConvertTabsToSpacesEnabled() ? new string(' ', _editorOptions.GetTabSize()) : "\t"; if (_startPoint.LineNumber == _endPoint.LineNumber) { return(_startPoint.InsertIndent()); } using (ITextEdit edit = TextBuffer.AdvancedTextBuffer.CreateEdit()) { ITextSnapshot snapshot = TextBuffer.AdvancedTextBuffer.CurrentSnapshot; for (int i = _startPoint.LineNumber; i <= _endPoint.LineNumber; i++) { ITextSnapshotLine line = snapshot.GetLineFromLineNumber(i); if ((line.Length > 0) && (line.Start != _endPoint.CurrentPosition)) { if (!edit.Insert(line.Start, textToInsert)) { return(false); } } } edit.Apply(); if (edit.Canceled) { return(false); } } return(true); }