config/sublime/Packages/Web Inspector/swi.py

1264 lines
No EOL
42 KiB
Python

import hashlib
import functools
import glob
import sublime
import sublime_plugin
import websocket
import urllib2
import threading
import json
import types
import os
import re
import wip
import time
from wip import utils
from wip import Console
from wip import Runtime
from wip import Debugger
from wip import Network
from wip import Page
import sys
reload(sys.modules['wip.utils'])
reload(sys.modules['wip.Console'])
reload(sys.modules['wip.Runtime'])
reload(sys.modules['wip.Debugger'])
reload(sys.modules['wip.Network'])
reload(sys.modules['wip.Page'])
brk_object = {}
buffers = {}
protocol = None
original_layout = None
window = None
debug_view = None
debug_url = None
file_to_scriptId = []
project_folders = []
last_clicked = None
paused = False
current_line = None
reload_on_start = False
reload_on_save = False
set_script_source = False
current_call_frame = None
current_call_frame_position = None
open_stack_current_in_new_tab = True
timing = time.time()
# scriptId_fileName = {}
breakpoint_active_icon = '../Web Inspector/icons/breakpoint_active'
breakpoint_inactive_icon = '../Web Inspector/icons/breakpoint_inactive'
breakpoint_current_icon = '../Web Inspector/icons/breakpoint_current'
####################################################################################
# PROTOCOL
####################################################################################
# Define protocol to communicate with remote debugger by web sockets
class Protocol(object):
def __init__(self):
self.next_id = 0
self.commands = {}
self.notifications = {}
self.last_log_object = None
def connect(self, url, on_open=None, on_close=None):
print 'SWI: Connecting to ' + url
websocket.enableTrace(False)
self.last_break = None
self.last_log_object = None
self.url = url
self.on_open = on_open
self.on_close = on_close
thread = threading.Thread(target=self.thread_callback)
thread.start()
# start connect with new thread
def thread_callback(self):
print 'SWI: Thread started'
self.socket = websocket.WebSocketApp(self.url, on_message=self.message_callback, on_open=self.open_callback, on_close=self.close_callback)
self.socket.run_forever()
print 'SWI: Thread stoped'
# send command and increment command counter
def send(self, command, callback=None, options=None):
command.id = self.next_id
command.callback = callback
command.options = options
self.commands[command.id] = command
self.next_id += 1
# print 'SWI: ->> ' + json.dumps(command.request)
self.socket.send(json.dumps(command.request))
# subscribe to notification with callback
def subscribe(self, notification, callback):
notification.callback = callback
self.notifications[notification.name] = notification
# unsubscribe
def unsubscribe(self, notification):
del self.notifications[notification.name]
# unsubscribe
def message_callback(self, ws, message):
parsed = json.loads(message)
# print 'SWI: <<- ' + message
# print ''
if 'method' in parsed:
if parsed['method'] in self.notifications:
notification = self.notifications[parsed['method']]
if 'params' in parsed:
data = notification.parser(parsed['params'])
else:
data = None
notification.callback(data, notification)
# else:
# print 'SWI: New unsubscrib notification --- ' + parsed['method']
else:
if parsed['id'] in self.commands:
command = self.commands[parsed['id']]
if 'error' in parsed:
sublime.set_timeout(lambda: sublime.error_message(parsed['error']['message']), 0)
else:
if 'result' in parsed:
command.data = command.parser(parsed['result'])
else:
command.data = None
if command.callback:
command.callback(command)
# print 'SWI: Command response with ID ' + str(parsed['id'])
def open_callback(self, ws):
if self.on_open:
self.on_open()
print 'SWI: WebSocket opened'
def close_callback(self, ws):
if self.on_close:
self.on_close()
print 'SWI: WebSocket closed'
####################################################################################
# COMMANDS
####################################################################################
class SwiDebugCommand(sublime_plugin.TextCommand):
'''
The SWIdebug main quick panel menu
'''
def run(self, editswi):
mapping = {}
try:
urllib2.urlopen('http://127.0.0.1:' + get_setting('chrome_remote_port') + '/json')
mapping = {}
if paused:
mapping['swi_debug_resume'] = 'Resume execution'
mapping['swi_debug_evaluate_on_call_frame'] = 'Evaluate selection'
#mapping['swi_debug_step_into'] = 'Step into'
#mapping['swi_debug_step_out'] = 'Step out'
#mapping['swi_debug_step_over'] = 'Step over'
else:
#mapping['swi_debug_clear_all_breakpoint'] = 'Clear all Breakpoints'
mapping['swi_debug_breakpoint'] = 'Add/Remove Breakpoint'
if protocol:
mapping['swi_debug_clear_console'] = 'Clear console'
mapping['swi_debug_stop'] = 'Stop debugging'
mapping['swi_debug_reload'] = 'Reload page'
else:
mapping['swi_debug_start'] = 'Start debugging'
except:
mapping['swi_debug_start_chrome'] = 'Start Google Chrome with remote debug port ' + get_setting('chrome_remote_port')
self.cmds = mapping.keys()
self.items = mapping.values()
self.view.window().show_quick_panel(self.items, self.command_selected)
def command_selected(self, index):
if index == -1:
return
command = self.cmds[index]
if command == 'swi_debug_start':
response = urllib2.urlopen('http://127.0.0.1:' + get_setting('chrome_remote_port') + '/json')
pages = json.loads(response.read())
mapping = {}
for page in pages:
if 'webSocketDebuggerUrl' in page:
if page['url'].find('chrome-extension://') == -1:
mapping[page['webSocketDebuggerUrl']] = page['url']
self.urls = mapping.keys()
items = mapping.values()
self.view.window().show_quick_panel(items, self.remote_debug_url_selected)
return
self.view.run_command(command)
def remote_debug_url_selected(self, index):
if index == -1:
return
url = self.urls[index]
global window
window = sublime.active_window()
global original_layout
original_layout = window.get_layout()
global debug_view
debug_view = window.active_view()
window.set_layout(get_setting('console_layout'))
load_breaks()
self.view.run_command('swi_debug_start', {'url': url})
class SwiDebugStartChromeCommand(sublime_plugin.TextCommand):
def run(self, edit):
window = sublime.active_window()
window.run_command('exec', {
"cmd": [os.getenv('GOOGLE_CHROME_PATH', '')+get_setting('chrome_path')[sublime.platform()], '--remote-debugging-port=' + get_setting('chrome_remote_port')]
})
class SwiDebugStartCommand(sublime_plugin.TextCommand):
def run(self, edit, url):
global file_to_scriptId
file_to_scriptId = []
window = sublime.active_window()
global project_folders
project_folders = window.folders()
print 'Starting SWI'
self.url = url
global protocol
if(protocol):
print 'SWI: Socket closed'
protocol.socket.close()
else:
print 'SWI: Creating protocol'
protocol = Protocol()
protocol.connect(self.url, self.connected, self.disconnected)
global reload_on_start
reload_on_start = get_setting('reload_on_start')
global reload_on_save
reload_on_save = get_setting('reload_on_save')
global set_script_source
set_script_source = get_setting('set_script_source')
global open_stack_current_in_new_tab
open_stack_current_in_new_tab = get_setting('open_stack_current_in_new_tab')
def connected(self):
protocol.subscribe(wip.Console.messageAdded(), self.messageAdded)
protocol.subscribe(wip.Console.messageRepeatCountUpdated(), self.messageRepeatCountUpdated)
protocol.subscribe(wip.Console.messagesCleared(), self.messagesCleared)
protocol.subscribe(wip.Debugger.scriptParsed(), self.scriptParsed)
protocol.subscribe(wip.Debugger.paused(), self.paused)
protocol.subscribe(wip.Debugger.resumed(), self.resumed)
protocol.send(wip.Debugger.enable())
protocol.send(wip.Console.enable())
protocol.send(wip.Debugger.canSetScriptSource(), self.canSetScriptSource)
if reload_on_start:
protocol.send(wip.Network.clearBrowserCache())
protocol.send(wip.Page.reload(), on_reload)
def disconnected(self):
sublime.set_timeout(lambda: debug_view.run_command('swi_debug_stop'), 0)
def messageAdded(self, data, notification):
sublime.set_timeout(lambda: console_add_message(data), 0)
def messageRepeatCountUpdated(self, data, notification):
sublime.set_timeout(lambda: console_repeat_message(data['count']), 0)
def messagesCleared(self, data, notification):
sublime.set_timeout(lambda: clear_view('console'), 0)
def scriptParsed(self, data, notification):
url = data['url']
if url != '':
url_parts = url.split("/")
scriptId = str(data['scriptId'])
file_name = ''
script = get_script(data['url'])
if script:
script['scriptId'] = str(scriptId)
file_name = script['file']
else:
del url_parts[0:3]
while len(url_parts) > 0:
for folder in project_folders:
if sublime.platform() == "windows":
files = glob.glob(folder + "\\" + "\\".join(url_parts))
else:
files = glob.glob(folder + "/" + "/".join(url_parts))
if len(files) > 0 and files[0] != '':
file_name = files[0]
file_to_scriptId.append({'file': file_name, 'scriptId': str(scriptId), 'sha1': hashlib.sha1(data['url']).hexdigest()})
del url_parts[0]
if get_breakpoints_by_full_path(file_name):
for line in get_breakpoints_by_full_path(file_name).keys():
location = wip.Debugger.Location({'lineNumber': int(line), 'scriptId': scriptId})
protocol.send(wip.Debugger.setBreakpoint(location), self.breakpointAdded)
def paused(self, data, notification):
sublime.set_timeout(lambda: window.set_layout(get_setting('stack_layout')), 0)
sublime.set_timeout(lambda: console_show_stack(data['callFrames']), 0)
scriptId = data['callFrames'][0].location.scriptId
line_number = data['callFrames'][0].location.lineNumber
file_name = find_script(str(scriptId))
first_scope = data['callFrames'][0].scopeChain[0]
if open_stack_current_in_new_tab:
title = {'objectId': first_scope.object.objectId, 'name': "%s:%s (%s)" % (file_name, line_number, first_scope.type)}
else:
title = {'objectId': first_scope.object.objectId, 'name': "Breakpoint Local"}
global current_call_frame
current_call_frame = data['callFrames'][0].callFrameId
global current_call_frame_position
current_call_frame_position = "%s:%s" % (file_name, line_number)
sublime.set_timeout(lambda: protocol.send(wip.Runtime.getProperties(first_scope.object.objectId, True), console_add_properties, title), 30)
sublime.set_timeout(lambda: open_script_and_focus_line(scriptId, line_number), 100)
global paused
paused = True
def resumed(self, data, notification):
sublime.set_timeout(lambda: clear_view('stack'), 0)
global current_line
current_line = None
global current_call_frame
current_call_frame = None
global current_call_frame_position
current_call_frame_position = None
sublime.set_timeout(lambda: lookup_view(self.view).view_breakpoints(), 50)
global paused
paused = False
def breakpointAdded(self, command):
breakpointId = command.data['breakpointId']
scriptId = command.data['actualLocation'].scriptId
lineNumber = command.data['actualLocation'].lineNumber
try:
breakpoint = get_breakpoints_by_scriptId(str(scriptId))[str(lineNumber)]
breakpoint['status'] = 'enabled'
breakpoint['breakpointId'] = str(breakpointId)
except:
pass
try:
breaks = get_breakpoints_by_scriptId(str(scriptId))[str(lineNumber)]
lineNumber = str(lineNumber)
lineNumberSend = str(command.params['lineNumber'])
if lineNumberSend in breaks and lineNumber != lineNumberSend:
breaks[lineNumber] = breaks[lineNumberSend].copy()
del breaks[lineNumberSend]
breaks[lineNumber]['status'] = 'enabled'
breaks[lineNumber]['breakpointId'] = str(breakpointId)
except:
pass
sublime.set_timeout(lambda: save_breaks(), 0)
sublime.set_timeout(lambda: lookup_view(self.view).view_breakpoints(), 0)
def canSetScriptSource(self, command):
global set_script_source
if set_script_source:
set_script_source = command.data['result']
class SwiDebugResumeCommand(sublime_plugin.TextCommand):
def run(self, edit):
protocol.send(wip.Debugger.resume())
class SwiDebugStepIntoCommand(sublime_plugin.TextCommand):
def run(self, edit):
protocol.send(wip.Debugger.stepInto())
class SwiDebugStepOutCommand(sublime_plugin.TextCommand):
def run(self, edit):
protocol.send(wip.Debugger.stepOut())
class SwiDebugStepOverCommand(sublime_plugin.TextCommand):
def run(self, edit):
protocol.send(wip.Debugger.stepOver())
class SwiDebugClearConsoleCommand(sublime_plugin.TextCommand):
def run(self, edit):
sublime.set_timeout(lambda: clear_view('console'), 0)
class SwiDebugEvaluateOnCallFrameCommand(sublime_plugin.TextCommand):
def run(self, edit):
for region in self.view.sel():
title = self.view.substr(region)
if current_call_frame_position:
title = "%s on %s" % (self.view.substr(region), current_call_frame_position)
protocol.send(wip.Debugger.evaluateOnCallFrame(current_call_frame, self.view.substr(region)), self.evaluated, {'name': title})
def evaluated(self, command):
if command.data.type == 'object':
protocol.send(wip.Runtime.getProperties(command.data.objectId, True), console_add_properties, command.options)
else:
sublime.set_timeout(lambda: console_add_evaluate(command.data), 0)
class SwiDebugBreakpointCommand(sublime_plugin.TextCommand):
'''
Toggle a breakpoint
'''
def run(self, edit):
view = lookup_view(self.view)
row = str(view.rows(view.lines())[0])
init_breakpoint_for_file(view.file_name())
breaks = get_breakpoints_by_full_path(view.file_name())
if row in breaks:
if protocol:
if row in breaks:
protocol.send(wip.Debugger.removeBreakpoint(breaks[row]['breakpointId']))
del_breakpoint_by_full_path(view.file_name(), row)
else:
if protocol:
scriptId = find_script(view.file_name())
if scriptId:
location = wip.Debugger.Location({'lineNumber': int(row), 'scriptId': scriptId})
protocol.send(wip.Debugger.setBreakpoint(location), self.breakpointAdded, view.file_name())
else:
set_breakpoint_by_full_path(view.file_name(), row)
view.view_breakpoints()
def breakpointAdded(self, command):
breakpointId = command.data['breakpointId']
scriptId = command.data['actualLocation'].scriptId
lineNumber = command.data['actualLocation'].lineNumber
init_breakpoint_for_file(command.options)
sublime.set_timeout(lambda: set_breakpoint_by_scriptId(str(scriptId), str(lineNumber), 'enabled', breakpointId), 0)
# Scroll to position where breakpoints have resolved
sublime.set_timeout(lambda: lookup_view(self.view).view_breakpoints(), 0)
class SwiDebugStopCommand(sublime_plugin.TextCommand):
def run(self, edit):
global window
window.focus_group(1)
for view in window.views_in_group(1):
window.run_command("close")
window.focus_group(2)
for view in window.views_in_group(2):
window.run_command("close")
window.set_layout(original_layout)
disable_all_breakpoints()
lookup_view(self.view).view_breakpoints()
global paused
paused = False
global current_line
current_line = None
sublime.set_timeout(lambda: lookup_view(self.view).view_breakpoints(), 0)
global protocol
if protocol:
try:
protocol.socket.close()
except:
print 'SWI: Can\'t close soket'
finally:
protocol = None
class SwiDebugReloadCommand(sublime_plugin.TextCommand):
def run(self, view):
if(protocol):
protocol.send(wip.Network.clearBrowserCache())
protocol.send(wip.Page.reload(), on_reload)
####################################################################################
# VIEW
####################################################################################
class SwiDebugView(object):
'''
The SWIDebugView is sort of a normal view with some convenience methods.
See lookup_view.
'''
def __init__(self, view):
self.view = view
self.context_data = {}
self.clicks = []
self.prev_click_position = 0
def __getattr__(self, attr):
if hasattr(self.view, attr):
return getattr(self.view, attr)
if attr.startswith('on_'):
return self
raise(AttributeError, "%s does not exist" % attr)
def __call__(self, *args, **kwargs):
pass
def uri(self):
return 'file://' + os.path.realpath(self.view.file_name())
def lines(self, data=None):
lines = []
if data is None:
regions = self.view.sel()
else:
if type(data) != types.ListType:
data = [data]
regions = []
for item in data:
if type(item) == types.IntType or item.isdigit():
regions.append(self.view.line(self.view.text_point(int(item) - 1, 0)))
else:
regions.append(item)
for region in regions:
lines.extend(self.view.split_by_newlines(region))
return [self.view.line(line) for line in lines]
def rows(self, lines):
if not type(lines) == types.ListType:
lines = [lines]
return [self.view.rowcol(line.begin())[0] + 1 for line in lines]
def insert_click(self, a, b, click_type, data):
insert_before = 0
new_region = sublime.Region(a, b)
regions = self.view.get_regions('swi_log_clicks')
for region in regions:
if new_region.b < region.a:
break
insert_before += 1
self.clicks.insert(insert_before, {'click_type': click_type, 'data': data})
regions.append(new_region)
self.view.add_regions('swi_log_clicks', regions, get_setting('interactive_scope'), sublime.DRAW_EMPTY_AS_OVERWRITE | sublime.DRAW_OUTLINED)
def print_click(self, edit, position, text, click_type, data):
insert_length = self.insert(edit, position, text)
self.insert_click(position, position + insert_length, click_type, data)
def remove_click(self, index):
regions = self.view.get_regions('swi_log_clicks')
del regions[index]
self.view.add_regions('swi_log_clicks', regions, get_setting('interactive_scope'), sublime.DRAW_EMPTY_AS_OVERWRITE | sublime.DRAW_OUTLINED)
def clear_clicks(self):
self.clicks = []
def view_breakpoints(self):
self.view.erase_regions('swi_breakpoint_inactive')
self.view.erase_regions('swi_breakpoint_active')
self.view.erase_regions('swi_breakpoint_current')
if not self.view.file_name():
return
breaks = get_breakpoints_by_full_path(self.view.file_name())
if not breaks:
return
enabled = []
disabled = []
for key in breaks.keys():
if breaks[key]['status'] == 'enabled' and str(current_line) != key:
enabled.append(key)
if breaks[key]['status'] == 'disabled' and str(current_line) != key:
disabled.append(key)
self.view.add_regions('swi_breakpoint_active', self.lines(enabled), get_setting('breakpoint_scope'), breakpoint_active_icon, sublime.HIDDEN)
self.view.add_regions('swi_breakpoint_inactive', self.lines(disabled), get_setting('breakpoint_scope'), breakpoint_inactive_icon, sublime.HIDDEN)
if current_line:
self.view.add_regions('swi_breakpoint_current', self.lines([current_line]), get_setting('current_line_scope'), breakpoint_current_icon, sublime.DRAW_EMPTY)
def check_click(self):
if not self.name().startswith('SWI'):
return
cursor = self.sel()[0].a
if cursor == self.prev_click_position:
return
self.prev_click_position = cursor
click_counter = 0
click_regions = self.get_regions('swi_log_clicks')
for click in click_regions:
if cursor > click.a and cursor < click.b:
if click_counter < len(self.clicks):
click = self.clicks[click_counter]
if click['click_type'] == 'goto_file_line':
open_script_and_focus_line(click['data']['scriptId'], click['data']['line'])
if click['click_type'] == 'goto_call_frame':
callFrame = click['data']['callFrame']
scriptId = callFrame.location.scriptId
line_number = callFrame.location.lineNumber
file_name = find_script(str(scriptId))
open_script_and_focus_line(scriptId, line_number)
first_scope = callFrame.scopeChain[0]
if open_stack_current_in_new_tab:
title = {'objectId': first_scope.object.objectId, 'name': "%s:%s (%s)" % (file_name.split('/')[-1], line_number, first_scope.type)}
else:
title = {'objectId': first_scope.object.objectId, 'name': "Breakpoint Local"}
sublime.set_timeout(lambda: protocol.send(wip.Runtime.getProperties(first_scope.object.objectId, True), console_add_properties, title), 30)
global current_call_frame
current_call_frame = callFrame.callFrameId
global current_call_frame_position
current_call_frame_position = "%s:%s" % (file_name.split('/')[-1], line_number)
if click['click_type'] == 'get_params':
if protocol:
protocol.send(wip.Runtime.getProperties(click['data']['objectId'], True), console_add_properties, click['data'])
if click['click_type'] == 'command':
self.remove_click(click_counter)
self.run_command(click['data'])
click_counter += 1
def lookup_view(v):
'''
Convert a Sublime View into an SWIDebugView
'''
if isinstance(v, SwiDebugView):
return v
if isinstance(v, sublime.View):
id = v.buffer_id()
if id in buffers:
buffers[id].view = v
else:
buffers[id] = SwiDebugView(v)
return buffers[id]
return None
####################################################################################
# EventListener
####################################################################################
class EventListener(sublime_plugin.EventListener):
def on_new(self, view):
lookup_view(view).on_new()
def on_clone(self, view):
lookup_view(view).on_clone()
def on_load(self, view):
lookup_view(view).view_breakpoints()
lookup_view(view).on_load()
def on_close(self, view):
lookup_view(view).on_close()
def on_pre_save(self, view):
lookup_view(view).on_pre_save()
def on_post_save(self, view):
print view.file_name().find('.js')
if protocol and reload_on_save:
protocol.send(wip.Network.clearBrowserCache())
if view.file_name().find('.css') > 0 or view.file_name().find('.less') > 0 or view.file_name().find('.sass') > 0 or view.file_name().find('.scss') > 0:
protocol.send(wip.Runtime.evaluate("var files = document.getElementsByTagName('link');var links = [];for (var a = 0, l = files.length; a < l; a++) {var elem = files[a];var rel = elem.rel;if (typeof rel != 'string' || rel.length === 0 || rel === 'stylesheet') {links.push({'elem': elem,'href': elem.getAttribute('href').split('?')[0],'last': false});}}for ( a = 0, l = links.length; a < l; a++) {var link = links[a];link.elem.setAttribute('href', (link.href + '?x=' + Math.random()));}"))
elif view.file_name().find('.js') > 0:
scriptId = find_script(view.file_name())
if scriptId and set_script_source:
scriptSource = view.substr(sublime.Region(0, view.size()))
protocol.send(wip.Debugger.setScriptSource(scriptId, scriptSource), self.paused)
else:
protocol.send(wip.Page.reload(), on_reload)
else:
protocol.send(wip.Page.reload(), on_reload)
lookup_view(view).on_post_save()
def on_modified(self, view):
lookup_view(view).on_modified()
lookup_view(view).view_breakpoints()
def on_selection_modified(self, view):
#lookup_view(view).on_selection_modified()
global timing
now = time.time()
if now - timing > 0.08:
timing = now
sublime.set_timeout(lambda: lookup_view(view).check_click(), 0)
else:
timing = now
def on_activated(self, view):
lookup_view(view).on_activated()
lookup_view(view).view_breakpoints()
def on_deactivated(self, view):
lookup_view(view).on_deactivated()
def on_query_context(self, view, key, operator, operand, match_all):
lookup_view(view).on_query_context(key, operator, operand, match_all)
def paused(self, command):
global paused
if not paused:
return
data = command.data
sublime.set_timeout(lambda: window.set_layout(get_setting('stack_layout')), 0)
sublime.set_timeout(lambda: console_show_stack(data['callFrames']), 0)
scriptId = data['callFrames'][0].location.scriptId
line_number = data['callFrames'][0].location.lineNumber
file_name = find_script(str(scriptId))
first_scope = data['callFrames'][0].scopeChain[0]
if open_stack_current_in_new_tab:
title = {'objectId': first_scope.object.objectId, 'name': "%s:%s (%s)" % (file_name, line_number, first_scope.type)}
else:
title = {'objectId': first_scope.object.objectId, 'name': "Breakpoint Local"}
sublime.set_timeout(lambda: protocol.send(wip.Runtime.getProperties(first_scope.object.objectId, True), console_add_properties, title), 30)
sublime.set_timeout(lambda: open_script_and_focus_line(scriptId, line_number), 100)
####################################################################################
# GLOBAL HANDLERS
####################################################################################
def on_reload(command):
global file_to_scriptId
file_to_scriptId = []
####################################################################################
# Console
####################################################################################
def find_view(console_type, title=''):
found = False
v = None
window = sublime.active_window()
if console_type.startswith('console'):
group = 1
fullName = "SWI Console"
if console_type == 'stack':
group = 2
fullName = "SWI Breakpoint stack"
if console_type.startswith('eval'):
group = 1
fullName = "SWI Object evaluate"
fullName = fullName + ' ' + title
for v in window.views():
if v.name() == fullName:
found = True
break
if not found:
v = window.new_file()
v.set_scratch(True)
v.set_read_only(False)
v.set_name(fullName)
v.settings().set('word_wrap', False)
window.set_view_index(v, group, 0)
if console_type.startswith('console'):
v.set_syntax_file('Packages/Web Inspector/swi_log.tmLanguage')
if console_type == 'stack':
v.set_syntax_file('Packages/Web Inspector/swi_stack.tmLanguage')
if console_type.startswith('eval'):
v.set_syntax_file('Packages/Web Inspector/swi_log.tmLanguage')
window.focus_view(v)
v.set_read_only(False)
return lookup_view(v)
def clear_view(view):
v = find_view(view)
edit = v.begin_edit()
v.erase(edit, sublime.Region(0, v.size()))
v.end_edit(edit)
v.show(v.size())
window.focus_group(0)
lookup_view(v).clear_clicks()
def console_repeat_message(count):
v = find_view('console')
edit = v.begin_edit()
if count > 2:
erase_to = v.size() - len(u' \u21AA Repeat:' + str(count - 1) + '\n')
v.erase(edit, sublime.Region(erase_to, v.size()))
v.insert(edit, v.size(), u' \u21AA Repeat:' + str(count) + '\n')
v.end_edit(edit)
v.show(v.size())
window.focus_group(0)
def console_add_evaluate(eval_object):
v = find_view('console')
edit = v.begin_edit()
insert_position = v.size()
v.insert(edit, insert_position, str(eval_object) + ' ')
v.insert(edit, v.size(), "\n")
v.end_edit(edit)
v.show(v.size())
window.focus_group(0)
def console_add_message(message):
v = find_view('console')
edit = v.begin_edit()
if message.level == 'debug':
level = "D"
if message.level == 'error':
level = "E"
if message.level == 'log':
level = "L"
if message.level == 'tip':
level = "T"
if message.level == 'warning':
level = "W"
v.insert(edit, v.size(), "[%s] " % (level))
# Add file and line
scriptId = None
if message.url:
scriptId = find_script(message.url)
if scriptId:
url = message.url.split("/")[-1]
else:
url = message.url
else:
url = '---'
if message.line:
line = message.line
else:
line = 0
insert_position = v.size()
insert_length = v.insert(edit, insert_position, "%s:%d" % (url, line))
if scriptId and line > 0:
v.insert_click(insert_position, insert_position + insert_length, 'goto_file_line', {'scriptId': scriptId, 'line': str(line)})
v.insert(edit, v.size(), " ")
# Add text
if len(message.parameters) > 0:
for param in message.parameters:
insert_position = v.size()
insert_length = v.insert(edit, insert_position, str(param) + ' ')
if param.type == 'object':
v.insert_click(insert_position, insert_position + insert_length - 1, 'get_params', {'objectId': param.objectId})
else:
v.insert(edit, v.size(), message.text)
v.insert(edit, v.size(), "\n")
if level == "E" and message.stackTrace:
stack_start = v.size()
for callFrame in message.stackTrace:
scriptId = find_script(callFrame.url)
file_name = callFrame.url.split('/')[-1]
v.insert(edit, v.size(), u'\t\u21E1 ')
if scriptId:
v.print_click(edit, v.size(), "%s:%s %s" % (file_name, callFrame.lineNumber, callFrame.functionName), 'goto_file_line', {'scriptId': scriptId, 'line': str(callFrame.lineNumber)})
else:
v.insert(edit, v.size(), "%s:%s %s" % (file_name, callFrame.lineNumber, callFrame.functionName))
v.insert(edit, v.size(), "\n")
v.fold(sublime.Region(stack_start-1, v.size()-1))
v.end_edit(edit)
v.show(v.size())
window.focus_group(0)
def console_add_properties(command):
sublime.set_timeout(lambda: console_print_properties(command), 0)
def console_print_properties(command):
if 'name' in command.options:
name = command.options['name']
else:
name = str(command.options['objectId'])
if 'prev' in command.options:
prev = command.options['prev'] + ' -> ' + name
else:
prev = name
v = find_view('eval', name)
edit = v.begin_edit()
v.erase(edit, sublime.Region(0, v.size()))
v.insert(edit, v.size(), prev)
v.insert(edit, v.size(), "\n\n")
for prop in command.data:
v.insert(edit, v.size(), prop.name + ': ')
insert_position = v.size()
if(prop.value):
insert_length = v.insert(edit, insert_position, str(prop.value) + '\n')
if prop.value.type == 'object':
v.insert_click(insert_position, insert_position + insert_length - 1, 'get_params', {'objectId': prop.value.objectId, 'name': prop.name, 'prev': prev})
v.end_edit(edit)
v.show(0)
window.focus_group(0)
def console_show_stack(callFrames):
v = find_view('stack')
edit = v.begin_edit()
v.erase(edit, sublime.Region(0, v.size()))
v.insert(edit, v.size(), "\n")
v.print_click(edit, v.size(), "\tResume\t", 'command', 'swi_debug_resume')
v.print_click(edit, v.size(), "\tStep Over\t", 'command', 'swi_debug_step_over')
v.print_click(edit, v.size(), "\tStep Into\t", 'command', 'swi_debug_step_into')
v.print_click(edit, v.size(), "\tStep Out\t", 'command', 'swi_debug_step_out')
v.insert(edit, v.size(), "\n\n")
for callFrame in callFrames:
line = str(callFrame.location.lineNumber)
file_name = find_script(str(callFrame.location.scriptId))
if file_name:
file_name = file_name.split('/')[-1]
else:
file_name = '-'
insert_position = v.size()
insert_length = v.insert(edit, insert_position, "%s:%s" % (file_name, line))
if file_name != '-':
v.insert_click(insert_position, insert_position + insert_length, 'goto_call_frame', {'callFrame': callFrame})
v.insert(edit, v.size(), " %s\n" % (callFrame.functionName))
for scope in callFrame.scopeChain:
v.insert(edit, v.size(), "\t")
insert_position = v.size()
insert_length = v.insert(edit, v.size(), "%s\n" % (scope.type))
if scope.object.type == 'object':
v.insert_click(insert_position, insert_position + insert_length - 1, 'get_params', {'objectId': scope.object.objectId, 'name': "%s:%s (%s)" % (file_name, line, scope.type)})
v.end_edit(edit)
v.show(0)
window.focus_group(0)
####################################################################################
# All about breaks
####################################################################################
def get_project():
if not sublime.active_window():
return None
win_id = sublime.active_window().id()
project = None
reg_session = os.path.join(sublime.packages_path(), "..", "Settings", "Session.sublime_session")
auto_save = os.path.join(sublime.packages_path(), "..", "Settings", "Auto Save Session.sublime_session")
session = auto_save if os.path.exists(auto_save) else reg_session
if not os.path.exists(session) or win_id == None:
return project
try:
with open(session, 'r') as f:
# Tabs in strings messes things up for some reason
j = json.JSONDecoder(strict=False).decode(f.read())
for w in j['windows']:
if w['window_id'] == win_id:
if "workspace_name" in w:
if sublime.platform() == "windows":
# Account for windows specific formatting
project = os.path.normpath(w["workspace_name"].lstrip("/").replace("/", ":/", 1))
else:
project = w["workspace_name"]
break
except:
pass
# Throw out empty project names
if project == None or re.match(".*\\.sublime-project", project) == None or not os.path.exists(project):
project = None
return project
def load_breaks():
# if not get_project():
# sublime.error_message('Can\' load breaks')
# brk_object = {}
# return
# breaks_file = os.path.splitext(get_project())[0] + '-breaks.json'
# global brk_object
# if not os.path.exists(breaks_file):
# with open(breaks_file, 'w') as f:
# f.write('{}')
# try:
# with open(breaks_file, 'r') as f:
# brk_object = json.loads(f.read())
# except:
# brk_object = {}
global brk_object
brk_object = get_setting('breaks')
def save_breaks():
# try:
# breaks_file = os.path.splitext(get_project())[0] + '-breaks.json'
# with open(breaks_file, 'w') as f:
# f.write(json.dumps(brk_object, sort_keys=True, indent=4, separators=(',', ': ')))
# except:
# pass
s = sublime.load_settings("swi.sublime-settings")
s.set('breaks', brk_object)
sublime.save_settings("swi.sublime-settings")
#print breaks
def full_path_to_file_name(path):
return os.path.basename(os.path.realpath(path))
def set_breakpoint_by_full_path(file_name, line, status='disabled', breakpointId=None):
breaks = get_breakpoints_by_full_path(file_name)
if not line in breaks:
breaks[line] = {}
breaks[line]['status'] = status
breaks[line]['breakpointId'] = str(breakpointId)
else:
breaks[line]['status'] = status
breaks[line]['breakpointId'] = str(breakpointId)
save_breaks()
def del_breakpoint_by_full_path(file_name, line):
breaks = get_breakpoints_by_full_path(file_name)
if line in breaks:
del breaks[line]
save_breaks()
def get_breakpoints_by_full_path(file_name):
if file_name in brk_object:
return brk_object[file_name]
return None
def set_breakpoint_by_scriptId(scriptId, line, status='disabled', breakpointId=None):
file_name = find_script(str(scriptId))
if file_name:
set_breakpoint_by_full_path(file_name, line, status, breakpointId)
def del_breakpoint_by_scriptId(scriptId, line):
file_name = find_script(str(scriptId))
if file_name:
del_breakpoint_by_full_path(file_name, line)
def get_breakpoints_by_scriptId(scriptId):
file_name = find_script(str(scriptId))
if file_name:
return get_breakpoints_by_full_path(file_name)
return None
def init_breakpoint_for_file(file_path):
if not file_path in brk_object:
brk_object[file_path] = {}
def disable_all_breakpoints():
for file_name in brk_object:
for line in brk_object[file_name]:
brk_object[file_name][line]['status'] = 'disabled'
if 'breakpointId' in brk_object[file_name][line]:
del brk_object[file_name][line]['breakpointId']
save_breaks()
####################################################################################
# Utils
####################################################################################
def get_setting(key):
s = sublime.load_settings("swi.sublime-settings")
if s and s.has(key):
return s.get(key)
def find_script(scriptId_or_file_or_url):
sha = hashlib.sha1(scriptId_or_file_or_url).hexdigest()
for item in file_to_scriptId:
if item['scriptId'] == scriptId_or_file_or_url:
return item['file']
if item['file'] == scriptId_or_file_or_url:
return item['scriptId']
if item['sha1'] == sha:
return item['scriptId']
return None
def get_script(scriptId_or_file_or_url):
sha = hashlib.sha1(scriptId_or_file_or_url).hexdigest()
for item in file_to_scriptId:
if item['scriptId'] == scriptId_or_file_or_url:
return item
if item['file'] == scriptId_or_file_or_url:
return item
if item['sha1'] == sha:
return item
return None
def do_when(conditional, callback, *args, **kwargs):
if conditional():
return callback(*args, **kwargs)
sublime.set_timeout(functools.partial(do_when, conditional, callback, *args, **kwargs), 50)
def open_script_and_focus_line(scriptId, line_number):
file_name = find_script(str(scriptId))
window = sublime.active_window()
window.focus_group(0)
view = window.open_file(file_name, sublime.TRANSIENT)
do_when(lambda: not view.is_loading(), lambda: view.run_command("goto_line", {"line": line_number}))
def open_script_and_show_current_breakpoint(scriptId, line_number):
file_name = find_script(str(scriptId))
window.focus_group(0)
view = window.open_file(file_name, sublime.TRANSIENT)
do_when(lambda: not view.is_loading(), lambda: view.run_command("goto_line", {"line": line_number}))
#do_when(lambda: not view.is_loading(), lambda: focus_line_and_highlight(view, line_number))
def focus_line_and_highlight(view, line_number):
view.run_command("goto_line", {"line": line_number})
global current_line
current_line = line_number
lookup_view(view).view_breakpoints()
sublime.set_timeout(lambda: load_breaks(), 1000)