UI: Add a custom text editor preference
Add a user preference to set up a custom text editor for editing text files with the "Edit Source" action in the UI context menu. - An operator TEXT_OT_jump_to_file_at_point has been added. - A custom editor can be set in the user preferences. - A preset has been included for "Visual Studio Code". - When the editor is not set, use Blender's internal editor. Ref !108299.
This commit is contained in:
@@ -28,6 +28,7 @@ _modules = [
|
||||
"screen_play_rendered_anim",
|
||||
"sequencer",
|
||||
"spreadsheet",
|
||||
"text",
|
||||
"userpref",
|
||||
"uvcalc_follow_active",
|
||||
"uvcalc_lightmap",
|
||||
|
||||
@@ -413,6 +413,24 @@ class AddPresetHairDynamics(AddPresetBase, Operator):
|
||||
]
|
||||
|
||||
|
||||
class AddPresetTextEditor(AddPresetBase, Operator):
|
||||
"""Add or remove a Text Editor Preset"""
|
||||
bl_idname = "text_editor.preset_add"
|
||||
bl_label = "Add Text Editor Preset"
|
||||
preset_menu = "USERPREF_PT_text_editor_presets"
|
||||
|
||||
preset_defines = [
|
||||
"filepaths = bpy.context.preferences.filepaths"
|
||||
]
|
||||
|
||||
preset_values = [
|
||||
"filepaths.text_editor",
|
||||
"filepaths.text_editor_args"
|
||||
]
|
||||
|
||||
preset_subdir = "text_editor"
|
||||
|
||||
|
||||
class AddPresetTrackingCamera(AddPresetBase, Operator):
|
||||
"""Add or remove a Tracking Camera Intrinsics Preset"""
|
||||
bl_idname = "clip.camera_preset_add"
|
||||
@@ -692,6 +710,7 @@ classes = (
|
||||
AddPresetOperator,
|
||||
AddPresetRender,
|
||||
AddPresetCameraSafeAreas,
|
||||
AddPresetTextEditor,
|
||||
AddPresetTrackingCamera,
|
||||
AddPresetTrackingSettings,
|
||||
AddPresetTrackingTrackColor,
|
||||
|
||||
74
scripts/startup/bl_operators/text.py
Normal file
74
scripts/startup/bl_operators/text.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
||||
|
||||
class TEXT_OT_jump_to_file_at_point(Operator):
|
||||
bl_idname = "text.jump_to_file_at_point"
|
||||
bl_label = "Open Text File at point"
|
||||
bl_description = "Edit text file in external text editor"
|
||||
|
||||
filepath: StringProperty(name="filepath")
|
||||
line: IntProperty(name="line")
|
||||
column: IntProperty(name="column")
|
||||
|
||||
def execute(self, context):
|
||||
import shlex
|
||||
import subprocess
|
||||
from string import Template
|
||||
|
||||
if not self.properties.is_property_set("filepath"):
|
||||
text = context.space_data.text
|
||||
if not text:
|
||||
return {'CANCELLED'}
|
||||
self.filepath = text.filepath
|
||||
self.line = text.current_line_index
|
||||
self.column = text.current_character
|
||||
|
||||
text_editor = context.preferences.filepaths.text_editor
|
||||
text_editor_args = context.preferences.filepaths.text_editor_args
|
||||
|
||||
if not text_editor_args:
|
||||
self.report(
|
||||
{'ERROR_INVALID_INPUT'},
|
||||
"Provide text editor argument format in File Paths/Applications Preferences, "
|
||||
"see input field tool-tip for more information.",
|
||||
)
|
||||
return {'CANCELLED'}
|
||||
|
||||
if "$filepath" not in text_editor_args:
|
||||
self.report({'ERROR_INVALID_INPUT'}, "Text Editor Args Format must contain $filepath")
|
||||
return {'CANCELLED'}
|
||||
|
||||
args = [text_editor]
|
||||
template_vars = {
|
||||
"filepath": self.filepath,
|
||||
"line": self.line + 1,
|
||||
"column": self.column + 1,
|
||||
"line0": self.line,
|
||||
"column0": self.column,
|
||||
}
|
||||
|
||||
try:
|
||||
args.extend([Template(arg).substitute(**template_vars) for arg in shlex.split(text_editor_args)])
|
||||
except Exception as ex:
|
||||
self.report({'ERROR'}, "Exception parsing template: %r" % ex)
|
||||
return {'CANCELLED'}
|
||||
|
||||
try:
|
||||
# With `check=True` if `process.returncode != 0` an exception will be raised.
|
||||
subprocess.run(args, check=True)
|
||||
except Exception as ex:
|
||||
self.report({'ERROR'}, "Exception running external editor: %r" % ex)
|
||||
return {'CANCELLED'}
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
classes = (
|
||||
TEXT_OT_jump_to_file_at_point,
|
||||
)
|
||||
@@ -256,7 +256,13 @@ class TEXT_MT_text(Menu):
|
||||
|
||||
if text:
|
||||
layout.separator()
|
||||
layout.operator("text.reload")
|
||||
row = layout.row()
|
||||
row.operator("text.reload")
|
||||
row.enabled = not text.is_in_memory
|
||||
|
||||
row = layout.row()
|
||||
op = row.operator("text.jump_to_file_at_point", text="Edit Externally")
|
||||
row.enabled = (not text.is_in_memory and context.preferences.filepaths.text_editor != "")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("text.save", icon='FILE_TICK')
|
||||
|
||||
@@ -10,6 +10,7 @@ from bpy.app.translations import (
|
||||
pgettext_iface as iface_,
|
||||
pgettext_tip as tip_,
|
||||
)
|
||||
from bl_ui.utils import PresetPanel
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -1399,6 +1400,13 @@ class USERPREF_PT_file_paths_render(FilePathsPanel, Panel):
|
||||
col.prop(paths, "render_cache_directory", text="Render Cache")
|
||||
|
||||
|
||||
class USERPREF_PT_text_editor_presets(PresetPanel, Panel):
|
||||
bl_label = "Text Editor Presets"
|
||||
preset_subdir = "text_editor"
|
||||
preset_operator = "script.execute_preset"
|
||||
preset_add_operator = "text_editor.preset_add"
|
||||
|
||||
|
||||
class USERPREF_PT_file_paths_applications(FilePathsPanel, Panel):
|
||||
bl_label = "Applications"
|
||||
|
||||
@@ -1416,6 +1424,25 @@ class USERPREF_PT_file_paths_applications(FilePathsPanel, Panel):
|
||||
col.prop(paths, "animation_player", text="Player")
|
||||
|
||||
|
||||
class USERPREF_PT_text_editor(FilePathsPanel, Panel):
|
||||
bl_label = "Text Editor"
|
||||
bl_parent_id = "USERPREF_PT_file_paths_applications"
|
||||
|
||||
def draw_header_preset(self, _context):
|
||||
USERPREF_PT_text_editor_presets.draw_panel_header(self.layout)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
paths = context.preferences.filepaths
|
||||
|
||||
col = layout.column()
|
||||
col.prop(paths, "text_editor", text="Program")
|
||||
col.prop(paths, "text_editor_args", text="Arguments")
|
||||
|
||||
|
||||
class USERPREF_PT_file_paths_development(FilePathsPanel, Panel):
|
||||
bl_label = "Development"
|
||||
|
||||
@@ -2510,6 +2537,8 @@ classes = (
|
||||
USERPREF_PT_file_paths_script_directories,
|
||||
USERPREF_PT_file_paths_render,
|
||||
USERPREF_PT_file_paths_applications,
|
||||
USERPREF_PT_text_editor,
|
||||
USERPREF_PT_text_editor_presets,
|
||||
USERPREF_PT_file_paths_development,
|
||||
USERPREF_PT_file_paths_asset_libraries,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user