Skip to content

Instantly share code, notes, and snippets.

@Nightro
Last active April 23, 2025 01:23
Show Gist options
  • Save Nightro/05b761e4d92155940e7c40c0338fe7a6 to your computer and use it in GitHub Desktop.
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
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
## 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