Godot 4 源码分析 - 代码自动补全流程

Godot4中的代码自动补全功能通过CodeTextEditor组件实现,利用时钟定时器_code_complete_timer触发。当文本改变时,开始计时,若在设定时间内无中断,则调用_code_complete_timer_timeout请求代码补全。在GDScript中,完成代码补全涉及语法解析、语义分析和候选选项处理。该机制提升了开发效率。

使用Godot 4过程中,有一点比较吸引我:代码自动补全

用RAD开发时,代码自动补全功能一直被吐槽,主要是速度慢

但我看Godot 4中的Script编写过程中,代码补全很快,这个可以研究一下。

研究代码可找到,代码补全触发过程

1) CodeTextEditor中创建时钟code_complete_timer,其timeout超时信号绑定_code_complete_timer_timeout函数。超时量缺省值为0.3,单位应该是秒。

	code_complete_timer = memnew(Timer);
	add_child(code_complete_timer);
	code_complete_timer->set_one_shot(true);
	code_complete_timer->set_wait_time(EDITOR_GET("text_editor/completion/code_complete_delay"));
    ...
    code_complete_timer->connect("timeout", callable_mp(this, &CodeTextEditor::_code_complete_timer_timeout));

2) 编辑器中文本变化时,触发CodeTextEditor::_text_changed,时钟code_complete_timer开始计时。

void CodeTextEditor::_text_changed() {
	if (text_editor->is_insert_text_operation()) {
		code_complete_timer_line = text_editor->get_caret_line();
		code_complete_timer->start();
	}

	idle->start();

	if (find_replace_bar) {
		find_replace_bar->needs_to_count_results = true;
	}
}

而_line_col_changed函数(绑定caret_changed事件)会停止时钟。

void CodeTextEditor::_line_col_changed() {
	if (!code_complete_timer->is_stopped() && code_complete_timer_line != text_editor->get_caret_line()) {
		code_complete_timer->stop();
	}

	String line = text_editor->get_line(text_editor->get_caret_line());

	int positional_column = 0;
	for (int i = 0; i < text_editor->get_caret_column(); i++) {
		if (line[i] == '\t') {
			positional_column += text_editor->get_indent_size(); //tab size
		} else {
			positional_column += 1;
		}
	}

	StringBuilder sb;
	sb.append(itos(text_editor->get_caret_line() + 1).lpad(4));
	sb.append(" : ");
	sb.append(itos(positional_column + 1).lpad(3));

	line_and_col_txt->set_text(sb.as_string());

	if (find_replace_bar) {
		if (!find_replace_bar->line_col_changed_for_result) {
			find_replace_bar->needs_to_count_results = true;
		}

		find_replace_bar->line_col_changed_for_result = false;
	}
}

3)时钟启动后在超时期内未被中止,则会触发timeout信号,调用_code_complete_timer_timeout

void CodeTextEditor::_code_complete_timer_timeout() {
	if (!is_visible_in_tree()) {
		return;
	}
	text_editor->request_code_completion();
}

在CodeEdit::request_code_completion函数中发出信号code_completion_requested

void CodeEdit::request_code_completion(bool p_force) {
	if (GDVIRTUAL_CALL(_request_code_completion, p_force)) {
		return;
	}

	/* Don't re-query if all existing options are quoted types, eg path, signal. */
	bool ignored = code_completion_active && !code_completion_options.is_empty();
	if (ignored) {
		ScriptLanguage::CodeCompletionKind kind = ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT;
		const ScriptLanguage::CodeCompletionOption *previous_option = nullptr;
		for (int i = 0; i < code_completion_options.size(); i++) {
			const ScriptLanguage::CodeCompletionOption &current_option = code_completion_options[i];
			if (!previous_option) {
				previous_option = &current_option;
				kind = current_option.kind;
			}
			if (previous_option->kind != current_option.kind) {
				ignored = false;
				break;
			}
		}
		ignored = ignored && (kind == ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH || kind == ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH || kind == ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL);
	}

	if (ignored) {
		return;
	}

	if (p_force) {
		emit_signal(SNAME("code_completion_requested"));
		return;
	}

	String line = get_line(get_caret_line());
	int ofs = CLAMP(get_caret_column(), 0, line.length());

	if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || !is_symbol(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) {
		emit_signal(SNAME("code_completion_requested"));
	} else if (ofs > 1 && line[ofs - 1] == ' ' && code_com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值