prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <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!
  
  </issue>
  <code>
  [start of plugin/edit.py]
  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
  [end of plugin/edit.py]
  [start of plugin/core/edit.py]
  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 of plugin/core/edit.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: 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():
  
verification_info: {"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"}]}