private void FormatLine() { // getEditorPreferences(TextView); // SnapshotPoint caret = this.TextView.Caret.Position.BufferPosition; ITextSnapshotLine line = caret.GetContainingLine(); // On what line are we ? bool alignOnPrev = false; int lineNumber = line.LineNumber; int indentation = -1; // we calculate the indent based on the previous line so we must be on the second line if (lineNumber > 0) { if (caret.Position < line.End.Position) { alignOnPrev = true; } // wait until we can work while (_buffer.EditInProgress) { System.Threading.Thread.Sleep(100); } var editSession = _buffer.CreateEdit(); // This will calculate the desired indentation of the current line, based on the previous one // and may de-Indent the previous line if needed indentation = getDesiredIndentation(line, editSession, alignOnPrev); // try { // but we may need to re-Format the previous line for Casing and Identifiers // so, do it before indenting the current line. lineNumber = lineNumber - 1; ITextSnapshotLine prevLine = line.Snapshot.GetLineFromLineNumber(lineNumber); this.formatLineCase(editSession, prevLine); CommandFilterHelper.FormatLineIndent(this.TextView, editSession, line, indentation); } finally { if (editSession.HasEffectiveChanges) { editSession.Apply(); } else { editSession.Cancel(); } } } }
/// <summary> /// the indentation is measured in # of characters /// </summary> /// <param name="line"></param> /// <param name="editSession"></param> /// <param name="alignOnPrev"></param> /// <returns></returns> private int getDesiredIndentation(ITextSnapshotLine line, ITextEdit editSession, bool alignOnPrev) { try { // if (_indentStyle != vsIndentStyle.vsIndentStyleSmart) { return(-1); } // How many spaces do we need ? int indentValue = 0; string outdentToken; // On what line are we ? int lineNumber = line.LineNumber; if (lineNumber > 0) { // We need to analyze the Previous line lineNumber = lineNumber - 1; ITextSnapshotLine prevLine = line.Snapshot.GetLineFromLineNumber(lineNumber); bool doSkipped; string keyword = getFirstKeywordInLine(prevLine, out doSkipped, out indentValue); if (indentValue < 0) { indentValue = 0; } _lastIndentValue = indentValue; if (alignOnPrev) { return(_lastIndentValue); } // ok, now check what we have, starting the previous line if (!String.IsNullOrEmpty(keyword))// && !doSkipped) { // Start of a block of code ? if (_codeBlockKeywords.Contains <String>(keyword)) { if (!_alignMethod) { indentValue += _indentSize; } } else if (_specialCodeBlockKeywords.Contains <String>(keyword)) { if (!_alignMethod) { indentValue += _indentSize; } } else if (_indentKeywords.Contains <String>(keyword)) { indentValue += _indentSize; } else if ((outdentToken = searchSpecialOutdentKeyword(keyword)) != null) { // Ok, let's try to make it smooth... int specialOutdentValue = -1; // The startToken is a list of possible tokens specialOutdentValue = alignToSpecificTokens(line, outdentToken); if (specialOutdentValue >= 0) { indentValue = (int)specialOutdentValue; } // De-Indent previous line !!! try { CommandFilterHelper.FormatLineIndent(this.TextView, editSession, prevLine, indentValue); } catch (Exception ex) { XSharpProjectPackage.Instance.DisplayOutPutMessage("Indentation of previous line failed"); XSharpProjectPackage.Instance.DisplayException(ex); } } else { string startToken = searchMiddleKeyword(keyword); int specialIndentValue = -1; if (startToken != null) { // Retrieve the Indentation for the previous line specialIndentValue = alignToSpecificTokens(line, startToken); } else { if (doSkipped && keyword == "CASE") { if (!_alignDoCase) { indentValue += _indentSize; } } else { // We could have "special" middle keyword : CASE or OTHERWISE startToken = searchSpecialMiddleKeyword(keyword); if (startToken != null) { // The startToken is a list of possible tokens specialIndentValue = alignToSpecificTokens(line, startToken); // The can be aligned to SWITCH/DO CASE or indented if (!_alignDoCase) { specialIndentValue += _indentSize; } } } } if (specialIndentValue != -1) { try { // De-Indent previous line !!! CommandFilterHelper.FormatLineIndent(this.TextView, editSession, prevLine, specialIndentValue); indentValue = specialIndentValue + _indentSize; } catch (Exception ex) { XSharpProjectPackage.Instance.DisplayOutPutMessage("Error indenting of current line "); XSharpProjectPackage.Instance.DisplayException(ex); } } } if (indentValue < 0) { indentValue = 0; } _lastIndentValue = indentValue; } return(_lastIndentValue); } } catch (Exception ex) { XSharpProjectPackage.Instance.DisplayOutPutMessage("SmartIndent.GetDesiredIndentation failed: "); XSharpProjectPackage.Instance.DisplayException(ex); } return(_lastIndentValue); }
private void FormatDocument() { XSharpProjectPackage.Instance.DisplayOutPutMessage("CommandFilter.FormatDocument()"); if (!_buffer.CheckEditAccess()) { // can't edit ! return; } // Read Settings getEditorPreferences(TextView); formatCaseForWholeBuffer(); // Try to retrieve an already parsed list of Tags if (_classifier != null) { #if TRACE // Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); #endif // ITextSnapshot snapshot = _classifier.Snapshot; SnapshotSpan Span = new SnapshotSpan(snapshot, 0, snapshot.Length); var classifications = _classifier.GetRegionTags(); // We cannot use SortedList, because we may have several Classification that start at the same position List <ClassificationSpan> sortedTags = new List <ClassificationSpan>(); foreach (var tag in classifications) { sortedTags.Add(tag); } sortedTags.Sort((a, b) => a.Span.Start.Position.CompareTo(b.Span.Start.Position)); // Now that Tags are sorted, we can use a stack to arrange them by pairs Stack <Span> regionStarts = new Stack <Span>(); List <Tuple <Span, Span> > regions = new List <Tuple <Span, Span> >(); // foreach (var tag in sortedTags) { if (tag.ClassificationType.IsOfType(ColorizerConstants.XSharpRegionStartFormat)) { regionStarts.Push(tag.Span.Span); } else if (tag.ClassificationType.IsOfType(ColorizerConstants.XSharpRegionStopFormat)) { if (regionStarts.Count > 0) { var start = regionStarts.Pop(); regions.Add(new Tuple <Span, Span>(start, tag.Span.Span)); } } } // In order to try to speed up the formatting process, it would be good to have the regions sorted by their Start regions.Sort((a, b) => a.Item1.Start.CompareTo(b.Item1.Start)); //Now, we have a list of Regions Start/Stop // wait until we can work while (_buffer.EditInProgress) { System.Threading.Thread.Sleep(100); } var editSession = _buffer.CreateEdit(); try { var lines = _buffer.CurrentSnapshot.Lines; int indentSize = 0; bool inComment = false; bool prevContinue = false; int prevIndentSize = 0; foreach (var snapLine in lines) { // Ignore Empty lines if (snapLine.Length > 0) { SnapshotSpan sSpan = new SnapshotSpan(snapLine.Start, snapLine.End); string lineText = sSpan.GetText(); lineText = lineText.Trim(); if (lineText.Length > 0) { char start = lineText.Substring(0, 1)[0]; char end = lineText.Substring(lineText.Length - 1, 1)[0]; // if (prevContinue) { indentSize = prevIndentSize; } else { indentSize = getDesiredIndentationInDocument(snapLine, regions, out inComment); } if (!inComment && (end == ';')) { // Keep the previous Indentation prevContinue = true; prevIndentSize = indentSize; } else { prevContinue = false; } } } formatLineCase(editSession, snapLine); CommandFilterHelper.FormatLineIndent(this.TextView, editSession, snapLine, indentSize); } } finally { if (editSession.HasEffectiveChanges) { editSession.Apply(); } else { editSession.Cancel(); } } // #if TRACE stopWatch.Stop(); // Get the elapsed time as a TimeSpan value. TimeSpan ts = stopWatch.Elapsed; // Format and display the TimeSpan value. string elapsedTime = String.Format("{0:00}h {1:00}m {2:00}.{3:00}s", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); // XSharpProjectPackage.Instance.DisplayOutPutMessage("FormatDocument : Done in " + elapsedTime); #endif } }