We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Formatting adding trailing newline clears last line
* OS and language server
Linux + Gopls
* How you installed LSP (Package Control or from git?)
Package Control
* Minimal reproduction steps
```go
package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello, world")
} // No newline!
```

Format

```go
package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello, world")
```
* Log
No diagnostic output. 

Initially reported to [gopls](https://github.com/golang/go/issues/33717), but they pointed out that the gopls commandline does the right thing.

Is this a LSP issue or Sublime itself?
Let me know if I can provide any other helpful information!

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `plugin/edit.py`
Content:
```
1 import sublime
2 import sublime_plugin
3 from .core.edit import sort_by_application_order
4 try:
5     from typing import List, Dict, Optional, Any, Iterable, Tuple
6     from .core.edit import TextEdit
7     assert List and Dict and Optional and Any and Iterable and Tuple and TextEdit
8 except ImportError:
9     pass
10 from .core.logging import debug
11 
12 
13 class LspApplyWorkspaceEditCommand(sublime_plugin.WindowCommand):
14     def run(self, changes: 'Optional[Dict[str, List[TextEdit]]]' = None):
15         documents_changed = 0
16         if changes:
17             for path, document_changes in changes.items():
18                 self.open_and_apply_edits(path, document_changes)
19                 documents_changed += 1
20 
21         if documents_changed > 0:
22             message = 'Applied changes to {} documents'.format(documents_changed)
23             self.window.status_message(message)
24         else:
25             self.window.status_message('No changes to apply to workspace')
26 
27     def open_and_apply_edits(self, path, file_changes):
28         view = self.window.open_file(path)
29         if view:
30             if view.is_loading():
31                 # TODO: wait for event instead.
32                 sublime.set_timeout_async(
33                     lambda: view.run_command('lsp_apply_document_edit', {'changes': file_changes}),
34                     500
35                 )
36             else:
37                 view.run_command('lsp_apply_document_edit',
38                                  {'changes': file_changes})
39         else:
40             debug('view not found to apply', path, file_changes)
41 
42 
43 class LspApplyDocumentEditCommand(sublime_plugin.TextCommand):
44     def run(self, edit, changes: 'Optional[List[TextEdit]]' = None):
45         # Apply the changes in reverse, so that we don't invalidate the range
46         # of any change that we haven't applied yet.
47         if changes:
48             for change in sort_by_application_order(changes):
49                 start, end, newText = change
50                 region = sublime.Region(self.view.text_point(*start), self.view.text_point(*end))
51                 self.apply_change(region, newText, edit)
52 
53     def apply_change(self, region: 'sublime.Region', newText: str, edit):
54         if region.empty():
55             self.view.insert(edit, region.a, newText)
56         else:
57             if len(newText) > 0:
58                 self.view.replace(edit, region, newText)
59             else:
60                 self.view.erase(edit, region)
61
```

Path: `plugin/core/edit.py`
Content:
```
1 from .url import uri_to_filename
2 try:
3     from typing import List, Dict, Optional, Any, Iterable, Tuple
4     TextEdit = Tuple[Tuple[int, int], Tuple[int, int], str]
5     assert List and Dict and Optional and Any and Iterable and Tuple
6 except ImportError:
7     pass
8 
9 
10 def parse_workspace_edit(workspace_edit: 'Dict[str, Any]') -> 'Dict[str, List[TextEdit]]':
11     changes = {}  # type: Dict[str, List[TextEdit]]
12     if 'changes' in workspace_edit:
13         for uri, file_changes in workspace_edit.get('changes', {}).items():
14             changes[uri_to_filename(uri)] = list(parse_text_edit(change) for change in file_changes)
15     if 'documentChanges' in workspace_edit:
16         for document_change in workspace_edit.get('documentChanges', []):
17             uri = document_change.get('textDocument').get('uri')
18             changes[uri_to_filename(uri)] = list(parse_text_edit(change) for change in document_change.get('edits'))
19     return changes
20 
21 
22 def parse_range(range: 'Dict[str, int]') -> 'Tuple[int, int]':
23     return range['line'], range['character']
24 
25 
26 def parse_text_edit(text_edit: 'Dict[str, Any]') -> 'TextEdit':
27     return (
28         parse_range(text_edit['range']['start']),
29         parse_range(text_edit['range']['end']),
30         text_edit.get('newText', '')
31     )
32 
33 
34 def sort_by_application_order(changes: 'Iterable[TextEdit]') -> 'List[TextEdit]':
35 
36     def get_start_position(pair: 'Tuple[int, TextEdit]'):
37         index, change = pair
38         return change[0][0], change[0][1], index
39 
40     # The spec reads:
41     # > However, it is possible that multiple edits have the same start position: multiple
42     # > inserts, or any number of inserts followed by a single remove or replace edit. If
43     # > multiple inserts have the same position, the order in the array defines the order in
44     # > which the inserted strings appear in the resulting text.
45     # So we sort by start position. But if multiple text edits start at the same position,
46     # we use the index in the array as the key.
47 
48     return list(map(lambda pair: pair[1], sorted(enumerate(changes), key=get_start_position, reverse=True)))
49
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/plugin/core/edit.py b/plugin/core/edit.py
--- a/plugin/core/edit.py
+++ b/plugin/core/edit.py
@@ -1,4 +1,5 @@
 from .url import uri_to_filename
+import operator
 try:
     from typing import List, Dict, Optional, Any, Iterable, Tuple
     TextEdit = Tuple[Tuple[int, int], Tuple[int, int], str]
@@ -32,11 +33,6 @@
 
 
 def sort_by_application_order(changes: 'Iterable[TextEdit]') -> 'List[TextEdit]':
-
-    def get_start_position(pair: 'Tuple[int, TextEdit]'):
-        index, change = pair
-        return change[0][0], change[0][1], index
-
     # The spec reads:
     # > However, it is possible that multiple edits have the same start position: multiple
     # > inserts, or any number of inserts followed by a single remove or replace edit. If
@@ -45,4 +41,4 @@
     # So we sort by start position. But if multiple text edits start at the same position,
     # we use the index in the array as the key.
 
-    return list(map(lambda pair: pair[1], sorted(enumerate(changes), key=get_start_position, reverse=True)))
+    return list(sorted(changes, key=operator.itemgetter(0)))
diff --git a/plugin/edit.py b/plugin/edit.py
--- a/plugin/edit.py
+++ b/plugin/edit.py
@@ -41,14 +41,24 @@
 
 
 class LspApplyDocumentEditCommand(sublime_plugin.TextCommand):
+
     def run(self, edit, changes: 'Optional[List[TextEdit]]' = None):
         # Apply the changes in reverse, so that we don't invalidate the range
         # of any change that we haven't applied yet.
         if changes:
-            for change in sort_by_application_order(changes):
+            last_row, last_col = self.view.rowcol(self.view.size())
+            for change in reversed(sort_by_application_order(changes)):
                 start, end, newText = change
                 region = sublime.Region(self.view.text_point(*start), self.view.text_point(*end))
-                self.apply_change(region, newText, edit)
+
+                if start[0] > last_row and newText[0] != '\n':
+                    # Handle when a language server (eg gopls) inserts at a row beyond the document
+                    # some editors create the line automatically, sublime needs to have the newline prepended.
+                    debug('adding new line for edit at line {}, document ended at line {}'.format(start[0], last_row))
+                    self.apply_change(region, '\n' + newText, edit)
+                    last_row, last_col = self.view.rowcol(self.view.size())
+                else:
+                    self.apply_change(region, newText, edit)
 
     def apply_change(self, region: 'sublime.Region', newText: str, edit):
         if region.empty():

{"golden_diff": "diff --git a/plugin/core/edit.py b/plugin/core/edit.py\n--- a/plugin/core/edit.py\n+++ b/plugin/core/edit.py\n@@ -1,4 +1,5 @@\n from .url import uri_to_filename\n+import operator\n try:\n     from typing import List, Dict, Optional, Any, Iterable, Tuple\n     TextEdit = Tuple[Tuple[int, int], Tuple[int, int], str]\n@@ -32,11 +33,6 @@\n \n \n def sort_by_application_order(changes: 'Iterable[TextEdit]') -> 'List[TextEdit]':\n-\n-    def get_start_position(pair: 'Tuple[int, TextEdit]'):\n-        index, change = pair\n-        return change[0][0], change[0][1], index\n-\n     # The spec reads:\n     # > However, it is possible that multiple edits have the same start position: multiple\n     # > inserts, or any number of inserts followed by a single remove or replace edit. If\n@@ -45,4 +41,4 @@\n     # So we sort by start position. But if multiple text edits start at the same position,\n     # we use the index in the array as the key.\n \n-    return list(map(lambda pair: pair[1], sorted(enumerate(changes), key=get_start_position, reverse=True)))\n+    return list(sorted(changes, key=operator.itemgetter(0)))\ndiff --git a/plugin/edit.py b/plugin/edit.py\n--- a/plugin/edit.py\n+++ b/plugin/edit.py\n@@ -41,14 +41,24 @@\n \n \n class LspApplyDocumentEditCommand(sublime_plugin.TextCommand):\n+\n     def run(self, edit, changes: 'Optional[List[TextEdit]]' = None):\n         # Apply the changes in reverse, so that we don't invalidate the range\n         # of any change that we haven't applied yet.\n         if changes:\n-            for change in sort_by_application_order(changes):\n+            last_row, last_col = self.view.rowcol(self.view.size())\n+            for change in reversed(sort_by_application_order(changes)):\n                 start, end, newText = change\n                 region = sublime.Region(self.view.text_point(*start), self.view.text_point(*end))\n-                self.apply_change(region, newText, edit)\n+\n+                if start[0] > last_row and newText[0] != '\\n':\n+                    # Handle when a language server (eg gopls) inserts at a row beyond the document\n+                    # some editors create the line automatically, sublime needs to have the newline prepended.\n+                    debug('adding new line for edit at line {}, document ended at line {}'.format(start[0], last_row))\n+                    self.apply_change(region, '\\n' + newText, edit)\n+                    last_row, last_col = self.view.rowcol(self.view.size())\n+                else:\n+                    self.apply_change(region, newText, edit)\n \n     def apply_change(self, region: 'sublime.Region', newText: str, edit):\n         if region.empty():\n", "issue": "Formatting adding trailing newline clears last line\n* OS and language server\r\nLinux + Gopls\r\n* How you installed LSP (Package Control or from git?)\r\nPackage Control\r\n* Minimal reproduction steps\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n)\r\n\r\nfunc main() {\r\n\tfmt.Println(\"Hello, world\")\r\n} // No newline!\r\n```\r\n\r\nFormat\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n)\r\n\r\nfunc main() {\r\n\tfmt.Println(\"Hello, world\")\r\n```\r\n* Log\r\nNo diagnostic output. \r\n\r\nInitially reported to [gopls](https://github.com/golang/go/issues/33717), but they pointed out that the gopls commandline does the right thing.\r\n\r\nIs this a LSP issue or Sublime itself?\r\nLet me know if I can provide any other helpful information!\n", "before_files": [{"content": "import sublime\nimport sublime_plugin\nfrom .core.edit import sort_by_application_order\ntry:\n    from typing import List, Dict, Optional, Any, Iterable, Tuple\n    from .core.edit import TextEdit\n    assert List and Dict and Optional and Any and Iterable and Tuple and TextEdit\nexcept ImportError:\n    pass\nfrom .core.logging import debug\n\n\nclass LspApplyWorkspaceEditCommand(sublime_plugin.WindowCommand):\n    def run(self, changes: 'Optional[Dict[str, List[TextEdit]]]' = None):\n        documents_changed = 0\n        if changes:\n            for path, document_changes in changes.items():\n                self.open_and_apply_edits(path, document_changes)\n                documents_changed += 1\n\n        if documents_changed > 0:\n            message = 'Applied changes to {} documents'.format(documents_changed)\n            self.window.status_message(message)\n        else:\n            self.window.status_message('No changes to apply to workspace')\n\n    def open_and_apply_edits(self, path, file_changes):\n        view = self.window.open_file(path)\n        if view:\n            if view.is_loading():\n                # TODO: wait for event instead.\n                sublime.set_timeout_async(\n                    lambda: view.run_command('lsp_apply_document_edit', {'changes': file_changes}),\n                    500\n                )\n            else:\n                view.run_command('lsp_apply_document_edit',\n                                 {'changes': file_changes})\n        else:\n            debug('view not found to apply', path, file_changes)\n\n\nclass LspApplyDocumentEditCommand(sublime_plugin.TextCommand):\n    def run(self, edit, changes: 'Optional[List[TextEdit]]' = None):\n        # Apply the changes in reverse, so that we don't invalidate the range\n        # of any change that we haven't applied yet.\n        if changes:\n            for change in sort_by_application_order(changes):\n                start, end, newText = change\n                region = sublime.Region(self.view.text_point(*start), self.view.text_point(*end))\n                self.apply_change(region, newText, edit)\n\n    def apply_change(self, region: 'sublime.Region', newText: str, edit):\n        if region.empty():\n            self.view.insert(edit, region.a, newText)\n        else:\n            if len(newText) > 0:\n                self.view.replace(edit, region, newText)\n            else:\n                self.view.erase(edit, region)\n", "path": "plugin/edit.py"}, {"content": "from .url import uri_to_filename\ntry:\n    from typing import List, Dict, Optional, Any, Iterable, Tuple\n    TextEdit = Tuple[Tuple[int, int], Tuple[int, int], str]\n    assert List and Dict and Optional and Any and Iterable and Tuple\nexcept ImportError:\n    pass\n\n\ndef parse_workspace_edit(workspace_edit: 'Dict[str, Any]') -> 'Dict[str, List[TextEdit]]':\n    changes = {}  # type: Dict[str, List[TextEdit]]\n    if 'changes' in workspace_edit:\n        for uri, file_changes in workspace_edit.get('changes', {}).items():\n            changes[uri_to_filename(uri)] = list(parse_text_edit(change) for change in file_changes)\n    if 'documentChanges' in workspace_edit:\n        for document_change in workspace_edit.get('documentChanges', []):\n            uri = document_change.get('textDocument').get('uri')\n            changes[uri_to_filename(uri)] = list(parse_text_edit(change) for change in document_change.get('edits'))\n    return changes\n\n\ndef parse_range(range: 'Dict[str, int]') -> 'Tuple[int, int]':\n    return range['line'], range['character']\n\n\ndef parse_text_edit(text_edit: 'Dict[str, Any]') -> 'TextEdit':\n    return (\n        parse_range(text_edit['range']['start']),\n        parse_range(text_edit['range']['end']),\n        text_edit.get('newText', '')\n    )\n\n\ndef sort_by_application_order(changes: 'Iterable[TextEdit]') -> 'List[TextEdit]':\n\n    def get_start_position(pair: 'Tuple[int, TextEdit]'):\n        index, change = pair\n        return change[0][0], change[0][1], index\n\n    # The spec reads:\n    # > However, it is possible that multiple edits have the same start position: multiple\n    # > inserts, or any number of inserts followed by a single remove or replace edit. If\n    # > multiple inserts have the same position, the order in the array defines the order in\n    # > which the inserted strings appear in the resulting text.\n    # So we sort by start position. But if multiple text edits start at the same position,\n    # we use the index in the array as the key.\n\n    return list(map(lambda pair: pair[1], sorted(enumerate(changes), key=get_start_position, reverse=True)))\n", "path": "plugin/core/edit.py"}], "after_files": [{"content": "import sublime\nimport sublime_plugin\nfrom .core.edit import sort_by_application_order\ntry:\n    from typing import List, Dict, Optional, Any, Iterable, Tuple\n    from .core.edit import TextEdit\n    assert List and Dict and Optional and Any and Iterable and Tuple and TextEdit\nexcept ImportError:\n    pass\nfrom .core.logging import debug\n\n\nclass LspApplyWorkspaceEditCommand(sublime_plugin.WindowCommand):\n    def run(self, changes: 'Optional[Dict[str, List[TextEdit]]]' = None):\n        documents_changed = 0\n        if changes:\n            for path, document_changes in changes.items():\n                self.open_and_apply_edits(path, document_changes)\n                documents_changed += 1\n\n        if documents_changed > 0:\n            message = 'Applied changes to {} documents'.format(documents_changed)\n            self.window.status_message(message)\n        else:\n            self.window.status_message('No changes to apply to workspace')\n\n    def open_and_apply_edits(self, path, file_changes):\n        view = self.window.open_file(path)\n        if view:\n            if view.is_loading():\n                # TODO: wait for event instead.\n                sublime.set_timeout_async(\n                    lambda: view.run_command('lsp_apply_document_edit', {'changes': file_changes}),\n                    500\n                )\n            else:\n                view.run_command('lsp_apply_document_edit',\n                                 {'changes': file_changes})\n        else:\n            debug('view not found to apply', path, file_changes)\n\n\nclass LspApplyDocumentEditCommand(sublime_plugin.TextCommand):\n\n    def run(self, edit, changes: 'Optional[List[TextEdit]]' = None):\n        # Apply the changes in reverse, so that we don't invalidate the range\n        # of any change that we haven't applied yet.\n        if changes:\n            last_row, last_col = self.view.rowcol(self.view.size())\n            for change in reversed(sort_by_application_order(changes)):\n                start, end, newText = change\n                region = sublime.Region(self.view.text_point(*start), self.view.text_point(*end))\n\n                if start[0] > last_row and newText[0] != '\\n':\n                    # Handle when a language server (eg gopls) inserts at a row beyond the document\n                    # some editors create the line automatically, sublime needs to have the newline prepended.\n                    debug('adding new line for edit at line {}, document ended at line {}'.format(start[0], last_row))\n                    self.apply_change(region, '\\n' + newText, edit)\n                    last_row, last_col = self.view.rowcol(self.view.size())\n                else:\n                    self.apply_change(region, newText, edit)\n\n    def apply_change(self, region: 'sublime.Region', newText: str, edit):\n        if region.empty():\n            self.view.insert(edit, region.a, newText)\n        else:\n            if len(newText) > 0:\n                self.view.replace(edit, region, newText)\n            else:\n                self.view.erase(edit, region)\n", "path": "plugin/edit.py"}, {"content": "from .url import uri_to_filename\nimport operator\ntry:\n    from typing import List, Dict, Optional, Any, Iterable, Tuple\n    TextEdit = Tuple[Tuple[int, int], Tuple[int, int], str]\n    assert List and Dict and Optional and Any and Iterable and Tuple\nexcept ImportError:\n    pass\n\n\ndef parse_workspace_edit(workspace_edit: 'Dict[str, Any]') -> 'Dict[str, List[TextEdit]]':\n    changes = {}  # type: Dict[str, List[TextEdit]]\n    if 'changes' in workspace_edit:\n        for uri, file_changes in workspace_edit.get('changes', {}).items():\n            changes[uri_to_filename(uri)] = list(parse_text_edit(change) for change in file_changes)\n    if 'documentChanges' in workspace_edit:\n        for document_change in workspace_edit.get('documentChanges', []):\n            uri = document_change.get('textDocument').get('uri')\n            changes[uri_to_filename(uri)] = list(parse_text_edit(change) for change in document_change.get('edits'))\n    return changes\n\n\ndef parse_range(range: 'Dict[str, int]') -> 'Tuple[int, int]':\n    return range['line'], range['character']\n\n\ndef parse_text_edit(text_edit: 'Dict[str, Any]') -> 'TextEdit':\n    return (\n        parse_range(text_edit['range']['start']),\n        parse_range(text_edit['range']['end']),\n        text_edit.get('newText', '')\n    )\n\n\ndef sort_by_application_order(changes: 'Iterable[TextEdit]') -> 'List[TextEdit]':\n    # The spec reads:\n    # > However, it is possible that multiple edits have the same start position: multiple\n    # > inserts, or any number of inserts followed by a single remove or replace edit. If\n    # > multiple inserts have the same position, the order in the array defines the order in\n    # > which the inserted strings appear in the resulting text.\n    # So we sort by start position. But if multiple text edits start at the same position,\n    # we use the index in the array as the key.\n\n    return list(sorted(changes, key=operator.itemgetter(0)))\n", "path": "plugin/core/edit.py"}]}