Repository: sublimelsp/LSP

Issue:
On Windows, the drive letter in server responsed file URIs are lowercase.
**Describe the bug**

I tried both intelephense and pyright, they both returned lowercased drive letter thus I suspect it's a standard. (or maybe VSCode's LSP lib does it)

https://user-images.githubusercontent.com/6594915/123961095-96286c80-d9e2-11eb-8ada-0da9af754a55.mp4

In "Goto Definition...", this causes ST to open a file whose drive letter is in lowercase. And that may cause various mysterious problem sometimes... Or maybe, this should be fixed in ST core.

**To Reproduce**
Steps to reproduce the behavior:
1. Install LSP-intelephense with a Windows build ST
2. Open a PHP project
3. Make sure the definition file is not opened in a tab already
4. Do "Goto Definition"
5. The newly opened tab should have a lower drive letter

**Expected behavior**

The drive letter should be uppercase.

**Environment (please complete the following information):**
- OS: Win10 21H1 x64
- Sublime Text version: 4109
- LSP version: 4070-1.6.1
- Language servers used: intelephense, pyright

**Additional context**

This is a Windows-only issue as it's case-insensitive.



Discussion:
> The drive letter should be uppercase.

Why?
> > The drive letter should be uppercase.
> 
> Why?

Because (I think) it's Windows' default, unless the user somehow uses lowercase intentionally. Or LSP is able to tell which case is actually used.
There is another problem with the lowercase drive letters when opening a file: even though Sublime highlights the correct file in the sidebar (if it is a file from an opened folder), it doesn't it doesn't show the git status in the status bar for files with lowercase drive letter (if it is part of a git repo). So we should normalize the drive letter to uppercase.
Another thing that doesn't work as intended is the quick panel for "Find References" if *"show_references_in_quick_panel"* is `true` in LSP settings. It shows the full path, but without drive letter (i.e. starting with `:`). That seems to happen because `os.path.commonprefix((pathname, base_dir))` called in `location_to_human_readable` is an empty string (base_dir has uppercase drive letter and pathname has lowercase drive letter). I already have a fix to normalize to uppercase drive letter in `uri_to_filename` in core/url.py (that function is marked as deprecated though).

And I think I've found another bug (?) in https://github.com/sublimelsp/LSP/blob/ef40138111ad47b492139134f66cc7e96b95224e/plugin/core/url.py#L24
this line does nothing because the return value is not assigned to a variable (strings are immutable in Python afaik).

Resolving patch:
diff --git a/plugin/core/url.py b/plugin/core/url.py
--- a/plugin/core/url.py
+++ b/plugin/core/url.py
@@ -21,7 +21,6 @@ def filename_to_uri(file_name: str) -> str:
     if file_name.startswith(prefix) and not os.path.exists(file_name):
         return _to_resource_uri(file_name, prefix)
     path = pathname2url(file_name)
-    re.sub(r"^([A-Z]):/", _lowercase_driveletter, path)
     return urljoin("file:", path)
 
 
@@ -42,7 +41,8 @@ def uri_to_filename(uri: str) -> str:
     assert parsed.scheme == "file"
     if os.name == 'nt':
         # url2pathname does not understand %3A (VS Code's encoding forced on all servers :/)
-        return url2pathname(parsed.path).strip('\\')
+        path = url2pathname(parsed.path).strip('\\')
+        return re.sub(r"^([a-z]):", _uppercase_driveletter, path)
     else:
         return url2pathname(parsed.path)
 
@@ -71,8 +71,8 @@ def _to_resource_uri(path: str, prefix: str) -> str:
     return "res://Packages{}".format(quote(path[len(prefix):]))
 
 
-def _lowercase_driveletter(match: Any) -> str:
+def _uppercase_driveletter(match: Any) -> str:
     """
-    For compatibility with certain other language clients.
+    For compatibility with Sublime's VCS status in the status bar.
     """
-    return "{}:/".format(match.group(1).lower())
+    return "{}:".format(match.group(1).upper())


Test patch:
diff --git a/tests/test_url.py b/tests/test_url.py
--- a/tests/test_url.py
+++ b/tests/test_url.py
@@ -16,7 +16,7 @@ def test_converts_uri_to_path(self):
     @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
     def test_converts_encoded_bad_drive_uri_to_path(self):
         # url2pathname does not understand %3A
-        self.assertEqual("c:\\dir ectory\\file.txt", uri_to_filename("file:///c%3A/dir%20ectory/file.txt"))
+        self.assertEqual("C:\\dir ectory\\file.txt", uri_to_filename("file:///c%3A/dir%20ectory/file.txt"))
 
 
 class NixTests(unittest.TestCase):