Skip to content

Commit

Permalink
Merge branch 'main' into feat/diag-annotations
Browse files Browse the repository at this point in the history
* main:
  Parse position or selection from link fragment (#2049)
  • Loading branch information
rchl committed Sep 12, 2022
2 parents ea8186e + 5926089 commit e923ed5
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 30 deletions.
55 changes: 55 additions & 0 deletions plugin/core/open.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,62 @@
from .protocol import DocumentUri
from .protocol import Range
from .protocol import RangeLsp
from .protocol import UINT_MAX
from .typing import Dict, Tuple, Optional
from .typing import cast
from .url import parse_uri
from .views import range_to_region
from urllib.parse import unquote, urlparse
import os
import re
import sublime
import subprocess
import webbrowser


opening_files = {} # type: Dict[str, Tuple[Promise[Optional[sublime.View]], ResolveFunc[Optional[sublime.View]]]]
FRAGMENT_PATTERN = re.compile(r'^L?(\d+)(?:,(\d+))?(?:-L?(\d+)(?:,(\d+))?)?')


def open_file_uri(
window: sublime.Window, uri: DocumentUri, flags: int = 0, group: int = -1
) -> Promise[Optional[sublime.View]]:

def parse_fragment(fragment: str) -> Optional[RangeLsp]:
match = FRAGMENT_PATTERN.match(fragment)
if match:
selection = {'start': {'line': 0, 'character': 0}, 'end': {'line': 0, 'character': 0}} # type: RangeLsp
# Line and column numbers in the fragment are assumed to be 1-based and need to be converted to 0-based
# numbers for the LSP Position structure.
start_line, start_column, end_line, end_column = [max(0, int(g) - 1) if g else None for g in match.groups()]
if start_line:
selection['start']['line'] = start_line
selection['end']['line'] = start_line
if start_column:
selection['start']['character'] = start_column
selection['end']['character'] = start_column
if end_line:
selection['end']['line'] = end_line
selection['end']['character'] = UINT_MAX
if end_column is not None:
selection['end']['character'] = end_column
return selection
return None

decoded_uri = unquote(uri) # decode percent-encoded characters
parsed = urlparse(decoded_uri)
open_promise = open_file(window, decoded_uri, flags, group)
if parsed.fragment:
selection = parse_fragment(parsed.fragment)
if selection:
return open_promise.then(lambda view: _select_and_center(view, cast(RangeLsp, selection)))
return open_promise


def _select_and_center(view: Optional[sublime.View], r: RangeLsp) -> Optional[sublime.View]:
if view:
return center_selection(view, r)
return None


def _return_existing_view(flags: int, existing_view_group: int, active_group: int, specified_group: int) -> bool:
Expand Down Expand Up @@ -78,6 +125,14 @@ def center_selection(v: sublime.View, r: RangeLsp) -> sublime.View:
return v


def open_in_browser(uri: str) -> None:
# NOTE: Remove this check when on py3.8.
if not uri.lower().startswith(("http://", "https://")):
uri = "https://" + uri
if not webbrowser.open(uri):
sublime.status_message("failed to open: " + uri)


def open_externally(uri: str, take_focus: bool) -> bool:
"""
A blocking function that invokes the OS's "open with default extension"
Expand Down
1 change: 1 addition & 0 deletions plugin/core/url.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def parse_uri(uri: str) -> Tuple[str, str]:
if os.name == 'nt':
netloc = url2pathname(parsed.netloc)
path = path.lstrip("\\")
path = re.sub(r"^/([a-zA-Z]:)", r"\1", path) # remove slash preceding drive letter
path = re.sub(r"^([a-z]):", _uppercase_driveletter, path)
if netloc:
# Convert to UNC path
Expand Down
18 changes: 4 additions & 14 deletions plugin/document_link.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from .core.logging import debug
from .core.open import open_file_uri
from .core.open import open_in_browser
from .core.protocol import DocumentLink, Request
from .core.registry import get_position
from .core.registry import LspTextCommand
from .core.typing import Optional
from urllib.parse import unquote, urlparse
import re
import sublime
import webbrowser


class LspOpenLinkCommand(LspTextCommand):
Expand Down Expand Up @@ -57,15 +56,6 @@ def open_target(self, target: str) -> None:
if target.startswith("file:"):
window = self.view.window()
if window:
decoded = unquote(target) # decode percent-encoded characters
parsed = urlparse(decoded)
filepath = parsed.path
if sublime.platform() == "windows":
filepath = re.sub(r"^/([a-zA-Z]:)", r"\1", filepath) # remove slash preceding drive letter
fn = "{}:{}".format(filepath, parsed.fragment) if parsed.fragment else filepath
window.open_file(fn, flags=sublime.ENCODED_POSITION)
open_file_uri(window, target)
else:
if not (target.lower().startswith("http://") or target.lower().startswith("https://")):
target = "http://" + target
if not webbrowser.open(target):
sublime.status_message("failed to open: " + target)
open_in_browser(target)
20 changes: 4 additions & 16 deletions plugin/hover.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .code_actions import actions_manager
from .code_actions import CodeActionOrCommand
from .core.logging import debug
from .core.open import open_file_uri
from .core.open import open_in_browser
from .core.promise import Promise
from .core.protocol import Diagnostic
from .core.protocol import DocumentLink
Expand Down Expand Up @@ -37,12 +38,9 @@
from .core.views import unpack_href_location
from .core.views import update_lsp_popup
from .session_view import HOVER_HIGHLIGHT_KEY
from urllib.parse import unquote, urlparse
import functools
import html
import re
import sublime
import webbrowser


SUBLIME_WORD_MASK = 515
Expand Down Expand Up @@ -330,13 +328,7 @@ def _on_navigate(self, href: str, point: int) -> None:
elif href.startswith("file:"):
window = self.view.window()
if window:
decoded = unquote(href) # decode percent-encoded characters
parsed = urlparse(decoded)
filepath = parsed.path
if sublime.platform() == "windows":
filepath = re.sub(r"^/([a-zA-Z]:)", r"\1", filepath) # remove slash preceding drive letter
fn = "{}:{}".format(filepath, parsed.fragment) if parsed.fragment else filepath
window.open_file(fn, flags=sublime.ENCODED_POSITION)
open_file_uri(window, href)
elif href.startswith('code-actions:'):
_, config_name = href.split(":")
actions = self._actions_by_config[config_name]
Expand All @@ -360,11 +352,7 @@ def _on_navigate(self, href: str, point: int) -> None:
r = {"start": position, "end": position} # type: RangeLsp
sublime.set_timeout_async(functools.partial(session.open_uri_async, uri, r))
else:
# NOTE: Remove this check when on py3.8.
if not (href.lower().startswith("http://") or href.lower().startswith("https://")):
href = "http://" + href
if not webbrowser.open(href):
debug("failed to open:", href)
open_in_browser(href)

def handle_code_action_select(self, config_name: str, index: int) -> None:
if index > -1:
Expand Down

0 comments on commit e923ed5

Please sign in to comment.