diff --git a/README.md b/README.md
index bcc3411..d6e3556 100644
--- a/README.md
+++ b/README.md
@@ -135,6 +135,17 @@ To customize these, hit shift+cmd+p to open the Command Palette, and
- **delimiters**
- characters at which auto-expansion of selected path stops, e.g. `` \t\n\r\"'`,*<>[](){}``
- the default settings are Markdown friendly
+- **delimiters_scoped**
+ - Same as delimiters, but for a specific scope, uses Sublime's [score_selector API](https://www.sublimetext.com/docs/api_reference.html#sublime.score_selector) to test whether a given key matches the scope of the current selection. The scope with the maximum match score wins.
+ - A list of `scope`/`scope_match_threshold`/`delimiters` dictionaries.
+ - **scope_match_threshold** Minimum score to consider a comparison a match (0 no match; >0 match, higher is better), e.g., for `text.html.markdown markup.list.unnumbered.markdown meta.paragraph.list.markdown`
+ - 0 `paragraph`
+ - 2 `text` `text.`
+ - 4 `text.html`
+ - 6 `text.html.markdown`
+ - 256 `meta.paragraph`
+- **scope_stop**
+ - A list of scopes to stop path expansion at, e.g., a valid path symbol `//` when it's scoped as a `punctuation.definition.comment`
- **trailing_delimiters**
- if any of these characters are seen at the end of a URL, they are recursively removed; for file and folder paths, URLs **with and without** trailing delimiters are tried; default is `;.:`
- **web_browser**
@@ -145,9 +156,13 @@ To customize these, hit shift+cmd+p to open the Command Palette, and
- the path to your web browser executable for opening web URLs
- this setting overrides the default web browser and the **web_browser** setting
- [read the top answer here](https://stackoverflow.com/questions/22445217/python-webbrowser-open-to-open-chrome-browser), or look in settings for examples
+- **enable_web_search**
+ - if selection is not recognized as a file/folder/URL, pass it to web searches
- **web_searchers**
- if your selection isn't a file, a folder, or a URL, you can choose to pass it to a web searcher, which is just a URL that searches for the selected text
- example: `{ "label": "google search", "url": "http://google.com/search?q=", "encoding": "utf-8" }`
+- **live_edit**
+ - if selection is not recognized as a file/folder/url, show a panel to edit it
- **aliases**
- first transform applied to URL, a dict with keys and values; replace each **key** in URL with corresponding **value**
- example: `{ "{{BASE_PATH}}": "src/base" }`
@@ -160,9 +175,13 @@ To customize these, hit shift+cmd+p to open the Command Palette, and
- **file_suffixes**
- path transform; adds these suffixes to filename only
- example: `[".js", ".ts", ".tsx"]`
+- **enable_file_commands**
+ - allows disabling file path handling, e.g., if you only want to open web links
- **file_custom_commands**
- pass a file to shell commands whose pattern matches the file path
- example, for copying the file path to the clipboard: `{ "label": "copy path", "commands": "printf '$url' | pbcopy" }`
+- **enable_folder_commands**
+ - allows disabling folder path handling, e.g., if you only want to open web links
- **folder_custom_commands**
- pass a folder to shell commands whose pattern matches the folder path
- example, for opening the folder in iTerm: `{ "label": "open in terminal", "commands": [ "open", "-a", "/Applications/iTerm.app" ] }`
diff --git a/open_url.py b/open_url.py
index 7dd990b..e8be4f3 100644
--- a/open_url.py
+++ b/open_url.py
@@ -17,15 +17,21 @@
"Settings",
{
"delimiters": str,
+ "delimiters_scoped": list,
+ "scope_stop": list,
"trailing_delimiters": str,
"web_browser": str,
"web_browser_path": list,
+ "enable_web_search": bool,
"web_searchers": list,
+ "live_edit": list,
"file_prefixes": list,
"file_suffixes": list,
"search_paths": list,
"aliases": dict,
+ "enable_file_commands": bool,
"file_custom_commands": list,
+ "enable_folder_commands": bool,
"folder_custom_commands": list,
"other_custom_commands": list,
},
@@ -34,15 +40,21 @@
# these are necessary to convert settings object to a dict, which can then be merged with project settings
settings_keys = [
"delimiters",
+ "delimiters_scoped",
+ "scope_stop",
"trailing_delimiters",
"web_browser",
"web_browser_path",
+ "enable_web_search",
"web_searchers",
+ "live_edit",
"file_prefixes",
"file_suffixes",
"search_paths",
"aliases",
+ "enable_file_commands",
"file_custom_commands",
+ "enable_folder_commands",
"folder_custom_commands",
"other_custom_commands",
]
@@ -196,6 +208,7 @@ def get_selection(self, region) -> str:
"""Returns selection. If selection contains no characters, expands it
until hitting delimiter chars.
"""
+ view = self.view
start: int = region.begin()
end: int = region.end()
@@ -206,17 +219,40 @@ def get_selection(self, region) -> str:
# nothing is selected, so expand selection to nearest delimiters
view_size: int = self.view.size()
delimiters = list(self.config["delimiters"])
+ scope_delim = list(self.config["delimiters_scoped"])
+ scope_stop = list(self.config["scope_stop"])
+ txt_pt = region.a # use first point for scope matching
+ txt_scope = view.scope_name(txt_pt) #e.g., "source.python meta.function…"
+ match_max = 0
+ for scope_i in scope_delim:
+ scope = scope_i['scope']
+ match_min = scope_i['min' ]
+ delim = scope_i['delim']
+ if (score := sublime.score_selector(txt_scope, scope)) >= match_min:
+ if score > match_max:
+ delimiters = delim
+ match_max = score
# move the selection back to the start of the url
while start > 0:
if self.view.substr(start - 1) in delimiters:
break
+ if scope_stop:
+ txt_scope = view.scope_name(start - 1)
+ for scope_i in scope_stop:
+ if scope_i in txt_scope:
+ break
start -= 1
# move end of selection forward to the end of the url
while end < view_size:
if self.view.substr(end) in delimiters:
break
+ if scope_stop:
+ txt_scope = view.scope_name(end)
+ for scope_i in scope_stop:
+ if scope_i in txt_scope:
+ break
end += 1
sel = self.view.substr(sublime.Region(start, end))
return sel.strip()
@@ -311,10 +347,19 @@ def modify_or_search_action(self, term: str):
"""Not a URL and not a local path; prompts user to modify path and looks
for it again, or searches for this term using a web searcher.
"""
- searchers = self.config["web_searchers"]
- opts = [f"modify path {term}"]
- opts += [f'{s["label"]} ({term})' for s in searchers]
- sublime.active_window().show_quick_panel(opts, lambda idx: self.modify_or_search_done(idx, searchers, term))
+ is_edit = self.config["live_edit"]
+ is_web = self.config["enable_web_search"]
+ if not is_edit and not is_web:
+ return
+
+ opts, searchers = [], []
+ if is_edit:
+ opts += [f"modify path {term}"]
+ if is_web:
+ searchers = self.config["web_searchers"]
+ opts += [f'{s["label"]} ({term})' for s in searchers]
+ if opts:
+ sublime.active_window().show_quick_panel(opts, lambda idx: self.modify_or_search_done(idx, searchers, term))
def modify_or_search_done(self, idx: int, searchers, term: str):
if idx < 0:
@@ -354,6 +399,8 @@ def other_done(self, idx, openers, path):
def folder_action(self, folder: str, show_menu: bool, raw_folder: str):
"""Choose from folder actions."""
+ if not self.config["enable_folder_commands"]:
+ return
openers = match_openers(self.config["folder_custom_commands"], folder)
if openers and not show_menu:
@@ -361,7 +408,10 @@ def folder_action(self, folder: str, show_menu: bool, raw_folder: str):
return
opts = [*[opener.get("label") for opener in openers], "search..."]
- sublime.active_window().show_quick_panel(opts, lambda idx: self.folder_done(idx, openers, folder, raw_folder))
+ sublime.active_window().show_quick_panel(opts, lambda idx: self.folder_done(idx, openers, folder, raw_folder),
+ sublime.QuickPanelFlags.NONE, -1, None, #selected_index on_highlight
+ f"📁 {raw_folder}"
+ )
def folder_done(self, idx: int, openers: list[dict], folder: str, raw_folder: str):
if idx < 0:
@@ -376,6 +426,8 @@ def folder_done(self, idx: int, openers: list[dict], folder: str, raw_folder: st
def file_action(self, path: str, show_menu: bool, raw_path: str) -> None:
"""Edit file or choose from file actions."""
+ if not self.config["enable_file_commands"]:
+ return
openers = match_openers(self.config["file_custom_commands"], path)
if not show_menu:
@@ -385,6 +437,8 @@ def file_action(self, path: str, show_menu: bool, raw_path: str) -> None:
sublime.active_window().show_quick_panel(
["edit", *[opener.get("label") for opener in openers], "search..."],
lambda idx: self.file_done(idx, openers, path, raw_path),
+ sublime.QuickPanelFlags.NONE, -1, None, #selected_index on_highlight
+ f"␜ {raw_path}"
)
def file_done(self, idx: int, openers: list[dict], path: str, raw_path: str):
diff --git a/open_url.sublime-settings b/open_url.sublime-settings
index 6572633..9236f3a 100644
--- a/open_url.sublime-settings
+++ b/open_url.sublime-settings
@@ -4,6 +4,16 @@
// delimiters for expanding selection, these are markdown friendly
"delimiters": " \t\n\r\"'`,*<>[](){}",
+ "delimiters_scoped":[ // same as delimiters, but for a specific scope, uses Sublime's score_selector API to test whether a given key matches the scope of the current selection (https://www.sublimetext.com/docs/api_reference.html#sublime.score_selector)
+ {"scope":"text.html.markdown", "min":2, "delim":" \t\n\r\"'`,*<>[](){}"},
+ // scope_match_threshold:↑ min score to consider a comparison a match (0 no match; >0 match, higher is better, see ReadMe for examples)
+ ],
+ "scope_stop": [ // stop expanding selection at these scopes
+ "punctuation.definition.comment",
+ "punctuation.definition.string.begin","punctuation.definition.string.end",
+ "punctuation.section.sequence.begin" ,"punctuation.section.sequence.end",
+ ],
+
// if these delimiting characters are seen at the end of the URL, they are removed, e.g. sublimetext.com.; becomes sublimetext.com
"trailing_delimiters": ";.:",
@@ -20,6 +30,9 @@
// example: chrome, linux
// "web_browser_path": "/usr/bin/google-chrome %s"
+ // Enable fallback to web searches
+ "enable_web_search": true,
+
// if URL is neither a local file nor a web URL, pass it to a web searcher
"web_searchers": [
{
@@ -29,6 +42,8 @@
}
],
+ "live_edit": true, //if selection is not recognized as a file/folder/url, show a panel to edit it
+
// path transforms
"aliases": {},
@@ -38,6 +53,9 @@
"file_suffixes": [".js"],
+ // Enable file path handling
+ "enable_file_commands": true,
+
// pass file that matches regex "pattern" to shell commands
"file_custom_commands": [
{ "label": "reveal", "os": "osx", "commands": ["open", "-R"] },
@@ -63,6 +81,9 @@
{ "label": "open with default application", "os": "linux", "commands": [] }
],
+ // Enable folder path handling
+ "enable_folder_commands": true,
+
// pass folder that matches regex "pattern" to shell commands
"folder_custom_commands": [
{ "label": "reveal", "os": "osx", "commands": ["open"] },