private int?DesiredIndentation(int position, Builder.Node root) { var nodes = FlatPath(root, position - 1).Where(a => !(a is null)) .ToList(); if (nodes.Count == 0) { return(null); } // Near the closing token. if (nodes.Count > 1) { var parentOfLast = nodes[nodes.Count - 2]; var children = parentOfLast.children; if (children.Count > 1) { var last = nodes.Last(); if (IsClosingBracket(children.Last()) && ReferenceEquals(children[children.Count - 2], last)) { return(IndentationLevel(nodes) - IndentationSize); } } } // In the middle of scope. return(IndentationLevel(nodes)); }
private bool IsStringOrComment() { Builder.Node root = ParseTree.Tree.Root(); var notStringOrComment = true; var notReached = true; var point = TextView.Caret.Position.BufferPosition; void Traverse(Builder.Node node) { if (node.children == null) { if (notReached && node.begin < point.Position && node.end >= point.Position) { if (node.name == "Comment" || (node.name == "String" && node.end != point.Position)) { notStringOrComment = false; } notReached = false; return; } return; } foreach (var child in node.children) { Traverse(child); } } Traverse(root); return(!notStringOrComment); }
private void Notify(Builder.Node node) { this.last = node; var snapshot = new SnapshotSpan(textView.TextBuffer.CurrentSnapshot, 0, textView.TextBuffer.CurrentSnapshot.Length); TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(snapshot)); }
public static bool IsName(this Builder.Node node) { if (node is null) { throw new ArgumentNullException(nameof(node)); } return(node.name == "Name"); }
// [Figure.1] Example structure of nodes, SelectSets and ExpressionParsingContext (EPC) //============================================================================================================================================================= // Node | SelectSet | Node 0 | Node 0_0 | Node 0_0_0 | Node 0_0_1 | Node 0_1 | Node 0_1_0 | Node 0_1_1 //------------------------------------------------------------------------------------------------------------------------------------------------------------- // + Node 0 // | localSelectSet EPC.row=-1 EPC.row=-1 EPC.row=-1 EPC.row-1 EPC.row=-1 EPC.row=-1 EPC.row=-1 // | /|\ /|\ /|\ /|\ /|\ /|\ /|\ // | | | | | | | | // | dataSelectSet EPC.row=-1 EPC.row=0 EPC.row=0 EPC.row=0 EPC.row=1 EPC.row=1 EPC.row=1 // | /|\ /|\ /|\ /|\ /|\ /|\ // |-+ Node 0_0 | | | | | | // | | localSelectSet EPC.row=-1 EPC.row=-1 EPC.row=-1 | | | // | | /|\ /|\ /|\ | | | // | | | | | | | | // | | dataSelectSet EPC.row=-1 EPC.row=0 EPC.row=1 | | | // | | /|\ /|\ | | | // | |- Node 0_0_0 | | | | | // | | localSelectSet EPC.row=-1 | | | | // | | /|\ | | | | // | | | | | | | // | | dataSelectSet EPC.row=-1 | | | | // | | | | | | // | |- Node 0_0_1 | | | | // | | | | | // | localSelectSet EPC.row=-1 | | | // | /|\ | | | // | | | | | // | dataSelectSet EPC.row=-1 | | | // | | | | // |-+ Node 0_1 | | | // | localSelectSet EPC.row=-1 EPC.row=-1 EPC.row=-1 // | /|\ /|\ /|\ // | | | | // | dataSelectSet EPC.row=-1 EPC.row=0 EPC.row=1 // | /|\ /|\ // |- Node 0_1_0 | | // | localSelectSet | | // | EPC.row=-1 | // | /|\ | // | dataSelectSet | | // | EPC.row=-1 | // |- Node 0_1_1 | // localSelectSet | // EPC.row=-1 // /|\ // dataSelectSet | // EPC.row=-1 //============================================================================================================================================================= // // Using this structure, name look-up are prioritized in this order: //========================================================================== // Node | Select Set | Data used from select set //-------------------------------------------------------------------------- // Node 0 // Node0.dataSelectSet all // Node0.localSelectSet all // Node 0_0 // Node0_0.dataSelectSet all // Node0_0.localSelectSet all // Node0.dataSelectSet row 0 // Node0.localSelectSet all // Node 0_1: // Node0_1.dataSelectSet all // Node0_1.localSelectSet all // Node0.dataSelectSet row 1 // Node0.localSelectSet all // Node 0_0_0 // Node0_0_0.dataSelectSet all // Node0_0_0.localSelectSet all // Node0_0.dataSelectSet row 0 // Node0_0.localSelectSet all // Node0.dataSelectSet row 0 // Node0.localSelectSet all // Node 0_0_1 // Node0_0_1.dataSelectSet all // Node0_0_1.localSelectSet all // Node0_0.dataSelectSet row 1 // Node0_0.localSelectSet all // Node0.dataSelectSet row 0 // Node0.localSelectSet all // Node 0_1_0 // Node0_1_0.dataSelectSet all // Node0_1_0.localSelectSet all // Node0_1.dataSelectSet row 0 // Node0_1.localSelectSet all // Node0.dataSelectSet row 1 // Node0.localSelectSet all // Node 0_1_1 // Node0_1_1.dataSelectSet all // Node0_1_1.localSelectSet all // Node0_1.dataSelectSet row 1 // Node0_1.localSelectSet all // Node0.dataSelectSet row 1 // Node0.localSelectSet all //========================================================================== public ViewTable(ViewSchema viewSchema, Schema baseSchema, Builder.Node node, ExpressionParsingContext parentExpressionParsingContext) : base(viewSchema) { this.ViewSchema = viewSchema; this.BaseSchema = baseSchema; this.node = node; ExpressionParsingContext = parentExpressionParsingContext; ParentExpressionParsingContext = parentExpressionParsingContext; }
private void TraverseEdit(Builder.Node node) { if (node.children == null) { AddDelimeter(node); return; } foreach (var child in node.children) { TraverseEdit(child); } }
// Gives {parent-of-the-next-item, ..., parent-of-the-last-item, last-item} public static IEnumerable <Builder.Node> FlatPath(Builder.Node node, int position) { var tree = node; while (!(tree is null) && !IsEmpty(tree.children)) { // We do not check the result of Parent, so the last item can be null, or terminal. tree = Parent(tree, position); yield return(tree); } bool IsEmpty(IEnumerable <Builder.Node> a) => a is null || !a.Any(); }
public static Builder.Node LabelName(this Builder.Node node) { if (node is null) { throw new ArgumentNullException(nameof(node)); } Debug.Assert(node.children.Any()); var name = node.children.First(); Debug.Assert(name.name == "Name"); return(name); }
/// <summary> Searches the node which holds a text at given position. </summary> /// <param name="node"></param> /// <param name="position"></param> /// <returns> /// Returns the child of the node, which contains the text, /// or contains other sub-node which contains the text, or other sub-node which... /// </returns> public static Builder.Node Parent(Builder.Node node, int position) { // Standard binary search works only with the items with the same type, // i.e. collection and item must have the same base type. // So, we workaround it by wrapping our item in the type of the collection's elements. // And then we use comparer specially prepared for the workaround. var idx = node.children.BinarySearch(new Builder.Node { begin = position }, new Comparer()); return(idx < 0 ? null : node.children[idx]); }
private void AddIndentation(Builder.Node currentToken) { if (lastToken.end <= selectionEnd && currentToken.begin >= selectionBegin && (lastToken.name == "CRLF" || lastToken.name == "LF" || isFirstToken)) { var beginLine = TextView.TextBuffer.CurrentSnapshot.GetLineFromPosition(selectionBegin); var changeSpanBegin = Math.Max(lastToken.end, beginLine.Start.Position); var endLine = TextView.TextBuffer.CurrentSnapshot.GetLineFromPosition(selectionEnd - 1); var changeSpanEnd = Math.Min(currentToken.begin, endLine.End.Position); string oldDelimeter = rawSource.Substring(changeSpanBegin, Math.Abs(changeSpanEnd - changeSpanBegin)); var newDelimeter = new string(indentationCharacter, bracketsStack.Count *indentationSize); if (newDelimeter != oldDelimeter) { EditInsert(changeSpanBegin, oldDelimeter, newDelimeter); } } }
/// <summary>Gives an outermost node which holds a text at given position.</summary> /// <returns> /// The sub-node which holds a text at the <paramref name="position"/>. /// <para/> /// Or <c>null</c>, if the <paramref name="node"/> has no such a sub-node. /// </returns> public static Builder.Node OldestAncestor(this Builder.Node node, int position) { if (node is null) { throw new ArgumentNullException(nameof(node)); } var idx = node.children.BinarySearch(new Builder.Node { begin = position }, new Comparer()); return (idx < 0 ? null : node.children[idx]); }
private bool findDefinition() { ParseTree.Builder.Node root = ParseTree.Tree.Root(); bool definitionFound = false; void Traverse(Builder.Node node) { if (node.children == null) { return; } if ((node.name == "Code" || node.name == "Object" || node.name == "List") && node.end < selectionEnd) { return; } if (node.name == "Label") { Builder.Node child = node.children[0]; currName = TextView.TextBuffer.CurrentSnapshot.GetText(child.begin, child.end - child.begin); if (currName == selectedName) { definitionBegin = child.begin; definitionEnd = child.end; definitionFound = true; } } foreach (var child in node.children) { if (child.begin < selectionEnd) { Traverse(child); } } } Traverse(root); return(definitionFound); }
/// <summary>Gives <c>(oldest-ancestor ... ancestor-of-youngest-ancestor youngest-ancestor)</c>.</summary> /// <param name="node">Node which holds the text at the <paramref name="position"/>.</param> /// <param name="position">Position in a text for which is needed to find the ancestors.</param> /// <returns>An empty generator if the <paramref name="node"/> holds no such a sub-nodes.</returns> public static IEnumerable <Builder.Node> AllAncestors(this Builder.Node node, int position) { Debug.Assert(position >= 0); var tree = node ?? throw new ArgumentNullException(nameof(node)); while (IsNotEmpty(tree)) { tree = tree.OldestAncestor(position); if (tree is null) { yield break; } yield return(tree); } bool IsNotEmpty(Builder.Node treeNode) => !(treeNode.children is null) && treeNode.children.Any(); }
public static bool IsScopeEnd(this Builder.Node node) => node.name == "']'" || node.name == "'}'" || node.name == "')'" || node.name == "';'";
public static bool IsScopeStart(this Builder.Node node) => node.name == "'['" || node.name == "'{'" || node.name == "'('" || node.name == "':'";
public static bool IsScope(this Builder.Node node) => node.name == "Object" || node.name == "Code" || node.name == "List" || node.IsLabel();
public static Builder.Node OldestAncestor(this Builder.Node node, int start, int end) => node.AllAncestors(start, end).FirstOrDefault();
public static IEnumerable <Builder.Node> AllAncestors(this Builder.Node node, int start, int end) => node.AllAncestors(start).TakeWhile(a => a.end >= end);
protected override bool Execute(VSConstants.VSStd2KCmdID command, uint options, IntPtr pvaIn, IntPtr pvaOut) { ThreadHelper.ThrowIfNotOnUIThread(); lastToken = new Builder.Node { line = 0 }; currentLevelBrackets = new Stack <Builder.Node>(); bracketsStack = new Stack <Stack <Builder.Node> >(); edit = TextView.TextBuffer.CreateEdit(); wasPoped = false; switch (MplPackage.Options.LineEndings) { case Options.LineEnding.Unix: leaveAsIs = false; lineEnding = "\n"; break; case Options.LineEnding.Windows: leaveAsIs = false; lineEnding = "\r\n"; break; case Options.LineEnding.Document: leaveAsIs = true; break; } if (TextView.Options.IsConvertTabsToSpacesEnabled()) { indentationCharacter = ' '; indentationSize = TextView.Options.GetIndentSize(); } else { indentationCharacter = '\t'; indentationSize = 1; } selectionBegin = 0; selectionEnd = TextView.TextBuffer.CurrentSnapshot.Length; rawSource = TextView.TextBuffer.CurrentSnapshot.GetText(); caretPoint = TextView.Caret.Position.Point.GetPoint(TextView.TextBuffer, TextView.Caret.Position.Affinity); switch (command) { case VSConstants.VSStd2KCmdID.FORMATDOCUMENT: break; case VSConstants.VSStd2KCmdID.FORMATSELECTION: if (!TextView.Selection.IsEmpty && caretPoint != null) { selectionBegin = TextView.Selection.Start.Position.Position; selectionEnd = TextView.Selection.End.Position.Position; } else if (TextView.Selection.IsEmpty && caretPoint != null) { var line = TextView.TextBuffer.CurrentSnapshot.GetLineFromPosition(caretPoint.Value.Position); selectionBegin = line.Start.Position; selectionEnd = line.End.Position; } break; } GetFormattedProgram(); if (valid) { edit.Apply(); } edit.Dispose(); return(true); }
public static Builder.Node YongestAncestor(this Builder.Node node, int position) => node.AllAncestors(position).LastOrDefault();
private static bool IsOpeningBracket(Builder.Node a) => starts.Contains(a.name);
private int?DesiredIndentation(int position, Builder.Node root) { var ancestors = Utils.AllAncestors(root, position - 1).ToList(); if (!ancestors.Any()) { return(null); } // TODO: Revise this explanation. // If at the right side of a caret is a closing symbol ) ; ] } // then indentation must be less than indentation of current block. // // In the beginning of this method, in the left side of a caret always will be a new-line character. // But it doesn't mean that it will be a new-line symbol. // For example, if caret placed inside a string, then this new-line character will be a part of a string, // but not the new-line symbol like LF or CRLF. // So, there are two possible situations: // a) \n caret a-closing-symbol // b) \n caret not-a-closing-symbol // And the \n can be: the part of a string; a new-line symbol. // So finally we can have 4 different situations: // 1) Code: \n| Parse-tree: \n Ancestors: (\n) // // 2) Code: (\n|) Parse-tree: list Ancestors: (list \n) // / | \ // ( \n ) // // 3) Code: ("\n|") Parse-tree: list Ancestors: (list string) // / | \ // ( string ) // // 4) Code: (\n|...) Parse-tree: list Ancestors: (list \n) // / | | \ // ( \n ... ) // // Here, we interested only in 2nd case (\n|). // For it, Ancestors will return (list \n) and for much complicated code it will return (... list \n). // In this sequence of symbols (list \n), the list will contain the closing bracket as the last child. // And instead of the list, there can be object, code, or label. // But the Ancestors will return similar elements for 2nd and 4th cases, it will be (list \n), // so we must check the parse-tree, if it is contains something between caret and closing brace. if (ancestors.Count > 1) { var last = ancestors.Last(); var parentOfLast = ancestors[ancestors.Count - 2]; var children = parentOfLast.children; if (last.IsEol()) { if (children.Count > 1) { if (children.Last().IsScopeEnd()) { // Check if the closing bracket and a caret at the same line, // and between them stands nothing except tabs/spaces. if (ReferenceEquals(children[children.Count - 2], last)) { return(IndentationLevel(ancestors) - IndentationSize); } } } } // If it is not the end-of-line, then it should be a string. // TODO: What should we do if a caret placed inside of a string? } // In the middle of scope. return(IndentationLevel(ancestors)); }
public static bool IsLabel(this Builder.Node node) => node.name == "Label";
public static bool IsEol(this Builder.Node node) => node.name == "LF" || node.name == "CRLF" || node.name == "CR";
private bool IsClosingBracket(Builder.Node a) => ends.Contains(a.name);
public static bool IsEof(this Builder.Node node) => node.name == "EOF";
private void AddDelimeter(Builder.Node currentToken) { switch (currentToken.name) { case "EOF": if (lastToken.name != "CRLF" && lastToken.name != "LF") { if (rawSource.Substring(lastToken.end) != lineEnding && currentToken.end == selectionEnd) { edit.Delete(lastToken.end, rawSource.Length - lastToken.end); edit.Insert(lastToken.end, lineEnding); } } else { if (rawSource.Substring(lastToken.end) != lineEnding && currentToken.end == selectionEnd) { edit.Delete(lastToken.end, rawSource.Length - lastToken.end); } } return; case "']'": case "'}'": case "')'": case "';'": if (currentLevelBrackets.Count == 0 && bracketsStack.Count > 0 && bracketsStack.Peek().Count != 0) { if (currentToken.line == bracketsStack.Peek().Peek().line) { bracketsStack.Peek().Pop(); if (wasPoped) { currentLevelBrackets = bracketsStack.Pop(); wasPoped = false; } else if (bracketsStack.Peek().Count == 0) { bracketsStack.Pop(); } } else { currentLevelBrackets = bracketsStack.Pop(); currentLevelBrackets.Pop(); } } else if (currentLevelBrackets.Count > 0) { currentLevelBrackets.Pop(); } else if (bracketsStack.Count > 0 && bracketsStack.Peek().Count == 0) { bracketsStack.Pop(); } AddIndentation(currentToken); break; case "CRLF": if (currentToken.end <= selectionEnd && currentToken.end >= selectionBegin) { if (lastToken.end != currentToken.begin) { edit.Delete(lastToken.end, currentToken.begin - lastToken.end); } if (lineEnding != "\r\n" && !leaveAsIs) { EditInsert(currentToken.begin, "\r\n", lineEnding); } } break; case "LF": if (currentToken.end <= selectionEnd && currentToken.end >= selectionBegin) { if (lastToken.end != currentToken.begin) { edit.Delete(lastToken.end, currentToken.begin - lastToken.end); } if (lineEnding != "\n" && !leaveAsIs) { EditInsert(currentToken.begin, "\n", lineEnding); } } break; default: AddIndentation(currentToken); break; } if (currentToken.IsScopeStart()) { if (bracketsStack.Count == 0 && currentLevelBrackets.Count == 0) { bracketsStack.Push(new Stack <Builder.Node>()); bracketsStack.Peek().Push(currentToken); } else if (bracketsStack.Count != 0 && bracketsStack.Peek().Peek().line == currentToken.line) { bracketsStack.Peek().Push(currentToken); } else if (currentLevelBrackets.Count != 0) { bracketsStack.Push(new Stack <Builder.Node>(currentLevelBrackets)); currentLevelBrackets.Clear(); bracketsStack.Peek().Push(currentToken); wasPoped = true; } else { bracketsStack.Push(new Stack <Builder.Node>()); bracketsStack.Peek().Push(currentToken); } } lastToken = currentToken; if (isFirstToken) { isFirstToken = !isFirstToken; } }