Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better "use strict" directive highlighting in ECMAScript #6606

Open
johannesrld opened this issue May 10, 2024 · 2 comments
Open

Better "use strict" directive highlighting in ECMAScript #6606

johannesrld opened this issue May 10, 2024 · 2 comments
Labels
highlights Issues or PRs about highlighting queries

Comments

@johannesrld
Copy link

johannesrld commented May 10, 2024

Describe the highlighting problem

Currently in ecmascript files strings matching "use strict" exactly are highlighted using @keyword.directive, this is mostly fine as most of the time you'll actually see this string is in a valid context, however the current query also highlights this string in places where it makes no sense, for example in const x = "use strict"

I have created some queries that fix the issue, however they are kinda ugly and i hope there is a better way of fixing this:

(program ; matches "use strict" if its the first expression in the file
  .
  (expression_statement
    .
    (string
      (string_fragment) @keyword.directive
        (#eq? @keyword.directive "use strict"))))
(function_declaration ; matches use strict if its the first expression in a function
  body:
    (statement_block
      .
      (expression_statement
        (string
          (string_fragment) @keyword.directive
            (#eq? @keyword.directive "use strict")))))
(arrow_function ; same as above but for arrow functions with blocks
  body:
    (statement_block
      .
      (expression_statement
        (string
          (string_fragment) @keyword.directive
            (#eq? @keyword.directive "use strict")))))

Example snippet that causes the problem

"use strict" // VALID

"use strict"

const a = "use strict"

"use strict"

function a() {
    "use strict"; // VALID
    "use strict"
    function b() {
        "use strict"; //VALID
        "use strict"
    }
}
{
    "use strict";
}

const a = () => {
    "use strict" // VALID
    { "use strict" }
}

function foo() {
    const lmao = 5;
    "use strict"
}

Tree-sitter parsing result

(expression_statement) ; [1:1 - 21] javascript
 (string) ; [1:1 - 12] javascript
  """ ; [1:1 - 1] javascript
  (string_fragment) ; [1:2 - 11] javascript
  """ ; [1:12 - 12] javascript
 (comment) ; [1:14 - 21] javascript
(expression_statement) ; [3:1 - 12] javascript
 (string) ; [3:1 - 12] javascript
  """ ; [3:1 - 1] javascript
  (string_fragment) ; [3:2 - 11] javascript
  """ ; [3:12 - 12] javascript
(lexical_declaration) ; [5:1 - 22] javascript
 "const" ; [5:1 - 5] javascript
 (variable_declarator) ; [5:7 - 22] javascript
  name: (identifier) ; [5:7 - 7] javascript
  "=" ; [5:9 - 9] javascript
  value: (string) ; [5:11 - 22] javascript
   """ ; [5:11 - 11] javascript
   (string_fragment) ; [5:12 - 21] javascript
   """ ; [5:22 - 22] javascript
(expression_statement) ; [7:1 - 12] javascript
 (string) ; [7:1 - 12] javascript
  """ ; [7:1 - 1] javascript
  (string_fragment) ; [7:2 - 11] javascript
  """ ; [7:12 - 12] javascript
(function_declaration) ; [9:1 - 16:1] javascript
 "function" ; [9:1 - 8] javascript
 name: (identifier) ; [9:10 - 10] javascript
 parameters: (formal_parameters) ; [9:11 - 12] javascript
  "(" ; [9:11 - 11] javascript
  ")" ; [9:12 - 12] javascript
 body: (statement_block) ; [9:14 - 16:1] javascript
  "{" ; [9:14 - 14] javascript
  (expression_statement) ; [10:5 - 17] javascript
   (string) ; [10:5 - 16] javascript
    """ ; [10:5 - 5] javascript
    (string_fragment) ; [10:6 - 15] javascript
    """ ; [10:16 - 16] javascript
   ";" ; [10:17 - 17] javascript
  (comment) ; [10:19 - 26] javascript
  (expression_statement) ; [11:5 - 16] javascript
   (string) ; [11:5 - 16] javascript
    """ ; [11:5 - 5] javascript
    (string_fragment) ; [11:6 - 15] javascript
    """ ; [11:16 - 16] javascript
  (function_declaration) ; [12:5 - 15:5] javascript
   "function" ; [12:5 - 12] javascript
   name: (identifier) ; [12:14 - 14] javascript
   parameters: (formal_parameters) ; [12:15 - 16] javascript
    "(" ; [12:15 - 15] javascript
    ")" ; [12:16 - 16] javascript
   body: (statement_block) ; [12:18 - 15:5] javascript
    "{" ; [12:18 - 18] javascript
    (expression_statement) ; [13:9 - 21] javascript
     (string) ; [13:9 - 20] javascript
      """ ; [13:9 - 9] javascript
      (string_fragment) ; [13:10 - 19] javascript
      """ ; [13:20 - 20] javascript
     ";" ; [13:21 - 21] javascript
    (comment) ; [13:23 - 29] javascript
    (expression_statement) ; [14:9 - 20] javascript
     (string) ; [14:9 - 20] javascript
      """ ; [14:9 - 9] javascript
      (string_fragment) ; [14:10 - 19] javascript
      """ ; [14:20 - 20] javascript
    "}" ; [15:5 - 5] javascript
  "}" ; [16:1 - 1] javascript
(statement_block) ; [17:1 - 19:1] javascript
 "{" ; [17:1 - 1] javascript
 (expression_statement) ; [18:5 - 17] javascript
  (string) ; [18:5 - 16] javascript
   """ ; [18:5 - 5] javascript
   (string_fragment) ; [18:6 - 15] javascript
   """ ; [18:16 - 16] javascript
  ";" ; [18:17 - 17] javascript
 "}" ; [19:1 - 1] javascript
(lexical_declaration) ; [21:1 - 24:1] javascript
 "const" ; [21:1 - 5] javascript
 (variable_declarator) ; [21:7 - 24:1] javascript
  name: (identifier) ; [21:7 - 7] javascript
  "=" ; [21:9 - 9] javascript
  value: (arrow_function) ; [21:11 - 24:1] javascript
   parameters: (formal_parameters) ; [21:11 - 12] javascript
    "(" ; [21:11 - 11] javascript
    ")" ; [21:12 - 12] javascript
   "=>" ; [21:14 - 15] javascript
   body: (statement_block) ; [21:17 - 24:1] javascript
    "{" ; [21:17 - 17] javascript
    (expression_statement) ; [22:5 - 25] javascript
     (string) ; [22:5 - 16] javascript
      """ ; [22:5 - 5] javascript
      (string_fragment) ; [22:6 - 15] javascript
      """ ; [22:16 - 16] javascript
     (comment) ; [22:18 - 25] javascript
    (statement_block) ; [23:5 - 20] javascript
     "{" ; [23:5 - 5] javascript
     (expression_statement) ; [23:7 - 18] javascript
      (string) ; [23:7 - 18] javascript
       """ ; [23:7 - 7] javascript
       (string_fragment) ; [23:8 - 17] javascript
       """ ; [23:18 - 18] javascript
     "}" ; [23:20 - 20] javascript
    "}" ; [24:1 - 1] javascript
(function_declaration) ; [26:1 - 29:1] javascript
 "function" ; [26:1 - 8] javascript
 name: (identifier) ; [26:10 - 12] javascript
 parameters: (formal_parameters) ; [26:13 - 14] javascript
  "(" ; [26:13 - 13] javascript
  ")" ; [26:14 - 14] javascript
 body: (statement_block) ; [26:16 - 29:1] javascript
  "{" ; [26:16 - 16] javascript
  (lexical_declaration) ; [27:5 - 19] javascript
   "const" ; [27:5 - 9] javascript
   (variable_declarator) ; [27:11 - 18] javascript
    name: (identifier) ; [27:11 - 14] javascript
    "=" ; [27:16 - 16] javascript
    value: (number) ; [27:18 - 18] javascript
   ";" ; [27:19 - 19] javascript
  (expression_statement) ; [28:5 - 16] javascript
   (string) ; [28:5 - 16] javascript
    """ ; [28:5 - 5] javascript
    (string_fragment) ; [28:6 - 15] javascript
    """ ; [28:16 - 16] javascript
  "}" ; [29:1 - 1] javascript

Example screenshot

current highlights new highlights
image image

Expected behavior

The Query should only highlight the use strict directive when it actually means something

Output of :checkhealth nvim-treesitter

──────────────────────────────────────────────────────────────────────────────
nvim-treesitter: require("nvim-treesitter.health").check()

Installation
- WARNING tree-sitter executable not found (parser generator, only needed for :TSInstallFromGrammar, not required for :TSInstall)
- OK node found v21.7.2 (only needed for :TSInstallFromGrammar)
- OK git executable found.
- OK cc executable found. Selected from { vim.NIL, "cc", "gcc", "clang", "cl", "zig" }
  Version: cc (SUSE Linux) 13.2.1 20240206 [revision 67ac78caf31f7cb3202177e6428a46d829b70f23]
- OK Neovim was compiled with tree-sitter runtime ABI version 14 (required >=13). Parsers must be compatible with runtime ABI.

OS Info:
{
  machine = "x86_64",
  release = "5.15.146.1-microsoft-standard-WSL2",
  sysname = "Linux",
  version = "#1 SMP Thu Jan 11 04:09:03 UTC 2024"
}

Parser/Features         H L F I J
  - bash                ✓ ✓ ✓ . ✓
  - c                   ✓ ✓ ✓ ✓ ✓
  - clojure             ✓ ✓ ✓ . ✓
  - commonlisp          ✓ ✓ ✓ . .
  - css                 ✓ . ✓ ✓ ✓
  - diff                ✓ . . . .
  - fennel              ✓ ✓ ✓ . ✓
  - git_config          ✓ . ✓ . ✓
  - git_rebase          ✓ . . . ✓
  - gitattributes       ✓ ✓ . . ✓
  - gitcommit           ✓ . . . ✓
  - gitignore           ✓ . . . .
  - html                ✓ ✓ ✓ ✓ ✓
  - javascript          ✓ ✓ ✓ ✓ ✓
  - jq                  ✓ ✓ . . ✓
  - jsdoc               ✓ . . . .
  - json                ✓ ✓ ✓ ✓ .
  - json5               ✓ . . . ✓
  - jsonc               ✓ ✓ ✓ ✓ ✓
  - lua                 ✓ ✓ ✓ ✓ ✓
  - luadoc              ✓ . . . .
  - markdown            ✓ . ✓ ✓ ✓
  - markdown_inline     ✓ . . . ✓
  - mermaid             ✓ . . . .
  - ocaml               ✓ ✓ ✓ ✓ ✓
  - org                 . . . . .
  - printf              ✓ . . . .
  - python              ✓ ✓ ✓ ✓ ✓
  - query               ✓ ✓ ✓ ✓ ✓
  - regex               ✓ . . . .
  - requirements        ✓ . . . ✓
  - sql                 ✓ . . ✓ ✓
  - toml                ✓ ✓ ✓ ✓ ✓
  - typescript          ✓ ✓ ✓ ✓ ✓
  - vim                 ✓ ✓ ✓ . ✓
  - vimdoc              ✓ . . . ✓
  - xml                 ✓ ✓ ✓ ✓ ✓
  - yaml                ✓ ✓ ✓ ✓ ✓

  Legend: H[ighlight], L[ocals], F[olds], I[ndents], In[j]ections
         +) multiple parsers found, only one will be used
         x) errors found in the query, try to run :TSUpdate {lang}

Output of nvim --version

NVIM v0.9.5
Build type: RelWithDebInfo
LuaJIT 2.1.1707061634
Compilation: /usr/bin/cc  -Og -g -Wall -Wextra -pedantic -Wno-unused-parameter -Wstrict-prototypes -std=gnu99 -Wshadow -Wconversion -Wvla -Wdouble-promotion -Wmissing-noreturn -Wmissing-format-attribute -Wmissing-prototypes -fno-common -Wno-unused-result -Wimplicit-fallthrough -fdiagnostics-color=auto -fstack-protector-strong -DUNIT_TESTING -DINCLUDE_GENERATED_DECLARATIONS -D_GNU_SOURCE -I/usr/include/luajit-5_1-2.1 -I/usr/include -I/usr/include/lua5.1 -I/home/abuild/rpmbuild/BUILD/neovim-0.9.5/build/src/nvim/auto -I/home/abuild/rpmbuild/BUILD/neovim-0.9.5/build/include -I/home/abuild/rpmbuild/BUILD/neovim-0.9.5/build/cmake.config -I/home/abuild/rpmbuild/BUILD/neovim-0.9.5/src -I/usr/include -I/usr/include -I/usr/include -I/usr/include -I/usr/include -I/usr/include -I/usr/include

   system vimrc file: "$VIM/sysinit.vim"
  fall-back for $VIM: "/usr/share/nvim"

Run :checkhealth for more info

Additional context

No response

@johannesrld johannesrld added the highlights Issues or PRs about highlighting queries label May 10, 2024
@ribru17
Copy link
Collaborator

ribru17 commented May 12, 2024

Thank you for this! It is unfortunate that the query has to be so complicated, though. Additionally, I think it would need to support comments before the 'use strict' as well as having the directive highlighted in a function_expression, something like

const a = function() {
  'use strict'
}

at which point it may not be worth the effort. My guess is it is pretty uncommon to just have a random string be 'use strict'?

@johannesrld
Copy link
Author

Hmm, its not too hard to fix both of those, I did it in a couple of minutes (you can find the updated queries here) but I agree that it might be more trouble than its worth.

At the very least I'd like to see it not highlighted when it isnt an expression (e.g. when its passed as an argument or if its a variable), this dosent fix highlighting the expression in invalid context but its a much simpler query

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
highlights Issues or PRs about highlighting queries
Projects
None yet
Development

No branches or pull requests

2 participants