There’s no default behaviour for something like this in Sublime, unfortunately. Although you can create multiple views into the same file (File > New View into File
or similar in the Command Palette), such a clone view shares the same underlying text buffer
but has it’s own unique view
object wrapping it.
The buffer
represents the file contents so that changes in any cloned view are reflected in all of the clones, but the state of the selection and the current cursor location (which is actually just a selection of length 0) are local to the view.
I attempted to create a plugin that would synchronize the state of the selection between a view and it’s clones, which would allow this to transparently work as in your current work flow, but unfortunately there’s a bug in Sublime that stops this from working as intended.
As a workaround, below is a simple plugin that will do something like this, albeit with a slightly different work flow. To use this, select Tools > Developer > New Plugin...
from the menu and then replace the stub code with the code below, and save it as something like clone_select.py
in the default location that Sublime will propose (your User
package).
import sublime
import sublime_plugin
class ClonePaneSelectionCommand(sublime_plugin.TextCommand):
"""
If the current view has exactly one clone, modify the selection in the
current view so that it starts at the first selection in the other view
and ends at the first selection in this view.
"""
def run(self, edit, update_clone=False):
clone = self.get_clone()
span = sublime.Region(clone.sel()[0].begin(),
self.view.sel()[0].begin())
self.view.sel().clear()
self.view.sel().add(span)
if update_clone:
clone.sel().clear()
clone.sel().add(span)
def get_clone(self):
for window in sublime.windows():
for view in window.views():
if view.buffer_id() == self.view.buffer_id():
if view.id() != self.view.id():
return view
def is_enabled(self, update_clone=False):
return self.view.settings().get("_clones", 0) == 1
class CloneViewListener(sublime_plugin.EventListener):
"""
Maintain a view setting named "_clones" that indicates how many clones of
this view exist. The setting is removed when there are no clones.
"""
def clone_count(self, view, update):
count = view.settings().get("_clones", 0) + update
if update != 0:
view.settings().set("_clones", count)
return count
def on_window_command(self, window, cmd, args):
if cmd == "clone_file":
self.clone_count(window.active_view(), +1)
def on_close(self, view):
if view.settings().has("_clones"):
count = self.clone_count(view, -1)
if count == 0:
view.settings().erase("_clones")
This has two parts that tie together. The CloneViewListener
class is responsible for adding, updating and deleting a custom setting named _clones
that says how many clones of this view there currently are. The setting is added when the first clone is created and removed when the last clone is closed (leaving only a single view).
The command enables itself only when there is exactly one clone of the current view, and will update the selection in the current view so that it spans from the cursor location in the clone to the cursor position in the current view. Optionally it can also update the selection in the other buffer to match the current one.
Note: Arguably the part of this that tracks the number of clones is not really needed and the code could be adapted to not need it, but I’d already written that part before I discovered my initial approach would not work.
With this in place, you can bind the command clone_pane_selection
to some key combination, then your workflow would be similar to the above:
- Go to the first line that you want to select and place the cursor where the selection should start
- Create a second view into the same file
- In the second pane search for the last line of the intended selection and place the cursor where the selection should end
- Press the key combination to update the selection
An example of such a key binding would be the following (update the key as appropriate for your needs):
{
"keys": ["ctrl+alt+s"],
"command": "clone_pane_selection",
"args": {
"update_clone": true
}
},
This sample binding sets the update_clone
argument to the command to true
, which will make it update the other view (the one your cursor is not in) to have the same selection as what gets set in this view. Presumably that’s how it would work in Nedit. If you like you can change that to false
instead, and it will leave the selection in the first view alone.