Continuing the post from: https://forum.sublimetext.com/t/how-to-replace-dots-with-slashes-when-using-file-regex/45764
I managed to do it as described on the first post. I just need to patch exec.py
to allow it to copy the new settings to the output view, and create these new settings on my project build:
My.sublime-project
{
"folders":
[
{ "path": ".", },
],
"build_systems":
[
{
"full_regex": "^\\d\\d:\\d\\d:\\d\\d:\\d\\d\\d.\\d\\d\\d\\d\\d\\d \\d.\\d\\de-\\d\\d - (?P<file>\\w+(?:\\.\\w+(?!\\.\\w+:))*)[^:]+:(?P<line>\\d+) - (?P<message>.*)$",
"result_dir": "$project_path/source",
"replaceby": [ [ "\\.", "\\\\" ], [ "(.*)", "\\1.py" ] ],
"name": "Single Test",
"target": "run_python_tests",
"shell_cmd": "python unit_tests.py -v {test_class}.{test_func} -f",
"working_dir": "$project_path/source",
},
],
}
exec.py
--- a/exec.py
+++ b/exec.py
class FixedToggleFindPanelCommand(sublime_plugin.WindowCommand):
@@ -438,6 +522,9 @@ class ExecCommand(sublime_plugin.WindowCommand, ProcessListener):
spell_check=None,
gutter=None,
syntax="Packages/Text/Plain text.tmLanguage",
+ full_regex="",
+ result_dir="",
+ replaceby={},
# Catches "path" and "shell"
**kwargs):
# print( 'ExecCommand arguments: ', locals())
@@ -475,6 +562,10 @@ class ExecCommand(sublime_plugin.WindowCommand, ProcessListener):
if spell_check is None: spell_check = view_settings.get("build_view_spell_check", False)
if gutter is None: gutter = view_settings.get("gutter", True)
+ self.output_view.settings().set("result_full_regex", full_regex)
+ self.output_view.settings().set("result_replaceby", replaceby)
+ self.output_view.settings().set("result_real_dir", result_dir)
+
self.output_view.settings().set("result_file_regex", file_regex)
self.output_view.settings().set("result_line_regex", line_regex)
full_regex.py
import os
import re
import time
import sublime
import sublime_plugin
g_last_click_time = time.time()
g_last_click_buttons = None
class HackListener(sublime_plugin.EventListener):
def replaceby(self, string, replacements):
# print('replacements', replacements)
for items in replacements:
regex = items[0]
replacement = items[1]
string = re.sub( regex, replacement, string )
return string
def on_text_command(self, view, command_name, args):
# print('command_name', command_name, 'args', args)
result_full_regex = view.settings().get('result_full_regex')
# print('result_full_regex', result_full_regex)
if result_full_regex and command_name == 'drag_select' and 'event' in args:
global g_last_click_time
global g_last_click_buttons
clicks_buttons = args['event']
new_click = time.time()
if clicks_buttons == g_last_click_buttons:
click_time = new_click - g_last_click_time
if click_time < 0.6:
view_selections = view.sel()
if view_selections:
full_line = view.substr( view.full_line( view_selections[0] ) )
# print('Double clicking', click_time, 'full_line', full_line )
full_regex_object = re.compile( result_full_regex )
matchobject = full_regex_object.search( full_line )
if matchobject:
groupindex = full_regex_object.groupindex
# https://github.com/SublimeTextIssues/Core/issues/227
file_name = matchobject.group('file').strip( ' ' ) if 'file' in groupindex else None
line = matchobject.group('line').strip( ' ' ) if 'line' in groupindex else "0"
column = matchobject.group('column').strip( ' ' ) if 'column' in groupindex else "0"
window = view.window() or sublime.active_window()
extract_variables = window.extract_variables()
# github.com/SublimeTextIssues/Core/issues/1482
active_view = window.active_view()
group, view_index = window.get_view_index( active_view )
window.set_view_index( active_view, group, 0 )
# https://github.com/SublimeTextIssues/Core/issues/938
result_replaceby = view.settings().get('result_replaceby', {})
result_real_dir = view.settings().get('result_real_dir', os.path.abspath('.') )
if file_name:
real_dir_file = os.path.join( result_real_dir, file_name )
real_dir_file = sublime.expand_variables( real_dir_file, extract_variables )
real_dir_file = self.replaceby( real_dir_file, result_replaceby )
if os.path.exists( real_dir_file ):
file_name = real_dir_file
else:
base_dir_file = view.settings().get('result_base_dir')
file_name = os.path.join( base_dir_file, file_name )
file_name = sublime.expand_variables( file_name, extract_variables )
file_name = self.replaceby( file_name, result_replaceby )
file_name = os.path.normpath( file_name )
else:
file_name = active_view.file_name()
fileview = window.open_file(
file_name + ":" + line + ":" + column,
sublime.ENCODED_POSITION | sublime.FORCE_GROUP
)
# https://github.com/SublimeTextIssues/Core/issues/2506
restore_view( fileview, window, lambda: None )
window.set_view_index( active_view, group, view_index )
# window.focus_group( group )
# window.focus_view( fileview )
g_last_click_time = new_click
g_last_click_buttons = clicks_buttons
TIME_AFTER_FOCUS_VIEW = 30
TIME_AFTER_RESTORE_VIEW = 15
def restore_view(view, window, next_target, withfocus=True):
""" Taken from the https://github.com/evandrocoan/FixProjectSwitchRestartBug package
Because on Linux, set_viewport was not restoring the scroll.
"""
if view.is_loading():
sublime.set_timeout( lambda: restore_view( view, window, next_target, withfocus=withfocus ), 200 )
else:
selections = view.sel()
file_name = view.file_name()
if len( selections ):
first_selection = selections[0].begin()
original_selections = list( selections )
def super_refocus():
view.run_command( "move", {"by": "lines", "forward": False} )
view.run_command( "move", {"by": "lines", "forward": True} )
def fix_selections():
selections.clear()
for selection in original_selections:
selections.add( selection )
sublime.set_timeout( next_target, TIME_AFTER_RESTORE_VIEW )
sublime.set_timeout( fix_selections, TIME_AFTER_RESTORE_VIEW )
if file_name and withfocus:
def reforce_focus():
# https://github.com/SublimeTextIssues/Core/issues/1482
group, view_index = window.get_view_index( view )
window.set_view_index( view, group, 0 )
# https://github.com/SublimeTextIssues/Core/issues/538
row, column = view.rowcol( first_selection )
window.open_file( "%s:%d:%d" % ( file_name, row + 1, column + 1 ), sublime.ENCODED_POSITION )
window.set_view_index( view, group, view_index )
# print( 'Super reforce focus focusing...' )
sublime.set_timeout( super_refocus, TIME_AFTER_RESTORE_VIEW )
view.show_at_center( first_selection )
sublime.set_timeout( reforce_focus, TIME_AFTER_FOCUS_VIEW )
else:
view.show_at_center( first_selection )
sublime.set_timeout( super_refocus, TIME_AFTER_RESTORE_VIEW )
As a new alternative for file_regex
, I implemented the full_regex
setting which is manipulated by the replaceby
list of lists. For example a valid replaceby
and file full_regex
settings:
{
"cmd": [ "python", "main.py" ],
"full_regex": "^\\d\\d:\\d\\d:\\d\\d:\\d\\d\\d.\\d\\d\\d\\d\\d\\d \\d.\\d\\de-\\d\\d - (?P<file>\\w+(?:\\.\\w+(?!\\.\\w+:))*)[^:]+:(?P<line>\\d+) - (?P<message>.*)$",
"result_dir": "$project_path/source",
"replaceby": [ [ "\\.", "\\\\" ], [ "(.*)", "\\1.py" ] ],
...
}
With these settings, after matching the full_regex
expression, it performs the following algorithm:
value = view.substr( view.full_line( view_selections[0] ) )
replacements = view.settings().get('result_replaceby', {})
for items in replacements:
source = items[0]
replacement = items[1]
value = re.sub( source, replacement, value)
return value
As example match:
16:14:42:849.032402 1.22e-04 - grammar.grammar.<module>:58 - Importing grammar.grammar
- The capture groups are 1)
grammar.grammar
, 2)58
and 3)Importing grammar.grammar
- The first replacement
[ "\\.", "\\\\" ]
fromreplaceby
will do 1)grammar.grammar
becomegrammar/grammar
and the second replacement[ "(.*)", "\\1.py" ]
will dogrammar/grammar
becomegrammar/grammar.py