commands/FBFlickerCommands.py (128 lines of code) (raw):
#!/usr/bin/python
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import os
import sys
import fbchisellldbbase as fb
import fbchisellldbobjcruntimehelpers as runtimeHelpers
import fbchisellldbviewhelpers as viewHelpers
import lldb
def lldbcommands():
return [FBFlickerViewCommand(), FBViewSearchCommand()]
class FBFlickerViewCommand(fb.FBCommand):
def name(self):
return "flicker"
def description(self):
return "Quickly show and hide a view to quickly help visualize where it is."
def args(self):
return [
fb.FBCommandArgument(
arg="viewOrLayer", type="UIView/NSView*", help="The view to flicker."
)
]
def run(self, arguments, options):
object = fb.evaluateObjectExpression(arguments[0])
isHidden = fb.evaluateBooleanExpression("[" + object + " isHidden]")
shouldHide = not isHidden
for _ in range(0, 2):
viewHelpers.setViewHidden(object, shouldHide)
viewHelpers.setViewHidden(object, isHidden)
class FBViewSearchCommand(fb.FBCommand):
def name(self):
return "vs"
def description(self):
return "Interactively search for a view by walking the hierarchy."
def args(self):
return [
fb.FBCommandArgument(arg="view", type="UIView*", help="The view to border.")
]
def run(self, arguments, options):
print(
"\nUse the following and (q) to quit.\n(w) move to superview\n(s) move to first subview\n(a) move to previous sibling\n(d) move to next sibling\n(p) print the hierarchy\n"
)
object = fb.evaluateInputExpression(arguments[0])
walker = FlickerWalker(object)
walker.run()
class FlickerWalker:
def __init__(self, startView):
self.setCurrentView(startView)
def run(self):
self.keepRunning = True
initialAsync = lldb.debugger.GetAsync()
# Needed so XCode doesn't hang if tap on Continue while lldb
# is waiting for user input in 'vs' mode
lldb.debugger.SetAsync(True)
while self.keepRunning:
charRead = sys.stdin.readline().rstrip("\n")
self.inputCallback(charRead)
else:
lldb.debugger.SetAsync(initialAsync)
def inputCallback(self, input):
oldView = self.currentView
if input == "q":
cmd = 'echo %s | tr -d "\n" | pbcopy' % oldView
os.system(cmd)
print(
"\nI hope "
+ oldView
+ " was what you were looking for. I put it on your clipboard."
)
viewHelpers.unmaskView(oldView)
self.keepRunning = False
elif input == "w":
v = superviewOfView(self.currentView)
if not v:
print("There is no superview. Where are you trying to go?!")
self.setCurrentView(v, oldView)
elif input == "s":
v = firstSubviewOfView(self.currentView)
if not v:
print("\nThe view has no subviews.\n")
self.setCurrentView(v, oldView)
elif input == "d":
v = nthSiblingOfView(self.currentView, -1)
if v == oldView:
print("\nThere are no sibling views to this view.\n")
self.setCurrentView(v, oldView)
elif input == "a":
v = nthSiblingOfView(self.currentView, 1)
if v == oldView:
print("\nThere are no sibling views to this view.\n")
self.setCurrentView(v, oldView)
elif input == "p":
recursionName = "recursiveDescription"
isMac = runtimeHelpers.isMacintoshArch()
if isMac:
recursionName = "_subtreeDescription"
print(fb.describeObject("[(id){} {}]".format(oldView, recursionName)))
else:
print("\nI really have no idea what you meant by '" + input + "'... =\\\n")
def setCurrentView(self, view, oldView=None):
if view:
self.currentView = view
if oldView:
viewHelpers.unmaskView(oldView)
viewHelpers.maskView(self.currentView, "red", "0.4")
print(fb.describeObject(view))
def superviewOfView(view):
superview = fb.evaluateObjectExpression("[" + view + " superview]")
if int(superview, 16) == 0:
return None
return superview
def subviewsOfView(view):
return fb.evaluateObjectExpression("[" + view + " subviews]")
def firstSubviewOfView(view):
subviews = subviewsOfView(view)
numViews = fb.evaluateIntegerExpression("[(id)" + subviews + " count]")
if numViews == 0:
return None
else:
return fb.evaluateObjectExpression("[" + subviews + " objectAtIndex:0]")
def nthSiblingOfView(view, n):
subviews = subviewsOfView(superviewOfView(view))
numViews = fb.evaluateIntegerExpression("[(id)" + subviews + " count]")
idx = fb.evaluateIntegerExpression(
"[(id)" + subviews + " indexOfObject:" + view + "]"
)
newIdx = idx + n
while newIdx < 0:
newIdx += numViews
newIdx = newIdx % numViews
return fb.evaluateObjectExpression(
"[(id)" + subviews + " objectAtIndex:" + str(newIdx) + "]"
)