Skip to content

Instantly share code, notes, and snippets.

@iccir
Last active December 8, 2025 16:14
Show Gist options
  • Select an option

  • Save iccir/92b344d27fec2f5e687c5752d03dfc2d to your computer and use it in GitHub Desktop.

Select an option

Save iccir/92b344d27fec2f5e687c5752d03dfc2d to your computer and use it in GitHub Desktop.
Works around Sublime Text #6815 by manually sending selection to AppKit.
# Attempts to work around Issue #6815 by manually sending
# the selected text to the macOS Find Pasteboard
#
# https://github.com/sublimehq/sublime_text/issues/6815
from __future__ import annotations
import ctypes
import ctypes.util
import sublime
import sublime_plugin
# If True, loads AppKit via ctypes and uses the system NSPasteboard API
# If False, attempts to slurp find string with Sublime API
ShouldSlurpWithAppKit = True
class FindPasteboard():
def __init__(self):
AppKit = ctypes.cdll.LoadLibrary(ctypes.util.find_library("AppKit"))
objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("objc"))
objc.objc_getClass.restype = ctypes.c_void_p
objc.sel_registerName.restype = ctypes.c_void_p
NSAutoreleasePool = objc.objc_getClass(b'NSAutoreleasePool')
NSPasteboard = objc.objc_getClass(b'NSPasteboard')
NSString = objc.objc_getClass(b'NSString')
NSPasteboardNameFind = ctypes.c_void_p.in_dll(AppKit, "NSPasteboardNameFind")
NSPasteboardTypeString = ctypes.c_void_p.in_dll(AppKit, "NSPasteboardTypeString")
def m_(object, selector, *args, restype = ctypes.c_void_p):
argtypes = [ ctypes.c_void_p, ctypes.c_void_p ]
for arg in args:
argtypes.append(ctypes.c_void_p)
objc.objc_msgSend.restype = restype
objc.objc_msgSend.argtypes = argtypes
return objc.objc_msgSend(object, objc.sel_registerName(selector), *args)
def read() -> str:
pool = m_(m_(NSAutoreleasePool, b"alloc"), b"init")
pasteboard = m_(NSPasteboard, b"pasteboardWithName:", NSPasteboardNameFind)
ns_string = m_(pasteboard, b"stringForType:", NSPasteboardTypeString)
b = m_(ns_string, b"UTF8String", restype=ctypes.c_char_p)
string = b.decode() if b else None
m_(pool, b"release")
return string
def write(in_string: Optional[str]) -> None:
pool = m_(m_(NSAutoreleasePool, b"alloc"), b"init")
pasteboard = m_(NSPasteboard, b"pasteboardWithName:", NSPasteboardNameFind)
m_(pasteboard, b"clearContents")
if in_string:
ns_string = m_(NSString, b'stringWithUTF8String:', in_string.encode())
m_(pasteboard, b"setString:forType:", ns_string, NSPasteboardTypeString)
m_(pool, b"release")
self.__read = read
self.__write = write
def read(self) -> str:
return self.__read()
def write(self, str: str) -> None:
self.__write(str)
find_pasteboard = None
def do_slurp_with_sublime_api(in_view: sublime.View, string: str) -> None:
# From testing, the "slurp_find_string" command only works when a
# sublime.View object has a None element()
# To work around this, we need to create a new View, append the selection
# string to it, and then run the "slurp_find_string" command
tmp_view = in_view.window().new_file(flags=sublime.NewFileFlags.TRANSIENT)
tmp_view.set_scratch(True)
tmp_view.run_command("append", { "characters": string })
tmp_view.run_command("select_all")
tmp_view.run_command("slurp_find_string")
tmp_view.close()
# Creating the temporary View shifted input focus, re-focus to the original
in_view.window().focus_view(in_view)
def do_slurp_with_appkit(string: str) -> None:
global find_pasteboard
if not find_pasteboard:
find_pasteboard = FindPasteboard()
find_pasteboard.write(string)
class FixedSlurpFindString(sublime_plugin.TextCommand):
def run(self, edit):
view = self.view
# Only use our workaround if the currently focused input view has an element()
if view.element():
selection = view.sel()
if len(selection) == 0: return
string = view.substr(selection[0])
if ShouldSlurpWithAppKit:
do_slurp_with_appkit(string)
else:
do_slurp_with_sublime_api(view, string)
# Else, go ahead and use the built-in "slurp_find_string"
else:
view.run_command("slurp_find_string")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment