Current line cursor position

Feb 11, 2012 at 12:02 AM
Edited Feb 13, 2012 at 5:40 AM

Well i need to know the exact location of the cursor in the line. (not from the hole text)

So i did this function:

    Private Function GetLineCursorPosition(ByVal index As Integer, ByVal fixed As Boolean) As Integer
        Dim count As Integer = 0
        For Each line As Line In SyntaxHandle.Lines
            If line.Number < index Then
                count += line.Text.Length
            Else
                Exit For
            End If
        Next
        If SyntaxHandle.Lines.Count < 10000 Then
            Return If(fixed, SyntaxHandle.Selection.Start - count + 1, SyntaxHandle.Lines.Current.StartPosition - count)
        Else
            Return If(fixed, SyntaxHandle.Lines.Current.StartPosition - count + 1, SyntaxHandle.Lines.Current.StartPosition - count)
        End If
    End Function

This function work fine when the doc hasn't many lines, but i did a test with a doc that has about 55.000 lines and it does not return the right value.

 

Can someone give me any idea of how to do this please?

Feb 13, 2012 at 1:05 AM
Edited Feb 13, 2012 at 5:40 AM

No one actyally know any other way to get the cursor position from the current line? (please don't say:

"SyntaxHandle.Lines.Current.StartPosition" or "SyntaxHandle.Lines.Current.SelectionStartPosition" because this actually does not work the way i need.)

 

Edit:

I've also tryed like this:

    Private Function GetLineCursorPosition(ByVal index As Integer, ByVal fixed As Boolean) As Integer
        Return If(fixed, SyntaxHandle.Selection.Start - SyntaxHandle.Lines.Current.StartPosition + 1, SyntaxHandle.Selection.Start - SyntaxHandle.Lines.Current.StartPosition)
    End Function
But the function still does not work.

Coordinator
Feb 14, 2012 at 4:59 PM
Edited Feb 14, 2012 at 4:59 PM

Try:

int overAllPosition = scintilla.Caret.Position;
int lineStartPosition = scintilla.Lines.Current.StartPosition;
int yourAnswer = overAllPosition - lineStartPosition;

NOTE: As has been discussed numerous times on this forum, your unit of measure here is bytes--not characters.

Feb 15, 2012 at 3:48 AM

It was bugged on the same point as both mine, and it return exaclty the same value. But you give me an idea and it did work.

So if someone want the function, here it is:

 

    Private Function GetLineCursorPosition(ByVal fixed As Boolean) As Integer
        Return If(fixed, SyntaxHandle.Selection.Start - SyntaxHandle.Lines.Current.IndentPosition + 1, SyntaxHandle.Selection.Start - SyntaxHandle.Lines.Current.IndentPosition)
    End Function


 

 

Coordinator
Feb 16, 2012 at 1:58 AM

I'm glad you found a solution that works, but I think it's flawed. Based on what you asked for ("the exact location of the cursor in the line") the solution I provided will do just that, however, the solution that you've arrived at will not.

The Scintilla.Lines.Current.IndentPosition property will give you the overall document index following any leading tabs/indents on that line. So if your current line has no leading indentation you'll be fine, however, if it does, your calculations will be off. For example, if I had the following document (the tab on line two being intentional):

In a hole in the ground
	there lived a hobbit.

On line two the Scintilla.Lines.Current.IndentPosition property will return 26 while the Scintilla.Lines.Current.StartPosition for that same line will return 25. The first pertains to the position of the word 'there'--the first word following any leading indentation, while the second is the position of the tab character (\t) at the start of line two--the first character of that line.

So unless we have different definitions of "the exact location of the cursor in the line", the position of the cursor at the start of line two would be at index 25 and not 26.

In short, using the Scintilla.Lines.Current.IndentPosition is probably not what you want.

What you might be after is what we could call the line 'column'. Using the same example above, the tab character (in my browser) is rendered as eight characters wide. That's what it is rendered as, not what it actually is. In actuality that line has a single \t character, not eight space characters even though it may look like that. To get the column your cursor is on, try the Scintilla.GetColumn method. It will convert your document index into a column value--taking the width of tabs into account. Mind you it is probably only useful with monospaced fonts.

 

Cheers

Feb 18, 2012 at 9:36 PM

what you are saying has all the logic, however when i did test that code i did got strages values (get a pos ~900 when the line.length was of ~670).

Coordinator
Feb 19, 2012 at 12:30 AM

I can't explain that. All my tests had the expected results.

Would you be able to send me a document and/or code snippet that you think is returning the incorrect result? If there is a bug here I would like to fix it.

 

Thanks

Feb 19, 2012 at 6:23 PM

Document: anyone with 50.000 lines or more (may lower works to, but i did the test with one of this size)

Codes: All abobe exept the one i sed that works for me.

 

Function where i use the code function:

Private Function GetCurrentParamIndex(Optional ByVal fix As Boolean = False, Optional ByVal remove As Boolean = False) As Integer
        If GetCurrentFunction(True) = "" Then Return -1
        Static index As Integer, lastcall As Long
        If lastcall = 0 OrElse (GetTickCount() - lastcall) > 500 Then
            Try
                Dim tmp(1) As String, pos(1) As Integer
                tmp(1) = Mid(SyntaxHandle.Lines.Current.Text.Replace(vbCrLf, "").Replace(vbTab, ""), 1, SyntaxHandle.Lines.Current.Text.Length)
                pos(0) = GetLineCursorPosition()'Here
                tmp(1) = tmp(1).Remove(pos(0), tmp(1).Length - pos(0))
                tmp(0) = StrReverse(Mid(SyntaxHandle.Lines.Current.Text.Replace(vbCrLf, "").Replace(vbTab, ""), 1, SyntaxHandle.Lines.Current.Text.Length))
                While tmp(0).IndexOf("(") > -1 AndAlso tmp(0).IndexOf(")") > -1
                    pos(0) = tmp(0).IndexOf(")")
                    pos(1) = tmp(0).IndexOf(",", tmp(0).IndexOf("(", tmp(0).IndexOf(")")))
                    tmp(0) = tmp(0).Remove(pos(0), pos(1) - pos(0))
                End While
                If tmp(0).IndexOf(",") = -1 Then Return 0
                If remove AndAlso tmp(0).StartsWith("(") Then tmp(0) = tmp(0).Remove(0, 1)
                If tmp(0).IndexOf("(", tmp(0).IndexOf("(") + 1) > -1 Then
                    pos(0) = tmp(0).IndexOf(",", tmp(0).IndexOf("("))
                    tmp(0) = tmp(0).Remove(pos(0), tmp(0).Length - pos(0))
                End If
                tmp(0) = Trim(StrReverse(tmp(0)))
                index = CountEqualCharsFromString(tmp(0), ",", tmp(0).IndexOf(tmp(1)))
                lastcall = GetTickCount()
                Return If(fix, index - 1, index)
            Catch ex As Exception
                Return -1
            End Try
        Else
            Return If(fix, index - 1, index)
        End If
    End Function

This function return the current param index of the function where the user is:
Ex: "FunctioName(param0, param1, Function2Name(param0, "Some text" " (cursor is placed at the end of param1)

This function will return 1.

Coordinator
Feb 20, 2012 at 5:35 AM

So I did as you suggested and made numerous tests on documents with 50,000+ lines and never encountered a problem.

I think the issue is that the result returned from most Scintilla position APIs (including Scintilla.Caret.Position, Scintilla.Lines.Current.StartPosition, Scintilla.Selection.Start, etc...) all deal with byte offsets, but all your logic above deals with characters. If you have any Unicode values in your document, it could take several bytes to represent one character and that would explain why it doesn't match the character count you're expecting.

It's something that bites nearly every Scintilla user at least once.

I would suggest reworking your GetLineCursorPosition method to ensure it is returning a character offset. There are numerous discussions and resources on how to accomplish this in the forums.

May 28, 2014 at 10:26 PM
This appears to work quite well:
 this.sbLineNo.Text = String.Format("Row {0} Col {1}", this.ActiveDocument.VBScriptTextBox.Caret.LineNumber + 1, this.ActiveDocument.VBScriptTextBox.Caret.Position - this.ActiveDocument.VBScriptTextBox.Lines.Current.StartPosition);
 this.sbCount.Text = String.Format("Bytes: {0}", this.ActiveDocument.VBScriptTextBox.TextLength);