private void UpdateLineCache( UserLine line )
		{
			line.CachedLines = new List<CachedLine>();
			line.CachedWrapAt = CharsPerLine;

			int len = 0;

			CachedLine cachedLine = null;

			foreach( var origSpan in line.Spans )
			{
				if( cachedLine == null )
				{
					cachedLine = new CachedLine( line );
					line.CachedLines.Add( cachedLine );
				}

				if( (origSpan.Text.Length + len) < CharsPerLine )
				{
					cachedLine.Spans.Add( origSpan );
					len += origSpan.Text.Length;
				}
				else
				{
					string spanText = origSpan.Text;

					while( spanText.Length > 0 )
					{
						string substring = spanText.Substring( 0, Math.Min( CharsPerLine - len, spanText.Length ) );
						spanText = spanText.Substring( Math.Min( CharsPerLine - len, spanText.Length ) );
						len = 0;

						var cachedSpan = new Span( substring, origSpan.ForegroundColour, origSpan.BackgroundColour );
						cachedLine.Spans.Add( cachedSpan );

						if( spanText.Length > 0 )
						{
							cachedLine = new CachedLine( line );
							line.CachedLines.Add( cachedLine );

							cachedLine.Spans.Add( line.IsOutput ? PromptOutputWrap : PromptWrap );
							len += line.IsOutput ? PromptOutputWrap.Text.Length : PromptWrap.Text.Length;
						}
					}
				}
			}
		}
		HighlightedLine DoHighlightLine(int lineNumber, IDocumentLine documentLine, CachedLine cachedLine, ITextSourceVersion newVersion)
		{
			if (parseInfo == null) {
				if (forceParseOnNextRefresh) {
					forceParseOnNextRefresh = false;
					parseInfo = SD.ParserService.Parse(FileName.Create(document.FileName), document) as CSharpFullParseInformation;
				} else {
					parseInfo = SD.ParserService.GetCachedParseInformation(FileName.Create(document.FileName), newVersion) as CSharpFullParseInformation;
				}
			}
			if (parseInfo == null) {
				if (invalidLines != null && !invalidLines.Contains(documentLine)) {
					invalidLines.Add(documentLine);
					//Debug.WriteLine("Semantic highlighting for line {0} - marking as invalid", lineNumber);
				}
				
				if (cachedLine != null) {
					// If there's a cached version, adjust it to the latest document changes and return it.
					// This avoids flickering when changing a line that contains semantic highlighting.
					cachedLine.Update(newVersion);
					#if DEBUG
					cachedLine.HighlightedLine.ValidateInvariants();
					#endif
					return cachedLine.HighlightedLine;
				} else {
					return null;
				}
			}
			
			if (visitor.Resolver == null) {
				var compilation = SD.ParserService.GetCompilationForFile(parseInfo.FileName);
				visitor.Resolver = parseInfo.GetResolver(compilation);
			}
			
			line = new HighlightedLine(document, documentLine);
			this.lineNumber = lineNumber;
			visitor.UpdateLineInformation(lineNumber);

			if (Debugger.IsAttached) {
				parseInfo.SyntaxTree.AcceptVisitor(visitor);
				#if DEBUG
				line.ValidateInvariants();
				#endif
			} else {
				try {
					parseInfo.SyntaxTree.AcceptVisitor(visitor);
					#if DEBUG
					line.ValidateInvariants();
					#endif
				} catch (Exception ex) {
					hasCrashed = true;
					throw new ApplicationException("Error highlighting line " + lineNumber, ex);
				}
			}
			//Debug.WriteLine("Semantic highlighting for line {0} - added {1} sections", lineNumber, line.Sections.Count);
			if (cachedLines != null && document.Version != null) {
				cachedLines.Add(new CachedLine(line, document.Version));
			}
			return line;
		}