Skip to content

Commit e8bd001

Browse files
committed
Package update:
Added: Conditional breakpoints. Added: Option for disabling/enabling breakpoints by double-clicking on status icon before line number in Xdebug Breakpoint window.
1 parent 1779c81 commit e8bd001

8 files changed

+138
-29
lines changed

Context.sublime-menu

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
"caption": "Add/Remove Breakpoint",
99
"command": "xdebug_breakpoint"
1010
},
11+
{
12+
"caption": "Set Conditional Breakpoint",
13+
"command": "xdebug_conditional_breakpoint"
14+
},
1115
{
1216
"caption": "Clear All Breakpoints",
1317
"command": "xdebug_clear_breakpoints"

Default.sublime-commands

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
"caption": "Xdebug: Add/Remove Breakpoint",
2727
"command": "xdebug_breakpoint"
2828
},
29+
{
30+
"caption": "Xdebug: Set Conditional Breakpoint",
31+
"command": "xdebug_conditional_breakpoint"
32+
},
2933
{
3034
"caption": "Xdebug: Clear All Breakpoints",
3135
"command": "xdebug_clear_breakpoints"

Default.sublime-keymap

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[
22
{"keys": ["ctrl+f8"], "command": "xdebug_breakpoint"},
3+
{"keys": ["ctrl+f9"], "command": "xdebug_conditional_breakpoint"},
34
{"keys": ["ctrl+shift+f5"], "command": "xdebug_continue", "args": {"command": "run"}},
45
{"keys": ["ctrl+shift+f6"], "command": "xdebug_continue", "args": {"command": "step_over"}},
56
{"keys": ["ctrl+shift+f7"], "command": "xdebug_continue", "args": {"command": "step_into"}},

Main.sublime-menu

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
"caption": "Add/Remove Breakpoint",
3939
"command": "xdebug_breakpoint"
4040
},
41+
{
42+
"caption": "Set Conditional Breakpoint",
43+
"command": "xdebug_conditional_breakpoint"
44+
},
4145
{
4246
"caption": "Clear All Breakpoints",
4347
"command": "xdebug_clear_breakpoints"

main.py

+56-19
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,21 @@ def on_selection_modified(self, view):
5050
# Show details in output panel of selected variable in context window
5151
if view.name() == V.TITLE_WINDOW_CONTEXT:
5252
V.show_context_output(view)
53+
elif view.name() == V.TITLE_WINDOW_BREAKPOINT:
54+
V.toggle_breakpoint(view)
5355
else:
5456
pass
5557

5658

5759
class XdebugBreakpointCommand(sublime_plugin.TextCommand):
5860
"""
5961
Add/Remove breakpoint(s) for rows (line numbers) in selection.
60-
61-
TODO: By argument setting expression for conditional breakpoint
6262
"""
63-
def run(self, edit, rows=None, expression=None):
63+
def run(self, edit, rows=None, condition=None, enabled=None, filename=None):
6464
# Get filename in current view and check if is a valid filename
65-
filename = self.view.file_name()
66-
if not filename:
65+
if filename is None:
66+
filename = self.view.file_name()
67+
if not filename or not os.path.isfile(filename):
6768
return
6869

6970
# Add entry for file in breakpoint data
@@ -76,40 +77,76 @@ def run(self, edit, rows=None, expression=None):
7677

7778
# Loop through rows
7879
for row in rows:
79-
# Add breakpoint
80-
if row not in S.BREAKPOINT[filename]:
81-
S.BREAKPOINT[filename][row] = { 'id': None, 'enabled': True, 'expression': expression }
82-
if session.is_connected():
80+
expression = None
81+
if condition is not None and len(condition.strip()) > 0:
82+
expression = condition
83+
# Check if breakpoint exists
84+
breakpoint_exists = row in S.BREAKPOINT[filename]
85+
# Disable/Remove breakpoint
86+
if breakpoint_exists:
87+
if session.is_connected() and S.BREAKPOINT[filename][row]['id'] is not None:
8388
try:
84-
S.SESSION.send(dbgp.BREAKPOINT_SET, t='line', f=util.get_real_path(filename, True), n=row)
89+
S.SESSION.send(dbgp.BREAKPOINT_REMOVE, d=S.BREAKPOINT[filename][row]['id'])
8590
response = S.SESSION.read().firstChild
86-
breakpoint_id = response.getAttribute('id')
87-
if breakpoint_id:
88-
S.BREAKPOINT[filename][row]['id'] = breakpoint_id
8991
except (socket.error, session.ProtocolConnectionException):
9092
e = sys.exc_info()[1]
9193
session.connection_error("%s" % e)
92-
# Remove breakpoint
93-
else:
94-
if session.is_connected() and S.BREAKPOINT[filename][row]['id'] is not None:
94+
if enabled is False:
95+
S.BREAKPOINT[filename][row]['enabled'] = False
96+
elif enabled is None:
97+
del S.BREAKPOINT[filename][row]
98+
# Add/Enable breakpoint
99+
if not breakpoint_exists or enabled is True:
100+
if row not in S.BREAKPOINT[filename]:
101+
S.BREAKPOINT[filename][row] = { 'id': None, 'enabled': True, 'expression': expression }
102+
else:
103+
S.BREAKPOINT[filename][row]['enabled'] = True
104+
if condition is not None:
105+
S.BREAKPOINT[filename][row]['expression'] = expression
106+
else:
107+
expression = S.BREAKPOINT[filename][row]['expression']
108+
if session.is_connected():
95109
try:
96-
S.SESSION.send(dbgp.BREAKPOINT_REMOVE, d=S.BREAKPOINT[filename][row]['id'])
110+
S.SESSION.send(dbgp.BREAKPOINT_SET, t='line', f=util.get_real_path(filename, True), n=row, expression=expression)
97111
response = S.SESSION.read().firstChild
112+
breakpoint_id = response.getAttribute('id')
113+
if breakpoint_id:
114+
S.BREAKPOINT[filename][row]['id'] = breakpoint_id
98115
except (socket.error, session.ProtocolConnectionException):
99116
e = sys.exc_info()[1]
100117
session.connection_error("%s" % e)
101-
del S.BREAKPOINT[filename][row]
102118

103119
# Render breakpoint markers
104120
V.render_regions()
105121

106122
# Update breakpoint list
107-
V.show_content(V.DATA_BREAKPOINT)
123+
try:
124+
if sublime.active_window().get_layout() == S.LAYOUT_DEBUG:
125+
V.show_content(V.DATA_BREAKPOINT)
126+
except:
127+
pass
108128

109129
# Save breakpoint data to file
110130
util.save_breakpoint_data()
111131

112132

133+
class XdebugConditionalBreakpointCommand(sublime_plugin.TextCommand):
134+
"""
135+
Add conditional breakpoint(s) for rows (line numbers) in selection.
136+
"""
137+
def run(self, edit):
138+
self.view.window().show_input_panel('Breakpoint condition', '', self.on_done, self.on_change, self.on_cancel)
139+
140+
def on_done(self, condition):
141+
self.view.run_command('xdebug_breakpoint', {'condition': condition, 'enabled': True})
142+
143+
def on_change(self, line):
144+
pass
145+
146+
def on_cancel(self):
147+
pass
148+
149+
113150
class XdebugClearBreakpointsCommand(sublime_plugin.TextCommand):
114151
"""
115152
Clear all breakpoints in selected view.

xdebug/load.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ def xdebug():
5959
for package in packages:
6060
if package.endswith(package_extension):
6161
package = package[:-len(package_extension)]
62-
if package.lower().count("xdebug") and package != current_package:
62+
if (package.lower().count("xdebug") or package.lower().count("moai")) and package != current_package:
6363
duplicates.append(package)
6464
# Show message if other Xdebug packages have been found
6565
if duplicates:
6666
info("Multiple 'xdebug' packages detected.")
6767
if not S.get_window_value('hide_conflict', False):
68-
sublime.error_message("The following Xdebug package(s) could cause conflicts with '{package}':\n\n{other}\n\nPlease consider removing the package(s) above when experiencing any complications." \
68+
sublime.error_message("The following package(s) could cause conflicts with '{package}':\n\n{other}\n\nPlease consider removing the package(s) above when experiencing any complications." \
6969
.format(package=S.PACKAGE_FOLDER, other='\n'.join(duplicates)))
7070
S.set_window_value('hide_conflict', True)
7171
else:

xdebug/settings.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717

1818
REGION_KEY_BREAKPOINT = 'xdebug_breakpoint'
1919
REGION_KEY_CURRENT = 'xdebug_current'
20-
REGION_SCOPE_BREAKPOINT = 'comment'
21-
REGION_SCOPE_CURRENT = 'string'
20+
REGION_KEY_DISABLED = 'xdebug_disabled'
21+
REGION_SCOPE_BREAKPOINT = 'comment.line.settings'
22+
REGION_SCOPE_CURRENT = 'string.quoted.settings'
2223

2324
ICON_BREAKPOINT = 'circle'
2425
ICON_BREAKPOINT_CURRENT = 'circle'
26+
ICON_BREAKPOINT_DISABLED = 'dot'
2527
ICON_CURRENT = 'bookmark'
2628

2729
# Window layout for debugging output

xdebug/view.py

+63-6
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ def show_content(data, content=None):
165165

166166
# Set content for view and fold all indendation blocks
167167
view.run_command('xdebug_view_update', {'data': content, 'readonly': True})
168-
view.run_command('fold_all')
168+
if data == DATA_CONTEXT:
169+
view.run_command('fold_all')
169170

170171
# Restore focus to previous active view/group
171172
if not previous_active is None:
@@ -312,16 +313,22 @@ def render_regions(view=None):
312313
# Remove all markers to avoid marker conflict
313314
view.erase_regions(S.REGION_KEY_BREAKPOINT)
314315
view.erase_regions(S.REGION_KEY_CURRENT)
316+
view.erase_regions(S.REGION_KEY_DISABLED)
315317

316318
# Get filename of current view and check if is a valid filename
317319
filename = view.file_name()
318320
if not filename:
319321
return
320322

321-
# Get all breakpoint rows (line numbers) for file
322-
breakpoint_rows = {}
323-
if filename in S.BREAKPOINT:
324-
breakpoint_rows = H.dictionary_keys(S.BREAKPOINT[filename])
323+
# Get all (disabled) breakpoint rows (line numbers) for file
324+
breakpoint_rows = []
325+
disabled_rows = []
326+
if filename in S.BREAKPOINT and isinstance(S.BREAKPOINT[filename], dict):
327+
for lineno, bp in S.BREAKPOINT[filename].items():
328+
if bp['enabled']:
329+
breakpoint_rows.append(lineno)
330+
else:
331+
disabled_rows.append(lineno)
325332

326333
# Get current line from breakpoint hit
327334
if S.BREAKPOINT_ROW is not None:
@@ -332,9 +339,59 @@ def render_regions(view=None):
332339
if S.BREAKPOINT_ROW['lineno'] in breakpoint_rows:
333340
icon = S.ICON_BREAKPOINT_CURRENT
334341
breakpoint_rows.remove(S.BREAKPOINT_ROW['lineno'])
342+
if S.BREAKPOINT_ROW['lineno'] in disabled_rows:
343+
disabled_rows.remove(S.BREAKPOINT_ROW['lineno'])
335344
# Set current line marker
336345
view.add_regions(S.REGION_KEY_CURRENT, rows_to_region(S.BREAKPOINT_ROW['lineno']), S.REGION_SCOPE_CURRENT, icon, sublime.HIDDEN)
337346

338347
# Set breakpoint marker(s)
339348
if breakpoint_rows:
340-
view.add_regions(S.REGION_KEY_BREAKPOINT, rows_to_region(breakpoint_rows), S.REGION_SCOPE_BREAKPOINT, S.ICON_BREAKPOINT, sublime.HIDDEN)
349+
view.add_regions(S.REGION_KEY_BREAKPOINT, rows_to_region(breakpoint_rows), S.REGION_SCOPE_BREAKPOINT, S.ICON_BREAKPOINT, sublime.HIDDEN)
350+
if disabled_rows:
351+
view.add_regions(S.REGION_KEY_DISABLED, rows_to_region(disabled_rows), S.REGION_SCOPE_BREAKPOINT, S.ICON_BREAKPOINT_DISABLED, sublime.HIDDEN)
352+
353+
354+
def toggle_breakpoint(view):
355+
try:
356+
# Get selected point in view
357+
point = view.sel()[0]
358+
# Check if selected point uses breakpoint line scope
359+
if point.size() == 3 and sublime.score_selector(view.scope_name(point.a), 'xdebug.output.breakpoint.line'):
360+
# Find line number of breakpoint
361+
line = view.substr(view.line(point))
362+
pattern = re.compile('^\\s*(?:(\\|\\+\\|)|(\\|-\\|))\\s*(?P<line_number>\\d+)\\s*(?:(--)(.*)|.*)')
363+
match = pattern.match(line)
364+
# Check if it has found line number
365+
if match and match.group('line_number'):
366+
# Get all breakpoint filenames
367+
breakpoint_file = view.find_by_selector('xdebug.output.breakpoint.file')
368+
# Locate line with filename related to selected breakpoint
369+
file_line = None
370+
for entry in breakpoint_file:
371+
# Stop searching if we have passed selected breakpoint
372+
if entry > point:
373+
break
374+
file_line = view.substr(view.line(entry))
375+
# Do not continue without line containing filename
376+
if file_line is None:
377+
return
378+
# Remove unnecessary text from line to get filename
379+
file_pattern = re.compile('^\\s*(=>)\\s*(?P<filename>.*)')
380+
file_match = file_pattern.match(file_line)
381+
# Check if it is a valid filename
382+
if file_match and file_match.group('filename'):
383+
filename = file_match.group('filename')
384+
line_number = match.group('line_number')
385+
enabled = None
386+
# Disable breakpoint
387+
if sublime.score_selector(view.scope_name(point.a), 'entity') and S.BREAKPOINT[filename][line_number]['enabled']:
388+
enabled = False
389+
# Enable breakpoint
390+
if sublime.score_selector(view.scope_name(point.a), 'keyword') and not S.BREAKPOINT[filename][line_number]['enabled']:
391+
enabled = True
392+
# Toggle breakpoint only if it has valid value
393+
if enabled is None:
394+
return
395+
sublime.active_window().run_command('xdebug_breakpoint', {"enabled": enabled, "rows": [line_number], "filename": filename})
396+
except:
397+
pass

0 commit comments

Comments
 (0)