diff --git a/.gitmodules b/.gitmodules index c582187c..2c096761 100644 --- a/.gitmodules +++ b/.gitmodules @@ -266,6 +266,9 @@ [submodule "assets/syntaxes/02_Extra/Idris2"] path = assets/syntaxes/02_Extra/Idris2 url = https://github.com/buzden/sublime-syntax-idris2 +[submodule "assets/syntaxes/02_Extra/GDScript-sublime"] + path = assets/syntaxes/02_Extra/GDScript-sublime + url = https://github.com/beefsack/GDScript-sublime [submodule "assets/syntaxes/02_Extra/sublime-odin"] path = assets/syntaxes/02_Extra/sublime-odin url = https://github.com/odin-lang/sublime-odin diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a8409fb..5f679045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - Prevent `--list-themes` from outputting default theme info to stdout when it is piped, see #3189 (@einfachIrgendwer0815) - Rename some submodules to fix Dependabot submodule updates, see issue #3198 and PR #3201 (@victor-gp) - Make highlight tests fail when new syntaxes don't have fixtures PR #3255 (@dan-hipschman) +- Fix crash for multibyte characters in file path, see issue #3230 and PR #3245 (@HSM95) +- Add missing mappings for various bash/zsh files, see PR #3262 (@AdamGaskins) ## Other @@ -26,6 +28,7 @@ - Map `ndjson` extension to JSON syntax, see #3209 (@keith-hall) - Map files with `csproj`, `vbproj`, `props` and `targets` extensions to XML syntax, see #3213 (@keith-hall) - Add debsources syntax to highlight `/etc/apt/sources.list` files, see #3215 (@keith-hall) +- Add syntax definition and test file for GDScript highlighting, see #3236 (@chetanjangir0) - Add syntax test file for Odin highlighting, see #3241 (@chetanjangir0) ## Themes diff --git a/Cargo.lock b/Cargo.lock index ddd32b4e..5bb62706 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -153,6 +153,7 @@ dependencies = [ "terminal-colorsaurus", "thiserror 2.0.11", "toml", + "unicode-segmentation", "unicode-width 0.1.14", "wait-timeout", "walkdir", @@ -1678,6 +1679,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.1.14" diff --git a/Cargo.toml b/Cargo.toml index a52a3f99..2ee1b872 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ bytesize = { version = "1.3.0" } encoding_rs = "0.8.35" execute = { version = "0.2.13", optional = true } terminal-colorsaurus = "0.4" +unicode-segmentation = "1.12.0" [dependencies.git2] version = "0.20" diff --git a/assets/syntaxes/02_Extra/Apache b/assets/syntaxes/02_Extra/Apache index 163bc03a..cf6cefc5 160000 --- a/assets/syntaxes/02_Extra/Apache +++ b/assets/syntaxes/02_Extra/Apache @@ -1 +1 @@ -Subproject commit 163bc03ae8998a237dfb4be353d0aea198ea17f5 +Subproject commit cf6cefc51ebb46b1b54906433edbae0fe9c71cad diff --git a/assets/syntaxes/02_Extra/GDScript-sublime b/assets/syntaxes/02_Extra/GDScript-sublime new file mode 160000 index 00000000..96f5dcf2 --- /dev/null +++ b/assets/syntaxes/02_Extra/GDScript-sublime @@ -0,0 +1 @@ +Subproject commit 96f5dcf29728aa987123321e2544330eed991a3e diff --git a/assets/syntaxes/02_Extra/PowerShell b/assets/syntaxes/02_Extra/PowerShell index c0372a1d..a08b55bf 160000 --- a/assets/syntaxes/02_Extra/PowerShell +++ b/assets/syntaxes/02_Extra/PowerShell @@ -1 +1 @@ -Subproject commit c0372a1d2df136ca6b3d1a9f7b85031dedf117f9 +Subproject commit a08b55bf1146c210f58e844be53c2aa78fd5e610 diff --git a/assets/themes/zenburn b/assets/themes/zenburn index 86d4ee7a..9c588ebc 160000 --- a/assets/themes/zenburn +++ b/assets/themes/zenburn @@ -1 +1 @@ -Subproject commit 86d4ee7a1f884851a1d21d66249687f527fced32 +Subproject commit 9c588ebc11c3e6487b081bd54528af08baa8a09a diff --git a/src/printer.rs b/src/printer.rs index c62be3f6..dd2323cf 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -15,6 +15,7 @@ use content_inspector::ContentType; use encoding_rs::{UTF_16BE, UTF_16LE}; +use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthChar; use crate::assets::{HighlightingAssets, SyntaxReferenceInSet}; @@ -388,14 +389,18 @@ impl<'a> InteractivePrinter<'a> { handle: &mut OutputHandle, content: &str, ) -> Result<()> { - let mut content = content; let content_width = self.config.term_width - self.get_header_component_indent_length(); - while content.len() > content_width { - let (content_line, remaining) = content.split_at(content_width); - self.print_header_component_with_indent(handle, content_line)?; - content = remaining; + if content.chars().count() <= content_width { + return self.print_header_component_with_indent(handle, content); } - self.print_header_component_with_indent(handle, content) + + let mut content_graphemes: Vec<&str> = content.graphemes(true).collect(); + while content_graphemes.len() > content_width { + let (content_line, remaining) = content_graphemes.split_at(content_width); + self.print_header_component_with_indent(handle, content_line.join("").as_str())?; + content_graphemes = remaining.iter().cloned().collect(); + } + self.print_header_component_with_indent(handle, content_graphemes.join("").as_str()) } fn highlight_regions_for_line<'b>( diff --git a/src/syntax_mapping/builtins/unix-family/50-shell.toml b/src/syntax_mapping/builtins/unix-family/50-shell.toml index d015ca81..cd59a84e 100644 --- a/src/syntax_mapping/builtins/unix-family/50-shell.toml +++ b/src/syntax_mapping/builtins/unix-family/50-shell.toml @@ -2,4 +2,24 @@ "Bourne Again Shell (bash)" = [ # used by lots of shells "/etc/profile", + + "bashrc", + "*.bashrc", + "bash_profile", + "*.bash_profile", + "bash_login", + "*.bash_login", + "bash_logout", + "*.bash_logout", + + "zshrc", + "*.zshrc", + "zprofile", + "*.zprofile", + "zlogin", + "*.zlogin", + "zlogout", + "*.zlogout", + "zshenv", + "*.zshenv" ] diff --git a/tests/examples/test.A—B가 b/tests/examples/test.A—B가 new file mode 100644 index 00000000..e69de29b diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 3aafb17c..97dbd550 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1600,6 +1600,17 @@ oken .stderr(""); } +#[test] +fn header_narrow_terminal_with_multibyte_chars() { + bat() + .arg("--terminal-width=30") + .arg("--decorations=always") + .arg("test.A—B가") + .assert() + .success() + .stderr(""); +} + #[test] #[cfg(feature = "git")] // Expected output assumes git is enabled fn header_default() { diff --git a/tests/syntax-tests/highlighted/GDScript/test.gd b/tests/syntax-tests/highlighted/GDScript/test.gd new file mode 100644 index 00000000..4ea498cf --- /dev/null +++ b/tests/syntax-tests/highlighted/GDScript/test.gd @@ -0,0 +1,71 @@ +extends Node + +signal custom_signal(param) + +const PI = 3.14159 + +var untyped_var = "Hello, World!" +var typed_int: int = 42 +var typed_float: float = 3.14 +var typed_string: String = "GDScript Test" +var typed_array: Array = [1, 2, 3, 4] +var typed_dict: Dictionary = {"key": "value", "number": 100} + +onready var label = $Label + +func say_hello() -> void: + print("Hello from GDScript!") + +func add_numbers(a: int, b: int = 10) -> int: + return a + b + +func process_value(value: int) -> String: + if value < 0: + return "Negative" + elif value == 0: + return "Zero" + else: + return "Positive" + +func sum_array(arr: Array) -> int: + var total: int = 0 + for num in arr: + total += num + return total + +func describe_number(num: int) -> String: + match num: + 0: + return "Zero" + 1, 2, 3: + return "Small number" + _: + return "Large number" + +func long_description() -> String: + return """This is a test file for GDScript. +It covers variables, functions, control structures, loops, signals, inner classes, +multiline strings, arrays, and dictionaries.""" + +class InnerExample: + var inner_value: int = 99 + func show_value() -> void: + print("Inner value is:", inner_value) + +func test_inner_class() -> void: + var inner = InnerExample.new() + inner.show_value() + +func trigger_signal() -> void: + emit_signal("custom_signal", "TestParam") + +func _ready() -> void: + say_hello() + var result_add = add_numbers(5) + print("Add result:", result_add) + print("Process value for -5:", process_value(-5)) + print("Sum of array [10, 20, 30]:", sum_array([10, 20, 30])) + print("Description for 2:", describe_number(2)) + print("Long description:\n", long_description()) + test_inner_class() + trigger_signal() diff --git a/tests/syntax-tests/source/GDScript/test.gd b/tests/syntax-tests/source/GDScript/test.gd new file mode 100644 index 00000000..7192dfec --- /dev/null +++ b/tests/syntax-tests/source/GDScript/test.gd @@ -0,0 +1,71 @@ +extends Node + +signal custom_signal(param) + +const PI = 3.14159 + +var untyped_var = "Hello, World!" +var typed_int: int = 42 +var typed_float: float = 3.14 +var typed_string: String = "GDScript Test" +var typed_array: Array = [1, 2, 3, 4] +var typed_dict: Dictionary = {"key": "value", "number": 100} + +onready var label = $Label + +func say_hello() -> void: + print("Hello from GDScript!") + +func add_numbers(a: int, b: int = 10) -> int: + return a + b + +func process_value(value: int) -> String: + if value < 0: + return "Negative" + elif value == 0: + return "Zero" + else: + return "Positive" + +func sum_array(arr: Array) -> int: + var total: int = 0 + for num in arr: + total += num + return total + +func describe_number(num: int) -> String: + match num: + 0: + return "Zero" + 1, 2, 3: + return "Small number" + _: + return "Large number" + +func long_description() -> String: + return """This is a test file for GDScript. +It covers variables, functions, control structures, loops, signals, inner classes, +multiline strings, arrays, and dictionaries.""" + +class InnerExample: + var inner_value: int = 99 + func show_value() -> void: + print("Inner value is:", inner_value) + +func test_inner_class() -> void: + var inner = InnerExample.new() + inner.show_value() + +func trigger_signal() -> void: + emit_signal("custom_signal", "TestParam") + +func _ready() -> void: + say_hello() + var result_add = add_numbers(5) + print("Add result:", result_add) + print("Process value for -5:", process_value(-5)) + print("Sum of array [10, 20, 30]:", sum_array([10, 20, 30])) + print("Description for 2:", describe_number(2)) + print("Long description:\n", long_description()) + test_inner_class() + trigger_signal()