Merge branch 'blender-v4.0-release'
This commit is contained in:
commit
ed7b7c2cde
|
@ -0,0 +1,53 @@
|
|||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
__all__ = (
|
||||
"open_external_editor"
|
||||
)
|
||||
|
||||
|
||||
def open_external_editor(filepath, line, column, /):
|
||||
# Internal Python implementation for `TEXT_OT_jump_to_file_at_point`.
|
||||
# Returning a non-empty string represents an error, an empty string for success.
|
||||
import shlex
|
||||
import subprocess
|
||||
from string import Template
|
||||
from bpy import context
|
||||
|
||||
text_editor = context.preferences.filepaths.text_editor
|
||||
text_editor_args = context.preferences.filepaths.text_editor_args
|
||||
|
||||
# The caller should check this.
|
||||
assert text_editor
|
||||
|
||||
if not text_editor_args:
|
||||
return (
|
||||
"Provide text editor argument format in File Paths/Applications Preferences, "
|
||||
"see input field tool-tip for more information",
|
||||
)
|
||||
|
||||
if "$filepath" not in text_editor_args:
|
||||
return "Text Editor Args Format must contain $filepath"
|
||||
|
||||
args = [text_editor]
|
||||
template_vars = {
|
||||
"filepath": filepath,
|
||||
"line": line + 1,
|
||||
"column": column + 1,
|
||||
"line0": line,
|
||||
"column0": column,
|
||||
}
|
||||
|
||||
try:
|
||||
args.extend([Template(arg).substitute(**template_vars) for arg in shlex.split(text_editor_args)])
|
||||
except BaseException as ex:
|
||||
return "Exception parsing template: %r" % ex
|
||||
|
||||
try:
|
||||
# With `check=True` if `process.returncode != 0` an exception will be raised.
|
||||
subprocess.run(args, check=True)
|
||||
except BaseException as ex:
|
||||
return "Exception running external editor: %r" % ex
|
||||
|
||||
return ""
|
|
@ -31,7 +31,6 @@ _modules = [
|
|||
"screen_play_rendered_anim",
|
||||
"sequencer",
|
||||
"spreadsheet",
|
||||
"text",
|
||||
"userpref",
|
||||
"uvcalc_follow_active",
|
||||
"uvcalc_lightmap",
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy
|
||||
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 = getattr(getattr(context, "space_data", None), "text", None)
|
||||
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
|
||||
|
||||
# Use the internal text editor.
|
||||
if not text_editor:
|
||||
return bpy.ops.text.jump_to_file_at_point_internal(
|
||||
filepath=self.filepath,
|
||||
line=self.line,
|
||||
column=self.column,
|
||||
)
|
||||
|
||||
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 BaseException 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 BaseException as ex:
|
||||
self.report({'ERROR'}, "Exception running external editor: %r" % ex)
|
||||
return {'CANCELLED'}
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
classes = (
|
||||
TEXT_OT_jump_to_file_at_point,
|
||||
)
|
|
@ -206,7 +206,7 @@ static void text_operatortypes()
|
|||
WM_operatortype_append(TEXT_OT_replace_set_selected);
|
||||
|
||||
WM_operatortype_append(TEXT_OT_start_find);
|
||||
WM_operatortype_append(TEXT_OT_jump_to_file_at_point_internal);
|
||||
WM_operatortype_append(TEXT_OT_jump_to_file_at_point);
|
||||
|
||||
WM_operatortype_append(TEXT_OT_to_3d_object);
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ void TEXT_OT_find(wmOperatorType *ot);
|
|||
void TEXT_OT_find_set_selected(wmOperatorType *ot);
|
||||
void TEXT_OT_replace(wmOperatorType *ot);
|
||||
void TEXT_OT_replace_set_selected(wmOperatorType *ot);
|
||||
void TEXT_OT_jump_to_file_at_point_internal(wmOperatorType *ot);
|
||||
void TEXT_OT_jump_to_file_at_point(wmOperatorType *ot);
|
||||
|
||||
/* text_find = open properties, activate search button */
|
||||
void TEXT_OT_start_find(wmOperatorType *ot);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
|
@ -3987,19 +3988,58 @@ void TEXT_OT_replace_set_selected(wmOperatorType *ot)
|
|||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Jump to File at Point (Internal)
|
||||
*
|
||||
* \note This is the internal implementation, typically `TEXT_OT_jump_to_file_at_point`
|
||||
* should be used because it respects the "External Editor" preference.
|
||||
/** \name Jump to File at Point
|
||||
* \{ */
|
||||
|
||||
static int text_jump_to_file_at_point_internal_exec(bContext *C, wmOperator *op)
|
||||
static bool text_jump_to_file_at_point_external(bContext *C,
|
||||
ReportList *reports,
|
||||
const char *filepath,
|
||||
const int line_index,
|
||||
const int column_index)
|
||||
{
|
||||
char filepath[FILE_MAX];
|
||||
RNA_string_get(op->ptr, "filepath", filepath);
|
||||
const int line = RNA_int_get(op->ptr, "line");
|
||||
const int column = RNA_int_get(op->ptr, "column");
|
||||
bool success = false;
|
||||
#ifdef WITH_PYTHON
|
||||
BPy_RunErrInfo err_info = {};
|
||||
err_info.reports = reports;
|
||||
err_info.report_prefix = "External editor";
|
||||
|
||||
const char *expr_imports[] = {"bl_text_utils", "bl_text_utils.external_editor", "os", nullptr};
|
||||
std::string expr;
|
||||
{
|
||||
std::stringstream expr_stream;
|
||||
expr_stream << "bl_text_utils.external_editor.open_external_editor(os.fsdecode(b'";
|
||||
for (const char *ch = filepath; *ch; ch++) {
|
||||
expr_stream << "\\x" << std::hex << int(*ch);
|
||||
}
|
||||
expr_stream << "'), " << std::dec << line_index << ", " << std::dec << column_index << ")";
|
||||
expr = expr_stream.str();
|
||||
}
|
||||
|
||||
char *expr_result = nullptr;
|
||||
if (BPY_run_string_as_string(C, expr_imports, expr.c_str(), &err_info, &expr_result)) {
|
||||
/* No error. */
|
||||
if (expr_result[0] == '\0') {
|
||||
BKE_reportf(
|
||||
reports, RPT_INFO, "See '%s' in the external editor", BLI_path_basename(filepath));
|
||||
success = true;
|
||||
}
|
||||
else {
|
||||
BKE_report(reports, RPT_ERROR, expr_result);
|
||||
}
|
||||
MEM_freeN(expr_result);
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(C, reports, filepath, line_index, column_index);
|
||||
#endif /* WITH_PYTHON */
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool text_jump_to_file_at_point_internal(bContext *C,
|
||||
ReportList *reports,
|
||||
const char *filepath,
|
||||
const int line_index,
|
||||
const int column_index)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Text *text = nullptr;
|
||||
|
||||
|
@ -4015,34 +4055,77 @@ static int text_jump_to_file_at_point_internal_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
|
||||
if (text == nullptr) {
|
||||
BKE_reportf(op->reports, RPT_WARNING, "File '%s' cannot be opened", filepath);
|
||||
return OPERATOR_CANCELLED;
|
||||
BKE_reportf(reports, RPT_WARNING, "File '%s' cannot be opened", filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
txt_move_to(text, line, column, false);
|
||||
txt_move_to(text, line_index, column_index, false);
|
||||
|
||||
/* naughty!, find text area to set, not good behavior
|
||||
* but since this is a developer tool lets allow it - campbell */
|
||||
if (!ED_text_activate_in_screen(C, text)) {
|
||||
BKE_reportf(op->reports, RPT_INFO, "See '%s' in the text editor", text->id.name + 2);
|
||||
BKE_reportf(reports, RPT_INFO, "See '%s' in the text editor", text->id.name + 2);
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TEXT_OT_jump_to_file_at_point_internal(wmOperatorType *ot)
|
||||
static int text_jump_to_file_at_point_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
PropertyRNA *prop_filepath = RNA_struct_find_property(op->ptr, "filepath");
|
||||
PropertyRNA *prop_line = RNA_struct_find_property(op->ptr, "line");
|
||||
PropertyRNA *prop_column = RNA_struct_find_property(op->ptr, "column");
|
||||
|
||||
if (!RNA_property_is_set(op->ptr, prop_filepath)) {
|
||||
if (const Text *text = CTX_data_edit_text(C)) {
|
||||
if (text->filepath != nullptr) {
|
||||
const TextLine *line = text->curl;
|
||||
const int line_index = BLI_findindex(&text->lines, text->curl);
|
||||
const int column_index = BLI_str_utf8_offset_to_index(line->line, line->len, text->curc);
|
||||
|
||||
RNA_property_string_set(op->ptr, prop_filepath, text->filepath);
|
||||
RNA_property_int_set(op->ptr, prop_line, line_index);
|
||||
RNA_property_int_set(op->ptr, prop_column, column_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char filepath[FILE_MAX];
|
||||
RNA_property_string_get(op->ptr, prop_filepath, filepath);
|
||||
const int line_index = RNA_property_int_get(op->ptr, prop_line);
|
||||
const int column_index = RNA_property_int_get(op->ptr, prop_column);
|
||||
|
||||
if (filepath[0] == '\0') {
|
||||
BKE_report(op->reports, RPT_WARNING, "File path property not set");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bool success;
|
||||
if (U.text_editor[0] != '\0') {
|
||||
success = text_jump_to_file_at_point_external(
|
||||
C, op->reports, filepath, line_index, column_index);
|
||||
}
|
||||
else {
|
||||
success = text_jump_to_file_at_point_internal(
|
||||
C, op->reports, filepath, line_index, column_index);
|
||||
}
|
||||
|
||||
return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void TEXT_OT_jump_to_file_at_point(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Jump to File at Point (Internal)";
|
||||
ot->idname = "TEXT_OT_jump_to_file_at_point_internal";
|
||||
ot->description = "Jump to a file for the internal text editor";
|
||||
ot->name = "Jump to File at Point";
|
||||
ot->idname = "TEXT_OT_jump_to_file_at_point";
|
||||
ot->description = "Jump to a file for the text editor";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = text_jump_to_file_at_point_internal_exec;
|
||||
ot->exec = text_jump_to_file_at_point_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = 0;
|
||||
|
|
Loading…
Reference in New Issue