Represents a line inside a TextDocument.

The TextDocument.Lines collection contains one DocumentLine instance for every line in the document. This collection is read-only to user code and is automatically updated to reflect the current document content.

Internally, the DocumentLine instances are arranged in a binary tree that allows for both efficient updates and lookup. Converting between offset and line number is possible in O(lg N) time, and the data structure also updates all offsets in O(lg N) whenever a line is inserted or removed.

Inheritance: IDocumentLine
示例#1
0
		public void Rebuild()
		{
			// keep the first document line
			var ls = documentLineTree.GetByNumber(1);
			// but mark all other lines as deleted, and detach them from the other nodes
			for (var line = ls.NextLine; line != null; line = line.NextLine)
			{
				line.isDeleted = true;
				line.parent = line.left = line.right = null;
			}
			// Reset the first line to detach it from the deleted lines
			ls.ResetLine();
			var ds = NewLineFinder.NextNewLine(document, 0);
			var lines = new List<DocumentLine>();
			var lastDelimiterEnd = 0;
			while (ds != SimpleSegment.Invalid)
			{
				ls.TotalLength = ds.Offset + ds.Length - lastDelimiterEnd;
				ls.DelimiterLength = ds.Length;
				lastDelimiterEnd = ds.Offset + ds.Length;
				lines.Add(ls);

				ls = new DocumentLine(document);
				ds = NewLineFinder.NextNewLine(document, lastDelimiterEnd);
			}
			ls.TotalLength = document.TextLength - lastDelimiterEnd;
			lines.Add(ls);
			documentLineTree.RebuildTree(lines);
			foreach (var lineTracker in lineTrackers)
				lineTracker.RebuildDocument();
		}
		void ILineTracker.LineInserted(DocumentLine insertionPos, DocumentLine newLine)
		{
			var targetTracker = targetObject.Target as ILineTracker;
			if (targetTracker != null)
				targetTracker.LineInserted(insertionPos, newLine);
			else
				Deregister();
		}
		void ILineTracker.SetLineLength(DocumentLine line, int newTotalLength)
		{
			var targetTracker = targetObject.Target as ILineTracker;
			if (targetTracker != null)
				targetTracker.SetLineLength(line, newTotalLength);
			else
				Deregister();
		}
		void ILineTracker.BeforeRemoveLine(DocumentLine line)
		{
			var targetTracker = targetObject.Target as ILineTracker;
			if (targetTracker != null)
				targetTracker.BeforeRemoveLine(line);
			else
				Deregister();
		}
示例#5
0
		/// <summary>
		///     Sets the total line length and checks the delimiter.
		///     This method can cause line to be deleted when it contains a single '\n' character
		///     and the previous line ends with '\r'.
		/// </summary>
		/// <returns>
		///     Usually returns <paramref name="line" />, but if line was deleted due to
		///     the "\r\n" merge, returns the previous line.
		/// </returns>
		private DocumentLine SetLineLength(DocumentLine line, int newTotalLength)
		{
			//			changedLines.Add(line);
			//			deletedOrChangedLines.Add(line);
			var delta = newTotalLength - line.TotalLength;
			if (delta != 0)
			{
				foreach (var lt in lineTrackers)
					lt.SetLineLength(line, newTotalLength);
				line.TotalLength = newTotalLength;
				DocumentLineTree.UpdateAfterChildrenChange(line);
			}
			// determine new DelimiterLength
			if (newTotalLength == 0)
			{
				line.DelimiterLength = 0;
			}
			else
			{
				var lineOffset = line.Offset;
				var lastChar = document.GetCharAt(lineOffset + newTotalLength - 1);
				if (lastChar == '\r')
				{
					line.DelimiterLength = 1;
				}
				else if (lastChar == '\n')
				{
					if (newTotalLength >= 2 && document.GetCharAt(lineOffset + newTotalLength - 2) == '\r')
					{
						line.DelimiterLength = 2;
					}
					else if (newTotalLength == 1 && lineOffset > 0 && document.GetCharAt(lineOffset - 1) == '\r')
					{
						// we need to join this line with the previous line
						var previousLine = line.PreviousLine;
						RemoveLine(line);
						return SetLineLength(previousLine, previousLine.TotalLength + 1);
					}
					else
					{
						line.DelimiterLength = 1;
					}
				}
				else
				{
					line.DelimiterLength = 0;
				}
			}
			return line;
		}
		/// <inheritdoc cref="IIndentationStrategy.IndentLine" />
		public override int IndentLine(TextDocument document, DocumentLine line, int caretOffset)
		{
			var lineNr = line.LineNumber;
			var acc = new TextDocumentAccessor(document, lineNr, lineNr);
			var result = Indent(acc, false, caretOffset);

			var t = acc.Text;
			if (t.Length == 0)
			{
				// use AutoIndentation for new lines in comments / verbatim strings.
				return base.IndentLine(document, line, caretOffset);
			}

			return result;
		}
		/// <inheritdoc />
		public virtual int IndentLine(TextDocument document, DocumentLine line, int caretOffset)
		{
			if (document == null)
				throw new ArgumentNullException("document");
			if (line == null)
				throw new ArgumentNullException("line");

			var previousLine = line.PreviousLine;
			if (previousLine != null)
			{
				var indentationSegment = TextUtilities.GetWhitespaceAfter(document, previousLine.Offset);
				var indentation = document.GetText(indentationSegment);
				// copy indentation to line
				indentationSegment = TextUtilities.GetWhitespaceAfter(document, line.Offset);
				document.Replace(indentationSegment, indentation);
			}

			return caretOffset;
		}
        /// <inheritdoc cref="IIndentationStrategy.IndentLine" />
        public override int IndentLine(TextDocument document, DocumentLine line, int caretIndex)
        {
            if (line == null)
            {
                return caretIndex;
            }

            var lineNr = line.LineNumber;
            var acc = new TextDocumentAccessor(document, lineNr, lineNr);

            var leadingWhiteSpaceBefore = TextUtilities.GetLeadingWhitespace(document, line).Length;
            var result = Indent(acc, false, caretIndex);
            var t = acc.Text;

            result = caretIndex + TextUtilities.GetLeadingWhitespace(document, line).Length - leadingWhiteSpaceBefore;

            if (t.Length == 0)
            {
                // use AutoIndentation for new lines in comments / verbatim strings.
                return base.IndentLine(document, line, caretIndex);
            }

            return result;
        }
		public static ISegment GetTrailingWhitespace(TextDocument document, DocumentLine documentLine)
		{
			if (documentLine == null)
				throw new ArgumentNullException("documentLine");
			var segment = GetWhitespaceBefore(document, documentLine.EndOffset);
			// If the whole line consists of whitespace, we consider all of it as leading whitespace,
			// so return an empty segment as trailing whitespace.
			if (segment.Offset == documentLine.Offset)
				return new SimpleSegment(documentLine.EndOffset, 0);
			return segment;
		}
示例#10
0
		public static ISegment GetLeadingWhitespace(TextDocument document, DocumentLine documentLine)
		{
			if (documentLine == null)
				throw new ArgumentNullException("documentLine");
			return GetWhitespaceAfter(document, documentLine.Offset);
		}
		/// <inheritdoc />
		public bool MoveNext()
		{
			if (lineDirty)
			{
				doc.Replace(line, text);
				lineDirty = false;
			}
			++LineNumber;
			if (LineNumber > maxLine) return false;
			line = doc.GetLineByNumber(LineNumber);
			text = doc.GetText(line);
			return true;
		}
示例#12
0
		// optimization note: I tried packing color and isDeleted into a single byte field, but that
		// actually increased the memory requirements. The JIT packs two bools and a byte (delimiterSize)
		// into a single DWORD, but two bytes get each their own DWORD. Three bytes end up in the same DWORD, so
		// apparently the JIT only optimizes for memory when there are at least three small fields.
		// Currently, DocumentLine takes 36 bytes on x86 (8 byte object overhead, 3 pointers, 3 ints, and another DWORD
		// for the small fields).
		// TODO: a possible optimization would be to combine 'totalLength' and the small fields into a single uint.
		// delimiterSize takes only two bits, the two bools take another two bits; so there's still 
		// 28 bits left for totalLength. 268435455 characters per line should be enough for everyone :)

		/// <summary>
		///     Resets the line to enable its reuse after a document rebuild.
		/// </summary>
		internal void ResetLine()
		{
			totalLength = delimiterLength = 0;
			isDeleted = color = false;
			left = right = parent = null;
		}
示例#13
0
		private DocumentLine InsertLineAfter(DocumentLine line, int length)
		{
			var newLine = documentLineTree.InsertLineAfter(line, length);
			foreach (var lt in lineTrackers)
				lt.LineInserted(line, newLine);
			return newLine;
		}
示例#14
0
		private void RemoveLine(DocumentLine lineToRemove)
		{
			foreach (var lt in lineTrackers)
				lt.BeforeRemoveLine(lineToRemove);
			documentLineTree.RemoveLine(lineToRemove);
			//			foreach (ILineTracker lt in lineTracker)
			//				lt.AfterRemoveLine(lineToRemove);
			//			deletedLines.Add(lineToRemove);
			//			deletedOrChangedLines.Add(lineToRemove);
		}