Dispatch events for when a view is scrolled, so we can for example scroll other unfocused views in relation to the current focused view. The current ugly hack is to loop forever and see if the scroll changed
API Suggestions
This gonna be hard to explain and Im sorry, We need “light” versions of on_selection_modified and on_modified .
Because these two apis are too verbose and everyone use it wrong, Then later people complain ST lags, when they triggering heavy tasks on every key press or cursor move. Some people even believe they use it right because they move to threads, and actually this is worse, there’s no need to dispatch 30 gonna-be-useless threads because you hold a key to type or to move the cursor for a few seconds.
The current workaround for this is insane, you loop in a thread with while(True) and sleep, then try to check if the view/selection changed and also if it is worth to wait a bit more
I suggest something in the lines of ;
- on_after_selection_modified - event fires when the cursor stops moving for some given milliseconds
- on_after_modified - event fires when the user stops modifying the view for some given milliseconds
Interface Suggestions
This is a pretty curated list, and Im sure you will found some worth post, specially all of these related to bugs, ST API works most of the time but has some bugs that are really frustrating and defeats the purpose of having an api in the first place, because you need workarounds that does not couple correctly or does decouple with other functionality and everything becomes a workaround of a workaround to make things work.
This is the most important stuff. Current API should work, flawless, if does not because of some edge case, then explain it
Some APIs conflict with ST itself, for example, you have an API to scroll the view to a certain position, but you don’t have any way to stop ST Core from scrolling a view. So you start fighting and racing with ST to see which one will scroll a view. Defeats the purpose of the API because you cannot use it, sublime will simply overwrite it as it wish.
For an API to work correctly, every default behaviour of the editor should be configurable. There are many, many examples of this. For example imagine you want to run a task to all the file selected on sidebar, and you need to do this repetitively, well if you change of view, your selection on sidebar vanishes, hence an API to get the current selection of files gonna be useless for a lot of cases. Want another example? on_close is not dispatched if you switch projects
This is more a dream than anything else, it would be nice to have “linked regions”, regions that are foreign to the current document, this will allow to change for example more than one file from the same view.
Something like this, I did on package FindResultsApplyChanges https://github.com/titoBouzout/FindResultsApplyChanges
Basically you can edit the view of the find results directly and then press CTRL+S to save the changes you did to the different files
The possibilities of this are out of my imagination
Possibly an API to expose the fussy match logic that Sublime has implemented such that we could apply the matching algorithm to a list of strings or list of objects.
Example
sublime.fuzzy_match(list, match_string, key)
- list: list to apply the matching against
- match_string: used to match against
- key: A key to be used if supplying a list of objects or tuples.
Not sure if anyone would find this helpful or not, but there have been some cases where I want to apply the fuzzy matching against a list of items without using one of the built in panels.
Add to this settings too:
"minimap": true,
"status_bar": true,
"tabs": true
Custom Key Bindings -- syntax and conventions for sorting lines
Placeholder for fold
Importance: Minor
Description: Add an optional placeholder argument to the fold function, i.e. change the signature to fold(regions, <placeholder>)
and fold([regions], <placeholder>)
. If the function is executed the placeholder text is inside the yellow rectangle instead of the 3 dots.
In addition add a function is_folded(point)
, which returns whether a point is currently folded.
Motivation: This could be used to fold redundant and unnecessary code away to improve readability while keeping the key information. E.g. fold all function
keywords in JavaScript to f
or python lambda:
to λ
. In LaTeX it could be used to fold labels to l
and greek characters and some operators to their unicode character, e.g. \alpha
to α
.
Looking for an orgmode like link presentation
Directly accept colors in add_regions
Importance: Major
Description: Change the add_regions
method to accept rgb encoded colors. The function add_regions(key, [regions], <scope>, <icon>, <flags>)
currently only accepts a scope and retrieves the color from the colorscheme. It would be nice, if it was possible to pass rbg encoded colors as an argument. This could either be done by changing the signature to accept an additional keyword argument <color>
or by adding a flag sublime.SCOPE_IS_COLOR
to treat the scope as a color.
Motivation: When creating a highlighted region one often just wants to define the color itself instead of a scope with a similar color.
At the moment some packages (e.g. SublimeLinter and ColorHighlighter) even change the colorscheme of the user to inject their colors. This has some disadvantages for the user and does not scale as a general solution.
Interface Suggestions
Query contexts
A way to query a view for certain contexts. Much like a keybinding does, but within a plugin, to make the code easier to follow without reinventing the wheel. i.e. it would execute any on_query_context
EventListener
s behind the scenes.
view.query_context(key, operator, operand, match_all)
(maybe also include a region
or point
?)
operator is one of:
-
sublime.OP_EQUAL
. Is the value of the context equal to the operand? -
sublime.OP_NOT_EQUAL
. Is the value of the context not equal to the operand? -
sublime.OP_REGEX_MATCH
. Does the value of the context match the regex given in operand? -
sublime.OP_NOT_REGEX_MATCH
. Does the value of the context not match the regex given in operand? -
sublime.OP_REGEX_CONTAINS
. Does the value of the context contain a substring matching the regex given in operand? -
sublime.OP_NOT_REGEX_CONTAINS
. Does the value of the context not contain a substring matching the regex given in operand?
match_all
should be used if the context relates to the selections: does every selection have to match (match_all = True
), or is at least one matching enough (match_all = False
)?
Importance: Major
One potential advantage of this would be the ability to use PCRE flavor RegEx expressions instead of Python ones from within a plugin, which support a few extra features (like recursion).
Interface Suggestions
[test]: simulate key press
Programmatic way to query context
Importance: Somewhat
Description: A hook for when text is about to be self-inserted, and a hook for when a key is pressed for which there is no key binding.
Motivation: There are no hooks for telling when text has been inserted. Therefore a number of things are not possible, including “inserting the same character N times” ala Emacs.
Having a hook called when there’s no binding for a key would allow us to implement ^Q which would insert the character that is normally bound to another command.
Interface Suggestions
Importance: Medium
Description: Make it possible to close a view on a modified file without requiring confirmation iff that same file is displayed in at least one other view.
Today if you split the window and and view the same file, and you want to close the window without saving, you are bothered with the confirmation even though there’s another view still open on the file.
Test coverage reports for sublime syntax files
Mentioned by @FichteFoll here:
Interface Suggestions
Add an EventListener callback that is run whenever a project changes or is opened.
Scale: Minor
class MyProjectChangeListener extends sublime_plugin.EventListener:
def on_project_changed(self, window, new_project_data):
""" Called when the current project data changes. """
...
sublime.open_project('/path/to/.sublime-project')
sublime.open_folder('/path/to/folder')
sublime.active_window().add_folder('/path/to/folder')
The above api removes boilerplate such as:
gerardroche/sublime-open-sesame
class Window():
def open_project_in_new(self, sublime_project_file):
"""
Open a project in a new window
"""
if not sublime_project_file:
return
if not os.path.isfile(sublime_project_file):
return
if not re.match('^.+\.sublime-project$', sublime_project_file):
return
sublime.set_timeout_async(lambda: subl(['--new-window', '--project', sublime_project_file]))
def open_folder_in_new(self, folder):
"""
Open a folder in a new window
"""
if not folder:
return
if not os.path.isdir(folder):
return
sublime.set_timeout_async(lambda: subl(['--new-window', folder]))
def add_folder_to_current(self, folder):
"""
Add a folder to the current window
"""
window = sublime.active_window()
if not window:
return
if not folder:
return
if not os.path.isdir(folder):
return
project_data = window.project_data() if window.project_data() else {'folders': []}
# normalise folder
# @todo folder should be normalised to be relative paths to project file
folder = os.path.normpath(folder)
project_file_name = window.project_file_name()
if project_file_name:
project_file_dir = os.path.dirname(project_file_name)
if project_file_dir == folder:
folder = '.'
# check if it already exists
for f in project_data['folders']:
if f['path'] and folder == f['path']:
return # already exists
folder_struct = {
'path': folder
}
if folder != '.':
folder_struct['follow_symlinks'] = True
project_data['folders'].append(folder_struct)
window.set_project_data(project_data)
def subl(args=[]):
# credit: randy3k/Project-Manager
executable_path = sublime.executable_path()
if sublime.platform() == 'osx':
app_path = executable_path[:executable_path.rfind('.app/') + 5]
executable_path = app_path + 'Contents/SharedSupport/bin/subl'
subprocess.Popen([executable_path] + args)
Interface Suggestions
Keymap commands
class KeymapCommand(Command):
def is_context(self):
return False
def run(self):
pass
Usage
*.sublime-keymap
{
"keys": ["tab"],
"command": "command_name"
}
class CommandName(KeymapCommand):
def is_context(self):
# ...
def run(self):
# ...
Motivation
Removes plugin boilerplate, duplication and allows plugins to circumvents possible performance issues.
For example, one way to currently achieve something similar to this api, but impacts performace and introduces duplication and more complicated code, is to use an event listener.
class FinishCompletionContext(sublime_plugin.EventListener):
def on_query_context(self, view, key, operator, operand, match_all):
if key == 'finish_completion':
if is_finish_completion_context(view):
return True
return None
The above can then be used as keymap context.
{
"keys": [")"],
"command": "finish_completion",
"context": [
{ "key": "finish_completion", "operator": "equal", "operand": true, "match_all": true }
]
}
The event listener in this case only needs to be fired for the )
keymap.
Interface Suggestions
Builtin logging
The following api:
sublime.logger('{{PLUGIN_NAME}}').emergency(message)
sublime.logger('{{PLUGIN_NAME}}').alert(message)
sublime.logger('{{PLUGIN_NAME}}').critical(message)
sublime.logger('{{PLUGIN_NAME}}').error(message)
sublime.logger('{{PLUGIN_NAME}}').warning(message)
sublime.logger('{{PLUGIN_NAME}}').notice(message)
sublime.logger('{{PLUGIN_NAME}}').info(message)
sublime.logger('{{PLUGIN_NAME}}').debug(message)
sublime.logger('{{PLUGIN_NAME}}').log($level, message)
`{{PLUGIN_NAME}}` is optional. Default to global logger.
Logging is enabled/disabled as follows:
`sublime.active_window().active_view().settings().get('log_level')`
`sublime.active_window().active_view().settings().get('{{PLUGIN_NAME}}.log_level')`
Default log_level
is info
To disable logger set to null
.
Motivation
Removes a lot of plugin boilerplate.
Typically I use the following boilerplate in a lot of plugins:
DEBUG_MODE=bool(os.getenv('SUBLIME_{{PLUGIN_NAME}}_DEBUG'))
if DEBUG_MODE:
def debug_message(message):
"""
Prints a debug level message.
"""
print('[{{PLUGIN_NAME}}] %s' % str(message))
else:
def debug_message(message):
pass
Vintageous writes its own plugin logger. One problem with that is that it puts log files in Packages/.log/*.log
. Ideally they would be put somewhere more appropriate.
2 posts were split to a new topic: API Suggestion Discussion: Builtin logging