GetCharAt() public method

public GetCharAt ( Mono.TextEditor.DocumentLocation location ) : char
location Mono.TextEditor.DocumentLocation
return char
示例#1
0
        public override ICompletionDataList CodeCompletionCommand(CodeCompletionContext completionContext)
        {
            int  triggerWordLength = 0;
            char ch = completionContext.TriggerOffset > 0 ? textEditorData.GetCharAt(completionContext.TriggerOffset - 1) : '\0';

            return(InternalHandleCodeCompletion(completionContext, ch, true, ref triggerWordLength));
        }
		public static string FindAttributeName (TextEditorData editor, ICompilationUnit unit, string fileName)
		{
			int caretOffset = editor.Caret.Offset;
			int pos = -1;
			for (int i = caretOffset - 1; i >= 0; i--) {
				if (editor.GetCharAt (i) == '[') {
					pos = i;
					break;
				}
			}
			if (pos <= 0)
				return null;
			pos++;
			StringBuilder result = new StringBuilder ();
			while (pos < caretOffset) {
				char ch = editor.GetCharAt (pos);
				if (!(Char.IsLetterOrDigit (ch) || ch == '_'))
					break;
				result.Append (ch);
				pos++;
			}
			return result.ToString ();
		}
		void ConvertVerbatimStringToNormal (TextEditorData textEditorData, int offset)
		{
			var endOffset = offset;
			while (endOffset < textEditorData.Length) {
				char ch = textEditorData.GetCharAt (endOffset);
				if (ch == '"' && (endOffset + 1 < textEditorData.Length && textEditorData.GetCharAt (endOffset + 1) == '"'))  {
					endOffset += 2;
					continue;
				}
				if (ch == '"')
					break;
				endOffset++;
			}
			textEditorData.Replace (offset, endOffset - offset, ConvertToStringLiteral (ConvertFromVerbatimString (textEditorData.GetTextAt (offset, endOffset - offset))));
		}
		public ExpressionContext FindExactContextForObjectInitializer (TextEditorData editor, ICompilationUnit unit, string fileName, IType callingType)
		{
			int caretOffset = editor.Caret.Offset;
			
			// create a table with all opening brackets
			Dictionary<int, int> brackets = new Dictionary<int, int> ();
			Stack<int> bracketStack = new Stack<int> ();
			for (int i = 0; i < caretOffset; i++) {
				char ch = editor.GetCharAt (i);
				switch (ch) {
				case '/':
					if (i + 1 < caretOffset) {
						if (editor.GetCharAt (i + 1) == '/') {
							while (i < caretOffset) {
								if (editor.GetCharAt (i) == '\n')
									break;
								i++;
							}
						} else if (editor.GetCharAt (i + 1) == '*') {
							while (i + 1 < caretOffset) {
								if (editor.GetCharAt (i) == '*' && editor.GetCharAt (i + 1) == '/')
									break;
								i++;
							}
						}
					}
					break;
				case '(':
				case '{':
				case '[':
					bracketStack.Push (i);
					break;
				case ')':
				case '}':
				case ']':
					if (bracketStack.Count > 0)
						brackets[i] = bracketStack.Pop ();
					break;
				}
			}
			bool foundCurlyBrace = false;
			for (int i = caretOffset - 1; i >= 0; i--) {
				char ch = editor.GetCharAt (i);
				if (ch == '{') {
					foundCurlyBrace = true;
				} else if (ch == ')' || ch == '}' || ch == ']') {
					int newPos;
					if (brackets.TryGetValue (i, out newPos)) {
						i = newPos;
						// we've had a  Property = new Name (), expression, now search for the '='
						// otherwise the "new Name" would be falsly taken as object initializer
						if (!foundCurlyBrace) {
							while (i >= 0) {
								if (editor.GetCharAt (i) == '=' || editor.GetCharAt (i) == ';')
									break;
								i--;
							}
						}
						continue;
					}
				}
				if (i + 4 < caretOffset && editor.GetTextAt (i, 4) == "new ") {
					bool skip = false;
					for (int j2 = i; j2 < caretOffset; j2++) {
						if (editor.GetCharAt (j2) == '{')
							break;
						if (editor.GetCharAt (j2) == ',') {
							skip = true;
							break;
						}
					}
					
					if (skip)
						continue;
					
					int j = i + 4;
					while (j < caretOffset && Char.IsWhiteSpace (editor.GetCharAt (j)))
						j++;
//					int start = j;
					while (j < caretOffset && (Char.IsLetterOrDigit (editor.GetCharAt (j)) || editor.GetCharAt (j) == '_' || editor.GetCharAt (j) == '.'))
						j++;
					
					ExpressionResult firstExprs = FindExpression (editor, j);
					if (firstExprs.Expression != null) {
						// skip ws
						while (j < caretOffset && Char.IsWhiteSpace (editor.GetCharAt (j)))
							j++;
						if (editor.GetCharAt (j) == '[') {
							StringBuilder expr = new StringBuilder (firstExprs.Expression);
							while (j < caretOffset) {
								ch = editor.GetCharAt (j);
								switch (ch) {
								case '[':
								case ',':
								case ']':
									expr.Append (ch);
									break;
								case ' ':
								case '\t':
									break;
								default:
									j = caretOffset;
									break;
								}
								j++;
							}
							firstExprs.Expression = expr.ToString ();
						}
						IReturnType unresolvedReturnType = NRefactoryResolver.ParseReturnType (firstExprs);
						if (unresolvedReturnType != null) {
							IType resolvedType = projectContent.SearchType (unit, (INode)callingType ?? unit, unresolvedReturnType);
							return ExpressionContext.TypeDerivingFrom (resolvedType != null ? new DomReturnType (resolvedType) : null, unresolvedReturnType, true);
						}
					}
					
				}
			}
			return null;
		}
        //
        // TODO - It seems the support for properties varies
        // depending on the ECMA script requested version.
        // Fix the code here.
        //
        public static int GuessSemicolonInsertionOffset(TextEditorData data, DocumentLine currLine, int caretOffset)
        {
            int lastNonWsOffset = caretOffset;
            char lastNonWsChar = '\0';
            int max = currLine.EndOffset;

            // PORT NOTE: Honestly, this looks like a hack.
            if (caretOffset - 2 >= currLine.Offset && data.Document.GetCharAt (caretOffset - 2) == ')')
                return caretOffset;

            int end = caretOffset;
            while (end > 1 && Char.IsWhiteSpace (data.GetCharAt (end)))
                end--;

            int end2 = end;
            while (end2 > 1 && Char.IsLetter (data.GetCharAt (end2 - 1)))
                end2--;

            if (end != end2) {
                string token = data.GetTextBetween (end2, end + 1);
                // guess property context
                if (token == "get" || token == "set")
                    return caretOffset;
            }

            bool isInDQuotedString = false, isInSQuotedString = false;
            bool isInLineComment = false , isInBlockComment = false;

            // Compare the first check against the original one.
            for (int pos = caretOffset; pos < max; pos++) {
                if (pos == caretOffset) {
                    if (isInDQuotedString || isInSQuotedString || isInLineComment || isInBlockComment)
                        return pos;
                }

                char ch = data.Document.GetCharAt (pos);
                switch (ch) {
                case '/':
                    if (isInBlockComment) {
                        if (pos > 0 && data.Document.GetCharAt (pos - 1) == '*')
                            isInBlockComment = false;

                    } else if (!(isInDQuotedString || isInSQuotedString) && pos + 1 < max) {
                        char nextChar = data.Document.GetCharAt (pos + 1);
                        if (nextChar == '/') {
                            isInLineComment = true;
                            return lastNonWsOffset;
                        }
                        if (!isInLineComment && nextChar == '*') {
                            isInBlockComment = true;
                            return lastNonWsOffset;
                        }
                    }

                    break;
                case '\\':
                    if (isInSQuotedString || isInDQuotedString)
                        pos++;

                    break;
                case '"':
                    if (!(isInSQuotedString || isInLineComment || isInBlockComment)) {
                        if (isInDQuotedString && pos + 1 < max && data.Document.GetCharAt (pos + 1) == '"')
                            pos++;
                        else
                            isInDQuotedString = !isInDQuotedString;
                    }

                    break;
                case '\'':
                    if (!(isInDQuotedString || isInLineComment || isInBlockComment))
                        isInSQuotedString = !isInSQuotedString;

                    break;
                }

                if (!Char.IsWhiteSpace (ch)) {
                    lastNonWsOffset = pos;
                    lastNonWsChar = ch;
                }
            }

            // if the line ends with ';' the line end is not the correct place for a new semicolon.
            if (lastNonWsChar == ';')
                return caretOffset;

            return lastNonWsOffset;
        }
		public static List<InsertionPoint> GetInsertionPoints (TextEditorData data, ParsedDocument parsedDocument, IUnresolvedTypeDefinition type)
		{
			if (data == null)
				throw new ArgumentNullException ("data");
			if (parsedDocument == null)
				throw new ArgumentNullException ("parsedDocument");
			if (type == null)
				throw new ArgumentNullException ("type");
			
			// update type from parsed document, since this is always newer.
			//type = parsedDocument.GetInnermostTypeDefinition (type.GetLocation ()) ?? type;
			List<InsertionPoint> result = new List<InsertionPoint> ();
			int offset = data.LocationToOffset (type.Region.Begin);
			if (offset < 0 || type.BodyRegion.IsEmpty)
				return result;
			while (offset < data.Length && data.GetCharAt (offset) != '{') {
				offset++;
			}
			var realStartLocation = data.OffsetToLocation (offset);
			result.Add (GetInsertionPosition (data.Document, realStartLocation.Line, realStartLocation.Column));
			result [0].LineBefore = NewLineInsertion.None;
			
			foreach (var member in type.Members) {
				TextLocation domLocation = member.BodyRegion.End;
				if (domLocation.Line <= 0) {
					DocumentLine lineSegment = data.GetLine (member.Region.BeginLine);
					if (lineSegment == null)
						continue;
					domLocation = new TextLocation (member.Region.BeginLine, lineSegment.Length + 1);
				}
				result.Add (GetInsertionPosition (data.Document, domLocation.Line, domLocation.Column));
			}

			foreach (var nestedType in type.NestedTypes) {
				TextLocation domLocation = nestedType.BodyRegion.End;
				if (domLocation.Line <= 0) {
					DocumentLine lineSegment = data.GetLine (nestedType.Region.BeginLine);
					if (lineSegment == null)
						continue;
					domLocation = new TextLocation (nestedType.Region.BeginLine, lineSegment.Length + 1);
				}
				result.Add (GetInsertionPosition (data.Document, domLocation.Line, domLocation.Column));
			}

			result [result.Count - 1].LineAfter = NewLineInsertion.None;
			CheckStartPoint (data.Document, result [0], result.Count == 1);
			if (result.Count > 1) {
				result.RemoveAt (result.Count - 1); 
				NewLineInsertion insertLine;
				var lineBefore = data.GetLine (type.BodyRegion.EndLine - 1);
				if (lineBefore != null && lineBefore.Length == lineBefore.GetIndentation (data.Document).Length) {
					insertLine = NewLineInsertion.None;
				} else {
					insertLine = NewLineInsertion.Eol;
				}
				// search for line start
				int col = type.BodyRegion.EndColumn - 1;
				var line = data.GetLine (type.BodyRegion.EndLine);
				if (line != null) {
					while (col > 1 && char.IsWhiteSpace (data.GetCharAt (line.Offset + col - 2)))
						col--;
				}
				result.Add (new InsertionPoint (new DocumentLocation (type.BodyRegion.EndLine, col), insertLine, NewLineInsertion.Eol));
				CheckEndPoint (data.Document, result [result.Count - 1], result.Count == 1);
			}
			
			foreach (var region in parsedDocument.UserRegions.Where (r => type.BodyRegion.IsInside (r.Region.Begin))) {
				result.Add (new InsertionPoint (new DocumentLocation (region.Region.BeginLine + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol));
				result.Add (new InsertionPoint (new DocumentLocation (region.Region.EndLine, 1), NewLineInsertion.Eol, NewLineInsertion.Eol));
				result.Add (new InsertionPoint (new DocumentLocation (region.Region.EndLine + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol));
			}
			result.Sort ((left, right) => left.Location.CompareTo (right.Location));
			return result;
		}
		static void ConvertVerbatimStringToNormal (TextEditorData textEditorData, int offset)
		{
			var endOffset = offset;
			while (endOffset < textEditorData.Length) {
				char ch = textEditorData.GetCharAt (endOffset);
				if (ch == '"' && (endOffset + 1 < textEditorData.Length && textEditorData.GetCharAt (endOffset + 1) == '"')) {
					endOffset += 2;
					continue;
				}
				if (ch == '"') {
					break;
				}
				endOffset++;
			}
			var plainText = TextPasteUtils.VerbatimStringStrategy.Decode (textEditorData.GetTextAt (offset, endOffset - offset));
			var newText = TextPasteUtils.StringLiteralPasteStrategy.Instance.Encode (plainText);
			textEditorData.Replace (offset, endOffset - offset, newText);
		}
		public static void SetCompletionText (TextEditorData data, CodeCompletionContext ctx, string partialWord, string completeWord, int wordOffset)
		{
			if (data == null || data.Document == null)
				return;

			int triggerOffset = ctx.TriggerOffset;
			int length = String.IsNullOrEmpty (partialWord) ? 0 : partialWord.Length;

			// for named arguments invoke(arg:<Expr>);
			if (completeWord.EndsWith (":", StringComparison.Ordinal)) {
				if (data.GetCharAt (triggerOffset + length) == ':')
					length++;
			}

			bool blockMode = false;
			if (data.IsSomethingSelected) {
				blockMode = data.MainSelection.SelectionMode == Mono.TextEditor.SelectionMode.Block;
				if (blockMode) {
					data.Caret.PreserveSelection = true;
					triggerOffset = data.Caret.Offset - length;
				} else {
					if (data.SelectionRange.Offset < ctx.TriggerOffset)
						triggerOffset = ctx.TriggerOffset - data.SelectionRange.Length;
					data.DeleteSelectedText ();
				}
				length = 0;
			}

			// | in the completion text now marks the caret position
			int idx = completeWord.IndexOf ('|');
			if (idx >= 0) {
				completeWord = completeWord.Remove (idx, 1);
			}
			
			triggerOffset += data.EnsureCaretIsNotVirtual ();
			if (blockMode) {
				using (var undo = data.OpenUndoGroup ()) {

					int minLine = data.MainSelection.MinLine;
					int maxLine = data.MainSelection.MaxLine;
					int column = triggerOffset - data.Document.GetLineByOffset (triggerOffset).Offset;
					for (int lineNumber = minLine; lineNumber <= maxLine; lineNumber++) {
						DocumentLine lineSegment = data.Document.GetLine (lineNumber);
						if (lineSegment == null)
							continue;
						int offset = lineSegment.Offset + column;
						data.Replace (offset, length, completeWord);
					}
					int minColumn = Math.Min (data.MainSelection.Anchor.Column, data.MainSelection.Lead.Column);
					data.MainSelection = data.MainSelection.WithRange (
						new Mono.TextEditor.DocumentLocation (data.Caret.Line == minLine ? maxLine : minLine, minColumn),
						data.Caret.Location
					);

					data.Document.CommitMultipleLineUpdate (data.MainSelection.MinLine, data.MainSelection.MaxLine);
					data.Caret.PreserveSelection = false;
				}
			} else {
				data.Replace (triggerOffset, length, completeWord);
			}
			
			data.Document.CommitLineUpdate (data.Caret.Line);
			if (idx >= 0)
				data.Caret.Offset = triggerOffset + idx;

		}
        public MonoDevelop.Projects.Dom.ResolveResult GetLanguageItem(ProjectDom dom, Mono.TextEditor.TextEditorData data, int offset, string expression)
        {
            string fileName = data.Document.FileName;

            MonoDevelop.Ide.Gui.Document doc = IdeApp.Workbench.ActiveDocument;
            if (doc == null)
            {
                return(null);
            }

            IParser parser = ProjectDomService.GetParser(fileName);

            if (parser == null)
            {
                return(null);
            }

            IResolver         resolver         = parser.CreateResolver(dom, doc, fileName);
            IExpressionFinder expressionFinder = parser.CreateExpressionFinder(dom);

            if (resolver == null || expressionFinder == null)
            {
                return(null);
            }
            int wordEnd = offset;

            while (wordEnd < data.Length && (Char.IsLetterOrDigit(data.GetCharAt(wordEnd)) || data.GetCharAt(wordEnd) == '_'))
            {
                wordEnd++;
            }
            ExpressionResult expressionResult = new ExpressionResult(expression);

            expressionResult.ExpressionContext = ExpressionContext.MethodBody;

            DocumentLocation loc             = data.Document.OffsetToLocation(offset);
            string           savedExpression = null;
            ResolveResult    resolveResult;

            if (expressionResult.ExpressionContext == ExpressionContext.Attribute)
            {
                savedExpression                    = expressionResult.Expression;
                expressionResult.Expression       += "Attribute";
                expressionResult.ExpressionContext = ExpressionContext.ObjectCreation;
            }
            resolveResult = resolver.Resolve(expressionResult, new DomLocation(loc.Line, loc.Column));
            if (savedExpression != null && resolveResult == null)
            {
                expressionResult.Expression = savedExpression;
                resolveResult = resolver.Resolve(expressionResult, new DomLocation(loc.Line, loc.Column));
            }
            if (expressionResult.Region.End.IsEmpty)
            {
                return(resolveResult);
            }
            // Search for possible generic parameters.
//			if (this.resolveResult == null || this.resolveResult.ResolvedType == null || String.IsNullOrEmpty (this.resolveResult.ResolvedType.Name)) {
            int j       = data.Document.LocationToOffset(expressionResult.Region.End.Line, expressionResult.Region.End.Column);
            int bracket = 0;

            for (int i = j; i >= 0 && i < data.Document.Length; i++)
            {
                char ch = data.Document.GetCharAt(i);
                if (Char.IsWhiteSpace(ch))
                {
                    continue;
                }
                if (ch == '<')
                {
                    bracket++;
                }
                else if (ch == '>')
                {
                    bracket--;
                    if (bracket == 0)
                    {
                        expressionResult.Expression       += data.Document.GetTextBetween(j, i + 1);
                        expressionResult.ExpressionContext = ExpressionContext.ObjectCreation;
                        resolveResult = resolver.Resolve(expressionResult, new DomLocation(loc.Line, loc.Column)) ?? resolveResult;
                        break;
                    }
                }
                else
                {
                    if (bracket == 0)
                    {
                        break;
                    }
                }
            }
//			}

            // To resolve method overloads the full expression must be parsed.
            // ex.: Overload (1)/ Overload("one") - parsing "Overload" gives just a MethodResolveResult
            if (resolveResult is MethodResolveResult)
            {
                resolveResult = resolver.Resolve(expressionFinder.FindFullExpression(data, wordEnd), new DomLocation(loc.Line, loc.Column)) ?? resolveResult;
            }
            return(resolveResult);
        }
示例#10
0
        static bool TryFindSymbolBlock(TextEditorData data, char command, out SymbolBlock result)
        {
            char end, begin;
            if (!BeginToEndCharMap.TryGetValue (command, out end)) end = command;
            if (!EndToBeginCharMap.TryGetValue (command, out begin)) begin = command;

            var offset = data.Caret.Offset;

            var startTokenOffset = ParseForChar(data, offset, 0, end, begin, false);
            var endTokenOffset = ParseForChar(data, offset, data.Length, begin, end, true);

            // Use the editor's FindMatchingBrace built-in functionality. It's better at handling erroneous braces
            // inside quotes. We still need to do the above paragraph because we needed to find the braces.
            var matchingStartBrace = endTokenOffset.HasValue ? data.Document.GetMatchingBracketOffset(
                endTokenOffset.GetValueOrDefault ()) : -1;
            if (matchingStartBrace >= 0 && (!startTokenOffset.HasValue
                                            || matchingStartBrace != startTokenOffset.GetValueOrDefault ()))
                startTokenOffset = matchingStartBrace;

            var matchingEndBrace = startTokenOffset.HasValue && data.GetCharAt (offset) != end ?
                data.Document.GetMatchingBracketOffset(startTokenOffset.GetValueOrDefault ()) : -1;
            if (matchingEndBrace >= 0 && (!endTokenOffset.HasValue
                                          || matchingEndBrace != endTokenOffset.GetValueOrDefault ()))
                endTokenOffset = matchingEndBrace;

            if (!startTokenOffset.HasValue || !endTokenOffset.HasValue) throw new ViModeAbortException();

            result = new SymbolBlock
            {
                StartOffset = startTokenOffset.GetValueOrDefault (),
                EndOffset = endTokenOffset.GetValueOrDefault (),
                StartLine = data.GetLineByOffset (startTokenOffset.GetValueOrDefault()),
                EndLine = data.GetLineByOffset (endTokenOffset.GetValueOrDefault()),
            };
            return true;
        }
示例#11
0
 static int? ParseForChar(TextEditorData data, int fromOffset, int toOffset, char oppositeToken, char findToken, bool forward)
 {
     int increment = forward ? 1 : -1;
     var symbolCount = 0;
     for (int i = fromOffset; forward && i < toOffset || !forward && i >= toOffset; i += increment)
     {
         var c = data.GetCharAt(i);
         if (c == oppositeToken)
             symbolCount++;
         else if (c == findToken)
         {
             if (symbolCount == 0) return i;
             symbolCount--;
         }
     }
     return null;
 }
		string GetWordAtOffset (TextEditorData text, int offset, out int begin)
		{
			if (offset < 0 || offset >= text.Length)
			{
				begin = offset;
				return String.Empty;
			}
			
			StringBuilder sb = new StringBuilder ();
			int i = offset;
			char c;
			
			// Look forward for break char
			for (i = offset; i < text.Length; i++)
			{
				c = text.GetCharAt (i);
				
				if (Char.IsWhiteSpace (c))
					break;
				
				bool needsBreak = false;
				
				switch (c) {
				case '(':
				case ')':
				case '[':
				case ']':
				case '{':
				case '}':
				case ':':
				case ',':
				case '@':
				case '.':
					needsBreak = true;
					break;
				default:
					sb.Append (c);
					break;
				}
				
				if (Char.IsWhiteSpace (c) || needsBreak)
					break;
			}
			
			if (offset > 0)
			{
				// look backwards for break char
				for (i = offset - 1; i > 0; i--)
				{
					c = text.GetCharAt (i);
					
					if (Char.IsWhiteSpace (c))
						break;
					
					bool needsBreak = false;
					
					switch (c) {
					case '(':
					case ')':
					case '[':
					case ']':
					case '{':
					case '}':
					case ':':
					case ',':
					case '@':
					case '.':
						needsBreak = true;
						break;
					default:
						sb.Insert (0, c);
						break;
					}
					
					if (needsBreak)
						break;
				}
			}
			
			begin = i;
			
			return sb.ToString ();
		}
		public static bool GuessSemicolonInsertionOffset (TextEditorData data, ISegment curLine, int caretOffset, out int outOffset)
		{
			int lastNonWsOffset = caretOffset;
			char lastNonWsChar = '\0';
			outOffset = caretOffset;

			int max = curLine.EndOffset;

			int end = caretOffset;
			while (end > 1 && char.IsWhiteSpace (data.GetCharAt (end)))
				end--;
			int end2 = end;
			while (end2 > 1 && char.IsLetter (data.GetCharAt (end2 - 1)))
				end2--;
			if (end != end2) {
				string token = data.GetTextBetween (end2, end + 1);
				// guess property context
				if (token == "get" || token == "set")
					return false;
			}

			var offset = curLine.Offset;
			string lineText = data.GetTextAt (caretOffset, max - caretOffset);
			var lexer = new CSharpCompletionEngineBase.MiniLexer (lineText);
			lexer.Parse ((ch, i) => {
				if (lexer.IsInSingleComment || lexer.IsInMultiLineComment)
					return true;
				if (ch == '}' && lexer.IsFistNonWs && !IsSemicolonalreadyPlaced (data, caretOffset)) {
					lastNonWsChar = ';';
					return true;
				}
				if (!char.IsWhiteSpace (ch)) {
					lastNonWsOffset = caretOffset + i;
					lastNonWsChar = ch;
				}
				return false;
			}); 
			// if the line ends with ';' the line end is not the correct place for a new semicolon.
			if (lastNonWsChar == ';')
				return false;
			outOffset = lastNonWsOffset;
			return true;
		}
		static int SearchMatchingBracket (TextEditorData editor, int offset, char openBracket, char closingBracket, int direction)
		{
			bool isInString       = false;
			bool isInChar         = false;	
			bool isInBlockComment = false;
			int depth = -1;
			while (offset >= 0 && offset < editor.Length) {
				char ch = editor.GetCharAt (offset);
				switch (ch) {
					case '/':
						if (isInBlockComment) 
							isInBlockComment = editor.GetCharAt (offset + direction) != '*';
						if (!isInString && !isInChar && offset - direction < editor.Length) 
							isInBlockComment = offset > 0 && editor.GetCharAt (offset - direction) == '*';
						break;
					case '"':
						if (!isInChar && !isInBlockComment) 
							isInString = !isInString;
						break;
					case '\'':
						if (!isInString && !isInBlockComment) 
							isInChar = !isInChar;
						break;
					default :
						if (ch == closingBracket) {
							if (!(isInString || isInChar || isInBlockComment)) 
								--depth;
						} else if (ch == openBracket) {
							if (!(isInString || isInChar || isInBlockComment)) {
								++depth;
								if (depth == 0) 
									return offset;
							}
						}
						break;
				}
				offset += direction;
			}
			return -1;
		}
        public MonoDevelop.Projects.Dom.ResolveResult GetLanguageItem(ProjectDom dom, Mono.TextEditor.TextEditorData data, int offset)
        {
            if (offset < 0)
            {
                return(null);
            }
            string  fileName = data.Document.FileName;
            IParser parser   = ProjectDomService.GetParser(fileName);

            if (parser == null)
            {
                return(null);
            }

            MonoDevelop.Ide.Gui.Document doc = IdeApp.Workbench.ActiveDocument;
            if (doc == null)
            {
                return(null);
            }

            IResolver resolver = parser.CreateResolver(dom, doc, fileName);

            if (resolver == null)
            {
                return(null);
            }
            var expressionFinder = new NewCSharpExpressionFinder(dom);

            int wordEnd = Math.Min(offset, data.Length - 1);

            if (wordEnd < 0)
            {
                return(null);
            }
            if (data.GetCharAt(wordEnd) == '@')
            {
                wordEnd++;
            }
            while (wordEnd < data.Length && (Char.IsLetterOrDigit(data.GetCharAt(wordEnd)) || data.GetCharAt(wordEnd) == '_'))
            {
                wordEnd++;
            }

            while (wordEnd < data.Length - 1 && Char.IsWhiteSpace(data.GetCharAt(wordEnd)))
            {
                wordEnd++;
            }

            /* is checked at the end.
             * int saveEnd = wordEnd;
             * if (wordEnd < data.Length && data.GetCharAt (wordEnd) == '<') {
             *      int matchingBracket = data.Document.GetMatchingBracketOffset (wordEnd);
             *      if (matchingBracket > 0)
             *              wordEnd = matchingBracket;
             *      while (wordEnd < data.Length - 1 && Char.IsWhiteSpace (data.GetCharAt (wordEnd)))
             *              wordEnd++;
             * }
             *
             * bool wasMethodCall = false;
             * if (data.GetCharAt (wordEnd) == '(') {
             *      int matchingBracket = data.Document.GetMatchingBracketOffset (wordEnd);
             *      if (matchingBracket > 0) {
             *              wordEnd = matchingBracket;
             *              wasMethodCall = true;
             *      }
             * }
             * if (!wasMethodCall)
             *      wordEnd = saveEnd;*/

            ExpressionResult expressionResult = expressionFinder.FindExpression(data, wordEnd);

            if (expressionResult == null)
            {
                return(null);
            }
            ResolveResult    resolveResult;
            DocumentLocation loc             = data.Document.OffsetToLocation(offset);
            string           savedExpression = null;

            // special handling for 'var' "keyword"
            if (expressionResult.ExpressionContext == ExpressionContext.IdentifierExpected && expressionResult.Expression != null && expressionResult.Expression.Trim() == "var")
            {
                int           endOffset = data.Document.LocationToOffset(expressionResult.Region.End.Line, expressionResult.Region.End.Column);
                StringBuilder identifer = new StringBuilder();
                for (int i = endOffset; i >= 0 && i < data.Document.Length; i++)
                {
                    char ch = data.Document.GetCharAt(i);
                    if (Char.IsWhiteSpace(ch))
                    {
                        continue;
                    }
                    if (ch == '=')
                    {
                        break;
                    }
                    if (Char.IsLetterOrDigit(ch) || ch == '_')
                    {
                        identifer.Append(ch);
                        continue;
                    }
                    identifer.Length = 0;
                    break;
                }
                if (identifer.Length > 0)
                {
                    expressionResult.Expression = identifer.ToString();
                    resolveResult = resolver.Resolve(expressionResult, new DomLocation(loc.Line, loc.Column));
                    if (resolveResult != null)
                    {
                        resolveResult = new MemberResolveResult(dom.GetType(resolveResult.ResolvedType));
                        resolveResult.ResolvedExpression = expressionResult;
                        return(resolveResult);
                    }
                }
            }

            if (expressionResult.ExpressionContext == ExpressionContext.Attribute && !string.IsNullOrEmpty(expressionResult.Expression))
            {
                savedExpression                    = expressionResult.Expression;
                expressionResult.Expression        = expressionResult.Expression.Trim() + "Attribute";
                expressionResult.ExpressionContext = ExpressionContext.ObjectCreation;
            }

            resolveResult = resolver.Resolve(expressionResult, new DomLocation(loc.Line, loc.Column));
            if (savedExpression != null && resolveResult == null)
            {
                expressionResult.Expression = savedExpression;
                resolveResult = resolver.Resolve(expressionResult, new DomLocation(loc.Line, loc.Column));
            }
            // Search for possible generic parameters.
//			if (this.resolveResult == null || this.resolveResult.ResolvedType == null || String.IsNullOrEmpty (this.resolveResult.ResolvedType.Name)) {
            if (!expressionResult.Region.IsEmpty)
            {
                int j       = data.Document.LocationToOffset(expressionResult.Region.End.Line, expressionResult.Region.End.Column);
                int bracket = 0;
                for (int i = j; i >= 0 && i < data.Document.Length; i++)
                {
                    char ch = data.Document.GetCharAt(i);
                    if (Char.IsWhiteSpace(ch))
                    {
                        continue;
                    }
                    if (ch == '<')
                    {
                        bracket++;
                    }
                    else if (ch == '>')
                    {
                        bracket--;
                        if (bracket == 0)
                        {
                            expressionResult.Expression       += data.Document.GetTextBetween(j, i + 1);
                            expressionResult.ExpressionContext = ExpressionContext.ObjectCreation;
                            resolveResult = resolver.Resolve(expressionResult, new DomLocation(loc.Line, loc.Column));
                            break;
                        }
                    }
                    else
                    {
                        if (bracket == 0)
                        {
                            break;
                        }
                    }
                }
            }

            // To resolve method overloads the full expression must be parsed.
            // ex.: Overload (1)/ Overload("one") - parsing "Overload" gives just a MethodResolveResult
            // and for constructor initializers it's tried too to to resolve constructor overloads.
            if (resolveResult is ThisResolveResult ||
                resolveResult is BaseResolveResult ||
                resolveResult is MethodResolveResult && ((MethodResolveResult)resolveResult).Methods.Count > 1)
            {
                // put the search offset at the end of the invocation to be able to find the full expression
                // the resolver finds it itself if spaces are between the method name and the argument opening parentheses.
                while (wordEnd < data.Length - 1 && Char.IsWhiteSpace(data.GetCharAt(wordEnd)))
                {
                    wordEnd++;
                }
                if (data.GetCharAt(wordEnd) == '(')
                {
                    int matchingBracket = data.Document.GetMatchingBracketOffset(wordEnd);
                    if (matchingBracket > 0)
                    {
                        wordEnd = matchingBracket;
                    }
                }
                //Console.WriteLine (expressionFinder.FindFullExpression (txt, wordEnd));
                ResolveResult possibleResult = resolver.Resolve(expressionFinder.FindFullExpression(data, wordEnd), new DomLocation(loc.Line, loc.Column)) ?? resolveResult;
                //Console.WriteLine ("possi:" + resolver.Resolve (expressionFinder.FindFullExpression (txt, wordEnd), new DomLocation (loc.Line, loc.Column)));
                if (possibleResult is MethodResolveResult)
                {
                    resolveResult = possibleResult;
                }
            }
            return(resolveResult);
        }
示例#16
0
		static int FindPrevWordStart (TextEditorData editor, int offset)
		{
			while (--offset >= 0 && !Char.IsWhiteSpace (editor.GetCharAt (offset))) 
				;
			return ++offset;
		}
		public ExpressionContext FindExactContextForNewCompletion (TextEditorData editor, ICompilationUnit unit, string fileName, IType callingType, int cursorPos)
		{
			// find expression on left hand side of the assignment
			int caretOffset = editor.Caret.Offset;
			int pos = -1;
			for (int i = caretOffset - 1; i >= 0; i--) {
				if (editor.GetCharAt (i) == '=') {
					if (i > 0 && (editor.GetCharAt (i - 1) == '+' || editor.GetCharAt (i - 1) == '-'))
						i--;
					pos = i;
					break;
				}
			}
			if (pos <= 0)
				return null;
			
			// check if new +=/-=/= is right before "new"
			for (int i = pos; i < cursorPos; i++) {
				char ch = editor.GetCharAt (i);
				if (Char.IsWhiteSpace (ch))
					continue;
				if (ch != '=' && ch != '+' && ch != '-' && ch != 'n' && ch != 'e' && ch != 'w')
					return null;
			}
			int lastWs = pos - 1;
			while (lastWs > 0 && char.IsWhiteSpace (editor.GetCharAt (lastWs)))
				lastWs--;
			
			int varTypePos = lastWs;
			while (varTypePos > 0 && !char.IsWhiteSpace (editor.GetCharAt (varTypePos)))
				varTypePos--;
			while (varTypePos > 0 && char.IsWhiteSpace (editor.GetCharAt (varTypePos)))
				varTypePos--;
			
			ExpressionResult possibleTypeExpression = FindFullExpression (editor, varTypePos);
			if (possibleTypeExpression.Expression != null) {
				if (possibleTypeExpression.Expression == "var")
					return ExpressionContext.TypeDerivingFrom (DomReturnType.Object, DomReturnType.Object, true);
				IReturnType unresolvedReturnType = NRefactoryResolver.ParseReturnType (possibleTypeExpression);
				if (unresolvedReturnType != null) {
					IType resolvedType = projectContent.SearchType (unit, callingType, new DomLocation (editor.Caret.Line, editor.Caret.Column), unresolvedReturnType);
					if (resolvedType != null)
						return ExpressionContext.TypeDerivingFrom (new DomReturnType (resolvedType), unresolvedReturnType, true);
				}
			}
			
			ExpressionResult lhsExpr = FindFullExpression (editor, lastWs);
			if (lhsExpr.Expression != null) {
				NRefactoryResolver resolver = new NRefactoryResolver (projectContent, unit, ICSharpCode.OldNRefactory.SupportedLanguage.CSharp, editor, fileName);
				
				ResolveResult rr = resolver.Resolve (lhsExpr, new DomLocation (editor.Caret.Line, editor.Caret.Column));
				//ResolveResult rr = ParserService.Resolve (lhsExpr, currentLine.LineNumber, pos, editor.FileName, editor.Text);
				if (rr != null && rr.ResolvedType != null) {
					ExpressionContext context;
					IType c;
					/*					if (rr.ResolvedType.IsArrayReturnType) {
						// when creating an array, all classes deriving from the array's element type are allowed
						IReturnType elementType = rr.ResolvedType.CastToArrayReturnType().ArrayElementType;
						c = elementType != null ? dom.GetType (elementType) : null;
						context = ExpressionContext.TypeDerivingFrom(elementType, false);
					} else */					{
						// when creating a normal instance, all non-abstract classes deriving from the type
						// are allowed
						c = projectContent.GetType (rr.ResolvedType);
						context = ExpressionContext.TypeDerivingFrom (rr.ResolvedType, null, true);
					}
					if (c != null && !context.FilterEntry (c)) {
						// Try to suggest an entry (List<int> a = new => suggest List<int>).
						
						string suggestedClassName = null;
						/*LanguageProperties.CSharp.CodeGenerator.GenerateCode(
							CodeGenerator.ConvertType(
								rr.ResolvedType,
								new ClassFinder(ParserService.GetParseInformation(editor.FileName), editor.ActiveTextAreaControl.Caret.Line + 1, editor.ActiveTextAreaControl.Caret.Column + 1)
							), "");*/						if (suggestedClassName != c.Name) {
							// create an IType instance that includes the type arguments in its name
							//context.DefaultItem = new RenamedClass (c, suggestedClassName);
						} else {
							context.DefaultItem = c;
						}
					}
					return context;
				}
			}
		
			return null;
		}
		static void ConvertNormalToVerbatimString (TextEditorData textEditorData, int offset)
		{
			var endOffset = offset;
			while (endOffset < textEditorData.Length) {
				char ch = textEditorData.GetCharAt (endOffset);
				if (ch == '\\') {
					if (endOffset + 1 < textEditorData.Length && NewLine.IsNewLine (textEditorData.GetCharAt (endOffset + 1)))
						return;

					endOffset += 2;
					continue;
				}
				if (ch == '"')
					break;
				if (NewLine.IsNewLine (ch))
					return;
				endOffset++;
			}
			if (offset > endOffset || endOffset == textEditorData.Length)
				return;
			var plainText = TextPasteUtils.StringLiteralPasteStrategy.Instance.Decode (textEditorData.GetTextAt (offset, endOffset - offset));
			var newText = TextPasteUtils.VerbatimStringStrategy.Encode (plainText);
			textEditorData.Replace (offset, endOffset - offset, newText);
		}
		public static List<InsertionPoint> GetInsertionPoints (TextEditorData data, ParsedDocument parsedDocument, IType type)
		{
			if (data == null)
				throw new ArgumentNullException ("data");
			if (parsedDocument == null)
				throw new ArgumentNullException ("parsedDocument");
			if (type == null)
				throw new ArgumentNullException ("type");
			List<InsertionPoint> result = new List<InsertionPoint> ();
			int offset = data.LocationToOffset (type.BodyRegion.Start.Line, type.BodyRegion.Start.Column);
			if (offset < 0)
				return result;
			
			while (offset < data.Length && data.GetCharAt (offset) != '{') {
				offset++;
			}
			
			var realStartLocation = data.OffsetToLocation (offset);
			result.Add (GetInsertionPosition (data.Document, realStartLocation.Line, realStartLocation.Column));
			result[0].LineBefore = NewLineInsertion.None;
			foreach (IMember member in type.Members) {
				DomLocation domLocation = member.BodyRegion.End;
				if (domLocation.Line <= 0) {
					LineSegment lineSegment = data.GetLine (member.Location.Line);
					if (lineSegment == null)
						continue;
					domLocation = new DomLocation (member.Location.Line, lineSegment.EditableLength + 1);
				}
				result.Add (GetInsertionPosition (data.Document, domLocation.Line, domLocation.Column));
			}
			result[result.Count - 1].LineAfter = NewLineInsertion.None;
			CheckStartPoint (data.Document, result[0], result.Count == 1);
			if (result.Count > 1) {
				result.RemoveAt (result.Count - 1); 
				NewLineInsertion insertLine;
				var lineBefore = data.GetLine (type.BodyRegion.End.Line - 1);
				if (lineBefore != null && lineBefore.EditableLength == lineBefore.GetIndentation (data.Document).Length) {
					insertLine = NewLineInsertion.None;
				} else {
					insertLine = NewLineInsertion.Eol;
				}
				// search for line start
				var line = data.GetLine (type.BodyRegion.End.Line);
				int col = type.BodyRegion.End.Column - 1;
				while (col > 1 && char.IsWhiteSpace (data.GetCharAt (line.Offset + col - 2)))
					col--;
				result.Add (new InsertionPoint (new DocumentLocation (type.BodyRegion.End.Line, col), insertLine, NewLineInsertion.Eol));
				CheckEndPoint (data.Document, result[result.Count - 1], result.Count == 1);
			}
			
			foreach (var region in parsedDocument.UserRegions.Where (r => type.BodyRegion.Contains (r.Region))) {
				result.Add (new InsertionPoint (new DocumentLocation (region.Region.Start.Line + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol));
				result.Add (new InsertionPoint (new DocumentLocation (region.Region.End.Line, 1), NewLineInsertion.Eol, NewLineInsertion.Eol));
				result.Add (new InsertionPoint (new DocumentLocation (region.Region.End.Line + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol));
			}
			result.Sort ((left, right) => left.Location.CompareTo (right.Location));
			return result;
		}
		public static bool GuessSemicolonInsertionOffset (TextEditorData data, IDocumentLine curLine, int caretOffset, out int outOffset)
		{
			int lastNonWsOffset = caretOffset;
			char lastNonWsChar = '\0';
			outOffset = caretOffset;
			int max = curLine.EndOffset;
	//		if (caretOffset - 2 >= curLine.Offset && data.Document.GetCharAt (caretOffset - 2) == ')' && !IsSemicolonalreadyPlaced (data, caretOffset))
	//			return false;

			int end = caretOffset;
			while (end > 1 && char.IsWhiteSpace (data.GetCharAt (end)))
				end--;
			int end2 = end;
			while (end2 > 1 && char.IsLetter(data.GetCharAt (end2 - 1)))
				end2--;
			if (end != end2) {
				string token = data.GetTextBetween (end2, end + 1);
				// guess property context
				if (token == "get" || token == "set")
					return false;
			}

			bool isInString = false , isInChar= false , isVerbatimString= false;
			bool isInLineComment = false , isInBlockComment= false;
			bool firstChar = true;
			for (int pos = caretOffset; pos < max; pos++) {
				if (pos == caretOffset) {
					if (isInString || isInChar || isVerbatimString || isInLineComment || isInBlockComment) {
						outOffset = pos;
						return true;
					}
				}
				char ch = data.Document.GetCharAt (pos);
				switch (ch) {
				case '}':
					if (firstChar && !IsSemicolonalreadyPlaced (data, caretOffset))
						return false;
					break;
				case '/':
					if (isInBlockComment) {
						if (pos > 0 && data.Document.GetCharAt (pos - 1) == '*') 
							isInBlockComment = false;
					} else if (!isInString && !isInChar && pos + 1 < max) {
						char nextChar = data.Document.GetCharAt (pos + 1);
						if (nextChar == '/') {
							outOffset = lastNonWsOffset;
							return true;
						}
						if (!isInLineComment && nextChar == '*') {
							outOffset = lastNonWsOffset;
							return true;
						}
					}
					break;
				case '\\':
					if (isInChar || (isInString && !isVerbatimString))
						pos++;
					break;
				case '@':
					if (!(isInString || isInChar || isInLineComment || isInBlockComment) && pos + 1 < max && data.Document.GetCharAt (pos + 1) == '"') {
						isInString = true;
						isVerbatimString = true;
						pos++;
					}
					break;
				case '"':
					if (!(isInChar || isInLineComment || isInBlockComment)) {
						if (isInString && isVerbatimString && pos + 1 < max && data.Document.GetCharAt (pos + 1) == '"') {
							pos++;
						} else {
							isInString = !isInString;
							isVerbatimString = false;
						}
					}
					break;
				case '\'':
					if (!(isInString || isInLineComment || isInBlockComment)) 
						isInChar = !isInChar;
					break;
				}
				if (!char.IsWhiteSpace (ch)) {
					firstChar = false;
					lastNonWsOffset = pos;
					lastNonWsChar = ch;
				}
			}
			// if the line ends with ';' the line end is not the correct place for a new semicolon.
			if (lastNonWsChar == ';')
				return false;
			outOffset = lastNonWsOffset;
			return true;
		}
		public ExpressionContext FindExactContextForAsCompletion (TextEditorData editor, ICompilationUnit unit, string fileName, IType callingType)
		{
			// find expression on left hand side of the assignment
			int caretOffset = editor.Caret.Offset;
			int pos = -1;
			for (int i = caretOffset - 1; i >= 0; i--) {
				char ch = editor.GetCharAt (i);
				if (Char.IsWhiteSpace (ch))
					continue;
				if (ch == '=') {
					pos = i;
					break;
				}
				if (!(Char.IsLetterOrDigit (ch) || ch == '_'))
					return null;
				
			}
			
			if (pos <= 0)
				return null;
			
			int lastWs = pos - 1;
			while (lastWs > 0 && Char.IsWhiteSpace (editor.GetCharAt (lastWs)))
				lastWs--;
			while (lastWs > 0 && !Char.IsWhiteSpace (editor.GetCharAt (lastWs)))
				lastWs--;
			ExpressionResult firstExprs = FindExpression (editor, lastWs);
			if (firstExprs.Expression != null) {
				IReturnType unresolvedReturnType = NRefactoryResolver.ParseReturnType (firstExprs);
				if (unresolvedReturnType != null) {
					IType resolvedType = projectContent.SearchType (unit, (INode)callingType ?? unit, unresolvedReturnType);
					return ExpressionContext.TypeDerivingFrom (resolvedType != null ? new DomReturnType (resolvedType) : null, unresolvedReturnType, true);
				}
				
			}
			
			ExpressionResult lhsExpr = FindExpression (editor, pos);
			if (lhsExpr.Expression != null) {
				NRefactoryResolver resolver = new NRefactoryResolver (projectContent, unit, ICSharpCode.NRefactory.SupportedLanguage.CSharp, editor, fileName);
				
				ResolveResult rr = resolver.Resolve (lhsExpr, new DomLocation (editor.Caret.Line, editor.Caret.Column));
				//ResolveResult rr = ParserService.Resolve (lhsExpr, currentLine.LineNumber, pos, editor.FileName, editor.Text);
				
				if (rr != null && rr.ResolvedType != null) {
					ExpressionContext context;
					IType c;
					/*					if (rr.ResolvedType.IsArrayReturnType) {
						// when creating an array, all classes deriving from the array's element type are allowed
						IReturnType elementType = rr.ResolvedType.CastToArrayReturnType().ArrayElementType;
						c = elementType != null ? dom.GetType (elementType) : null;
						context = ExpressionContext.TypeDerivingFrom(elementType, false);
					} else */					{
						// when creating a normal instance, all non-abstract classes deriving from the type
						// are allowed
						c = projectContent.GetType (rr.ResolvedType);
						context = ExpressionContext.TypeDerivingFrom (rr.ResolvedType, null, true);
					}
					if (c != null && !context.FilterEntry (c)) {
						// Try to suggest an entry (List<int> a = new => suggest List<int>).
						
						string suggestedClassName = null;
						/*LanguageProperties.CSharp.CodeGenerator.GenerateCode(
							CodeGenerator.ConvertType(
								rr.ResolvedType,
								new ClassFinder(ParserService.GetParseInformation(editor.FileName), editor.ActiveTextAreaControl.Caret.Line, editor.ActiveTextAreaControl.Caret.Column)
							), "");*/						if (suggestedClassName != c.Name) {
							// create an IType instance that includes the type arguments in its name
							//context.DefaultItem = new RenamedClass (c, suggestedClassName);
						} else {
							context.DefaultItem = c;
						}
					}
					return context;
				}
			}
			return null;
		}
示例#22
0
        private InsertionPoint GetInsertionPoint(MonoDevelop.Ide.Gui.Document document, IType type)
        {
            data = document.Editor;
            if (data == null) {
                throw new System.ArgumentNullException ("data");
            }
            var parsedDocument = document.ParsedDocument;
            if (parsedDocument == null) {
                throw new System.ArgumentNullException ("parsedDocument");
            }
            if (type == null) {
                throw new System.ArgumentNullException ("type");
            }
            type = (parsedDocument.CompilationUnit.GetTypeAt (type.Location) ?? type);
            DomRegion domRegion = type.BodyRegion;
            var start = type.BodyRegion.Start.Line;
            indent = data.GetLine(start).GetIndentation(data.Document);
            DomLocation domLocation = domRegion.End;
            int num = data.LocationToOffset (domLocation.Line, 1);
            while (num < data.Length && data.GetCharAt(num) != '}') {
                num++;
            }
            num++;
            DocumentLocation documentLocation = data.OffsetToLocation (num);

            LineSegment lineAfterClassEnd = data.GetLine (domLocation.Line + 1);
            NewLineInsertion lineAfter;
            if (lineAfterClassEnd != null && lineAfterClassEnd.EditableLength == lineAfterClassEnd.GetIndentation (data.Document).Length)
                lineAfter = NewLineInsertion.BlankLine;
            else
                lineAfter = NewLineInsertion.None;

            return new InsertionPoint (documentLocation, NewLineInsertion.None, lineAfter);
        }
示例#23
0
        public static void Left(TextEditorData data)
        {
            using (var undo = data.OpenUndoGroup()) {
                if (Platform.IsMac && data.IsSomethingSelected && !data.Caret.PreserveSelection)
                {
                    data.Caret.Offset = System.Math.Min(data.SelectionAnchor, data.Caret.Offset);
                    data.ClearSelection();
                    return;
                }

                if (data.Caret.Column > DocumentLocation.MinColumn)
                {
                    DocumentLine line = data.Document.GetLine(data.Caret.Line);
                    if (data.Caret.Column > line.Length + 1)
                    {
                        if (data.Caret.AllowCaretBehindLineEnd)
                        {
                            data.Caret.Column--;
                        }
                        else
                        {
                            data.Caret.Column = line.Length + 1;
                        }
                    }
                    else
                    {
                        int  offset       = data.Caret.Offset - 1;
                        bool foundFolding = false;
                        foreach (var folding in data.Document.GetFoldingsFromOffset(offset).Where(f => f.IsCollapsed && f.Offset < offset))
                        {
                            offset       = System.Math.Min(offset, folding.Offset);
                            foundFolding = true;
                        }

                        if (!foundFolding)
                        {
                            var layout = data.Parent?.TextViewMargin?.GetLayout(line);
                            if (layout != null && data.Caret.Column < line.Length)
                            {
                                uint curIndex = 0, byteIndex = 0;
                                int  utf8ByteIndex = (int)layout.TranslateToUTF8Index((uint)(offset - line.Offset), ref curIndex, ref byteIndex);
                                layout.Layout.GetCursorPos(utf8ByteIndex, out var strong_pos, out var weak_pos);
                                if (strong_pos.X != weak_pos.X)
                                {
                                    offset--;
                                }
                            }
                        }

                        data.Caret.Offset = offset;
                    }
                }
                else if (data.Caret.Line > DocumentLocation.MinLine)
                {
                    DocumentLine prevLine     = data.Document.GetLine(data.Caret.Line - 1);
                    var          nextLocation = new DocumentLocation(data.Caret.Line - 1, prevLine.Length + 1);
                    if (data.HasIndentationTracker && data.Options.IndentStyle == IndentStyle.Virtual && nextLocation.Column == DocumentLocation.MinColumn)
                    {
                        nextLocation = new DocumentLocation(data.Caret.Line - 1, data.GetVirtualIndentationColumn(nextLocation));
                    }
                    data.Caret.Location = nextLocation;
                }
                var curOffset = data.Caret.Offset;
                if (curOffset > 0 && curOffset < data.Length && ((ushort)data.GetCharAt(curOffset) & LowSurrogateMarker) == LowSurrogateMarker)
                {
                    data.Caret.Offset--;
                }
            }
        }