Browse Source

Merge pull request #12950 from ianb96/code_folding

Code Folding
Rémi Verschelde 8 years ago
parent
commit
d826b043ad

+ 7 - 0
editor/code_editor.cpp

@@ -56,6 +56,7 @@ void GotoLineDialog::ok_pressed() {
 
 	if (get_line() < 1 || get_line() > text_editor->get_line_count())
 		return;
+	text_editor->unfold_line(get_line() - 1);
 	text_editor->cursor_set_line(get_line() - 1);
 	hide();
 }
@@ -139,6 +140,7 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
 
 	if (found) {
 		if (!preserve_cursor) {
+			text_edit->unfold_line(line);
 			text_edit->cursor_set_line(line, false);
 			text_edit->cursor_set_column(col + text.length(), false);
 			text_edit->center_viewport_to_cursor();
@@ -167,6 +169,7 @@ void FindReplaceBar::_replace() {
 	if (result_line != -1 && result_col != -1) {
 		text_edit->begin_complex_operation();
 
+		text_edit->unfold_line(result_line);
 		text_edit->select(result_line, result_col, result_line, result_col + get_search_text().length());
 		text_edit->insert_text_at_cursor(get_replace_text());
 
@@ -214,6 +217,7 @@ void FindReplaceBar::_replace_all() {
 
 		prev_match = Point2i(result_line, result_col + replace_text.length());
 
+		text_edit->unfold_line(result_line);
 		text_edit->select(result_line, result_col, result_line, match_to.y);
 
 		if (selection_enabled && is_selection_only()) {
@@ -751,6 +755,7 @@ bool FindReplaceDialog::_search() {
 
 	if (found) {
 		// print_line("found");
+		text_edit->unfold_line(line);
 		text_edit->cursor_set_line(line);
 		if (is_backwards())
 			text_edit->cursor_set_column(col);
@@ -1093,6 +1098,8 @@ void CodeTextEditor::update_editor_settings() {
 	text_editor->cursor_set_blink_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink"));
 	text_editor->cursor_set_blink_speed(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink_speed"));
 	text_editor->set_draw_breakpoint_gutter(EditorSettings::get_singleton()->get("text_editor/line_numbers/show_breakpoint_gutter"));
+	text_editor->set_hiding_enabled(EditorSettings::get_singleton()->get("text_editor/line_numbers/code_folding"));
+	text_editor->set_draw_fold_gutter(EditorSettings::get_singleton()->get("text_editor/line_numbers/code_folding"));
 	text_editor->cursor_set_block_mode(EditorSettings::get_singleton()->get("text_editor/cursor/block_caret"));
 	text_editor->set_smooth_scroll_enabled(EditorSettings::get_singleton()->get("text_editor/open_scripts/smooth_scrolling"));
 	text_editor->set_v_scroll_speed(EditorSettings::get_singleton()->get("text_editor/open_scripts/v_scroll_speed"));

+ 1 - 0
editor/editor_settings.cpp

@@ -334,6 +334,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 	_initial_set("text_editor/line_numbers/show_line_numbers", true);
 	_initial_set("text_editor/line_numbers/line_numbers_zero_padded", false);
 	_initial_set("text_editor/line_numbers/show_breakpoint_gutter", true);
+	_initial_set("text_editor/line_numbers/code_folding", true);
 	_initial_set("text_editor/line_numbers/show_line_length_guideline", false);
 	_initial_set("text_editor/line_numbers/line_length_guideline_column", 80);
 	hints["text_editor/line_numbers/line_length_guideline_column"] = PropertyInfo(Variant::INT, "text_editor/line_numbers/line_length_guideline_column", PROPERTY_HINT_RANGE, "20, 160, 10");

+ 116 - 67
editor/plugins/script_text_editor.cpp

@@ -518,7 +518,9 @@ void ScriptTextEditor::tag_saved_version() {
 }
 
 void ScriptTextEditor::goto_line(int p_line, bool p_with_error) {
-	code_editor->get_text_edit()->call_deferred("cursor_set_line", p_line);
+	TextEdit *tx = code_editor->get_text_edit();
+	tx->unfold_line(p_line);
+	tx->call_deferred("cursor_set_line", p_line);
 }
 
 void ScriptTextEditor::ensure_focus() {
@@ -712,15 +714,6 @@ void ScriptTextEditor::_breakpoint_toggled(int p_row) {
 	ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_edit()->is_line_set_as_breakpoint(p_row));
 }
 
-static void swap_lines(TextEdit *tx, int line1, int line2) {
-	String tmp = tx->get_line(line1);
-	String tmp2 = tx->get_line(line2);
-	tx->set_line(line2, tmp);
-	tx->set_line(line1, tmp2);
-
-	tx->cursor_set_line(line2);
-}
-
 void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_column) {
 
 	Node *base = get_tree()->get_edited_scene_root();
@@ -799,39 +792,41 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
 
 void ScriptTextEditor::_edit_option(int p_op) {
 
+	TextEdit *tx = code_editor->get_text_edit();
+
 	switch (p_op) {
 		case EDIT_UNDO: {
-			code_editor->get_text_edit()->undo();
-			code_editor->get_text_edit()->call_deferred("grab_focus");
+
+			tx->undo();
+			tx->call_deferred("grab_focus");
 		} break;
 		case EDIT_REDO: {
-			code_editor->get_text_edit()->redo();
-			code_editor->get_text_edit()->call_deferred("grab_focus");
+
+			tx->redo();
+			tx->call_deferred("grab_focus");
 		} break;
 		case EDIT_CUT: {
 
-			code_editor->get_text_edit()->cut();
-			code_editor->get_text_edit()->call_deferred("grab_focus");
+			tx->cut();
+			tx->call_deferred("grab_focus");
 		} break;
 		case EDIT_COPY: {
-			code_editor->get_text_edit()->copy();
-			code_editor->get_text_edit()->call_deferred("grab_focus");
 
+			tx->copy();
+			tx->call_deferred("grab_focus");
 		} break;
 		case EDIT_PASTE: {
-			code_editor->get_text_edit()->paste();
-			code_editor->get_text_edit()->call_deferred("grab_focus");
 
+			tx->paste();
+			tx->call_deferred("grab_focus");
 		} break;
 		case EDIT_SELECT_ALL: {
 
-			code_editor->get_text_edit()->select_all();
-			code_editor->get_text_edit()->call_deferred("grab_focus");
-
+			tx->select_all();
+			tx->call_deferred("grab_focus");
 		} break;
 		case EDIT_MOVE_LINE_UP: {
 
-			TextEdit *tx = code_editor->get_text_edit();
 			Ref<Script> scr = script;
 			if (scr.is_null())
 				return;
@@ -850,6 +845,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
 					if (line_id == 0 || next_id < 0)
 						return;
 
+					tx->unfold_line(line_id);
+					tx->unfold_line(next_id);
+
 					tx->swap_lines(line_id, next_id);
 					tx->cursor_set_line(next_id);
 				}
@@ -863,16 +861,17 @@ void ScriptTextEditor::_edit_option(int p_op) {
 				if (line_id == 0 || next_id < 0)
 					return;
 
+				tx->unfold_line(line_id);
+				tx->unfold_line(next_id);
+
 				tx->swap_lines(line_id, next_id);
 				tx->cursor_set_line(next_id);
 			}
 			tx->end_complex_operation();
 			tx->update();
-
 		} break;
 		case EDIT_MOVE_LINE_DOWN: {
 
-			TextEdit *tx = code_editor->get_text_edit();
 			Ref<Script> scr = get_edited_script();
 			if (scr.is_null())
 				return;
@@ -891,6 +890,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
 					if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count())
 						return;
 
+					tx->unfold_line(line_id);
+					tx->unfold_line(next_id);
+
 					tx->swap_lines(line_id, next_id);
 					tx->cursor_set_line(next_id);
 				}
@@ -904,6 +906,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
 				if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count())
 					return;
 
+				tx->unfold_line(line_id);
+				tx->unfold_line(next_id);
+
 				tx->swap_lines(line_id, next_id);
 				tx->cursor_set_line(next_id);
 			}
@@ -913,7 +918,6 @@ void ScriptTextEditor::_edit_option(int p_op) {
 		} break;
 		case EDIT_INDENT_LEFT: {
 
-			TextEdit *tx = code_editor->get_text_edit();
 			Ref<Script> scr = get_edited_script();
 			if (scr.is_null())
 				return;
@@ -938,11 +942,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
 			tx->end_complex_operation();
 			tx->update();
 			//tx->deselect();
-
 		} break;
 		case EDIT_INDENT_RIGHT: {
 
-			TextEdit *tx = code_editor->get_text_edit();
 			Ref<Script> scr = get_edited_script();
 			if (scr.is_null())
 				return;
@@ -959,11 +961,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
 			tx->end_complex_operation();
 			tx->update();
 			//tx->deselect();
-
 		} break;
 		case EDIT_DELETE_LINE: {
 
-			TextEdit *tx = code_editor->get_text_edit();
 			Ref<Script> scr = get_edited_script();
 			if (scr.is_null())
 				return;
@@ -972,13 +972,12 @@ void ScriptTextEditor::_edit_option(int p_op) {
 			int line = tx->cursor_get_line();
 			tx->set_line(tx->cursor_get_line(), "");
 			tx->backspace_at_cursor();
+			tx->unfold_line(line);
 			tx->cursor_set_line(line);
 			tx->end_complex_operation();
-
 		} break;
 		case EDIT_CLONE_DOWN: {
 
-			TextEdit *tx = code_editor->get_text_edit();
 			Ref<Script> scr = get_edited_script();
 			if (scr.is_null())
 				return;
@@ -997,6 +996,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
 			tx->begin_complex_operation();
 			for (int i = from_line; i <= to_line; i++) {
 
+				tx->unfold_line(i);
 				if (i >= tx->get_line_count() - 1) {
 					tx->set_line(i, tx->get_line(i) + "\n");
 				}
@@ -1012,11 +1012,29 @@ void ScriptTextEditor::_edit_option(int p_op) {
 
 			tx->end_complex_operation();
 			tx->update();
+		} break;
+		case EDIT_FOLD_LINE: {
+
+			tx->fold_line(tx->cursor_get_line());
+			tx->update();
+		} break;
+		case EDIT_UNFOLD_LINE: {
+
+			tx->unfold_line(tx->cursor_get_line());
+			tx->update();
+		} break;
+		case EDIT_FOLD_ALL_LINES: {
+
+			tx->fold_all_lines();
+			tx->update();
+		} break;
+		case EDIT_UNFOLD_ALL_LINES: {
 
+			tx->unhide_all_lines();
+			tx->update();
 		} break;
 		case EDIT_TOGGLE_COMMENT: {
 
-			TextEdit *tx = code_editor->get_text_edit();
 			Ref<Script> scr = get_edited_script();
 			if (scr.is_null())
 				return;
@@ -1065,62 +1083,65 @@ void ScriptTextEditor::_edit_option(int p_op) {
 			tx->end_complex_operation();
 			tx->update();
 			//tx->deselect();
-
 		} break;
 		case EDIT_COMPLETE: {
 
-			code_editor->get_text_edit()->query_code_comple();
-
+			tx->query_code_comple();
 		} break;
 		case EDIT_AUTO_INDENT: {
 
-			TextEdit *te = code_editor->get_text_edit();
-			String text = te->get_text();
+			String text = tx->get_text();
 			Ref<Script> scr = get_edited_script();
 			if (scr.is_null())
 				return;
 
-			te->begin_complex_operation();
+			tx->begin_complex_operation();
 			int begin, end;
-			if (te->is_selection_active()) {
-				begin = te->get_selection_from_line();
-				end = te->get_selection_to_line();
+			if (tx->is_selection_active()) {
+				begin = tx->get_selection_from_line();
+				end = tx->get_selection_to_line();
 				// ignore if the cursor is not past the first column
-				if (te->get_selection_to_column() == 0) {
+				if (tx->get_selection_to_column() == 0) {
 					end--;
 				}
 			} else {
 				begin = 0;
-				end = te->get_line_count() - 1;
+				end = tx->get_line_count() - 1;
 			}
 			scr->get_language()->auto_indent_code(text, begin, end);
 			Vector<String> lines = text.split("\n");
 			for (int i = begin; i <= end; ++i) {
-				te->set_line(i, lines[i]);
+				tx->set_line(i, lines[i]);
 			}
 
-			te->end_complex_operation();
-
+			tx->end_complex_operation();
 		} break;
 		case EDIT_TRIM_TRAILING_WHITESAPCE: {
+
 			trim_trailing_whitespace();
 		} break;
 		case EDIT_CONVERT_INDENT_TO_SPACES: {
+
 			convert_indent_to_spaces();
 		} break;
 		case EDIT_CONVERT_INDENT_TO_TABS: {
+
 			convert_indent_to_tabs();
 		} break;
 		case EDIT_PICK_COLOR: {
+
 			color_panel->popup();
 		} break;
 		case EDIT_TO_UPPERCASE: {
+
 			_convert_case(UPPER);
 		} break;
 		case EDIT_TO_LOWERCASE: {
+
 			_convert_case(LOWER);
 		} break;
 		case EDIT_CAPITALIZE: {
+
 			_convert_case(CAPITALIZE);
 		} break;
 		case SEARCH_FIND: {
@@ -1145,41 +1166,47 @@ void ScriptTextEditor::_edit_option(int p_op) {
 		} break;
 		case SEARCH_GOTO_LINE: {
 
-			goto_line_dialog->popup_find_line(code_editor->get_text_edit());
+			goto_line_dialog->popup_find_line(tx);
 		} break;
 		case DEBUG_TOGGLE_BREAKPOINT: {
-			int line = code_editor->get_text_edit()->cursor_get_line();
-			bool dobreak = !code_editor->get_text_edit()->is_line_set_as_breakpoint(line);
-			code_editor->get_text_edit()->set_line_as_breakpoint(line, dobreak);
+
+			int line = tx->cursor_get_line();
+			bool dobreak = !tx->is_line_set_as_breakpoint(line);
+			tx->set_line_as_breakpoint(line, dobreak);
 			ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(get_edited_script()->get_path(), line + 1, dobreak);
 		} break;
 		case DEBUG_REMOVE_ALL_BREAKPOINTS: {
+
 			List<int> bpoints;
-			code_editor->get_text_edit()->get_breakpoints(&bpoints);
+			tx->get_breakpoints(&bpoints);
 
 			for (List<int>::Element *E = bpoints.front(); E; E = E->next()) {
 				int line = E->get();
-				bool dobreak = !code_editor->get_text_edit()->is_line_set_as_breakpoint(line);
-				code_editor->get_text_edit()->set_line_as_breakpoint(line, dobreak);
+				bool dobreak = !tx->is_line_set_as_breakpoint(line);
+				tx->set_line_as_breakpoint(line, dobreak);
 				ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(get_edited_script()->get_path(), line + 1, dobreak);
 			}
 		}
 		case DEBUG_GOTO_NEXT_BREAKPOINT: {
+
 			List<int> bpoints;
-			code_editor->get_text_edit()->get_breakpoints(&bpoints);
+			tx->get_breakpoints(&bpoints);
 			if (bpoints.size() <= 0) {
 				return;
 			}
 
-			int line = code_editor->get_text_edit()->cursor_get_line();
+			int line = tx->cursor_get_line();
+
 			// wrap around
 			if (line >= bpoints[bpoints.size() - 1]) {
-				code_editor->get_text_edit()->cursor_set_line(bpoints[0]);
+				tx->unfold_line(bpoints[0]);
+				tx->cursor_set_line(bpoints[0]);
 			} else {
 				for (List<int>::Element *E = bpoints.front(); E; E = E->next()) {
 					int bline = E->get();
 					if (bline > line) {
-						code_editor->get_text_edit()->cursor_set_line(bline);
+						tx->unfold_line(bline);
+						tx->cursor_set_line(bline);
 						return;
 					}
 				}
@@ -1187,21 +1214,24 @@ void ScriptTextEditor::_edit_option(int p_op) {
 
 		} break;
 		case DEBUG_GOTO_PREV_BREAKPOINT: {
+
 			List<int> bpoints;
-			code_editor->get_text_edit()->get_breakpoints(&bpoints);
+			tx->get_breakpoints(&bpoints);
 			if (bpoints.size() <= 0) {
 				return;
 			}
 
-			int line = code_editor->get_text_edit()->cursor_get_line();
+			int line = tx->cursor_get_line();
 			// wrap around
 			if (line <= bpoints[0]) {
-				code_editor->get_text_edit()->cursor_set_line(bpoints[bpoints.size() - 1]);
+				tx->unfold_line(bpoints[bpoints.size() - 1]);
+				tx->cursor_set_line(bpoints[bpoints.size() - 1]);
 			} else {
 				for (List<int>::Element *E = bpoints.back(); E; E = E->prev()) {
 					int bline = E->get();
 					if (bline < line) {
-						code_editor->get_text_edit()->cursor_set_line(bline);
+						tx->unfold_line(bline);
+						tx->cursor_set_line(bline);
 						return;
 					}
 				}
@@ -1210,9 +1240,10 @@ void ScriptTextEditor::_edit_option(int p_op) {
 		} break;
 
 		case HELP_CONTEXTUAL: {
-			String text = code_editor->get_text_edit()->get_selection_text();
+
+			String text = tx->get_selection_text();
 			if (text == "")
-				text = code_editor->get_text_edit()->get_word_under_cursor();
+				text = tx->get_word_under_cursor();
 			if (text != "") {
 				emit_signal("request_help_search", text);
 			}
@@ -1398,6 +1429,9 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
 			Vector2 mpos = mb->get_global_position() - tx->get_global_position();
 			bool have_selection = (tx->get_selection_text().length() > 0);
 			bool have_color = (tx->get_word_at_pos(mpos) == "Color");
+			int fold_state = 0;
+			bool can_fold = tx->can_fold(row);
+			bool is_folded = tx->is_folded(row);
 			if (have_color) {
 
 				String line = tx->get_line(row);
@@ -1428,7 +1462,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
 					have_color = false;
 				}
 			}
-			_make_context_menu(have_selection, have_color);
+			_make_context_menu(have_selection, have_color, can_fold, is_folded);
 		}
 	}
 }
@@ -1447,7 +1481,7 @@ void ScriptTextEditor::_color_changed(const Color &p_color) {
 	code_editor->get_text_edit()->set_line(color_line, new_line);
 }
 
-void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color) {
+void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded) {
 
 	context_menu->clear();
 	if (p_selection) {
@@ -1467,6 +1501,13 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color) {
 		context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT);
 		context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
 	}
+	if (p_can_fold) {
+		// can fold
+		context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_line"), EDIT_FOLD_LINE);
+	} else if (p_is_folded) {
+		// can unfold
+		context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_line"), EDIT_UNFOLD_LINE);
+	}
 	if (p_color) {
 		context_menu->add_separator();
 		context_menu->add_item(TTR("Pick Color"), EDIT_PICK_COLOR);
@@ -1530,6 +1571,10 @@ ScriptTextEditor::ScriptTextEditor() {
 	edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE);
 	edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
 	edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN);
+	edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_line"), EDIT_FOLD_LINE);
+	edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_all_lines"), EDIT_FOLD_ALL_LINES);
+	edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_line"), EDIT_UNFOLD_LINE);
+	edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES);
 	edit_menu->get_popup()->add_separator();
 #ifdef OSX_ENABLED
 	edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE);
@@ -1607,6 +1652,10 @@ void ScriptTextEditor::register_editor() {
 	ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), 0);
 	ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KEY_MASK_CMD | KEY_K);
 	ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B);
+	ED_SHORTCUT("script_text_editor/fold_line", TTR("Fold Line"), KEY_MASK_ALT | KEY_LEFT);
+	ED_SHORTCUT("script_text_editor/unfold_line", TTR("Unfold Line"), KEY_MASK_ALT | KEY_RIGHT);
+	ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), 0);
+	ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0);
 #ifdef OSX_ENABLED
 	ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE);
 #else

+ 5 - 1
editor/plugins/script_text_editor.h

@@ -91,6 +91,10 @@ class ScriptTextEditor : public ScriptEditorBase {
 		EDIT_TO_UPPERCASE,
 		EDIT_TO_LOWERCASE,
 		EDIT_CAPITALIZE,
+		EDIT_FOLD_LINE,
+		EDIT_UNFOLD_LINE,
+		EDIT_FOLD_ALL_LINES,
+		EDIT_UNFOLD_ALL_LINES,
 		SEARCH_FIND,
 		SEARCH_FIND_NEXT,
 		SEARCH_FIND_PREV,
@@ -118,7 +122,7 @@ protected:
 	static void _bind_methods();
 
 	void _edit_option(int p_op);
-	void _make_context_menu(bool p_selection, bool p_color);
+	void _make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded);
 	void _text_edit_gui_input(const Ref<InputEvent> &ev);
 	void _color_changed(const Color &p_color);
 

File diff suppressed because it is too large
+ 512 - 72
scene/gui/text_edit.cpp


+ 38 - 1
scene/gui/text_edit.h

@@ -73,6 +73,9 @@ class TextEdit : public Control {
 	struct Cache {
 
 		Ref<Texture> tab_icon;
+		Ref<Texture> can_fold_icon;
+		Ref<Texture> folded_icon;
+		Ref<Texture> folded_eol_icon;
 		Ref<StyleBox> style_normal;
 		Ref<StyleBox> style_focus;
 		Ref<Font> font;
@@ -105,6 +108,7 @@ class TextEdit : public Control {
 		int line_spacing;
 		int line_number_w;
 		int breakpoint_gutter_width;
+		int fold_gutter_width;
 		Size2 size;
 	} cache;
 
@@ -136,6 +140,7 @@ class TextEdit : public Control {
 			int width_cache : 24;
 			bool marked : 1;
 			bool breakpoint : 1;
+			bool hidden : 1;
 			Map<int, ColorRegionInfo> region_info;
 			String data;
 		};
@@ -160,6 +165,8 @@ class TextEdit : public Control {
 		bool is_marked(int p_line) const { return text[p_line].marked; }
 		void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; }
 		bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
+		void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
+		bool is_hidden(int p_line) const { return text[p_line].hidden; }
 		void insert(int p_at, const String &p_text);
 		void remove(int p_at);
 		int size() const { return text.size(); }
@@ -251,6 +258,9 @@ class TextEdit : public Control {
 	int line_length_guideline_col;
 	bool draw_breakpoint_gutter;
 	int breakpoint_gutter_width;
+	bool draw_fold_gutter;
+	int fold_gutter_width;
+	bool hiding_enabled;
 
 	bool highlight_all_occurrences;
 	bool scroll_past_end_of_file_enabled;
@@ -293,9 +303,14 @@ class TextEdit : public Control {
 	int search_result_line;
 	int search_result_col;
 
+	double line_scroll_pos;
+
 	bool context_menu_enabled;
 
 	int get_visible_rows() const;
+	int get_total_unhidden_rows() const;
+	double get_line_scroll_pos(bool p_recalculate = false) const;
+	void update_line_scroll_pos();
 
 	int get_char_count();
 
@@ -303,6 +318,7 @@ class TextEdit : public Control {
 	int get_column_x_offset(int p_char, String p_str);
 
 	void adjust_viewport_to_cursor();
+	double get_scroll_line_diff() const;
 	void _scroll_moved(double);
 	void _update_scrollbars();
 	void _v_scroll_input();
@@ -405,6 +421,18 @@ public:
 	void set_line_as_breakpoint(int p_line, bool p_breakpoint);
 	bool is_line_set_as_breakpoint(int p_line) const;
 	void get_breakpoints(List<int> *p_breakpoints) const;
+
+	void set_line_as_hidden(int p_line, bool p_hidden);
+	bool is_line_hidden(int p_line) const;
+	void fold_all_lines();
+	void unhide_all_lines();
+	int num_lines_from(int p_line_from, int unhidden_amount) const;
+	int get_whitespace_level(int p_line) const;
+	bool can_fold(int p_line) const;
+	bool is_folded(int p_line) const;
+	void fold_line(int p_line);
+	void unfold_line(int p_line);
+
 	String get_text();
 	String get_line(int line) const;
 	void set_line(int line, String new_text);
@@ -433,7 +461,7 @@ public:
 	void center_viewport_to_cursor();
 
 	void cursor_set_column(int p_col, bool p_adjust_viewport = true);
-	void cursor_set_line(int p_row, bool p_adjust_viewport = true);
+	void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true);
 
 	int cursor_get_column() const;
 	int cursor_get_line() const;
@@ -538,6 +566,15 @@ public:
 	void set_breakpoint_gutter_width(int p_gutter_width);
 	int get_breakpoint_gutter_width() const;
 
+	void set_draw_fold_gutter(bool p_draw);
+	bool is_drawing_fold_gutter() const;
+
+	void set_fold_gutter_width(int p_gutter_width);
+	int get_fold_gutter_width() const;
+
+	void set_hiding_enabled(int p_enabled);
+	int is_hiding_enabled() const;
+
 	void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata);
 
 	void set_completion(bool p_enabled, const Vector<String> &p_prefixes);

Some files were not shown because too many files changed in this diff