sublimehq / sublime_text

Issue tracker for Sublime Text
https://www.sublimetext.com
807 stars 39 forks source link

view.sel() raises IndexError with when typing at the end of file #3108

Closed deathaxe closed 4 years ago

deathaxe commented 4 years ago

Description

While writing the issue report for #3099 the traceback below was realized while typing text at the end of a python source document. Tried the same with ST3. It started to stuck a bit but didn't throw an exception. EDIT: Happens as well.

The error is reproducable with my setup. It is even throught right after startup, if the cursor is at the eof.

Unfortunatelly I can't provide reliable "steps to reproduce" at this point, because there are numerous packages installed with some of them being ported/opted-in to python 3.8 already. Will be hard to narrow down the culprit.

The issue doesn't appear with a vanilla install so it seems to be related with delays all the plugins might cause, even though they are not obvious.

Steps to reproduce

  1. Open ST4 with quite a number of plugins being installed. (LSP seems to be related)

  2. Create a plugin with the following code

    import sublime_plugin
    
    class PopupListener(sublime_plugin.EventListener):
    
       def on_selection_modified(self, view):
           view.show_popup("Hello World", location=view.sel()[0].begin())
  3. Quickly type text at the end of a python source file with python language server being active via LSP package. Was typing within the plugin's code above without saving it to disc.

Expected behavior

view.sel()[0] should always return a value.

Actual behavior

Traceback (most recent call last):
  File "C:\Apps\Sublime Text 4\Lib\python38\sublime_plugin.py", line 796, in on_selection_modified
    run_view_callbacks('on_selection_modified', view_id)
  File "C:\Apps\Sublime Text 4\Lib\python38\sublime_plugin.py", line 637, in run_view_callbacks
    callback(v, *args)
  File "C:\Apps\Sublime Text 4\Lib\python38\sublime_plugin.py", line 129, in profiler
    return event_handler(*args)
  File "C:\Apps\Sublime Text 4\Data\Packages\User\debug_popup.py", line 8, in on_selection_modified
    pt = view.sel()[0].begin()
  File "C:\Apps\Sublime Text 4\Lib\python38\sublime.py", line 753, in __getitem__
    raise IndexError()
IndexError

Environment

Installed Packages

0_package_control_loader.sublime-package
AlignTab.sublime-package
ApacheConf.sublime-package
Atomic Color Scheme.sublime-package
AutoFoldCode.sublime-package
AutomaticPackageReloader.sublime-package
ayu.sublime-package
Base16 Color Schemes.sublime-package
Behave Color Scheme.sublime-package
BetterTabCycling.sublime-package
Brackets Color Scheme.sublime-package
Chain of Command.sublime-package
ChannelRepositoryTools.sublime-package
CMake.sublime-package
CMakeBuilder.sublime-package
ColorHelper.sublime-package
Column Select.sublime-package
Dockerfile Syntax Highlighting.sublime-package
DoxyDoxygen (evolution).sublime-package
Edge Theme Addon - Linter Theme.sublime-package
EditorConfig.sublime-package
Emmet2.sublime-package
ExpandRegion.sublime-package
ExportHtml.sublime-package
ExternalTools.sublime-package
Folder Aliaser.sublime-package
FuzzyFileNav.sublime-package
GitGutter.sublime-package
GitSavvy.sublime-package
Gutter Themes for SL.sublime-package
Hyperion for gettext.sublime-package
Inc-Dec-Value.sublime-package
Indent XML.sublime-package
INI.sublime-package
Insert Nums.sublime-package
JSCustom.sublime-package
JSONComma.sublime-package
Language - German.sublime-package
LSP.sublime-package
MarkdownEditing.sublime-package
MarkdownPreview.sublime-package
MiniPy.sublime-package
MonokaiFree.sublime-package
More Layouts.sublime-package
MoveTab.sublime-package
MultiEditUtils.sublime-package
nginx.sublime-package
Object Pascal.sublime-package
One Dark Color Scheme.sublime-package
OverrideAudit.sublime-package
PackageResourceViewer.sublime-package
packages.txt
Pandoc.sublime-package
Paste as Column.sublime-package
PostgreSQL Syntax Highlighting.sublime-package
PowerShell.sublime-package
ProjectManager.sublime-package
Rainglow.sublime-package
ReadmePlease.sublime-package
Sass.sublime-package
Sherumite - Color Scheme.sublime-package
SmartMarkdown.sublime-package
Solarized Color Scheme.sublime-package
Sublime Text API Helper.sublime-package
SublimeLinter-contrib-yamllint.sublime-package
SublimeLinter-pycodestyle.sublime-package
SublimeLinter-pylint.sublime-package
SublimeLinter.sublime-package
TabsExtra.sublime-package
Terraform.sublime-package
Theme - Monokai Pro.sublime-package
Theme - SoDaReloaded.sublime-package
Theme - Windows 10.sublime-package
Transpose Word.sublime-package
Trimmer.sublime-package
UnicodeCompletion.sublime-package
UnicodeMath.sublime-package
UnitTesting.sublime-package
Virtualenv.sublime-package
XSL.sublime-package

Loosen Packages

A File Icon
AlignCursors
AltUp
BetterFindBuffer
BetterTabCycling
Brackets Color Scheme
Clock
CNC BoschRexroth MTX
CNC Sinumerik 840D
Column Select
coverage
CSS
dateutil
Default
DTD Snippets
Exalt
FileManager
Folder Aliaser
FuzzyFileNav
Git Formats Plugins
HexBinDec
Inc-Dec-Value
Java
JoinLinesEnhanced
jsonschema
lxml
markupsafe
MaxPane
mdpopups
MoveTab
Package Control
PackageDev
package_setting_context
pathlib
PlainTasks
pygments
pymdownx
pyte
python-jinja2
python-markdown
pywinpty
pyyaml
Regex Replace
ruamel-yaml
SCP
Skins
StringUtilities
sublime_lib
tabulate
Terminus
Theme - DAneo
UnitTesting
User
wcwidth
yaml_macros_engine
zzz A File Icon zzz

Profiled Events

This list shows how much time each plugin has taken to respond to each event:

decorator:
    MaxPane.max_pane: 0.000s total, mean: 0.000s, max: 0.000s

on_activated:
    Clock.clock: 0.000s total, mean: 0.000s, max: 0.000s
    EditorConfig.EditorConfig: 0.019s total, mean: 0.002s, max: 0.013s
    Emmet2.abbreviation: 0.000s total, mean: 0.000s, max: 0.000s
    FuzzyFileNav.fuzzy_file_nav: 0.000s total, mean: 0.000s, max: 0.000s
    GitSavvy.common.global_events: 0.000s total, mean: 0.000s, max: 0.000s
    GitSavvy.core.commands.inline_diff: 0.000s total, mean: 0.000s, max: 0.000s
    GitSavvy.core.commands.log_graph: 0.002s total, mean: 0.000s, max: 0.001s
    GitSavvy.core.commands.status_bar: 0.013s total, mean: 0.002s, max: 0.004s
    PlainTasks.PlainTasks: 0.106s total, mean: 0.007s, max: 0.106s
    PlainTasks.PlainTasksDates: 0.000s total, mean: 0.000s, max: 0.000s

on_deactivated:
    AlignTab.hist: 0.013s total, mean: 0.002s, max: 0.012s
    Default.history_list: 0.001s total, mean: 0.000s, max: 0.001s

on_hover:
    CNC Sinumerik 840D.hmi.events: 0.000s total, mean: 0.000s, max: 0.000s
    Default.symbol: 0.000s total, mean: 0.000s, max: 0.000s
    LSP.plugin.hover: 0.000s total, mean: 0.000s, max: 0.000s
    OverrideAudit.src.events: 0.000s total, mean: 0.000s, max: 0.000s
    Terminus.terminus.mouse: 0.000s total, mean: 0.000s, max: 0.000s

on_modified:
    AlignTab.table: 0.031s total, mean: 0.002s, max: 0.030s
    Default.menubar_visibility: 0.004s total, mean: 0.000s, max: 0.001s
    FuzzyFileNav.fuzzy_file_nav: 0.000s total, mean: 0.000s, max: 0.000s
    LSP.plugin.completion: 0.025s total, mean: 0.005s, max: 0.024s
    LSP.plugin.core.documents: 0.018s total, mean: 0.004s, max: 0.018s
    PackageResourceViewer.package_resource_viewer: 0.008s total, mean: 0.000s, max: 0.006s
    Terminus.terminus.core: 0.001s total, mean: 0.000s, max: 0.001s

on_post_text_command:
    Default.paste_from_history: 0.001s total, mean: 0.000s, max: 0.001s
    Emmet2.abbreviation: 0.000s total, mean: 0.000s, max: 0.000s
    Emmet2.select_item: 0.006s total, mean: 0.000s, max: 0.006s
    PackageDev.plugins_.settings: 0.000s total, mean: 0.000s, max: 0.000s
    Terminus.terminus.clipboard: 0.012s total, mean: 0.001s, max: 0.012s
    Terminus.terminus.core: 0.007s total, mean: 0.000s, max: 0.006s

on_post_window_command:
    GitSavvy.common.global_events: 0.000s total
    GitSavvy.core.commands.log_graph: 0.000s total
    Terminus.terminus.edit_settings: 0.000s total

on_query_completions:
    CSS.css_completions: 0.000s total, mean: 0.000s, max: 0.000s
    DoxyDoxygen (evolution).Doxy: 0.005s total, mean: 0.002s, max: 0.004s
    Emmet2.abbreviation: 0.000s total, mean: 0.000s, max: 0.000s
    HTML.html_completions: 0.000s total, mean: 0.000s, max: 0.000s
    LSP.plugin.completion: 0.001s total, mean: 0.000s, max: 0.001s
    PackageDev.plugins_.command_completions: 0.000s total, mean: 0.000s, max: 0.000s
    PackageDev.plugins_.syntax_dev_legacy: 0.000s total, mean: 0.000s, max: 0.000s
    Sass.sass_completions: 0.000s total, mean: 0.000s, max: 0.000s
    UnicodeMath.unicodecomplete: 0.000s total, mean: 0.000s, max: 0.000s

on_query_context:
    Default.block: 0.000s total, mean: 0.000s, max: 0.000s
    Emmet2.abbreviation: 0.000s total, mean: 0.000s, max: 0.000s
    Emmet2.go_to_tag_pair: 0.000s total, mean: 0.000s, max: 0.000s
    ExpandRegion.ExpandRegion: 0.000s total, mean: 0.000s, max: 0.000s
    FuzzyFileNav.fuzzy_file_nav: 0.000s total, mean: 0.000s, max: 0.000s
    LSP.plugin.signature_help: 0.000s total, mean: 0.000s, max: 0.000s
    MultiEditUtils.selection_fields: 0.000s total, mean: 0.000s, max: 0.000s
    OverrideAudit.src.contexts: 0.000s total, mean: 0.000s, max: 0.000s
    PackageDev.plugins_.syntaxtest_dev: 0.000s total, mean: 0.000s, max: 0.000s
    Terminus.terminus.query: 0.015s total, mean: 0.000s, max: 0.012s
    UnicodeMath.unicodecomplete: 0.000s total, mean: 0.000s, max: 0.000s
    package_setting_context.all.package_setting_context: 0.000s total, mean: 0.000s, max: 0.000s

on_selection_modified:
    GitSavvy.core.commands.commit: 0.007s total, mean: 0.000s, max: 0.006s
    Insert Nums.InsertNums: 0.001s total, mean: 0.000s, max: 0.001s
    MultiEditUtils.MultiEditUtils: 0.012s total, mean: 0.001s, max: 0.012s
    Terminus.terminus.core: 0.000s total, mean: 0.000s, max: 0.000s
    User.debug_popup: 0.129s total, mean: 0.006s, max: 0.011s

on_text_command:
    AlignTab.table: 0.046s total, mean: 0.003s, max: 0.024s
    Default.history_list: 0.002s total, mean: 0.000s, max: 0.001s
    Emmet2.abbreviation: 0.000s total, mean: 0.000s, max: 0.000s
    Emmet2.comment: 0.000s total, mean: 0.000s, max: 0.000s
    FileManager.FileManager: 0.000s total, mean: 0.000s, max: 0.000s
    JSCustom.src.listeners.jsx_close_tag: 0.000s total, mean: 0.000s, max: 0.000s
    LSP.plugin.completion: 0.000s total, mean: 0.000s, max: 0.000s
    Terminus.terminus.core: 0.007s total, mean: 0.000s, max: 0.006s
    Terminus.terminus.mouse: 0.000s total, mean: 0.000s, max: 0.000s

on_window_command:
    Default.history_list: 0.000s total
    MaxPane.max_pane: 0.000s total
    ProjectManager.project_manager: 0.012s total
    Terminus.terminus.core: 0.006s total

wrapper:
    Emmet2.abbreviation: 0.129s total, mean: 0.003s, max: 0.102s
deathaxe commented 4 years ago

Hmm. Seems to be related with the LSP package.

OdatNurd commented 4 years ago

Something like view.sel().clear(); view.sel()[0] in the console will trigger this; so I would guess that a package (LSP?) is adjusting the selection in a way that's interrupting other commands that require the selection?

If that's the case, aren't plugins supposed to be thread safe in a way that something like this shouldn't happen?

deathaxe commented 4 years ago

With your information I searched all and added a print to all positions, LSP runs a sel.clear().

The result is:

PopupListener.on_selection_modified() for view 23
lsp_update_panel.run() for view 37
PopupListener.on_selection_modified() for view 37
Traceback (most recent call last):
  File "C:\Apps\Sublime Text 4\Lib\python38\sublime_plugin.py", line 796, in on_selection_modified
    run_view_callbacks('on_selection_modified', view_id)
  File "C:\Apps\Sublime Text 4\Lib\python38\sublime_plugin.py", line 637, in run_view_callbacks
    callback(v, *args)
  File "C:\Apps\Sublime Text 4\Lib\python38\sublime_plugin.py", line 129, in profiler
    return event_handler(*args)
  File "C:\Apps\Sublime Text 4\Data\Packages\User\debug_popup.py", line 8, in on_selection_modified
    view.show_popup("Hello World", location=view.sel()[0].begin())
  File "C:\Apps\Sublime Text 4\Lib\python38\sublime.py", line 753, in __getitem__
    raise IndexError()
IndexError

caused by the following command:

class LspUpdatePanelCommand(sublime_plugin.TextCommand):
    """
    A update_panel command to update the error panel with new text.
    """

    def run(self, edit, characters):
        # Clear folds
        self.view.unfold(sublime.Region(0, self.view.size()))

        self.view.replace(edit, sublime.Region(0, self.view.size()), characters)
        print("lsp_update_panel.run() for view", self.view.id())
        # Clear the selection
        selection = self.view.sel()
        selection.clear()

The PopupListener is triggered by typing in the normal view (id: 23) and then by the selection.clear() as it changes the selection of the output panel (id: 37).

From this perspective, everything works pretty fine and the IndexError is just a "normal" situation a plugin needs to handle which means: hey boy, someone removed all cursors from the view.

So any plugin which wants to access the selection, within an on_selection_modified handler would need to handle this situation as follows.

import sublime_plugin

class PopupListener(sublime_plugin.EventListener):

    def on_selection_modified(self, view):
        print("PopupListener.on_selection_modified() for view", view.id())
        try:
            view.show_popup("Hello World", location=view.sel()[0].begin())
            # ... 
            # ... some other failing array operation with IndexError
        except IndexError:
            print(" -> No cursor to do something with.")

As a result, I woudn't call it a bug any more but rather fine behavior.

jskinner commented 4 years ago

IMO any plugin leaving the selection in an empty state is a bug. For example, there's no way to get a view into a empty selection state in a vanilla Sublime Text install (and if there is, I'd consider that a bug)

FichteFoll commented 4 years ago

For example, there's no way to get a view into a empty selection state in a vanilla Sublime Text install

There is, with alt+drag.