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

Respect User Indent Settings #147

Closed
wants to merge 18 commits into from

Conversation

thecoolwinter
Copy link
Collaborator

@thecoolwinter thecoolwinter commented Feb 19, 2023

Description

Adds an indentOption binding to the initializer to determine the editor's behavior on a tab key press. Before, our indent string was determined by creating a string repeating the space character tabWidth times. tabWidth should be used for the number of spaces a tab character takes up visually, and will have to be in a different PR. This PR also removes the TabReplacementFilter object as it's no longer needed.

IndentOption is a Codable-conforming enum that can be either spaces with a number of spaces to insert, or tabs. The Codable inheritance was added due to the fact that this option will most likely be stored for user preferences.

This PR will have a related PR in CodeEdit for the settings required to enable this functionality.

Related Issues

Screenshots

Screen.Recording.2023-02-19.at.1.43.36.PM.mov

@thecoolwinter thecoolwinter marked this pull request as draft February 21, 2023 17:45
@lukepistrol
Copy link
Member

Is this still WIP?

lukepistrol and others added 13 commits March 10, 2023 10:20
- issue template has been updated to reflect the templates in `CodeEdit`
<!--- IMPORTANT: If this PR addresses multiple unrelated issues, it will
be closed until separated. -->

### Description

The STTextViewDelegate textDidChange function changed to
textViewDidChangeText. Without this change, the text binding value
wasn't updated.

Before, in a situation like the one below, the prompt variable was never
updated since the delegate function was not called because of the
mismatched signature.
```
@State var prompt: String = "..."
(...)

 CodeEditTextView(
    $prompt,
    language: .default,
    theme: $theme,
    font: $font,
    tabWidth: $tabWidth,
    lineHeight: $lineHeight, wrapLines: .constant(true),
    editorOverscroll: $editorOverscroll
)
.onChange(of: prompt, perform: { promptUpdate in
    print(promptUpdate)
})
```

With the proposed update, the behaviour is as expected. The `onChange`
is now triggered.

The STTextViewDelegate:
[https://github.com/krzyzanowskim/STTextView/blob/main/Sources/STTextView/STTextViewDelegate.swift](https://github.com/krzyzanowskim/STTextView/blob/main/Sources/STTextView/STTextViewDelegate.swift)

<!--- REQUIRED: Describe what changed in detail -->

### Checklist

<!--- Add things that are not yet implemented above -->

- [x] I read and understood the [contributing
guide](https://github.com/CodeEditApp/CodeEdit/blob/main/CONTRIBUTING.md)
as well as the [code of
conduct](https://github.com/CodeEditApp/CodeEdit/blob/main/CODE_OF_CONDUCT.md)
- [ ] The issues this PR addresses are related to each other
- [x] My changes generate no new warnings
- [x] My code builds and runs on my machine
- [ ] My changes are all related to the related issue above
- [x] I documented my code

Author: @miguel-arrf 
Approved by: @thecoolwinter
In essence, the line is calculated by dividing the y-position of the
text segment with the cursor by the line height:
```swift 
var line = Int(textSegmentFrame.maxY / textSegmentFrame.height)
```
However, this counts the preceding line wraps as lines too. As a result,
I have to count the preceding line wraps with:
```swift
textLayoutManager.enumerateTextLayoutFragments(from: textLayoutManager.documentRange.location,
                                                           options: [.ensuresLayout, .ensuresExtraLineFragment]
            ) { textLayoutFragment in

                guard let cursorTextLineFragment = textLayoutManager.textLineFragment(at: insertionPointLocation)
                else { return false }

                /// Check whether the textLayoutFragment has line wraps
                if textLayoutFragment.textLineFragments.count > 1 {
                    for lineFragment in textLayoutFragment.textLineFragments {
                        lineWrapsCount += 1
                        /// Do not count lineFragments after the lineFragment where the cursor is placed
                        if lineFragment == cursorTextLineFragment { break }
                    }

                    /// The first lineFragment will be counted as an actual line
                        lineWrapsCount -= 1
                }

                if textLayoutFragment.textLineFragments.contains(cursorTextLineFragment) {
                    return false
                }
                return true
            }
```
Unfortunately, this does scale with the line count of the file. So we
might want to change that if we can come up with a better alternative in
the future. As a first implementation, I think it works.

---------

Co-authored-by: Lukas Pistrol <[email protected]>
<!--- IMPORTANT: If this PR addresses multiple unrelated issues, it will
be closed until separated. -->

### Description

1. Add 20 pixels of inset to the left of the ruler and 8 pixels of inset
to the right of the ruler. I tried to match it as close as possible to
Xcode.
2. Set the background color of the highlight ruler line to the same
color as the highlighted line in the textView.

### Related Issues

<!--- REQUIRED: Tag all related issues (e.g. * CodeEditApp#123) -->
<!--- If this PR resolves the issue please specify (e.g. * closes CodeEditApp#123)
-->
<!--- If this PR addresses multiple issues, these issues must be related
to one other -->

* closes CodeEditApp#156
* closes CodeEditApp#128

### Checklist

<!--- Add things that are not yet implemented above -->

- [x] I read and understood the [contributing
guide](https://github.com/CodeEditApp/CodeEdit/blob/main/CONTRIBUTING.md)
as well as the [code of
conduct](https://github.com/CodeEditApp/CodeEdit/blob/main/CODE_OF_CONDUCT.md)
- [x] The issues this PR addresses are related to each other
- [x] My changes generate no new warnings
- [x] My code builds and runs on my machine
- [x] My changes are all related to the related issue above
- [x] I documented my code

### Screenshots

<!--- REQUIRED: if issue is UI related -->

<img width="765" alt="Screenshot 2023-03-16 at 19 05 59"
src="https://user-images.githubusercontent.com/82230675/225717006-40530a7b-7864-404b-8d14-5307f6f83bdb.png">


<!--- IMPORTANT: Fill out all required fields. Otherwise we might close
this PR temporarily -->
# Description

This PR adds support for injected languages using tree-sitter as
described in CodeEditApp#16 and in the [tree-sitter
documentation](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection).
Languages can contain _injected_ languages which are described in a
language's `injections.scm` file. Some examples of injected languages
are:
- HTML contains CSS and Javascript in `style` and `script` tags
- Javascript contains Regex literals
- PHP contains HTML between the `<php` tags
- C++ can contain rawstring literals of arbitrary languages

# Details

This PR is a rework of the `TreeSitterClient` class. Specifically it:
- Adds a `layers` array and `primaryLayer` property. `layers` contains
all language layers in the document, and `primaryLayer` is the ID of the
document's primary language.
- Each layer is a `LanguageLayer` object. These objects represent an
injected language-range(s) combination. Each layer can have one or more
range associated with it depending on if it should be [parsed as one
document or
multiple](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection).
- When editing:
- Each layer's ranges are updated using a similar algorithm to
tree-sitter, and edits are applied to make use of the incremental
parsing tree-sitter gives.
- Each layer is checked for any injections, inserting any new injected
layers and keeping track of any 'untouched' layers to remove.
  - Any layers that were not touched are removed.

The highlight query algorithm is largely the same, but keeps track of
any ranges not used by any injected layers, and only queries the primary
layer for those ranges so as not to override any injected highlights.

# Related Issues

- Closes CodeEditApp#16 

# Screenshots

Before, other languages were detected but parsed and highlighted as
normal text.
<img width="1104" alt="Screenshot 2023-02-28 at 3 08 52 PM"
src="https://user-images.githubusercontent.com/35942988/221980502-3aa61b6a-136a-43b9-a545-8fd835945002.png">

With Injected languages, in this case CSS and JS embedded in HTML and a
second layer of Regex embedded in JS embedded in HTML:
<img width="889" alt="Screenshot 2023-03-24 at 2 45 15 PM"
src="https://user-images.githubusercontent.com/35942988/227628557-df55d986-a104-4a24-ab6c-97c8e69f6136.png">
@thecoolwinter thecoolwinter marked this pull request as ready for review March 26, 2023 16:32
@thecoolwinter thecoolwinter self-assigned this Mar 26, 2023
austincondiff pushed a commit that referenced this pull request Mar 28, 2023
<!--- IMPORTANT: If this PR addresses multiple unrelated issues, it will
be closed until separated. -->

### Description

> This is a near clone of #147, but git got messed up on that branch.
This PR improves that branch anyways.

This enables configuration of the behavior when the tab key is pressed.
Previously all tabs were converted to spaces and inserted `tabWidth`
spaces in place of the tab character. This PR clarifies that the
`tabWidth` parameter should be used for the *visual* width of tabs, and
adds an `indentOption` parameter that specifies how to handle inserting
tab characters.

Adds an `IndentOption` enum with two cases for this behavior:
- `spaces(count: Int)`
- `tab`

If `spaces(count: Int)` is specified, the editor will insert the given
number of spaces when the tab key is pressed, otherwise the tab
character will be kept.

### Related Issues

<!--- REQUIRED: Tag all related issues (e.g. * #123) -->
<!--- If this PR resolves the issue please specify (e.g. * closes #123)
-->
<!--- If this PR addresses multiple issues, these issues must be related
to one other -->

* #80 - Does not close, needs an additional PR for the tab width
setting.

### Checklist

<!--- Add things that are not yet implemented above -->

- [x] I read and understood the [contributing
guide](https://github.com/CodeEditApp/CodeEdit/blob/main/CONTRIBUTING.md)
as well as the [code of
conduct](https://github.com/CodeEditApp/CodeEdit/blob/main/CODE_OF_CONDUCT.md)
- [x] The issues this PR addresses are related to each other
- [x] My changes generate no new warnings
- [x] My code builds and runs on my machine
- [x] My changes are all related to the related issue above
- [x] I documented my code

### Screenshots


https://user-images.githubusercontent.com/35942988/228014785-85a20e2e-0465-4767-9d53-b97b4df2e11e.mov
@thecoolwinter thecoolwinter deleted the respect-tab-width branch July 12, 2023 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants