Last active
April 23, 2025 01:23
-
-
Save Nightro/05b761e4d92155940e7c40c0338fe7a6 to your computer and use it in GitHub Desktop.
Python script for 10x to easily jump to whitespace lines adjacent to code blocks, also includes subword jumping - drop into %appdata%\10x\PythonScripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import N10X | |
# Uses MoveCursorUp/MoveCursorDown/SelectUp/SelectDown to avoid expanding regions | |
#------------------------------------------------------------------------ | |
def MoveToPreviousBlockEdge(): | |
x, y = N10X.Editor.GetCursorPos() | |
is_space = N10X.Editor.GetLine(y).isspace() | |
was_space = is_space | |
# when is_space != was_space, we're done (unless its the first line) | |
lines_traversed = 0 | |
while y > 0 and (is_space == was_space or lines_traversed <= 1): | |
N10X.Editor.ExecuteCommand("MoveCursorUp") | |
x, y = N10X.Editor.GetCursorPos() | |
was_space = is_space | |
is_space = N10X.Editor.GetLine(y).isspace() | |
lines_traversed += 1 | |
if (y == 0): | |
N10X.Editor.SetCursorPos((0,y)) | |
elif was_space: | |
# don't land on text | |
N10X.Editor.ExecuteCommand("MoveCursorDown") | |
x, y = N10X.Editor.GetCursorPos() | |
#------------------------------------------------------------------------ | |
def MoveToNextBlockEdge(): | |
line_count = N10X.Editor.GetLineCount() | |
x, y = N10X.Editor.GetCursorPos() | |
is_space = N10X.Editor.GetLine(y).isspace() | |
was_space = is_space | |
# when is_space != was_space, we're done (unless its the first line) | |
lines_traversed = 0 | |
while y < line_count - 1 and (is_space == was_space or lines_traversed <= 1): | |
N10X.Editor.ExecuteCommand("MoveCursorDown") | |
x, y = N10X.Editor.GetCursorPos() | |
was_space = is_space | |
is_space = N10X.Editor.GetLine(y).isspace() | |
lines_traversed += 1 | |
# don't land on text | |
if was_space: | |
N10X.Editor.ExecuteCommand("MoveCursorUp") | |
x, y = N10X.Editor.GetCursorPos() | |
if y == line_count - 1: | |
N10X.Editor.SetCursorPos((len(N10X.Editor.GetLine(y)),y)) | |
#------------------------------------------------------------------------ | |
def SelectToPreviousBlockEdge(): | |
x, y = N10X.Editor.GetCursorPos() | |
is_space = N10X.Editor.GetLine(y).isspace() | |
was_space = is_space | |
# when is_space != was_space, we're done (unless its the first line) | |
lines_traversed = 0 | |
while y > 0 and (is_space == was_space or lines_traversed <= 1): | |
N10X.Editor.ExecuteCommand("SelectUp") | |
x, y = N10X.Editor.GetCursorPos() | |
was_space = is_space | |
is_space = N10X.Editor.GetLine(y).isspace() | |
lines_traversed += 1 | |
if (y == 0): | |
N10X.Editor.SetCursorPosSelect((0,y)) | |
elif was_space: | |
# don't land on text | |
N10X.Editor.ExecuteCommand("SelectDown") | |
x, y = N10X.Editor.GetCursorPos() | |
#------------------------------------------------------------------------ | |
def SelectToNextBlockEdge(): | |
line_count = N10X.Editor.GetLineCount() | |
x, y = N10X.Editor.GetCursorPos() | |
is_space = N10X.Editor.GetLine(y).isspace() | |
was_space = is_space | |
# when is_space != was_space, we're done (unless its the first line) | |
lines_traversed = 0 | |
while y < line_count - 1 and (is_space == was_space or lines_traversed <= 1): | |
N10X.Editor.ExecuteCommand("SelectDown") | |
x, y = N10X.Editor.GetCursorPos() | |
was_space = is_space | |
is_space = N10X.Editor.GetLine(y).isspace() | |
lines_traversed += 1 | |
# don't land on text | |
if was_space: | |
N10X.Editor.ExecuteCommand("SelectUp") | |
x, y = N10X.Editor.GetCursorPos() | |
if y == line_count - 1: | |
N10X.Editor.SetCursorPosSelect((len(N10X.Editor.GetLine(y)),y)) | |
#------------------------------------------------------------------------ | |
def MoveCursorNextSubWord(): | |
for c in range(N10X.Editor.GetCursorCount()): | |
x, y = N10X.Editor.GetCursorPos(c) | |
N10X.Editor.SetCursorPos(GetNextSubWordCoord(x, y), c) | |
#------------------------------------------------------------------------ | |
def MoveCursorPrevSubWord(): | |
for c in range(N10X.Editor.GetCursorCount()): | |
x, y = N10X.Editor.GetCursorPos(c) | |
N10X.Editor.SetCursorPos(GetPrevSubWordCoord(x, y), c) | |
#------------------------------------------------------------------------ | |
def SelectToNextSubWord(): | |
for c in range(N10X.Editor.GetCursorCount()): | |
x, y = N10X.Editor.GetCursorPos(c) | |
nx, ny = GetNextSubWordCoord(x, y) | |
(sx1, sy1), (sx2, sy2) = N10X.Editor.GetCursorSelection(c) | |
if (x == sx2 and y == sy2): | |
# cursor is on right side | |
N10X.Editor.SetSelection((sx1, sy1), (nx, ny), c) | |
else: | |
# cursor is on left side | |
N10X.Editor.SetSelection((sx2, sy2), (nx, ny), c) | |
#------------------------------------------------------------------------ | |
def SelectToPrevSubWord(): | |
for c in range(N10X.Editor.GetCursorCount()): | |
x, y = N10X.Editor.GetCursorPos(c) | |
nx, ny = GetPrevSubWordCoord(x, y) | |
(sx1, sy1), (sx2, sy2) = N10X.Editor.GetCursorSelection(c) | |
if (x == sx2 and y == sy2): | |
# cursor is on right side | |
N10X.Editor.SetSelection((sx1, sy1), (nx, ny), c) | |
else: | |
# cursor is on left side | |
N10X.Editor.SetSelection((sx2, sy2), (nx, ny), c) | |
#------------------------------------------------------------------------ | |
def DeleteToNextSubWord(): | |
N10X.Editor.PushUndoGroup() | |
x1, y1 = N10X.Editor.GetCursorPos() | |
x2, y2 = N10X.Editor.GetSelectionEnd() | |
if (y1 < y2 or x1 < x2): | |
N10X.Editor.ExecuteCommand("Delete") | |
else: | |
N10X.Editor.ExecuteCommand("SelectToNextSubWord") | |
N10X.Editor.ExecuteCommand("Delete") | |
N10X.Editor.PopUndoGroup() | |
#------------------------------------------------------------------------ | |
def GetNextSubWordCoord(x, y): | |
line_count = N10X.Editor.GetLineCount() | |
line = N10X.Editor.GetLine(y).rstrip("\r\n") | |
length = len(line) | |
if x >= length: | |
if y < line_count - 1: | |
# reached the end of the line, go to the next | |
y += 1 | |
x = 0 | |
line = N10X.Editor.GetLine(y).rstrip("\r\n") | |
length = len(line) | |
if length <= 0 or not line[x].isspace(): | |
# we are already at the first character of the new line | |
return (x, y) | |
else: | |
# EOF | |
return (x, y) | |
ch = line[x] | |
x += 1 | |
if x < length: | |
nch = line[x] | |
if ch.isspace(): | |
while x < length and line[x].isspace(): | |
x += 1 | |
elif ch.isalnum(): | |
if ch.isupper() and nch.isupper(): | |
while x < length - 1 and line[x+1].isupper(): | |
x += 1 | |
elif ch.isdigit(): | |
while x < length and line[x].isdigit(): | |
x += 1 | |
else: | |
while x < length and line[x].isalpha() and not line[x].isupper(): | |
x += 1 | |
else: | |
dx = length - x; | |
if dx >= 2 and ShouldSkip3(line[x-1:x+2]): | |
x += 2 | |
elif dx >= 1 and ShouldSkip2(line[x-1:x+1]): | |
x += 1 | |
else: | |
while x < length and line[x] == ch: | |
x += 1 | |
return (x, y) | |
#------------------------------------------------------------------------ | |
def GetPrevSubWordCoord(x, y): | |
line_count = N10X.Editor.GetLineCount() | |
line = N10X.Editor.GetLine(y) | |
length = len(line) | |
if x > 0 and x >= length: | |
return (x-1, y) | |
if x <= 0: | |
if y > 0: | |
y -= 1 | |
line = N10X.Editor.GetLine(y).rstrip("\r\n") | |
x = len(line) | |
return (x, y) | |
ch = line[x] | |
x -= 1 | |
nch = line[x] | |
if nch.isspace(): | |
while x > 0 and line[x-1].isspace(): | |
x -= 1 | |
elif nch.isalnum(): | |
if nch.isupper() and ch.isupper(): | |
while x > 0 and line[x-1].isupper(): | |
x -= 1 | |
elif nch.isdigit(): | |
while x > 0 and line[x-1].isdigit(): | |
x -= 1 | |
else: | |
while x > 0 and line[x-1].isalpha() and not line[x].isupper(): | |
x -= 1 | |
else: | |
if x >= 2 and ShouldSkip3(line[x-2:x+1]): | |
x -= 2 | |
elif x >= 1 and ShouldSkip2(line[x-1:x+1]): | |
x -= 1 | |
else: | |
while x > 0 and line[x-1] == nch: | |
x -= 1 | |
return (x, y) | |
#------------------------------------------------------------------------ | |
def ShouldSkip3(s): | |
valid_ops = { | |
"::*", "->*", | |
} | |
return s in valid_ops | |
#------------------------------------------------------------------------ | |
def ShouldSkip2(s): | |
valid_ops = { | |
"!=", ">=", "<=", "->", # Comparison/pointer (C/C++) | |
"+=", "-=", "*=", "/=", "%=", # Compound assignment (both) | |
"&=", "|=", "^=", ">>=", "<<=", # Compound bitwise assignment (both) | |
"->", "@=", # Python: "->" in function annotations, "@=" for decorators | |
} | |
return s in valid_ops |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Recommended key bindings | |
# Jump previous block | |
Control Up: MoveToPreviousBlockEdge | |
# Jump next block | |
Control Down: MoveToNextBlockEdge | |
# Jump select previous block | |
Control Shift Up: SelectToPreviousBlockEdge | |
# Jump select next block | |
Control Shift Down: SelectToNextBlockEdge | |
# Move to next subword | |
Alt End: MoveCursorNextSubWord | |
# Move to previous subword | |
Alt Home: MoveCursorPrevSubWord | |
# Select to next subword | |
Shift Alt End: SelectToNextSubWord | |
# Select to previous subword | |
Shift Alt Home: SelectToPrevSubWord | |
# Delete to next subword | |
Shift Alt Delete: DeleteToNextSubWord |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment