Repository: Textualize/textual

Issue:
Document.end line number is off by one (1-indexed)
`Document.start` and `Document.end` were introduced in https://github.com/Textualize/textual/pull/4267

`Document.start` maps to `(0, 0)`, but `Document.end` is 1-indexed, with its line number computed from `Document.line_count`. This is confusing, and breaks with the standard from other functions that return or accept a `Location`.

For example, this is the current behavior:

```py
>>> from textual.widgets.text_area import Document
>>> doc = Document("foo\nbar")
>>> doc.end
(2, 3)
```

Expected:
```py
>>> doc.end
(1, 3)
```

# Textual Diagnostics

## Versions

| Name    | Value  |
|---------|--------|
| Textual | 0.56.4 |
| Rich    | 13.7.1 |

## Python

| Name           | Value                                                                            |
|----------------|----------------------------------------------------------------------------------|
| Version        | 3.8.10                                                                           |
| Implementation | CPython                                                                          |
| Compiler       | GCC 9.3.0                                                                        |
| Executable     | /home/tco/.cache/pypoetry/virtualenvs/textual-textarea-mNcIuqFZ-py3.8/bin/python |

## Operating System

| Name    | Value                               |
|---------|-------------------------------------|
| System  | Linux                               |
| Release | 5.15.146.1-microsoft-standard-WSL2  |
| Version | #1 SMP Thu Jan 11 04:09:03 UTC 2024 |

## Terminal

| Name                 | Value            |
|----------------------|------------------|
| Terminal Application | Windows Terminal |
| TERM                 | xterm-256color   |
| COLORTERM            | truecolor        |
| FORCE_COLOR          | *Not set*        |
| NO_COLOR             | *Not set*        |

## Rich Console options

| Name           | Value                |
|----------------|----------------------|
| size           | width=120, height=30 |
| legacy_windows | False                |
| min_width      | 1                    |
| max_width      | 120                  |
| is_terminal    | True                 |
| encoding       | utf-8                |
| max_height     | 30                   |
| justify        | None                 |
| overflow       | None                 |
| no_wrap        | False                |
| highlight      | None                 |
| markup         | None                 |
| height         | None                 |



Discussion:
We found the following entry in the [FAQ](https://textual.textualize.io/FAQ/) which you may find helpful:


- [How do I center a widget in a screen?](https://textual.textualize.io/FAQ/#how-do-i-center-a-widget-in-a-screen)

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This is an automated reply, generated by [FAQtory](https://github.com/willmcgugan/faqtory)

Resolving patch:
diff --git a/src/textual/document/_document.py b/src/textual/document/_document.py
--- a/src/textual/document/_document.py
+++ b/src/textual/document/_document.py
@@ -351,7 +351,7 @@ def start(self) -> Location:
     def end(self) -> Location:
         """Returns the location of the end of the document."""
         last_line = self._lines[-1]
-        return (self.line_count, len(last_line))
+        return (self.line_count - 1, len(last_line))
 
     def get_index_from_location(self, location: Location) -> int:
         """Given a location, returns the index from the document's text.


Test patch:
diff --git a/tests/document/test_document.py b/tests/document/test_document.py
--- a/tests/document/test_document.py
+++ b/tests/document/test_document.py
@@ -137,3 +137,16 @@ def test_location_from_index(text):
         len(lines) - 1,
         len(lines[-1]),
     )
+
+
+@pytest.mark.parametrize(
+    "text", [TEXT, TEXT_NEWLINE, TEXT_WINDOWS, TEXT_WINDOWS_NEWLINE]
+)
+def test_document_end(text):
+    """The location is always what we expect."""
+    document = Document(text)
+    expected_line_number = (
+        len(text.splitlines()) if text.endswith("\n") else len(text.splitlines()) - 1
+    )
+    expected_pos = 0 if text.endswith("\n") else (len(text.splitlines()[-1]))
+    assert document.end == (expected_line_number, expected_pos)