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

New representation for scope handler tests #1524

Closed
3 tasks
pokey opened this issue Jun 12, 2023 · 17 comments · Fixed by #2053
Closed
3 tasks

New representation for scope handler tests #1524

pokey opened this issue Jun 12, 2023 · 17 comments · Fixed by #2053
Assignees
Labels
code quality Improvements to code quality to discuss Plan to discuss at meet-up
Milestone

Comments

@pokey
Copy link
Member

pokey commented Jun 12, 2023

The problem

We need a better way to represent our test cases for scope handlers, because today we end up needing to record a bunch of commands to test edge cases, and the tests aren't easy to read. In addition, once #1523 is merged, we expect that most scope type development will be done by viewing the output of the scope visualizer, so we'd like test cases that mirror the scope visualizer as closely as possible

The solution

We'd like an ascii representation that is readable by humans and parsable by machine. The idea is as follows:

  • We intersperse comment lines among the source code that point out regions in the line above
  • The comment lines begin with the language's comment token (eg //), followed by !
  • Each source code line is indented by extra whitespace at the beginning to allow the comment lines to be able to point at the start of the line above if necessary. If we didn't do this, then a source code line could begin at the same spot as the // on the line below, so we couldn't point out the beginning of the line.
  • The content range is marked by {^^^}
  • The domain is marked by [---]
  • The removal range is marked by (xxx)
  • The iteration scope is marked by <***>
  • We need to figure out interior / leadingDelimiter / trailingDelimiter
    • Would like to avoid unicode if possible
    • Here are suggestions from chatGPT:
      • The interior is marked by /###\
      • The leadingDelimiter is marked by «@@@»
      • The trailingDelimiter is marked by §$$$¶
  • If any range is equal to contentRange, it does not get a line, as that is the implicit assumption
  • In each of the above, the surrounding brackets are outside of the range, so that we can handle empty ranges by using eg {}
  • Each range type (eg content, domain, removal, etc) gets its own comment line
  • If there are overlapping ranges, the range with later start or earlier end gets its own set of lines after the other scope, beginning with //!2 (or //!3, etc). Note that ranges are overlapping even if they are 1 character apart, because otherwise there's no room for the brackets
  • The start of the file will contain metadata lines to indicate scope type being tested and language id, eg
     //!! scope: {type: namedFunction}
     //!! languageId: typescript, plaintext
  • Note that multiple examples of the same scope type could be present in one file, so that we don't need a million test files like we have today
  • Note that we don't handle indicating isPreferredOver; need special tests for that

Components

  • Automatically generate test case from source code. Can probably steal some code from Scope visualizer #1523
  • Parse output to run as a test. Before running the scope handler, it should remove all the special comment lines and dedent every line by the correct amount. Note that it should be able to only look at the delimiters (eg {}), as the internal markers (eg ^^^) are just for readability

Considerations

Examples

value

//!! scopeType: {type: value}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
//!                    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
//!  ----------------------------------------------------------------------------

name

//!! scopeType: {type: name}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!        ^^^^^^^^^^^^                                                          
//!  ----------------------------------------------------------------------------

statement

//!! scopeType: {type: statement}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

namedFunction

//!! scopeType: {type: namedFunction}
//!! languageId: typescript

      function aaa() {
//!1 {^^^^^^^^^^^^^^^^
        function bbb() {
//!2   {^^^^^^^^^^^^^^^^
          const ccc = "ddd";
        }
//!2  ^^^}
      }
//!1  ^}

collectionKey

//!! scopeType: {type: collectionKey}
//!! languageId: typescript

     const aaa = {
       bbb: {
//!1   ^^^   
//!1  [------
         ccc: "ddd",
//!2     ^^^        
//!2    [-----------]
       },
//!1   --]
     }

token

#!! scopeType: {type: token}
#!! languageId: plaintext

     foo.bar.baz
#!1  ^^^        
#!2     ^       
#!3      ^^^    
#!4         ^   
#!5          ^^^

Here are some alternative representations to consider:

Merge ranges onto one line

value

//!! scopeType: {type: value}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!  ------------------xxx^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-

name

//!! scopeType: {type: name}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!  ------^^^^^^^^^^^^----------------------------------------------------------

statement

//!! scopeType: {type: statement}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

namedFunction

//!! scopeType: {type: namedFunction}
//!! languageId: typescript

      function aaa() {
//!1 {^^^^^^^^^^^^^^^^
        function bbb() {
//!2   {^^^^^^^^^^^^^^^^
          const ccc = "ddd";
        }
//!2  ^^^}
      }
//!1  ^}

collectionKey

//!! scopeType: {type: collectionKey}
//!! languageId: typescript

     const aaa = {
       bbb: {
//!1   ^^^ 
//!1  [------
         ccc: "ddd",
//!2     ^^^--------
       },
//!1   --]
     }

token

#!! scopeType: {type: token}
#!! languageId: plaintext

     foo.bar.baz
#!1  ^^^        
#!2     ^       
#!3      ^^^    
#!4         ^   
#!5          ^^^
Always surrounding brackets

value

//!! scopeType: {type: value}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!                      {^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^}
//!                   (xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)
//! [----------------------------------------------------------------------------]

name

//!! scopeType: {type: name}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!       {^^^^^^^^^^^^}
//! [----------------------------------------------------------------------------]

statement

//!! scopeType: {type: statement}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//! {^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^}

namedFunction

//!! scopeType: {type: namedFunction}
//!! languageId: typescript

      function aaa() {
//!  {^^^^^^^^^^^^^^^^
        function bbb() {
//!2   {^^^^^^^^^^^^^^^^
          const ccc = "ddd";
        }
//!2  ^^^}
      }
//!   ^}

collectionKey

//!! scopeType: {type: collectionKey}
//!! languageId: typescript

     const aaa = {
       bbb: {
//!   {^^^}
//!   [------
         ccc: "ddd",
//!2    {^^^}
//!2    [-----------]
       },
//!    --]
     }

token

#!! scopeType: {type: token}
#!! languageId: plaintext

     foo.bar.baz
#!  {^^^}  {^}
#!2    {^}  {^^^}
#!3     {^^^}
Internal markers on every line of range

value, name, statement, and token

(as above)

namedFunction

//!! scopeType: {type: namedFunction}
//!! languageId: typescript

      function aaa() {
//!  {^^^^^^^^^^^^^^^^
        function bbb() {
//!   ^^^^^^^^^^^^^^^^^^
//!2   {^^^^^^^^^^^^^^^^
          const ccc = "ddd";
//!   ^^^^^^^^^^^^^^^^^^^^^^
//!2  ^^^^^^^^^^^^^^^^^^^^^^
        }
//!   ^^^
//!2  ^^^}
      }
//!   ^}

collectionKey

//!! scopeType: {type: collectionKey}
//!! languageId: typescript

     const aaa = {
       bbb: {
//!   {^^^}
//!   [------
         ccc: "ddd",
//!    -------------
//!2    {^^^}
//!2    [-----------]
       },
//!    --]
     }
No internal markers

value

//!! scopeType: {type: value}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!                      {                                                      }
//! [                                                                            ]
//!                   (                                                         )

name

//!! scopeType: {type: name}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!       {            }
//! [                                                                            ]

statement

//!! scopeType: {type: statement}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//! {                                                                            }

namedFunction

//!! scopeType: {type: namedFunction}
//!! languageId: typescript

      function aaa() {
//!  {
        function bbb() {
//!2   {
          const ccc = "ddd";
        }
//!2     }
      }
//!    }

collectionKey

//!! scopeType: {type: collectionKey}
//!! languageId: typescript

     const aaa = {
       bbb: {
//!   {   }
//!   [
         ccc: "ddd",
//!2    {   }
//!2    [           ]
       },
//!      ]
     }

token

#!! scopeType: {type: token}
#!! languageId: plaintext

     foo.bar.baz
#!  {   }  { }
#!2    { }  {   }
#!3     {   }
Using `.` to indicate whitespace

value

//!! scopeType: {type: value}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!  ....................{......................................................}
//! [............................................................................]
//!  .................(.........................................................)

name

//!! scopeType: {type: name}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!  .....{............}.........................................................
//! [............................................................................]

statement

//!! scopeType: {type: statement}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//! {............................................................................}

namedFunction

//!! scopeType: {type: namedFunction}
//!! languageId: typescript

      function aaa() {
//!  {................
        function bbb() {
//!2  .{................
          const ccc = "ddd";
        }
//!2  ...}
      }
//!   .}

collectionKey

//!! scopeType: {type: collectionKey}
//!! languageId: typescript

     const aaa = {
       bbb: {
//!  .{...}..
//!  .[......
         ccc: "ddd",
//!2 ...{...}.......
//!2 ...[...........]
       },
//!  ....]
     }

token

#!! scopeType: {type: token}
#!! languageId: plaintext

     foo.bar.baz
#!  {...}..{.}..
#!2  ..{.}..{...}
#!3  ...{...}...
Using `.` only where necessary

value

//!! scopeType: {type: value}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!  .                 . {.                                                    .}
//! [.                 .                                                       ..]
//!                   (.                                                       .)

name

//!! scopeType: {type: name}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!  .    {.          .}                                                        .
//! [.                                                                          .]

statement

//!! scopeType: {type: statement}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//! {.                                                                          .}

namedFunction

//!! scopeType: {type: namedFunction}
//!! languageId: typescript

      function aaa() {
//!  {.
        function bbb() {
//!2   {.
          const ccc = "ddd";
        }
//!2    .}
      }
//!   .}

collectionKey

//!! scopeType: {type: collectionKey}
//!! languageId: typescript

     const aaa = {
       bbb: {
//!   {. .}
//!   [.
         ccc: "ddd",
//!2    {. .}      .
//!2    [.         .]
       },
//!     .]
     }

token

#!! scopeType: {type: token}
#!! languageId: plaintext

     foo.bar.baz
#!  {. .}. {.}
#!2    {.} .{. .}
#!3     {. .}
Using markers only where necessary

value

//!! scopeType: {type: value}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!  .                 . {^                                                    ^}
//! [-                 .                                                       .-]
//!                   (x                                                       x)

name

//!! scopeType: {type: name}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!  .    {^          ^}                                                        .
//! [-                                                                          -]

statement

//!! scopeType: {type: statement}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//! {^                                                                          ^}

namedFunction

//!! scopeType: {type: namedFunction}
//!! languageId: typescript

      function aaa() {
//!  {^
        function bbb() {
//!2   {^
          const ccc = "ddd";
        }
//!2    ^}
      }
//!   ^}

collectionKey

//!! scopeType: {type: collectionKey}
//!! languageId: typescript

     const aaa = {
       bbb: {
//!   {^ ^}
//!   [-
         ccc: "ddd",
//!2    {^ ^}      .
//!2    [-         -]
       },
//!     -]
     }

token

#!! scopeType: {type: token}
#!! languageId: plaintext

     foo.bar.baz
#!  {^ ^}. {^} .
#!2    {^} .{^ ^}
#!3     {^ ^}
@pokey pokey added this to the Short list milestone Jun 12, 2023
@pokey pokey added code quality Improvements to code quality to discuss Plan to discuss at meet-up labels Jun 12, 2023
@pokey pokey removed the to discuss Plan to discuss at meet-up label Jun 13, 2023
@pokey
Copy link
Member Author

pokey commented Jun 14, 2023

A few questions:

  • should we put leading / trailing delimiter on same line as content range? They never overlap
  • should we show removal range if it's just equal to trailing ?? leading?
  • should we show leading / trailing / removal when it's just token delimiter ranges (ie whitespace)? On the one hand we could do that to write nice test cases for token removal, but on the other hand I worry it will lead to lots of noise for scopes that just get token removal by default, esp if removing them doesn't make much semantic sense. But for those where we actually want those token removal semantics it might be nice to have that tested 🤔. Maybe tree-sitter scopes could opt in / out of token removal behaviour?

cc/ @AndreasArvidsson @phillco

@AndreasArvidsson
Copy link
Member

  1. Please make an example of that I think it sounds reasonable
  2. I believe so. It's not always obvious if it's going to be trailing or leading for a particular case and we might change that algorithm in the future.
  3. I guess we could start with them and if it becomes too noisy we could remove them in the future?

@pokey
Copy link
Member Author

pokey commented Jun 14, 2023

  1. Please make an example of that I think it sounds reasonable
//!! scopeType: {type: value}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!                    xxx^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//!  ----------------------------------------------------------------------------
  1. I believe so. It's not always obvious if it's going to be trailing or leading for a particular case and we might change that algorithm in the future.

Mixed feelings. It will be extra noise I think, and while I do agree it can be unintuitive when you're just using Cursorless, in this case the leading / trailing are marked quite clearly visually. But easiest to just show them for now, and then see how annoying it is. If it feels like noise, I think just having a check if

removalRange.isRangeEqual(
  contentRange.union(trailingDelimiter ?? leadingDelimiter ?? contentRange)
);

is easy to do, and should be fairly intuitive

  1. I guess we could start with them and if it becomes too noisy we could remove them in the future?

Sounds good

@AndreasArvidsson
Copy link
Member

  1. That looks like removal range and content range on the same line. I still feel that removal range probably should be its own line.
    Something like this:
//!! scopeType: {type: value}
//!! languageId: typescript

     const removalRange = getRelatedRange(match, scopeTypeType, "removal", true);
//!                    @@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//!                    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//!  ----------------------------------------------------------------------------

@pokey
Copy link
Member Author

pokey commented Jun 14, 2023

Yeah fair. I'm happy with that

@pokey
Copy link
Member Author

pokey commented Jun 16, 2023

I wonder if we should support a compact version of this syntax inside comments in our query definitions 🤔. Eg:

(
  [
    ;;!! const foo = "bar";
    ;;!        ^^^
    ;;!  ------------------
    ;;!! let foo = "bar";
    ;;!      ^^^
    ;;!  ----------------
    (lexical_declaration
      (variable_declarator
          name: (_) @name
      )
    )

    ;;!! var foo = "bar";
    ;;!      ^^^
    ;;!  --------------
    (variable_declaration
      (variable_declarator
          name: (_) @name
      )
    )
  ] @_.domain
  (#not-parent-type? @_.domain export_statement)

  ;; Handle multiple variable declarators in one statement, eg
  ;;!! let aaa = "bbb", ccc = "ddd";
  ;;!      ^^^          ^^^
  ;;!  -----------------------------
  (#allow-multiple! @name)
)

where comments beginning with ;;!! are lines of code, and beginning with ;;! indicate a marker line. We could parse these out and run them as tests on all languages that import the given .scm file. One issue is how to indicate which scope type it is testing. I want the syntax to be quite lightweight, eg as close as possible to the type of comment we try to use above a pattern to indicate its purpose anyway. If there's only one scope type mentioned in a given pattern, we can infer they're referring to that one, but I guess need a way to disambiguate if it refers to multiple. Maybe that's not the common case so having an extra line to disambiguate when that happens is reasonable 🤔

not sure we'd want all our tests to be inline like this, as it could get pretty verbose, eg for that final one we'd want to test const and var as well, but that would make the definition file pretty long

edit: I made a draft of using these style comments in #1535. I didn't go all the way in that PR, because I wanted to preserve some of the convenient notations like (foo | bar) / [baz] / ...

edit: Maybe we could support indicating the scope type with something like the following?

    ;;!! function foo() {}
    ;;!  ^^^^^^^^^^^^^^^^^ namedFunction
    ;;!  ---------^^^----- functionName
    (function_declaration
      name: (_) @functionName
    ) @namedFunction @functionName.domain

edit: It would be cool if there were some way to check that the match for that test case was actually coming from that pattern 🤔

@josharian
Copy link
Collaborator

Any thoughts about what to do about tabs that are part of ranges? There's no right answer about how many dashes/whatever to use under them. (Can discuss over voice if the answer is long.)

@pokey
Copy link
Member Author

pokey commented Aug 14, 2023

Yes tabs continue to be the bane of our existence in cursorless. See eg

I have no idea how to handle them tbh 😅. For some situations we could possibly read the tab width setting, as suggested here, but I don't think that helps us in this situation, as different users might have different settings, and we're baking in one setting to the recorded test.

My instinct is to avoid tabs wherever possible in our tests, but that will potentially cause skew from real usage in the case of Go, where I'm guessing most users will be using tabs due to gofmt

Very much open to ideas here

@josharian
Copy link
Collaborator

The only "works always" fix that I am aware of is to fight tabs with tabs: allows tabs anywhere in ranges as a no-op, and put them below any real tabs so that the alignment is always preserved. But that creates all sorts of knock-on problems.

Let's kick around a few ideas over voice sometime.

@josharian
Copy link
Collaborator

@josharian
Copy link
Collaborator

terrible idea: replace tabs with:

U+2409 ␉ SYMBOL FOR HORIZONTAL TABULATION

@josharian
Copy link
Collaborator

not that it helps us, but elastic tab stops are nice:

https://nick-gravgaard.com/elastic-tabstops/
https://pkg.go.dev/text/tabwriter

@josharian
Copy link
Collaborator

josharian commented Aug 15, 2023

Another annoying question. (Sorry, thinking through all this stuff a bit.) How will this representation represent comment scopes? The scope system will pick up the annotations as comments themselves...

@josharian
Copy link
Collaborator

FWIW, I think the "fixed but inconvenient length" problem (how do you keep symbol-by-symbol alignment in the face of grapheme clusters) may be easier: use the correct number of zero-width spaces to preserve alignment.

And yes, there's a opposite type of this category of problem, which is zero-width spaces in content. Maybe we can assume nobody uses those. 😅

@pokey
Copy link
Member Author

pokey commented Aug 15, 2023

Another annoying question. (Sorry, thinking through all this stuff a bit.) How will this representation represent comment scopes? The scope system will pick up the annotations as comments themselves...

That's what the ! is for in //!

github-merge-queue bot pushed a commit that referenced this issue Oct 5, 2023
We only want nested value scopes if there is more than one declarator in
a statement.

Before:

<img width="186" alt="image"
src="https://github.com/cursorless-dev/cursorless/assets/755842/0aaf4491-2c68-4be2-849d-ff5c72ac968a">

After:

<img width="185" alt="image"
src="https://github.com/cursorless-dev/cursorless/assets/755842/3058a007-73b0-48cc-81dd-6f5b126fa07a">

Unfortunately we have no good way to test this change until we have
#1524

**Edit**: I found a way to test it, but it's a bit hacky.

## Checklist

- [ ] I have added
[tests](https://www.cursorless.org/docs/contributing/test-case-recorder/)
- [ ] I have updated the
[docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and
[cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet)
- [ ] I have not broken the cheatsheet
fidgetingbits pushed a commit to fidgetingbits/cursorless that referenced this issue Nov 3, 2023
We only want nested value scopes if there is more than one declarator in
a statement.

Before:

<img width="186" alt="image"
src="https://github.com/cursorless-dev/cursorless/assets/755842/0aaf4491-2c68-4be2-849d-ff5c72ac968a">

After:

<img width="185" alt="image"
src="https://github.com/cursorless-dev/cursorless/assets/755842/3058a007-73b0-48cc-81dd-6f5b126fa07a">

Unfortunately we have no good way to test this change until we have
cursorless-dev#1524

**Edit**: I found a way to test it, but it's a bit hacky.

## Checklist

- [ ] I have added
[tests](https://www.cursorless.org/docs/contributing/test-case-recorder/)
- [ ] I have updated the
[docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and
[cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet)
- [ ] I have not broken the cheatsheet
@AndreasArvidsson
Copy link
Member

AndreasArvidsson commented Nov 20, 2023

I have an idea for a really simple format to test scopes

  • Folder name denotes language. If all tests in this folders should be the same language there's really no point duplicating the language as a field in the fixture.
  • Fixture file name contains facet id from: Language support tables #2010
  • Scope type is derived from facet id
  • Fixture file only needs example code and the range representations described in this issue

example
typescript/ternary.condition.txt

;;!! true ? 0 : 1
;;!  ^^^^
;;!  ------------

An alternative format could be the code follow by a delimiter and then separate ranges. C=Content range, D=Domain etc...

true ? 0 : 1
---

  true ? 0 : 1
C ^^^^

  true ? 0 : 1
D ^^^^^^^^^^^^

Or even more verbose

true ? 0 : 1
---

[Content]
true ? 0 : 1
^^^^

[Domain]
true ? 0 : 1
^^^^^^^^^^^^

Personally I'm quite partial to the last one. If the file only tests a single facet I see no need for them to be so terse. I also like that the indentation is the actual indentation from the code and there are no prefixes on the line.

@AndreasArvidsson AndreasArvidsson added the to discuss Plan to discuss at meet-up label Nov 20, 2023
@AndreasArvidsson
Copy link
Member

AndreasArvidsson commented Nov 20, 2023

Me and @josharian just had discussion about my post above. We are both in agreement that the third and more verbose format is the way to go. We came up with a few thoughts.

  • Inside the language folder we can support sub folders with arbitrary names. You might want to group function facets for example.
  • Only one range per header. If we need multiple content ranges we would have multiple content headers. This means that there is never range interleaving and there is no need to denote the start or end of a range.
  • The only part that's actually gonna be parsed/read from these files are the code at the top and to the delimiter. Therefore we can have a custom format for the rest of the file if we like. If we in the future need a parsable format we could switch to something like txtars

@AndreasArvidsson AndreasArvidsson mentioned this issue Nov 22, 2023
2 tasks
@AndreasArvidsson AndreasArvidsson linked a pull request Dec 5, 2023 that will close this issue
2 tasks
@AndreasArvidsson AndreasArvidsson self-assigned this Dec 5, 2023
github-merge-queue bot pushed a commit that referenced this issue Dec 6, 2023
Started implementing a new scope handler test representation


```
[Content]
const name = "Hello world";
              ^^^^^^^^^^^^^
```

Originally I was thinking of doing `^` for each character. That worked
fine until @pokey asked the question about empty ranges which we
actually has in a few places. So I switch to a multiline representation
of `[---]` where the empty range would be `[]`.


```
[Content]
 function myFunk() {
[-------------------
 
 }
 -]
```

I do wonder if we want to start multiline ranges above the source code
and end it below. Makes it so the code is actually inside the range
endings.

```
[Content]
[-------------------
 function myFunk() {
 
 }
 -]
```

Relevant issues
#1524
#2010

## Checklist

- [x] I have added
[tests](https://www.cursorless.org/docs/contributing/test-case-recorder/)
- [ ] I have updated the
[docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and
[cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet)
- [-] I have not broken the cheatsheet

---------

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
code quality Improvements to code quality to discuss Plan to discuss at meet-up
Projects
Development

Successfully merging a pull request may close this issue.

3 participants