From e25ccfb5943cb790df7dacc638bbe8bcf33afb3a Mon Sep 17 00:00:00 2001 From: juftin Date: Wed, 14 Feb 2024 10:30:18 -0700 Subject: [PATCH 01/53] fix(text-area): updated existing languages --- src/textual/tree-sitter/highlights/bash.scm | 199 ++++------- src/textual/tree-sitter/highlights/css.scm | 129 +++----- src/textual/tree-sitter/highlights/html.scm | 69 +--- src/textual/tree-sitter/highlights/json.scm | 34 +- src/textual/tree-sitter/highlights/python.scm | 309 ++++-------------- src/textual/tree-sitter/highlights/regex.scm | 70 ++-- src/textual/tree-sitter/highlights/toml.scm | 21 +- 7 files changed, 233 insertions(+), 598 deletions(-) diff --git a/src/textual/tree-sitter/highlights/bash.scm b/src/textual/tree-sitter/highlights/bash.scm index 23bf03e697..f33a7c2d3a 100644 --- a/src/textual/tree-sitter/highlights/bash.scm +++ b/src/textual/tree-sitter/highlights/bash.scm @@ -1,145 +1,56 @@ -(simple_expansion) @none -(expansion - "${" @punctuation.special - "}" @punctuation.special) @none [ - "(" - ")" - "((" - "))" - "{" - "}" - "[" - "]" - "[[" - "]]" - ] @punctuation.bracket - -[ - ";" - ";;" - (heredoc_start) - ] @punctuation.delimiter - -[ - "$" -] @punctuation.special - -[ - ">" - ">>" - "<" - "<<" - "&" - "&&" - "|" - "||" - "=" - "=~" - "==" - "!=" - ] @operator - -[ - (string) - (raw_string) - (ansi_c_string) - (heredoc_body) -] @string @spell - -(variable_assignment (word) @string) - -[ - "if" - "then" - "else" - "elif" - "fi" - "case" - "in" - "esac" - ] @conditional - -[ - "for" - "do" - "done" - "select" - "until" - "while" - ] @repeat - -[ - "declare" - "export" - "local" - "readonly" - "unset" - ] @keyword - -"function" @keyword.function - -(special_variable_name) @constant - -; trap -l -((word) @constant.builtin - (#match? @constant.builtin "^SIG(HUP|INT|QUIT|ILL|TRAP|ABRT|BUS|FPE|KILL|USR[12]|SEGV|PIPE|ALRM|TERM|STKFLT|CHLD|CONT|STOP|TSTP|TT(IN|OU)|URG|XCPU|XFSZ|VTALRM|PROF|WINCH|IO|PWR|SYS|RTMIN([+]([1-9]|1[0-5]))?|RTMAX(-([1-9]|1[0-4]))?)$")) - -((word) @boolean - (#any-of? @boolean "true" "false")) - -(comment) @comment @spell -(test_operator) @string - -(command_substitution - [ "$(" ")" ] @punctuation.bracket) - -(process_substitution - [ "<(" ")" ] @punctuation.bracket) - - -(function_definition - name: (word) @function) - -(command_name (word) @function.call) - -((command_name (word) @function.builtin) - (#any-of? @function.builtin - "alias" "bg" "bind" "break" "builtin" "caller" "cd" - "command" "compgen" "complete" "compopt" "continue" - "coproc" "dirs" "disown" "echo" "enable" "eval" - "exec" "exit" "fc" "fg" "getopts" "hash" "help" - "history" "jobs" "kill" "let" "logout" "mapfile" - "popd" "printf" "pushd" "pwd" "read" "readarray" - "return" "set" "shift" "shopt" "source" "suspend" - "test" "time" "times" "trap" "type" "typeset" - "ulimit" "umask" "unalias" "wait")) - -(command - argument: [ - (word) @parameter - (concatenation (word) @parameter) - ]) - -((word) @number - (#lua-match? @number "^[0-9]+$")) - -(file_redirect - descriptor: (file_descriptor) @operator - destination: (word) @parameter) - -(expansion - [ "${" "}" ] @punctuation.bracket) - -(variable_name) @variable - -((variable_name) @constant - (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) - -(case_item - value: (word) @parameter) - -(regex) @string.regex - -((program . (comment) @preproc) - (#lua-match? @preproc "^#!/")) + (string) + (raw_string) + (heredoc_body) + (heredoc_start) +] @string + +(command_name) @function + +(variable_name) @property + +[ + "case" + "do" + "done" + "elif" + "else" + "esac" + "export" + "fi" + "for" + "function" + "if" + "in" + "select" + "then" + "unset" + "until" + "while" +] @keyword + +(comment) @comment + +(function_definition name: (word) @function) + +(file_descriptor) @number + +[ + (command_substitution) + (process_substitution) + (expansion) +]@embedded + +[ + "$" + "&&" + ">" + ">>" + "<" + "|" +] @operator + +( + (command (_) @constant) + (#match? @constant "^-") +) diff --git a/src/textual/tree-sitter/highlights/css.scm b/src/textual/tree-sitter/highlights/css.scm index b26f0ec96c..763661af76 100644 --- a/src/textual/tree-sitter/highlights/css.scm +++ b/src/textual/tree-sitter/highlights/css.scm @@ -1,91 +1,64 @@ -[ - "@media" - "@charset" - "@namespace" - "@supports" - "@keyframes" - (at_keyword) - (to) - (from) - ] @keyword +(comment) @comment -"@import" @include +(tag_name) @tag +(nesting_selector) @tag +(universal_selector) @tag -(comment) @comment @spell +"~" @operator +">" @operator +"+" @operator +"-" @operator +"*" @operator +"/" @operator +"=" @operator +"^=" @operator +"|=" @operator +"~=" @operator +"$=" @operator +"*=" @operator -[ - (tag_name) - (nesting_selector) - (universal_selector) - ] @type - -(function_name) @function - -[ - "~" - ">" - "+" - "-" - "*" - "/" - "=" - "^=" - "|=" - "~=" - "$=" - "*=" - "and" - "or" - "not" - "only" - ] @operator - -(important) @type.qualifier +"and" @operator +"or" @operator +"not" @operator +"only" @operator (attribute_selector (plain_value) @string) -(pseudo_element_selector "::" (tag_name) @property) -(pseudo_class_selector (class_name) @property) +(pseudo_element_selector (tag_name) @attribute) +(pseudo_class_selector (class_name) @attribute) -[ - (class_name) - (id_name) - (property_name) - (feature_name) - (attribute_name) - ] @property +(class_name) @property +(id_name) @property +(namespace_name) @property +(property_name) @property +(feature_name) @property -(namespace_name) @namespace +(attribute_name) @attribute -((property_name) @type.definition - (#lua-match? @type.definition "^[-][-]")) -((plain_value) @type - (#lua-match? @type "^[-][-]")) +(function_name) @function -[ - (string_value) - (color_value) - (unit) - ] @string +((property_name) @variable + (#match? @variable "^--")) +((plain_value) @variable + (#match? @variable "^--")) -[ - (integer_value) - (float_value) - ] @number +"@media" @keyword +"@import" @keyword +"@charset" @keyword +"@namespace" @keyword +"@supports" @keyword +"@keyframes" @keyword +(at_keyword) @keyword +(to) @keyword +(from) @keyword +(important) @keyword -[ - "#" - "," - "." - ":" - "::" - ";" - ] @punctuation.delimiter +(string_value) @string +(color_value) @string.special -[ - "{" - ")" - "(" - "}" - ] @punctuation.bracket +(integer_value) @number +(float_value) @number +(unit) @type -(ERROR) @error +"#" @punctuation.delimiter +"," @punctuation.delimiter +":" @punctuation.delimiter diff --git a/src/textual/tree-sitter/highlights/html.scm b/src/textual/tree-sitter/highlights/html.scm index 15f2adb436..ea0ff4e308 100644 --- a/src/textual/tree-sitter/highlights/html.scm +++ b/src/textual/tree-sitter/highlights/html.scm @@ -1,64 +1,13 @@ (tag_name) @tag -(erroneous_end_tag_name) @html.end_tag_error +(erroneous_end_tag_name) @tag.error +(doctype) @constant +(attribute_name) @attribute +(attribute_value) @string (comment) @comment -(attribute_name) @tag.attribute -(attribute - (quoted_attribute_value) @string) -(text) @text @spell - -((element (start_tag (tag_name) @_tag) (text) @text.title) - (#eq? @_tag "title")) - -((element (start_tag (tag_name) @_tag) (text) @text.title.1) - (#eq? @_tag "h1")) - -((element (start_tag (tag_name) @_tag) (text) @text.title.2) - (#eq? @_tag "h2")) - -((element (start_tag (tag_name) @_tag) (text) @text.title.3) - (#eq? @_tag "h3")) - -((element (start_tag (tag_name) @_tag) (text) @text.title.4) - (#eq? @_tag "h4")) - -((element (start_tag (tag_name) @_tag) (text) @text.title.5) - (#eq? @_tag "h5")) - -((element (start_tag (tag_name) @_tag) (text) @text.title.6) - (#eq? @_tag "h6")) - -((element (start_tag (tag_name) @_tag) (text) @text.strong) - (#any-of? @_tag "strong" "b")) - -((element (start_tag (tag_name) @_tag) (text) @text.emphasis) - (#any-of? @_tag "em" "i")) - -((element (start_tag (tag_name) @_tag) (text) @text.strike) - (#any-of? @_tag "s" "del")) - -((element (start_tag (tag_name) @_tag) (text) @text.underline) - (#eq? @_tag "u")) - -((element (start_tag (tag_name) @_tag) (text) @text.literal) - (#any-of? @_tag "code" "kbd")) - -((element (start_tag (tag_name) @_tag) (text) @text.uri) - (#eq? @_tag "a")) - -((attribute - (attribute_name) @_attr - (quoted_attribute_value (attribute_value) @text.uri)) - (#any-of? @_attr "href" "src")) [ - "<" - ">" - "" -] @tag.delimiter - -"=" @operator - -(doctype) @constant - -"" + "" +] @punctuation.bracket diff --git a/src/textual/tree-sitter/highlights/json.scm b/src/textual/tree-sitter/highlights/json.scm index c23e7b3ce9..ece8392f0b 100644 --- a/src/textual/tree-sitter/highlights/json.scm +++ b/src/textual/tree-sitter/highlights/json.scm @@ -1,32 +1,16 @@ -[ - (true) - (false) -] @boolean +(pair + key: (_) @string.special.key) -(null) @json.null +(string) @string (number) @number -(pair key: (string) @json.label) -(pair value: (string) @string) - -(array (string) @string) - -(string_content) @spell - -(ERROR) @json.error - -["," ":"] @punctuation.delimiter - [ - "[" "]" - "{" "}" -] @punctuation.bracket + (null) + (true) + (false) +] @constant.builtin -(("\"" @conceal) - (#set! conceal "")) +(escape_sequence) @escape -(escape_sequence) @string.escape -((escape_sequence) @conceal - (#eq? @conceal "\\\"") - (#set! conceal "\"")) +(comment) @comment diff --git a/src/textual/tree-sitter/highlights/python.scm b/src/textual/tree-sitter/highlights/python.scm index 37aceef1fd..8ccf82b87d 100644 --- a/src/textual/tree-sitter/highlights/python.scm +++ b/src/textual/tree-sitter/highlights/python.scm @@ -1,191 +1,61 @@ -;; From tree-sitter-python licensed under MIT License -; Copyright (c) 2016 Max Brunsfeld -; Adapted for Textual from: -; https://github.com/nvim-treesitter/nvim-treesitter/blob/f95ffd09ed35880c3a46ad2b968df361fa592a76/queries/python/highlights.scm +; Identifier naming conventions -; Variables -(identifier) @variable - -; Reset highlighting in f-string interpolations -(interpolation) @none +((identifier) @constructor + (#match? @constructor "^[A-Z]")) -;; Identifier naming conventions -((identifier) @type - (#lua-match? @type "^[A-Z].*[a-z]")) ((identifier) @constant - (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) - -((identifier) @constant.builtin - (#lua-match? @constant.builtin "^__[a-zA-Z0-9_]*__$")) - -((identifier) @constant.builtin - (#any-of? @constant.builtin - ;; https://docs.python.org/3/library/constants.html - "NotImplemented" - "Ellipsis" - "quit" - "exit" - "copyright" - "credits" - "license")) - -((attribute - attribute: (identifier) @field) - (#match? @field "^([A-Z])@!.*$")) - -((identifier) @type.builtin - (#any-of? @type.builtin - ;; https://docs.python.org/3/library/exceptions.html - "BaseException" "Exception" "ArithmeticError" "BufferError" "LookupError" "AssertionError" "AttributeError" - "EOFError" "FloatingPointError" "GeneratorExit" "ImportError" "ModuleNotFoundError" "IndexError" "KeyError" - "KeyboardInterrupt" "MemoryError" "NameError" "NotImplementedError" "OSError" "OverflowError" "RecursionError" - "ReferenceError" "RuntimeError" "StopIteration" "StopAsyncIteration" "SyntaxError" "IndentationError" "TabError" - "SystemError" "SystemExit" "TypeError" "UnboundLocalError" "UnicodeError" "UnicodeEncodeError" "UnicodeDecodeError" - "UnicodeTranslateError" "ValueError" "ZeroDivisionError" "EnvironmentError" "IOError" "WindowsError" - "BlockingIOError" "ChildProcessError" "ConnectionError" "BrokenPipeError" "ConnectionAbortedError" - "ConnectionRefusedError" "ConnectionResetError" "FileExistsError" "FileNotFoundError" "InterruptedError" - "IsADirectoryError" "NotADirectoryError" "PermissionError" "ProcessLookupError" "TimeoutError" "Warning" - "UserWarning" "DeprecationWarning" "PendingDeprecationWarning" "SyntaxWarning" "RuntimeWarning" - "FutureWarning" "ImportWarning" "UnicodeWarning" "BytesWarning" "ResourceWarning" - ;; https://docs.python.org/3/library/stdtypes.html - "bool" "int" "float" "complex" "list" "tuple" "range" "str" - "bytes" "bytearray" "memoryview" "set" "frozenset" "dict" "type")) + (#match? @constant "^[A-Z][A-Z_]*$")) -((assignment - left: (identifier) @type.definition - (type (identifier) @_annotation)) - (#eq? @_annotation "TypeAlias")) +; Builtin functions -((assignment - left: (identifier) @type.definition - right: (call - function: (identifier) @_func)) - (#any-of? @_func "TypeVar" "NewType")) +((call + function: (identifier) @function.builtin) + (#match? + @function.builtin + "^(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes|callable|chr|classmethod|compile|complex|delattr|dict|dir|divmod|enumerate|eval|exec|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|isinstance|issubclass|iter|len|list|locals|map|max|memoryview|min|next|object|oct|open|ord|pow|print|property|range|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|vars|zip|__import__)$")) ; Function calls -(call - function: (identifier) @function.call) +(decorator) @function (call - function: (attribute - attribute: (identifier) @method.call)) - -((call - function: (identifier) @constructor) - (#lua-match? @constructor "^[A-Z]")) - -((call - function: (attribute - attribute: (identifier) @constructor)) - (#lua-match? @constructor "^[A-Z]")) - -;; Decorators - -((decorator "@" @attribute) - (#set! "priority" 101)) - -(decorator - (identifier) @attribute) -(decorator - (attribute - attribute: (identifier) @attribute)) -(decorator - (call (identifier) @attribute)) -(decorator - (call (attribute - attribute: (identifier) @attribute))) - -((decorator - (identifier) @attribute.builtin) - (#any-of? @attribute.builtin "classmethod" "property")) - -;; Builtin functions - -((call - function: (identifier) @function.builtin) - (#any-of? @function.builtin - "abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray" "bytes" "callable" "chr" "classmethod" - "compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec" "filter" "float" "format" - "frozenset" "getattr" "globals" "hasattr" "hash" "help" "hex" "id" "input" "int" "isinstance" "issubclass" - "iter" "len" "list" "locals" "map" "max" "memoryview" "min" "next" "object" "oct" "open" "ord" "pow" - "print" "property" "range" "repr" "reversed" "round" "set" "setattr" "slice" "sorted" "staticmethod" "str" - "sum" "super" "tuple" "type" "vars" "zip" "__import__")) + function: (attribute attribute: (identifier) @function.method)) +(call + function: (identifier) @function) -;; Function definitions +; Function definitions (function_definition name: (identifier) @function) +(identifier) @variable +(attribute attribute: (identifier) @property) (type (identifier) @type) -(type - (subscript - (identifier) @type)) ; type subscript: Tuple[int] - -((call - function: (identifier) @_isinstance - arguments: (argument_list - (_) - (identifier) @type)) - (#eq? @_isinstance "isinstance")) - -;; Normal parameters -(parameters - (identifier) @parameter) -;; Lambda parameters -(lambda_parameters - (identifier) @parameter) -(lambda_parameters - (tuple_pattern - (identifier) @parameter)) -; Default parameters -(keyword_argument - name: (identifier) @parameter) -; Naming parameters on call-site -(default_parameter - name: (identifier) @parameter) -(typed_parameter - (identifier) @parameter) -(typed_default_parameter - (identifier) @parameter) -; Variadic parameters *args, **kwargs -(parameters - (list_splat_pattern ; *args - (identifier) @parameter)) -(parameters - (dictionary_splat_pattern ; **kwargs - (identifier) @parameter)) - - -;; Literals -(none) @constant.builtin -[(true) (false)] @boolean -((identifier) @variable.builtin - (#eq? @variable.builtin "self")) -((identifier) @variable.builtin - (#eq? @variable.builtin "cls")) +; Literals -(integer) @number -(float) @float - -(comment) @comment @spell +[ + (none) + (true) + (false) +] @constant.builtin -((module . (comment) @preproc) - (#match? @preproc "^#!/")) +[ + (integer) + (float) +] @number +(comment) @comment (string) @string -(escape_sequence) @string.escape +(escape_sequence) @escape -; doc-strings -(expression_statement (string) @spell) - -; Tokens +(interpolation + "{" @punctuation.special + "}" @punctuation.special) @embedded [ "-" "-=" - ":=" "!=" "*" "**" @@ -202,6 +72,7 @@ "^" "^=" "+" + "->" "+=" "<" "<<" @@ -209,119 +80,53 @@ "<=" "<>" "=" + ":=" "==" ">" ">=" ">>" ">>=" - "@" - "@=" "|" "|=" "~" - "->" -] @operator - -; Keywords -[ + "@=" "and" "in" "is" "not" "or" - "del" -] @keyword.operator - -[ - "def" - "lambda" -] @keyword.function +] @operator [ + "as" "assert" "async" "await" + "break" "class" + "continue" + "def" + "del" + "elif" + "else" + "except" "exec" + "finally" + "for" + "from" "global" + "if" + "import" + "lambda" "nonlocal" "pass" "print" - "with" - "as" -] @keyword - -[ + "raise" "return" - "yield" -] @keyword.return -(yield "from" @keyword.return) - -(future_import_statement - "from" @include - "__future__" @constant.builtin) -(import_from_statement "from" @include) -"import" @include - -(aliased_import "as" @include) - -["if" "elif" "else" "match" "case"] @conditional - -["for" "while" "break" "continue"] @repeat - -[ "try" - "except" - "raise" - "finally" -] @exception - -(raise_statement "from" @exception) - -(try_statement - (else_clause - "else" @exception)) - -["(" ")" "[" "]" "{" "}"] @punctuation.bracket - -(interpolation - "{" @punctuation.special - "}" @punctuation.special) - -["," "." ":" ";" (ellipsis)] @punctuation.delimiter - -;; Class definitions - -(class_definition name: (identifier) @type.class) - -(class_definition - body: (block - (function_definition - name: (identifier) @method))) - -(class_definition - superclasses: (argument_list - (identifier) @type)) - -((class_definition - body: (block - (expression_statement - (assignment - left: (identifier) @field)))) - (#match? @field "^([A-Z])@!.*$")) -((class_definition - body: (block - (expression_statement - (assignment - left: (_ - (identifier) @field))))) - (#match? @field "^([A-Z])@!.*$")) - -((class_definition - (block - (function_definition - name: (identifier) @constructor))) - (#any-of? @constructor "__new__" "__init__")) - -;; Error -(ERROR) @error + "while" + "with" + "yield" + "match" + "case" +] @keyword diff --git a/src/textual/tree-sitter/highlights/regex.scm b/src/textual/tree-sitter/highlights/regex.scm index 7c671c2c04..8b653465b4 100644 --- a/src/textual/tree-sitter/highlights/regex.scm +++ b/src/textual/tree-sitter/highlights/regex.scm @@ -1,34 +1,50 @@ -;; Forked from tree-sitter-regex -;; The MIT License (MIT) Copyright (c) 2014 Max Brunsfeld [ - "(" - ")" - "(?" - "(?:" - "(?<" - ">" - "[" - "]" - "{" - "}" -] @regex.punctuation.bracket + "(" + ")" + "(?" + "(?:" + "(?<" + ">" + "[" + "]" + "{" + "}" +] @punctuation.bracket (group_name) @property -;; These are escaped special characters that lost their special meaning -;; -> no special highlighting -(identity_escape) @string.regex - -(class_character) @constant +[ + (identity_escape) + (control_letter_escape) + (character_class_escape) + (control_escape) + (start_assertion) + (end_assertion) + (boundary_assertion) + (non_boundary_assertion) +] @escape [ - (control_letter_escape) - (character_class_escape) - (control_escape) - (start_assertion) - (end_assertion) - (boundary_assertion) - (non_boundary_assertion) -] @string.escape + "*" + "+" + "?" + "|" + "=" + "!" +] @operator + +(count_quantifier + [ + (decimal_digits) @number + "," @punctuation.delimiter + ]) + +(character_class + [ + "^" @operator + (class_range "-" @operator) + ]) + +(class_character) @constant.character -[ "*" "+" "?" "|" "=" "!" ] @regex.operator +(pattern_character) @string diff --git a/src/textual/tree-sitter/highlights/toml.scm b/src/textual/tree-sitter/highlights/toml.scm index 9228d28072..e4d6966fcb 100644 --- a/src/textual/tree-sitter/highlights/toml.scm +++ b/src/textual/tree-sitter/highlights/toml.scm @@ -1,22 +1,21 @@ ; Properties ;----------- -(bare_key) @toml.type +(bare_key) @property (quoted_key) @string -(pair (bare_key)) @property ; Literals ;--------- -(boolean) @boolean -(comment) @comment @spell +(boolean) @constant.builtin +(comment) @comment (string) @string (integer) @number -(float) @float -(offset_date_time) @toml.datetime -(local_date_time) @toml.datetime -(local_date) @toml.datetime -(local_time) @toml.datetime +(float) @number +(offset_date_time) @string.special +(local_date_time) @string.special +(local_date) @string.special +(local_time) @string.special ; Punctuation ;------------ @@ -24,7 +23,7 @@ "." @punctuation.delimiter "," @punctuation.delimiter -"=" @toml.operator +"=" @operator "[" @punctuation.bracket "]" @punctuation.bracket @@ -32,5 +31,3 @@ "]]" @punctuation.bracket "{" @punctuation.bracket "}" @punctuation.bracket - -(ERROR) @toml.error From c9fac897e595d9d0e4d918fc368e1e980964428f Mon Sep 17 00:00:00 2001 From: juftin Date: Wed, 14 Feb 2024 15:12:23 -0700 Subject: [PATCH 02/53] feat(text-area): new languages --- src/textual/document/_languages.py | 36 +- src/textual/tree-sitter/highlights/c.scm | 81 ++++ .../tree-sitter/highlights/c_sharp.scm | 254 +++++++++++ src/textual/tree-sitter/highlights/cpp.scm | 74 ++++ .../tree-sitter/highlights/dockerfile.scm | 51 +++ src/textual/tree-sitter/highlights/dot.scm | 46 ++ src/textual/tree-sitter/highlights/elisp.scm | 72 ++++ src/textual/tree-sitter/highlights/elixir.scm | 199 +++++++++ src/textual/tree-sitter/highlights/elm.scm | 76 ++++ .../highlights/embedded_template.scm | 12 + src/textual/tree-sitter/highlights/erlang.scm | 228 ++++++++++ .../tree-sitter/highlights/fortran.scm | 197 +++++++++ src/textual/tree-sitter/highlights/go.scm | 123 ++++++ src/textual/tree-sitter/highlights/gomod.scm | 17 + src/textual/tree-sitter/highlights/hack.scm | 31 ++ .../tree-sitter/highlights/haskell.scm | 156 +++++++ src/textual/tree-sitter/highlights/java.scm | 142 +++++++ .../tree-sitter/highlights/javascript.scm | 205 +++++++++ src/textual/tree-sitter/highlights/jsdoc.scm | 2 + src/textual/tree-sitter/highlights/kotlin.scm | 380 +++++++++++++++++ src/textual/tree-sitter/highlights/make.scm | 171 ++++++++ src/textual/tree-sitter/highlights/objc.scm | 395 ++++++++++++++++++ src/textual/tree-sitter/highlights/ocaml.scm | 151 +++++++ src/textual/tree-sitter/highlights/php.scm | 122 ++++++ src/textual/tree-sitter/highlights/r.scm | 128 ++++++ src/textual/tree-sitter/highlights/ruby.scm | 154 +++++++ src/textual/tree-sitter/highlights/rust.scm | 155 +++++++ src/textual/tree-sitter/highlights/scala.scm | 261 ++++++++++++ src/textual/tree-sitter/highlights/sqlite.scm | 179 ++++++++ .../tree-sitter/highlights/typescript.scm | 35 ++ 30 files changed, 4130 insertions(+), 3 deletions(-) create mode 100644 src/textual/tree-sitter/highlights/c.scm create mode 100644 src/textual/tree-sitter/highlights/c_sharp.scm create mode 100644 src/textual/tree-sitter/highlights/cpp.scm create mode 100644 src/textual/tree-sitter/highlights/dockerfile.scm create mode 100644 src/textual/tree-sitter/highlights/dot.scm create mode 100644 src/textual/tree-sitter/highlights/elisp.scm create mode 100644 src/textual/tree-sitter/highlights/elixir.scm create mode 100644 src/textual/tree-sitter/highlights/elm.scm create mode 100644 src/textual/tree-sitter/highlights/embedded_template.scm create mode 100644 src/textual/tree-sitter/highlights/erlang.scm create mode 100644 src/textual/tree-sitter/highlights/fortran.scm create mode 100644 src/textual/tree-sitter/highlights/go.scm create mode 100644 src/textual/tree-sitter/highlights/gomod.scm create mode 100644 src/textual/tree-sitter/highlights/hack.scm create mode 100644 src/textual/tree-sitter/highlights/haskell.scm create mode 100644 src/textual/tree-sitter/highlights/java.scm create mode 100644 src/textual/tree-sitter/highlights/javascript.scm create mode 100644 src/textual/tree-sitter/highlights/jsdoc.scm create mode 100644 src/textual/tree-sitter/highlights/kotlin.scm create mode 100644 src/textual/tree-sitter/highlights/make.scm create mode 100644 src/textual/tree-sitter/highlights/objc.scm create mode 100644 src/textual/tree-sitter/highlights/ocaml.scm create mode 100644 src/textual/tree-sitter/highlights/php.scm create mode 100644 src/textual/tree-sitter/highlights/r.scm create mode 100644 src/textual/tree-sitter/highlights/ruby.scm create mode 100644 src/textual/tree-sitter/highlights/rust.scm create mode 100644 src/textual/tree-sitter/highlights/scala.scm create mode 100644 src/textual/tree-sitter/highlights/sqlite.scm create mode 100644 src/textual/tree-sitter/highlights/typescript.scm diff --git a/src/textual/document/_languages.py b/src/textual/document/_languages.py index a33f7544e8..efc5aa8c42 100644 --- a/src/textual/document/_languages.py +++ b/src/textual/document/_languages.py @@ -1,13 +1,43 @@ BUILTIN_LANGUAGES = sorted( [ - "markdown", - "yaml", - "sql", + "bash", + "c", + "c_sharp", + "cpp", "css", + "dockerfile", + "dot", + "elisp", + "elixir", + "elm", + "embedded_template", + "erlang", + "fortran", + "go", + "gomod", + "hack", + "haskell", "html", + "java", + "javascript", + "jsdoc", "json", + "kotlin", + "make", + "markdown", + "objc", + "ocaml", + "php", "python", + "r", "regex", + "ruby", + "rust", + "scala", + "sql", + "sqlite", "toml", + "typescript", + "yaml", ] ) diff --git a/src/textual/tree-sitter/highlights/c.scm b/src/textual/tree-sitter/highlights/c.scm new file mode 100644 index 0000000000..04d9a04f38 --- /dev/null +++ b/src/textual/tree-sitter/highlights/c.scm @@ -0,0 +1,81 @@ +"break" @keyword +"case" @keyword +"const" @keyword +"continue" @keyword +"default" @keyword +"do" @keyword +"else" @keyword +"enum" @keyword +"extern" @keyword +"for" @keyword +"if" @keyword +"inline" @keyword +"return" @keyword +"sizeof" @keyword +"static" @keyword +"struct" @keyword +"switch" @keyword +"typedef" @keyword +"union" @keyword +"volatile" @keyword +"while" @keyword + +"#define" @keyword +"#elif" @keyword +"#else" @keyword +"#endif" @keyword +"#if" @keyword +"#ifdef" @keyword +"#ifndef" @keyword +"#include" @keyword +(preproc_directive) @keyword + +"--" @operator +"-" @operator +"-=" @operator +"->" @operator +"=" @operator +"!=" @operator +"*" @operator +"&" @operator +"&&" @operator +"+" @operator +"++" @operator +"+=" @operator +"<" @operator +"==" @operator +">" @operator +"||" @operator + +"." @delimiter +";" @delimiter + +(string_literal) @string +(system_lib_string) @string + +(null) @constant +(number_literal) @number +(char_literal) @number + +(call_expression + function: (identifier) @function) +(call_expression + function: (field_expression + field: (field_identifier) @function)) +(function_declarator + declarator: (identifier) @function) +(preproc_function_def + name: (identifier) @function.special) + +(field_identifier) @property +(statement_identifier) @label +(type_identifier) @type +(primitive_type) @type +(sized_type_specifier) @type + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z\\d_]*$")) + +(identifier) @variable + +(comment) @comment diff --git a/src/textual/tree-sitter/highlights/c_sharp.scm b/src/textual/tree-sitter/highlights/c_sharp.scm new file mode 100644 index 0000000000..12ab524df2 --- /dev/null +++ b/src/textual/tree-sitter/highlights/c_sharp.scm @@ -0,0 +1,254 @@ +;; Methods +(method_declaration name: (identifier) @function) +(local_function_statement name: (identifier) @function) + +;; Types +(interface_declaration name: (identifier) @type) +(class_declaration name: (identifier) @type) +(enum_declaration name: (identifier) @type) +(struct_declaration (identifier) @type) +(record_declaration (identifier) @type) +(record_struct_declaration (identifier) @type) +(namespace_declaration name: (identifier) @module) + +(constructor_declaration name: (identifier) @constructor) +(destructor_declaration name: (identifier) @constructor) + +[ + (implicit_type) + (predefined_type) +] @type.builtin + +(_ type: (identifier) @type) + +;; Enum +(enum_member_declaration (identifier) @property.definition) + +;; Literals +[ + (real_literal) + (integer_literal) +] @number + +[ + (character_literal) + (string_literal) + (verbatim_string_literal) + (interpolated_string_text) + (interpolated_verbatim_string_text) + "\"" + "$\"" + "@$\"" + "$@\"" + ] @string + +[ + (boolean_literal) + (null_literal) +] @constant.builtin + +;; Comments +(comment) @comment + +;; Tokens +[ + ";" + "." + "," +] @punctuation.delimiter + +[ + "--" + "-" + "-=" + "&" + "&=" + "&&" + "+" + "++" + "+=" + "<" + "<=" + "<<" + "<<=" + "=" + "==" + "!" + "!=" + "=>" + ">" + ">=" + ">>" + ">>=" + ">>>" + ">>>=" + "|" + "|=" + "||" + "?" + "??" + "??=" + "^" + "^=" + "~" + "*" + "*=" + "/" + "/=" + "%" + "%=" + ":" +] @operator + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +;; Keywords +(modifier) @keyword +(this_expression) @keyword +(escape_sequence) @keyword + +[ + "add" + "alias" + "as" + "base" + "break" + "case" + "catch" + "checked" + "class" + "continue" + "default" + "delegate" + "do" + "else" + "enum" + "event" + "explicit" + "extern" + "finally" + "for" + "foreach" + "global" + "goto" + "if" + "implicit" + "interface" + "is" + "lock" + "namespace" + "notnull" + "operator" + "params" + "return" + "remove" + "sizeof" + "stackalloc" + "static" + "struct" + "switch" + "throw" + "try" + "typeof" + "unchecked" + "using" + "while" + "new" + "await" + "in" + "yield" + "get" + "set" + "when" + "out" + "ref" + "from" + "where" + "select" + "record" + "init" + "with" + "let" +] @keyword + + +;; Linq +(from_clause (identifier) @variable) +(group_clause (identifier) @variable) +(order_by_clause (identifier) @variable) +(join_clause (identifier) @variable) +(select_clause (identifier) @variable) +(query_continuation (identifier) @variable) @keyword + +;; Record +(with_expression + (with_initializer_expression + (simple_assignment_expression + (identifier) @variable))) + +;; Exprs +(binary_expression (identifier) @variable (identifier) @variable) +(binary_expression (identifier)* @variable) +(conditional_expression (identifier) @variable) +(prefix_unary_expression (identifier) @variable) +(postfix_unary_expression (identifier)* @variable) +(assignment_expression (identifier) @variable) +(cast_expression (_) (identifier) @variable) + +;; Class +(base_list (identifier) @type) ;; applies to record_base too +(property_declaration (generic_name)) +(property_declaration + name: (identifier) @variable) +(property_declaration + name: (identifier) @variable) +(property_declaration + name: (identifier) @variable) + +;; Lambda +(lambda_expression) @variable + +;; Attribute +(attribute) @attribute + +;; Parameter +(parameter + name: (identifier) @variable.parameter) +(parameter (identifier) @variable.parameter) +(parameter_modifier) @keyword + +;; Variable declarations +(variable_declarator (identifier) @variable) +(for_each_statement left: (identifier) @variable) +(catch_declaration (_) (identifier) @variable) + +;; Return +(return_statement (identifier) @variable) +(yield_statement (identifier) @variable) + +;; Type +(generic_name (identifier) @type) +(type_parameter (identifier) @property.definition) +(type_argument_list (identifier) @type) +(as_expression right: (identifier) @type) +(is_expression right: (identifier) @type) + +;; Type constraints +(type_parameter_constraints_clause (identifier) @property.definition) + +;; Switch +(switch_statement (identifier) @variable) +(switch_expression (identifier) @variable) + +;; Lock statement +(lock_statement (identifier) @variable) + +;; Method calls +(invocation_expression (member_access_expression name: (identifier) @function)) diff --git a/src/textual/tree-sitter/highlights/cpp.scm b/src/textual/tree-sitter/highlights/cpp.scm new file mode 100644 index 0000000000..4d1f1c045e --- /dev/null +++ b/src/textual/tree-sitter/highlights/cpp.scm @@ -0,0 +1,74 @@ +; Functions + +(call_expression + function: (qualified_identifier + name: (identifier) @function)) + +(template_function + name: (identifier) @function) + +(template_method + name: (field_identifier) @function) + +(template_function + name: (identifier) @function) + +(function_declarator + declarator: (qualified_identifier + name: (identifier) @function)) + +(function_declarator + declarator: (qualified_identifier + name: (identifier) @function)) + +(function_declarator + declarator: (field_identifier) @function) + +; Types + +((namespace_identifier) @type + (#match? @type "^[A-Z]")) + +(auto) @type + +; Constants + +(this) @variable.builtin +(null "nullptr" @constant) + +; Keywords + +[ + "catch" + "class" + "co_await" + "co_return" + "co_yield" + "constexpr" + "constinit" + "consteval" + "delete" + "explicit" + "final" + "friend" + "mutable" + "namespace" + "noexcept" + "new" + "override" + "private" + "protected" + "public" + "template" + "throw" + "try" + "typename" + "using" + "virtual" + "concept" + "requires" +] @keyword + +; Strings + +(raw_string_literal) @string diff --git a/src/textual/tree-sitter/highlights/dockerfile.scm b/src/textual/tree-sitter/highlights/dockerfile.scm new file mode 100644 index 0000000000..5a945fb9bf --- /dev/null +++ b/src/textual/tree-sitter/highlights/dockerfile.scm @@ -0,0 +1,51 @@ +[ + "FROM" + "AS" + "RUN" + "CMD" + "LABEL" + "EXPOSE" + "ENV" + "ADD" + "COPY" + "ENTRYPOINT" + "VOLUME" + "USER" + "WORKDIR" + "ARG" + "ONBUILD" + "STOPSIGNAL" + "HEALTHCHECK" + "SHELL" + "MAINTAINER" + "CROSS_BUILD" +] @keyword + +[ + ":" + "@" +] @operator + +(comment) @comment + + +(image_spec + (image_tag + ":" @punctuation.special) + (image_digest + "@" @punctuation.special)) + +(double_quoted_string) @string + +(expansion + [ + "$" + "{" + "}" + ] @punctuation.special +) @none + +((variable) @constant + (#match? @constant "^[A-Z][A-Z_0-9]*$")) + + diff --git a/src/textual/tree-sitter/highlights/dot.scm b/src/textual/tree-sitter/highlights/dot.scm new file mode 100644 index 0000000000..d792cdb4b0 --- /dev/null +++ b/src/textual/tree-sitter/highlights/dot.scm @@ -0,0 +1,46 @@ +(keyword) @keyword +(string_literal) @string +(number_literal) @number + +[ + (edgeop) + (operator) +] @operator + +[ + "," + ";" +] @punctuation.delimiter + +[ + "{" + "}" + "[" + "]" + "<" + ">" +] @punctuation.bracket + +(subgraph + id: (id + (identifier) @namespace) +) + +(attribute + name: (id + (identifier) @type) +) + +(attribute + value: (id + (identifier) @constant) +) + +[ +(comment) +(preproc) +] @comment + +(ERROR) @error + +(identifier) @variable diff --git a/src/textual/tree-sitter/highlights/elisp.scm b/src/textual/tree-sitter/highlights/elisp.scm new file mode 100644 index 0000000000..d78b960f04 --- /dev/null +++ b/src/textual/tree-sitter/highlights/elisp.scm @@ -0,0 +1,72 @@ +;; Special forms +[ + "and" + "catch" + "cond" + "condition-case" + "defconst" + "defvar" + "function" + "if" + "interactive" + "lambda" + "let" + "let*" + "or" + "prog1" + "prog2" + "progn" + "quote" + "save-current-buffer" + "save-excursion" + "save-restriction" + "setq" + "setq-default" + "unwind-protect" + "while" +] @keyword + +;; Function definitions +[ + "defun" + "defsubst" + ] @keyword +(function_definition name: (symbol) @function) +(function_definition parameters: (list (symbol) @variable.parameter)) +(function_definition docstring: (string) @comment) + +;; Highlight macro definitions the same way as function definitions. +"defmacro" @keyword +(macro_definition name: (symbol) @function) +(macro_definition parameters: (list (symbol) @variable.parameter)) +(macro_definition docstring: (string) @comment) + +(comment) @comment + +(integer) @number +(float) @number +(char) @number + +(string) @string + +[ + "(" + ")" + "#[" + "[" + "]" +] @punctuation.bracket + +[ + "`" + "#'" + "'" + "," + ",@" +] @operator + +;; Highlight nil and t as constants, unlike other symbols +[ + "nil" + "t" +] @constant.builtin diff --git a/src/textual/tree-sitter/highlights/elixir.scm b/src/textual/tree-sitter/highlights/elixir.scm new file mode 100644 index 0000000000..7423e31cbc --- /dev/null +++ b/src/textual/tree-sitter/highlights/elixir.scm @@ -0,0 +1,199 @@ +; Reserved keywords + +["when" "and" "or" "not" "in" "not in" "fn" "do" "end" "catch" "rescue" "after" "else"] @keyword + +; Operators + +; * doc string +(unary_operator + operator: "@" @comment.doc + operand: (call + target: (identifier) @comment.doc.__attribute__ + (arguments + [ + (string) @comment.doc + (charlist) @comment.doc + (sigil + quoted_start: _ @comment.doc + quoted_end: _ @comment.doc) @comment.doc + (boolean) @comment.doc + ])) + (#match? @comment.doc.__attribute__ "^(moduledoc|typedoc|doc)$")) + +; * module attribute +(unary_operator + operator: "@" @attribute + operand: [ + (identifier) @attribute + (call + target: (identifier) @attribute) + (boolean) @attribute + (nil) @attribute + ]) + +; * capture operand +(unary_operator + operator: "&" + operand: (integer) @operator) + +(operator_identifier) @operator + +(unary_operator + operator: _ @operator) + +(binary_operator + operator: _ @operator) + +(dot + operator: _ @operator) + +(stab_clause + operator: _ @operator) + +; Literals + +[ + (boolean) + (nil) +] @constant + +[ + (integer) + (float) +] @number + +(alias) @module + +(call + target: (dot + left: (atom) @module)) + +(char) @constant + +; Quoted content + +(interpolation "#{" @punctuation.special "}" @punctuation.special) @embedded + +(escape_sequence) @string.escape + +[ + (atom) + (quoted_atom) + (keyword) + (quoted_keyword) +] @string.special.symbol + +[ + (string) + (charlist) +] @string + +; Note that we explicitly target sigil quoted start/end, so they are not overridden by delimiters + +(sigil + (sigil_name) @__name__ + quoted_start: _ @string + quoted_end: _ @string + (#match? @__name__ "^[sS]$")) @string + +(sigil + (sigil_name) @__name__ + quoted_start: _ @string.regex + quoted_end: _ @string.regex + (#match? @__name__ "^[rR]$")) @string.regex + +(sigil + (sigil_name) @__name__ + quoted_start: _ @string.special + quoted_end: _ @string.special) @string.special + +; Calls + +; * definition keyword +(call + target: (identifier) @keyword + (#match? @keyword "^(def|defdelegate|defexception|defguard|defguardp|defimpl|defmacro|defmacrop|defmodule|defn|defnp|defoverridable|defp|defprotocol|defstruct)$")) + +; * kernel or special forms keyword +(call + target: (identifier) @keyword + (#match? @keyword "^(alias|case|cond|else|for|if|import|quote|raise|receive|require|reraise|super|throw|try|unless|unquote|unquote_splicing|use|with)$")) + +; * function call +(call + target: [ + ; local + (identifier) @function + ; remote + (dot + right: (identifier) @function) + ]) + +; * just identifier in function definition +(call + target: (identifier) @keyword + (arguments + [ + (identifier) @function + (binary_operator + left: (identifier) @function + operator: "when") + ]) + (#match? @keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp)$")) + +; * pipe into identifier (definition) +(call + target: (identifier) @keyword + (arguments + (binary_operator + operator: "|>" + right: (identifier) @variable)) + (#match? @keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp)$")) + +; * pipe into identifier (function call) +(binary_operator + operator: "|>" + right: (identifier) @function) + +; Identifiers + +; * special +( + (identifier) @constant.builtin + (#match? @constant.builtin "^(__MODULE__|__DIR__|__ENV__|__CALLER__|__STACKTRACE__)$") +) + +; * unused +( + (identifier) @comment.unused + (#match? @comment.unused "^_") +) + +; * regular +(identifier) @variable + +; Comment + +(comment) @comment + +; Punctuation + +[ + "%" +] @punctuation + +[ + "," + ";" +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "<<" + ">>" +] @punctuation.bracket diff --git a/src/textual/tree-sitter/highlights/elm.scm b/src/textual/tree-sitter/highlights/elm.scm new file mode 100644 index 0000000000..8cd68257df --- /dev/null +++ b/src/textual/tree-sitter/highlights/elm.scm @@ -0,0 +1,76 @@ +; Keywords +[ + "if" + "then" + "else" + "let" + "in" + ] @keyword.control.elm +(case) @keyword.control.elm +(of) @keyword.control.elm + +(colon) @keyword.other.elm +(backslash) @keyword.other.elm +(as) @keyword.other.elm +(port) @keyword.other.elm +(exposing) @keyword.other.elm +(alias) @keyword.other.elm +(infix) @keyword.other.elm + +(arrow) @keyword.operator.arrow.elm + +(port) @keyword.other.port.elm + +(type_annotation(lower_case_identifier) @function.elm) +(port_annotation(lower_case_identifier) @function.elm) +(function_declaration_left(lower_case_identifier) @function.elm) +(function_call_expr target: (value_expr) @function.elm) + +(field_access_expr(value_expr(value_qid)) @local.function.elm) +(lower_pattern) @local.function.elm +(record_base_identifier) @local.function.elm + + +(operator_identifier) @keyword.operator.elm +(eq) @keyword.operator.assignment.elm + + +"(" @punctuation.section.braces +")" @punctuation.section.braces + +"|" @keyword.other.elm +"," @punctuation.separator.comma.elm + +(import) @meta.import.elm +(module) @keyword.other.elm + +(number_constant_expr) @constant.numeric.elm + + +(type) @keyword.type.elm + +(type_declaration(upper_case_identifier) @storage.type.elm) +(type_ref) @storage.type.elm +(type_alias_declaration name: (upper_case_identifier) @storage.type.elm) + +(union_variant(upper_case_identifier) @union.elm) +(union_pattern) @union.elm +(value_expr(upper_case_qid(upper_case_identifier)) @union.elm) + +; comments +(line_comment) @comment.elm +(block_comment) @comment.elm + +; strings +(string_escape) @character.escape.elm + +(open_quote) @string.elm +(close_quote) @string.elm +(regular_string_part) @string.elm + +(open_char) @char.elm +(close_char) @char.elm + + +; glsl +(glsl_content) @source.glsl diff --git a/src/textual/tree-sitter/highlights/embedded_template.scm b/src/textual/tree-sitter/highlights/embedded_template.scm new file mode 100644 index 0000000000..0bf76a7d49 --- /dev/null +++ b/src/textual/tree-sitter/highlights/embedded_template.scm @@ -0,0 +1,12 @@ +(comment_directive) @comment + +[ + "<%#" + "<%" + "<%=" + "<%_" + "<%-" + "%>" + "-%>" + "_%>" +] @keyword diff --git a/src/textual/tree-sitter/highlights/erlang.scm b/src/textual/tree-sitter/highlights/erlang.scm new file mode 100644 index 0000000000..5fb817efc6 --- /dev/null +++ b/src/textual/tree-sitter/highlights/erlang.scm @@ -0,0 +1,228 @@ +;; Copyright (c) Facebook, Inc. and its affiliates. +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; --------------------------------------------------------------------- + +;; Based initially on the contents of https://github.com/WhatsApp/tree-sitter-erlang/issues/2 by @Wilfred +;; and https://github.com/the-mikedavis/tree-sitter-erlang/blob/main/queries/highlights.scm +;; +;; The tests are also based on those in +;; https://github.com/the-mikedavis/tree-sitter-erlang/tree/main/test/highlight +;; + + +;; First match wins in this file + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Attributes + +;; module attribute +(module_attribute + name: (atom) @module) + +;; behaviour +(behaviour_attribute name: (atom) @module) + +;; export + +;; Import attribute +(import_attribute + module: (atom) @module) + +;; export_type + +;; optional_callbacks + +;; compile +(compile_options_attribute + options: (tuple + expr: (atom) + expr: (list + exprs: (binary_op_expr + lhs: (atom) + rhs: (integer))))) + +;; file attribute + +;; record +(record_decl name: (atom) @type) +(record_decl name: (macro_call_expr name: (var) @constant)) +(record_field name: (atom) @property) + +;; type alias + +;; opaque + +;; Spec attribute +(spec fun: (atom) @function) +(spec + module: (module name: (atom) @module) + fun: (atom) @function) + +;; callback +(callback fun: (atom) @function) + +;; wild attribute +(wild_attribute name: (attr_name name: (atom) @keyword)) + +;; fun decl + +;; include/include_lib + +;; ifdef/ifndef +(pp_ifdef name: (_) @keyword.directive) +(pp_ifndef name: (_) @keyword.directive) + +;; define +(pp_define + lhs: (macro_lhs + name: (_) @keyword.directive + args: (var_args args: (var)))) +(pp_define + lhs: (macro_lhs + name: (var) @constant)) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Functions +(fa fun: (atom) @function) +(type_name name: (atom) @function) +(call expr: (atom) @function) +(function_clause name: (atom) @function) +(internal_fun fun: (atom) @function) + +;; This is a fudge, we should check that the operator is '/' +;; But our grammar does not (currently) provide it +(binary_op_expr lhs: (atom) @function rhs: (integer)) + +;; Others +(remote_module module: (atom) @module) +(remote fun: (atom) @function) +(macro_call_expr name: (var) @keyword.directive args: (_) ) +(macro_call_expr name: (var) @constant) +(macro_call_expr name: (atom) @keyword.directive) +(record_field_name name: (atom) @property) +(record_name name: (atom) @type) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Reserved words +[ "after" + "and" + "band" + "begin" + "behavior" + "behaviour" + "bnot" + "bor" + "bsl" + "bsr" + "bxor" + "callback" + "case" + "catch" + "compile" + "define" + "div" + "elif" + "else" + "end" + "endif" + "export" + "export_type" + "file" + "fun" + "if" + "ifdef" + "ifndef" + "import" + "include" + "include_lib" + "module" + "of" + "opaque" + "optional_callbacks" + "or" + "receive" + "record" + "spec" + "try" + "type" + "undef" + "unit" + "when" + "xor"] @keyword + +["andalso" "orelse"] @keyword.operator + +;; Punctuation +["," "." ";"] @punctuation.delimiter +["(" ")" "{" "}" "[" "]" "<<" ">>"] @punctuation.bracket + +;; Operators +["!" + "->" + "<-" + "#" + "::" + "|" + ":" + "=" + "||" + + "+" + "-" + "bnot" + "not" + + "/" + "*" + "div" + "rem" + "band" + "and" + + "+" + "-" + "bor" + "bxor" + "bsl" + "bsr" + "or" + "xor" + + "++" + "--" + + "==" + "/=" + "=<" + "<" + ">=" + ">" + "=:=" + "=/=" + ] @operator + +;;; Comments +((var) @comment.discard + (#match? @comment.discard "^_")) + +(dotdotdot) @comment.discard +(comment) @comment + +;; Primitive types +(string) @string +(char) @constant +(integer) @number +(var) @variable +(atom) @string.special.symbol diff --git a/src/textual/tree-sitter/highlights/fortran.scm b/src/textual/tree-sitter/highlights/fortran.scm new file mode 100644 index 0000000000..418e40dfe9 --- /dev/null +++ b/src/textual/tree-sitter/highlights/fortran.scm @@ -0,0 +1,197 @@ +(identifier) @variable +(string_literal) @string +(number_literal) @number +(boolean_literal) @boolean +(comment) @comment + +[ + (intrinsic_type) + "allocatable" + "attributes" + "device" + "dimension" + "endtype" + "global" + "grid_global" + "host" + "import" + "in" + "inout" + "intent" + "optional" + "out" + "pointer" + "type" + "value" + ] @type + +[ + "contains" + "private" + "public" + ] @include + +[ + (none) + "implicit" + ] @attribute + +[ + "endfunction" + "endprogram" + "endsubroutine" + "function" + "procedure" + "subroutine" + ] @keyword.function + +[ + (default) + (procedure_qualifier) + "abstract" + "bind" + "call" + "class" + "continue" + "cycle" + "endenum" + "endinterface" + "endmodule" + "endprocedure" + "endprogram" + "endsubmodule" + "enum" + "enumerator" + "equivalence" + "exit" + "extends" + "format" + "goto" + "include" + "interface" + "intrinsic" + "non_intrinsic" + "module" + "namelist" + "only" + "parameter" + "print" + "procedure" + "program" + "read" + "stop" + "submodule" + "use" + "write" + ] @keyword + +"return" @keyword.return + +[ + "else" + "elseif" + "elsewhere" + "endif" + "endwhere" + "if" + "then" + "where" + ] @conditional + +[ + "do" + "enddo" + "forall" + "while" + ] @repeat + +[ + "*" + "+" + "-" + "/" + "=" + "<" + ">" + "<=" + ">=" + "==" + "/=" + ] @operator + +[ + "\\.and\\." + "\\.or\\." + "\\.lt\\." + "\\.gt\\." + "\\.ge\\." + "\\.le\\." + "\\.eq\\." + "\\.eqv\\." + "\\.neqv\\." + ] @keyword.operator + +;; Brackets +[ + "(" + ")" + "[" + "]" + "<<<" + ">>>" + ] @punctuation.bracket + +;; Delimiter +[ + "::" + "," + "%" + ] @punctuation.delimiter + +(parameters + (identifier) @parameter) + +(program_statement + (name) @namespace) + +(module_statement + (name) @namespace) + +(submodule_statement + (module_name) (name) @namespace) + +(function_statement + (name) @function) + +(subroutine_statement + (name) @function) + +(module_procedure_statement + (name) @function) + +(end_program_statement + (name) @namespace) + +(end_module_statement + (name) @namespace) + +(end_submodule_statement + (name) @namespace) + +(end_function_statement + (name) @function) + +(end_subroutine_statement + (name) @function) + +(end_module_procedure_statement + (name) @function) + +(subroutine_call + (identifier) @function) + +(keyword_argument + name: (identifier) @keyword) + +(derived_type_member_expression + (type_member) @property) diff --git a/src/textual/tree-sitter/highlights/go.scm b/src/textual/tree-sitter/highlights/go.scm new file mode 100644 index 0000000000..7e1d625272 --- /dev/null +++ b/src/textual/tree-sitter/highlights/go.scm @@ -0,0 +1,123 @@ +; Function calls + +(call_expression + function: (identifier) @function.builtin + (.match? @function.builtin "^(append|cap|close|complex|copy|delete|imag|len|make|new|panic|print|println|real|recover)$")) + +(call_expression + function: (identifier) @function) + +(call_expression + function: (selector_expression + field: (field_identifier) @function.method)) + +; Function definitions + +(function_declaration + name: (identifier) @function) + +(method_declaration + name: (field_identifier) @function.method) + +; Identifiers + +(type_identifier) @type +(field_identifier) @property +(identifier) @variable + +; Operators + +[ + "--" + "-" + "-=" + ":=" + "!" + "!=" + "..." + "*" + "*" + "*=" + "/" + "/=" + "&" + "&&" + "&=" + "%" + "%=" + "^" + "^=" + "+" + "++" + "+=" + "<-" + "<" + "<<" + "<<=" + "<=" + "=" + "==" + ">" + ">=" + ">>" + ">>=" + "|" + "|=" + "||" + "~" +] @operator + +; Keywords + +[ + "break" + "case" + "chan" + "const" + "continue" + "default" + "defer" + "else" + "fallthrough" + "for" + "func" + "go" + "goto" + "if" + "import" + "interface" + "map" + "package" + "range" + "return" + "select" + "struct" + "switch" + "type" + "var" +] @keyword + +; Literals + +[ + (interpreted_string_literal) + (raw_string_literal) + (rune_literal) +] @string + +(escape_sequence) @escape + +[ + (int_literal) + (float_literal) + (imaginary_literal) +] @number + +[ + (true) + (false) + (nil) + (iota) +] @constant.builtin + +(comment) @comment diff --git a/src/textual/tree-sitter/highlights/gomod.scm b/src/textual/tree-sitter/highlights/gomod.scm new file mode 100644 index 0000000000..63e1f0128a --- /dev/null +++ b/src/textual/tree-sitter/highlights/gomod.scm @@ -0,0 +1,17 @@ +[ + "require" + "replace" + "go" + "exclude" + "retract" + "module" +] @keyword + +"=>" @operator + +(comment) @comment + +[ +(version) +(go_version) +] @string diff --git a/src/textual/tree-sitter/highlights/hack.scm b/src/textual/tree-sitter/highlights/hack.scm new file mode 100644 index 0000000000..921c97e432 --- /dev/null +++ b/src/textual/tree-sitter/highlights/hack.scm @@ -0,0 +1,31 @@ +(comment) @comment + +(string) @string +(heredoc) @string +(prefixed_string) @string + +[ + "class" + "interface" + "trait" + "public" + "protected" + "private" + "static" + "async" + "function" + "return" + "if" + "else" + "elseif" + "while" + "for" + "foreach" + "break" + "continue" + "type" + "new" + "throw" +] @keyword + +(type_specifier) @type diff --git a/src/textual/tree-sitter/highlights/haskell.scm b/src/textual/tree-sitter/highlights/haskell.scm new file mode 100644 index 0000000000..3ab843e833 --- /dev/null +++ b/src/textual/tree-sitter/highlights/haskell.scm @@ -0,0 +1,156 @@ +;; Copyright 2022 nvim-treesitter +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. + +;; ---------------------------------------------------------------------------- +;; Literals and comments + +(integer) @number +(exp_negation) @number +(exp_literal (float)) @float +(char) @character +(string) @string + +(con_unit) @symbol ; unit, as in () + +(comment) @comment + + +;; ---------------------------------------------------------------------------- +;; Punctuation + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + (comma) + ";" +] @punctuation.delimiter + + +;; ---------------------------------------------------------------------------- +;; Keywords, operators, includes + +[ + "forall" + "∀" +] @repeat + +(pragma) @constant.macro + +[ + "if" + "then" + "else" + "case" + "of" +] @conditional + +(exp_lambda_cases "\\" ("cases" @conditional)) + +[ + "import" + "qualified" + "module" +] @include + +[ + (operator) + (constructor_operator) + (type_operator) + (tycon_arrow) + (qualified_module) ; grabs the `.` (dot), ex: import System.IO + (all_names) + (wildcard) + "=" + "|" + "::" + "=>" + "->" + "<-" + "\\" + "`" + "@" +] @operator + +(module) @namespace + +[ + (where) + "let" + "in" + "class" + "instance" + "data" + "newtype" + "family" + "type" + "as" + "hiding" + "deriving" + "via" + "stock" + "anyclass" + "do" + "mdo" + "rec" + "infix" + "infixl" + "infixr" +] @keyword + + +;; ---------------------------------------------------------------------------- +;; Functions and variables + +(variable) @variable +(pat_wildcard) @variable + +(signature name: (variable) @type) +(function + name: (variable) @function + patterns: (patterns)) +((signature (fun)) . (function (variable) @function)) +((signature (context (fun))) . (function (variable) @function)) +((signature (forall (context (fun)))) . (function (variable) @function)) + +(exp_infix (variable) @operator) ; consider infix functions as operators + +(exp_infix (exp_name) @function (#set! "priority" 101)) +(exp_apply . (exp_name (variable) @function)) +(exp_apply . (exp_name (qualified_variable (variable) @function))) + + +;; ---------------------------------------------------------------------------- +;; Types + +(type) @type +(type_variable) @type + +(constructor) @constructor + +; True or False +((constructor) @_bool (#match? @_bool "(True|False)")) @boolean + + +;; ---------------------------------------------------------------------------- +;; Quasi-quotes + +(quoter) @function +; Highlighting of quasiquote_body is handled by injections.scm diff --git a/src/textual/tree-sitter/highlights/java.scm b/src/textual/tree-sitter/highlights/java.scm new file mode 100644 index 0000000000..301245b09a --- /dev/null +++ b/src/textual/tree-sitter/highlights/java.scm @@ -0,0 +1,142 @@ +; Methods + +(method_declaration + name: (identifier) @function.method) +(method_invocation + name: (identifier) @function.method) +(super) @function.builtin + +; Annotations + +(annotation + name: (identifier) @attribute) +(marker_annotation + name: (identifier) @attribute) + +"@" @operator + +; Types + +(type_identifier) @type + +(interface_declaration + name: (identifier) @type) +(class_declaration + name: (identifier) @type) +(enum_declaration + name: (identifier) @type) + +((field_access + object: (identifier) @type) + (#match? @type "^[A-Z]")) +((scoped_identifier + scope: (identifier) @type) + (#match? @type "^[A-Z]")) +((method_invocation + object: (identifier) @type) + (#match? @type "^[A-Z]")) +((method_reference + . (identifier) @type) + (#match? @type "^[A-Z]")) + +(constructor_declaration + name: (identifier) @type) + +[ + (boolean_type) + (integral_type) + (floating_point_type) + (floating_point_type) + (void_type) +] @type.builtin + +; Variables + +((identifier) @constant + (#match? @constant "^_*[A-Z][A-Z\\d_]+$")) + +(identifier) @variable + +(this) @variable.builtin + +; Literals + +[ + (hex_integer_literal) + (decimal_integer_literal) + (octal_integer_literal) + (decimal_floating_point_literal) + (hex_floating_point_literal) +] @number + +[ + (character_literal) + (string_literal) +] @string +(escape_sequence) @string.escape + +[ + (true) + (false) + (null_literal) +] @constant.builtin + +[ + (line_comment) + (block_comment) +] @comment + +; Keywords + +[ + "abstract" + "assert" + "break" + "case" + "catch" + "class" + "continue" + "default" + "do" + "else" + "enum" + "exports" + "extends" + "final" + "finally" + "for" + "if" + "implements" + "import" + "instanceof" + "interface" + "module" + "native" + "new" + "non-sealed" + "open" + "opens" + "package" + "private" + "protected" + "provides" + "public" + "requires" + "record" + "return" + "sealed" + "static" + "strictfp" + "switch" + "synchronized" + "throw" + "throws" + "to" + "transient" + "transitive" + "try" + "uses" + "volatile" + "while" + "with" +] @keyword diff --git a/src/textual/tree-sitter/highlights/javascript.scm b/src/textual/tree-sitter/highlights/javascript.scm new file mode 100644 index 0000000000..613a49a86f --- /dev/null +++ b/src/textual/tree-sitter/highlights/javascript.scm @@ -0,0 +1,205 @@ +; Special identifiers +;-------------------- + +([ + (identifier) + (shorthand_property_identifier) + (shorthand_property_identifier_pattern) + ] @constant + (#match? @constant "^[A-Z_][A-Z\\d_]+$")) + + +((identifier) @constructor + (#match? @constructor "^[A-Z]")) + +((identifier) @variable.builtin + (#match? @variable.builtin "^(arguments|module|console|window|document)$") + (#is-not? local)) + +((identifier) @function.builtin + (#eq? @function.builtin "require") + (#is-not? local)) + +; Function and method definitions +;-------------------------------- + +(function + name: (identifier) @function) +(function_declaration + name: (identifier) @function) +(method_definition + name: (property_identifier) @function.method) + +(pair + key: (property_identifier) @function.method + value: [(function) (arrow_function)]) + +(assignment_expression + left: (member_expression + property: (property_identifier) @function.method) + right: [(function) (arrow_function)]) + +(variable_declarator + name: (identifier) @function + value: [(function) (arrow_function)]) + +(assignment_expression + left: (identifier) @function + right: [(function) (arrow_function)]) + +; Function and method calls +;-------------------------- + +(call_expression + function: (identifier) @function) + +(call_expression + function: (member_expression + property: (property_identifier) @function.method)) + +; Variables +;---------- + +(identifier) @variable + +; Properties +;----------- + +(property_identifier) @property + +; Literals +;--------- + +(this) @variable.builtin +(super) @variable.builtin + +[ + (true) + (false) + (null) + (undefined) +] @constant.builtin + +(comment) @comment + +[ + (string) + (template_string) +] @string + +(regex) @string.special +(number) @number + +; Tokens +;------- + +(template_substitution + "${" @punctuation.special + "}" @punctuation.special) @embedded + +[ + ";" + (optional_chain) + "." + "," +] @punctuation.delimiter + +[ + "-" + "--" + "-=" + "+" + "++" + "+=" + "*" + "*=" + "**" + "**=" + "/" + "/=" + "%" + "%=" + "<" + "<=" + "<<" + "<<=" + "=" + "==" + "===" + "!" + "!=" + "!==" + "=>" + ">" + ">=" + ">>" + ">>=" + ">>>" + ">>>=" + "~" + "^" + "&" + "|" + "^=" + "&=" + "|=" + "&&" + "||" + "??" + "&&=" + "||=" + "??=" +] @operator + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + "as" + "async" + "await" + "break" + "case" + "catch" + "class" + "const" + "continue" + "debugger" + "default" + "delete" + "do" + "else" + "export" + "extends" + "finally" + "for" + "from" + "function" + "get" + "if" + "import" + "in" + "instanceof" + "let" + "new" + "of" + "return" + "set" + "static" + "switch" + "target" + "throw" + "try" + "typeof" + "var" + "void" + "while" + "with" + "yield" +] @keyword diff --git a/src/textual/tree-sitter/highlights/jsdoc.scm b/src/textual/tree-sitter/highlights/jsdoc.scm new file mode 100644 index 0000000000..4b4266c9fd --- /dev/null +++ b/src/textual/tree-sitter/highlights/jsdoc.scm @@ -0,0 +1,2 @@ +(tag_name) @keyword +(type) @type diff --git a/src/textual/tree-sitter/highlights/kotlin.scm b/src/textual/tree-sitter/highlights/kotlin.scm new file mode 100644 index 0000000000..d2e15a68ee --- /dev/null +++ b/src/textual/tree-sitter/highlights/kotlin.scm @@ -0,0 +1,380 @@ +;; Based on the nvim-treesitter highlighting, which is under the Apache license. +;; See https://github.com/nvim-treesitter/nvim-treesitter/blob/f8ab59861eed4a1c168505e3433462ed800f2bae/queries/kotlin/highlights.scm +;; +;; The only difference in this file is that queries using #lua-match? +;; have been removed. + +;;; Identifiers + +(simple_identifier) @variable + +; `it` keyword inside lambdas +; FIXME: This will highlight the keyword outside of lambdas since tree-sitter +; does not allow us to check for arbitrary nestation +((simple_identifier) @variable.builtin +(#eq? @variable.builtin "it")) + +; `field` keyword inside property getter/setter +; FIXME: This will highlight the keyword outside of getters and setters +; since tree-sitter does not allow us to check for arbitrary nestation +((simple_identifier) @variable.builtin +(#eq? @variable.builtin "field")) + +; `this` this keyword inside classes +(this_expression) @variable.builtin + +; `super` keyword inside classes +(super_expression) @variable.builtin + +(class_parameter + (simple_identifier) @property) + +(class_body + (property_declaration + (variable_declaration + (simple_identifier) @property))) + +; id_1.id_2.id_3: `id_2` and `id_3` are assumed as object properties +(_ + (navigation_suffix + (simple_identifier) @property)) + +(enum_entry + (simple_identifier) @constant) + +(type_identifier) @type + +((type_identifier) @type.builtin + (#any-of? @type.builtin + "Byte" + "Short" + "Int" + "Long" + "UByte" + "UShort" + "UInt" + "ULong" + "Float" + "Double" + "Boolean" + "Char" + "String" + "Array" + "ByteArray" + "ShortArray" + "IntArray" + "LongArray" + "UByteArray" + "UShortArray" + "UIntArray" + "ULongArray" + "FloatArray" + "DoubleArray" + "BooleanArray" + "CharArray" + "Map" + "Set" + "List" + "EmptyMap" + "EmptySet" + "EmptyList" + "MutableMap" + "MutableSet" + "MutableList" +)) + +(package_header + . (identifier)) @namespace + +(import_header + "import" @include) + + +; TODO: Seperate labeled returns/breaks/continue/super/this +; Must be implemented in the parser first +(label) @label + +;;; Function definitions + +(function_declaration + . (simple_identifier) @function) + +(getter + ("get") @function.builtin) +(setter + ("set") @function.builtin) + +(primary_constructor) @constructor +(secondary_constructor + ("constructor") @constructor) + +(constructor_invocation + (user_type + (type_identifier) @constructor)) + +(anonymous_initializer + ("init") @constructor) + +(parameter + (simple_identifier) @parameter) + +(parameter_with_optional_type + (simple_identifier) @parameter) + +; lambda parameters +(lambda_literal + (lambda_parameters + (variable_declaration + (simple_identifier) @parameter))) + +;;; Function calls + +; function() +(call_expression + . (simple_identifier) @function) + +; object.function() or object.property.function() +(call_expression + (navigation_expression + (navigation_suffix + (simple_identifier) @function) . )) + +(call_expression + . (simple_identifier) @function.builtin + (#any-of? @function.builtin + "arrayOf" + "arrayOfNulls" + "byteArrayOf" + "shortArrayOf" + "intArrayOf" + "longArrayOf" + "ubyteArrayOf" + "ushortArrayOf" + "uintArrayOf" + "ulongArrayOf" + "floatArrayOf" + "doubleArrayOf" + "booleanArrayOf" + "charArrayOf" + "emptyArray" + "mapOf" + "setOf" + "listOf" + "emptyMap" + "emptySet" + "emptyList" + "mutableMapOf" + "mutableSetOf" + "mutableListOf" + "print" + "println" + "error" + "TODO" + "run" + "runCatching" + "repeat" + "lazy" + "lazyOf" + "enumValues" + "enumValueOf" + "assert" + "check" + "checkNotNull" + "require" + "requireNotNull" + "with" + "suspend" + "synchronized" +)) + +;;; Literals + +[ + (line_comment) + (multiline_comment) + (shebang_line) +] @comment + +(real_literal) @float +[ + (integer_literal) + (long_literal) + (hex_literal) + (bin_literal) + (unsigned_literal) +] @number + +[ + "null" ; should be highlighted the same as booleans + (boolean_literal) +] @boolean + +(character_literal) @character + +(string_literal) @string + +(character_escape_seq) @string.escape + +; There are 3 ways to define a regex +; - "[abc]?".toRegex() +(call_expression + (navigation_expression + ((string_literal) @string.regex) + (navigation_suffix + ((simple_identifier) @_function + (#eq? @_function "toRegex"))))) + +; - Regex("[abc]?") +(call_expression + ((simple_identifier) @_function + (#eq? @_function "Regex")) + (call_suffix + (value_arguments + (value_argument + (string_literal) @string.regex)))) + +; - Regex.fromLiteral("[abc]?") +(call_expression + (navigation_expression + ((simple_identifier) @_class + (#eq? @_class "Regex")) + (navigation_suffix + ((simple_identifier) @_function + (#eq? @_function "fromLiteral")))) + (call_suffix + (value_arguments + (value_argument + (string_literal) @string.regex)))) + +;;; Keywords + +(type_alias "typealias" @keyword) +[ + (class_modifier) + (member_modifier) + (function_modifier) + (property_modifier) + (platform_modifier) + (variance_modifier) + (parameter_modifier) + (visibility_modifier) + (reification_modifier) + (inheritance_modifier) +]@keyword + +[ + "val" + "var" + "enum" + "class" + "object" + "interface" +; "typeof" ; NOTE: It is reserved for future use +] @keyword + +("fun") @keyword.function + +(jump_expression) @keyword.return + +[ + "if" + "else" + "when" +] @conditional + +[ + "for" + "do" + "while" +] @repeat + +[ + "try" + "catch" + "throw" + "finally" +] @exception + + +(annotation + "@" @attribute (use_site_target)? @attribute) +(annotation + (user_type + (type_identifier) @attribute)) +(annotation + (constructor_invocation + (user_type + (type_identifier) @attribute))) + +(file_annotation + "@" @attribute "file" @attribute ":" @attribute) +(file_annotation + (user_type + (type_identifier) @attribute)) +(file_annotation + (constructor_invocation + (user_type + (type_identifier) @attribute))) + +;;; Operators & Punctuation + +[ + "!" + "!=" + "!==" + "=" + "==" + "===" + ">" + ">=" + "<" + "<=" + "||" + "&&" + "+" + "++" + "+=" + "-" + "--" + "-=" + "*" + "*=" + "/" + "/=" + "%" + "%=" + "?." + "?:" + "!!" + "is" + "!is" + "in" + "!in" + "as" + "as?" + ".." + "->" +] @operator + +[ + "(" ")" + "[" "]" + "{" "}" +] @punctuation.bracket + +[ + "." + "," + ";" + ":" + "::" +] @punctuation.delimiter + +; NOTE: `interpolated_identifier`s can be highlighted in any way +(string_literal + "$" @punctuation.special + (interpolated_identifier) @none) +(string_literal + "${" @punctuation.special + (interpolated_expression) @none + "}" @punctuation.special) diff --git a/src/textual/tree-sitter/highlights/make.scm b/src/textual/tree-sitter/highlights/make.scm new file mode 100644 index 0000000000..9ea8016e59 --- /dev/null +++ b/src/textual/tree-sitter/highlights/make.scm @@ -0,0 +1,171 @@ +[ + "(" + ")" + "{" + "}" +] @punctuation.bracket + +[ + ":" + "&:" + "::" + "|" + ";" + "\"" + "'" + "," +] @punctuation.delimiter + +[ + "$" + "$$" +] @punctuation.special + +(automatic_variable + [ "@" "%" "<" "?" "^" "+" "/" "*" "D" "F"] @punctuation.special) + +(automatic_variable + "/" @error . ["D" "F"]) + +[ + "=" + ":=" + "::=" + "?=" + "+=" + "!=" + "@" + "-" + "+" +] @operator + +[ + (text) + (string) + (raw_text) +] @string + +(variable_assignment (word) @string) + +[ + "ifeq" + "ifneq" + "ifdef" + "ifndef" + "else" + "endif" + "if" + "or" ; boolean functions are conditional in make grammar + "and" +] @conditional + +"foreach" @repeat + +[ + "define" + "endef" + "vpath" + "undefine" + "export" + "unexport" + "override" + "private" +; "load" +] @keyword + +[ + "include" + "sinclude" + "-include" +] @include + +[ + "subst" + "patsubst" + "strip" + "findstring" + "filter" + "filter-out" + "sort" + "word" + "words" + "wordlist" + "firstword" + "lastword" + "dir" + "notdir" + "suffix" + "basename" + "addsuffix" + "addprefix" + "join" + "wildcard" + "realpath" + "abspath" + "call" + "eval" + "file" + "value" + "shell" +] @keyword.function + +[ + "error" + "warning" + "info" +] @exception + +;; Variable +(variable_assignment + name: (word) @constant) + +(variable_reference + (word) @constant) + +(comment) @comment + +((word) @clean @string.regex + (#match? @clean "[%\*\?]")) + +(function_call + function: "error" + (arguments (text) @text.danger)) + +(function_call + function: "warning" + (arguments (text) @text.warning)) + +(function_call + function: "info" + (arguments (text) @text.note)) + +;; Install Command Categories +;; Others special variables +;; Variables Used by Implicit Rules +[ + "VPATH" + ".RECIPEPREFIX" +] @constant.builtin + +(variable_assignment + name: (word) @clean @constant.builtin + (#match? @clean "^(AR|AS|CC|CXX|CPP|FC|M2C|PC|CO|GET|LEX|YACC|LINT|MAKEINFO|TEX|TEXI2DVI|WEAVE|CWEAVE|TANGLE|CTANGLE|RM|ARFLAGS|ASFLAGS|CFLAGS|CXXFLAGS|COFLAGS|CPPFLAGS|FFLAGS|GFLAGS|LDFLAGS|LDLIBS|LFLAGS|YFLAGS|PFLAGS|RFLAGS|LINTFLAGS|PRE_INSTALL|POST_INSTALL|NORMAL_INSTALL|PRE_UNINSTALL|POST_UNINSTALL|NORMAL_UNINSTALL|MAKEFILE_LIST|MAKE_RESTARTS|MAKE_TERMOUT|MAKE_TERMERR|\.DEFAULT_GOAL|\.RECIPEPREFIX|\.EXTRA_PREREQS)$")) + +(variable_reference + (word) @clean @constant.builtin + (#match? @clean "^(AR|AS|CC|CXX|CPP|FC|M2C|PC|CO|GET|LEX|YACC|LINT|MAKEINFO|TEX|TEXI2DVI|WEAVE|CWEAVE|TANGLE|CTANGLE|RM|ARFLAGS|ASFLAGS|CFLAGS|CXXFLAGS|COFLAGS|CPPFLAGS|FFLAGS|GFLAGS|LDFLAGS|LDLIBS|LFLAGS|YFLAGS|PFLAGS|RFLAGS|LINTFLAGS|PRE_INSTALL|POST_INSTALL|NORMAL_INSTALL|PRE_UNINSTALL|POST_UNINSTALL|NORMAL_UNINSTALL|MAKEFILE_LIST|MAKE_RESTARTS|MAKE_TERMOUT|MAKE_TERMERR|\.DEFAULT_GOAL|\.RECIPEPREFIX|\.EXTRA_PREREQS\.VARIABLES|\.FEATURES|\.INCLUDE_DIRS|\.LOADED)$")) + +;; Standart targets +(targets + (word) @constant.macro + (#match? @constant.macro "^(all|install|install-html|install-dvi|install-pdf|install-ps|uninstall|install-strip|clean|distclean|mostlyclean|maintainer-clean|TAGS|info|dvi|html|pdf|ps|dist|check|installcheck|installdirs)$")) + +(targets + (word) @constant.macro + (#match? @constant.macro "^(all|install|install-html|install-dvi|install-pdf|install-ps|uninstall|install-strip|clean|distclean|mostlyclean|maintainer-clean|TAGS|info|dvi|html|pdf|ps|dist|check|installcheck|installdirs)$")) + +;; Builtin targets +(targets + (word) @constant.macro + (#match? @constant.macro "^\.(PHONY|SUFFIXES|DEFAULT|PRECIOUS|INTERMEDIATE|SECONDARY|SECONDEXPANSION|DELETE_ON_ERROR|IGNORE|LOW_RESOLUTION_TIME|SILENT|EXPORT_ALL_VARIABLES|NOTPARALLEL|ONESHELL|POSIX)$")) + diff --git a/src/textual/tree-sitter/highlights/objc.scm b/src/textual/tree-sitter/highlights/objc.scm new file mode 100644 index 0000000000..ff4c8faa5a --- /dev/null +++ b/src/textual/tree-sitter/highlights/objc.scm @@ -0,0 +1,395 @@ +[ + (comment) + (pragma) +] @comment + +[ + (self) + (super) +] @variable.builtin + +[ + (getter) + (setter) + (nonnull) + (nullable) + (null_resettable) + (unsafe_unretained) + (null_unspecified) + (direct) + (readwrite) + (readonly) + (strong) + (weak) + (copy) + (assign) + (retain) + (atomic) + (nonatomic) + (class) + (NS_NONATOMIC_IOSONLY) + (DISPATCH_QUEUE_REFERENCE_TYPE) +] @keyword + +[ + "@interface" + "@protocol" + "@property" + "@end" + "@implementation" + "@compatibility_alias" + "@autoreleasepool" + "@synchronized" + "@class" + "@synthesize" + "@dynamic" + "@defs" + "@try" + "@catch" + "@finally" + "@throw" + "@selector" + "@encode" + (private) + (public) + (protected) + (package) + (optional) + (required) + "NS_ENUM" + "NS_ERROR_ENUM" + "NS_OPTIONS" + "NS_SWIFT_NAME" + (type_qualifier) + (storage_class_specifier) + "NS_NOESCAPE" + "const" + "default" + "enum" + "extern" + "inline" + "static" + "struct" + "typedef" + "typeof" + "__typeof" + "__typeof__" + "_Atomic" + "union" + "volatile" + "goto" + "register" + "__covariant" + "__contravariant" + "__GENERICS" +] @keyword + +"sizeof" @keyword.operator +"return" @keyword.return + +[ + "while" + "for" + "do" + "continue" + "break" +] @keyword.repeat + +"#define" @constant.macro + +[ + "#if" + "#ifdef" + "#ifndef" + "#else" + "#elif" + "#endif" + (preproc_directive) +] @keyword + +"#include" @include +"#import" @include +"@import" @include + +[ + "=" + + "-" + "*" + "/" + "+" + "%" + + "~" + "|" + "&" + "^" + "<<" + ">>" + + "->" + + "<" + "<=" + ">=" + ">" + "==" + "!=" + + "!" + "&&" + "||" + + "-=" + "+=" + "*=" + "/=" + "%=" + "|=" + "&=" + "^=" + ">>=" + "<<=" + "--" + "++" + "@" +] @operator + +[ + "if" + "else" + "case" + "switch" +] @keyword.conditional + +(conditional_expression [ "?" ":" ] @keyword.conditional) + +[ + (true) + (false) + (YES) + (NO) +] @keyword.boolean + +[ "." ";" ":" "," ] @punctuation.delimiter + +"..." @punctuation.special + +[ "(" ")" "[" "]" "{" "}"] @punctuation.bracket + +[ + (string_literal) + (string_expression) + (system_lib_string) + (module_string) +] @string + +(escape_sequence) @string.escape + +(null) @constant.builtin +(nil) @constant.builtin +(number_literal) @number +(number_expression) @number +(char_literal) @character + +[ + (preproc_arg) + (preproc_defined) +] @function.macro + +[ + (type_identifier) + (primitive_type) + (sized_type_specifier) + (type_descriptor) + (generics_type_reference) +] @type + +[ + (id) + (Class) + (Method) + (IMP) + (SEL) + (BOOL) + (instancetype) + (auto) +] @type.builtin + +(declaration (type_qualifier) @type) +(cast_expression type: (type_descriptor) @type) +(sizeof_expression value: (parenthesized_expression (identifier) @type)) + +;; Type Class & Category & Protocol +(class_interface name: (identifier) @type.class) +(category_interface name: (identifier) @type.class) +(category_interface category: (identifier) @type.category) +(superclass_reference name: (identifier) @type.class) +(parameterized_class_type_arguments) @type.class +(class_implementation name: (identifier) @type.class) +(category_implementation name: (identifier) @type.class) +(compatibility_alias_declaration (identifier) @type.class) +(parameterized_class_type_arguments (identifier) @type.class) +(category_implementation category: (identifier) @type.category) +(class_forward_declaration name: (identifier) @type.class) +(protocol_forward_declaration name: (identifier) @type.protocol) +(protocol_declaration name: (identifier) @type.protocol) +(protocol_qualifiers name: (identifier) @type.protocol) +(protocol_expression (identifier) @type.protocol) + +;; Preproc def / undef +(preproc_def + name: (_) @constant) +(preproc_call + directive: (preproc_directive) @_u + argument: (_) @constant + (#eq? @_u "#undef")) + +;; Property +(property_declaration + type: _ @type + declarator: (identifier) @property) + +(property_declaration + type: _ @type + declarator: (_ + declarator: (identifier) @property)) + +(property_declaration + type: _ @type + declarator: (_ + declarator: (_ + declarator: (identifier) @property))) + +(((field_expression + (field_identifier) @property)) @_parent + (#not-has-parent? @_parent function_declarator call_expression)) + +(field_expression + field: (field_identifier) @property) + +(((field_identifier) @property) + (#has-ancestor? @property field_declaration) + (#not-has-ancestor? @property function_declarator)) + +;; Variable +declarator: (identifier) @variable + +(cast_expression value: (identifier) @variable) + +;; Function +(call_expression + function: (identifier) @function) +(function_declarator + declarator: (identifier) @function) +(preproc_function_def + name: (identifier) @function.macro) +(selector_expression + name: (identifier) @function) +(method_declaration + selector: (identifier) @function) + +(method_declaration + (keyword_selector + (keyword_declarator + keyword: (identifier) @function))) + +(method_declaration + (keyword_selector + (keyword_declarator + name: (identifier) @variable.parameter))) + +(message_expression + receiver: (field_expression + field: (field_identifier) @function)) + +(method_definition + selector: (identifier) @function) + +(swift_name_attribute_sepcifier + method: (identifier) @function) + +(setter + name: (identifier) @function) + +(method_definition + (keyword_selector + (keyword_declarator + keyword: (identifier) @function))) + +(message_expression + selector: (identifier) @function) + +(method_definition + (keyword_selector + (keyword_declarator + name: (identifier) @variable.parameter))) + +(message_expression + selector: (keyword_argument_list + (keyword_argument + keyword: (identifier) @function))) + +(message_expression + selector: (keyword_argument_list + (keyword_argument + argument: (identifier) @variable.parameter))) + +(unary_expression argument: (identifier) @function) +(va_arg_expression) @function +(va_arg_expression va_list: (identifier) @variable) +(enumerator name: (identifier) @variable) + + +;; Parameters +(parameter_declaration + declarator: (identifier) @variable.parameter) + +(parameter_declaration + declarator: (pointer_declarator) @variable.parameter) + +(parameter_declaration + declarator: (pointer_declarator + declarator: (identifier) @variable.parameter)) + +(for_in_statement + loop: (identifier) @variable) + +(dictionary_expression + key: (_expression) @variable) +(dictionary_expression + value: (_expression) @variable) +(array_expression + (identifier) @variable) +(argument_list + (identifier) @variable) +(expression_statement + (identifier) @variable) +(_expression (identifier) @variable) + +[ + "__attribute" + "__attribute__" + "__cdecl" + "__clrcall" + "__stdcall" + "__fastcall" + "__thiscall" + "__vectorcall" + "_unaligned" + "__unaligned" + "__declspec" + "__unused" + "__builtin_available" + "@available" + (attribute_specifier) + (class_interface_attribute_sepcifier) + (method_variadic_arguments_attribute_specifier) +] @attribute + +(attribute_specifier) @attribute + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z0-9_$]+$")) + +(ERROR) @error \ No newline at end of file diff --git a/src/textual/tree-sitter/highlights/ocaml.scm b/src/textual/tree-sitter/highlights/ocaml.scm new file mode 100644 index 0000000000..fbd0e082e8 --- /dev/null +++ b/src/textual/tree-sitter/highlights/ocaml.scm @@ -0,0 +1,151 @@ +; Modules +;-------- + +[(module_name) (module_type_name)] @module + +; Types +;------ + +( + (type_constructor) @type.builtin + (#match? @type.builtin "^(int|char|bytes|string|float|bool|unit|exn|array|list|option|int32|int64|nativeint|format6|lazy_t)$") +) + +[(class_name) (class_type_name) (type_constructor)] @type + +[(constructor_name) (tag)] @constructor + +; Functions +;---------- + +(let_binding + pattern: (value_name) @function + (parameter)) + +(let_binding + pattern: (value_name) @function + body: [(fun_expression) (function_expression)]) + +(value_specification (value_name) @function) + +(external (value_name) @function) + +(method_name) @function.method + +; Application +;------------ + +( + (value_name) @function.builtin + (#match? @function.builtin "^(raise(_notrace)?|failwith|invalid_arg)$") +) + +(infix_expression + left: (value_path (value_name) @function) + operator: (concat_operator) @operator + (#eq? @operator "@@")) + +(infix_expression + operator: (rel_operator) @operator + right: (value_path (value_name) @function) + (#eq? @operator "|>")) + +(application_expression + function: (value_path (value_name) @function)) + +; Variables +;---------- + +[(value_name) (type_variable)] @variable + +(value_pattern) @variable.parameter + +; Properties +;----------- + +[(label_name) (field_name) (instance_variable_name)] @property + +; Constants +;---------- + +(boolean) @constant + +[(number) (signed_number)] @number + +[(string) (character)] @string + +(quoted_string "{" @string "}" @string) @string + +(escape_sequence) @escape + +(conversion_specification) @string.special + +; Operators +;---------- + +(match_expression (match_operator) @keyword) + +(value_definition [(let_operator) (let_and_operator)] @keyword) + +[ + (prefix_operator) + (sign_operator) + (pow_operator) + (mult_operator) + (add_operator) + (concat_operator) + (rel_operator) + (and_operator) + (or_operator) + (assign_operator) + (hash_operator) + (indexing_operator) + (let_operator) + (let_and_operator) + (match_operator) +] @operator + +["*" "#" "::" "<-"] @operator + +; Keywords +;--------- + +[ + "and" "as" "assert" "begin" "class" "constraint" "do" "done" "downto" "else" + "end" "exception" "external" "for" "fun" "function" "functor" "if" "in" + "include" "inherit" "initializer" "lazy" "let" "match" "method" "module" + "mutable" "new" "nonrec" "object" "of" "open" "private" "rec" "sig" "struct" + "then" "to" "try" "type" "val" "virtual" "when" "while" "with" +] @keyword + +; Punctuation +;------------ + +(attribute ["[@" "]"] @punctuation.special) +(item_attribute ["[@@" "]"] @punctuation.special) +(floating_attribute ["[@@@" "]"] @punctuation.special) +(extension ["[%" "]"] @punctuation.special) +(item_extension ["[%%" "]"] @punctuation.special) +(quoted_extension ["{%" "}"] @punctuation.special) +(quoted_item_extension ["{%%" "}"] @punctuation.special) + +"%" @punctuation.special + +["(" ")" "[" "]" "{" "}" "[|" "|]" "[<" "[>"] @punctuation.bracket + +(object_type ["<" ">"] @punctuation.bracket) + +[ + "," "." ";" ":" "=" "|" "~" "?" "+" "-" "!" ">" "&" + "->" ";;" ":>" "+=" ":=" ".." +] @punctuation.delimiter + +; Attributes +;----------- + +(attribute_id) @tag + +; Comments +;--------- + +[(comment) (line_number_directive) (directive) (shebang)] @comment diff --git a/src/textual/tree-sitter/highlights/php.scm b/src/textual/tree-sitter/highlights/php.scm new file mode 100644 index 0000000000..666a49be2a --- /dev/null +++ b/src/textual/tree-sitter/highlights/php.scm @@ -0,0 +1,122 @@ +(php_tag) @tag +"?>" @tag + +; Types + +(primitive_type) @type.builtin +(cast_type) @type.builtin +(named_type (name) @type) @type +(named_type (qualified_name) @type) @type + +; Functions + +(array_creation_expression "array" @function.builtin) +(list_literal "list" @function.builtin) + +(method_declaration + name: (name) @function.method) + +(function_call_expression + function: [(qualified_name (name)) (name)] @function) + +(scoped_call_expression + name: (name) @function) + +(member_call_expression + name: (name) @function.method) + +(function_definition + name: (name) @function) + +; Member + +(property_element + (variable_name) @property) + +(member_access_expression + name: (variable_name (name)) @property) +(member_access_expression + name: (name) @property) + +; Variables + +(relative_scope) @variable.builtin + +((name) @constant + (#match? @constant "^_?[A-Z][A-Z\\d_]+$")) +((name) @constant.builtin + (#match? @constant.builtin "^__[A-Z][A-Z\d_]+__$")) + +((name) @constructor + (#match? @constructor "^[A-Z]")) + +((name) @variable.builtin + (#eq? @variable.builtin "this")) + +(variable_name) @variable + +; Basic tokens +[ + (string) + (string_value) + (encapsed_string) + (heredoc) + (heredoc_body) + (nowdoc_body) +] @string +(boolean) @constant.builtin +(null) @constant.builtin +(integer) @number +(float) @number +(comment) @comment + +"$" @operator + +; Keywords + +"abstract" @keyword +"as" @keyword +"break" @keyword +"case" @keyword +"catch" @keyword +"class" @keyword +"const" @keyword +"continue" @keyword +"declare" @keyword +"default" @keyword +"do" @keyword +"echo" @keyword +"else" @keyword +"elseif" @keyword +"enddeclare" @keyword +"endforeach" @keyword +"endif" @keyword +"endswitch" @keyword +"endwhile" @keyword +"extends" @keyword +"final" @keyword +"finally" @keyword +"foreach" @keyword +"function" @keyword +"global" @keyword +"if" @keyword +"implements" @keyword +"include_once" @keyword +"include" @keyword +"insteadof" @keyword +"interface" @keyword +"namespace" @keyword +"new" @keyword +"private" @keyword +"protected" @keyword +"public" @keyword +"require_once" @keyword +"require" @keyword +"return" @keyword +"static" @keyword +"switch" @keyword +"throw" @keyword +"trait" @keyword +"try" @keyword +"use" @keyword +"while" @keyword diff --git a/src/textual/tree-sitter/highlights/r.scm b/src/textual/tree-sitter/highlights/r.scm new file mode 100644 index 0000000000..198d1ead8b --- /dev/null +++ b/src/textual/tree-sitter/highlights/r.scm @@ -0,0 +1,128 @@ +; highlights.scm + + +; Literals + +(integer) @number + +(float) @float + +(complex) @number + +(string) @string +(string (escape_sequence) @string.escape) + +(comment) @comment + +(identifier) @variable + +(formal_parameters (identifier) @parameter) +(formal_parameters (default_parameter (identifier) @parameter)) + +; Operators +[ + "=" + "<-" + "<<-" + "->>" + "->" +] @operator + +(unary operator: [ + "-" + "+" + "!" + "~" +] @operator) + +(binary operator: [ + "-" + "+" + "*" + "/" + "^" + "<" + ">" + "<=" + ">=" + "==" + "!=" + "||" + "|" + "&&" + "&" + ":" + "~" +] @operator) + +[ + "|>" + (special) +] @operator + +(lambda_function "\\" @operator) + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(dollar "$" @operator) + +(subset2 + [ + "[[" + "]]" + ] @punctuation.bracket) + +[ + "in" + (dots) + (break) + (next) + (inf) +] @keyword + +[ + (nan) + (na) + (null) +] @type.builtin + +[ + "if" + "else" + "switch" +] @conditional + +[ + "while" + "repeat" + "for" +] @repeat + +[ + (true) + (false) +] @boolean + +"function" @keyword.function + +(call function: (identifier) @function) +(default_argument name: (identifier) @parameter) + + +(namespace_get function: (identifier) @method) +(namespace_get_internal function: (identifier) @method) + +(namespace_get namespace: (identifier) @namespace + "::" @operator) +(namespace_get_internal namespace: (identifier) @namespace + ":::" @operator) + +; Error +(ERROR) @error diff --git a/src/textual/tree-sitter/highlights/ruby.scm b/src/textual/tree-sitter/highlights/ruby.scm new file mode 100644 index 0000000000..51d1c0b729 --- /dev/null +++ b/src/textual/tree-sitter/highlights/ruby.scm @@ -0,0 +1,154 @@ +; Keywords + +[ + "alias" + "and" + "begin" + "break" + "case" + "class" + "def" + "do" + "else" + "elsif" + "end" + "ensure" + "for" + "if" + "in" + "module" + "next" + "or" + "rescue" + "retry" + "return" + "then" + "unless" + "until" + "when" + "while" + "yield" +] @keyword + +((identifier) @keyword + (#match? @keyword "^(private|protected|public)$")) + +; Function calls + +((identifier) @function.method.builtin + (#eq? @function.method.builtin "require")) + +"defined?" @function.method.builtin + +(call + method: [(identifier) (constant)] @function.method) + +; Function definitions + +(alias (identifier) @function.method) +(setter (identifier) @function.method) +(method name: [(identifier) (constant)] @function.method) +(singleton_method name: [(identifier) (constant)] @function.method) + +; Identifiers + +[ + (class_variable) + (instance_variable) +] @property + +((identifier) @constant.builtin + (#match? @constant.builtin "^__(FILE|LINE|ENCODING)__$")) + +(file) @constant.builtin +(line) @constant.builtin +(encoding) @constant.builtin + +(hash_splat_nil + "**" @operator +) @constant.builtin + +((constant) @constant + (#match? @constant "^[A-Z\\d_]+$")) + +(constant) @constructor + +(self) @variable.builtin +(super) @variable.builtin + +(block_parameter (identifier) @variable.parameter) +(block_parameters (identifier) @variable.parameter) +(destructured_parameter (identifier) @variable.parameter) +(hash_splat_parameter (identifier) @variable.parameter) +(lambda_parameters (identifier) @variable.parameter) +(method_parameters (identifier) @variable.parameter) +(splat_parameter (identifier) @variable.parameter) + +(keyword_parameter name: (identifier) @variable.parameter) +(optional_parameter name: (identifier) @variable.parameter) + +((identifier) @function.method + (#is-not? local)) +(identifier) @variable + +; Literals + +[ + (string) + (bare_string) + (subshell) + (heredoc_body) + (heredoc_beginning) +] @string + +[ + (simple_symbol) + (delimited_symbol) + (hash_key_symbol) + (bare_symbol) +] @string.special.symbol + +(regex) @string.special.regex +(escape_sequence) @escape + +[ + (integer) + (float) +] @number + +[ + (nil) + (true) + (false) +]@constant.builtin + +(interpolation + "#{" @punctuation.special + "}" @punctuation.special) @embedded + +(comment) @comment + +; Operators + +[ +"=" +"=>" +"->" +] @operator + +[ + "," + ";" + "." +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "%w(" + "%i(" +] @punctuation.bracket diff --git a/src/textual/tree-sitter/highlights/rust.scm b/src/textual/tree-sitter/highlights/rust.scm new file mode 100644 index 0000000000..c1556847b3 --- /dev/null +++ b/src/textual/tree-sitter/highlights/rust.scm @@ -0,0 +1,155 @@ +; Identifier conventions + +; Assume all-caps names are constants +((identifier) @constant + (#match? @constant "^[A-Z][A-Z\\d_]+$'")) + +; Assume that uppercase names in paths are types +((scoped_identifier + path: (identifier) @type) + (#match? @type "^[A-Z]")) +((scoped_identifier + path: (scoped_identifier + name: (identifier) @type)) + (#match? @type "^[A-Z]")) +((scoped_type_identifier + path: (identifier) @type) + (#match? @type "^[A-Z]")) +((scoped_type_identifier + path: (scoped_identifier + name: (identifier) @type)) + (#match? @type "^[A-Z]")) + +; Assume other uppercase names are enum constructors +((identifier) @constructor + (#match? @constructor "^[A-Z]")) + +; Assume all qualified names in struct patterns are enum constructors. (They're +; either that, or struct names; highlighting both as constructors seems to be +; the less glaring choice of error, visually.) +(struct_pattern + type: (scoped_type_identifier + name: (type_identifier) @constructor)) + +; Function calls + +(call_expression + function: (identifier) @function) +(call_expression + function: (field_expression + field: (field_identifier) @function.method)) +(call_expression + function: (scoped_identifier + "::" + name: (identifier) @function)) + +(generic_function + function: (identifier) @function) +(generic_function + function: (scoped_identifier + name: (identifier) @function)) +(generic_function + function: (field_expression + field: (field_identifier) @function.method)) + +(macro_invocation + macro: (identifier) @function.macro + "!" @function.macro) + +; Function definitions + +(function_item (identifier) @function) +(function_signature_item (identifier) @function) + +; Other identifiers + +(type_identifier) @type +(primitive_type) @type.builtin +(field_identifier) @property + +(line_comment) @comment +(block_comment) @comment + +"(" @punctuation.bracket +")" @punctuation.bracket +"[" @punctuation.bracket +"]" @punctuation.bracket +"{" @punctuation.bracket +"}" @punctuation.bracket + +(type_arguments + "<" @punctuation.bracket + ">" @punctuation.bracket) +(type_parameters + "<" @punctuation.bracket + ">" @punctuation.bracket) + +"::" @punctuation.delimiter +":" @punctuation.delimiter +"." @punctuation.delimiter +"," @punctuation.delimiter +";" @punctuation.delimiter + +(parameter (identifier) @variable.parameter) + +(lifetime (identifier) @label) + +"as" @keyword +"async" @keyword +"await" @keyword +"break" @keyword +"const" @keyword +"continue" @keyword +"default" @keyword +"dyn" @keyword +"else" @keyword +"enum" @keyword +"extern" @keyword +"fn" @keyword +"for" @keyword +"if" @keyword +"impl" @keyword +"in" @keyword +"let" @keyword +"loop" @keyword +"macro_rules!" @keyword +"match" @keyword +"mod" @keyword +"move" @keyword +"pub" @keyword +"ref" @keyword +"return" @keyword +"static" @keyword +"struct" @keyword +"trait" @keyword +"type" @keyword +"union" @keyword +"unsafe" @keyword +"use" @keyword +"where" @keyword +"while" @keyword +(crate) @keyword +(mutable_specifier) @keyword +(use_list (self) @keyword) +(scoped_use_list (self) @keyword) +(scoped_identifier (self) @keyword) +(super) @keyword + +(self) @variable.builtin + +(char_literal) @string +(string_literal) @string +(raw_string_literal) @string + +(boolean_literal) @constant.builtin +(integer_literal) @constant.builtin +(float_literal) @constant.builtin + +(escape_sequence) @escape + +(attribute_item) @attribute +(inner_attribute_item) @attribute + +"*" @operator +"&" @operator +"'" @operator diff --git a/src/textual/tree-sitter/highlights/scala.scm b/src/textual/tree-sitter/highlights/scala.scm new file mode 100644 index 0000000000..ed20b27404 --- /dev/null +++ b/src/textual/tree-sitter/highlights/scala.scm @@ -0,0 +1,261 @@ +; CREDITS @stumash (stuart.mashaal@gmail.com) + +(class_definition + name: (identifier) @type) + +(enum_definition + name: (identifier) @type) + +(object_definition + name: (identifier) @type) + +(trait_definition + name: (identifier) @type) + +(full_enum_case + name: (identifier) @type) + +(simple_enum_case + name: (identifier) @type) + +;; variables + +(class_parameter + name: (identifier) @parameter) + +(self_type (identifier) @parameter) + +(interpolation (identifier) @none) +(interpolation (block) @none) + +;; types + +(type_definition + name: (type_identifier) @type.definition) + +(type_identifier) @type + +;; val/var definitions/declarations + +(val_definition + pattern: (identifier) @variable) + +(var_definition + pattern: (identifier) @variable) + +(val_declaration + name: (identifier) @variable) + +(var_declaration + name: (identifier) @variable) + +; method definition + +(function_declaration + name: (identifier) @method) + +(function_definition + name: (identifier) @method) + +; imports/exports + +(import_declaration + path: (identifier) @namespace) +((stable_identifier (identifier) @namespace)) + +((import_declaration + path: (identifier) @type) (#match? @type "^[A-Z]")) +((stable_identifier (identifier) @type) (#match? @type "^[A-Z]")) + +(export_declaration + path: (identifier) @namespace) +((stable_identifier (identifier) @namespace)) + +((export_declaration + path: (identifier) @type) (#match? @type "^[A-Z]")) +((stable_identifier (identifier) @type) (#match? @type "^[A-Z]")) + +((namespace_selectors (identifier) @type) (#match? @type "^[A-Z]")) + +; method invocation + +(call_expression + function: (identifier) @function.call) + +(call_expression + function: (operator_identifier) @function.call) + +(call_expression + function: (field_expression + field: (identifier) @method.call)) + +((call_expression + function: (identifier) @constructor) + (#match? @constructor "^[A-Z]")) + +(generic_function + function: (identifier) @function.call) + +(interpolated_string_expression + interpolator: (identifier) @function.call) + +; function definitions + +(function_definition + name: (identifier) @function) + +(parameter + name: (identifier) @parameter) + +(binding + name: (identifier) @parameter) + +; expressions + +(field_expression field: (identifier) @property) +(field_expression value: (identifier) @type + (#match? @type "^[A-Z]")) + +(infix_expression operator: (identifier) @operator) +(infix_expression operator: (operator_identifier) @operator) +(infix_type operator: (operator_identifier) @operator) +(infix_type operator: (operator_identifier) @operator) + +; literals + +(boolean_literal) @boolean +(integer_literal) @number +(floating_point_literal) @float + +[ + (symbol_literal) + (string) + (character_literal) + (interpolated_string_expression) +] @string + +(interpolation "$" @punctuation.special) + +;; keywords + +(opaque_modifier) @type.qualifier +(infix_modifier) @keyword +(transparent_modifier) @type.qualifier +(open_modifier) @type.qualifier + +[ + "case" + "class" + "enum" + "extends" + "derives" + "finally" +;; `forSome` existential types not implemented yet +;; `macro` not implemented yet + "object" + "override" + "package" + "trait" + "type" + "val" + "var" + "with" + "given" + "using" + "end" + "implicit" + "extension" + "with" +] @keyword + +[ + "abstract" + "final" + "lazy" + "sealed" + "private" + "protected" +] @type.qualifier + +(inline_modifier) @storageclass + +(null_literal) @constant.builtin + +(wildcard) @parameter + +(annotation) @attribute + +;; special keywords + +"new" @keyword.operator + +[ + "else" + "if" + "match" + "then" +] @conditional + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + "." + "," +] @punctuation.delimiter + +[ + "do" + "for" + "while" + "yield" +] @repeat + +"def" @keyword.function + +[ + "=>" + "<-" + "@" +] @operator + +["import" "export"] @include + +[ + "try" + "catch" + "throw" +] @exception + +"return" @keyword.return + +(comment) @comment @spell +(block_comment) @comment @spell + +;; `case` is a conditional keyword in case_block + +(case_block + (case_clause ("case") @conditional)) +(indented_cases + (case_clause ("case") @conditional)) + +(operator_identifier) @operator + +((identifier) @type (#match? @type "^[A-Z]")) +((identifier) @variable.builtin + (#match? @variable.builtin "^this$")) + +( + (identifier) @function.builtin + (#match? @function.builtin "^super$") +) + +;; Scala CLI using directives +(using_directive_key) @parameter +(using_directive_value) @string diff --git a/src/textual/tree-sitter/highlights/sqlite.scm b/src/textual/tree-sitter/highlights/sqlite.scm new file mode 100644 index 0000000000..24a8cd4489 --- /dev/null +++ b/src/textual/tree-sitter/highlights/sqlite.scm @@ -0,0 +1,179 @@ +;;; + +";" @punctuation.delimiter +"," @punctuation.delimiter +"." @punctuation.delimiter + +"(" @punctuation.bracket +")" @punctuation.bracket + +[ + "||" + "~" "&" "|" "<<" ">>" + "+" "-" "*" "/" "%" + "=" "==" "!=" "<>" ">" ">=" "<" "<=" + "->" "->>" +] @operator + +;;; + +(signed_number) @number + +(numeric_literal) @number +(string_literal) @string +(blob_literal) @string.special +(identifier) @constant +(bind_parameter) @variable.parameter +(comment) @comment + +;;; + +(ABORT) @keyword +(ACTION) @keyword +(ADD) @keyword +(AFTER) @keyword +(ALL) @keyword +(ALTER) @keyword +(ALWAYS) @keyword +(ANALYZE) @keyword +(AND) @keyword +(AS) @keyword +(ASC) @keyword +(ATTACH) @keyword +(AUTOINCREMENT) @keyword +(BEFORE) @keyword +(BEGIN) @keyword +(BETWEEN) @keyword +(BY) @keyword +(CASCADE) @keyword +(CASE) @keyword +(CAST) @keyword +(CHECK) @keyword +(COLLATE) @keyword +(COLUMN) @keyword +(COMMIT) @keyword +(CONFLICT) @keyword +(CONSTRAINT) @keyword +(CREATE) @keyword +(CROSS) @keyword +(CURRENT) @keyword +(CURRENT_DATE) @keyword +(CURRENT_TIME) @keyword +(CURRENT_TIMESTAMP) @keyword +(DATABASE) @keyword +(DEFAULT) @keyword +(DEFERRABLE) @keyword +(DEFERRED) @keyword +(DELETE) @keyword +(DESC) @keyword +(DETACH) @keyword +(DISTINCT) @keyword +(DO) @keyword +(DROP) @keyword +(EACH) @keyword +(ELSE) @keyword +(END) @keyword +(ESCAPE) @keyword +(EXCEPT) @keyword +(EXCLUDE) @keyword +(EXCLUSIVE) @keyword +(EXISTS) @keyword +(EXPLAIN) @keyword +(FAIL) @keyword +(FALSE) @keyword +(FILTER) @keyword +(FIRST) @keyword +(FOLLOWING) @keyword +(FOR) @keyword +(FOREIGN) @keyword +(FROM) @keyword +(GENERATED) @keyword +(GLOB) @keyword +(GROUP) @keyword +(GROUPS) @keyword +(HAVING) @keyword +(IF) @keyword +(IGNORE) @keyword +(IMMEDIATE) @keyword +(IN) @keyword +(INDEX) @keyword +(INDEXED) @keyword +(INITIALLY) @keyword +(INNER) @keyword +(INSERT) @keyword +(INSTEAD) @keyword +(INTERSECT) @keyword +(INTO) @keyword +(IS) @keyword +(ISNULL) @keyword +(JOIN) @keyword +(KEY) @keyword +(LAST) @keyword +(LEFT) @keyword +(LIKE) @keyword +(LIMIT) @keyword +(MATCH) @keyword +(MATERIALIZED) @keyword +(NATURAL) @keyword +(NO) @keyword +(NOT) @keyword +(NOTHING) @keyword +(NOTNULL) @keyword +(NULL) @keyword +(NULLS) @keyword +(OF) @keyword +(OFFSET) @keyword +(ON) @keyword +(OR) @keyword +(ORDER) @keyword +(OTHERS) @keyword +(OUTER) @keyword +(OVER) @keyword +(PARTITION) @keyword +(PLAN) @keyword +(PRAGMA) @keyword +(PRECEDING) @keyword +(PRIMARY) @keyword +(QUERY) @keyword +(RAISE) @keyword +(RANGE) @keyword +(RECURSIVE) @keyword +(REFERENCES) @keyword +(REGEXP) @keyword +(REINDEX) @keyword +(RELEASE) @keyword +(RENAME) @keyword +(REPLACE) @keyword +(RESTRICT) @keyword +(RETURNING) @keyword +(ROLLBACK) @keyword +(ROW) @keyword +(ROWID) @keyword +(ROWS) @keyword +(SAVEPOINT) @keyword +(SELECT) @keyword +(SET) @keyword +(STORED) @keyword +(TABLE) @keyword +(TEMP) @keyword +(TEMPORARY) @keyword +(THEN) @keyword +(TIES) @keyword +(TO) @keyword +(TRANSACTION) @keyword +(TRIGGER) @keyword +(TRUE) @keyword +(UNBOUNDED) @keyword +(UNION) @keyword +(UNIQUE) @keyword +(UPDATE) @keyword +(USING) @keyword +(VACUUM) @keyword +(VALUES) @keyword +(VIEW) @keyword +(VIRTUAL) @keyword +(WHEN) @keyword +(WHERE) @keyword +(WINDOW) @keyword +(WITH) @keyword +(WITHOUT) @keyword diff --git a/src/textual/tree-sitter/highlights/typescript.scm b/src/textual/tree-sitter/highlights/typescript.scm new file mode 100644 index 0000000000..c758b516b9 --- /dev/null +++ b/src/textual/tree-sitter/highlights/typescript.scm @@ -0,0 +1,35 @@ +; Types + +(type_identifier) @type +(predefined_type) @type.builtin + +((identifier) @type + (#match? @type "^[A-Z]")) + +(type_arguments + "<" @punctuation.bracket + ">" @punctuation.bracket) + +; Variables + +(required_parameter (identifier) @variable.parameter) +(optional_parameter (identifier) @variable.parameter) + +; Keywords + +[ "abstract" + "declare" + "enum" + "export" + "implements" + "interface" + "keyof" + "namespace" + "private" + "protected" + "public" + "type" + "readonly" + "override" + "satisfies" +] @keyword From c8d64362db962f1a345df7b7b3afc2d2037a2395 Mon Sep 17 00:00:00 2001 From: juftin Date: Wed, 14 Feb 2024 16:09:45 -0700 Subject: [PATCH 03/53] fix(text-area): tree-sitter-languages pin --- poetry.lock | 135 +++++++++++++++++++++++-------------------------- pyproject.toml | 2 +- 2 files changed, 64 insertions(+), 73 deletions(-) diff --git a/poetry.lock b/poetry.lock index f248950165..56509910ad 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -2015,79 +2015,70 @@ setuptools = {version = ">=60.0.0", markers = "python_version >= \"3.12\""} [[package]] name = "tree-sitter-languages" -version = "1.9.1" +version = "1.10.2" description = "Binary Python wheels for all tree sitter languages." optional = true python-versions = "*" files = [ - {file = "tree_sitter_languages-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5dee458cf1bd1e725470949124e24db842dc789039ea7ff5ba46b338e5f0dc60"}, - {file = "tree_sitter_languages-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81921135fa15469586b1528088f78553e60a900d3045f4f37021ad3836219216"}, - {file = "tree_sitter_languages-1.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edd60780d14c727179acb7bb48fbe4f79da9b830abdeb0d12c06a9f2c37928c7"}, - {file = "tree_sitter_languages-1.9.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a28da3f60a6bc23195d6850836e477c149d4aaf58cdb0eb662741dca4f6401e2"}, - {file = "tree_sitter_languages-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9778c00a58ee77006abc5af905b591551b158ce106c8cc6c3b4148d624ccabf"}, - {file = "tree_sitter_languages-1.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6f68cfec0d74d6344db9c83414f401dcfc753916e71fac7d37f3a5e35b79e5ec"}, - {file = "tree_sitter_languages-1.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:02142d81b2cd759b5fe246d403e4fba80b70268d108bd2b108301e64a84437a6"}, - {file = "tree_sitter_languages-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ca4e0041c2ead2a8b354b9c229faee152bfd4617480c85cf2b352acf459db3cc"}, - {file = "tree_sitter_languages-1.9.1-cp310-cp310-win32.whl", hash = "sha256:506ff5c3646e7b3a533f9e925221d4fe63b88dad0b7ffc1fb96db4c271994606"}, - {file = "tree_sitter_languages-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:3ac3899e05f2bf0a7c8da70ef5a077ab3dbd442f99eb7452aabbe67bc7b29ddf"}, - {file = "tree_sitter_languages-1.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:823426c3768eea88b6a4fd70dc668b72de90cc9f44d041a579c76d024d7d0697"}, - {file = "tree_sitter_languages-1.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51f64b11f30cef3c5c9741e06221a46948f7c82d53ea2468139028eaf4858cca"}, - {file = "tree_sitter_languages-1.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1c384bcd2695ebf873bc63eccfa0b9e1c3c944cd6a6ebdd1139a2528d2d6f"}, - {file = "tree_sitter_languages-1.9.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fecf8553645fc1ad84921e97b03615d84aca22c35d020f629bb44cb6a28a302e"}, - {file = "tree_sitter_languages-1.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1a499004189bf9f338f3412d4c1c05a643e86d4619a60ba4b3ae56bc4bf5db9"}, - {file = "tree_sitter_languages-1.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:634ef22744b4af2ed9a43fea8309ec1171b062e37c609c3463364c790a08dae3"}, - {file = "tree_sitter_languages-1.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9394eb34208abcfa9c26ece39778037a8d97da3ef59501185303fef0ab850290"}, - {file = "tree_sitter_languages-1.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:221c367be0129da540fbb84170e18c5b8c56c09fd2f6143e116eebbef72c780e"}, - {file = "tree_sitter_languages-1.9.1-cp311-cp311-win32.whl", hash = "sha256:15d03f54f913f47ac36277d8a521cd425415a25b020e0845d7b8843f5f5e1209"}, - {file = "tree_sitter_languages-1.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:7c565c18cebc72417ebc8f0f4cd5cb91dda51874164045cc274f47c913b194aa"}, - {file = "tree_sitter_languages-1.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cde380cdc37594e7fcbade6a4b396dbeab52a1cecfe884cd814e1a1541ca6b93"}, - {file = "tree_sitter_languages-1.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9c4f2409e5460bdec5921ee445f748ea7c319469e347a13373e3c7086dbf0315"}, - {file = "tree_sitter_languages-1.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a17bbe91a78a29a9c14ab8bb07ed3761bb2708b58815bafc02d0965b15cb99e5"}, - {file = "tree_sitter_languages-1.9.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:369402e2b395de2655d769e515401fe7c7df247a83aa28a6362e808b8a017fae"}, - {file = "tree_sitter_languages-1.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a382d1e463e6ae60bbbd0c1f3db48e83b3c1a3af98d652af11de4c0e6171fc"}, - {file = "tree_sitter_languages-1.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bc60fb35f377143b30f4319fbaac0503b12cfb49de34082a479c7f0cc28927f1"}, - {file = "tree_sitter_languages-1.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e953fb43767e327bf5c1d0585ee39236eaff47683cbda2811cbe0227fd41ad7"}, - {file = "tree_sitter_languages-1.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c5a6df25eae23a5e2d448218b130207476cb8a613ac40570d49008243b0915bb"}, - {file = "tree_sitter_languages-1.9.1-cp312-cp312-win32.whl", hash = "sha256:2720f9a639f5d5c17692135f3f2d60506c240699d0c1becdb895546e553f2339"}, - {file = "tree_sitter_languages-1.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:f19157c33ddc1e75ae7843b813e65575ed2040e1638643251bd603bb0f52046b"}, - {file = "tree_sitter_languages-1.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:40880b5e774c3d5759b726273c36f83042d39c600c3aeefaf39248c3adec92d0"}, - {file = "tree_sitter_languages-1.9.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad71366ee2458bda6df5a7476fc0e465a1e1579f53335ce901935efc5c67fdeb"}, - {file = "tree_sitter_languages-1.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8000c6bf889e35e8b75407ea2d56153534b3f80c3b768378f4ca5a6fe286c0f"}, - {file = "tree_sitter_languages-1.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7e20ead363d70b3f0f0b04cf6da30257d22a166700fa39e06c9f263b527688"}, - {file = "tree_sitter_languages-1.9.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:444d2662912bc439c54c1b0ffe38354ae648f1f1ac8d1254b14fa768aa1a8587"}, - {file = "tree_sitter_languages-1.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cceac9018359310fee46204b452860bfdcb3da00f4518d430790f909cbbf6b4c"}, - {file = "tree_sitter_languages-1.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:332c182afbd9f7601e268426470e8c453740769a6227e7d1a9636d905cd7d707"}, - {file = "tree_sitter_languages-1.9.1-cp36-cp36m-win32.whl", hash = "sha256:25e993a41ad11fc433cb18ce0cc1d51eb7a285560c5cdddf781139312dac1881"}, - {file = "tree_sitter_languages-1.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:57419c215092ba9ba1964e07620dd386fc88ebb075b981fbb80f68f58004d4b4"}, - {file = "tree_sitter_languages-1.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06747cac4789c436affa7c6b3483f68cc234e6a75b508a0f8369c77eb1faa04b"}, - {file = "tree_sitter_languages-1.9.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b40bc82005543309c9cd4059f362c9d0d51277c942c71a5fdbed118389e5543a"}, - {file = "tree_sitter_languages-1.9.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44920c9654ae03e94baa45c6e8c4b36a5f7bdd0c93877c72931bd77e862adaf1"}, - {file = "tree_sitter_languages-1.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82e44f63a5449a41c5de3e9350967dc1c9183d9375881af5efb970c58c3fcfd8"}, - {file = "tree_sitter_languages-1.9.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:df177fa87b655f6234e4dae540ba3917cf8e87c3646423b809415711e926765e"}, - {file = "tree_sitter_languages-1.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:abdc8793328aa13fbd1cef3a0dff1c2e057a430fe2a64251628bbc97c4774eba"}, - {file = "tree_sitter_languages-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8b3f319f95f4464c35381755422f6dc0a518ad7d295d3cfe57bbaa564d225f3f"}, - {file = "tree_sitter_languages-1.9.1-cp37-cp37m-win32.whl", hash = "sha256:9f3a59bb4e8ec0a598566e02b7900eb8142236bda6c8b1069c4f3cdaf641950d"}, - {file = "tree_sitter_languages-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:517bdfe34bf24a05a496d441bee836fa77a6864f256508b82457ac28a9ac36bc"}, - {file = "tree_sitter_languages-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9383331026f736bcbdf6b67f9b45417fe8fbb47225fe2517a1e4f974c319d9a8"}, - {file = "tree_sitter_languages-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bba45ff3715e20e6e9a9b402f1ec2f2fc5ce11ce7b223584d0b5be5a4f8c60bb"}, - {file = "tree_sitter_languages-1.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03558927c6e731d81706e3a8b26276eaa4fadba17e2fd83a5e0bc2a32b261975"}, - {file = "tree_sitter_languages-1.9.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f0231140e2d29fcf987216277483c93bc7ce4c2f88b8af77756d796e17a2957"}, - {file = "tree_sitter_languages-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ead59b416f03da262df26e282cd40eb487f15384c90290f5105451e9a8ecfea"}, - {file = "tree_sitter_languages-1.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fd27b7bdb95a2b35b730069d7dea60d0f6cc37e5ab2e900d2940a82d1db608bd"}, - {file = "tree_sitter_languages-1.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d8b65a5fafd774a6c6dcacd9ac8b4c258c9f1efe2bfdca0a63818c83e591b949"}, - {file = "tree_sitter_languages-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f32f7a7b8fd9952f82e2b881c1c8701a467b27db209590e0effb2fb4d71fe3d3"}, - {file = "tree_sitter_languages-1.9.1-cp38-cp38-win32.whl", hash = "sha256:b52321e2a3a7cd1660cd7dadea16d7c7b9c981e177e0f77f9735e04cd89de015"}, - {file = "tree_sitter_languages-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8752bec9372937094a2557d9bfff357f30f5aa398e41e76e656baf53b4939d3"}, - {file = "tree_sitter_languages-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:119f32cfc7c561e252e8958259ef997f2adfd4587ae43e82819b56f2810b8b42"}, - {file = "tree_sitter_languages-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:582b04e11c67706b0a5ea64fd53ce4910fe11ad29d74ec7680c4014a02d09d4a"}, - {file = "tree_sitter_languages-1.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a816f76c52f6c9fb3316c5d44195f8de48e09f2214b7fdb5f9232395033c789c"}, - {file = "tree_sitter_languages-1.9.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a099b2f69cf22ab77de811b148de7d2d8ba8c51176a64bc56304cf42a627dd4"}, - {file = "tree_sitter_languages-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:447b6c62c59255c89341ec0968e467e8c59c60fc5c2c3dc1f7dfe159a820dd3c"}, - {file = "tree_sitter_languages-1.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41f4fee9b7de9646ef9711b6dbcdd5a4e7079e3d175089c8ef3f2c68b5adb5f4"}, - {file = "tree_sitter_languages-1.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ee3b70594b79ff1155d5d9fea64e3af240d9327a52526d446e6bd792ac5b43cf"}, - {file = "tree_sitter_languages-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:087b82cc3943fc5ffac30dc1b4192936a27c3c06fbd8718935a269e30dedc83b"}, - {file = "tree_sitter_languages-1.9.1-cp39-cp39-win32.whl", hash = "sha256:155483058dc11de302f47922d31feec5e1bb9888e661aed7be0dad6f70bfe691"}, - {file = "tree_sitter_languages-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:5335405a937f788a2608d1b25c654461dddddbc6a1341672c833d2c8943397a8"}, + {file = "tree_sitter_languages-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5580348f0b20233b1d5431fa178ccd3d07423ca4a3275df02a44608fd72344b9"}, + {file = "tree_sitter_languages-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:103c7466644486b1e9e03850df46fc6aa12f13ca636c74f173270276220ac80b"}, + {file = "tree_sitter_languages-1.10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d13db84511c6f1a7dc40383b66deafa74dabd8b877e3d65ab253f3719eccafd6"}, + {file = "tree_sitter_languages-1.10.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57adfa32be7e465b54aa72f915f6c78a2b66b227df4f656b5d4fbd1ca7a92b3f"}, + {file = "tree_sitter_languages-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6385e033e460ceb8f33f3f940335f422ef2b763700a04f0089391a68b56153"}, + {file = "tree_sitter_languages-1.10.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dfa3f38cc5381c5aba01dd7494f59b8a9050e82ff6e06e1233e3a0cbae297e3c"}, + {file = "tree_sitter_languages-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9f195155acf47f8bc5de7cee46ecd07b2f5697f007ba89435b51ef4c0b953ea5"}, + {file = "tree_sitter_languages-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2de330e2ac6d7426ca025a3ec0f10d5640c3682c1d0c7702e812dcfb44b58120"}, + {file = "tree_sitter_languages-1.10.2-cp310-cp310-win32.whl", hash = "sha256:c9731cf745f135d9770eeba9bb4e2ff4dabc107b5ae9b8211e919f6b9100ea6d"}, + {file = "tree_sitter_languages-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:6dd75851c41d0c3c4987a9b7692d90fa8848706c23115669d8224ffd6571e357"}, + {file = "tree_sitter_languages-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7eb7d7542b2091c875fe52719209631fca36f8c10fa66970d2c576ae6a1b8289"}, + {file = "tree_sitter_languages-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b41bcb00974b1c8a1800c7f1bb476a1d15a0463e760ee24872f2d53b08ee424"}, + {file = "tree_sitter_languages-1.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f370cd7845c6c81df05680d5bd96db8a99d32b56f4728c5d05978911130a853"}, + {file = "tree_sitter_languages-1.10.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1dc195c88ef4c72607e112a809a69190e096a2e5ebc6201548b3e05fdd169ad"}, + {file = "tree_sitter_languages-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ae34ac314a7170be24998a0f994c1ac80761d8d4bd126af27ee53a023d3b849"}, + {file = "tree_sitter_languages-1.10.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:01b5742d5f5bd675489486b582bd482215880b26dde042c067f8265a6e925d9c"}, + {file = "tree_sitter_languages-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ab1cbc46244d34fd16f21edaa20231b2a57f09f092a06ee3d469f3117e6eb954"}, + {file = "tree_sitter_languages-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0b1149e7467a4e92b8a70e6005fe762f880f493cf811fc003554b29f04f5e7c8"}, + {file = "tree_sitter_languages-1.10.2-cp311-cp311-win32.whl", hash = "sha256:049276343962f4696390ee555acc2c1a65873270c66a6cbe5cb0bca83bcdf3c6"}, + {file = "tree_sitter_languages-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:7f3fdd468a577f04db3b63454d939e26e360229b53c80361920aa1ebf2cd7491"}, + {file = "tree_sitter_languages-1.10.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c0f4c8b2734c45859edc7fcaaeaab97a074114111b5ba51ab4ec7ed52104763c"}, + {file = "tree_sitter_languages-1.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eecd3c1244ac3425b7a82ba9125b4ddb45d953bbe61de114c0334fd89b7fe782"}, + {file = "tree_sitter_languages-1.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15db3c8510bc39a80147ee7421bf4782c15c09581c1dc2237ea89cefbd95b846"}, + {file = "tree_sitter_languages-1.10.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92c6487a6feea683154d3e06e6db68c30e0ae749a7ce4ce90b9e4e46b78c85c7"}, + {file = "tree_sitter_languages-1.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2f1cd1d1bdd65332f9c2b67d49dcf148cf1ded752851d159ac3e5ee4f4d260"}, + {file = "tree_sitter_languages-1.10.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:976c8039165b8e12f17a01ddee9f4e23ec6e352b165ad29b44d2bf04e2fbe77e"}, + {file = "tree_sitter_languages-1.10.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:dafbbdf16bf668a580902e1620f4baa1913e79438abcce721a50647564c687b9"}, + {file = "tree_sitter_languages-1.10.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1aeabd3d60d6d276b73cd8f3739d595b1299d123cc079a317f1a5b3c5461e2ca"}, + {file = "tree_sitter_languages-1.10.2-cp312-cp312-win32.whl", hash = "sha256:fab8ee641914098e8933b87ea3d657bea4dd00723c1ee7038b847b12eeeef4f5"}, + {file = "tree_sitter_languages-1.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:5e606430d736367e5787fa5a7a0c5a1ec9b85eded0b3596bbc0d83532a40810b"}, + {file = "tree_sitter_languages-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:838d5b48a7ed7a17658721952c77fda4570d2a069f933502653b17e15a9c39c9"}, + {file = "tree_sitter_languages-1.10.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:987b3c71b1d278c2889e018ee77b8ee05c384e2e3334dec798f8b611c4ab2d1e"}, + {file = "tree_sitter_languages-1.10.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faa00abcb2c819027df58472da055d22fa7dfcb77c77413d8500c32ebe24d38b"}, + {file = "tree_sitter_languages-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e102fbbf02322d9201a86a814e79a9734ac80679fdb9682144479044f401a73"}, + {file = "tree_sitter_languages-1.10.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8f0b87cf1a7b03174ba18dfd81582be82bfed26803aebfe222bd20e444aba003"}, + {file = "tree_sitter_languages-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c0f1b9af9cb67f0b942b020da9fdd000aad5e92f2383ae0ba7a330b318d31912"}, + {file = "tree_sitter_languages-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5a4076c921f7a4d31e643843de7dfe040b65b63a238a5aa8d31d93aabe6572aa"}, + {file = "tree_sitter_languages-1.10.2-cp37-cp37m-win32.whl", hash = "sha256:fa6391a3a5d83d32db80815161237b67d70576f090ce5f38339206e917a6f8bd"}, + {file = "tree_sitter_languages-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:55649d3f254585a064121513627cf9788c1cfdadbc5f097f33d5ba750685a4c0"}, + {file = "tree_sitter_languages-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6f85d1edaa2d22d80d4ea5b6d12b95cf3644017b6c227d0d42854439e02e8893"}, + {file = "tree_sitter_languages-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d78feed4a764ef3141cb54bf00fe94d514d8b6e26e09423e23b4c616fcb7938c"}, + {file = "tree_sitter_languages-1.10.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1aca27531f9dd5308637d76643372856f0f65d0d28677d1bcf4211e8ed1ad0"}, + {file = "tree_sitter_languages-1.10.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1031ea440dafb72237437d754eff8940153a3b051e3d18932ac25e75ce060a15"}, + {file = "tree_sitter_languages-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99d3249beaef2c9fe558ecc9a97853c260433a849dcc68266d9770d196c2e102"}, + {file = "tree_sitter_languages-1.10.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:59a4450f262a55148fb7e68681522f0c2a2f6b7d89666312a2b32708d8f416e1"}, + {file = "tree_sitter_languages-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ce74eab0e430370d5e15a96b6c6205f93405c177a8b2e71e1526643b2fb9bab1"}, + {file = "tree_sitter_languages-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9b4dd2b6b3d24c85dffe33d6c343448869eaf4f41c19ddba662eb5d65d8808f4"}, + {file = "tree_sitter_languages-1.10.2-cp38-cp38-win32.whl", hash = "sha256:92d734fb968fe3927a7596d9f0459f81a8fa7b07e16569476b28e27d0d753348"}, + {file = "tree_sitter_languages-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:46a13f7d38f2eeb75f7cf127d1201346093748c270d686131f0cbc50e42870a1"}, + {file = "tree_sitter_languages-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f8c6a936ae99fdd8857e91f86c11c2f5e507ff30631d141d98132bb7ab2c8638"}, + {file = "tree_sitter_languages-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c283a61423f49cdfa7b5a5dfbb39221e3bd126fca33479cd80749d4d7a6b7349"}, + {file = "tree_sitter_languages-1.10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76e60be6bdcff923386a54a5edcb6ff33fc38ab0118636a762024fa2bc98de55"}, + {file = "tree_sitter_languages-1.10.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c00069f9575bd831eabcce2cdfab158dde1ed151e7e5614c2d985ff7d78a7de1"}, + {file = "tree_sitter_languages-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:475ff53203d8a43ccb19bb322fa2fb200d764001cc037793f1fadd714bb343da"}, + {file = "tree_sitter_languages-1.10.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26fe7c9c412e4141dea87ea4b3592fd12e385465b5bdab106b0d5125754d4f60"}, + {file = "tree_sitter_languages-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8fed27319957458340f24fe14daad467cd45021da034eef583519f83113a8c5e"}, + {file = "tree_sitter_languages-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3657a491a7f96cc75a3568ddd062d25f3be82b6a942c68801a7b226ff7130181"}, + {file = "tree_sitter_languages-1.10.2-cp39-cp39-win32.whl", hash = "sha256:33f7d584d01a7a3c893072f34cfc64ec031f3cfe57eebc32da2f8ac046e101a7"}, + {file = "tree_sitter_languages-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:1b944af3ee729fa70fc8ae82224a9ff597cdb63addea084e0ea2fa2b0ec39bb7"}, ] [package.dependencies] @@ -2359,9 +2350,9 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.link testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [extras] -syntax = ["tree-sitter", "tree_sitter_languages"] +syntax = ["tree-sitter", "tree-sitter-languages"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "e82a92dbbd8916df2ee09402ca7c4bc4c8dddb713e2723dc6eb144b373c99d1c" +content-hash = "3aa2a1401ae3764382763db2f6b01a4697a937ba87e2367ccce291065a5e64b8" diff --git a/pyproject.toml b/pyproject.toml index 71c101fcb1..d4b36659e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ rich = ">=13.3.3" #rich = {path="../rich", develop=true} typing-extensions = "^4.4.0" tree-sitter = { version = "^0.20.1", optional = true } -tree_sitter_languages = { version = ">=1.7.0", optional = true } +tree-sitter-languages = { version = "1.10.2", optional = true } [tool.poetry.extras] syntax = ["tree-sitter", "tree_sitter_languages"] From 7ace58dbc02c398b69899ead8134017d0abffb62 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Sun, 24 Mar 2024 11:28:10 +0000 Subject: [PATCH 04/53] fix(button): render with console markup --- src/textual/widgets/_button.py | 2 +- .../__snapshots__/test_snapshots.ambr | 165 ++++++++++++++++++ .../snapshot_apps/button_markup.py | 14 ++ tests/snapshot_tests/test_snapshots.py | 5 + 4 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 tests/snapshot_tests/snapshot_apps/button_markup.py diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index 110646e109..c39d0a702c 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -227,7 +227,7 @@ def validate_label(self, label: TextType) -> Text: def render(self) -> RenderResult: assert isinstance(self.label, Text) label = self.label.copy() - label.stylize(self.rich_style) + label.stylize_before(self.rich_style) return HorizontalPad( label, 1, diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 0d054a47ee..108d4331f7 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -2321,6 +2321,171 @@ ''' # --- +# name: test_button_with_console_markup + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ButtonsWithMarkupApp + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Focused Button + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Blurred Button + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Disabled Button + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + + + + + + + + + + + + + + + + ''' +# --- # name: test_buttons_render ''' diff --git a/tests/snapshot_tests/snapshot_apps/button_markup.py b/tests/snapshot_tests/snapshot_apps/button_markup.py new file mode 100644 index 0000000000..2cd68a4719 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/button_markup.py @@ -0,0 +1,14 @@ +from textual.app import App, ComposeResult +from textual.widgets import Button + + +class ButtonsWithMarkupApp(App): + def compose(self) -> ComposeResult: + yield Button("[italic red]Focused[/] Button") + yield Button("[italic red]Blurred[/] Button") + yield Button("[italic red]Disabled[/] Button", disabled=True) + + +if __name__ == "__main__": + app = ButtonsWithMarkupApp() + app.run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index d33a4178f3..ba6509f2c0 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -1162,3 +1162,8 @@ def test_button_widths(snap_compare): """Test that button widths expand auto containers as expected.""" # https://github.com/Textualize/textual/issues/4024 assert snap_compare(SNAPSHOT_APPS_DIR / "button_widths.py") + + +def test_button_with_console_markup(snap_compare): + """Regression test for https://github.com/Textualize/textual/issues/4328""" + assert snap_compare(SNAPSHOT_APPS_DIR / "button_markup.py") From 7cca00df38022b882f03006abcfedc6a4d44f5b3 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:07:21 +0000 Subject: [PATCH 05/53] update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a0cb30a51..1643a05850 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed a crash in `TextArea` when undoing an edit to a selection the selection was made backwards https://github.com/Textualize/textual/issues/4301 - Fixed issue with flickering scrollbars https://github.com/Textualize/textual/pull/4315 - Fix progress bar ETA not updating when setting `total` reactive https://github.com/Textualize/textual/pull/4316 +- Fixed `Button` not rendering correctly with console markup https://github.com/Textualize/textual/issues/4328 -### Changed +### Changed - ProgressBar won't show ETA until there is at least one second of samples https://github.com/Textualize/textual/pull/4316 From 9296c851284588ae506049fbbca0ba66625e2d00 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 26 Mar 2024 12:38:16 +0000 Subject: [PATCH 06/53] Fix priority bindings not appearing in Footer when key clashes with a key on the focused widget --- src/textual/app.py | 12 ++++++------ src/textual/widgets/_footer.py | 4 +--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index aaf799a66c..372cf4b278 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -846,7 +846,7 @@ def focused(self) -> Widget | None: return focused @property - def namespace_bindings(self) -> dict[str, tuple[DOMNode, Binding]]: + def active_bindings(self) -> list[tuple[DOMNode, Binding]]: """Get currently active bindings. If no widget is focused, then app-level bindings are returned. @@ -855,15 +855,15 @@ def namespace_bindings(self) -> dict[str, tuple[DOMNode, Binding]]: This property may be used to inspect current bindings. Returns: - A mapping of keys onto pairs of nodes and bindings. + A list of tuples containing bindings that are currently active and the DOMNode the binding is associated with. """ - namespace_binding_map: dict[str, tuple[DOMNode, Binding]] = {} + nodes_and_bindings: list[tuple[DOMNode, Binding]] = [] for namespace, bindings in reversed(self._binding_chain): - for key, binding in bindings.keys.items(): - namespace_binding_map[key] = (namespace, binding) + for binding in bindings.keys.values(): + nodes_and_bindings.append((namespace, binding)) - return namespace_binding_map + return nodes_and_bindings def _set_active(self) -> None: """Set this app to be the currently active app.""" diff --git a/src/textual/widgets/_footer.py b/src/textual/widgets/_footer.py index 5f3c60d925..f50087f6c7 100644 --- a/src/textual/widgets/_footer.py +++ b/src/textual/widgets/_footer.py @@ -103,9 +103,7 @@ def _make_key_text(self) -> Text: description_style = self.get_component_rich_style("footer--description") bindings = [ - binding - for (_, binding) in self.app.namespace_bindings.values() - if binding.show + binding for (_, binding) in self.app.active_bindings if binding.show ] action_to_bindings = defaultdict(list) From 0db1b980f55091312db95a6c54d788552eb8a47c Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 26 Mar 2024 12:41:58 +0000 Subject: [PATCH 07/53] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d8b9e2e2..e04f4bdbb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,10 +13,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed issue with flickering scrollbars https://github.com/Textualize/textual/pull/4315 - Fixed issue where narrow TextArea would repeatedly wrap due to scrollbar appearing/disappearing https://github.com/Textualize/textual/pull/4334 - Fix progress bar ETA not updating when setting `total` reactive https://github.com/Textualize/textual/pull/4316 +- Fix priority bindings not appearing in footer when key clashes with focused widget https://github.com/Textualize/textual/pull/4342 ### Changed - ProgressBar won't show ETA until there is at least one second of samples https://github.com/Textualize/textual/pull/4316 +- App.namespace_bindings renamed to App.active_bindings and now returns a list instead of a dict https://github.com/Textualize/textual/pull/4342 ## [0.53.1] - 2023-03-18 From 01063141035ac009718759ae2f728b4414777829 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 26 Mar 2024 13:02:40 +0000 Subject: [PATCH 08/53] Minimising the change --- src/textual/app.py | 20 +++++++++++++------- src/textual/widgets/_footer.py | 4 +++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index 372cf4b278..0c985a09df 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -846,7 +846,7 @@ def focused(self) -> Widget | None: return focused @property - def active_bindings(self) -> list[tuple[DOMNode, Binding]]: + def namespace_bindings(self) -> dict[str, tuple[DOMNode, Binding]]: """Get currently active bindings. If no widget is focused, then app-level bindings are returned. @@ -855,15 +855,21 @@ def active_bindings(self) -> list[tuple[DOMNode, Binding]]: This property may be used to inspect current bindings. Returns: - A list of tuples containing bindings that are currently active and the DOMNode the binding is associated with. + A map of keys to a tuple containing the DOMNode and Binding that key corresponds to. """ - nodes_and_bindings: list[tuple[DOMNode, Binding]] = [] + bindings_map: dict[str, tuple[DOMNode, Binding]] = {} for namespace, bindings in reversed(self._binding_chain): - for binding in bindings.keys.values(): - nodes_and_bindings.append((namespace, binding)) - - return nodes_and_bindings + for key, binding in bindings.keys.items(): + existing_key_and_binding = bindings_map.get(key) + if existing_key_and_binding: + _, existing_binding = existing_key_and_binding + if binding.priority and not existing_binding.priority: + bindings_map[key] = (namespace, binding) + else: + bindings_map[key] = (namespace, binding) + + return bindings_map def _set_active(self) -> None: """Set this app to be the currently active app.""" diff --git a/src/textual/widgets/_footer.py b/src/textual/widgets/_footer.py index f50087f6c7..5f3c60d925 100644 --- a/src/textual/widgets/_footer.py +++ b/src/textual/widgets/_footer.py @@ -103,7 +103,9 @@ def _make_key_text(self) -> Text: description_style = self.get_component_rich_style("footer--description") bindings = [ - binding for (_, binding) in self.app.active_bindings if binding.show + binding + for (_, binding) in self.app.namespace_bindings.values() + if binding.show ] action_to_bindings = defaultdict(list) From 5aaa88d609706684527ccce26f82ab3f23f5eebd Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 26 Mar 2024 16:30:03 +0000 Subject: [PATCH 09/53] inline driver --- src/textual/app.py | 23 ++- src/textual/driver.py | 5 + src/textual/drivers/linux_inline_driver.py | 176 +++++++++++++++++++++ 3 files changed, 198 insertions(+), 6 deletions(-) create mode 100644 src/textual/drivers/linux_inline_driver.py diff --git a/src/textual/app.py b/src/textual/app.py index 15811232c9..a60cf9527d 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -45,7 +45,6 @@ Type, TypeVar, Union, - cast, overload, ) from weakref import WeakKeyDictionary, WeakSet @@ -92,7 +91,6 @@ from .design import ColorSystem from .dom import DOMNode, NoScreen from .driver import Driver -from .drivers.headless_driver import HeadlessDriver from .errors import NoWidget from .features import FeatureFlag, parse_features from .file_monitor import FileMonitor @@ -1443,6 +1441,7 @@ async def run_app(app: App) -> None: async def run_async( self, *, + inline: bool = True, headless: bool = False, size: tuple[int, int] | None = None, auto_pilot: AutopilotCallbackType | None = None, @@ -1501,6 +1500,7 @@ async def run_auto_pilot( await app._process_messages( ready_callback=None if auto_pilot is None else app_ready, headless=headless, + inline=inline, terminal_size=size, ) finally: @@ -1516,6 +1516,7 @@ def run( self, *, headless: bool = False, + inline: bool = False, size: tuple[int, int] | None = None, auto_pilot: AutopilotCallbackType | None = None, ) -> ReturnType | None: @@ -2297,6 +2298,7 @@ async def _process_messages( self, ready_callback: CallbackType | None = None, headless: bool = False, + inline: bool = False, terminal_size: tuple[int, int] | None = None, message_hook: Callable[[Message], None] | None = None, ) -> None: @@ -2406,10 +2408,19 @@ async def invoke_ready_callback() -> None: await self._dispatch_message(load_event) driver: Driver - driver_class = cast( - "type[Driver]", - HeadlessDriver if headless else self.driver_class, - ) + + driver_class: type[Driver] + if headless: + from .drivers.headless_driver import HeadlessDriver + + driver_class = HeadlessDriver + elif inline: + from .drivers.linux_inline_driver import LinuxInlineDriver + + driver_class = LinuxInlineDriver + else: + driver_class = self.driver_class + driver = self._driver = driver_class( self, debug=constants.DEBUG, diff --git a/src/textual/driver.py b/src/textual/driver.py index c70edf9588..fbdf48ab2f 100644 --- a/src/textual/driver.py +++ b/src/textual/driver.py @@ -43,6 +43,11 @@ def is_headless(self) -> bool: """Is the driver 'headless' (no output)?""" return False + @property + def is_inline(self) -> bool: + """Is the driver 'inline' (not full-screen)?""" + return False + @property def can_suspend(self) -> bool: """Can this driver be suspended?""" diff --git a/src/textual/drivers/linux_inline_driver.py b/src/textual/drivers/linux_inline_driver.py new file mode 100644 index 0000000000..f06e0cdc19 --- /dev/null +++ b/src/textual/drivers/linux_inline_driver.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +import asyncio +import os +import selectors +import signal +import sys +from codecs import getincrementaldecoder +from threading import Event, Thread +from typing import TYPE_CHECKING + +import rich.repr + +from .. import events +from .._xterm_parser import XTermParser +from ..driver import Driver +from ..geometry import Size + +if TYPE_CHECKING: + from ..app import App + + +@rich.repr.auto(angular=True) +class LinuxInlineDriver(Driver): + + def __init__( + self, + app: App, + *, + debug: bool = False, + size: tuple[int, int] | None = None, + ): + super().__init__(app, debug=debug, size=size) + self._file = sys.__stderr__ + self.fileno = sys.__stdin__.fileno() + self.exit_event = Event() + + def __rich_repr__(self) -> rich.repr.Result: + yield self._app + + @property + def is_inline(self) -> bool: + return True + + def _enable_bracketed_paste(self) -> None: + """Enable bracketed paste mode.""" + self.write("\x1b[?2004h") + + def _disable_bracketed_paste(self) -> None: + """Disable bracketed paste mode.""" + self.write("\x1b[?2004l") + + def _get_terminal_size(self) -> tuple[int, int]: + """Detect the terminal size. + + Returns: + The size of the terminal as a tuple of (WIDTH, HEIGHT). + """ + width: int | None = 80 + height: int | None = 25 + import shutil + + try: + width, height = shutil.get_terminal_size() + except (AttributeError, ValueError, OSError): + try: + width, height = shutil.get_terminal_size() + except (AttributeError, ValueError, OSError): + pass + width = width or 80 + height = height or 25 + return width, height + + def write(self, data: str) -> None: + self._file.write(data) + + def _run_input_thread(self) -> None: + """ + Key thread target that wraps run_input_thread() to die gracefully if it raises + an exception + """ + try: + self.run_input_thread() + except BaseException as error: + import rich.traceback + + self._app.call_later( + self._app.panic, + rich.traceback.Traceback(), + ) + + def run_input_thread(self) -> None: + """Wait for input and dispatch events.""" + selector = selectors.SelectSelector() + selector.register(self.fileno, selectors.EVENT_READ) + + fileno = self.fileno + EVENT_READ = selectors.EVENT_READ + + def more_data() -> bool: + """Check if there is more data to parse.""" + + for _key, events in selector.select(0.01): + if events & EVENT_READ: + return True + return False + + parser = XTermParser(more_data, self._debug) + feed = parser.feed + + utf8_decoder = getincrementaldecoder("utf-8")().decode + decode = utf8_decoder + read = os.read + + try: + while not self.exit_event.is_set(): + selector_events = selector.select(0.1) + for _selector_key, mask in selector_events: + if mask & EVENT_READ: + unicode_data = decode( + read(fileno, 1024), final=self.exit_event.is_set() + ) + for event in feed(unicode_data): + self.process_event(event) + finally: + selector.close() + + def start_application_mode(self) -> None: + + loop = asyncio.get_running_loop() + + def send_size_event(): + terminal_size = self._get_terminal_size() + width, height = terminal_size + textual_size = Size(width, height) + event = events.Resize(textual_size, textual_size) + asyncio.run_coroutine_threadsafe( + self._app._post_message(event), + loop=loop, + ) + + def on_terminal_resize(signum, stack) -> None: + send_size_event() + + signal.signal(signal.SIGWINCH, on_terminal_resize) + + self.write("\x1b[?25l") # Hide cursor + self.write("\033[?1004h\n") # Enable FocusIn/FocusOut. + + self._key_thread = Thread(target=self._run_input_thread) + send_size_event() + self._key_thread.start() + + def disable_input(self) -> None: + """Disable further input.""" + try: + if not self.exit_event.is_set(): + signal.signal(signal.SIGWINCH, signal.SIG_DFL) + self.exit_event.set() + if self._key_thread is not None: + self._key_thread.join() + self.exit_event.clear() + + except Exception as error: + # TODO: log this + pass + + def stop_application_mode(self) -> None: + """Stop application mode, restore state.""" + self._disable_bracketed_paste() + self.disable_input() + + # Alt screen false, show cursor + self.write("\x1b[?25h") + self.write("\033[?1004l\n") # Disable FocusIn/FocusOut. + self.flush() From 9b058e46243ffaa67867ee4d532c241e8cc1bafe Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 26 Mar 2024 16:33:11 +0000 Subject: [PATCH 10/53] Fix changelog --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03cfc63017..9351414a1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,10 +11,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fix priority bindings not appearing in footer when key clashes with focused widget https://github.com/Textualize/textual/pull/4342 -### Changed - -- App.namespace_bindings renamed to App.active_bindings and now returns a list instead of a dict https://github.com/Textualize/textual/pull/4342 - ## [0.54.0] - 2024-03-26 ### Fixed From f0fa8d5474cc655211b8ad9783842497f022a82d Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 26 Mar 2024 16:52:07 +0000 Subject: [PATCH 11/53] add mouse to inline --- src/textual/__main__.py | 2 +- src/textual/app.py | 5 +- src/textual/demo.py | 2 +- src/textual/drivers/linux_inline_driver.py | 97 ++++++++++++++++++++-- 4 files changed, 96 insertions(+), 10 deletions(-) diff --git a/src/textual/__main__.py b/src/textual/__main__.py index 212d16b928..b62cb9d5b9 100644 --- a/src/textual/__main__.py +++ b/src/textual/__main__.py @@ -2,4 +2,4 @@ if __name__ == "__main__": app = DemoApp() - app.run() + app.run(inline=True) diff --git a/src/textual/app.py b/src/textual/app.py index a60cf9527d..7d0df77560 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -1441,8 +1441,8 @@ async def run_app(app: App) -> None: async def run_async( self, *, - inline: bool = True, headless: bool = False, + inline: bool = False, size: tuple[int, int] | None = None, auto_pilot: AutopilotCallbackType | None = None, ) -> ReturnType | None: @@ -1539,6 +1539,7 @@ async def run_app() -> None: try: await self.run_async( headless=headless, + inline=inline, size=size, auto_pilot=auto_pilot, ) @@ -2316,7 +2317,6 @@ async def _process_messages( self.log.system("---") - self.log.system(driver=self.driver_class) self.log.system(loop=asyncio.get_running_loop()) self.log.system(features=self.features) if constants.LOG_FILE is not None: @@ -2426,6 +2426,7 @@ async def invoke_ready_callback() -> None: debug=constants.DEBUG, size=terminal_size, ) + self.log(driver=driver) if not self._exit: driver.start_application_mode() diff --git a/src/textual/demo.py b/src/textual/demo.py index 5d2a67d74e..2b3af4c053 100644 --- a/src/textual/demo.py +++ b/src/textual/demo.py @@ -393,4 +393,4 @@ def action_screenshot(self, filename: str | None = None, path: str = "./") -> No app = DemoApp() if __name__ == "__main__": - app.run() + app.run(inline=True) diff --git a/src/textual/drivers/linux_inline_driver.py b/src/textual/drivers/linux_inline_driver.py index f06e0cdc19..096b9582e0 100644 --- a/src/textual/drivers/linux_inline_driver.py +++ b/src/textual/drivers/linux_inline_driver.py @@ -5,9 +5,11 @@ import selectors import signal import sys +import termios +import tty from codecs import getincrementaldecoder from threading import Event, Thread -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any import rich.repr @@ -33,6 +35,7 @@ def __init__( super().__init__(app, debug=debug, size=size) self._file = sys.__stderr__ self.fileno = sys.__stdin__.fileno() + self.attrs_before: list[Any] | None = None self.exit_event = Event() def __rich_repr__(self) -> rich.repr.Result: @@ -71,6 +74,26 @@ def _get_terminal_size(self) -> tuple[int, int]: height = height or 25 return width, height + def _enable_mouse_support(self) -> None: + """Enable reporting of mouse events.""" + write = self.write + write("\x1b[?1000h") # SET_VT200_MOUSE + write("\x1b[?1003h") # SET_ANY_EVENT_MOUSE + write("\x1b[?1015h") # SET_VT200_HIGHLIGHT_MOUSE + write("\x1b[?1006h") # SET_SGR_EXT_MODE_MOUSE + + # write("\x1b[?1007h") + self.flush() + + def _disable_mouse_support(self) -> None: + """Disable reporting of mouse events.""" + write = self.write + write("\x1b[?1000l") # + write("\x1b[?1003l") # + write("\x1b[?1015l") + write("\x1b[?1006l") + self.flush() + def write(self, data: str) -> None: self._file.write(data) @@ -129,7 +152,7 @@ def start_application_mode(self) -> None: loop = asyncio.get_running_loop() - def send_size_event(): + def send_size_event() -> None: terminal_size = self._get_terminal_size() width, height = terminal_size textual_size = Size(width, height) @@ -147,19 +170,76 @@ def on_terminal_resize(signum, stack) -> None: self.write("\x1b[?25l") # Hide cursor self.write("\033[?1004h\n") # Enable FocusIn/FocusOut. + self._enable_mouse_support() + try: + self.attrs_before = termios.tcgetattr(self.fileno) + except termios.error: + # Ignore attribute errors. + self.attrs_before = None + + try: + newattr = termios.tcgetattr(self.fileno) + except termios.error: + pass + else: + newattr[tty.LFLAG] = self._patch_lflag(newattr[tty.LFLAG]) + newattr[tty.IFLAG] = self._patch_iflag(newattr[tty.IFLAG]) + + # VMIN defines the number of characters read at a time in + # non-canonical mode. It seems to default to 1 on Linux, but on + # Solaris and derived operating systems it defaults to 4. (This is + # because the VMIN slot is the same as the VEOF slot, which + # defaults to ASCII EOT = Ctrl-D = 4.) + newattr[tty.CC][termios.VMIN] = 1 + + termios.tcsetattr(self.fileno, termios.TCSANOW, newattr) + self._key_thread = Thread(target=self._run_input_thread) send_size_event() self._key_thread.start() + @classmethod + def _patch_lflag(cls, attrs: int) -> int: + """Patch termios lflag. + + Args: + attributes: New set attributes. + + Returns: + New lflag. + + """ + # if TEXTUAL_ALLOW_SIGNALS env var is set, then allow Ctrl+C to send signals + ISIG = 0 if os.environ.get("TEXTUAL_ALLOW_SIGNALS") else termios.ISIG + + return attrs & ~(termios.ECHO | termios.ICANON | termios.IEXTEN | ISIG) + + @classmethod + def _patch_iflag(cls, attrs: int) -> int: + return attrs & ~( + # Disable XON/XOFF flow control on output and input. + # (Don't capture Ctrl-S and Ctrl-Q.) + # Like executing: "stty -ixon." + termios.IXON + | termios.IXOFF + | + # Don't translate carriage return into newline on input. + termios.ICRNL + | termios.INLCR + | termios.IGNCR + ) + def disable_input(self) -> None: """Disable further input.""" try: if not self.exit_event.is_set(): signal.signal(signal.SIGWINCH, signal.SIG_DFL) + self._disable_mouse_support() self.exit_event.set() if self._key_thread is not None: self._key_thread.join() self.exit_event.clear() + termios.tcflush(self.fileno, termios.TCIFLUSH) except Exception as error: # TODO: log this @@ -170,7 +250,12 @@ def stop_application_mode(self) -> None: self._disable_bracketed_paste() self.disable_input() - # Alt screen false, show cursor - self.write("\x1b[?25h") - self.write("\033[?1004l\n") # Disable FocusIn/FocusOut. - self.flush() + if self.attrs_before is not None: + try: + termios.tcsetattr(self.fileno, termios.TCSANOW, self.attrs_before) + except termios.error: + pass + + self.write("\x1b[?25h") # Show cursor + self.write("\033[?1004l\n") # Disable FocusIn/FocusOut. + self.flush() From b1895aebb67897661a0b564117e27d3c18a1ebea Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Wed, 27 Mar 2024 13:42:10 +0000 Subject: [PATCH 12/53] Revert Python --- src/textual/tree-sitter/highlights/python.scm | 309 ++++++++++++++---- 1 file changed, 252 insertions(+), 57 deletions(-) diff --git a/src/textual/tree-sitter/highlights/python.scm b/src/textual/tree-sitter/highlights/python.scm index 8ccf82b87d..37aceef1fd 100644 --- a/src/textual/tree-sitter/highlights/python.scm +++ b/src/textual/tree-sitter/highlights/python.scm @@ -1,61 +1,191 @@ -; Identifier naming conventions +;; From tree-sitter-python licensed under MIT License +; Copyright (c) 2016 Max Brunsfeld +; Adapted for Textual from: +; https://github.com/nvim-treesitter/nvim-treesitter/blob/f95ffd09ed35880c3a46ad2b968df361fa592a76/queries/python/highlights.scm -((identifier) @constructor - (#match? @constructor "^[A-Z]")) +; Variables +(identifier) @variable + +; Reset highlighting in f-string interpolations +(interpolation) @none +;; Identifier naming conventions +((identifier) @type + (#lua-match? @type "^[A-Z].*[a-z]")) ((identifier) @constant - (#match? @constant "^[A-Z][A-Z_]*$")) + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) -; Builtin functions +((identifier) @constant.builtin + (#lua-match? @constant.builtin "^__[a-zA-Z0-9_]*__$")) -((call - function: (identifier) @function.builtin) - (#match? - @function.builtin - "^(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes|callable|chr|classmethod|compile|complex|delattr|dict|dir|divmod|enumerate|eval|exec|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|isinstance|issubclass|iter|len|list|locals|map|max|memoryview|min|next|object|oct|open|ord|pow|print|property|range|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|vars|zip|__import__)$")) +((identifier) @constant.builtin + (#any-of? @constant.builtin + ;; https://docs.python.org/3/library/constants.html + "NotImplemented" + "Ellipsis" + "quit" + "exit" + "copyright" + "credits" + "license")) -; Function calls +((attribute + attribute: (identifier) @field) + (#match? @field "^([A-Z])@!.*$")) + +((identifier) @type.builtin + (#any-of? @type.builtin + ;; https://docs.python.org/3/library/exceptions.html + "BaseException" "Exception" "ArithmeticError" "BufferError" "LookupError" "AssertionError" "AttributeError" + "EOFError" "FloatingPointError" "GeneratorExit" "ImportError" "ModuleNotFoundError" "IndexError" "KeyError" + "KeyboardInterrupt" "MemoryError" "NameError" "NotImplementedError" "OSError" "OverflowError" "RecursionError" + "ReferenceError" "RuntimeError" "StopIteration" "StopAsyncIteration" "SyntaxError" "IndentationError" "TabError" + "SystemError" "SystemExit" "TypeError" "UnboundLocalError" "UnicodeError" "UnicodeEncodeError" "UnicodeDecodeError" + "UnicodeTranslateError" "ValueError" "ZeroDivisionError" "EnvironmentError" "IOError" "WindowsError" + "BlockingIOError" "ChildProcessError" "ConnectionError" "BrokenPipeError" "ConnectionAbortedError" + "ConnectionRefusedError" "ConnectionResetError" "FileExistsError" "FileNotFoundError" "InterruptedError" + "IsADirectoryError" "NotADirectoryError" "PermissionError" "ProcessLookupError" "TimeoutError" "Warning" + "UserWarning" "DeprecationWarning" "PendingDeprecationWarning" "SyntaxWarning" "RuntimeWarning" + "FutureWarning" "ImportWarning" "UnicodeWarning" "BytesWarning" "ResourceWarning" + ;; https://docs.python.org/3/library/stdtypes.html + "bool" "int" "float" "complex" "list" "tuple" "range" "str" + "bytes" "bytearray" "memoryview" "set" "frozenset" "dict" "type")) -(decorator) @function +((assignment + left: (identifier) @type.definition + (type (identifier) @_annotation)) + (#eq? @_annotation "TypeAlias")) + +((assignment + left: (identifier) @type.definition + right: (call + function: (identifier) @_func)) + (#any-of? @_func "TypeVar" "NewType")) + +; Function calls (call - function: (attribute attribute: (identifier) @function.method)) + function: (identifier) @function.call) + (call - function: (identifier) @function) + function: (attribute + attribute: (identifier) @method.call)) + +((call + function: (identifier) @constructor) + (#lua-match? @constructor "^[A-Z]")) + +((call + function: (attribute + attribute: (identifier) @constructor)) + (#lua-match? @constructor "^[A-Z]")) + +;; Decorators + +((decorator "@" @attribute) + (#set! "priority" 101)) + +(decorator + (identifier) @attribute) +(decorator + (attribute + attribute: (identifier) @attribute)) +(decorator + (call (identifier) @attribute)) +(decorator + (call (attribute + attribute: (identifier) @attribute))) + +((decorator + (identifier) @attribute.builtin) + (#any-of? @attribute.builtin "classmethod" "property")) + +;; Builtin functions + +((call + function: (identifier) @function.builtin) + (#any-of? @function.builtin + "abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray" "bytes" "callable" "chr" "classmethod" + "compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec" "filter" "float" "format" + "frozenset" "getattr" "globals" "hasattr" "hash" "help" "hex" "id" "input" "int" "isinstance" "issubclass" + "iter" "len" "list" "locals" "map" "max" "memoryview" "min" "next" "object" "oct" "open" "ord" "pow" + "print" "property" "range" "repr" "reversed" "round" "set" "setattr" "slice" "sorted" "staticmethod" "str" + "sum" "super" "tuple" "type" "vars" "zip" "__import__")) -; Function definitions +;; Function definitions (function_definition name: (identifier) @function) -(identifier) @variable -(attribute attribute: (identifier) @property) (type (identifier) @type) +(type + (subscript + (identifier) @type)) ; type subscript: Tuple[int] -; Literals +((call + function: (identifier) @_isinstance + arguments: (argument_list + (_) + (identifier) @type)) + (#eq? @_isinstance "isinstance")) -[ - (none) - (true) - (false) -] @constant.builtin +;; Normal parameters +(parameters + (identifier) @parameter) +;; Lambda parameters +(lambda_parameters + (identifier) @parameter) +(lambda_parameters + (tuple_pattern + (identifier) @parameter)) +; Default parameters +(keyword_argument + name: (identifier) @parameter) +; Naming parameters on call-site +(default_parameter + name: (identifier) @parameter) +(typed_parameter + (identifier) @parameter) +(typed_default_parameter + (identifier) @parameter) +; Variadic parameters *args, **kwargs +(parameters + (list_splat_pattern ; *args + (identifier) @parameter)) +(parameters + (dictionary_splat_pattern ; **kwargs + (identifier) @parameter)) -[ - (integer) - (float) -] @number -(comment) @comment +;; Literals + +(none) @constant.builtin +[(true) (false)] @boolean +((identifier) @variable.builtin + (#eq? @variable.builtin "self")) +((identifier) @variable.builtin + (#eq? @variable.builtin "cls")) + +(integer) @number +(float) @float + +(comment) @comment @spell + +((module . (comment) @preproc) + (#match? @preproc "^#!/")) + (string) @string -(escape_sequence) @escape +(escape_sequence) @string.escape -(interpolation - "{" @punctuation.special - "}" @punctuation.special) @embedded +; doc-strings +(expression_statement (string) @spell) + +; Tokens [ "-" "-=" + ":=" "!=" "*" "**" @@ -72,7 +202,6 @@ "^" "^=" "+" - "->" "+=" "<" "<<" @@ -80,53 +209,119 @@ "<=" "<>" "=" - ":=" "==" ">" ">=" ">>" ">>=" + "@" + "@=" "|" "|=" "~" - "@=" + "->" +] @operator + +; Keywords +[ "and" "in" "is" "not" "or" -] @operator + "del" +] @keyword.operator + +[ + "def" + "lambda" +] @keyword.function [ - "as" "assert" "async" "await" - "break" "class" - "continue" - "def" - "del" - "elif" - "else" - "except" "exec" - "finally" - "for" - "from" "global" - "if" - "import" - "lambda" "nonlocal" "pass" "print" - "raise" - "return" - "try" - "while" "with" - "yield" - "match" - "case" + "as" ] @keyword + +[ + "return" + "yield" +] @keyword.return +(yield "from" @keyword.return) + +(future_import_statement + "from" @include + "__future__" @constant.builtin) +(import_from_statement "from" @include) +"import" @include + +(aliased_import "as" @include) + +["if" "elif" "else" "match" "case"] @conditional + +["for" "while" "break" "continue"] @repeat + +[ + "try" + "except" + "raise" + "finally" +] @exception + +(raise_statement "from" @exception) + +(try_statement + (else_clause + "else" @exception)) + +["(" ")" "[" "]" "{" "}"] @punctuation.bracket + +(interpolation + "{" @punctuation.special + "}" @punctuation.special) + +["," "." ":" ";" (ellipsis)] @punctuation.delimiter + +;; Class definitions + +(class_definition name: (identifier) @type.class) + +(class_definition + body: (block + (function_definition + name: (identifier) @method))) + +(class_definition + superclasses: (argument_list + (identifier) @type)) + +((class_definition + body: (block + (expression_statement + (assignment + left: (identifier) @field)))) + (#match? @field "^([A-Z])@!.*$")) +((class_definition + body: (block + (expression_statement + (assignment + left: (_ + (identifier) @field))))) + (#match? @field "^([A-Z])@!.*$")) + +((class_definition + (block + (function_definition + name: (identifier) @constructor))) + (#any-of? @constructor "__new__" "__init__")) + +;; Error +(ERROR) @error From 51258f1e462cb7433fd7a642d8fbb695d19c045f Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Wed, 27 Mar 2024 14:08:45 +0000 Subject: [PATCH 13/53] Remove elm, add go support --- src/textual/tree-sitter/highlights/elm.scm | 76 ---------------------- tests/snapshot_tests/language_snippets.py | 72 ++++++++++++++++++++ 2 files changed, 72 insertions(+), 76 deletions(-) delete mode 100644 src/textual/tree-sitter/highlights/elm.scm diff --git a/src/textual/tree-sitter/highlights/elm.scm b/src/textual/tree-sitter/highlights/elm.scm deleted file mode 100644 index 8cd68257df..0000000000 --- a/src/textual/tree-sitter/highlights/elm.scm +++ /dev/null @@ -1,76 +0,0 @@ -; Keywords -[ - "if" - "then" - "else" - "let" - "in" - ] @keyword.control.elm -(case) @keyword.control.elm -(of) @keyword.control.elm - -(colon) @keyword.other.elm -(backslash) @keyword.other.elm -(as) @keyword.other.elm -(port) @keyword.other.elm -(exposing) @keyword.other.elm -(alias) @keyword.other.elm -(infix) @keyword.other.elm - -(arrow) @keyword.operator.arrow.elm - -(port) @keyword.other.port.elm - -(type_annotation(lower_case_identifier) @function.elm) -(port_annotation(lower_case_identifier) @function.elm) -(function_declaration_left(lower_case_identifier) @function.elm) -(function_call_expr target: (value_expr) @function.elm) - -(field_access_expr(value_expr(value_qid)) @local.function.elm) -(lower_pattern) @local.function.elm -(record_base_identifier) @local.function.elm - - -(operator_identifier) @keyword.operator.elm -(eq) @keyword.operator.assignment.elm - - -"(" @punctuation.section.braces -")" @punctuation.section.braces - -"|" @keyword.other.elm -"," @punctuation.separator.comma.elm - -(import) @meta.import.elm -(module) @keyword.other.elm - -(number_constant_expr) @constant.numeric.elm - - -(type) @keyword.type.elm - -(type_declaration(upper_case_identifier) @storage.type.elm) -(type_ref) @storage.type.elm -(type_alias_declaration name: (upper_case_identifier) @storage.type.elm) - -(union_variant(upper_case_identifier) @union.elm) -(union_pattern) @union.elm -(value_expr(upper_case_qid(upper_case_identifier)) @union.elm) - -; comments -(line_comment) @comment.elm -(block_comment) @comment.elm - -; strings -(string_escape) @character.escape.elm - -(open_quote) @string.elm -(close_quote) @string.elm -(regular_string_part) @string.elm - -(open_char) @char.elm -(close_char) @char.elm - - -; glsl -(glsl_content) @source.glsl diff --git a/tests/snapshot_tests/language_snippets.py b/tests/snapshot_tests/language_snippets.py index 6b55775159..b445e8de8c 100644 --- a/tests/snapshot_tests/language_snippets.py +++ b/tests/snapshot_tests/language_snippets.py @@ -452,6 +452,77 @@ def say_hello(): a(?!b) # Negative lookahead: matches "a" that is not followed by "b" """ +GO = """\ +package main + +import ( + "fmt" + "math" + "strings" +) + +const PI = 3.14159 + +type Shape interface { + Area() float64 +} + +type Circle struct { + Radius float64 +} + +func (c Circle) Area() float64 { + return PI * c.Radius * c.Radius +} + +func main() { + var name string = "John" + age := 30 + isStudent := true + + fmt.Printf("Hello, %s! You are %d years old.\n", name, age) + + if age >= 18 && isStudent { + fmt.Println("You are an adult student.") + } else if age >= 18 { + fmt.Println("You are an adult.") + } else { + fmt.Println("You are a minor.") + } + + numbers := []int{1, 2, 3, 4, 5} + sum := 0 + for _, num := range numbers { + sum += num + } + fmt.Printf("The sum is: %d\n", sum) + + message := "Hello, World!" + uppercaseMessage := strings.ToUpper(message) + fmt.Println(uppercaseMessage) + + circle := Circle{Radius: 5} + fmt.Printf("Circle area: %.2f\n", circle.Area()) + + result := factorial(5) + fmt.Printf("Factorial of 5: %d\n", result) + + defer fmt.Println("Program finished.") + + sqrt := func(x float64) float64 { + return math.Sqrt(x) + } + fmt.Printf("Square root of 16: %.2f\n", sqrt(16)) +} + +func factorial(n int) int { + if n == 0 { + return 1 + } + return n * factorial(n-1) +} +""" + SNIPPETS = { "python": PYTHON, "markdown": MARKDOWN, @@ -462,4 +533,5 @@ def say_hello(): "html": HTML, "json": JSON, "regex": REGEX, + "go": GO, } From 64ded73eb01258bf3836bd1996a951472e859975 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Wed, 27 Mar 2024 14:16:45 +0000 Subject: [PATCH 14/53] Add JavaScript and Bash to TextArea --- tests/snapshot_tests/language_snippets.py | 184 ++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/tests/snapshot_tests/language_snippets.py b/tests/snapshot_tests/language_snippets.py index b445e8de8c..d159803cd9 100644 --- a/tests/snapshot_tests/language_snippets.py +++ b/tests/snapshot_tests/language_snippets.py @@ -523,6 +523,188 @@ def say_hello(): } """ +JAVASCRIPT = """\ +// Variable declarations +const name = "John"; +let age = 30; +var isStudent = true; + +// Template literals +console.log(`Hello, ${name}! You are ${age} years old.`); + +// Conditional statements +if (age >= 18 && isStudent) { + console.log("You are an adult student."); +} else if (age >= 18) { + console.log("You are an adult."); +} else { + console.log("You are a minor."); +} + +// Arrays and array methods +const numbers = [1, 2, 3, 4, 5]; +const doubledNumbers = numbers.map((num) => num * 2); +console.log("Doubled numbers:", doubledNumbers); + +// Objects +const person = { + firstName: "John", + lastName: "Doe", + getFullName() { + return `${this.firstName} ${this.lastName}`; + }, +}; +console.log("Full name:", person.getFullName()); + +// Classes +class Rectangle { + constructor(width, height) { + this.width = width; + this.height = height; + } + + getArea() { + return this.width * this.height; + } +} +const rectangle = new Rectangle(5, 3); +console.log("Rectangle area:", rectangle.getArea()); + +// Async/Await and Promises +async function fetchData() { + try { + const response = await fetch("https://api.example.com/data"); + const data = await response.json(); + console.log("Fetched data:", data); + } catch (error) { + console.error("Error:", error); + } +} +fetchData(); + +// Arrow functions +const greet = (name) => { + console.log(`Hello, ${name}!`); +}; +greet("Alice"); + +// Destructuring assignment +const [a, b, ...rest] = [1, 2, 3, 4, 5]; +console.log(a, b, rest); + +// Spread operator +const arr1 = [1, 2, 3]; +const arr2 = [4, 5, 6]; +const combinedArr = [...arr1, ...arr2]; +console.log("Combined array:", combinedArr); + +// Ternary operator +const message = age >= 18 ? "You are an adult." : "You are a minor."; +console.log(message); +""" + +BASH = """\ +#!/bin/bash + +# Variables +name="John" +age=30 +is_student=true + +# Printing variables +echo "Hello, $name! You are $age years old." + +# Conditional statements +if [[ $age -ge 18 && $is_student == true ]]; then + echo "You are an adult student." +elif [[ $age -ge 18 ]]; then + echo "You are an adult." +else + echo "You are a minor." +fi + +# Arrays +numbers=(1 2 3 4 5) +echo "Numbers: ${numbers[@]}" + +# Loops +for num in "${numbers[@]}"; do + echo "Number: $num" +done + +# Functions +greet() { + local name=$1 + echo "Hello, $name!" +} +greet "Alice" + +# Command substitution +current_date=$(date +%Y-%m-%d) +echo "Current date: $current_date" + +# File operations +touch file.txt +echo "Some content" > file.txt +cat file.txt + +# Conditionals with file checks +if [[ -f file.txt ]]; then + echo "file.txt exists." +else + echo "file.txt does not exist." +fi + +# Case statement +case $age in + 18) + echo "You are 18 years old." + ;; + 30) + echo "You are 30 years old." + ;; + *) + echo "You are neither 18 nor 30 years old." + ;; +esac + +# While loop +counter=0 +while [[ $counter -lt 5 ]]; do + echo "Counter: $counter" + ((counter++)) +done + +# Until loop +until [[ $counter -eq 0 ]]; do + echo "Counter: $counter" + ((counter--)) +done + +# Heredoc +cat << EOF +This is a heredoc. +It allows you to write multiple lines of text. +EOF + +# Redirection +ls > file_list.txt +grep "file" file_list.txt > filtered_list.txt + +# Pipes +cat file_list.txt | wc -l + +# Arithmetic operations +result=$((10 + 5)) +echo "Result: $result" + +# Exporting variables +export DB_PASSWORD="secret" + +# Sourcing external files +source config.sh +""" + SNIPPETS = { "python": PYTHON, "markdown": MARKDOWN, @@ -534,4 +716,6 @@ def say_hello(): "json": JSON, "regex": REGEX, "go": GO, + "javascript": JAVASCRIPT, + "bash": BASH, } From 732cf54ffce0d373c805fd7deb73397c7147dd5e Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Wed, 27 Mar 2024 14:29:52 +0000 Subject: [PATCH 15/53] Adding Rust support --- src/textual/_text_area_theme.py | 4 + tests/snapshot_tests/language_snippets.py | 207 ++++++++++++++++++++++ 2 files changed, 211 insertions(+) diff --git a/src/textual/_text_area_theme.py b/src/textual/_text_area_theme.py index b9f920600b..a40252322b 100644 --- a/src/textual/_text_area_theme.py +++ b/src/textual/_text_area_theme.py @@ -223,6 +223,7 @@ def default(cls) -> TextAreaTheme: "method": Style(color="#A6E22E"), "method.call": Style(color="#A6E22E"), "boolean": Style(color="#66D9EF", italic=True), + "constant.builtin": Style(color="#66D9EF", italic=True), "json.null": Style(color="#66D9EF", italic=True), "regex.punctuation.bracket": Style(color="#F92672"), "regex.operator": Style(color="#F92672"), @@ -272,6 +273,7 @@ def default(cls) -> TextAreaTheme: "method": Style(color="#50fa7b"), "method.call": Style(color="#50fa7b"), "boolean": Style(color="#bd93f9"), + "constant.builtin": Style(color="#bd93f9"), "json.null": Style(color="#bd93f9"), "regex.punctuation.bracket": Style(color="#ff79c6"), "regex.operator": Style(color="#ff79c6"), @@ -321,6 +323,7 @@ def default(cls) -> TextAreaTheme: "method": Style(color="#4EC9B0"), "method.call": Style(color="#4EC9B0"), "boolean": Style(color="#7DAF9C"), + "constant.builtin": Style(color="#7DAF9C"), "json.null": Style(color="#7DAF9C"), "tag": Style(color="#EFCB43"), "yaml.field": Style(color="#569cd6", bold=True), @@ -366,6 +369,7 @@ def default(cls) -> TextAreaTheme: "function": Style(color="#6639BB"), "method": Style(color="#6639BB"), "boolean": Style(color="#7DAF9C"), + "constant.builtin": Style(color="#7DAF9C"), "tag": Style(color="#6639BB"), "yaml.field": Style(color="#6639BB"), "json.label": Style(color="#6639BB"), diff --git a/tests/snapshot_tests/language_snippets.py b/tests/snapshot_tests/language_snippets.py index d159803cd9..21134866cf 100644 --- a/tests/snapshot_tests/language_snippets.py +++ b/tests/snapshot_tests/language_snippets.py @@ -705,6 +705,211 @@ class Rectangle { source config.sh """ +KOTLIN = """\ +// Variables +val name = "John" +var age = 30 +var isStudent = true + +// Printing variables +println("Hello, $name! You are $age years old.") + +// Conditional statements +when { + age >= 18 && isStudent -> println("You are an adult student.") + age >= 18 -> println("You are an adult.") + else -> println("You are a minor.") +} + +// Arrays +val numbers = arrayOf(1, 2, 3, 4, 5) +println("Numbers: ${numbers.contentToString()}") + +// Lists +val fruits = listOf("apple", "banana", "orange") +println("Fruits: $fruits") + +// Loops +for (num in numbers) { + println("Number: $num") +} + +// Functions +fun greet(name: String) { + println("Hello, $name!") +} +greet("Alice") + +// Lambda functions +val square = { num: Int -> num * num } +println("Square of 5: ${square(5)}") + +// Extension functions +fun String.reverse(): String { + return this.reversed() +} +val reversed = "Hello".reverse() +println("Reversed: $reversed") + +// Data classes +data class Person(val name: String, val age: Int) +val person = Person("John", 30) +println("Person: $person") + +// Null safety +var nullable: String? = null +println("Length: ${nullable?.length}") + +// Elvis operator +val length = nullable?.length ?: 0 +println("Length (Elvis): $length") + +// Smart casts +fun printLength(obj: Any) { + if (obj is String) { + println("Length: ${obj.length}") + } +} +printLength("Hello") + +// Object expressions +val comparator = object : Comparator { + override fun compare(a: Int, b: Int): Int { + return a - b + } +} +val sortedNumbers = numbers.sortedWith(comparator) +println("Sorted numbers: ${sortedNumbers.contentToString()}") + +// Companion objects +class MyClass { + companion object { + fun create(): MyClass { + return MyClass() + } + } +} +val obj = MyClass.create() + +// Sealed classes +sealed class Result { + data class Success(val data: String) : Result() + data class Error(val message: String) : Result() +} +val result: Result = Result.Success("Data") +when (result) { + is Result.Success -> println("Success: ${result.data}") + is Result.Error -> println("Error: ${result.message}") +} +""" + +RUST = """\ +use std::collections::HashMap; + +// Constants +const PI: f64 = 3.14159; + +// Structs +struct Rectangle { + width: u32, + height: u32, +} + +impl Rectangle { + fn area(&self) -> u32 { + self.width * self.height + } +} + +// Enums +enum Result { + Ok(T), + Err(E), +} + +// Functions +fn greet(name: &str) { + println!("Hello, {}!", name); +} + +fn main() { + // Variables + let name = "John"; + let mut age = 30; + let is_student = true; + + // Printing variables + println!("Hello, {}! You are {} years old.", name, age); + + // Conditional statements + if age >= 18 && is_student { + println!("You are an adult student."); + } else if age >= 18 { + println!("You are an adult."); + } else { + println!("You are a minor."); + } + + // Arrays + let numbers = [1, 2, 3, 4, 5]; + println!("Numbers: {:?}", numbers); + + // Vectors + let mut fruits = vec!["apple", "banana", "orange"]; + fruits.push("grape"); + println!("Fruits: {:?}", fruits); + + // Loops + for num in &numbers { + println!("Number: {}", num); + } + + // Pattern matching + let result = Result::Ok(42); + match result { + Result::Ok(value) => println!("Value: {}", value), + Result::Err(error) => println!("Error: {:?}", error), + } + + // Ownership and borrowing + let s1 = String::from("hello"); + let s2 = s1.clone(); + println!("s1: {}, s2: {}", s1, s2); + + // References + let rect = Rectangle { + width: 10, + height: 20, + }; + println!("Rectangle area: {}", rect.area()); + + // Hash maps + let mut scores = HashMap::new(); + scores.insert("Alice", 100); + scores.insert("Bob", 80); + println!("Alice's score: {}", scores["Alice"]); + + // Closures + let square = |num: i32| num * num; + println!("Square of 5: {}", square(5)); + + // Traits + trait Printable { + fn print(&self); + } + + impl Printable for Rectangle { + fn print(&self) { + println!("Rectangle: width={}, height={}", self.width, self.height); + } + } + rect.print(); + + // Modules + greet("Alice"); +} +""" + SNIPPETS = { "python": PYTHON, "markdown": MARKDOWN, @@ -718,4 +923,6 @@ class Rectangle { "go": GO, "javascript": JAVASCRIPT, "bash": BASH, + "kotlin": KOTLIN, + "rust": RUST, } From 95675eaf1f876887df67ac157ce11bc265f72865 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Wed, 27 Mar 2024 15:21:32 +0000 Subject: [PATCH 16/53] Fix Golang snippet in TextArea highlighting test --- src/textual/tree-sitter/highlights/json.scm | 34 ++++++++++++++----- src/textual/tree-sitter/highlights/python.scm | 14 -------- tests/snapshot_tests/language_snippets.py | 10 +++--- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/textual/tree-sitter/highlights/json.scm b/src/textual/tree-sitter/highlights/json.scm index ece8392f0b..c23e7b3ce9 100644 --- a/src/textual/tree-sitter/highlights/json.scm +++ b/src/textual/tree-sitter/highlights/json.scm @@ -1,16 +1,32 @@ -(pair - key: (_) @string.special.key) +[ + (true) + (false) +] @boolean -(string) @string +(null) @json.null (number) @number +(pair key: (string) @json.label) +(pair value: (string) @string) + +(array (string) @string) + +(string_content) @spell + +(ERROR) @json.error + +["," ":"] @punctuation.delimiter + [ - (null) - (true) - (false) -] @constant.builtin + "[" "]" + "{" "}" +] @punctuation.bracket -(escape_sequence) @escape +(("\"" @conceal) + (#set! conceal "")) -(comment) @comment +(escape_sequence) @string.escape +((escape_sequence) @conceal + (#eq? @conceal "\\\"") + (#set! conceal "\"")) diff --git a/src/textual/tree-sitter/highlights/python.scm b/src/textual/tree-sitter/highlights/python.scm index 37aceef1fd..6f844b6135 100644 --- a/src/textual/tree-sitter/highlights/python.scm +++ b/src/textual/tree-sitter/highlights/python.scm @@ -15,20 +15,6 @@ ((identifier) @constant (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) -((identifier) @constant.builtin - (#lua-match? @constant.builtin "^__[a-zA-Z0-9_]*__$")) - -((identifier) @constant.builtin - (#any-of? @constant.builtin - ;; https://docs.python.org/3/library/constants.html - "NotImplemented" - "Ellipsis" - "quit" - "exit" - "copyright" - "credits" - "license")) - ((attribute attribute: (identifier) @field) (#match? @field "^([A-Z])@!.*$")) diff --git a/tests/snapshot_tests/language_snippets.py b/tests/snapshot_tests/language_snippets.py index 21134866cf..89ef6c9c29 100644 --- a/tests/snapshot_tests/language_snippets.py +++ b/tests/snapshot_tests/language_snippets.py @@ -480,7 +480,7 @@ def say_hello(): age := 30 isStudent := true - fmt.Printf("Hello, %s! You are %d years old.\n", name, age) + fmt.Printf("Hello, %s! You are %d years old.", name, age) if age >= 18 && isStudent { fmt.Println("You are an adult student.") @@ -495,24 +495,24 @@ def say_hello(): for _, num := range numbers { sum += num } - fmt.Printf("The sum is: %d\n", sum) + fmt.Printf("The sum is: %d", sum) message := "Hello, World!" uppercaseMessage := strings.ToUpper(message) fmt.Println(uppercaseMessage) circle := Circle{Radius: 5} - fmt.Printf("Circle area: %.2f\n", circle.Area()) + fmt.Printf("Circle area: %.2f", circle.Area()) result := factorial(5) - fmt.Printf("Factorial of 5: %d\n", result) + fmt.Printf("Factorial of 5: %d", result) defer fmt.Println("Program finished.") sqrt := func(x float64) float64 { return math.Sqrt(x) } - fmt.Printf("Square root of 16: %.2f\n", sqrt(16)) + fmt.Printf("Square root of 16: %.2f", sqrt(16)) } func factorial(n int) int { From 1c75dd117e83327f3c763ff1cbc3d9d2d0e887f3 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 27 Mar 2024 15:47:03 +0000 Subject: [PATCH 17/53] inline driver --- src/textual/_compositor.py | 56 ++++++++++++++++++++-- src/textual/app.py | 15 ++++-- src/textual/drivers/linux_inline_driver.py | 3 ++ src/textual/screen.py | 16 ++++++- 4 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index 6b786e8b26..426f38458e 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -144,6 +144,41 @@ def __rich_repr__(self) -> rich.repr.Result: yield self.region +@rich.repr.auto(angular=True) +class InlineUpdate(CompositorUpdate): + + def __init__(self, strips: list[Strip]) -> None: + self.strips = strips + + def __rich_console__( + self, console: Console, options: ConsoleOptions + ) -> RenderResult: + new_line = Segment.line() + for last, line in loop_last(self.strips): + yield from line + if not last: + yield new_line + + def render_segments(self, console: Console) -> str: + """Render the update to raw data, suitable for writing to terminal. + + Args: + console: Console instance. + + Returns: + Raw data with escape sequences. + """ + sequences: list[str] = [] + append = sequences.append + append("\x1b[J") + for last, strip in loop_last(self.strips): + append(strip.render(console)) + if not last: + append("\n") + append(f"\x1b[{len(self.strips)-1}A\r") + return "".join(sequences) + + @rich.repr.auto(angular=True) class ChopsUpdate(CompositorUpdate): """A renderable that applies updated spans to the screen.""" @@ -966,6 +1001,19 @@ def render_update( else: return self.render_partial_update() + def render_inline( + self, size: Size, screen_stack: list[Screen] | None = None + ) -> RenderableType: + visible_screen_stack.set([] if screen_stack is None else screen_stack) + + # from rich.live_render import LiveRender + + # from textual.strip import StripRenderable + + # return LiveRender(StripRenderable(self.render_strips(size), size.width)) + + return InlineUpdate(self.render_strips(size)) + def render_full_update(self) -> LayoutUpdate: """Render a full update. @@ -999,14 +1047,16 @@ def render_partial_update(self) -> ChopsUpdate | None: chop_ends = [cut_set[1:] for cut_set in self.cuts] return ChopsUpdate(chops, spans, chop_ends) - def render_strips(self) -> list[Strip]: + def render_strips(self, size: Size | None = None) -> list[Strip]: """Render to a list of strips. Returns: A list of strips with the screen content. """ - chops = self._render_chops(self.size.region, lambda y: True) - render_strips = [Strip.join(chop.values()) for chop in chops] + if size is None: + size = self.size + chops = self._render_chops(size.region, lambda y: True) + render_strips = [Strip.join(chop.values()) for chop in chops[: size.height]] return render_strips def _render_chops( diff --git a/src/textual/app.py b/src/textual/app.py index 7d0df77560..06225953d1 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -777,12 +777,17 @@ def debug(self) -> bool: @property def is_headless(self) -> bool: - """Is the driver running in 'headless' mode? + """Is the app running in 'headless' mode? Headless mode is used when running tests with [run_test][textual.app.App.run_test]. """ return False if self._driver is None else self._driver.is_headless + @property + def is_inline(self) -> bool: + """Is the app running in 'inline' mode?""" + return False if self._driver is None else self._driver.is_inline + @property def screen_stack(self) -> Sequence[Screen[Any]]: """A snapshot of the current screen stack. @@ -977,6 +982,7 @@ def __rich_repr__(self) -> rich.repr.Result: @property def animator(self) -> Animator: + """The animator object.""" return self._animator @property @@ -2751,9 +2757,10 @@ def _display(self, screen: Screen, renderable: RenderableType | None) -> None: if isinstance(renderable, CompositorUpdate): cursor_x, cursor_y = self.cursor_position terminal_sequence = renderable.render_segments(console) - terminal_sequence += Control.move_to( - cursor_x, cursor_y - ).segment.text + if not self.is_inline: + terminal_sequence += Control.move_to( + cursor_x, cursor_y + ).segment.text else: segments = console.render(renderable) terminal_sequence = console._render_buffer(segments) diff --git a/src/textual/drivers/linux_inline_driver.py b/src/textual/drivers/linux_inline_driver.py index 096b9582e0..8bbefa8373 100644 --- a/src/textual/drivers/linux_inline_driver.py +++ b/src/textual/drivers/linux_inline_driver.py @@ -250,6 +250,8 @@ def stop_application_mode(self) -> None: self._disable_bracketed_paste() self.disable_input() + self.write("\x1b[A\x1b[J") + if self.attrs_before is not None: try: termios.tcsetattr(self.fileno, termios.TCSANOW, self.attrs_before) @@ -258,4 +260,5 @@ def stop_application_mode(self) -> None: self.write("\x1b[?25h") # Show cursor self.write("\033[?1004l\n") # Disable FocusIn/FocusOut. + self.flush() diff --git a/src/textual/screen.py b/src/textual/screen.py index b9713e7256..0567af2c00 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -664,7 +664,21 @@ async def _on_idle(self, event: events.Idle) -> None: def _compositor_refresh(self) -> None: """Perform a compositor refresh.""" - if self is self.app.screen: + + if self.app.is_inline: + size = self.app.size + inline_height = self.get_content_height(size, size, size.width) + self.app._display( + self, + self._compositor.render_inline( + Size(size.width, inline_height), + screen_stack=self.app._background_screens, + ), + ) + self._dirty_widgets.clear() + self._compositor._dirty_regions.clear() + + elif self is self.app.screen: # Top screen update = self._compositor.render_update( screen_stack=self.app._background_screens From b5a646d7a9efd9637e0d11efb629f5929ce46d27 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Wed, 27 Mar 2024 15:56:29 +0000 Subject: [PATCH 18/53] Remove spelling check from toml scm file --- src/textual/tree-sitter/highlights/toml.scm | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/textual/tree-sitter/highlights/toml.scm b/src/textual/tree-sitter/highlights/toml.scm index e4d6966fcb..26e3982fef 100644 --- a/src/textual/tree-sitter/highlights/toml.scm +++ b/src/textual/tree-sitter/highlights/toml.scm @@ -1,21 +1,22 @@ ; Properties ;----------- -(bare_key) @property +(bare_key) @toml.type (quoted_key) @string +(pair (bare_key)) @property ; Literals ;--------- -(boolean) @constant.builtin +(boolean) @boolean (comment) @comment (string) @string (integer) @number -(float) @number -(offset_date_time) @string.special -(local_date_time) @string.special -(local_date) @string.special -(local_time) @string.special +(float) @float +(offset_date_time) @toml.datetime +(local_date_time) @toml.datetime +(local_date) @toml.datetime +(local_time) @toml.datetime ; Punctuation ;------------ @@ -23,7 +24,7 @@ "." @punctuation.delimiter "," @punctuation.delimiter -"=" @operator +"=" @toml.operator "[" @punctuation.bracket "]" @punctuation.bracket @@ -31,3 +32,5 @@ "]]" @punctuation.bracket "{" @punctuation.bracket "}" @punctuation.bracket + +(ERROR) @toml.error From 69d1de6a347d608fcb59fe2da53620ed6faf275b Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 27 Mar 2024 15:56:34 +0000 Subject: [PATCH 19/53] clear on resize --- src/textual/drivers/linux_inline_driver.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/textual/drivers/linux_inline_driver.py b/src/textual/drivers/linux_inline_driver.py index 8bbefa8373..9ffdeb9585 100644 --- a/src/textual/drivers/linux_inline_driver.py +++ b/src/textual/drivers/linux_inline_driver.py @@ -163,6 +163,7 @@ def send_size_event() -> None: ) def on_terminal_resize(signum, stack) -> None: + self.write("\x1b[J") send_size_event() signal.signal(signal.SIGWINCH, on_terminal_resize) From 4247753d54198f65f27d3958eb07c929fcd8b49f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 27 Mar 2024 17:53:44 +0000 Subject: [PATCH 20/53] calculate inline height --- examples/calculator.py | 2 +- src/textual/_compositor.py | 5 ++++- src/textual/css/constants.py | 1 + src/textual/demo.tcss | 3 +++ src/textual/screen.py | 23 +++++++++++++++++++++++ src/textual/widget.py | 2 ++ 6 files changed, 34 insertions(+), 2 deletions(-) diff --git a/examples/calculator.py b/examples/calculator.py index 9c8f2f9e76..8f6442ebee 100644 --- a/examples/calculator.py +++ b/examples/calculator.py @@ -168,4 +168,4 @@ def pressed_equals(self) -> None: if __name__ == "__main__": - CalculatorApp().run() + CalculatorApp().run(inline=True) diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index 426f38458e..000d0c2a57 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -175,7 +175,10 @@ def render_segments(self, console: Console) -> str: append(strip.render(console)) if not last: append("\n") - append(f"\x1b[{len(self.strips)-1}A\r") + if len(self.strips) > 1: + append(f"\x1b[{len(self.strips)-1}A\r") + else: + append("\r") return "".join(sequences) diff --git a/src/textual/css/constants.py b/src/textual/css/constants.py index 52c13cb067..27c662dde9 100644 --- a/src/textual/css/constants.py +++ b/src/textual/css/constants.py @@ -68,6 +68,7 @@ "focus-within", "focus", "hover", + "inline", "light", } VALID_OVERLAY: Final = {"none", "screen"} diff --git a/src/textual/demo.tcss b/src/textual/demo.tcss index 2e9d54e25b..fc5f45b23f 100644 --- a/src/textual/demo.tcss +++ b/src/textual/demo.tcss @@ -5,6 +5,9 @@ Screen { layers: base overlay notes notifications; overflow: hidden; + &:inline { + height: 50vh; + } } diff --git a/src/textual/screen.py b/src/textual/screen.py index 0567af2c00..52ff33207c 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -137,6 +137,11 @@ class Screen(Generic[ScreenResultType], Widget): layout: vertical; overflow-y: auto; background: $surface; + + &:inline { + height: auto; + min-height: 1; + } } """ @@ -668,6 +673,8 @@ def _compositor_refresh(self) -> None: if self.app.is_inline: size = self.app.size inline_height = self.get_content_height(size, size, size.width) + inline_height = self.size.height + self.app._display( self, self._compositor.render_inline( @@ -768,6 +775,8 @@ def _pop_result_callback(self) -> None: def _refresh_layout(self, size: Size | None = None, scroll: bool = False) -> None: """Refresh the layout (can change size and positions of widgets).""" size = self.outer_size if size is None else size + if self.app.is_inline: + size = Size(size.width, self.get_inline_height(self.app.size)) if not size: return self._compositor.update_widgets(self._dirty_widgets) @@ -860,6 +869,20 @@ async def _on_update_scroll(self, message: messages.UpdateScroll) -> None: self._scroll_required = True self.check_idle() + def get_inline_height(self, size: Size) -> int: + height_scalar = self.styles.height + if height_scalar is None or height_scalar.is_auto: + inline_height = self.get_content_height(size, size, size.width) + else: + inline_height = int(height_scalar.resolve(size, size)) + min_height = self.styles.min_height + max_height = self.styles.max_height + if min_height is not None: + inline_height = max(inline_height, int(min_height.resolve(size, size))) + if max_height is not None: + inline_height = min(inline_height, int(max_height.resolve(size, size))) + return inline_height + def _screen_resized(self, size: Size): """Called by App when the screen is resized.""" self._refresh_layout(size) diff --git a/src/textual/widget.py b/src/textual/widget.py index 4bfaabdee0..5158d524bf 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -3064,6 +3064,8 @@ def get_pseudo_classes(self) -> Iterable[str]: yield "focus-within" break node = node._parent + if self.app.is_inline: + yield "inline" def get_pseudo_class_state(self) -> PseudoClasses: """Get an object describing whether each pseudo class is present on this object or not. From 2a06e288ea318870990e975395a9a2f0b54c968e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Gr=C3=A9goire?= <32612304+valentingregoire@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:22:27 +0100 Subject: [PATCH 21/53] fix: Typo fixed. --- docs/guide/animation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/animation.md b/docs/guide/animation.md index 50bc87d35d..dde2ec7bb3 100644 --- a/docs/guide/animation.md +++ b/docs/guide/animation.md @@ -1,6 +1,6 @@ # Animation -Ths chapter discusses how to use Textual's animation system to create visual effects such as movement, blending, and fading. +Thss chapter discusses how to use Textual's animation system to create visual effects such as movement, blending, and fading. ## Animating styles From f05d727c01dcb62cf60b81cd23cf24e8f8a99a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Gr=C3=A9goire?= <32612304+valentingregoire@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:22:55 +0100 Subject: [PATCH 22/53] =?UTF-8?q?fix:=20Fix=20the=20fix=20to=20fix=20the?= =?UTF-8?q?=20typo=20=F0=9F=98=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide/animation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/animation.md b/docs/guide/animation.md index dde2ec7bb3..1bf55fb203 100644 --- a/docs/guide/animation.md +++ b/docs/guide/animation.md @@ -1,6 +1,6 @@ # Animation -Thss chapter discusses how to use Textual's animation system to create visual effects such as movement, blending, and fading. +This chapter discusses how to use Textual's animation system to create visual effects such as movement, blending, and fading. ## Animating styles From 70a026c5eaad72414c930651bb638f4e61c91013 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 28 Mar 2024 10:19:14 +0000 Subject: [PATCH 23/53] Supporting a few new languages --- src/textual/document/_languages.py | 25 -- src/textual/tree-sitter/highlights/c.scm | 81 ---- .../tree-sitter/highlights/c_sharp.scm | 254 ----------- src/textual/tree-sitter/highlights/cpp.scm | 74 ---- src/textual/tree-sitter/highlights/css.scm | 129 +++--- .../tree-sitter/highlights/dockerfile.scm | 51 --- src/textual/tree-sitter/highlights/dot.scm | 46 -- src/textual/tree-sitter/highlights/elisp.scm | 72 ---- src/textual/tree-sitter/highlights/elixir.scm | 199 --------- .../highlights/embedded_template.scm | 12 - src/textual/tree-sitter/highlights/erlang.scm | 228 ---------- .../tree-sitter/highlights/fortran.scm | 197 --------- src/textual/tree-sitter/highlights/gomod.scm | 17 - src/textual/tree-sitter/highlights/hack.scm | 31 -- .../tree-sitter/highlights/haskell.scm | 156 ------- src/textual/tree-sitter/highlights/jsdoc.scm | 2 - src/textual/tree-sitter/highlights/make.scm | 171 -------- src/textual/tree-sitter/highlights/objc.scm | 395 ------------------ src/textual/tree-sitter/highlights/ocaml.scm | 151 ------- src/textual/tree-sitter/highlights/php.scm | 122 ------ src/textual/tree-sitter/highlights/r.scm | 128 ------ src/textual/tree-sitter/highlights/ruby.scm | 154 ------- src/textual/tree-sitter/highlights/scala.scm | 261 ------------ src/textual/tree-sitter/highlights/sqlite.scm | 179 -------- .../tree-sitter/highlights/typescript.scm | 35 -- tests/snapshot_tests/language_snippets.py | 107 +++++ 26 files changed, 185 insertions(+), 3092 deletions(-) delete mode 100644 src/textual/tree-sitter/highlights/c.scm delete mode 100644 src/textual/tree-sitter/highlights/c_sharp.scm delete mode 100644 src/textual/tree-sitter/highlights/cpp.scm delete mode 100644 src/textual/tree-sitter/highlights/dockerfile.scm delete mode 100644 src/textual/tree-sitter/highlights/dot.scm delete mode 100644 src/textual/tree-sitter/highlights/elisp.scm delete mode 100644 src/textual/tree-sitter/highlights/elixir.scm delete mode 100644 src/textual/tree-sitter/highlights/embedded_template.scm delete mode 100644 src/textual/tree-sitter/highlights/erlang.scm delete mode 100644 src/textual/tree-sitter/highlights/fortran.scm delete mode 100644 src/textual/tree-sitter/highlights/gomod.scm delete mode 100644 src/textual/tree-sitter/highlights/hack.scm delete mode 100644 src/textual/tree-sitter/highlights/haskell.scm delete mode 100644 src/textual/tree-sitter/highlights/jsdoc.scm delete mode 100644 src/textual/tree-sitter/highlights/make.scm delete mode 100644 src/textual/tree-sitter/highlights/objc.scm delete mode 100644 src/textual/tree-sitter/highlights/ocaml.scm delete mode 100644 src/textual/tree-sitter/highlights/php.scm delete mode 100644 src/textual/tree-sitter/highlights/r.scm delete mode 100644 src/textual/tree-sitter/highlights/ruby.scm delete mode 100644 src/textual/tree-sitter/highlights/scala.scm delete mode 100644 src/textual/tree-sitter/highlights/sqlite.scm delete mode 100644 src/textual/tree-sitter/highlights/typescript.scm diff --git a/src/textual/document/_languages.py b/src/textual/document/_languages.py index efc5aa8c42..d075dfd368 100644 --- a/src/textual/document/_languages.py +++ b/src/textual/document/_languages.py @@ -1,43 +1,18 @@ BUILTIN_LANGUAGES = sorted( [ "bash", - "c", - "c_sharp", - "cpp", "css", - "dockerfile", - "dot", - "elisp", - "elixir", - "elm", - "embedded_template", - "erlang", - "fortran", "go", - "gomod", - "hack", - "haskell", "html", "java", "javascript", - "jsdoc", "json", "kotlin", - "make", "markdown", - "objc", - "ocaml", - "php", "python", - "r", - "regex", - "ruby", "rust", - "scala", "sql", - "sqlite", "toml", - "typescript", "yaml", ] ) diff --git a/src/textual/tree-sitter/highlights/c.scm b/src/textual/tree-sitter/highlights/c.scm deleted file mode 100644 index 04d9a04f38..0000000000 --- a/src/textual/tree-sitter/highlights/c.scm +++ /dev/null @@ -1,81 +0,0 @@ -"break" @keyword -"case" @keyword -"const" @keyword -"continue" @keyword -"default" @keyword -"do" @keyword -"else" @keyword -"enum" @keyword -"extern" @keyword -"for" @keyword -"if" @keyword -"inline" @keyword -"return" @keyword -"sizeof" @keyword -"static" @keyword -"struct" @keyword -"switch" @keyword -"typedef" @keyword -"union" @keyword -"volatile" @keyword -"while" @keyword - -"#define" @keyword -"#elif" @keyword -"#else" @keyword -"#endif" @keyword -"#if" @keyword -"#ifdef" @keyword -"#ifndef" @keyword -"#include" @keyword -(preproc_directive) @keyword - -"--" @operator -"-" @operator -"-=" @operator -"->" @operator -"=" @operator -"!=" @operator -"*" @operator -"&" @operator -"&&" @operator -"+" @operator -"++" @operator -"+=" @operator -"<" @operator -"==" @operator -">" @operator -"||" @operator - -"." @delimiter -";" @delimiter - -(string_literal) @string -(system_lib_string) @string - -(null) @constant -(number_literal) @number -(char_literal) @number - -(call_expression - function: (identifier) @function) -(call_expression - function: (field_expression - field: (field_identifier) @function)) -(function_declarator - declarator: (identifier) @function) -(preproc_function_def - name: (identifier) @function.special) - -(field_identifier) @property -(statement_identifier) @label -(type_identifier) @type -(primitive_type) @type -(sized_type_specifier) @type - -((identifier) @constant - (#match? @constant "^[A-Z][A-Z\\d_]*$")) - -(identifier) @variable - -(comment) @comment diff --git a/src/textual/tree-sitter/highlights/c_sharp.scm b/src/textual/tree-sitter/highlights/c_sharp.scm deleted file mode 100644 index 12ab524df2..0000000000 --- a/src/textual/tree-sitter/highlights/c_sharp.scm +++ /dev/null @@ -1,254 +0,0 @@ -;; Methods -(method_declaration name: (identifier) @function) -(local_function_statement name: (identifier) @function) - -;; Types -(interface_declaration name: (identifier) @type) -(class_declaration name: (identifier) @type) -(enum_declaration name: (identifier) @type) -(struct_declaration (identifier) @type) -(record_declaration (identifier) @type) -(record_struct_declaration (identifier) @type) -(namespace_declaration name: (identifier) @module) - -(constructor_declaration name: (identifier) @constructor) -(destructor_declaration name: (identifier) @constructor) - -[ - (implicit_type) - (predefined_type) -] @type.builtin - -(_ type: (identifier) @type) - -;; Enum -(enum_member_declaration (identifier) @property.definition) - -;; Literals -[ - (real_literal) - (integer_literal) -] @number - -[ - (character_literal) - (string_literal) - (verbatim_string_literal) - (interpolated_string_text) - (interpolated_verbatim_string_text) - "\"" - "$\"" - "@$\"" - "$@\"" - ] @string - -[ - (boolean_literal) - (null_literal) -] @constant.builtin - -;; Comments -(comment) @comment - -;; Tokens -[ - ";" - "." - "," -] @punctuation.delimiter - -[ - "--" - "-" - "-=" - "&" - "&=" - "&&" - "+" - "++" - "+=" - "<" - "<=" - "<<" - "<<=" - "=" - "==" - "!" - "!=" - "=>" - ">" - ">=" - ">>" - ">>=" - ">>>" - ">>>=" - "|" - "|=" - "||" - "?" - "??" - "??=" - "^" - "^=" - "~" - "*" - "*=" - "/" - "/=" - "%" - "%=" - ":" -] @operator - -[ - "(" - ")" - "[" - "]" - "{" - "}" -] @punctuation.bracket - -;; Keywords -(modifier) @keyword -(this_expression) @keyword -(escape_sequence) @keyword - -[ - "add" - "alias" - "as" - "base" - "break" - "case" - "catch" - "checked" - "class" - "continue" - "default" - "delegate" - "do" - "else" - "enum" - "event" - "explicit" - "extern" - "finally" - "for" - "foreach" - "global" - "goto" - "if" - "implicit" - "interface" - "is" - "lock" - "namespace" - "notnull" - "operator" - "params" - "return" - "remove" - "sizeof" - "stackalloc" - "static" - "struct" - "switch" - "throw" - "try" - "typeof" - "unchecked" - "using" - "while" - "new" - "await" - "in" - "yield" - "get" - "set" - "when" - "out" - "ref" - "from" - "where" - "select" - "record" - "init" - "with" - "let" -] @keyword - - -;; Linq -(from_clause (identifier) @variable) -(group_clause (identifier) @variable) -(order_by_clause (identifier) @variable) -(join_clause (identifier) @variable) -(select_clause (identifier) @variable) -(query_continuation (identifier) @variable) @keyword - -;; Record -(with_expression - (with_initializer_expression - (simple_assignment_expression - (identifier) @variable))) - -;; Exprs -(binary_expression (identifier) @variable (identifier) @variable) -(binary_expression (identifier)* @variable) -(conditional_expression (identifier) @variable) -(prefix_unary_expression (identifier) @variable) -(postfix_unary_expression (identifier)* @variable) -(assignment_expression (identifier) @variable) -(cast_expression (_) (identifier) @variable) - -;; Class -(base_list (identifier) @type) ;; applies to record_base too -(property_declaration (generic_name)) -(property_declaration - name: (identifier) @variable) -(property_declaration - name: (identifier) @variable) -(property_declaration - name: (identifier) @variable) - -;; Lambda -(lambda_expression) @variable - -;; Attribute -(attribute) @attribute - -;; Parameter -(parameter - name: (identifier) @variable.parameter) -(parameter (identifier) @variable.parameter) -(parameter_modifier) @keyword - -;; Variable declarations -(variable_declarator (identifier) @variable) -(for_each_statement left: (identifier) @variable) -(catch_declaration (_) (identifier) @variable) - -;; Return -(return_statement (identifier) @variable) -(yield_statement (identifier) @variable) - -;; Type -(generic_name (identifier) @type) -(type_parameter (identifier) @property.definition) -(type_argument_list (identifier) @type) -(as_expression right: (identifier) @type) -(is_expression right: (identifier) @type) - -;; Type constraints -(type_parameter_constraints_clause (identifier) @property.definition) - -;; Switch -(switch_statement (identifier) @variable) -(switch_expression (identifier) @variable) - -;; Lock statement -(lock_statement (identifier) @variable) - -;; Method calls -(invocation_expression (member_access_expression name: (identifier) @function)) diff --git a/src/textual/tree-sitter/highlights/cpp.scm b/src/textual/tree-sitter/highlights/cpp.scm deleted file mode 100644 index 4d1f1c045e..0000000000 --- a/src/textual/tree-sitter/highlights/cpp.scm +++ /dev/null @@ -1,74 +0,0 @@ -; Functions - -(call_expression - function: (qualified_identifier - name: (identifier) @function)) - -(template_function - name: (identifier) @function) - -(template_method - name: (field_identifier) @function) - -(template_function - name: (identifier) @function) - -(function_declarator - declarator: (qualified_identifier - name: (identifier) @function)) - -(function_declarator - declarator: (qualified_identifier - name: (identifier) @function)) - -(function_declarator - declarator: (field_identifier) @function) - -; Types - -((namespace_identifier) @type - (#match? @type "^[A-Z]")) - -(auto) @type - -; Constants - -(this) @variable.builtin -(null "nullptr" @constant) - -; Keywords - -[ - "catch" - "class" - "co_await" - "co_return" - "co_yield" - "constexpr" - "constinit" - "consteval" - "delete" - "explicit" - "final" - "friend" - "mutable" - "namespace" - "noexcept" - "new" - "override" - "private" - "protected" - "public" - "template" - "throw" - "try" - "typename" - "using" - "virtual" - "concept" - "requires" -] @keyword - -; Strings - -(raw_string_literal) @string diff --git a/src/textual/tree-sitter/highlights/css.scm b/src/textual/tree-sitter/highlights/css.scm index 763661af76..b26f0ec96c 100644 --- a/src/textual/tree-sitter/highlights/css.scm +++ b/src/textual/tree-sitter/highlights/css.scm @@ -1,64 +1,91 @@ -(comment) @comment +[ + "@media" + "@charset" + "@namespace" + "@supports" + "@keyframes" + (at_keyword) + (to) + (from) + ] @keyword -(tag_name) @tag -(nesting_selector) @tag -(universal_selector) @tag +"@import" @include -"~" @operator -">" @operator -"+" @operator -"-" @operator -"*" @operator -"/" @operator -"=" @operator -"^=" @operator -"|=" @operator -"~=" @operator -"$=" @operator -"*=" @operator +(comment) @comment @spell -"and" @operator -"or" @operator -"not" @operator -"only" @operator +[ + (tag_name) + (nesting_selector) + (universal_selector) + ] @type + +(function_name) @function + +[ + "~" + ">" + "+" + "-" + "*" + "/" + "=" + "^=" + "|=" + "~=" + "$=" + "*=" + "and" + "or" + "not" + "only" + ] @operator + +(important) @type.qualifier (attribute_selector (plain_value) @string) -(pseudo_element_selector (tag_name) @attribute) -(pseudo_class_selector (class_name) @attribute) +(pseudo_element_selector "::" (tag_name) @property) +(pseudo_class_selector (class_name) @property) -(class_name) @property -(id_name) @property -(namespace_name) @property -(property_name) @property -(feature_name) @property +[ + (class_name) + (id_name) + (property_name) + (feature_name) + (attribute_name) + ] @property -(attribute_name) @attribute +(namespace_name) @namespace -(function_name) @function +((property_name) @type.definition + (#lua-match? @type.definition "^[-][-]")) +((plain_value) @type + (#lua-match? @type "^[-][-]")) -((property_name) @variable - (#match? @variable "^--")) -((plain_value) @variable - (#match? @variable "^--")) +[ + (string_value) + (color_value) + (unit) + ] @string -"@media" @keyword -"@import" @keyword -"@charset" @keyword -"@namespace" @keyword -"@supports" @keyword -"@keyframes" @keyword -(at_keyword) @keyword -(to) @keyword -(from) @keyword -(important) @keyword +[ + (integer_value) + (float_value) + ] @number -(string_value) @string -(color_value) @string.special +[ + "#" + "," + "." + ":" + "::" + ";" + ] @punctuation.delimiter -(integer_value) @number -(float_value) @number -(unit) @type +[ + "{" + ")" + "(" + "}" + ] @punctuation.bracket -"#" @punctuation.delimiter -"," @punctuation.delimiter -":" @punctuation.delimiter +(ERROR) @error diff --git a/src/textual/tree-sitter/highlights/dockerfile.scm b/src/textual/tree-sitter/highlights/dockerfile.scm deleted file mode 100644 index 5a945fb9bf..0000000000 --- a/src/textual/tree-sitter/highlights/dockerfile.scm +++ /dev/null @@ -1,51 +0,0 @@ -[ - "FROM" - "AS" - "RUN" - "CMD" - "LABEL" - "EXPOSE" - "ENV" - "ADD" - "COPY" - "ENTRYPOINT" - "VOLUME" - "USER" - "WORKDIR" - "ARG" - "ONBUILD" - "STOPSIGNAL" - "HEALTHCHECK" - "SHELL" - "MAINTAINER" - "CROSS_BUILD" -] @keyword - -[ - ":" - "@" -] @operator - -(comment) @comment - - -(image_spec - (image_tag - ":" @punctuation.special) - (image_digest - "@" @punctuation.special)) - -(double_quoted_string) @string - -(expansion - [ - "$" - "{" - "}" - ] @punctuation.special -) @none - -((variable) @constant - (#match? @constant "^[A-Z][A-Z_0-9]*$")) - - diff --git a/src/textual/tree-sitter/highlights/dot.scm b/src/textual/tree-sitter/highlights/dot.scm deleted file mode 100644 index d792cdb4b0..0000000000 --- a/src/textual/tree-sitter/highlights/dot.scm +++ /dev/null @@ -1,46 +0,0 @@ -(keyword) @keyword -(string_literal) @string -(number_literal) @number - -[ - (edgeop) - (operator) -] @operator - -[ - "," - ";" -] @punctuation.delimiter - -[ - "{" - "}" - "[" - "]" - "<" - ">" -] @punctuation.bracket - -(subgraph - id: (id - (identifier) @namespace) -) - -(attribute - name: (id - (identifier) @type) -) - -(attribute - value: (id - (identifier) @constant) -) - -[ -(comment) -(preproc) -] @comment - -(ERROR) @error - -(identifier) @variable diff --git a/src/textual/tree-sitter/highlights/elisp.scm b/src/textual/tree-sitter/highlights/elisp.scm deleted file mode 100644 index d78b960f04..0000000000 --- a/src/textual/tree-sitter/highlights/elisp.scm +++ /dev/null @@ -1,72 +0,0 @@ -;; Special forms -[ - "and" - "catch" - "cond" - "condition-case" - "defconst" - "defvar" - "function" - "if" - "interactive" - "lambda" - "let" - "let*" - "or" - "prog1" - "prog2" - "progn" - "quote" - "save-current-buffer" - "save-excursion" - "save-restriction" - "setq" - "setq-default" - "unwind-protect" - "while" -] @keyword - -;; Function definitions -[ - "defun" - "defsubst" - ] @keyword -(function_definition name: (symbol) @function) -(function_definition parameters: (list (symbol) @variable.parameter)) -(function_definition docstring: (string) @comment) - -;; Highlight macro definitions the same way as function definitions. -"defmacro" @keyword -(macro_definition name: (symbol) @function) -(macro_definition parameters: (list (symbol) @variable.parameter)) -(macro_definition docstring: (string) @comment) - -(comment) @comment - -(integer) @number -(float) @number -(char) @number - -(string) @string - -[ - "(" - ")" - "#[" - "[" - "]" -] @punctuation.bracket - -[ - "`" - "#'" - "'" - "," - ",@" -] @operator - -;; Highlight nil and t as constants, unlike other symbols -[ - "nil" - "t" -] @constant.builtin diff --git a/src/textual/tree-sitter/highlights/elixir.scm b/src/textual/tree-sitter/highlights/elixir.scm deleted file mode 100644 index 7423e31cbc..0000000000 --- a/src/textual/tree-sitter/highlights/elixir.scm +++ /dev/null @@ -1,199 +0,0 @@ -; Reserved keywords - -["when" "and" "or" "not" "in" "not in" "fn" "do" "end" "catch" "rescue" "after" "else"] @keyword - -; Operators - -; * doc string -(unary_operator - operator: "@" @comment.doc - operand: (call - target: (identifier) @comment.doc.__attribute__ - (arguments - [ - (string) @comment.doc - (charlist) @comment.doc - (sigil - quoted_start: _ @comment.doc - quoted_end: _ @comment.doc) @comment.doc - (boolean) @comment.doc - ])) - (#match? @comment.doc.__attribute__ "^(moduledoc|typedoc|doc)$")) - -; * module attribute -(unary_operator - operator: "@" @attribute - operand: [ - (identifier) @attribute - (call - target: (identifier) @attribute) - (boolean) @attribute - (nil) @attribute - ]) - -; * capture operand -(unary_operator - operator: "&" - operand: (integer) @operator) - -(operator_identifier) @operator - -(unary_operator - operator: _ @operator) - -(binary_operator - operator: _ @operator) - -(dot - operator: _ @operator) - -(stab_clause - operator: _ @operator) - -; Literals - -[ - (boolean) - (nil) -] @constant - -[ - (integer) - (float) -] @number - -(alias) @module - -(call - target: (dot - left: (atom) @module)) - -(char) @constant - -; Quoted content - -(interpolation "#{" @punctuation.special "}" @punctuation.special) @embedded - -(escape_sequence) @string.escape - -[ - (atom) - (quoted_atom) - (keyword) - (quoted_keyword) -] @string.special.symbol - -[ - (string) - (charlist) -] @string - -; Note that we explicitly target sigil quoted start/end, so they are not overridden by delimiters - -(sigil - (sigil_name) @__name__ - quoted_start: _ @string - quoted_end: _ @string - (#match? @__name__ "^[sS]$")) @string - -(sigil - (sigil_name) @__name__ - quoted_start: _ @string.regex - quoted_end: _ @string.regex - (#match? @__name__ "^[rR]$")) @string.regex - -(sigil - (sigil_name) @__name__ - quoted_start: _ @string.special - quoted_end: _ @string.special) @string.special - -; Calls - -; * definition keyword -(call - target: (identifier) @keyword - (#match? @keyword "^(def|defdelegate|defexception|defguard|defguardp|defimpl|defmacro|defmacrop|defmodule|defn|defnp|defoverridable|defp|defprotocol|defstruct)$")) - -; * kernel or special forms keyword -(call - target: (identifier) @keyword - (#match? @keyword "^(alias|case|cond|else|for|if|import|quote|raise|receive|require|reraise|super|throw|try|unless|unquote|unquote_splicing|use|with)$")) - -; * function call -(call - target: [ - ; local - (identifier) @function - ; remote - (dot - right: (identifier) @function) - ]) - -; * just identifier in function definition -(call - target: (identifier) @keyword - (arguments - [ - (identifier) @function - (binary_operator - left: (identifier) @function - operator: "when") - ]) - (#match? @keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp)$")) - -; * pipe into identifier (definition) -(call - target: (identifier) @keyword - (arguments - (binary_operator - operator: "|>" - right: (identifier) @variable)) - (#match? @keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp)$")) - -; * pipe into identifier (function call) -(binary_operator - operator: "|>" - right: (identifier) @function) - -; Identifiers - -; * special -( - (identifier) @constant.builtin - (#match? @constant.builtin "^(__MODULE__|__DIR__|__ENV__|__CALLER__|__STACKTRACE__)$") -) - -; * unused -( - (identifier) @comment.unused - (#match? @comment.unused "^_") -) - -; * regular -(identifier) @variable - -; Comment - -(comment) @comment - -; Punctuation - -[ - "%" -] @punctuation - -[ - "," - ";" -] @punctuation.delimiter - -[ - "(" - ")" - "[" - "]" - "{" - "}" - "<<" - ">>" -] @punctuation.bracket diff --git a/src/textual/tree-sitter/highlights/embedded_template.scm b/src/textual/tree-sitter/highlights/embedded_template.scm deleted file mode 100644 index 0bf76a7d49..0000000000 --- a/src/textual/tree-sitter/highlights/embedded_template.scm +++ /dev/null @@ -1,12 +0,0 @@ -(comment_directive) @comment - -[ - "<%#" - "<%" - "<%=" - "<%_" - "<%-" - "%>" - "-%>" - "_%>" -] @keyword diff --git a/src/textual/tree-sitter/highlights/erlang.scm b/src/textual/tree-sitter/highlights/erlang.scm deleted file mode 100644 index 5fb817efc6..0000000000 --- a/src/textual/tree-sitter/highlights/erlang.scm +++ /dev/null @@ -1,228 +0,0 @@ -;; Copyright (c) Facebook, Inc. and its affiliates. -;; -;; Licensed under the Apache License, Version 2.0 (the "License"); -;; you may not use this file except in compliance with the License. -;; You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. -;; --------------------------------------------------------------------- - -;; Based initially on the contents of https://github.com/WhatsApp/tree-sitter-erlang/issues/2 by @Wilfred -;; and https://github.com/the-mikedavis/tree-sitter-erlang/blob/main/queries/highlights.scm -;; -;; The tests are also based on those in -;; https://github.com/the-mikedavis/tree-sitter-erlang/tree/main/test/highlight -;; - - -;; First match wins in this file - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Attributes - -;; module attribute -(module_attribute - name: (atom) @module) - -;; behaviour -(behaviour_attribute name: (atom) @module) - -;; export - -;; Import attribute -(import_attribute - module: (atom) @module) - -;; export_type - -;; optional_callbacks - -;; compile -(compile_options_attribute - options: (tuple - expr: (atom) - expr: (list - exprs: (binary_op_expr - lhs: (atom) - rhs: (integer))))) - -;; file attribute - -;; record -(record_decl name: (atom) @type) -(record_decl name: (macro_call_expr name: (var) @constant)) -(record_field name: (atom) @property) - -;; type alias - -;; opaque - -;; Spec attribute -(spec fun: (atom) @function) -(spec - module: (module name: (atom) @module) - fun: (atom) @function) - -;; callback -(callback fun: (atom) @function) - -;; wild attribute -(wild_attribute name: (attr_name name: (atom) @keyword)) - -;; fun decl - -;; include/include_lib - -;; ifdef/ifndef -(pp_ifdef name: (_) @keyword.directive) -(pp_ifndef name: (_) @keyword.directive) - -;; define -(pp_define - lhs: (macro_lhs - name: (_) @keyword.directive - args: (var_args args: (var)))) -(pp_define - lhs: (macro_lhs - name: (var) @constant)) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Functions -(fa fun: (atom) @function) -(type_name name: (atom) @function) -(call expr: (atom) @function) -(function_clause name: (atom) @function) -(internal_fun fun: (atom) @function) - -;; This is a fudge, we should check that the operator is '/' -;; But our grammar does not (currently) provide it -(binary_op_expr lhs: (atom) @function rhs: (integer)) - -;; Others -(remote_module module: (atom) @module) -(remote fun: (atom) @function) -(macro_call_expr name: (var) @keyword.directive args: (_) ) -(macro_call_expr name: (var) @constant) -(macro_call_expr name: (atom) @keyword.directive) -(record_field_name name: (atom) @property) -(record_name name: (atom) @type) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Reserved words -[ "after" - "and" - "band" - "begin" - "behavior" - "behaviour" - "bnot" - "bor" - "bsl" - "bsr" - "bxor" - "callback" - "case" - "catch" - "compile" - "define" - "div" - "elif" - "else" - "end" - "endif" - "export" - "export_type" - "file" - "fun" - "if" - "ifdef" - "ifndef" - "import" - "include" - "include_lib" - "module" - "of" - "opaque" - "optional_callbacks" - "or" - "receive" - "record" - "spec" - "try" - "type" - "undef" - "unit" - "when" - "xor"] @keyword - -["andalso" "orelse"] @keyword.operator - -;; Punctuation -["," "." ";"] @punctuation.delimiter -["(" ")" "{" "}" "[" "]" "<<" ">>"] @punctuation.bracket - -;; Operators -["!" - "->" - "<-" - "#" - "::" - "|" - ":" - "=" - "||" - - "+" - "-" - "bnot" - "not" - - "/" - "*" - "div" - "rem" - "band" - "and" - - "+" - "-" - "bor" - "bxor" - "bsl" - "bsr" - "or" - "xor" - - "++" - "--" - - "==" - "/=" - "=<" - "<" - ">=" - ">" - "=:=" - "=/=" - ] @operator - -;;; Comments -((var) @comment.discard - (#match? @comment.discard "^_")) - -(dotdotdot) @comment.discard -(comment) @comment - -;; Primitive types -(string) @string -(char) @constant -(integer) @number -(var) @variable -(atom) @string.special.symbol diff --git a/src/textual/tree-sitter/highlights/fortran.scm b/src/textual/tree-sitter/highlights/fortran.scm deleted file mode 100644 index 418e40dfe9..0000000000 --- a/src/textual/tree-sitter/highlights/fortran.scm +++ /dev/null @@ -1,197 +0,0 @@ -(identifier) @variable -(string_literal) @string -(number_literal) @number -(boolean_literal) @boolean -(comment) @comment - -[ - (intrinsic_type) - "allocatable" - "attributes" - "device" - "dimension" - "endtype" - "global" - "grid_global" - "host" - "import" - "in" - "inout" - "intent" - "optional" - "out" - "pointer" - "type" - "value" - ] @type - -[ - "contains" - "private" - "public" - ] @include - -[ - (none) - "implicit" - ] @attribute - -[ - "endfunction" - "endprogram" - "endsubroutine" - "function" - "procedure" - "subroutine" - ] @keyword.function - -[ - (default) - (procedure_qualifier) - "abstract" - "bind" - "call" - "class" - "continue" - "cycle" - "endenum" - "endinterface" - "endmodule" - "endprocedure" - "endprogram" - "endsubmodule" - "enum" - "enumerator" - "equivalence" - "exit" - "extends" - "format" - "goto" - "include" - "interface" - "intrinsic" - "non_intrinsic" - "module" - "namelist" - "only" - "parameter" - "print" - "procedure" - "program" - "read" - "stop" - "submodule" - "use" - "write" - ] @keyword - -"return" @keyword.return - -[ - "else" - "elseif" - "elsewhere" - "endif" - "endwhere" - "if" - "then" - "where" - ] @conditional - -[ - "do" - "enddo" - "forall" - "while" - ] @repeat - -[ - "*" - "+" - "-" - "/" - "=" - "<" - ">" - "<=" - ">=" - "==" - "/=" - ] @operator - -[ - "\\.and\\." - "\\.or\\." - "\\.lt\\." - "\\.gt\\." - "\\.ge\\." - "\\.le\\." - "\\.eq\\." - "\\.eqv\\." - "\\.neqv\\." - ] @keyword.operator - -;; Brackets -[ - "(" - ")" - "[" - "]" - "<<<" - ">>>" - ] @punctuation.bracket - -;; Delimiter -[ - "::" - "," - "%" - ] @punctuation.delimiter - -(parameters - (identifier) @parameter) - -(program_statement - (name) @namespace) - -(module_statement - (name) @namespace) - -(submodule_statement - (module_name) (name) @namespace) - -(function_statement - (name) @function) - -(subroutine_statement - (name) @function) - -(module_procedure_statement - (name) @function) - -(end_program_statement - (name) @namespace) - -(end_module_statement - (name) @namespace) - -(end_submodule_statement - (name) @namespace) - -(end_function_statement - (name) @function) - -(end_subroutine_statement - (name) @function) - -(end_module_procedure_statement - (name) @function) - -(subroutine_call - (identifier) @function) - -(keyword_argument - name: (identifier) @keyword) - -(derived_type_member_expression - (type_member) @property) diff --git a/src/textual/tree-sitter/highlights/gomod.scm b/src/textual/tree-sitter/highlights/gomod.scm deleted file mode 100644 index 63e1f0128a..0000000000 --- a/src/textual/tree-sitter/highlights/gomod.scm +++ /dev/null @@ -1,17 +0,0 @@ -[ - "require" - "replace" - "go" - "exclude" - "retract" - "module" -] @keyword - -"=>" @operator - -(comment) @comment - -[ -(version) -(go_version) -] @string diff --git a/src/textual/tree-sitter/highlights/hack.scm b/src/textual/tree-sitter/highlights/hack.scm deleted file mode 100644 index 921c97e432..0000000000 --- a/src/textual/tree-sitter/highlights/hack.scm +++ /dev/null @@ -1,31 +0,0 @@ -(comment) @comment - -(string) @string -(heredoc) @string -(prefixed_string) @string - -[ - "class" - "interface" - "trait" - "public" - "protected" - "private" - "static" - "async" - "function" - "return" - "if" - "else" - "elseif" - "while" - "for" - "foreach" - "break" - "continue" - "type" - "new" - "throw" -] @keyword - -(type_specifier) @type diff --git a/src/textual/tree-sitter/highlights/haskell.scm b/src/textual/tree-sitter/highlights/haskell.scm deleted file mode 100644 index 3ab843e833..0000000000 --- a/src/textual/tree-sitter/highlights/haskell.scm +++ /dev/null @@ -1,156 +0,0 @@ -;; Copyright 2022 nvim-treesitter -;; -;; Licensed under the Apache License, Version 2.0 (the "License"); -;; you may not use this file except in compliance with the License. -;; You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. - -;; ---------------------------------------------------------------------------- -;; Literals and comments - -(integer) @number -(exp_negation) @number -(exp_literal (float)) @float -(char) @character -(string) @string - -(con_unit) @symbol ; unit, as in () - -(comment) @comment - - -;; ---------------------------------------------------------------------------- -;; Punctuation - -[ - "(" - ")" - "{" - "}" - "[" - "]" -] @punctuation.bracket - -[ - (comma) - ";" -] @punctuation.delimiter - - -;; ---------------------------------------------------------------------------- -;; Keywords, operators, includes - -[ - "forall" - "∀" -] @repeat - -(pragma) @constant.macro - -[ - "if" - "then" - "else" - "case" - "of" -] @conditional - -(exp_lambda_cases "\\" ("cases" @conditional)) - -[ - "import" - "qualified" - "module" -] @include - -[ - (operator) - (constructor_operator) - (type_operator) - (tycon_arrow) - (qualified_module) ; grabs the `.` (dot), ex: import System.IO - (all_names) - (wildcard) - "=" - "|" - "::" - "=>" - "->" - "<-" - "\\" - "`" - "@" -] @operator - -(module) @namespace - -[ - (where) - "let" - "in" - "class" - "instance" - "data" - "newtype" - "family" - "type" - "as" - "hiding" - "deriving" - "via" - "stock" - "anyclass" - "do" - "mdo" - "rec" - "infix" - "infixl" - "infixr" -] @keyword - - -;; ---------------------------------------------------------------------------- -;; Functions and variables - -(variable) @variable -(pat_wildcard) @variable - -(signature name: (variable) @type) -(function - name: (variable) @function - patterns: (patterns)) -((signature (fun)) . (function (variable) @function)) -((signature (context (fun))) . (function (variable) @function)) -((signature (forall (context (fun)))) . (function (variable) @function)) - -(exp_infix (variable) @operator) ; consider infix functions as operators - -(exp_infix (exp_name) @function (#set! "priority" 101)) -(exp_apply . (exp_name (variable) @function)) -(exp_apply . (exp_name (qualified_variable (variable) @function))) - - -;; ---------------------------------------------------------------------------- -;; Types - -(type) @type -(type_variable) @type - -(constructor) @constructor - -; True or False -((constructor) @_bool (#match? @_bool "(True|False)")) @boolean - - -;; ---------------------------------------------------------------------------- -;; Quasi-quotes - -(quoter) @function -; Highlighting of quasiquote_body is handled by injections.scm diff --git a/src/textual/tree-sitter/highlights/jsdoc.scm b/src/textual/tree-sitter/highlights/jsdoc.scm deleted file mode 100644 index 4b4266c9fd..0000000000 --- a/src/textual/tree-sitter/highlights/jsdoc.scm +++ /dev/null @@ -1,2 +0,0 @@ -(tag_name) @keyword -(type) @type diff --git a/src/textual/tree-sitter/highlights/make.scm b/src/textual/tree-sitter/highlights/make.scm deleted file mode 100644 index 9ea8016e59..0000000000 --- a/src/textual/tree-sitter/highlights/make.scm +++ /dev/null @@ -1,171 +0,0 @@ -[ - "(" - ")" - "{" - "}" -] @punctuation.bracket - -[ - ":" - "&:" - "::" - "|" - ";" - "\"" - "'" - "," -] @punctuation.delimiter - -[ - "$" - "$$" -] @punctuation.special - -(automatic_variable - [ "@" "%" "<" "?" "^" "+" "/" "*" "D" "F"] @punctuation.special) - -(automatic_variable - "/" @error . ["D" "F"]) - -[ - "=" - ":=" - "::=" - "?=" - "+=" - "!=" - "@" - "-" - "+" -] @operator - -[ - (text) - (string) - (raw_text) -] @string - -(variable_assignment (word) @string) - -[ - "ifeq" - "ifneq" - "ifdef" - "ifndef" - "else" - "endif" - "if" - "or" ; boolean functions are conditional in make grammar - "and" -] @conditional - -"foreach" @repeat - -[ - "define" - "endef" - "vpath" - "undefine" - "export" - "unexport" - "override" - "private" -; "load" -] @keyword - -[ - "include" - "sinclude" - "-include" -] @include - -[ - "subst" - "patsubst" - "strip" - "findstring" - "filter" - "filter-out" - "sort" - "word" - "words" - "wordlist" - "firstword" - "lastword" - "dir" - "notdir" - "suffix" - "basename" - "addsuffix" - "addprefix" - "join" - "wildcard" - "realpath" - "abspath" - "call" - "eval" - "file" - "value" - "shell" -] @keyword.function - -[ - "error" - "warning" - "info" -] @exception - -;; Variable -(variable_assignment - name: (word) @constant) - -(variable_reference - (word) @constant) - -(comment) @comment - -((word) @clean @string.regex - (#match? @clean "[%\*\?]")) - -(function_call - function: "error" - (arguments (text) @text.danger)) - -(function_call - function: "warning" - (arguments (text) @text.warning)) - -(function_call - function: "info" - (arguments (text) @text.note)) - -;; Install Command Categories -;; Others special variables -;; Variables Used by Implicit Rules -[ - "VPATH" - ".RECIPEPREFIX" -] @constant.builtin - -(variable_assignment - name: (word) @clean @constant.builtin - (#match? @clean "^(AR|AS|CC|CXX|CPP|FC|M2C|PC|CO|GET|LEX|YACC|LINT|MAKEINFO|TEX|TEXI2DVI|WEAVE|CWEAVE|TANGLE|CTANGLE|RM|ARFLAGS|ASFLAGS|CFLAGS|CXXFLAGS|COFLAGS|CPPFLAGS|FFLAGS|GFLAGS|LDFLAGS|LDLIBS|LFLAGS|YFLAGS|PFLAGS|RFLAGS|LINTFLAGS|PRE_INSTALL|POST_INSTALL|NORMAL_INSTALL|PRE_UNINSTALL|POST_UNINSTALL|NORMAL_UNINSTALL|MAKEFILE_LIST|MAKE_RESTARTS|MAKE_TERMOUT|MAKE_TERMERR|\.DEFAULT_GOAL|\.RECIPEPREFIX|\.EXTRA_PREREQS)$")) - -(variable_reference - (word) @clean @constant.builtin - (#match? @clean "^(AR|AS|CC|CXX|CPP|FC|M2C|PC|CO|GET|LEX|YACC|LINT|MAKEINFO|TEX|TEXI2DVI|WEAVE|CWEAVE|TANGLE|CTANGLE|RM|ARFLAGS|ASFLAGS|CFLAGS|CXXFLAGS|COFLAGS|CPPFLAGS|FFLAGS|GFLAGS|LDFLAGS|LDLIBS|LFLAGS|YFLAGS|PFLAGS|RFLAGS|LINTFLAGS|PRE_INSTALL|POST_INSTALL|NORMAL_INSTALL|PRE_UNINSTALL|POST_UNINSTALL|NORMAL_UNINSTALL|MAKEFILE_LIST|MAKE_RESTARTS|MAKE_TERMOUT|MAKE_TERMERR|\.DEFAULT_GOAL|\.RECIPEPREFIX|\.EXTRA_PREREQS\.VARIABLES|\.FEATURES|\.INCLUDE_DIRS|\.LOADED)$")) - -;; Standart targets -(targets - (word) @constant.macro - (#match? @constant.macro "^(all|install|install-html|install-dvi|install-pdf|install-ps|uninstall|install-strip|clean|distclean|mostlyclean|maintainer-clean|TAGS|info|dvi|html|pdf|ps|dist|check|installcheck|installdirs)$")) - -(targets - (word) @constant.macro - (#match? @constant.macro "^(all|install|install-html|install-dvi|install-pdf|install-ps|uninstall|install-strip|clean|distclean|mostlyclean|maintainer-clean|TAGS|info|dvi|html|pdf|ps|dist|check|installcheck|installdirs)$")) - -;; Builtin targets -(targets - (word) @constant.macro - (#match? @constant.macro "^\.(PHONY|SUFFIXES|DEFAULT|PRECIOUS|INTERMEDIATE|SECONDARY|SECONDEXPANSION|DELETE_ON_ERROR|IGNORE|LOW_RESOLUTION_TIME|SILENT|EXPORT_ALL_VARIABLES|NOTPARALLEL|ONESHELL|POSIX)$")) - diff --git a/src/textual/tree-sitter/highlights/objc.scm b/src/textual/tree-sitter/highlights/objc.scm deleted file mode 100644 index ff4c8faa5a..0000000000 --- a/src/textual/tree-sitter/highlights/objc.scm +++ /dev/null @@ -1,395 +0,0 @@ -[ - (comment) - (pragma) -] @comment - -[ - (self) - (super) -] @variable.builtin - -[ - (getter) - (setter) - (nonnull) - (nullable) - (null_resettable) - (unsafe_unretained) - (null_unspecified) - (direct) - (readwrite) - (readonly) - (strong) - (weak) - (copy) - (assign) - (retain) - (atomic) - (nonatomic) - (class) - (NS_NONATOMIC_IOSONLY) - (DISPATCH_QUEUE_REFERENCE_TYPE) -] @keyword - -[ - "@interface" - "@protocol" - "@property" - "@end" - "@implementation" - "@compatibility_alias" - "@autoreleasepool" - "@synchronized" - "@class" - "@synthesize" - "@dynamic" - "@defs" - "@try" - "@catch" - "@finally" - "@throw" - "@selector" - "@encode" - (private) - (public) - (protected) - (package) - (optional) - (required) - "NS_ENUM" - "NS_ERROR_ENUM" - "NS_OPTIONS" - "NS_SWIFT_NAME" - (type_qualifier) - (storage_class_specifier) - "NS_NOESCAPE" - "const" - "default" - "enum" - "extern" - "inline" - "static" - "struct" - "typedef" - "typeof" - "__typeof" - "__typeof__" - "_Atomic" - "union" - "volatile" - "goto" - "register" - "__covariant" - "__contravariant" - "__GENERICS" -] @keyword - -"sizeof" @keyword.operator -"return" @keyword.return - -[ - "while" - "for" - "do" - "continue" - "break" -] @keyword.repeat - -"#define" @constant.macro - -[ - "#if" - "#ifdef" - "#ifndef" - "#else" - "#elif" - "#endif" - (preproc_directive) -] @keyword - -"#include" @include -"#import" @include -"@import" @include - -[ - "=" - - "-" - "*" - "/" - "+" - "%" - - "~" - "|" - "&" - "^" - "<<" - ">>" - - "->" - - "<" - "<=" - ">=" - ">" - "==" - "!=" - - "!" - "&&" - "||" - - "-=" - "+=" - "*=" - "/=" - "%=" - "|=" - "&=" - "^=" - ">>=" - "<<=" - "--" - "++" - "@" -] @operator - -[ - "if" - "else" - "case" - "switch" -] @keyword.conditional - -(conditional_expression [ "?" ":" ] @keyword.conditional) - -[ - (true) - (false) - (YES) - (NO) -] @keyword.boolean - -[ "." ";" ":" "," ] @punctuation.delimiter - -"..." @punctuation.special - -[ "(" ")" "[" "]" "{" "}"] @punctuation.bracket - -[ - (string_literal) - (string_expression) - (system_lib_string) - (module_string) -] @string - -(escape_sequence) @string.escape - -(null) @constant.builtin -(nil) @constant.builtin -(number_literal) @number -(number_expression) @number -(char_literal) @character - -[ - (preproc_arg) - (preproc_defined) -] @function.macro - -[ - (type_identifier) - (primitive_type) - (sized_type_specifier) - (type_descriptor) - (generics_type_reference) -] @type - -[ - (id) - (Class) - (Method) - (IMP) - (SEL) - (BOOL) - (instancetype) - (auto) -] @type.builtin - -(declaration (type_qualifier) @type) -(cast_expression type: (type_descriptor) @type) -(sizeof_expression value: (parenthesized_expression (identifier) @type)) - -;; Type Class & Category & Protocol -(class_interface name: (identifier) @type.class) -(category_interface name: (identifier) @type.class) -(category_interface category: (identifier) @type.category) -(superclass_reference name: (identifier) @type.class) -(parameterized_class_type_arguments) @type.class -(class_implementation name: (identifier) @type.class) -(category_implementation name: (identifier) @type.class) -(compatibility_alias_declaration (identifier) @type.class) -(parameterized_class_type_arguments (identifier) @type.class) -(category_implementation category: (identifier) @type.category) -(class_forward_declaration name: (identifier) @type.class) -(protocol_forward_declaration name: (identifier) @type.protocol) -(protocol_declaration name: (identifier) @type.protocol) -(protocol_qualifiers name: (identifier) @type.protocol) -(protocol_expression (identifier) @type.protocol) - -;; Preproc def / undef -(preproc_def - name: (_) @constant) -(preproc_call - directive: (preproc_directive) @_u - argument: (_) @constant - (#eq? @_u "#undef")) - -;; Property -(property_declaration - type: _ @type - declarator: (identifier) @property) - -(property_declaration - type: _ @type - declarator: (_ - declarator: (identifier) @property)) - -(property_declaration - type: _ @type - declarator: (_ - declarator: (_ - declarator: (identifier) @property))) - -(((field_expression - (field_identifier) @property)) @_parent - (#not-has-parent? @_parent function_declarator call_expression)) - -(field_expression - field: (field_identifier) @property) - -(((field_identifier) @property) - (#has-ancestor? @property field_declaration) - (#not-has-ancestor? @property function_declarator)) - -;; Variable -declarator: (identifier) @variable - -(cast_expression value: (identifier) @variable) - -;; Function -(call_expression - function: (identifier) @function) -(function_declarator - declarator: (identifier) @function) -(preproc_function_def - name: (identifier) @function.macro) -(selector_expression - name: (identifier) @function) -(method_declaration - selector: (identifier) @function) - -(method_declaration - (keyword_selector - (keyword_declarator - keyword: (identifier) @function))) - -(method_declaration - (keyword_selector - (keyword_declarator - name: (identifier) @variable.parameter))) - -(message_expression - receiver: (field_expression - field: (field_identifier) @function)) - -(method_definition - selector: (identifier) @function) - -(swift_name_attribute_sepcifier - method: (identifier) @function) - -(setter - name: (identifier) @function) - -(method_definition - (keyword_selector - (keyword_declarator - keyword: (identifier) @function))) - -(message_expression - selector: (identifier) @function) - -(method_definition - (keyword_selector - (keyword_declarator - name: (identifier) @variable.parameter))) - -(message_expression - selector: (keyword_argument_list - (keyword_argument - keyword: (identifier) @function))) - -(message_expression - selector: (keyword_argument_list - (keyword_argument - argument: (identifier) @variable.parameter))) - -(unary_expression argument: (identifier) @function) -(va_arg_expression) @function -(va_arg_expression va_list: (identifier) @variable) -(enumerator name: (identifier) @variable) - - -;; Parameters -(parameter_declaration - declarator: (identifier) @variable.parameter) - -(parameter_declaration - declarator: (pointer_declarator) @variable.parameter) - -(parameter_declaration - declarator: (pointer_declarator - declarator: (identifier) @variable.parameter)) - -(for_in_statement - loop: (identifier) @variable) - -(dictionary_expression - key: (_expression) @variable) -(dictionary_expression - value: (_expression) @variable) -(array_expression - (identifier) @variable) -(argument_list - (identifier) @variable) -(expression_statement - (identifier) @variable) -(_expression (identifier) @variable) - -[ - "__attribute" - "__attribute__" - "__cdecl" - "__clrcall" - "__stdcall" - "__fastcall" - "__thiscall" - "__vectorcall" - "_unaligned" - "__unaligned" - "__declspec" - "__unused" - "__builtin_available" - "@available" - (attribute_specifier) - (class_interface_attribute_sepcifier) - (method_variadic_arguments_attribute_specifier) -] @attribute - -(attribute_specifier) @attribute - -((identifier) @constant - (#match? @constant "^[A-Z][A-Z0-9_$]+$")) - -(ERROR) @error \ No newline at end of file diff --git a/src/textual/tree-sitter/highlights/ocaml.scm b/src/textual/tree-sitter/highlights/ocaml.scm deleted file mode 100644 index fbd0e082e8..0000000000 --- a/src/textual/tree-sitter/highlights/ocaml.scm +++ /dev/null @@ -1,151 +0,0 @@ -; Modules -;-------- - -[(module_name) (module_type_name)] @module - -; Types -;------ - -( - (type_constructor) @type.builtin - (#match? @type.builtin "^(int|char|bytes|string|float|bool|unit|exn|array|list|option|int32|int64|nativeint|format6|lazy_t)$") -) - -[(class_name) (class_type_name) (type_constructor)] @type - -[(constructor_name) (tag)] @constructor - -; Functions -;---------- - -(let_binding - pattern: (value_name) @function - (parameter)) - -(let_binding - pattern: (value_name) @function - body: [(fun_expression) (function_expression)]) - -(value_specification (value_name) @function) - -(external (value_name) @function) - -(method_name) @function.method - -; Application -;------------ - -( - (value_name) @function.builtin - (#match? @function.builtin "^(raise(_notrace)?|failwith|invalid_arg)$") -) - -(infix_expression - left: (value_path (value_name) @function) - operator: (concat_operator) @operator - (#eq? @operator "@@")) - -(infix_expression - operator: (rel_operator) @operator - right: (value_path (value_name) @function) - (#eq? @operator "|>")) - -(application_expression - function: (value_path (value_name) @function)) - -; Variables -;---------- - -[(value_name) (type_variable)] @variable - -(value_pattern) @variable.parameter - -; Properties -;----------- - -[(label_name) (field_name) (instance_variable_name)] @property - -; Constants -;---------- - -(boolean) @constant - -[(number) (signed_number)] @number - -[(string) (character)] @string - -(quoted_string "{" @string "}" @string) @string - -(escape_sequence) @escape - -(conversion_specification) @string.special - -; Operators -;---------- - -(match_expression (match_operator) @keyword) - -(value_definition [(let_operator) (let_and_operator)] @keyword) - -[ - (prefix_operator) - (sign_operator) - (pow_operator) - (mult_operator) - (add_operator) - (concat_operator) - (rel_operator) - (and_operator) - (or_operator) - (assign_operator) - (hash_operator) - (indexing_operator) - (let_operator) - (let_and_operator) - (match_operator) -] @operator - -["*" "#" "::" "<-"] @operator - -; Keywords -;--------- - -[ - "and" "as" "assert" "begin" "class" "constraint" "do" "done" "downto" "else" - "end" "exception" "external" "for" "fun" "function" "functor" "if" "in" - "include" "inherit" "initializer" "lazy" "let" "match" "method" "module" - "mutable" "new" "nonrec" "object" "of" "open" "private" "rec" "sig" "struct" - "then" "to" "try" "type" "val" "virtual" "when" "while" "with" -] @keyword - -; Punctuation -;------------ - -(attribute ["[@" "]"] @punctuation.special) -(item_attribute ["[@@" "]"] @punctuation.special) -(floating_attribute ["[@@@" "]"] @punctuation.special) -(extension ["[%" "]"] @punctuation.special) -(item_extension ["[%%" "]"] @punctuation.special) -(quoted_extension ["{%" "}"] @punctuation.special) -(quoted_item_extension ["{%%" "}"] @punctuation.special) - -"%" @punctuation.special - -["(" ")" "[" "]" "{" "}" "[|" "|]" "[<" "[>"] @punctuation.bracket - -(object_type ["<" ">"] @punctuation.bracket) - -[ - "," "." ";" ":" "=" "|" "~" "?" "+" "-" "!" ">" "&" - "->" ";;" ":>" "+=" ":=" ".." -] @punctuation.delimiter - -; Attributes -;----------- - -(attribute_id) @tag - -; Comments -;--------- - -[(comment) (line_number_directive) (directive) (shebang)] @comment diff --git a/src/textual/tree-sitter/highlights/php.scm b/src/textual/tree-sitter/highlights/php.scm deleted file mode 100644 index 666a49be2a..0000000000 --- a/src/textual/tree-sitter/highlights/php.scm +++ /dev/null @@ -1,122 +0,0 @@ -(php_tag) @tag -"?>" @tag - -; Types - -(primitive_type) @type.builtin -(cast_type) @type.builtin -(named_type (name) @type) @type -(named_type (qualified_name) @type) @type - -; Functions - -(array_creation_expression "array" @function.builtin) -(list_literal "list" @function.builtin) - -(method_declaration - name: (name) @function.method) - -(function_call_expression - function: [(qualified_name (name)) (name)] @function) - -(scoped_call_expression - name: (name) @function) - -(member_call_expression - name: (name) @function.method) - -(function_definition - name: (name) @function) - -; Member - -(property_element - (variable_name) @property) - -(member_access_expression - name: (variable_name (name)) @property) -(member_access_expression - name: (name) @property) - -; Variables - -(relative_scope) @variable.builtin - -((name) @constant - (#match? @constant "^_?[A-Z][A-Z\\d_]+$")) -((name) @constant.builtin - (#match? @constant.builtin "^__[A-Z][A-Z\d_]+__$")) - -((name) @constructor - (#match? @constructor "^[A-Z]")) - -((name) @variable.builtin - (#eq? @variable.builtin "this")) - -(variable_name) @variable - -; Basic tokens -[ - (string) - (string_value) - (encapsed_string) - (heredoc) - (heredoc_body) - (nowdoc_body) -] @string -(boolean) @constant.builtin -(null) @constant.builtin -(integer) @number -(float) @number -(comment) @comment - -"$" @operator - -; Keywords - -"abstract" @keyword -"as" @keyword -"break" @keyword -"case" @keyword -"catch" @keyword -"class" @keyword -"const" @keyword -"continue" @keyword -"declare" @keyword -"default" @keyword -"do" @keyword -"echo" @keyword -"else" @keyword -"elseif" @keyword -"enddeclare" @keyword -"endforeach" @keyword -"endif" @keyword -"endswitch" @keyword -"endwhile" @keyword -"extends" @keyword -"final" @keyword -"finally" @keyword -"foreach" @keyword -"function" @keyword -"global" @keyword -"if" @keyword -"implements" @keyword -"include_once" @keyword -"include" @keyword -"insteadof" @keyword -"interface" @keyword -"namespace" @keyword -"new" @keyword -"private" @keyword -"protected" @keyword -"public" @keyword -"require_once" @keyword -"require" @keyword -"return" @keyword -"static" @keyword -"switch" @keyword -"throw" @keyword -"trait" @keyword -"try" @keyword -"use" @keyword -"while" @keyword diff --git a/src/textual/tree-sitter/highlights/r.scm b/src/textual/tree-sitter/highlights/r.scm deleted file mode 100644 index 198d1ead8b..0000000000 --- a/src/textual/tree-sitter/highlights/r.scm +++ /dev/null @@ -1,128 +0,0 @@ -; highlights.scm - - -; Literals - -(integer) @number - -(float) @float - -(complex) @number - -(string) @string -(string (escape_sequence) @string.escape) - -(comment) @comment - -(identifier) @variable - -(formal_parameters (identifier) @parameter) -(formal_parameters (default_parameter (identifier) @parameter)) - -; Operators -[ - "=" - "<-" - "<<-" - "->>" - "->" -] @operator - -(unary operator: [ - "-" - "+" - "!" - "~" -] @operator) - -(binary operator: [ - "-" - "+" - "*" - "/" - "^" - "<" - ">" - "<=" - ">=" - "==" - "!=" - "||" - "|" - "&&" - "&" - ":" - "~" -] @operator) - -[ - "|>" - (special) -] @operator - -(lambda_function "\\" @operator) - -[ - "(" - ")" - "[" - "]" - "{" - "}" -] @punctuation.bracket - -(dollar "$" @operator) - -(subset2 - [ - "[[" - "]]" - ] @punctuation.bracket) - -[ - "in" - (dots) - (break) - (next) - (inf) -] @keyword - -[ - (nan) - (na) - (null) -] @type.builtin - -[ - "if" - "else" - "switch" -] @conditional - -[ - "while" - "repeat" - "for" -] @repeat - -[ - (true) - (false) -] @boolean - -"function" @keyword.function - -(call function: (identifier) @function) -(default_argument name: (identifier) @parameter) - - -(namespace_get function: (identifier) @method) -(namespace_get_internal function: (identifier) @method) - -(namespace_get namespace: (identifier) @namespace - "::" @operator) -(namespace_get_internal namespace: (identifier) @namespace - ":::" @operator) - -; Error -(ERROR) @error diff --git a/src/textual/tree-sitter/highlights/ruby.scm b/src/textual/tree-sitter/highlights/ruby.scm deleted file mode 100644 index 51d1c0b729..0000000000 --- a/src/textual/tree-sitter/highlights/ruby.scm +++ /dev/null @@ -1,154 +0,0 @@ -; Keywords - -[ - "alias" - "and" - "begin" - "break" - "case" - "class" - "def" - "do" - "else" - "elsif" - "end" - "ensure" - "for" - "if" - "in" - "module" - "next" - "or" - "rescue" - "retry" - "return" - "then" - "unless" - "until" - "when" - "while" - "yield" -] @keyword - -((identifier) @keyword - (#match? @keyword "^(private|protected|public)$")) - -; Function calls - -((identifier) @function.method.builtin - (#eq? @function.method.builtin "require")) - -"defined?" @function.method.builtin - -(call - method: [(identifier) (constant)] @function.method) - -; Function definitions - -(alias (identifier) @function.method) -(setter (identifier) @function.method) -(method name: [(identifier) (constant)] @function.method) -(singleton_method name: [(identifier) (constant)] @function.method) - -; Identifiers - -[ - (class_variable) - (instance_variable) -] @property - -((identifier) @constant.builtin - (#match? @constant.builtin "^__(FILE|LINE|ENCODING)__$")) - -(file) @constant.builtin -(line) @constant.builtin -(encoding) @constant.builtin - -(hash_splat_nil - "**" @operator -) @constant.builtin - -((constant) @constant - (#match? @constant "^[A-Z\\d_]+$")) - -(constant) @constructor - -(self) @variable.builtin -(super) @variable.builtin - -(block_parameter (identifier) @variable.parameter) -(block_parameters (identifier) @variable.parameter) -(destructured_parameter (identifier) @variable.parameter) -(hash_splat_parameter (identifier) @variable.parameter) -(lambda_parameters (identifier) @variable.parameter) -(method_parameters (identifier) @variable.parameter) -(splat_parameter (identifier) @variable.parameter) - -(keyword_parameter name: (identifier) @variable.parameter) -(optional_parameter name: (identifier) @variable.parameter) - -((identifier) @function.method - (#is-not? local)) -(identifier) @variable - -; Literals - -[ - (string) - (bare_string) - (subshell) - (heredoc_body) - (heredoc_beginning) -] @string - -[ - (simple_symbol) - (delimited_symbol) - (hash_key_symbol) - (bare_symbol) -] @string.special.symbol - -(regex) @string.special.regex -(escape_sequence) @escape - -[ - (integer) - (float) -] @number - -[ - (nil) - (true) - (false) -]@constant.builtin - -(interpolation - "#{" @punctuation.special - "}" @punctuation.special) @embedded - -(comment) @comment - -; Operators - -[ -"=" -"=>" -"->" -] @operator - -[ - "," - ";" - "." -] @punctuation.delimiter - -[ - "(" - ")" - "[" - "]" - "{" - "}" - "%w(" - "%i(" -] @punctuation.bracket diff --git a/src/textual/tree-sitter/highlights/scala.scm b/src/textual/tree-sitter/highlights/scala.scm deleted file mode 100644 index ed20b27404..0000000000 --- a/src/textual/tree-sitter/highlights/scala.scm +++ /dev/null @@ -1,261 +0,0 @@ -; CREDITS @stumash (stuart.mashaal@gmail.com) - -(class_definition - name: (identifier) @type) - -(enum_definition - name: (identifier) @type) - -(object_definition - name: (identifier) @type) - -(trait_definition - name: (identifier) @type) - -(full_enum_case - name: (identifier) @type) - -(simple_enum_case - name: (identifier) @type) - -;; variables - -(class_parameter - name: (identifier) @parameter) - -(self_type (identifier) @parameter) - -(interpolation (identifier) @none) -(interpolation (block) @none) - -;; types - -(type_definition - name: (type_identifier) @type.definition) - -(type_identifier) @type - -;; val/var definitions/declarations - -(val_definition - pattern: (identifier) @variable) - -(var_definition - pattern: (identifier) @variable) - -(val_declaration - name: (identifier) @variable) - -(var_declaration - name: (identifier) @variable) - -; method definition - -(function_declaration - name: (identifier) @method) - -(function_definition - name: (identifier) @method) - -; imports/exports - -(import_declaration - path: (identifier) @namespace) -((stable_identifier (identifier) @namespace)) - -((import_declaration - path: (identifier) @type) (#match? @type "^[A-Z]")) -((stable_identifier (identifier) @type) (#match? @type "^[A-Z]")) - -(export_declaration - path: (identifier) @namespace) -((stable_identifier (identifier) @namespace)) - -((export_declaration - path: (identifier) @type) (#match? @type "^[A-Z]")) -((stable_identifier (identifier) @type) (#match? @type "^[A-Z]")) - -((namespace_selectors (identifier) @type) (#match? @type "^[A-Z]")) - -; method invocation - -(call_expression - function: (identifier) @function.call) - -(call_expression - function: (operator_identifier) @function.call) - -(call_expression - function: (field_expression - field: (identifier) @method.call)) - -((call_expression - function: (identifier) @constructor) - (#match? @constructor "^[A-Z]")) - -(generic_function - function: (identifier) @function.call) - -(interpolated_string_expression - interpolator: (identifier) @function.call) - -; function definitions - -(function_definition - name: (identifier) @function) - -(parameter - name: (identifier) @parameter) - -(binding - name: (identifier) @parameter) - -; expressions - -(field_expression field: (identifier) @property) -(field_expression value: (identifier) @type - (#match? @type "^[A-Z]")) - -(infix_expression operator: (identifier) @operator) -(infix_expression operator: (operator_identifier) @operator) -(infix_type operator: (operator_identifier) @operator) -(infix_type operator: (operator_identifier) @operator) - -; literals - -(boolean_literal) @boolean -(integer_literal) @number -(floating_point_literal) @float - -[ - (symbol_literal) - (string) - (character_literal) - (interpolated_string_expression) -] @string - -(interpolation "$" @punctuation.special) - -;; keywords - -(opaque_modifier) @type.qualifier -(infix_modifier) @keyword -(transparent_modifier) @type.qualifier -(open_modifier) @type.qualifier - -[ - "case" - "class" - "enum" - "extends" - "derives" - "finally" -;; `forSome` existential types not implemented yet -;; `macro` not implemented yet - "object" - "override" - "package" - "trait" - "type" - "val" - "var" - "with" - "given" - "using" - "end" - "implicit" - "extension" - "with" -] @keyword - -[ - "abstract" - "final" - "lazy" - "sealed" - "private" - "protected" -] @type.qualifier - -(inline_modifier) @storageclass - -(null_literal) @constant.builtin - -(wildcard) @parameter - -(annotation) @attribute - -;; special keywords - -"new" @keyword.operator - -[ - "else" - "if" - "match" - "then" -] @conditional - -[ - "(" - ")" - "[" - "]" - "{" - "}" -] @punctuation.bracket - -[ - "." - "," -] @punctuation.delimiter - -[ - "do" - "for" - "while" - "yield" -] @repeat - -"def" @keyword.function - -[ - "=>" - "<-" - "@" -] @operator - -["import" "export"] @include - -[ - "try" - "catch" - "throw" -] @exception - -"return" @keyword.return - -(comment) @comment @spell -(block_comment) @comment @spell - -;; `case` is a conditional keyword in case_block - -(case_block - (case_clause ("case") @conditional)) -(indented_cases - (case_clause ("case") @conditional)) - -(operator_identifier) @operator - -((identifier) @type (#match? @type "^[A-Z]")) -((identifier) @variable.builtin - (#match? @variable.builtin "^this$")) - -( - (identifier) @function.builtin - (#match? @function.builtin "^super$") -) - -;; Scala CLI using directives -(using_directive_key) @parameter -(using_directive_value) @string diff --git a/src/textual/tree-sitter/highlights/sqlite.scm b/src/textual/tree-sitter/highlights/sqlite.scm deleted file mode 100644 index 24a8cd4489..0000000000 --- a/src/textual/tree-sitter/highlights/sqlite.scm +++ /dev/null @@ -1,179 +0,0 @@ -;;; - -";" @punctuation.delimiter -"," @punctuation.delimiter -"." @punctuation.delimiter - -"(" @punctuation.bracket -")" @punctuation.bracket - -[ - "||" - "~" "&" "|" "<<" ">>" - "+" "-" "*" "/" "%" - "=" "==" "!=" "<>" ">" ">=" "<" "<=" - "->" "->>" -] @operator - -;;; - -(signed_number) @number - -(numeric_literal) @number -(string_literal) @string -(blob_literal) @string.special -(identifier) @constant -(bind_parameter) @variable.parameter -(comment) @comment - -;;; - -(ABORT) @keyword -(ACTION) @keyword -(ADD) @keyword -(AFTER) @keyword -(ALL) @keyword -(ALTER) @keyword -(ALWAYS) @keyword -(ANALYZE) @keyword -(AND) @keyword -(AS) @keyword -(ASC) @keyword -(ATTACH) @keyword -(AUTOINCREMENT) @keyword -(BEFORE) @keyword -(BEGIN) @keyword -(BETWEEN) @keyword -(BY) @keyword -(CASCADE) @keyword -(CASE) @keyword -(CAST) @keyword -(CHECK) @keyword -(COLLATE) @keyword -(COLUMN) @keyword -(COMMIT) @keyword -(CONFLICT) @keyword -(CONSTRAINT) @keyword -(CREATE) @keyword -(CROSS) @keyword -(CURRENT) @keyword -(CURRENT_DATE) @keyword -(CURRENT_TIME) @keyword -(CURRENT_TIMESTAMP) @keyword -(DATABASE) @keyword -(DEFAULT) @keyword -(DEFERRABLE) @keyword -(DEFERRED) @keyword -(DELETE) @keyword -(DESC) @keyword -(DETACH) @keyword -(DISTINCT) @keyword -(DO) @keyword -(DROP) @keyword -(EACH) @keyword -(ELSE) @keyword -(END) @keyword -(ESCAPE) @keyword -(EXCEPT) @keyword -(EXCLUDE) @keyword -(EXCLUSIVE) @keyword -(EXISTS) @keyword -(EXPLAIN) @keyword -(FAIL) @keyword -(FALSE) @keyword -(FILTER) @keyword -(FIRST) @keyword -(FOLLOWING) @keyword -(FOR) @keyword -(FOREIGN) @keyword -(FROM) @keyword -(GENERATED) @keyword -(GLOB) @keyword -(GROUP) @keyword -(GROUPS) @keyword -(HAVING) @keyword -(IF) @keyword -(IGNORE) @keyword -(IMMEDIATE) @keyword -(IN) @keyword -(INDEX) @keyword -(INDEXED) @keyword -(INITIALLY) @keyword -(INNER) @keyword -(INSERT) @keyword -(INSTEAD) @keyword -(INTERSECT) @keyword -(INTO) @keyword -(IS) @keyword -(ISNULL) @keyword -(JOIN) @keyword -(KEY) @keyword -(LAST) @keyword -(LEFT) @keyword -(LIKE) @keyword -(LIMIT) @keyword -(MATCH) @keyword -(MATERIALIZED) @keyword -(NATURAL) @keyword -(NO) @keyword -(NOT) @keyword -(NOTHING) @keyword -(NOTNULL) @keyword -(NULL) @keyword -(NULLS) @keyword -(OF) @keyword -(OFFSET) @keyword -(ON) @keyword -(OR) @keyword -(ORDER) @keyword -(OTHERS) @keyword -(OUTER) @keyword -(OVER) @keyword -(PARTITION) @keyword -(PLAN) @keyword -(PRAGMA) @keyword -(PRECEDING) @keyword -(PRIMARY) @keyword -(QUERY) @keyword -(RAISE) @keyword -(RANGE) @keyword -(RECURSIVE) @keyword -(REFERENCES) @keyword -(REGEXP) @keyword -(REINDEX) @keyword -(RELEASE) @keyword -(RENAME) @keyword -(REPLACE) @keyword -(RESTRICT) @keyword -(RETURNING) @keyword -(ROLLBACK) @keyword -(ROW) @keyword -(ROWID) @keyword -(ROWS) @keyword -(SAVEPOINT) @keyword -(SELECT) @keyword -(SET) @keyword -(STORED) @keyword -(TABLE) @keyword -(TEMP) @keyword -(TEMPORARY) @keyword -(THEN) @keyword -(TIES) @keyword -(TO) @keyword -(TRANSACTION) @keyword -(TRIGGER) @keyword -(TRUE) @keyword -(UNBOUNDED) @keyword -(UNION) @keyword -(UNIQUE) @keyword -(UPDATE) @keyword -(USING) @keyword -(VACUUM) @keyword -(VALUES) @keyword -(VIEW) @keyword -(VIRTUAL) @keyword -(WHEN) @keyword -(WHERE) @keyword -(WINDOW) @keyword -(WITH) @keyword -(WITHOUT) @keyword diff --git a/src/textual/tree-sitter/highlights/typescript.scm b/src/textual/tree-sitter/highlights/typescript.scm deleted file mode 100644 index c758b516b9..0000000000 --- a/src/textual/tree-sitter/highlights/typescript.scm +++ /dev/null @@ -1,35 +0,0 @@ -; Types - -(type_identifier) @type -(predefined_type) @type.builtin - -((identifier) @type - (#match? @type "^[A-Z]")) - -(type_arguments - "<" @punctuation.bracket - ">" @punctuation.bracket) - -; Variables - -(required_parameter (identifier) @variable.parameter) -(optional_parameter (identifier) @variable.parameter) - -; Keywords - -[ "abstract" - "declare" - "enum" - "export" - "implements" - "interface" - "keyof" - "namespace" - "private" - "protected" - "public" - "type" - "readonly" - "override" - "satisfies" -] @keyword diff --git a/tests/snapshot_tests/language_snippets.py b/tests/snapshot_tests/language_snippets.py index 89ef6c9c29..f0c6c77469 100644 --- a/tests/snapshot_tests/language_snippets.py +++ b/tests/snapshot_tests/language_snippets.py @@ -910,6 +910,112 @@ class MyClass { } """ +JAVA = """\ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +// Classes and interfaces +interface Shape { + double getArea(); +} + +class Rectangle implements Shape { + private double width; + private double height; + + public Rectangle(double width, double height) { + this.width = width; + this.height = height; + } + + @Override + public double getArea() { + return width * height; + } +} + +// Enums +enum DaysOfWeek { + MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY +} + +public class Main { + // Constants + private static final double PI = 3.14159; + + // Methods + public static int sum(int a, int b) { + return a + b; + } + + public static void main(String[] args) { + // Variables + String name = "John"; + int age = 30; + boolean isStudent = true; + + // Printing variables + System.out.println("Hello, " + name + "! You are " + age + " years old."); + + // Conditional statements + if (age >= 18 && isStudent) { + System.out.println("You are an adult student."); + } else if (age >= 18) { + System.out.println("You are an adult."); + } else { + System.out.println("You are a minor."); + } + + // Arrays + int[] numbers = {1, 2, 3, 4, 5}; + System.out.println("Numbers: " + Arrays.toString(numbers)); + + // Lists + List fruits = new ArrayList<>(); + fruits.add("apple"); + fruits.add("banana"); + fruits.add("orange"); + System.out.println("Fruits: " + fruits); + + // Loops + for (int num : numbers) { + System.out.println("Number: " + num); + } + + // Hash maps + Map scores = new HashMap<>(); + scores.put("Alice", 100); + scores.put("Bob", 80); + System.out.println("Alice's score: " + scores.get("Alice")); + + // Exception handling + try { + int result = 10 / 0; + } catch (ArithmeticException e) { + System.out.println("Error: " + e.getMessage()); + } + + // Instantiating objects + Rectangle rect = new Rectangle(10, 20); + System.out.println("Rectangle area: " + rect.getArea()); + + // Enums + DaysOfWeek today = DaysOfWeek.MONDAY; + System.out.println("Today is " + today); + + // Calling methods + int sum = sum(5, 10); + System.out.println("Sum: " + sum); + + // Ternary operator + String message = age >= 18 ? "You are an adult." : "You are a minor."; + System.out.println(message); + } +} +""" + SNIPPETS = { "python": PYTHON, "markdown": MARKDOWN, @@ -925,4 +1031,5 @@ class MyClass { "bash": BASH, "kotlin": KOTLIN, "rust": RUST, + "java": JAVA, } From 8b845b2209677defbde4d3efda25b91a9269f5ca Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 28 Mar 2024 10:28:53 +0000 Subject: [PATCH 24/53] Update snapshots for text area languages --- src/textual/document/_languages.py | 1 + .../__snapshots__/test_snapshots.ambr | 3218 +++++++++++++++-- 2 files changed, 2960 insertions(+), 259 deletions(-) diff --git a/src/textual/document/_languages.py b/src/textual/document/_languages.py index d075dfd368..fb313cf7ca 100644 --- a/src/textual/document/_languages.py +++ b/src/textual/document/_languages.py @@ -11,6 +11,7 @@ "markdown", "python", "rust", + "regex", "sql", "toml", "yaml", diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index c375135d8b..de582aec89 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -36564,6 +36564,486 @@ ''' # --- +# name: test_text_area_language_rendering[bash] + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TextAreaSnapshot + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +   1  #!/bin/bash +   2   +   3  # Variables +   4  name="John" +   5  age=30  +   6  is_student=true  +   7   +   8  # Printing variables +   9  echo"Hello, $name! You are $age years old." +  10   +  11  # Conditional statements +  12  if [[ $age -ge 18 &&$is_student == true ]]; then +  13  echo"You are an adult student." +  14  elif [[ $age -ge 18 ]]; then +  15  echo"You are an adult." +  16  else +  17  echo"You are a minor." +  18  fi +  19   +  20  # Arrays +  21  numbers=(1 2 3 4 5)  +  22  echo"Numbers: ${numbers[@]}" +  23   +  24  # Loops +  25  for num in"${numbers[@]}"do +  26  echo"Number: $num" +  27  done +  28   +  29  # Functions +  30  greet() {  +  31    local name=$ +  32  echo"Hello, $name!" +  33   +  34  greet"Alice" +  35   +  36  # Command substitution +  37  current_date=$(date +%Y-%m-%d)  +  38  echo"Current date: $current_date" +  39   +  40  # File operations +  41  touch file.txt  +  42  echo"Some content"> file.txt  +  43  cat file.txt  +  44   +  45  # Conditionals with file checks +  46  if [[ -f file.txt ]]; then +  47  echo"file.txt exists." +  48  else +  49  echo"file.txt does not exist." +  50  fi +  51   +  52  # Case statement +  53  case$age in +  54    18)  +  55  echo"You are 18 years old." +  56      ;;  +  57    30)  +  58  echo"You are 30 years old." +  59      ;;  +  60    *)  +  61  echo"You are neither 18 nor 30 years old." +  62      ;;  +  63  esac +  64   +  65  # While loop +  66  counter=0  +  67  while [[ $counter -lt 5 ]]; do +  68  echo"Counter: $counter" +  69    ((counter++))  +  70  done +  71   +  72  # Until loop +  73  until [[ $counter -eq 0 ]]; do +  74  echo"Counter: $counter" +  75    ((counter--))  +  76  done +  77   +  78  # Heredoc +  79  cat << EOF +  80  This is a heredoc.  +  81  It allows you to write multiple lines of text.  +  82  EOF  +  83   +  84  # Redirection +  85  ls> file_list.txt  +  86  grep"file" file_list.txt > filtered_list.txt  +  87   +  88  # Pipes +  89  cat file_list.txt |wc -l  +  90   +  91  # Arithmetic operations +  92  result=$((10 + 5))  +  93  echo"Result: $result" +  94   +  95  # Exporting variables +  96  export DB_PASSWORD="secret" +  97   +  98  # Sourcing external files +  99  source config.sh  + 100   + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + ''' +# --- # name: test_text_area_language_rendering[css] ''' @@ -36917,9 +37397,9 @@ ''' # --- -# name: test_text_area_language_rendering[html] +# name: test_text_area_language_rendering[go] ''' - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TextAreaSnapshot + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +  1  package main                                                             +  2   +  3  import (  +  4  "fmt" +  5  "math" +  6  "strings" +  7   +  8   +  9  const PI =3.14159 + 10   + 11  type Shape interface {  + 12      Area() float64  + 13   + 14   + 15  type Circle struct {  + 16      Radius float64  + 17   + 18   + 19  func (c Circle) Area() float64 {  + 20  return PI * c.Radius * c.Radius  + 21   + 22   + 23  funcmain() {  + 24  var name string ="John" + 25      age :=30 + 26      isStudent :=true + 27   + 28      fmt.Printf("Hello, %s! You are %d years old.", name, age)  + 29   + 30  if age >=18&& isStudent {  + 31          fmt.Println("You are an adult student." + 32      } elseif age >=18 {  + 33          fmt.Println("You are an adult." + 34      } else {  + 35          fmt.Println("You are a minor." + 36      }  + 37   + 38      numbers := []int{12345 + 39      sum :=0 + 40  for _, num :=range numbers {  + 41          sum += num  + 42      }  + 43      fmt.Printf("The sum is: %d", sum)  + 44   + 45      message :="Hello, World!" + 46      uppercaseMessage := strings.ToUpper(message)  + 47      fmt.Println(uppercaseMessage)  + 48   + 49      circle := Circle{Radius: 5 + 50      fmt.Printf("Circle area: %.2f", circle.Area())  + 51   + 52      result :=factorial(5 + 53      fmt.Printf("Factorial of 5: %d", result)  + 54   + 55  defer fmt.Println("Program finished." + 56   + 57      sqrt :=func(x float64) float64 {  + 58  return math.Sqrt(x)  + 59      }  + 60      fmt.Printf("Square root of 16: %.2f"sqrt(16))  + 61   + 62   + 63  funcfactorial(n int) int {  + 64  if n ==0 {  + 65  return1 + 66      }  + 67  return n *factorial(n-1 + 68   + 69   + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + ''' +# --- +# name: test_text_area_language_rendering[html] + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -37225,9 +38062,9 @@ ''' # --- -# name: test_text_area_language_rendering[json] +# name: test_text_area_language_rendering[java] ''' - + - - + + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TextAreaSnapshot + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +   1  import java.util.ArrayList;                                             +   2  import java.util.HashMap;  +   3  import java.util.List;  +   4  import java.util.Map;  +   5   +   6  // Classes and interfaces +   7  interface Shape {  +   8      double getArea();  +   9   +  10   +  11  class Rectangle implements Shape {  +  12  private double width;  +  13  private double height;  +  14   +  15  public Rectangle(double width, double height) {  +  16          this.width = width;  +  17          this.height = height;  +  18      }  +  19   +  20  @Override  +  21  public double getArea() {  +  22  return width * height;  +  23      }  +  24   +  25   +  26  // Enums +  27  enum DaysOfWeek {  +  28      MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY  +  29   +  30   +  31  publicclass Main {  +  32  // Constants +  33  privatestaticfinal double PI = 3.14159 +  34   +  35  // Methods +  36  publicstatic int sum(int a, int b) {  +  37  return a + b;  +  38      }  +  39   +  40  publicstatic void main(String[] args) {  +  41  // Variables +  42          String name = "John" +  43          int age = 30 +  44          boolean isStudent = true +  45   +  46  // Printing variables +  47          System.out.println("Hello, " + name + "! You are " + age + " ye +  48   +  49  // Conditional statements +  50  if (age >= 18 && isStudent) {  +  51              System.out.println("You are an adult student.");  +  52          } elseif (age >= 18) {  +  53              System.out.println("You are an adult.");  +  54          } else {  +  55              System.out.println("You are a minor.");  +  56          }  +  57   +  58  // Arrays +  59          int[] numbers = {12345};  +  60          System.out.println("Numbers: " + Arrays.toString(numbers));  +  61   +  62  // Lists +  63          List<String> fruits = new ArrayList<>();  +  64          fruits.add("apple");  +  65          fruits.add("banana");  +  66          fruits.add("orange");  +  67          System.out.println("Fruits: " + fruits);  +  68   +  69  // Loops +  70  for (int num : numbers) {  +  71              System.out.println("Number: " + num);  +  72          }  +  73   +  74  // Hash maps +  75          Map<String, Integer> scores = new HashMap<>();  +  76          scores.put("Alice"100);  +  77          scores.put("Bob"80);  +  78          System.out.println("Alice's score: " + scores.get("Alice"));  +  79   +  80  // Exception handling +  81  try {  +  82              int result = 10 / 0 +  83          } catch (ArithmeticException e) {  +  84              System.out.println("Error: " + e.getMessage());  +  85          }  +  86   +  87  // Instantiating objects +  88          Rectangle rect = new Rectangle(1020);  +  89          System.out.println("Rectangle area: " + rect.getArea());  +  90   +  91  // Enums +  92          DaysOfWeek today = DaysOfWeek.MONDAY;  +  93          System.out.println("Today is " + today);  +  94   +  95  // Calling methods +  96          int sum = sum(510);  +  97          System.out.println("Sum: " + sum);  +  98   +  99  // Ternary operator + 100          String message = age >= 18 ? "You are an adult." : "You are a m + 101          System.out.println(message);  + 102      }  + 103   + 104   + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + ''' +# --- +# name: test_text_area_language_rendering[javascript] + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TextAreaSnapshot + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +  1  // Variable declarations +  2  const name ="John" +  3  let age =30 +  4  var isStudent =true +  5   +  6  // Template literals +  7  console.log(`Hello, ${name}! You are ${age} years old.`);  +  8   +  9  // Conditional statements + 10  if (age >=18&& isStudent) {  + 11    console.log("You are an adult student.");  + 12  elseif (age >=18) {  + 13    console.log("You are an adult.");  + 14  else {  + 15    console.log("You are a minor.");  + 16   + 17   + 18  // Arrays and array methods + 19  const numbers = [12345];  + 20  const doubledNumbers = numbers.map((num) => num *2);  + 21  console.log("Doubled numbers:", doubledNumbers);  + 22   + 23  // Objects + 24  const person = {  + 25    firstName: "John" + 26    lastName: "Doe" + 27    getFullName() {  + 28  return`${this.firstName} ${this.lastName}` + 29    },  + 30  };  + 31  console.log("Full name:", person.getFullName());  + 32   + 33  // Classes + 34  class Rectangle {  + 35    constructor(width, height) {  + 36      this.width = width;  + 37      this.height = height;  + 38    }  + 39   + 40    getArea() {  + 41  return this.width * this.height;  + 42    }  + 43   + 44  const rectangle =new Rectangle(53);  + 45  console.log("Rectangle area:", rectangle.getArea());  + 46   + 47  // Async/Await and Promises + 48  asyncfunctionfetchData() {  + 49  try {  + 50  const response =awaitfetch("https://api.example.com/data");  + 51  const data =await response.json();  + 52      console.log("Fetched data:", data);  + 53    } catch (error) {  + 54      console.error("Error:", error);  + 55    }  + 56   + 57  fetchData();  + 58   + 59  // Arrow functions + 60  constgreet= (name) => {  + 61    console.log(`Hello, ${name}!`);  + 62  };  + 63  greet("Alice");  + 64   + 65  // Destructuring assignment + 66  const [a, b, ...rest] = [12345];  + 67  console.log(a, b, rest);  + 68   + 69  // Spread operator + 70  const arr1 = [123];  + 71  const arr2 = [456];  + 72  const combinedArr = [...arr1, ...arr2];  + 73  console.log("Combined array:", combinedArr);  + 74   + 75  // Ternary operator + 76  const message = age >=18 ? "You are an adult." : "You are a minor." + 77  console.log(message);  + 78   + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + ''' +# --- +# name: test_text_area_language_rendering[json] + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -37430,6 +39159,472 @@ ''' # --- +# name: test_text_area_language_rendering[kotlin] + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TextAreaSnapshot + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +  1  // Variables +  2  val name ="John" +  3  var age =30 +  4  var isStudent =true +  5   +  6  // Printing variables +  7  println("Hello, $name! You are $age years old." +  8   +  9  // Conditional statements + 10  when {  + 11      age >=18&& isStudent ->println("You are an adult student." + 12      age >=18->println("You are an adult." + 13  else->println("You are a minor." + 14   + 15   + 16  // Arrays + 17  val numbers =arrayOf(12345 + 18  println("Numbers: ${numbers.contentToString()}" + 19   + 20  // Lists + 21  val fruits =listOf("apple""banana""orange" + 22  println("Fruits: $fruits" + 23   + 24  // Loops + 25  for (num in numbers) {  + 26  println("Number: $num" + 27   + 28   + 29  // Functions + 30  fungreet(name: String) {  + 31  println("Hello, $name!" + 32   + 33  greet("Alice" + 34   + 35  // Lambda functions + 36  val square = { num: Int -> num * num }  + 37  println("Square of 5: ${square(5)}" + 38   + 39  // Extension functions + 40  fun String.reverse(): String {  + 41  return this.reversed() + 42   + 43  val reversed ="Hello".reverse()  + 44  println("Reversed: $reversed" + 45   + 46  // Data classes + 47  dataclass Person(val name: String, val age: Int)  + 48  val person =Person("John"30 + 49  println("Person: $person" + 50   + 51  // Null safety + 52  var nullable: String? =null + 53  println("Length: ${nullable?.length}" + 54   + 55  // Elvis operator + 56  val length = nullable?.length ?:0 + 57  println("Length (Elvis): $length" + 58   + 59  // Smart casts + 60  funprintLength(obj: Any) {  + 61  if (obj is String) {  + 62  println("Length: ${obj.length}" + 63      }  + 64   + 65  printLength("Hello" + 66   + 67  // Object expressions + 68  val comparator =object : Comparator<Int> {  + 69  overridefun compare(a: Int, b: Int): Int {  + 70  return a - b + 71      }  + 72   + 73  val sortedNumbers = numbers.sortedWith(comparator)  + 74  println("Sorted numbers: ${sortedNumbers.contentToString()}" + 75   + 76  // Companion objects + 77  class MyClass {  + 78      companion object {  + 79  funcreate(): MyClass {  + 80  return MyClass() + 81          }  + 82      }  + 83   + 84  val obj = MyClass.create()  + 85   + 86  // Sealed classes + 87  sealedclass Result {  + 88  dataclass Success(val data: String) : Result()  + 89  dataclass Error(val message: String) : Result()  + 90   + 91  val result: Result = Result.Success("Data" + 92  when (result) {  + 93  is Result.Success ->println("Success: ${result.data}" + 94  is Result.Error ->println("Error: ${result.message}" + 95   + 96   + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + ''' +# --- # name: test_text_area_language_rendering[markdown] ''' @@ -38206,155 +40401,659 @@ font-weight: 700; } - .terminal-250155606-matrix { + .terminal-3279709-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-250155606-title { + .terminal-3279709-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-250155606-r1 { fill: #1e1e1e } - .terminal-250155606-r2 { fill: #0178d4 } - .terminal-250155606-r3 { fill: #c5c8c6 } - .terminal-250155606-r4 { fill: #c2c2bf } - .terminal-250155606-r5 { fill: #272822 } - .terminal-250155606-r6 { fill: #f8f8f2 } - .terminal-250155606-r7 { fill: #90908a } - .terminal-250155606-r8 { fill: #f92672 } - .terminal-250155606-r9 { fill: #23568b } + .terminal-3279709-r1 { fill: #1e1e1e } + .terminal-3279709-r2 { fill: #0178d4 } + .terminal-3279709-r3 { fill: #c5c8c6 } + .terminal-3279709-r4 { fill: #c2c2bf } + .terminal-3279709-r5 { fill: #272822 } + .terminal-3279709-r6 { fill: #e6db74 } + .terminal-3279709-r7 { fill: #f8f8f2 } + .terminal-3279709-r8 { fill: #90908a } + .terminal-3279709-r9 { fill: #f92672 } + .terminal-3279709-r10 { fill: #ae81ff } + .terminal-3279709-r11 { fill: #23568b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TextAreaSnapshot + TextAreaSnapshot - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -  1  ^abc            # Matches any string that starts with "abc"              -  2  abc$            # Matches any string that ends with "abc"  -  3  ^abc$           # Matches the string "abc" and nothing else  -  4  a.b             # Matches any string containing "a", any character, then -  5  a[.]b           # Matches the string "a.b"  -  6  a|b             # Matches either "a" or "b"  -  7  a{2}            # Matches "aa"  -  8  a{2,}           # Matches two or more consecutive "a" characters  -  9  a{2,5}          # Matches between 2 and 5 consecutive "a" characters  - 10  a?              # Matches "a" or nothing (0 or 1 occurrence of "a") - 11  a*              # Matches zero or more consecutive "a" characters  - 12  a+              # Matches one or more consecutive "a" characters  - 13  \d              # Matches any digit (equivalent to [0-9]) - 14  \D              # Matches any non-digit  - 15  \w              # Matches any word character (equivalent to [a-zA-Z0-9_] - 16  \W              # Matches any non-word character  - 17  \s              # Matches any whitespace character (spaces, tabs, line b - 18  \S              # Matches any non-whitespace character  - 19  (?i)abc         # Case-insensitive match for "abc"  - 20  (?:a|b)         # Non-capturing group for either "a" or "b"  - 21  (?<=a)b         # Positive lookbehind: matches "b" that is preceded by " - 22  (?<!a)b         # Negative lookbehind: matches "b" that is not preceded  - 23  a(?=b)          # Positive lookahead: matches "a" that is followed by "b - 24  a(?!b)          # Negative lookahead: matches "a" that is not followed b - 25   - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +  1  ^abc            # Matches any string that starts with "abc" +  2  abc$            # Matches any string that ends with "abc" +  3  ^abc$           # Matches the string "abc" and nothing else +  4  a.b             # Matches any string containing "a", any character, then +  5  a[.]b           # Matches the string "a.b" +  6  a|b             # Matches either "a" or "b" +  7  a{2}            # Matches "aa" +  8  a{2,}           # Matches two or more consecutive "a" characters +  9  a{2,5}          # Matches between 2 and 5 consecutive "a" characters + 10  a?              # Matches "a" or nothing (0 or 1 occurrence of "a" + 11  a*              # Matches zero or more consecutive "a" characters + 12  a+              # Matches one or more consecutive "a" characters + 13  \d              # Matches any digit (equivalent to [0-9])  + 14  \D              # Matches any non-digit + 15  \w              # Matches any word character (equivalent to [a-zA-Z0-9_] + 16  \W              # Matches any non-word character + 17  \s              # Matches any whitespace character (spaces, tabs, line b + 18  \S              # Matches any non-whitespace character + 19  (?i)abc         # Case-insensitive match for "abc" + 20  (?:a|b)         # Non-capturing group for either "a" or "b" + 21  (?<=a)b         # Positive lookbehind: matches "b" that is preceded by " + 22  (?<!a)b         # Negative lookbehind: matches "b" that is not preceded  + 23  a(?=b)          # Positive lookahead: matches "a" that is followed by "b + 24  a(?!b)          # Negative lookahead: matches "a" that is not followed b + 25   + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + + ''' +# --- +# name: test_text_area_language_rendering[rust] + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TextAreaSnapshot + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +   1  use std::collections::HashMap;                                          +   2   +   3  // Constants +   4  const PI: f64 = 3.14159 +   5   +   6  // Structs +   7  struct Rectangle {  +   8      width: u32,  +   9      height: u32,  +  10   +  11   +  12  impl Rectangle {  +  13  fnarea(&self) -> u32 {  +  14          self.width * self.height  +  15      }  +  16   +  17   +  18  // Enums +  19  enum Result<T, E> {  +  20      Ok(T),  +  21      Err(E),  +  22   +  23   +  24  // Functions +  25  fngreet(name: &str) {  +  26      println!("Hello, {}!", name);  +  27   +  28   +  29  fnmain() {  +  30  // Variables +  31  let name = "John" +  32  letmut age = 30 +  33  let is_student = true +  34   +  35  // Printing variables +  36      println!("Hello, {}! You are {} years old.", name, age);  +  37   +  38  // Conditional statements +  39  if age >= 18 && is_student {  +  40          println!("You are an adult student.");  +  41      } elseif age >= 18 {  +  42          println!("You are an adult.");  +  43      } else {  +  44          println!("You are a minor.");  +  45      }  +  46   +  47  // Arrays +  48  let numbers = [12345];  +  49      println!("Numbers: {:?}", numbers);  +  50   +  51  // Vectors +  52  letmut fruits = vec!["apple""banana""orange"];  +  53      fruits.push("grape");  +  54      println!("Fruits: {:?}", fruits);  +  55   +  56  // Loops +  57  for num in&numbers {  +  58          println!("Number: {}", num);  +  59      }  +  60   +  61  // Pattern matching +  62  let result = Result::Ok(42);  +  63  match result {  +  64          Result::Ok(value) => println!("Value: {}", value),  +  65          Result::Err(error) => println!("Error: {:?}", error),  +  66      }  +  67   +  68  // Ownership and borrowing +  69  let s1 = String::from("hello");  +  70  let s2 = s1.clone();  +  71      println!("s1: {}, s2: {}", s1, s2);  +  72   +  73  // References +  74  let rect = Rectangle {  +  75          width: 10 +  76          height: 20 +  77      };  +  78      println!("Rectangle area: {}", rect.area());  +  79   +  80  // Hash maps +  81  letmut scores = HashMap::new();  +  82      scores.insert("Alice"100);  +  83      scores.insert("Bob"80);  +  84      println!("Alice's score: {}", scores["Alice"]);  +  85   +  86  // Closures +  87  let square = |num: i32| num * num;  +  88      println!("Square of 5: {}", square(5));  +  89   +  90  // Traits +  91  trait Printable {  +  92  fnprint(&self);  +  93      }  +  94   +  95  impl Printable for Rectangle {  +  96  fnprint(&self) {  +  97              println!("Rectangle: width={}, height={}", self.width, self +  98          }  +  99      }  + 100      rect.print();  + 101   + 102  // Modules + 103  greet("Alice");  + 104   + 105   + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ @@ -38384,234 +41083,235 @@ font-weight: 700; } - .terminal-1168310614-matrix { + .terminal-276599044-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1168310614-title { + .terminal-276599044-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1168310614-r1 { fill: #1e1e1e } - .terminal-1168310614-r2 { fill: #0178d4 } - .terminal-1168310614-r3 { fill: #c5c8c6 } - .terminal-1168310614-r4 { fill: #c2c2bf } - .terminal-1168310614-r5 { fill: #272822 } - .terminal-1168310614-r6 { fill: #75715e } - .terminal-1168310614-r7 { fill: #f8f8f2 } - .terminal-1168310614-r8 { fill: #90908a } - .terminal-1168310614-r9 { fill: #f92672 } - .terminal-1168310614-r10 { fill: #ae81ff } - .terminal-1168310614-r11 { fill: #e6db74 } - .terminal-1168310614-r12 { fill: #23568b } + .terminal-276599044-r1 { fill: #1e1e1e } + .terminal-276599044-r2 { fill: #0178d4 } + .terminal-276599044-r3 { fill: #c5c8c6 } + .terminal-276599044-r4 { fill: #c2c2bf } + .terminal-276599044-r5 { fill: #272822 } + .terminal-276599044-r6 { fill: #75715e } + .terminal-276599044-r7 { fill: #f8f8f2 } + .terminal-276599044-r8 { fill: #90908a } + .terminal-276599044-r9 { fill: #f92672 } + .terminal-276599044-r10 { fill: #ae81ff } + .terminal-276599044-r11 { fill: #66d9ef;font-style: italic; } + .terminal-276599044-r12 { fill: #e6db74 } + .terminal-276599044-r13 { fill: #23568b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TextAreaSnapshot - - - - - - - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -  1  -- This is a comment in SQL -  2   -  3  -- Create tables -  4  CREATETABLE Authors (  -  5      AuthorID INT PRIMARY KEY -  6      Name VARCHAR(255NOT NULL -  7      Country VARCHAR(50 -  8  );  -  9   - 10  CREATETABLE Books (  - 11      BookID INT PRIMARY KEY - 12      Title VARCHAR(255NOT NULL - 13      AuthorID INT,  - 14      PublishedDate DATE,  - 15      FOREIGN KEY (AuthorID) REFERENCES Authors(AuthorID)  - 16  );  - 17   - 18  -- Insert data - 19  INSERTINTO Authors (AuthorID, Name, Country) VALUES (1'George Orwell' - 20   - 21  INSERTINTO Books (BookID, Title, AuthorID, PublishedDate) VALUES (1'1 - 22   - 23  -- Update data - 24  UPDATE Authors SET Country ='United Kingdom'WHERE Country ='UK' - 25   - 26  -- Select data with JOIN - 27  SELECT Books.Title, Authors.Name   - 28  FROM Books   - 29  JOIN Authors ON Books.AuthorID = Authors.AuthorID;  - 30   - 31  -- Delete data (commented to preserve data for other examples) - 32  -- DELETE FROM Books WHERE BookID = 1; - 33   - 34  -- Alter table structure - 35  ALTER TABLE Authors ADD COLUMN BirthDate DATE;  - 36   - 37  -- Create index - 38  CREATEINDEX idx_author_name ON Authors(Name);  - 39   - 40  -- Drop index (commented to avoid actually dropping it) - 41  -- DROP INDEX idx_author_name ON Authors; - 42   - 43  -- End of script - 44   - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + TextAreaSnapshot + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +  1  -- This is a comment in SQL +  2   +  3  -- Create tables +  4  CREATETABLE Authors (  +  5      AuthorID INT PRIMARY KEY +  6      Name VARCHAR(255NOT NULL +  7      Country VARCHAR(50 +  8  );  +  9   + 10  CREATETABLE Books (  + 11      BookID INT PRIMARY KEY + 12      Title VARCHAR(255NOT NULL + 13      AuthorID INT,  + 14      PublishedDate DATE,  + 15      FOREIGN KEY (AuthorID) REFERENCES Authors(AuthorID)  + 16  );  + 17   + 18  -- Insert data + 19  INSERTINTO Authors (AuthorID, Name, Country) VALUES (1'George Orwell' + 20   + 21  INSERTINTO Books (BookID, Title, AuthorID, PublishedDate) VALUES (1'1 + 22   + 23  -- Update data + 24  UPDATE Authors SET Country ='United Kingdom'WHERE Country ='UK' + 25   + 26  -- Select data with JOIN + 27  SELECT Books.Title, Authors.Name   + 28  FROM Books   + 29  JOIN Authors ON Books.AuthorID = Authors.AuthorID;  + 30   + 31  -- Delete data (commented to preserve data for other examples) + 32  -- DELETE FROM Books WHERE BookID = 1; + 33   + 34  -- Alter table structure + 35  ALTER TABLE Authors ADD COLUMN BirthDate DATE;  + 36   + 37  -- Create index + 38  CREATEINDEX idx_author_name ON Authors(Name);  + 39   + 40  -- Drop index (commented to avoid actually dropping it) + 41  -- DROP INDEX idx_author_name ON Authors; + 42   + 43  -- End of script + 44   + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ From c3a73620d451018b2d072eb843b59afdd89ee044 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 28 Mar 2024 10:43:19 +0000 Subject: [PATCH 25/53] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a7ee5979d..fb0178275d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Added `Document.start` and `end` location properties for convenience https://github.com/Textualize/textual/pull/4267 +- Added support for JavaScript, Golang, Rust, Bash, Java and Kotlin to `TextArea` https://github.com/Textualize/textual/pull/4350 ## [0.54.0] - 2024-03-26 From 1e7438c60a569bd658da08efb907ea443e02b221 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 28 Mar 2024 11:42:23 +0000 Subject: [PATCH 26/53] translate cursor position --- src/textual/_compositor.py | 3 ++- src/textual/_xterm_parser.py | 11 +++++++++++ src/textual/driver.py | 9 +++++++++ src/textual/drivers/linux_inline_driver.py | 5 ++++- src/textual/events.py | 8 ++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index 000d0c2a57..750cb36086 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -170,15 +170,16 @@ def render_segments(self, console: Console) -> str: """ sequences: list[str] = [] append = sequences.append - append("\x1b[J") for last, strip in loop_last(self.strips): append(strip.render(console)) if not last: append("\n") + append("\x1b[J") if len(self.strips) > 1: append(f"\x1b[{len(self.strips)-1}A\r") else: append("\r") + append("\x1b[6n") return "".join(sequences) diff --git a/src/textual/_xterm_parser.py b/src/textual/_xterm_parser.py index 78a4fce4ac..4dceb328b6 100644 --- a/src/textual/_xterm_parser.py +++ b/src/textual/_xterm_parser.py @@ -21,6 +21,8 @@ "^" + re.escape("\x1b[") + r"\?(?P\d+);(?P\d)\$y" ) +_re_cursor_position = re.compile(r"\x1b\[(?P\d+);(?P\d+)R") + BRACKETED_PASTE_START: Final[str] = "\x1b[200~" """Sequence received when a bracketed paste event starts.""" BRACKETED_PASTE_END: Final[str] = "\x1b[201~" @@ -253,6 +255,15 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None: ): on_token(messages.TerminalSupportsSynchronizedOutput()) break + + if ( + cursor_position_match := _re_cursor_position.match(sequence) + ) is not None: + row, column = cursor_position_match.groups() + on_token( + events.CursorPosition(x=int(column), y=int(row) - 1) + ) + else: if not bracketed_paste: for event in sequence_to_key_events(character): diff --git a/src/textual/driver.py b/src/textual/driver.py index fbdf48ab2f..5d65a5f6e5 100644 --- a/src/textual/driver.py +++ b/src/textual/driver.py @@ -37,6 +37,7 @@ def __init__( self._last_move_event: events.MouseMove | None = None self._auto_restart = True """Should the application auto-restart (where appropriate)?""" + self.cursor_origin = (0, 0) @property def is_headless(self) -> bool: @@ -72,6 +73,13 @@ def process_event(self, event: events.Event) -> None: # NOTE: This runs in a thread. # Avoid calling methods on the app. event.set_sender(self._app) + offset_x, offset_y = self.cursor_origin + if isinstance(event, events.MouseEvent): + event.x -= offset_x + event.y -= offset_y + event.screen_x -= offset_x + event.screen_y -= offset_y + if isinstance(event, events.MouseDown): if event.button: self._down_buttons.append(event.button) @@ -84,6 +92,7 @@ def process_event(self, event: events.Event) -> None: and not event.button and self._last_move_event is not None ): + # Deduplicate self._down_buttons while preserving order. buttons = list(dict.fromkeys(self._down_buttons).keys()) self._down_buttons.clear() diff --git a/src/textual/drivers/linux_inline_driver.py b/src/textual/drivers/linux_inline_driver.py index 9ffdeb9585..51c856eaaa 100644 --- a/src/textual/drivers/linux_inline_driver.py +++ b/src/textual/drivers/linux_inline_driver.py @@ -144,7 +144,10 @@ def more_data() -> bool: read(fileno, 1024), final=self.exit_event.is_set() ) for event in feed(unicode_data): - self.process_event(event) + if isinstance(event, events.CursorPosition): + self.cursor_origin = (event.x, event.y) + else: + self.process_event(event) finally: selector.close() diff --git a/src/textual/events.py b/src/textual/events.py index 68c3ae8f3d..ce5ecac4e3 100644 --- a/src/textual/events.py +++ b/src/textual/events.py @@ -55,6 +55,14 @@ class Shutdown(Event): pass +@dataclass +class CursorPosition(Event, bubble=False): + """Internal event used to retrieve the terminal's cursor position.""" + + x: int + y: int + + class Load(Event, bubble=False): """ Sent when the App is running but *before* the terminal is in application mode. From cc8ee74cf81b111444807387633be363d84a7411 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 28 Mar 2024 13:12:51 +0000 Subject: [PATCH 27/53] mouse support --- src/textual/_compositor.py | 9 ++++++--- src/textual/_xterm_parser.py | 3 ++- src/textual/screen.py | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index 750cb36086..9a3982ae9e 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -174,12 +174,15 @@ def render_segments(self, console: Console) -> str: append(strip.render(console)) if not last: append("\n") - append("\x1b[J") + # append("\x1b[B") + append("\n\x1b[J") # Clear down if len(self.strips) > 1: - append(f"\x1b[{len(self.strips)-1}A\r") + append( + f"\x1b[{len(self.strips)}A\r" + ) # Move cursor back to original position else: append("\r") - append("\x1b[6n") + append("\x1b[6n") # Query cursor position return "".join(sequences) diff --git a/src/textual/_xterm_parser.py b/src/textual/_xterm_parser.py index 4dceb328b6..f703c06bc3 100644 --- a/src/textual/_xterm_parser.py +++ b/src/textual/_xterm_parser.py @@ -261,8 +261,9 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None: ) is not None: row, column = cursor_position_match.groups() on_token( - events.CursorPosition(x=int(column), y=int(row) - 1) + events.CursorPosition(x=int(column) - 1, y=int(row) - 1) ) + break else: if not bracketed_paste: diff --git a/src/textual/screen.py b/src/textual/screen.py index 52ff33207c..754292e642 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -881,6 +881,7 @@ def get_inline_height(self, size: Size) -> int: inline_height = max(inline_height, int(min_height.resolve(size, size))) if max_height is not None: inline_height = min(inline_height, int(max_height.resolve(size, size))) + inline_height = min(self.app.size.height - 1, inline_height) return inline_height def _screen_resized(self, size: Size): From c5f485c63874c9a7036d86bcb5d086d357e9d68c Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 28 Mar 2024 13:40:21 +0000 Subject: [PATCH 28/53] Fix issue with undo and redo in TextArea --- src/textual/widgets/_text_area.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py index 4ad2023aad..f7cfd9a901 100644 --- a/src/textual/widgets/_text_area.py +++ b/src/textual/widgets/_text_area.py @@ -1300,11 +1300,11 @@ def _undo_batch(self, edits: Sequence[Edit]) -> None: end_location = ( edit._edit_result.end_location if edit._edit_result else (0, 0) ) - if edit.from_location < minimum_from: - minimum_from = edit.from_location + if edit.top < minimum_from: + minimum_from = edit.top if end_location > maximum_old_end: maximum_old_end = end_location - if edit.to_location > maximum_new_end: + if edit.bottom > maximum_new_end: maximum_new_end = edit.bottom new_gutter_width = self.gutter_width @@ -1346,12 +1346,12 @@ def _redo_batch(self, edits: Sequence[Edit]) -> None: end_location = ( edit._edit_result.end_location if edit._edit_result else (0, 0) ) - if edit.from_location < minimum_from: - minimum_from = edit.from_location + if edit.top < minimum_from: + minimum_from = edit.top if end_location > maximum_new_end: maximum_new_end = end_location - if edit.to_location > maximum_old_end: - maximum_old_end = edit.to_location + if edit.bottom > maximum_old_end: + maximum_old_end = edit.bottom new_gutter_width = self.gutter_width if old_gutter_width != new_gutter_width: From d260f898c4fe1b13715a3eb595765e6babff4861 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 28 Mar 2024 14:01:19 +0000 Subject: [PATCH 29/53] Some renaming of vars in TextArea, using top instead of from_location --- src/textual/widgets/_text_area.py | 44 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py index f7cfd9a901..37231b21aa 100644 --- a/src/textual/widgets/_text_area.py +++ b/src/textual/widgets/_text_area.py @@ -1292,27 +1292,27 @@ def _undo_batch(self, edits: Sequence[Edit]) -> None: return old_gutter_width = self.gutter_width - minimum_from = edits[-1].top - maximum_old_end = (0, 0) - maximum_new_end = (0, 0) + minimum_top = edits[-1].top + maximum_old_bottom = (0, 0) + maximum_new_bottom = (0, 0) for edit in reversed(edits): edit.undo(self) end_location = ( edit._edit_result.end_location if edit._edit_result else (0, 0) ) - if edit.top < minimum_from: - minimum_from = edit.top - if end_location > maximum_old_end: - maximum_old_end = end_location - if edit.bottom > maximum_new_end: - maximum_new_end = edit.bottom + if edit.top < minimum_top: + minimum_top = edit.top + if end_location > maximum_old_bottom: + maximum_old_bottom = end_location + if edit.bottom > maximum_new_bottom: + maximum_new_bottom = edit.bottom new_gutter_width = self.gutter_width if old_gutter_width != new_gutter_width: self.wrapped_document.wrap(self.wrap_width, self.indent_width) else: self.wrapped_document.wrap_range( - minimum_from, maximum_old_end, maximum_new_end + minimum_top, maximum_old_bottom, maximum_new_bottom ) self._refresh_size() @@ -1338,29 +1338,29 @@ def _redo_batch(self, edits: Sequence[Edit]) -> None: return old_gutter_width = self.gutter_width - minimum_from = edits[0].from_location - maximum_old_end = (0, 0) - maximum_new_end = (0, 0) + minimum_top = edits[0].top + maximum_old_bottom = (0, 0) + maximum_new_bottom = (0, 0) for edit in edits: edit.do(self, record_selection=False) end_location = ( edit._edit_result.end_location if edit._edit_result else (0, 0) ) - if edit.top < minimum_from: - minimum_from = edit.top - if end_location > maximum_new_end: - maximum_new_end = end_location - if edit.bottom > maximum_old_end: - maximum_old_end = edit.bottom + if edit.top < minimum_top: + minimum_top = edit.top + if end_location > maximum_new_bottom: + maximum_new_bottom = end_location + if edit.bottom > maximum_old_bottom: + maximum_old_bottom = edit.bottom new_gutter_width = self.gutter_width if old_gutter_width != new_gutter_width: self.wrapped_document.wrap(self.wrap_width, self.indent_width) else: self.wrapped_document.wrap_range( - minimum_from, - maximum_old_end, - maximum_new_end, + minimum_top, + maximum_old_bottom, + maximum_new_bottom, ) self._refresh_size() From a77f85ee65b76712bee7b49a0d6bcefc48badc87 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 28 Mar 2024 14:08:02 +0000 Subject: [PATCH 30/53] inline no clear --- examples/code_browser.tcss | 3 +++ src/textual/app.py | 13 +++++++++++++ src/textual/drivers/linux_inline_driver.py | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/examples/code_browser.tcss b/examples/code_browser.tcss index 05928614b3..21fc7cad00 100644 --- a/examples/code_browser.tcss +++ b/examples/code_browser.tcss @@ -1,5 +1,8 @@ Screen { background: $surface-darken-1; + &:inline { + height: 50vh; + } } #tree-view { diff --git a/src/textual/app.py b/src/textual/app.py index 06225953d1..b5beea54f1 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -1449,6 +1449,7 @@ async def run_async( *, headless: bool = False, inline: bool = False, + inline_no_clear: bool = False, size: tuple[int, int] | None = None, auto_pilot: AutopilotCallbackType | None = None, ) -> ReturnType | None: @@ -1456,6 +1457,8 @@ async def run_async( Args: headless: Run in headless mode (no output). + inline: Run the app inline (under the prompt). + inline_no_clear: Don't clear the app output when exiting an inline app. size: Force terminal size to `(WIDTH, HEIGHT)`, or None to auto-detect. auto_pilot: An auto pilot coroutine. @@ -1507,6 +1510,7 @@ async def run_auto_pilot( ready_callback=None if auto_pilot is None else app_ready, headless=headless, inline=inline, + inline_no_clear=inline_no_clear, terminal_size=size, ) finally: @@ -1523,6 +1527,7 @@ def run( *, headless: bool = False, inline: bool = False, + inline_no_clear: bool = False, size: tuple[int, int] | None = None, auto_pilot: AutopilotCallbackType | None = None, ) -> ReturnType | None: @@ -1530,6 +1535,8 @@ def run( Args: headless: Run in headless mode (no output). + inline: Run the app inline (under the prompt). + inline_no_clear: Don't clear the app output when exiting an inline app. size: Force terminal size to `(WIDTH, HEIGHT)`, or None to auto-detect. auto_pilot: An auto pilot coroutine. @@ -1546,6 +1553,7 @@ async def run_app() -> None: await self.run_async( headless=headless, inline=inline, + inline_no_clear=inline_no_clear, size=size, auto_pilot=auto_pilot, ) @@ -2306,6 +2314,7 @@ async def _process_messages( ready_callback: CallbackType | None = None, headless: bool = False, inline: bool = False, + inline_no_clear: bool = False, terminal_size: tuple[int, int] | None = None, message_hook: Callable[[Message], None] | None = None, ) -> None: @@ -2442,6 +2451,10 @@ async def invoke_ready_callback() -> None: await run_process_messages() finally: + if inline_no_clear: + console = Console() + console.print(self.screen._compositor) + console.print() driver.stop_application_mode() except Exception as error: self._handle_exception(error) diff --git a/src/textual/drivers/linux_inline_driver.py b/src/textual/drivers/linux_inline_driver.py index 51c856eaaa..38d2fc0141 100644 --- a/src/textual/drivers/linux_inline_driver.py +++ b/src/textual/drivers/linux_inline_driver.py @@ -265,4 +265,4 @@ def stop_application_mode(self) -> None: self.write("\x1b[?25h") # Show cursor self.write("\033[?1004l\n") # Disable FocusIn/FocusOut. - self.flush() + self.flush() From eb3fd3bc0c3ce2a150c0f06b29479436a19f5d61 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 28 Mar 2024 14:40:43 +0000 Subject: [PATCH 31/53] Add a test around an undo/redo crash --- tests/text_area/test_history.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/text_area/test_history.py b/tests/text_area/test_history.py index b6a30e4132..8d50a63f83 100644 --- a/tests/text_area/test_history.py +++ b/tests/text_area/test_history.py @@ -341,3 +341,16 @@ async def test_redo_stack(pilot: Pilot, text_area: TextArea): text_area.redo() assert len(text_area.history.undo_stack) == 2 assert len(text_area.history.redo_stack) == 0 + + +async def test_backward_selection_undo_redo(pilot: Pilot, text_area: TextArea): + # Failed prior to https://github.com/Textualize/textual/pull/4352 + text_area.text = SIMPLE_TEXT + text_area.selection = Selection((3, 2), (0, 0)) + + await pilot.press("a") + + text_area.undo() + await pilot.press("down", "down", "down", "down") + + assert text_area.text == SIMPLE_TEXT From 9ec1ea8bc2b36e6a932455beb7e809ef838cd7bb Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 28 Mar 2024 15:16:12 +0000 Subject: [PATCH 32/53] fix clear on refresh --- src/textual/_layout.py | 2 +- src/textual/drivers/linux_inline_driver.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/textual/_layout.py b/src/textual/_layout.py index 8048a0f303..312f8c9fe5 100644 --- a/src/textual/_layout.py +++ b/src/textual/_layout.py @@ -160,7 +160,7 @@ def get_content_width(self, widget: Widget, container: Size, viewport: Size) -> if not widget._nodes: width = 0 else: - arrangement = widget._arrange(Size(container.width, 0)) + arrangement = widget._arrange(Size(0, 0)) return arrangement.total_region.right return width diff --git a/src/textual/drivers/linux_inline_driver.py b/src/textual/drivers/linux_inline_driver.py index 38d2fc0141..ffa5d0bcb7 100644 --- a/src/textual/drivers/linux_inline_driver.py +++ b/src/textual/drivers/linux_inline_driver.py @@ -166,7 +166,8 @@ def send_size_event() -> None: ) def on_terminal_resize(signum, stack) -> None: - self.write("\x1b[J") + self.write("\x1b[2J") + self.flush() send_size_event() signal.signal(signal.SIGWINCH, on_terminal_resize) From 80855c88147cb29eaf78f8a1755c88ca6239d8ec Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 29 Mar 2024 12:55:31 +0000 Subject: [PATCH 33/53] fix inline-height --- src/textual/_compositor.py | 7 ------- src/textual/screen.py | 12 ++++++------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index 9a3982ae9e..12009e6bfd 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -1012,13 +1012,6 @@ def render_inline( self, size: Size, screen_stack: list[Screen] | None = None ) -> RenderableType: visible_screen_stack.set([] if screen_stack is None else screen_stack) - - # from rich.live_render import LiveRender - - # from textual.strip import StripRenderable - - # return LiveRender(StripRenderable(self.render_strips(size), size.width)) - return InlineUpdate(self.render_strips(size)) def render_full_update(self) -> LayoutUpdate: diff --git a/src/textual/screen.py b/src/textual/screen.py index 754292e642..8822bcdb8c 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -137,10 +137,12 @@ class Screen(Generic[ScreenResultType], Widget): layout: vertical; overflow-y: auto; background: $surface; - + &:inline { height: auto; - min-height: 1; + # min-height: 1; + border-top: tall $surface; + border-bottom: tall $surface; } } """ @@ -672,13 +674,10 @@ def _compositor_refresh(self) -> None: if self.app.is_inline: size = self.app.size - inline_height = self.get_content_height(size, size, size.width) - inline_height = self.size.height - self.app._display( self, self._compositor.render_inline( - Size(size.width, inline_height), + Size(size.width, self.get_inline_height(size)), screen_stack=self.app._background_screens, ), ) @@ -875,6 +874,7 @@ def get_inline_height(self, size: Size) -> int: inline_height = self.get_content_height(size, size, size.width) else: inline_height = int(height_scalar.resolve(size, size)) + inline_height += self.styles.gutter.height min_height = self.styles.min_height max_height = self.styles.max_height if min_height is not None: From 34f37c84a3e1d59e92cf54e97eb24901da6191bf Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 29 Mar 2024 13:09:03 +0000 Subject: [PATCH 34/53] docstrings --- src/textual/__main__.py | 2 +- src/textual/_compositor.py | 18 +++++++++++++++--- src/textual/_layout.py | 2 +- src/textual/_xterm_parser.py | 11 +++++++---- src/textual/screen.py | 14 +++++++++++--- 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/textual/__main__.py b/src/textual/__main__.py index b62cb9d5b9..212d16b928 100644 --- a/src/textual/__main__.py +++ b/src/textual/__main__.py @@ -2,4 +2,4 @@ if __name__ == "__main__": app = DemoApp() - app.run(inline=True) + app.run() diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index 12009e6bfd..d9047e3fc3 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -146,6 +146,7 @@ def __rich_repr__(self) -> rich.repr.Result: @rich.repr.auto(angular=True) class InlineUpdate(CompositorUpdate): + """A renderable to write an inline update.""" def __init__(self, strips: list[Strip]) -> None: self.strips = strips @@ -174,7 +175,6 @@ def render_segments(self, console: Console) -> str: append(strip.render(console)) if not last: append("\n") - # append("\x1b[B") append("\n\x1b[J") # Clear down if len(self.strips) > 1: append( @@ -182,7 +182,7 @@ def render_segments(self, console: Console) -> str: ) # Move cursor back to original position else: append("\r") - append("\x1b[6n") # Query cursor position + append("\x1b[6n") # Query new cursor position return "".join(sequences) @@ -995,7 +995,7 @@ def render_update( """Render an update renderable. Args: - full: Enable full update, or `False` for a partial update. + screen_stack: Screen stack list. Defaults to None. Returns: A renderable for the update, or `None` if no update was required. @@ -1011,6 +1011,15 @@ def render_update( def render_inline( self, size: Size, screen_stack: list[Screen] | None = None ) -> RenderableType: + """Render an inline update. + + Args: + size: Inline size. + screen_stack: Screen stack list. Defaults to None. + + Returns: + A renderable. + """ visible_screen_stack.set([] if screen_stack is None else screen_stack) return InlineUpdate(self.render_strips(size)) @@ -1050,6 +1059,9 @@ def render_partial_update(self) -> ChopsUpdate | None: def render_strips(self, size: Size | None = None) -> list[Strip]: """Render to a list of strips. + Args: + size: Size of render. + Returns: A list of strips with the screen content. """ diff --git a/src/textual/_layout.py b/src/textual/_layout.py index 312f8c9fe5..8048a0f303 100644 --- a/src/textual/_layout.py +++ b/src/textual/_layout.py @@ -160,7 +160,7 @@ def get_content_width(self, widget: Widget, container: Size, viewport: Size) -> if not widget._nodes: width = 0 else: - arrangement = widget._arrange(Size(0, 0)) + arrangement = widget._arrange(Size(container.width, 0)) return arrangement.total_region.right return width diff --git a/src/textual/_xterm_parser.py b/src/textual/_xterm_parser.py index f703c06bc3..fc84391f39 100644 --- a/src/textual/_xterm_parser.py +++ b/src/textual/_xterm_parser.py @@ -237,8 +237,7 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None: if key_events: break # Or a mouse event? - mouse_match = _re_mouse_event.match(sequence) - if mouse_match is not None: + if (mouse_match := _re_mouse_event.match(sequence)) is not None: mouse_code = mouse_match.group(0) event = self.parse_mouse_code(mouse_code) if event: @@ -247,8 +246,11 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None: # Or a mode report? # (i.e. the terminal saying it supports a mode we requested) - mode_report_match = _re_terminal_mode_response.match(sequence) - if mode_report_match is not None: + if ( + mode_report_match := _re_terminal_mode_response.match( + sequence + ) + ) is not None: if ( mode_report_match["mode_id"] == "2026" and int(mode_report_match["setting_parameter"]) > 0 @@ -256,6 +258,7 @@ def reissue_sequence_as_keys(reissue_sequence: str) -> None: on_token(messages.TerminalSupportsSynchronizedOutput()) break + # Or a cursor position query? if ( cursor_position_match := _re_cursor_position.match(sequence) ) is not None: diff --git a/src/textual/screen.py b/src/textual/screen.py index 8822bcdb8c..b47b6962da 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -677,7 +677,7 @@ def _compositor_refresh(self) -> None: self.app._display( self, self._compositor.render_inline( - Size(size.width, self.get_inline_height(size)), + Size(size.width, self._get_inline_height(size)), screen_stack=self.app._background_screens, ), ) @@ -775,7 +775,7 @@ def _refresh_layout(self, size: Size | None = None, scroll: bool = False) -> Non """Refresh the layout (can change size and positions of widgets).""" size = self.outer_size if size is None else size if self.app.is_inline: - size = Size(size.width, self.get_inline_height(self.app.size)) + size = Size(size.width, self._get_inline_height(self.app.size)) if not size: return self._compositor.update_widgets(self._dirty_widgets) @@ -868,7 +868,15 @@ async def _on_update_scroll(self, message: messages.UpdateScroll) -> None: self._scroll_required = True self.check_idle() - def get_inline_height(self, size: Size) -> int: + def _get_inline_height(self, size: Size) -> int: + """Get the inline height (number of lines to display when running inline mode). + + Args: + size: Size of the terminal + + Returns: + Height for inline mode. + """ height_scalar = self.styles.height if height_scalar is None or height_scalar.is_auto: inline_height = self.get_content_height(size, size, size.width) From e039a19ca0c7936e54687f018d373b09b1d42316 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 29 Mar 2024 13:15:41 +0000 Subject: [PATCH 35/53] style inline --- examples/calculator.tcss | 4 ++++ src/textual/screen.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/calculator.tcss b/examples/calculator.tcss index f25b387fcd..180e3f8e0b 100644 --- a/examples/calculator.tcss +++ b/examples/calculator.tcss @@ -12,6 +12,10 @@ Screen { min-height: 25; min-width: 26; height: 100%; + + &:inline { + margin: 0 2; + } } Button { diff --git a/src/textual/screen.py b/src/textual/screen.py index b47b6962da..c5215654dd 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -141,8 +141,8 @@ class Screen(Generic[ScreenResultType], Widget): &:inline { height: auto; # min-height: 1; - border-top: tall $surface; - border-bottom: tall $surface; + border-top: tall $background; + border-bottom: tall $background; } } """ From bb01433de7e9db97fda60a268017936e701b8576 Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Fri, 29 Mar 2024 15:08:44 +0000 Subject: [PATCH 36/53] Clarify that `SelectionList` can use Rich Text Fixes the misleading text that prompted #4361. --- docs/widgets/selection_list.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/widgets/selection_list.md b/docs/widgets/selection_list.md index 1ece006784..73de670754 100644 --- a/docs/widgets/selection_list.md +++ b/docs/widgets/selection_list.md @@ -30,8 +30,8 @@ my_selection_list: SelectionList[int] = SelectionList(*selections) ## Examples A selection list is designed to be built up of single-line prompts (which -can be [Rich renderables](../guide/widgets.md#rich-renderables)) and an -associated unique value. +can be [Rich `Text`](https://rich.readthedocs.io/en/stable/text.html)) and +an associated unique value. ### Selections as tuples From f236ec31a1bca0d3a031fc3d97313be28f596da7 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 29 Mar 2024 16:22:37 +0000 Subject: [PATCH 37/53] fix cursor position --- CHANGELOG.md | 2 ++ src/textual/app.py | 30 ++++++++++++++++++---- src/textual/driver.py | 11 ++++++-- src/textual/drivers/linux_driver.py | 4 +++ src/textual/drivers/linux_inline_driver.py | 7 ++++- src/textual/drivers/web_driver.py | 9 +++++-- src/textual/drivers/windows_driver.py | 6 +++++ 7 files changed, 59 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a7ee5979d..bc50dfddaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Added `Document.start` and `end` location properties for convenience https://github.com/Textualize/textual/pull/4267 +- Added `inline` parameter to `run` and `run_async` to run app inline (under the promot). +- Added `mouse` parameter to disable mouse support ## [0.54.0] - 2024-03-26 diff --git a/src/textual/app.py b/src/textual/app.py index b5beea54f1..f23bfbdc51 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -474,6 +474,9 @@ def __init__( self._mouse_down_widget: Widget | None = None """The widget that was most recently mouse downed (used to create click events).""" + self._previous_cursor_position = Offset(0, 0) + """The previous cursor position""" + self.cursor_position = Offset(0, 0) """The position of the terminal cursor in screen-space. @@ -1450,6 +1453,7 @@ async def run_async( headless: bool = False, inline: bool = False, inline_no_clear: bool = False, + mouse: bool = False, size: tuple[int, int] | None = None, auto_pilot: AutopilotCallbackType | None = None, ) -> ReturnType | None: @@ -1459,6 +1463,7 @@ async def run_async( headless: Run in headless mode (no output). inline: Run the app inline (under the prompt). inline_no_clear: Don't clear the app output when exiting an inline app. + mouse: Enable mouse support. size: Force terminal size to `(WIDTH, HEIGHT)`, or None to auto-detect. auto_pilot: An auto pilot coroutine. @@ -1511,6 +1516,7 @@ async def run_auto_pilot( headless=headless, inline=inline, inline_no_clear=inline_no_clear, + mouse=mouse, terminal_size=size, ) finally: @@ -1528,6 +1534,7 @@ def run( headless: bool = False, inline: bool = False, inline_no_clear: bool = False, + mouse: bool = True, size: tuple[int, int] | None = None, auto_pilot: AutopilotCallbackType | None = None, ) -> ReturnType | None: @@ -1537,6 +1544,7 @@ def run( headless: Run in headless mode (no output). inline: Run the app inline (under the prompt). inline_no_clear: Don't clear the app output when exiting an inline app. + mouse: Enable mouse support. size: Force terminal size to `(WIDTH, HEIGHT)`, or None to auto-detect. auto_pilot: An auto pilot coroutine. @@ -1554,6 +1562,7 @@ async def run_app() -> None: headless=headless, inline=inline, inline_no_clear=inline_no_clear, + mouse=mouse, size=size, auto_pilot=auto_pilot, ) @@ -2315,6 +2324,7 @@ async def _process_messages( headless: bool = False, inline: bool = False, inline_no_clear: bool = False, + mouse: bool = True, terminal_size: tuple[int, int] | None = None, message_hook: Callable[[Message], None] | None = None, ) -> None: @@ -2439,6 +2449,7 @@ async def invoke_ready_callback() -> None: driver = self._driver = driver_class( self, debug=constants.DEBUG, + mouse=mouse, size=terminal_size, ) self.log(driver=driver) @@ -2451,6 +2462,11 @@ async def invoke_ready_callback() -> None: await run_process_messages() finally: + if self._driver.is_inline: + cursor_x, cursor_y = self._previous_cursor_position + self._driver.write( + Control.move(-cursor_x, -cursor_y).segment.text + ) if inline_no_clear: console = Console() console.print(self.screen._compositor) @@ -2768,12 +2784,16 @@ def _display(self, screen: Screen, renderable: RenderableType | None) -> None: try: try: if isinstance(renderable, CompositorUpdate): + cursor_x, cursor_y = self._previous_cursor_position + terminal_sequence = Control.move( + -cursor_x, -cursor_y + ).segment.text cursor_x, cursor_y = self.cursor_position - terminal_sequence = renderable.render_segments(console) - if not self.is_inline: - terminal_sequence += Control.move_to( - cursor_x, cursor_y - ).segment.text + terminal_sequence += renderable.render_segments(console) + terminal_sequence += Control.move( + cursor_x, cursor_y + ).segment.text + self._previous_cursor_position = self.cursor_position else: segments = console.render(renderable) terminal_sequence = console._render_buffer(segments) diff --git a/src/textual/driver.py b/src/textual/driver.py index 5d65a5f6e5..f0ad89c98b 100644 --- a/src/textual/driver.py +++ b/src/textual/driver.py @@ -20,6 +20,7 @@ def __init__( app: App, *, debug: bool = False, + mouse: bool = True, size: tuple[int, int] | None = None, ) -> None: """Initialize a driver. @@ -27,17 +28,19 @@ def __init__( Args: app: The App instance. debug: Enable debug mode. + mouse: Enable mouse support, size: Initial size of the terminal or `None` to detect. """ self._app = app self._debug = debug + self._mouse = mouse self._size = size self._loop = asyncio.get_running_loop() self._down_buttons: list[int] = [] self._last_move_event: events.MouseMove | None = None self._auto_restart = True """Should the application auto-restart (where appropriate)?""" - self.cursor_origin = (0, 0) + self.cursor_origin: tuple[int, int] | None = None @property def is_headless(self) -> bool: @@ -73,7 +76,11 @@ def process_event(self, event: events.Event) -> None: # NOTE: This runs in a thread. # Avoid calling methods on the app. event.set_sender(self._app) - offset_x, offset_y = self.cursor_origin + if self.cursor_origin is None: + offset_x = 0 + offset_y = 0 + else: + offset_x, offset_y = self.cursor_origin if isinstance(event, events.MouseEvent): event.x -= offset_x event.y -= offset_y diff --git a/src/textual/drivers/linux_driver.py b/src/textual/drivers/linux_driver.py index 3e7958837c..64185fd08d 100644 --- a/src/textual/drivers/linux_driver.py +++ b/src/textual/drivers/linux_driver.py @@ -111,6 +111,8 @@ def _get_terminal_size(self) -> tuple[int, int]: def _enable_mouse_support(self) -> None: """Enable reporting of mouse events.""" + if not self._mouse: + return write = self.write write("\x1b[?1000h") # SET_VT200_MOUSE write("\x1b[?1003h") # SET_ANY_EVENT_MOUSE @@ -133,6 +135,8 @@ def _disable_bracketed_paste(self) -> None: def _disable_mouse_support(self) -> None: """Disable reporting of mouse events.""" + if not self._mouse: + return write = self.write write("\x1b[?1000l") # write("\x1b[?1003l") # diff --git a/src/textual/drivers/linux_inline_driver.py b/src/textual/drivers/linux_inline_driver.py index ffa5d0bcb7..c43ff33e54 100644 --- a/src/textual/drivers/linux_inline_driver.py +++ b/src/textual/drivers/linux_inline_driver.py @@ -30,9 +30,10 @@ def __init__( app: App, *, debug: bool = False, + mouse: bool = True, size: tuple[int, int] | None = None, ): - super().__init__(app, debug=debug, size=size) + super().__init__(app, debug=debug, mouse=mouse, size=size) self._file = sys.__stderr__ self.fileno = sys.__stdin__.fileno() self.attrs_before: list[Any] | None = None @@ -76,6 +77,8 @@ def _get_terminal_size(self) -> tuple[int, int]: def _enable_mouse_support(self) -> None: """Enable reporting of mouse events.""" + if not self._mouse: + return write = self.write write("\x1b[?1000h") # SET_VT200_MOUSE write("\x1b[?1003h") # SET_ANY_EVENT_MOUSE @@ -87,6 +90,8 @@ def _enable_mouse_support(self) -> None: def _disable_mouse_support(self) -> None: """Disable reporting of mouse events.""" + if not self._mouse: + return write = self.write write("\x1b[?1000l") # write("\x1b[?1003l") # diff --git a/src/textual/drivers/web_driver.py b/src/textual/drivers/web_driver.py index 535e3109c0..e1fc7106a6 100644 --- a/src/textual/drivers/web_driver.py +++ b/src/textual/drivers/web_driver.py @@ -40,7 +40,12 @@ class WebDriver(Driver): """A headless driver that may be run remotely.""" def __init__( - self, app: App, *, debug: bool = False, size: tuple[int, int] | None = None + self, + app: App, + *, + debug: bool = False, + mouse: bool = True, + size: tuple[int, int] | None = None, ): if size is None: try: @@ -50,7 +55,7 @@ def __init__( pass else: size = width, height - super().__init__(app, debug=debug, size=size) + super().__init__(app, debug=debug, mouse=mouse, size=size) self.stdout = sys.__stdout__ self.fileno = sys.__stdout__.fileno() self._write = partial(os.write, self.fileno) diff --git a/src/textual/drivers/windows_driver.py b/src/textual/drivers/windows_driver.py index 1df31728ac..0ac68996aa 100644 --- a/src/textual/drivers/windows_driver.py +++ b/src/textual/drivers/windows_driver.py @@ -21,6 +21,7 @@ def __init__( app: App, *, debug: bool = False, + mouse: bool = True, size: tuple[int, int] | None = None, ) -> None: """Initialize Windows driver. @@ -28,6 +29,7 @@ def __init__( Args: app: The App instance. debug: Enable debug mode. + mouse: Enable mouse support. size: Initial size of the terminal or `None` to detect. """ super().__init__(app, debug=debug, size=size) @@ -53,6 +55,8 @@ def write(self, data: str) -> None: def _enable_mouse_support(self) -> None: """Enable reporting of mouse events.""" + if not self._mouse: + return write = self.write write("\x1b[?1000h") # SET_VT200_MOUSE write("\x1b[?1003h") # SET_ANY_EVENT_MOUSE @@ -62,6 +66,8 @@ def _enable_mouse_support(self) -> None: def _disable_mouse_support(self) -> None: """Disable reporting of mouse events.""" + if not self._mouse: + return write = self.write write("\x1b[?1000l") write("\x1b[?1003l") From dac035b6a0c4a12818091896d642dd526215dc15 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 29 Mar 2024 16:27:11 +0000 Subject: [PATCH 38/53] add mouse to win driver --- src/textual/drivers/windows_driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/drivers/windows_driver.py b/src/textual/drivers/windows_driver.py index 0ac68996aa..9c53d4f6da 100644 --- a/src/textual/drivers/windows_driver.py +++ b/src/textual/drivers/windows_driver.py @@ -32,7 +32,7 @@ def __init__( mouse: Enable mouse support. size: Initial size of the terminal or `None` to detect. """ - super().__init__(app, debug=debug, size=size) + super().__init__(app, debug=debug, mouse=mouse, size=size) self._file = sys.__stdout__ self.exit_event = Event() self._event_thread: Thread | None = None From ea63e8e0b49da4e3e452da38ca06864b3d7ceea4 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 29 Mar 2024 16:36:44 +0000 Subject: [PATCH 39/53] Note about run inline --- docs/guide/app.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/guide/app.md b/docs/guide/app.md index 5a59322802..9610f45892 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -38,6 +38,12 @@ If you hit ++ctrl+c++ Textual will exit application mode and return you to the c A side effect of application mode is that you may no longer be able to select and copy text in the usual way. Terminals typically offer a way to bypass this limit with a key modifier. On iTerm you can select text if you hold the ++option++ key. See the documentation for your terminal software for how to select text in application mode. +#### Run inline + +You can also run apps in _inline_ mode, which will cause the app to appear beneath the prompt (and won't go in to application mode). +Inline apps are useful for tools that integrate closely with the typical workflow of a terminal. + +To run an app in inline mode set the `inline` parameter to `True` when you call [App.run()][textual.app.App.run]. ## Events From 8cdeb4756263dd3ef40147441db3326068cbd274 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 29 Mar 2024 16:46:57 +0000 Subject: [PATCH 40/53] fix linux driver --- src/textual/drivers/linux_driver.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/textual/drivers/linux_driver.py b/src/textual/drivers/linux_driver.py index 64185fd08d..8b38190dc7 100644 --- a/src/textual/drivers/linux_driver.py +++ b/src/textual/drivers/linux_driver.py @@ -32,6 +32,7 @@ def __init__( app: App, *, debug: bool = False, + mouse: bool = True, size: tuple[int, int] | None = None, ) -> None: """Initialize Linux driver. @@ -39,9 +40,10 @@ def __init__( Args: app: The App instance. debug: Enable debug mode. + mouse: Enable mouse support. size: Initial size of the terminal or `None` to detect. """ - super().__init__(app, debug=debug, size=size) + super().__init__(app, debug=debug, mouse=mouse, size=size) self._file = sys.__stderr__ self.fileno = sys.__stdin__.fileno() self.attrs_before: list[Any] | None = None From 623d21b47bcfaab6ce92e8dd1fb549b59de4b0b5 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 29 Mar 2024 16:49:57 +0000 Subject: [PATCH 41/53] don't inline calculator --- examples/calculator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/calculator.py b/examples/calculator.py index 8f6442ebee..9c8f2f9e76 100644 --- a/examples/calculator.py +++ b/examples/calculator.py @@ -168,4 +168,4 @@ def pressed_equals(self) -> None: if __name__ == "__main__": - CalculatorApp().run(inline=True) + CalculatorApp().run() From 5606a40f075c689d36ded3d3cc5cdfb3fe3d9196 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Mar 2024 09:00:18 +0000 Subject: [PATCH 42/53] don't make demo inline --- docs/guide/app.md | 2 ++ examples/calculator.py | 2 +- src/textual/demo.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/guide/app.md b/docs/guide/app.md index 9610f45892..aea0235caa 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -40,6 +40,8 @@ If you hit ++ctrl+c++ Textual will exit application mode and return you to the c #### Run inline +!!! tip "Added in version 0.45.0" + You can also run apps in _inline_ mode, which will cause the app to appear beneath the prompt (and won't go in to application mode). Inline apps are useful for tools that integrate closely with the typical workflow of a terminal. diff --git a/examples/calculator.py b/examples/calculator.py index 9c8f2f9e76..8f6442ebee 100644 --- a/examples/calculator.py +++ b/examples/calculator.py @@ -168,4 +168,4 @@ def pressed_equals(self) -> None: if __name__ == "__main__": - CalculatorApp().run() + CalculatorApp().run(inline=True) diff --git a/src/textual/demo.py b/src/textual/demo.py index 2b3af4c053..5d2a67d74e 100644 --- a/src/textual/demo.py +++ b/src/textual/demo.py @@ -393,4 +393,4 @@ def action_screenshot(self, filename: str | None = None, path: str = "./") -> No app = DemoApp() if __name__ == "__main__": - app.run(inline=True) + app.run() From 95de754cd3425b35fe1286f3f4819d9df5e84661 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Mar 2024 09:42:27 +0000 Subject: [PATCH 43/53] docs --- docs/guide/CSS.md | 7 ++++--- src/textual/screen.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/guide/CSS.md b/docs/guide/CSS.md index 5eccb2391a..daff1349a3 100644 --- a/docs/guide/CSS.md +++ b/docs/guide/CSS.md @@ -321,12 +321,13 @@ The `background: green` is only applied to the Button underneath the mouse curso Here are some other pseudo classes: +- `:blur` Matches widgets which *do not* have input focus. +- `:dark` Matches widgets in dark mode (where `App.dark == True`). - `:disabled` Matches widgets which are in a disabled state. - `:enabled` Matches widgets which are in an enabled state. -- `:focus` Matches widgets which have input focus. -- `:blur` Matches widgets which *do not* have input focus. - `:focus-within` Matches widgets with a focused child widget. -- `:dark` Matches widgets in dark mode (where `App.dark == True`). +- `:focus` Matches widgets which have input focus. +- `:inline` Matches widgets when the app is running in inline mode. - `:light` Matches widgets in dark mode (where `App.dark == False`). ## Combinators diff --git a/src/textual/screen.py b/src/textual/screen.py index c5215654dd..aa6410e31e 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -140,7 +140,7 @@ class Screen(Generic[ScreenResultType], Widget): &:inline { height: auto; - # min-height: 1; + min-height: 1; border-top: tall $background; border-bottom: tall $background; } From 0e20fa09c03fc33eedbdcfe6f7227f0b49f12f6f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Mar 2024 09:45:26 +0000 Subject: [PATCH 44/53] changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc50dfddaf..75498cddfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Added `Document.start` and `end` location properties for convenience https://github.com/Textualize/textual/pull/4267 -- Added `inline` parameter to `run` and `run_async` to run app inline (under the promot). -- Added `mouse` parameter to disable mouse support +- Added `inline` parameter to `run` and `run_async` to run app inline (under the prompt). https://github.com/Textualize/textual/pull/4343 +- Added `mouse` parameter to disable mouse support https://github.com/Textualize/textual/pull/4343 ## [0.54.0] - 2024-03-26 From b5334ec8659042e752a4cde126fda5d32526d733 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Sat, 30 Mar 2024 09:53:28 +0000 Subject: [PATCH 45/53] docs: clarify unit of time --- src/textual/_animator.py | 2 +- src/textual/app.py | 2 +- src/textual/css/styles.py | 2 +- src/textual/message_pump.py | 4 ++-- src/textual/widget.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/textual/_animator.py b/src/textual/_animator.py index 27d6c96ce5..21234fee8b 100644 --- a/src/textual/_animator.py +++ b/src/textual/_animator.py @@ -191,7 +191,7 @@ def __call__( attribute: Name of the attribute to animate. value: The value to animate to. final_value: The final value of the animation. Defaults to `value` if not set. - duration: The duration of the animate. + duration: The duration (in seconds) of the animation. speed: The speed of the animation. delay: A delay (in seconds) before the animation starts. easing: An easing method. diff --git a/src/textual/app.py b/src/textual/app.py index 15811232c9..4883d76d54 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -741,7 +741,7 @@ def animate( attribute: Name of the attribute to animate. value: The value to animate to. final_value: The final value of the animation. - duration: The duration of the animate. + duration: The duration (in seconds) of the animation. speed: The speed of the animation. delay: A delay (in seconds) before the animation starts. easing: An easing method. diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index 8bc14d49c9..6566d61847 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -1152,7 +1152,7 @@ def animate( attribute: Name of the attribute to animate. value: The value to animate to. final_value: The final value of the animation. Defaults to `value` if not set. - duration: The duration of the animate. + duration: The duration (in seconds) of the animation. speed: The speed of the animation. delay: A delay (in seconds) before the animation starts. easing: An easing method. diff --git a/src/textual/message_pump.py b/src/textual/message_pump.py index 7c9ef51b0e..6e5a784c6a 100644 --- a/src/textual/message_pump.py +++ b/src/textual/message_pump.py @@ -350,7 +350,7 @@ def set_timer( """Make a function call after a delay. Args: - delay: Time to wait before invoking callback. + delay: Time (in seconds) to wait before invoking callback. callback: Callback to call after time has expired. name: Name of the timer (for debug). pause: Start timer paused. @@ -382,7 +382,7 @@ def set_interval( """Call a function at periodic intervals. Args: - interval: Time between calls. + interval: Time (in seconds) between calls. callback: Function to call. name: Name of the timer object. repeat: Number of times to repeat the call or 0 for continuous. diff --git a/src/textual/widget.py b/src/textual/widget.py index 4bfaabdee0..e1ac7e2b26 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -1792,7 +1792,7 @@ def animate( attribute: Name of the attribute to animate. value: The value to animate to. final_value: The final value of the animation. Defaults to `value` if not set. - duration: The duration of the animate. + duration: The duration (in seconds) of the animation. speed: The speed of the animation. delay: A delay (in seconds) before the animation starts. easing: An easing method. From c0151ca32b0440805b5f6442a28d480672d40b5e Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Sat, 30 Mar 2024 10:15:46 +0000 Subject: [PATCH 46/53] clarify notification timeout --- src/textual/app.py | 2 +- src/textual/notifications.py | 2 +- src/textual/widget.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index 4883d76d54..29153a738e 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -3387,7 +3387,7 @@ def notify( message: The message for the notification. title: The title for the notification. severity: The severity of the notification. - timeout: The timeout for the notification. + timeout: The timeout (in seconds) for the notification. The `notify` method is used to create an application-wide notification, shown in a [`Toast`][textual.widgets._toast.Toast], diff --git a/src/textual/notifications.py b/src/textual/notifications.py index 27f95ccd6d..2b2489bfbf 100644 --- a/src/textual/notifications.py +++ b/src/textual/notifications.py @@ -37,7 +37,7 @@ class Notification: """The severity level for the notification.""" timeout: float = 5 - """The timeout for the notification.""" + """The timeout (in seconds) for the notification.""" raised_at: float = field(default_factory=time) """The time when the notification was raised (in Unix time).""" diff --git a/src/textual/widget.py b/src/textual/widget.py index e1ac7e2b26..6889090f1b 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -3791,7 +3791,7 @@ def notify( message: The message for the notification. title: The title for the notification. severity: The severity of the notification. - timeout: The timeout for the notification. + timeout: The timeout (in seconds) for the notification. See [`App.notify`][textual.app.App.notify] for the full documentation for this method. From cc19158a9a1d9d18f6c9a395993c0aa7dad5c8e0 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Mar 2024 13:21:29 +0000 Subject: [PATCH 47/53] inline docs --- docs/examples/how-to/inline01.py | 31 ++++++++++++++++++++++++++ docs/examples/how-to/inline02.py | 38 ++++++++++++++++++++++++++++++++ docs/examples/widgets/clock.py | 2 +- docs/guide/app.md | 4 ++++ docs/how-to/style-inline-apps.md | 37 +++++++++++++++++++++++++++++++ mkdocs-nav.yml | 1 + 6 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 docs/examples/how-to/inline01.py create mode 100644 docs/examples/how-to/inline02.py create mode 100644 docs/how-to/style-inline-apps.md diff --git a/docs/examples/how-to/inline01.py b/docs/examples/how-to/inline01.py new file mode 100644 index 0000000000..65bd22f303 --- /dev/null +++ b/docs/examples/how-to/inline01.py @@ -0,0 +1,31 @@ +from datetime import datetime + +from textual.app import App, ComposeResult +from textual.widgets import Digits + + +class ClockApp(App): + CSS = """ + Screen { + align: center middle; + } + #clock { + width: auto; + } + """ + + def compose(self) -> ComposeResult: + yield Digits("", id="clock") + + def on_ready(self) -> None: + self.update_clock() + self.set_interval(1, self.update_clock) + + def update_clock(self) -> None: + clock = datetime.now().time() + self.query_one(Digits).update(f"{clock:%T}") + + +if __name__ == "__main__": + app = ClockApp() + app.run(inline=True) # (1)! diff --git a/docs/examples/how-to/inline02.py b/docs/examples/how-to/inline02.py new file mode 100644 index 0000000000..05b76e626a --- /dev/null +++ b/docs/examples/how-to/inline02.py @@ -0,0 +1,38 @@ +from datetime import datetime + +from textual.app import App, ComposeResult +from textual.widgets import Digits + + +class ClockApp(App): + CSS = """ + Screen { + align: center middle; + &:inline { + border: none; + height: 50vh; + Digits { + color: $success; + } + } + } + #clock { + width: auto; + } + """ + + def compose(self) -> ComposeResult: + yield Digits("", id="clock") + + def on_ready(self) -> None: + self.update_clock() + self.set_interval(1, self.update_clock) + + def update_clock(self) -> None: + clock = datetime.now().time() + self.query_one(Digits).update(f"{clock:%T}") + + +if __name__ == "__main__": + app = ClockApp() + app.run(inline=True) diff --git a/docs/examples/widgets/clock.py b/docs/examples/widgets/clock.py index 6b18af3c85..10fb281d99 100644 --- a/docs/examples/widgets/clock.py +++ b/docs/examples/widgets/clock.py @@ -28,4 +28,4 @@ def update_clock(self) -> None: if __name__ == "__main__": app = ClockApp() - app.run() + app.run(inline=True) diff --git a/docs/guide/app.md b/docs/guide/app.md index aea0235caa..81cd44e7c9 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -47,6 +47,10 @@ Inline apps are useful for tools that integrate closely with the typical workflo To run an app in inline mode set the `inline` parameter to `True` when you call [App.run()][textual.app.App.run]. +!!! tip + + You can apply [custom styles to inline apps](../how-to/style-inline-apps.md). + ## Events Textual has an event system you can use to respond to key presses, mouse actions, and internal state changes. Event handlers are methods prefixed with `on_` followed by the name of the event. diff --git a/docs/how-to/style-inline-apps.md b/docs/how-to/style-inline-apps.md new file mode 100644 index 0000000000..cef9498da1 --- /dev/null +++ b/docs/how-to/style-inline-apps.md @@ -0,0 +1,37 @@ +# Style Inline apps + +Version 0.55.0 of Textual added support for running apps *inline* (below the prompt). +Running an inline app is as simple as adding `inline=True` to [`run()`][textual.app.App.run]. + + + +Your apps will typically run inline without modification, but you may want to make some tweaks for inline mode, which you can do with a little CSS. +This How-To will explain how. + +Let's look at an inline app. +The following app displays the the current time (and keeps it up to date). + +```python hl_lines="31" +--8<-- "docs/examples/how-to/inline01.py" +``` + +1. The `inline=True` runs the app inline. + +With Textual's default settings, this clock will be displayed in 5 lines; 3 for the digits and 2 for a top and bottom border. + +You can change the height or the border with CSS and the `:inline` pseudo-selector, which only matches rules in inline mode. +Let's update this app to remove the default border, and increase the height: + +```python hl_lines="11-17" +--8<-- "docs/examples/how-to/inline02.py" +``` + +The highlighted CSS targets online inline mode. +By setting the `height` rule on Screen we can define how many lines the app should consume when it runs. +Setting `border: none` removes the default border when running in inline mode. + +We've also added a rule to change the color of the clock when running inline. + +## Summary + +Most apps will not require modification to run inline, but if you want to tweak the height and border you can write CSS that targets inline mode with the `:inline` pseudo-selector. diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml index 7dd0863b43..4fa9912b41 100644 --- a/mkdocs-nav.yml +++ b/mkdocs-nav.yml @@ -223,6 +223,7 @@ nav: - "how-to/design-a-layout.md" - "how-to/package-with-hatch.md" - "how-to/render-and-compose.md" + - "how-to/style-inline-apps.md" - "FAQ.md" - "roadmap.md" - "Blog": From 18a556b6058ad858c9c6d6ffdff65cfbb9f9f971 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Mar 2024 13:25:41 +0000 Subject: [PATCH 48/53] tweak --- docs/guide/app.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/guide/app.md b/docs/guide/app.md index 81cd44e7c9..7bc9e69e26 100644 --- a/docs/guide/app.md +++ b/docs/guide/app.md @@ -45,11 +45,7 @@ If you hit ++ctrl+c++ Textual will exit application mode and return you to the c You can also run apps in _inline_ mode, which will cause the app to appear beneath the prompt (and won't go in to application mode). Inline apps are useful for tools that integrate closely with the typical workflow of a terminal. -To run an app in inline mode set the `inline` parameter to `True` when you call [App.run()][textual.app.App.run]. - -!!! tip - - You can apply [custom styles to inline apps](../how-to/style-inline-apps.md). +To run an app in inline mode set the `inline` parameter to `True` when you call [App.run()][textual.app.App.run]. See [Style Inline Apps](../how-to/style-inline-apps.md) for how to apply additional styles to inline apps. ## Events From 3626ace4a69449e67f2c16f004e5e6ee4566b793 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Mar 2024 15:23:44 +0000 Subject: [PATCH 49/53] caps --- docs/how-to/style-inline-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to/style-inline-apps.md b/docs/how-to/style-inline-apps.md index cef9498da1..a726f048c3 100644 --- a/docs/how-to/style-inline-apps.md +++ b/docs/how-to/style-inline-apps.md @@ -1,4 +1,4 @@ -# Style Inline apps +# Style Inline Apps Version 0.55.0 of Textual added support for running apps *inline* (below the prompt). Running an inline app is as simple as adding `inline=True` to [`run()`][textual.app.App.run]. From ac10c2acdfd0b0913ea8ac73acd0496831006496 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 31 Mar 2024 16:38:48 +0100 Subject: [PATCH 50/53] width fix --- CHANGELOG.md | 1 + src/textual/_layout.py | 2 +- src/textual/_resolve.py | 2 +- src/textual/widgets/_button.py | 5 +++++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be347364b2..f663417931 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fix priority bindings not appearing in footer when key clashes with focused widget https://github.com/Textualize/textual/pull/4342 +- Reverted auto-width change ### Changed diff --git a/src/textual/_layout.py b/src/textual/_layout.py index 8048a0f303..312f8c9fe5 100644 --- a/src/textual/_layout.py +++ b/src/textual/_layout.py @@ -160,7 +160,7 @@ def get_content_width(self, widget: Widget, container: Size, viewport: Size) -> if not widget._nodes: width = 0 else: - arrangement = widget._arrange(Size(container.width, 0)) + arrangement = widget._arrange(Size(0, 0)) return arrangement.total_region.right return width diff --git a/src/textual/_resolve.py b/src/textual/_resolve.py index eecd99dcf1..647ee53f55 100644 --- a/src/textual/_resolve.py +++ b/src/textual/_resolve.py @@ -86,7 +86,7 @@ def resolve_fraction_unit( remaining_space: Fraction, resolve_dimension: Literal["width", "height"] = "width", ) -> Fraction: - """Calculate the fraction + """Calculate the fraction. Args: widget_styles: Styles for widgets with fraction units. diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index c39d0a702c..ef42b5b25a 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, cast import rich.repr +from rich.cells import cell_len from rich.console import ConsoleRenderable, RenderableType from rich.text import Text, TextType from typing_extensions import Literal, Self @@ -15,6 +16,7 @@ from ..binding import Binding from ..css._error_tools import friendly_list +from ..geometry import Size from ..message import Message from ..pad import HorizontalPad from ..reactive import reactive @@ -203,6 +205,9 @@ def __init__( self.active_effect_duration = 0.3 """Amount of time in seconds the button 'press' animation lasts.""" + def get_content_width(self, container: Size, viewport: Size) -> int: + return cell_len(str(self.label)) + 2 + def __rich_repr__(self) -> rich.repr.Result: yield from super().__rich_repr__() yield "variant", self.variant, "default" From ba1d8a99cce4020325c461258a7828ba1c391255 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 31 Mar 2024 16:39:58 +0100 Subject: [PATCH 51/53] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f663417931..06c19d9e26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fix priority bindings not appearing in footer when key clashes with focused widget https://github.com/Textualize/textual/pull/4342 -- Reverted auto-width change +- Reverted auto-width change https://github.com/Textualize/textual/pull/4369 ### Changed From a073be89183fb71b007f7fe5382d0a4925578709 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 31 Mar 2024 16:55:46 +0100 Subject: [PATCH 52/53] snapshot --- .../__snapshots__/test_snapshots.ambr | 157 ++++++++++++++++++ .../snapshot_tests/snapshot_apps/width_100.py | 36 ++++ tests/snapshot_tests/test_snapshots.py | 6 + 3 files changed, 199 insertions(+) create mode 100644 tests/snapshot_tests/snapshot_apps/width_100.py diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 67831582e8..4051913429 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -45653,6 +45653,163 @@ ''' # --- +# name: test_width_100 + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Width100PCentApp + + + + + + + + + + ─────────────────────────────────────────────────────────── + ───────────────────────────────────────────────────────── + I want to be 100% of my parent + ───────────────────────────────────────────────────────── + ───────────────────────────────────────────────────────── + I want my parent to be wide enough to wrap me and no more + ───────────────────────────────────────────────────────── + + + + + + + + + + + + + + + + + ─────────────────────────────────────────────────────────── + + + + + ''' +# --- # name: test_zero_scrollbar_size ''' diff --git a/tests/snapshot_tests/snapshot_apps/width_100.py b/tests/snapshot_tests/snapshot_apps/width_100.py new file mode 100644 index 0000000000..9e9eefa587 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/width_100.py @@ -0,0 +1,36 @@ +from textual.app import App, ComposeResult +from textual.containers import Vertical +from textual.widgets import Label + + +class Width100PCentApp(App[None]): + + CSS = """ + Vertical { + border: solid red; + width: auto; + + Label { + border: solid green; + } + + #first { + width: 100%; + } + + #second { + width: auto; + } + } + """ + + def compose(self) -> ComposeResult: + with Vertical(): + yield Label("I want to be 100% of my parent", id="first") + yield Label( + "I want my parent to be wide enough to wrap me and no more", id="second" + ) + + +if __name__ == "__main__": + Width100PCentApp().run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index f75257fe6f..600f48af06 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -686,6 +686,7 @@ async def run_before(pilot): run_before=run_before, ) + def test_markdown_space_squashing(snap_compare): assert snap_compare(SNAPSHOT_APPS_DIR / "markdown_whitespace.py") @@ -1174,3 +1175,8 @@ def test_welcome(snap_compare): def test_button_with_console_markup(snap_compare): """Regression test for https://github.com/Textualize/textual/issues/4328""" assert snap_compare(SNAPSHOT_APPS_DIR / "button_markup.py") + + +def test_width_100(snap_compare): + """Regression test for https://github.com/Textualize/textual/issues/4360""" + assert snap_compare(SNAPSHOT_APPS_DIR / "width_100.py") From 6dabbca46118b739e139672910be74822f30e713 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 1 Apr 2024 10:05:55 +0100 Subject: [PATCH 53/53] multi line label --- src/textual/widgets/_button.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index ef42b5b25a..532076c9f8 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -206,7 +206,11 @@ def __init__( """Amount of time in seconds the button 'press' animation lasts.""" def get_content_width(self, container: Size, viewport: Size) -> int: - return cell_len(str(self.label)) + 2 + try: + return max([cell_len(line) for line in self.label.plain.splitlines()]) + 2 + except ValueError: + # Empty string label + return 2 def __rich_repr__(self) -> rich.repr.Result: yield from super().__rich_repr__()