Quellcode durchsuchen

GDScript: Remove `leftmost_column` and `rightmost_column` fields

Danil Alexeev vor 2 Monaten
Ursprung
Commit
7aafa6ef18

+ 0 - 1
core/object/script_language.h

@@ -214,7 +214,6 @@ public:
 	/* EDITOR FUNCTIONS */
 	struct Warning {
 		int start_line = -1, end_line = -1;
-		int leftmost_column = -1, rightmost_column = -1;
 		int code;
 		String string_code;
 		String message;

+ 0 - 4
core/object/script_language_extension.h

@@ -347,8 +347,6 @@ public:
 				Dictionary warn = warning;
 				ERR_CONTINUE(!warn.has("start_line"));
 				ERR_CONTINUE(!warn.has("end_line"));
-				ERR_CONTINUE(!warn.has("leftmost_column"));
-				ERR_CONTINUE(!warn.has("rightmost_column"));
 				ERR_CONTINUE(!warn.has("code"));
 				ERR_CONTINUE(!warn.has("string_code"));
 				ERR_CONTINUE(!warn.has("message"));
@@ -356,8 +354,6 @@ public:
 				Warning swarn;
 				swarn.start_line = warn["start_line"];
 				swarn.end_line = warn["end_line"];
-				swarn.leftmost_column = warn["leftmost_column"];
-				swarn.rightmost_column = warn["rightmost_column"];
 				swarn.code = warn["code"];
 				swarn.string_code = warn["string_code"];
 				swarn.message = warn["message"];

+ 1 - 1
modules/gdscript/gdscript_compiler.cpp

@@ -79,7 +79,7 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N
 	error = p_error;
 	if (p_node) {
 		err_line = p_node->start_line;
-		err_column = p_node->leftmost_column;
+		err_column = p_node->start_column;
 	} else {
 		err_line = 0;
 		err_column = 0;

+ 0 - 2
modules/gdscript/gdscript_editor.cpp

@@ -144,8 +144,6 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
 			ScriptLanguage::Warning w;
 			w.start_line = warn.start_line;
 			w.end_line = warn.end_line;
-			w.leftmost_column = warn.leftmost_column;
-			w.rightmost_column = warn.rightmost_column;
 			w.code = (int)warn.code;
 			w.string_code = GDScriptWarning::get_name_from_code(warn.code);
 			w.message = warn.get_message();

+ 3 - 15
modules/gdscript/gdscript_parser.cpp

@@ -184,7 +184,7 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
 	if (p_origin == nullptr) {
 		errors.push_back({ p_message, previous.start_line, previous.start_column });
 	} else {
-		errors.push_back({ p_message, p_origin->start_line, p_origin->leftmost_column });
+		errors.push_back({ p_message, p_origin->start_line, p_origin->start_column });
 	}
 }
 
@@ -227,8 +227,6 @@ void GDScriptParser::apply_pending_warnings() {
 		warning.symbols = pw.symbols;
 		warning.start_line = pw.source->start_line;
 		warning.end_line = pw.source->end_line;
-		warning.leftmost_column = pw.source->leftmost_column;
-		warning.rightmost_column = pw.source->rightmost_column;
 
 		if (pw.treated_as_error) {
 			push_error(warning.get_message() + String(" (Warning treated as error.)"), pw.source);
@@ -412,8 +410,6 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
 		nd->start_line = 1;
 		nd->start_column = 0;
 		nd->end_line = 1;
-		nd->leftmost_column = 0;
-		nd->rightmost_column = 0;
 		push_warning(nd, GDScriptWarning::EMPTY_FILE);
 	}
 #endif
@@ -761,8 +757,6 @@ void GDScriptParser::parse_program() {
 
 	head->end_line = current.end_line;
 	head->end_column = current.end_column;
-	head->leftmost_column = MIN(head->leftmost_column, current.leftmost_column);
-	head->rightmost_column = MAX(head->rightmost_column, current.rightmost_column);
 
 	complete_extents(head);
 
@@ -1550,8 +1544,8 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum(bool p_is_abstract, bool p_
 			item.identifier = identifier;
 			item.parent_enum = enum_node;
 			item.line = previous.start_line;
-			item.leftmost_column = previous.leftmost_column;
-			item.rightmost_column = previous.rightmost_column;
+			item.start_column = previous.start_column;
+			item.end_column = previous.end_column;
 
 			if (elements.has(item.identifier->name)) {
 				push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier);
@@ -5463,8 +5457,6 @@ void GDScriptParser::complete_extents(Node *p_node) {
 void GDScriptParser::update_extents(Node *p_node) {
 	p_node->end_line = previous.end_line;
 	p_node->end_column = previous.end_column;
-	p_node->leftmost_column = MIN(p_node->leftmost_column, previous.leftmost_column);
-	p_node->rightmost_column = MAX(p_node->rightmost_column, previous.rightmost_column);
 }
 
 void GDScriptParser::reset_extents(Node *p_node, GDScriptTokenizer::Token p_token) {
@@ -5472,8 +5464,6 @@ void GDScriptParser::reset_extents(Node *p_node, GDScriptTokenizer::Token p_toke
 	p_node->end_line = p_token.end_line;
 	p_node->start_column = p_token.start_column;
 	p_node->end_column = p_token.end_column;
-	p_node->leftmost_column = p_token.leftmost_column;
-	p_node->rightmost_column = p_token.rightmost_column;
 }
 
 void GDScriptParser::reset_extents(Node *p_node, Node *p_from) {
@@ -5484,8 +5474,6 @@ void GDScriptParser::reset_extents(Node *p_node, Node *p_from) {
 	p_node->end_line = p_from->end_line;
 	p_node->start_column = p_from->start_column;
 	p_node->end_column = p_from->end_column;
-	p_node->leftmost_column = p_from->leftmost_column;
-	p_node->rightmost_column = p_from->rightmost_column;
 }
 
 /*---------- PRETTY PRINT FOR DEBUG ----------*/

+ 2 - 12
modules/gdscript/gdscript_parser.h

@@ -339,7 +339,6 @@ public:
 		Type type = NONE;
 		int start_line = 0, end_line = 0;
 		int start_column = 0, end_column = 0;
-		int leftmost_column = 0, rightmost_column = 0;
 		Node *next = nullptr;
 		List<AnnotationNode *> annotations;
 
@@ -536,8 +535,8 @@ public:
 			bool resolved = false;
 			int64_t value = 0;
 			int line = 0;
-			int leftmost_column = 0;
-			int rightmost_column = 0;
+			int start_column = 0;
+			int end_column = 0;
 #ifdef TOOLS_ENABLED
 			MemberDocData doc_data;
 #endif // TOOLS_ENABLED
@@ -1105,7 +1104,6 @@ public:
 
 			int start_line = 0, end_line = 0;
 			int start_column = 0, end_column = 0;
-			int leftmost_column = 0, rightmost_column = 0;
 
 			DataType get_datatype() const;
 			String get_name() const;
@@ -1121,8 +1119,6 @@ public:
 				end_line = p_constant->end_line;
 				start_column = p_constant->start_column;
 				end_column = p_constant->end_column;
-				leftmost_column = p_constant->leftmost_column;
-				rightmost_column = p_constant->rightmost_column;
 			}
 			Local(VariableNode *p_variable, FunctionNode *p_source_function) {
 				type = VARIABLE;
@@ -1134,8 +1130,6 @@ public:
 				end_line = p_variable->end_line;
 				start_column = p_variable->start_column;
 				end_column = p_variable->end_column;
-				leftmost_column = p_variable->leftmost_column;
-				rightmost_column = p_variable->rightmost_column;
 			}
 			Local(ParameterNode *p_parameter, FunctionNode *p_source_function) {
 				type = PARAMETER;
@@ -1147,8 +1141,6 @@ public:
 				end_line = p_parameter->end_line;
 				start_column = p_parameter->start_column;
 				end_column = p_parameter->end_column;
-				leftmost_column = p_parameter->leftmost_column;
-				rightmost_column = p_parameter->rightmost_column;
 			}
 			Local(IdentifierNode *p_identifier, FunctionNode *p_source_function) {
 				type = FOR_VARIABLE;
@@ -1160,8 +1152,6 @@ public:
 				end_line = p_identifier->end_line;
 				start_column = p_identifier->start_column;
 				end_column = p_identifier->end_column;
-				leftmost_column = p_identifier->leftmost_column;
-				rightmost_column = p_identifier->rightmost_column;
 			}
 		};
 		Local empty;

+ 0 - 51
modules/gdscript/gdscript_tokenizer.cpp

@@ -329,9 +329,6 @@ char32_t GDScriptTokenizerText::_advance() {
 	_current++;
 	column++;
 	position++;
-	if (column > rightmost_column) {
-		rightmost_column = column;
-	}
 	if (unlikely(_is_at_end())) {
 		// Add extra newline even if it's not there, to satisfy the parser.
 		newline(true);
@@ -367,8 +364,6 @@ GDScriptTokenizer::Token GDScriptTokenizerText::make_token(Token::Type p_type) {
 	token.end_line = line;
 	token.start_column = start_column;
 	token.end_column = column;
-	token.leftmost_column = leftmost_column;
-	token.rightmost_column = rightmost_column;
 	token.source = String::utf32(Span(_start, _current - _start));
 
 	if (p_type != Token::ERROR && cursor_line > -1) {
@@ -671,8 +666,6 @@ void GDScriptTokenizerText::newline(bool p_make_token) {
 		newline.end_line = line;
 		newline.start_column = column - 1;
 		newline.end_column = column;
-		newline.leftmost_column = newline.start_column;
-		newline.rightmost_column = newline.end_column;
 		pending_newline = true;
 		last_token = newline;
 		last_newline = newline;
@@ -681,7 +674,6 @@ void GDScriptTokenizerText::newline(bool p_make_token) {
 	// Increment line/column counters.
 	line++;
 	column = 1;
-	leftmost_column = 1;
 }
 
 GDScriptTokenizer::Token GDScriptTokenizerText::number() {
@@ -718,9 +710,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() {
 	if (base != 10 && is_underscore(_peek())) { // Disallow `0x_` and `0b_`.
 		Token error = make_error(vformat(R"(Unexpected underscore after "0%c".)", _peek(-1)));
 		error.start_column = column;
-		error.leftmost_column = column;
 		error.end_column = column + 1;
-		error.rightmost_column = column + 1;
 		push_error(error);
 		has_error = true;
 	}
@@ -730,9 +720,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() {
 			if (previous_was_underscore) {
 				Token error = make_error(R"(Multiple underscores cannot be adjacent in a numeric literal.)");
 				error.start_column = column;
-				error.leftmost_column = column;
 				error.end_column = column + 1;
-				error.rightmost_column = column + 1;
 				push_error(error);
 			}
 			previous_was_underscore = true;
@@ -750,25 +738,19 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() {
 		} else if (base == 10) {
 			Token error = make_error("Cannot use a decimal point twice in a number.");
 			error.start_column = column;
-			error.leftmost_column = column;
 			error.end_column = column + 1;
-			error.rightmost_column = column + 1;
 			push_error(error);
 			has_error = true;
 		} else if (base == 16) {
 			Token error = make_error("Cannot use a decimal point in a hexadecimal number.");
 			error.start_column = column;
-			error.leftmost_column = column;
 			error.end_column = column + 1;
-			error.rightmost_column = column + 1;
 			push_error(error);
 			has_error = true;
 		} else {
 			Token error = make_error("Cannot use a decimal point in a binary number.");
 			error.start_column = column;
-			error.leftmost_column = column;
 			error.end_column = column + 1;
-			error.rightmost_column = column + 1;
 			push_error(error);
 			has_error = true;
 		}
@@ -779,9 +761,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() {
 			if (is_underscore(_peek())) { // Disallow `10._`, but allow `10.`.
 				Token error = make_error(R"(Unexpected underscore after decimal point.)");
 				error.start_column = column;
-				error.leftmost_column = column;
 				error.end_column = column + 1;
-				error.rightmost_column = column + 1;
 				push_error(error);
 				has_error = true;
 			}
@@ -791,9 +771,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() {
 					if (previous_was_underscore) {
 						Token error = make_error(R"(Multiple underscores cannot be adjacent in a numeric literal.)");
 						error.start_column = column;
-						error.leftmost_column = column;
 						error.end_column = column + 1;
-						error.rightmost_column = column + 1;
 						push_error(error);
 					}
 					previous_was_underscore = true;
@@ -816,9 +794,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() {
 			if (!is_digit(_peek())) {
 				Token error = make_error(R"(Expected exponent value after "e".)");
 				error.start_column = column;
-				error.leftmost_column = column;
 				error.end_column = column + 1;
-				error.rightmost_column = column + 1;
 				push_error(error);
 			}
 			previous_was_underscore = false;
@@ -827,9 +803,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() {
 					if (previous_was_underscore) {
 						Token error = make_error(R"(Multiple underscores cannot be adjacent in a numeric literal.)");
 						error.start_column = column;
-						error.leftmost_column = column;
 						error.end_column = column + 1;
-						error.rightmost_column = column + 1;
 						push_error(error);
 					}
 					previous_was_underscore = true;
@@ -845,9 +819,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() {
 		// No digits in hex or bin literal.
 		Token error = make_error(vformat(R"(Expected %s digit after "0%c".)", (base == 16 ? "hexadecimal" : "binary"), (base == 16 ? 'x' : 'b')));
 		error.start_column = column;
-		error.leftmost_column = column;
 		error.end_column = column + 1;
-		error.rightmost_column = column + 1;
 		return error;
 	}
 
@@ -855,9 +827,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::number() {
 	if (!has_error && has_decimal && _peek() == '.' && _peek(1) != '.') {
 		Token error = make_error("Cannot use a decimal point twice in a number.");
 		error.start_column = column;
-		error.leftmost_column = column;
 		error.end_column = column + 1;
-		error.rightmost_column = column + 1;
 		push_error(error);
 		has_error = true;
 	} else if (is_unicode_identifier_start(_peek()) || is_unicode_identifier_continue(_peek())) {
@@ -936,9 +906,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::string() {
 				error = make_error("Invisible text direction control character present in the string, escape it (\"\\u" + String::num_int64(ch, 16) + "\") to avoid confusion.");
 			}
 			error.start_column = column;
-			error.leftmost_column = error.start_column;
 			error.end_column = column + 1;
-			error.rightmost_column = error.end_column;
 			push_error(error);
 		}
 
@@ -1032,9 +1000,7 @@ GDScriptTokenizer::Token GDScriptTokenizerText::string() {
 								// Make error, but keep parsing the string.
 								Token error = make_error("Invalid hexadecimal digit in unicode escape sequence.");
 								error.start_column = column;
-								error.leftmost_column = error.start_column;
 								error.end_column = column + 1;
-								error.rightmost_column = error.end_column;
 								push_error(error);
 								valid_escape = false;
 								break;
@@ -1063,7 +1029,6 @@ GDScriptTokenizer::Token GDScriptTokenizerText::string() {
 					default:
 						Token error = make_error("Invalid escape in string.");
 						error.start_column = column - 2;
-						error.leftmost_column = error.start_column;
 						push_error(error);
 						valid_escape = false;
 						break;
@@ -1078,7 +1043,6 @@ GDScriptTokenizer::Token GDScriptTokenizerText::string() {
 						} else {
 							Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate.");
 							error.start_column = column - 2;
-							error.leftmost_column = error.start_column;
 							push_error(error);
 							valid_escape = false;
 							prev = 0;
@@ -1087,7 +1051,6 @@ GDScriptTokenizer::Token GDScriptTokenizerText::string() {
 						if (prev == 0) {
 							Token error = make_error("Invalid UTF-16 sequence in string, unpaired trail surrogate.");
 							error.start_column = column - 2;
-							error.leftmost_column = error.start_column;
 							push_error(error);
 							valid_escape = false;
 						} else {
@@ -1098,7 +1061,6 @@ GDScriptTokenizer::Token GDScriptTokenizerText::string() {
 					if (prev != 0) {
 						Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate.");
 						error.start_column = prev_pos;
-						error.leftmost_column = error.start_column;
 						push_error(error);
 						prev = 0;
 					}
@@ -1112,7 +1074,6 @@ GDScriptTokenizer::Token GDScriptTokenizerText::string() {
 			if (prev != 0) {
 				Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
 				error.start_column = prev_pos;
-				error.leftmost_column = error.start_column;
 				push_error(error);
 				prev = 0;
 			}
@@ -1135,7 +1096,6 @@ GDScriptTokenizer::Token GDScriptTokenizerText::string() {
 			if (prev != 0) {
 				Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
 				error.start_column = prev_pos;
-				error.leftmost_column = error.start_column;
 				push_error(error);
 				prev = 0;
 			}
@@ -1149,7 +1109,6 @@ GDScriptTokenizer::Token GDScriptTokenizerText::string() {
 	if (prev != 0) {
 		Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
 		error.start_column = prev_pos;
-		error.leftmost_column = error.start_column;
 		push_error(error);
 		prev = 0;
 	}
@@ -1273,8 +1232,6 @@ void GDScriptTokenizerText::check_indent() {
 			Token error = make_error("Mixed use of tabs and spaces for indentation.");
 			error.start_line = line;
 			error.start_column = 1;
-			error.leftmost_column = 1;
-			error.rightmost_column = column;
 			push_error(error);
 		}
 
@@ -1293,8 +1250,6 @@ void GDScriptTokenizerText::check_indent() {
 					_get_indent_char_name(current_indent_char), _get_indent_char_name(indent_char)));
 			error.start_line = line;
 			error.start_column = 1;
-			error.leftmost_column = 1;
-			error.rightmost_column = column;
 			push_error(error);
 		}
 
@@ -1328,9 +1283,7 @@ void GDScriptTokenizerText::check_indent() {
 				Token error = make_error("Unindent doesn't match the previous indentation level.");
 				error.start_line = line;
 				error.start_column = 1;
-				error.leftmost_column = 1;
 				error.end_column = column + 1;
-				error.rightmost_column = column + 1;
 				push_error(error);
 				// Still, we'll be lenient and keep going, so keep this level in the stack.
 				indent_stack.push_back(indent_count);
@@ -1431,14 +1384,11 @@ GDScriptTokenizer::Token GDScriptTokenizerText::scan() {
 	_start = _current;
 	start_line = line;
 	start_column = column;
-	leftmost_column = column;
-	rightmost_column = column;
 
 	if (pending_indents != 0) {
 		// Adjust position for indent.
 		_start -= start_column - 1;
 		start_column = 1;
-		leftmost_column = 1;
 		if (pending_indents > 0) {
 			// Indents.
 			pending_indents--;
@@ -1448,7 +1398,6 @@ GDScriptTokenizer::Token GDScriptTokenizerText::scan() {
 			pending_indents++;
 			Token dedent = make_token(Token::DEDENT);
 			dedent.end_column += 1;
-			dedent.rightmost_column += 1;
 			return dedent;
 		}
 	}

+ 0 - 2
modules/gdscript/gdscript_tokenizer.h

@@ -165,7 +165,6 @@ public:
 		Type type = EMPTY;
 		Variant literal;
 		int start_line = 0, end_line = 0, start_column = 0, end_column = 0;
-		int leftmost_column = 0, rightmost_column = 0; // Column span for multiline tokens.
 		int cursor_position = -1;
 		CursorPlace cursor_place = CURSOR_NONE;
 		String source;
@@ -226,7 +225,6 @@ class GDScriptTokenizerText : public GDScriptTokenizer {
 	// Keep track of multichar tokens.
 	const char32_t *_start = nullptr;
 	int start_line = 0, start_column = 0;
-	int leftmost_column = 0, rightmost_column = 0;
 
 	// Info cache.
 	bool line_continuation = false; // Whether this line is a continuation of the previous, like when using '\'.

+ 0 - 1
modules/gdscript/gdscript_warning.h

@@ -157,7 +157,6 @@ public:
 
 	Code code = WARNING_MAX;
 	int start_line = -1, end_line = -1;
-	int leftmost_column = -1, rightmost_column = -1;
 	Vector<String> symbols;
 
 	String get_name() const;

+ 4 - 4
modules/gdscript/language_server/gdscript_extend_parser.cpp

@@ -403,8 +403,8 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
 				symbol.name = m.enum_value.identifier->name;
 				symbol.kind = LSP::SymbolKind::EnumMember;
 				symbol.deprecated = false;
-				symbol.range.start = GodotPosition(m.enum_value.line, m.enum_value.leftmost_column).to_lsp(lines);
-				symbol.range.end = GodotPosition(m.enum_value.line, m.enum_value.rightmost_column).to_lsp(lines);
+				symbol.range.start = GodotPosition(m.enum_value.line, m.enum_value.start_column).to_lsp(lines);
+				symbol.range.end = GodotPosition(m.enum_value.line, m.enum_value.end_column).to_lsp(lines);
 				symbol.selectionRange = range_of_node(m.enum_value.identifier);
 				symbol.documentation = m.enum_value.doc_data.description;
 				symbol.uri = uri;
@@ -439,8 +439,8 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
 					child.name = value.identifier->name;
 					child.kind = LSP::SymbolKind::EnumMember;
 					child.deprecated = false;
-					child.range.start = GodotPosition(value.line, value.leftmost_column).to_lsp(lines);
-					child.range.end = GodotPosition(value.line, value.rightmost_column).to_lsp(lines);
+					child.range.start = GodotPosition(value.line, value.start_column).to_lsp(lines);
+					child.range.end = GodotPosition(value.line, value.end_column).to_lsp(lines);
 					child.selectionRange = range_of_node(value.identifier);
 					child.documentation = value.doc_data.description;
 					child.uri = uri;

+ 27 - 11
modules/gdscript/tests/test_gdscript.cpp

@@ -64,25 +64,41 @@ static void test_tokenizer(const String &p_code, const Vector<String> &p_lines)
 		StringBuilder token;
 		token += " --> "; // Padding for line number.
 
+		if (current.start_line != current.end_line) {
+			// Print "vvvvvv" to point at the token.
+			StringBuilder pointer;
+			pointer += "     "; // Padding for line number.
+
+			int line_width = 0;
+			if (current.start_line - 1 >= 0 && current.start_line - 1 < p_lines.size()) {
+				line_width = p_lines[current.start_line - 1].replace("\t", tab).length();
+			}
+
+			const int offset = MAX(0, current.start_column - 1);
+			const int width = MAX(0, line_width - current.start_column + 1);
+			pointer += String::chr(' ').repeat(offset) + String::chr('v').repeat(width);
+
+			print_line(pointer.as_string());
+		}
+
 		for (int l = current.start_line; l <= current.end_line && l <= p_lines.size(); l++) {
 			print_line(vformat("%04d %s", l, p_lines[l - 1]).replace("\t", tab));
 		}
 
 		{
-			// Print carets to point at the token.
+			// Print "^^^^^^" to point at the token.
 			StringBuilder pointer;
 			pointer += "     "; // Padding for line number.
-			int rightmost_column = current.rightmost_column;
-			if (current.end_line > current.start_line) {
-				rightmost_column--; // Don't point to the newline as a column.
-			}
-			for (int col = 1; col < rightmost_column; col++) {
-				if (col < current.leftmost_column) {
-					pointer += " ";
-				} else {
-					pointer += "^";
-				}
+
+			if (current.start_line == current.end_line) {
+				const int offset = MAX(0, current.start_column - 1);
+				const int width = MAX(0, current.end_column - current.start_column);
+				pointer += String::chr(' ').repeat(offset) + String::chr('^').repeat(width);
+			} else {
+				const int width = MAX(0, current.end_column - 1);
+				pointer += String::chr('^').repeat(width);
 			}
+
 			print_line(pointer.as_string());
 		}