Repository: sublimelsp/LSP

Issue:
Goto definition in the same file should stay in the same tab
With more than one instance of the same file open, e.g. in 2 columns mode, goto definition always jumps to the first file instance instead of the currently focused file instance.

**To Reproduce**
Steps to reproduce the behavior:
1. Open 2 columns to view the same file
2. Go to the right one, goto definition on something
3. Focus jumps to the left file instance

**Expected behavior**
Focus stays on the right instance

**Environment (please complete the following information):**
- OS: macOS
- Sublime Text version: 4126
- LSP version: 1.16.3
- Language servers used: LSP-typescript

"Find references" opens a pane whose links always jump to the currently focused file instance, so I thought goto definition doing the same would be consistent.


Discussion:

Resolving patch:
diff --git a/plugin/core/open.py b/plugin/core/open.py
--- a/plugin/core/open.py
+++ b/plugin/core/open.py
@@ -28,9 +28,10 @@ def open_file(
     # to open as a separate view).
     view = window.find_open_file(file)
     if view:
-        opens_in_current_group = group == -1 or window.active_group() == group
-        opens_as_new_selection = (flags & (sublime.ADD_TO_SELECTION | sublime.REPLACE_MRU)) != 0
-        return_existing_view = opens_in_current_group and not opens_as_new_selection
+        opens_in_desired_group = not bool(flags & sublime.FORCE_GROUP) or \
+                                 window.get_view_index(view)[0] == window.active_group()
+        opens_in_side_by_side = bool(flags & (sublime.ADD_TO_SELECTION | sublime.REPLACE_MRU))
+        return_existing_view = opens_in_desired_group and not opens_in_side_by_side
         if return_existing_view:
             return Promise.resolve(view)
 
diff --git a/plugin/goto.py b/plugin/goto.py
--- a/plugin/goto.py
+++ b/plugin/goto.py
@@ -22,6 +22,7 @@ def is_enabled(
         event: Optional[dict] = None,
         point: Optional[int] = None,
         side_by_side: bool = False,
+        force_group: bool = True,
         fallback: bool = False
     ) -> bool:
         return fallback or super().is_enabled(event, point)
@@ -32,6 +33,7 @@ def run(
         event: Optional[dict] = None,
         point: Optional[int] = None,
         side_by_side: bool = False,
+        force_group: bool = True,
         fallback: bool = False
     ) -> None:
         session = self.best_session(self.capability)
@@ -40,8 +42,7 @@ def run(
             params = text_document_position_params(self.view, position)
             request = Request(self.method, params, self.view, progress=True)
             session.send_request(
-                request, functools.partial(self._handle_response_async, session, side_by_side, fallback)
-            )
+                request, functools.partial(self._handle_response_async, session, side_by_side, force_group, fallback))
         else:
             self._handle_no_results(fallback, side_by_side)
 
@@ -49,18 +50,19 @@ def _handle_response_async(
         self,
         session: Session,
         side_by_side: bool,
+        force_group: bool,
         fallback: bool,
         response: Union[None, Location, List[Location], List[LocationLink]]
     ) -> None:
         if isinstance(response, dict):
             self.view.run_command("add_jump_record", {"selection": [(r.a, r.b) for r in self.view.sel()]})
-            open_location_async(session, response, side_by_side)
+            open_location_async(session, response, side_by_side, force_group)
         elif isinstance(response, list):
             if len(response) == 0:
                 self._handle_no_results(fallback, side_by_side)
             elif len(response) == 1:
                 self.view.run_command("add_jump_record", {"selection": [(r.a, r.b) for r in self.view.sel()]})
-                open_location_async(session, response[0], side_by_side)
+                open_location_async(session, response[0], side_by_side, force_group)
             else:
                 self.view.run_command("add_jump_record", {"selection": [(r.a, r.b) for r in self.view.sel()]})
                 sublime.set_timeout(functools.partial(LocationPicker, self.view, session, response, side_by_side))
diff --git a/plugin/locationpicker.py b/plugin/locationpicker.py
--- a/plugin/locationpicker.py
+++ b/plugin/locationpicker.py
@@ -11,8 +11,15 @@
 import weakref
 
 
-def open_location_async(session: Session, location: Union[Location, LocationLink], side_by_side: bool) -> None:
+def open_location_async(
+    session: Session,
+    location: Union[Location, LocationLink],
+    side_by_side: bool,
+    force_group: bool
+) -> None:
     flags = sublime.ENCODED_POSITION
+    if force_group:
+        flags |= sublime.FORCE_GROUP
     if side_by_side:
         flags |= sublime.ADD_TO_SELECTION | sublime.SEMI_TRANSIENT
 
@@ -80,7 +87,8 @@ def _select_entry(self, index: int) -> None:
                 if not self._side_by_side:
                     open_basic_file(session, uri, position, flags)
             else:
-                sublime.set_timeout_async(functools.partial(open_location_async, session, location, self._side_by_side))
+                sublime.set_timeout_async(
+                    functools.partial(open_location_async, session, location, self._side_by_side, True))
         else:
             self._window.focus_view(self._view)
             # When in side-by-side mode close the current highlighted


Test patch: