search movement: f, F, t, T
This commit is contained in:
parent
802146e937
commit
0be5f4207c
|
@ -1,11 +1,15 @@
|
||||||
# vibreoffice
|
# vibreoffice
|
||||||
A Vi/Vim Mode Extension for Libreoffice and OpenOffice (Apache OpenOffice, OpenOffice.org)
|
A Vi/Vim Mode Extension for Libreoffice and OpenOffice (Apache OpenOffice, OpenOffice.org)
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
Coming soon.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
vibreoffice currently supports:
|
vibreoffice currently supports:
|
||||||
- Insert (`i`, `I`, `a`, `A`), Visual (`v`), Normal modes
|
- Insert (`i`, `I`, `a`, `A`), Visual (`v`), Normal modes
|
||||||
- Movement keys: `hjkl`, `w/W`, `b/B`, `e`, `$`, `^`, `{}`, `()`, `ctrl+d`, `ctrl+u`
|
- Movement keys: `hjkl`, `w`, `W`, `b`, `B`, `e`, `$`, `^`, `{}`, `()`, `C-d`, `C-u`
|
||||||
- Number modifiers: e.g. `5w`
|
- Search movement: `f`, `F`, `t`, `T`
|
||||||
|
- Number modifiers: e.g. `5w`, `4fa`
|
||||||
- Deletion: `x`, `d`, `c`, `dd`, `cc`
|
- Deletion: `x`, `d`, `c`, `dd`, `cc`
|
||||||
- Plus movement and number modifiers: e.g. `dw`, `c$`, `5dd`, `c3j`
|
- Plus movement and number modifiers: e.g. `dw`, `c$`, `5dd`, `c3j`
|
||||||
- More to come!
|
- More to come!
|
||||||
|
|
|
@ -67,7 +67,7 @@ Sub setRawStatus(rawText)
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
Sub setStatus(statusText)
|
Sub setStatus(statusText)
|
||||||
setRawStatus(MODE & " | " & statusText & " | special: " & getSpecial())
|
setRawStatus(MODE & " | " & statusText & " | special: " & getSpecial() & " | " & "modifier: " & getMovementModifier())
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
Sub setMode(modeName)
|
Sub setMode(modeName)
|
||||||
|
@ -79,6 +79,7 @@ Function gotoMode(sMode)
|
||||||
Select Case sMode
|
Select Case sMode
|
||||||
Case "NORMAL":
|
Case "NORMAL":
|
||||||
setMode("NORMAL")
|
setMode("NORMAL")
|
||||||
|
setMovementModifier("")
|
||||||
Case "INSERT":
|
Case "INSERT":
|
||||||
setMode("INSERT")
|
setMode("INSERT")
|
||||||
Case "VISUAL":
|
Case "VISUAL":
|
||||||
|
@ -100,6 +101,10 @@ Sub cursorReset(oTextCursor)
|
||||||
thisComponent.getCurrentController.Select(oTextCursor)
|
thisComponent.getCurrentController.Select(oTextCursor)
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
|
Function samePos(oPos1, oPos2)
|
||||||
|
samePos = oPos1.X() = oPos2.X() And oPos1.Y() = oPos2.Y()
|
||||||
|
End FUnction
|
||||||
|
|
||||||
|
|
||||||
' -----------------------------------
|
' -----------------------------------
|
||||||
' Special Mode (for chained commands)
|
' Special Mode (for chained commands)
|
||||||
|
@ -134,6 +139,22 @@ Sub resetSpecial(Optional bForce)
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
|
|
||||||
|
' -----------------
|
||||||
|
' Movement Modifier
|
||||||
|
' -----------------
|
||||||
|
'f,i,a
|
||||||
|
global MOVEMENT_MODIFIER As string
|
||||||
|
|
||||||
|
Sub setMovementModifier(modifierName)
|
||||||
|
MOVEMENT_MODIFIER = modifierName
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Function getMovementModifier()
|
||||||
|
getMovementModifier = MOVEMENT_MODIFIER
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
' --------------------
|
' --------------------
|
||||||
' Multiplier functions
|
' Multiplier functions
|
||||||
' --------------------
|
' --------------------
|
||||||
|
@ -215,7 +236,8 @@ function KeyHandler_KeyPressed(oEvent) as boolean
|
||||||
bConsumeInput = False
|
bConsumeInput = False
|
||||||
|
|
||||||
' If Change Mode
|
' If Change Mode
|
||||||
ElseIf MODE = "NORMAL" And Not bIsSpecial And ProcessModeKey(oEvent) Then
|
' ElseIf MODE = "NORMAL" And Not bIsSpecial And getMovementModifier() = "" And ProcessModeKey(oEvent) Then
|
||||||
|
ElseIf ProcessModeKey(oEvent) Then
|
||||||
' Pass
|
' Pass
|
||||||
|
|
||||||
' Multiplier Key
|
' Multiplier Key
|
||||||
|
@ -232,6 +254,11 @@ function KeyHandler_KeyPressed(oEvent) as boolean
|
||||||
ElseIf bIsModified Then
|
ElseIf bIsModified Then
|
||||||
bConsumeInput = False
|
bConsumeInput = False
|
||||||
|
|
||||||
|
' Movement modifier here?
|
||||||
|
ElseIf ProcessMovementModifierKey(oEvent.KeyChar) Then
|
||||||
|
' Pass
|
||||||
|
|
||||||
|
|
||||||
' If bIsSpecial but nothing matched, return to normal mode
|
' If bIsSpecial but nothing matched, return to normal mode
|
||||||
ElseIf bIsSpecial Then
|
ElseIf bIsSpecial Then
|
||||||
gotoMode("NORMAL")
|
gotoMode("NORMAL")
|
||||||
|
@ -242,7 +269,7 @@ function KeyHandler_KeyPressed(oEvent) as boolean
|
||||||
resetSpecial()
|
resetSpecial()
|
||||||
|
|
||||||
' Reset multiplier if last input was not number and not in special mode
|
' Reset multiplier if last input was not number and not in special mode
|
||||||
If not bIsMultiplier and getSpecial() = "" Then
|
If not bIsMultiplier and getSpecial() = "" and getMovementModifier() = "" Then
|
||||||
resetMultiplier()
|
resetMultiplier()
|
||||||
End If
|
End If
|
||||||
setStatus(getMultiplier())
|
setStatus(getMultiplier())
|
||||||
|
@ -301,6 +328,13 @@ End Function
|
||||||
|
|
||||||
|
|
||||||
Function ProcessModeKey(oEvent)
|
Function ProcessModeKey(oEvent)
|
||||||
|
' Don't change modes in these circumstances
|
||||||
|
If MODE <> "NORMAL" Or getSpecial <> "" Or getMovementModifier() <> "" Then
|
||||||
|
ProcessModeKey = False
|
||||||
|
Exit Function
|
||||||
|
End If
|
||||||
|
|
||||||
|
' Mode matching
|
||||||
dim bMatched
|
dim bMatched
|
||||||
bMatched = True
|
bMatched = True
|
||||||
Select Case oEvent.KeyChar
|
Select Case oEvent.KeyChar
|
||||||
|
@ -337,6 +371,9 @@ Function ProcessNormalKey(oEvent)
|
||||||
bMatched = bMatched or bMatchedMovement
|
bMatched = bMatched or bMatchedMovement
|
||||||
Next i
|
Next i
|
||||||
|
|
||||||
|
' Reset Movement Modifier
|
||||||
|
setMovementModifier("")
|
||||||
|
|
||||||
If bMatched Then
|
If bMatched Then
|
||||||
' If Special: d/c + movement
|
' If Special: d/c + movement
|
||||||
If bMatched And (getSpecial() = "d" Or getSpecial() = "c") Then
|
If bMatched And (getSpecial() = "d" Or getSpecial() = "c") Then
|
||||||
|
@ -466,6 +503,89 @@ Function ProcessDeleteKey(keyChar)
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
Function ProcessMovementModifierKey(keyChar)
|
||||||
|
dim bMatched
|
||||||
|
|
||||||
|
bMatched = True
|
||||||
|
Select Case keyChar
|
||||||
|
Case "f", "t", "F", "T":
|
||||||
|
setMovementModifier(keyChar)
|
||||||
|
Case Else:
|
||||||
|
bMatched = False
|
||||||
|
End Select
|
||||||
|
|
||||||
|
ProcessMovementModifierKey = bMatched
|
||||||
|
End Function
|
||||||
|
|
||||||
|
|
||||||
|
' Returns the resulting TextCursor
|
||||||
|
Function ProcessSearchKey(oTextCursor, searchType, keyChar, bExpand)
|
||||||
|
' REALLY ugly hack to figure out corner cases for backtracking
|
||||||
|
dim iBacktrack, oPos1, oPos2, oPos3
|
||||||
|
iBacktrack = 0
|
||||||
|
|
||||||
|
' In forward searching, we start searching from the next character.
|
||||||
|
' Thus, we need to move the cursor back 1 if the search fails.
|
||||||
|
' Exception:
|
||||||
|
' iBackTrack = 0 if cursor is at the end of the document
|
||||||
|
oPos1 = getCursor().getPosition()
|
||||||
|
getCursor().goRight(1, bExpand)
|
||||||
|
oPos2 = getCursor().getPosition()
|
||||||
|
If Not samePos(oPos1, oPos2) Then
|
||||||
|
getCursor().goRight(1, bExpand)
|
||||||
|
oPos3 = getCursor().getPosition()
|
||||||
|
' Cursor is not at the end of the document
|
||||||
|
If Not samePos(oPos2, oPos3) Then
|
||||||
|
iBacktrack = 1
|
||||||
|
getCursor().goLeft(1, bExpand)
|
||||||
|
End If
|
||||||
|
getCursor().goLeft(1, bExpand)
|
||||||
|
End If
|
||||||
|
' -------------------------
|
||||||
|
|
||||||
|
|
||||||
|
'-----------
|
||||||
|
' Searching
|
||||||
|
'-----------
|
||||||
|
dim oSearchDesc, oFoundRange, bIsBackwards, oStartRange
|
||||||
|
bIsBackwards = (searchType = "F" Or searchType = "T")
|
||||||
|
|
||||||
|
If Not bIsBackwards Then
|
||||||
|
' Start searching from next character
|
||||||
|
oTextCursor.goRight(1, bExpand)
|
||||||
|
oStartRange = oTextCursor.getEnd()
|
||||||
|
Else
|
||||||
|
oStartRange = oTextCursor.getStart()
|
||||||
|
End If
|
||||||
|
|
||||||
|
oSearchDesc = thisComponent.createSearchDescriptor()
|
||||||
|
oSearchDesc.setSearchString(keyChar)
|
||||||
|
oSearchDesc.SearchCaseSensitive = True
|
||||||
|
oSearchDesc.SearchBackwards = bIsBackwards
|
||||||
|
|
||||||
|
oFoundRange = thisComponent.findNext( oStartRange, oSearchDesc )
|
||||||
|
|
||||||
|
If not IsNull(oFoundRange) Then
|
||||||
|
oTextCursor.gotoRange(oFoundRange.getStart(), bExpand)
|
||||||
|
|
||||||
|
If getMovementModifier() = "t" Then
|
||||||
|
oTextCursor.goLeft(1, bExpand)
|
||||||
|
ElseIf getMovementModifier() = "T" Then
|
||||||
|
oTextCursor.goRight(1, bExpand)
|
||||||
|
End If
|
||||||
|
|
||||||
|
Else
|
||||||
|
' Backtrack hack
|
||||||
|
dim i
|
||||||
|
For i = 1 to iBacktrack
|
||||||
|
If not bIsBackwards Then
|
||||||
|
oTextCursor.goLeft(1, bExpand)
|
||||||
|
End If
|
||||||
|
Next i
|
||||||
|
End If
|
||||||
|
|
||||||
|
End Function
|
||||||
|
|
||||||
' -----------------------
|
' -----------------------
|
||||||
' Main Movement Function
|
' Main Movement Function
|
||||||
' -----------------------
|
' -----------------------
|
||||||
|
@ -499,63 +619,83 @@ Function ProcessMovementKey(keyChar, Optional bExpand, Optional keyModifiers)
|
||||||
' Set global cursor to oTextCursor's new position if moved
|
' Set global cursor to oTextCursor's new position if moved
|
||||||
bSetCursor = True
|
bSetCursor = True
|
||||||
|
|
||||||
Select Case keyChar
|
|
||||||
Case "l":
|
|
||||||
oTextCursor.goRight(1, bExpand)
|
|
||||||
Case "h":
|
|
||||||
oTextCursor.goLeft(1, bExpand)
|
|
||||||
|
|
||||||
' oTextCursor.goUp and oTextCursor.goDown SHOULD work, but doesn't (I dont know why).
|
' ------------------
|
||||||
' So this is a weird hack
|
' Movement matching
|
||||||
Case "k":
|
' ------------------
|
||||||
'oTextCursor.goUp(1, False)
|
|
||||||
getCursor().goUp(1, bExpand)
|
|
||||||
bSetCursor = False
|
|
||||||
Case "j":
|
|
||||||
'oTextCursor.goDown(1, False)
|
|
||||||
getCursor().goDown(1, bExpand)
|
|
||||||
bSetCursor = False
|
|
||||||
' ----------
|
|
||||||
|
|
||||||
Case "^":
|
' Special Case: Modified movements
|
||||||
getCursor().gotoStartOfLine(bExpand)
|
If getMovementModifier() <> "" Then
|
||||||
bSetCursor = False
|
Select Case getMovementModifier()
|
||||||
Case "$":
|
' ------------------
|
||||||
dim oldPos, newPos
|
' f,F,t,T searching
|
||||||
oldPos = getCursor().getPosition()
|
' ------------------
|
||||||
getCursor().gotoEndOfLine(bExpand)
|
Case "f", "t", "F", "T":
|
||||||
newPos = getCursor().getPosition()
|
processSearchKey(oTextCursor, getMovementModifier(), keyChar, bExpand)
|
||||||
|
|
||||||
' If the result is at the start of the line, then it must have
|
Case Else:
|
||||||
' jumped down a line; goLeft to return to the previous line.
|
bSetCursor = False
|
||||||
' Except for: Empty lines (check for oldPos = newPos)
|
bMatched = False
|
||||||
If getCursor().isAtStartOfLine() And oldPos.Y() <> newPos.Y() Then
|
End Select
|
||||||
getCursor().goLeft(1, bExpand)
|
|
||||||
End If
|
|
||||||
|
|
||||||
' maybe eventually cursorGoto... should return True/False for bsetCursor
|
ElseIf keyChar = "l" Then
|
||||||
bSetCursor = False
|
oTextCursor.goRight(1, bExpand)
|
||||||
|
|
||||||
Case "w", "W":
|
ElseIf keyChar = "h" Then
|
||||||
oTextCursor.gotoNextWord(bExpand)
|
oTextCursor.goLeft(1, bExpand)
|
||||||
Case "b", "B":
|
|
||||||
oTextCursor.gotoPreviousWord(bExpand)
|
|
||||||
Case "e":
|
|
||||||
oTextCursor.gotoEndOfWord(bExpand)
|
|
||||||
|
|
||||||
Case ")":
|
' oTextCursor.goUp and oTextCursor.goDown SHOULD work, but doesn't (I dont know why).
|
||||||
oTextCursor.gotoNextSentence(bExpand)
|
' So this is a weird hack
|
||||||
Case "(":
|
ElseIf keyChar = "k" Then
|
||||||
oTextCursor.gotoPreviousSentence(bExpand)
|
'oTextCursor.goUp(1, False)
|
||||||
Case "}":
|
getCursor().goUp(1, bExpand)
|
||||||
oTextCursor.gotoNextParagraph(bExpand)
|
bSetCursor = False
|
||||||
Case "{":
|
|
||||||
oTextCursor.gotoPreviousParagraph(bExpand)
|
|
||||||
|
|
||||||
Case Else:
|
ElseIf keyChar = "j" Then
|
||||||
bSetCursor = False
|
'oTextCursor.goDown(1, False)
|
||||||
bMatched = False
|
getCursor().goDown(1, bExpand)
|
||||||
End Select
|
bSetCursor = False
|
||||||
|
' ----------
|
||||||
|
|
||||||
|
ElseIf keyChar = "^" Then
|
||||||
|
getCursor().gotoStartOfLine(bExpand)
|
||||||
|
bSetCursor = False
|
||||||
|
ElseIf keyChar = "$" Then
|
||||||
|
dim oldPos, newPos
|
||||||
|
oldPos = getCursor().getPosition()
|
||||||
|
getCursor().gotoEndOfLine(bExpand)
|
||||||
|
newPos = getCursor().getPosition()
|
||||||
|
|
||||||
|
' If the result is at the start of the line, then it must have
|
||||||
|
' jumped down a line; goLeft to return to the previous line.
|
||||||
|
' Except for: Empty lines (check for oldPos = newPos)
|
||||||
|
If getCursor().isAtStartOfLine() And oldPos.Y() <> newPos.Y() Then
|
||||||
|
getCursor().goLeft(1, bExpand)
|
||||||
|
End If
|
||||||
|
|
||||||
|
' maybe eventually cursorGoto... should return True/False for bsetCursor
|
||||||
|
bSetCursor = False
|
||||||
|
|
||||||
|
ElseIf keyChar = "w" or keyChar = "W" Then
|
||||||
|
oTextCursor.gotoNextWord(bExpand)
|
||||||
|
ElseIf keyChar = "b" or keyChar = "B" Then
|
||||||
|
oTextCursor.gotoPreviousWord(bExpand)
|
||||||
|
ElseIf keyChar = "e" Then
|
||||||
|
oTextCursor.gotoEndOfWord(bExpand)
|
||||||
|
|
||||||
|
ElseIf keyChar = ")" Then
|
||||||
|
oTextCursor.gotoNextSentence(bExpand)
|
||||||
|
ElseIf keyChar = "(" Then
|
||||||
|
oTextCursor.gotoPreviousSentence(bExpand)
|
||||||
|
ElseIf keyChar = "}" Then
|
||||||
|
oTextCursor.gotoNextParagraph(bExpand)
|
||||||
|
ElseIf keyChar = "{" Then
|
||||||
|
oTextCursor.gotoPreviousParagraph(bExpand)
|
||||||
|
|
||||||
|
Else
|
||||||
|
bSetCursor = False
|
||||||
|
bMatched = False
|
||||||
|
End If
|
||||||
|
|
||||||
' If oTextCursor was moved, set global cursor to its position
|
' If oTextCursor was moved, set global cursor to its position
|
||||||
If bSetCursor Then
|
If bSetCursor Then
|
||||||
|
|
Loading…
Reference in New Issue