-
Notifications
You must be signed in to change notification settings - Fork 71
Feat/dart #68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/dart #68
Changes from 3 commits
0a0ff95
e193501
d349075
ca5e3e9
807ec41
e2cc4dd
93116f2
d66f736
c33215c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
from contextlib import asynccontextmanager | ||
import logging | ||
import os | ||
import pathlib | ||
import shutil | ||
from typing import AsyncIterator | ||
from multilspy.language_server import LanguageServer | ||
from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo | ||
import json | ||
|
||
|
||
class DartLanguageServer(LanguageServer): | ||
""" | ||
Provides Dart specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Dart. | ||
""" | ||
|
||
def __init__(self, config, logger, repository_root_path): | ||
""" | ||
Creates a DartServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. | ||
""" | ||
|
||
executable_path = self.setup_runtime_dependencies() | ||
super().__init__( | ||
config, | ||
logger, | ||
repository_root_path, | ||
ProcessLaunchInfo(cmd=executable_path, cwd=repository_root_path), | ||
"dart", | ||
) | ||
|
||
def setup_runtime_dependencies(self): | ||
dart_executable_path = shutil.which("dart") | ||
assert dart_executable_path, "Dart executable not found in PATH" | ||
|
||
return f"{dart_executable_path} language-server --client-id multilspy.dart --client-version 1.2" | ||
|
||
|
||
def _get_initialize_params(self, repository_absolute_path: str): | ||
""" | ||
Returns the initialize params for the Dart Language Server. | ||
""" | ||
with open( | ||
os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r" | ||
) as f: | ||
d = json.load(f) | ||
|
||
del d["_description"] | ||
|
||
d["processId"] = os.getpid() | ||
assert d["rootPath"] == "$rootPath" | ||
d["rootPath"] = repository_absolute_path | ||
|
||
assert d["rootUri"] == "$rootUri" | ||
d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() | ||
|
||
assert d["workspaceFolders"][0]["uri"] == "$uri" | ||
d["workspaceFolders"][0]["uri"] = pathlib.Path( | ||
repository_absolute_path | ||
).as_uri() | ||
|
||
assert d["workspaceFolders"][0]["name"] == "$name" | ||
d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) | ||
|
||
return d | ||
|
||
@asynccontextmanager | ||
async def start_server(self) -> AsyncIterator["DartLanguageServer"]: | ||
""" | ||
Start the language server and yield when the server is ready. | ||
""" | ||
|
||
async def execute_client_command_handler(params): | ||
return [] | ||
|
||
async def do_nothing(params): | ||
return | ||
|
||
async def check_experimental_status(params): | ||
pass | ||
|
||
async def window_log_message(msg): | ||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) | ||
|
||
self.server.on_request("client/registerCapability", do_nothing) | ||
self.server.on_notification("language/status", do_nothing) | ||
self.server.on_notification("window/logMessage", window_log_message) | ||
self.server.on_request( | ||
"workspace/executeClientCommand", execute_client_command_handler | ||
) | ||
self.server.on_notification("$/progress", do_nothing) | ||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing) | ||
self.server.on_notification("language/actionableNotification", do_nothing) | ||
self.server.on_notification( | ||
"experimental/serverStatus", check_experimental_status | ||
) | ||
|
||
async with super().start_server(): | ||
self.logger.log( | ||
"Starting dart-language-server server process", logging.INFO | ||
) | ||
await self.server.start() | ||
initialize_params = self._get_initialize_params(self.repository_root_path) | ||
self.logger.log( | ||
"Sending initialize request to dart-language-server", | ||
logging.DEBUG, | ||
) | ||
init_response = await self.server.send_request( | ||
"initialize", initialize_params | ||
) | ||
self.logger.log( | ||
f"Received initialize response from dart-language-server: {init_response}", | ||
logging.INFO, | ||
) | ||
|
||
self.server.notify.initialized({}) | ||
|
||
yield self | ||
|
||
await self.server.shutdown() | ||
await self.server.stop() |
v4rgas marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we also want to add a workspace configuration file https://github.com/dart-lang/sdk/blob/main/pkg/analysis_server/tool/lsp_spec/README.md#client-workspace-configuration? Could be useful, but can leave out for now if it is too much work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to be honest i do not know what that configuration is for, so i wouldnt be sure what to add exactly would u mind sending me some docs or adding it yourself if possible? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"_description": "This file contains the initialization parameters for the Dart Language Server.", | ||
"processId": "$processId", | ||
"rootPath": "$rootPath", | ||
"rootUri": "$rootUri", | ||
"capabilities": {}, | ||
"trace": "verbose", | ||
"workspaceFolders": [ | ||
{ | ||
"uri": "$uri", | ||
"name": "$name" | ||
} | ||
] | ||
} |
v4rgas marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
""" | ||
This file contains tests for running the Dart Language Server. | ||
""" | ||
|
||
import pytest | ||
from multilspy import LanguageServer | ||
from multilspy.multilspy_config import Language | ||
from tests.test_utils import create_test_context | ||
from pathlib import PurePath | ||
|
||
pytest_plugins = ("pytest_asyncio",) | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_multilspy_dart(): | ||
""" | ||
Test the working of multilspy with a Dart repository. | ||
""" | ||
code_language = Language.DART | ||
params = { | ||
"code_language": code_language, | ||
"repo_url": "https://github.com/simonoppowa/OpenNutriTracker/", | ||
"repo_commit": "2df39185bdd822dec6a0e521f4c14e3eab6b0805", | ||
} | ||
with create_test_context(params) as context: | ||
lsp = LanguageServer.create( | ||
context.config, context.logger, context.source_directory | ||
) | ||
|
||
async with lsp.start_server(): | ||
result = await lsp.request_document_symbols( | ||
str( | ||
PurePath("lib/core/presentation/widgets/copy_or_delete_dialog.dart") | ||
) | ||
) | ||
|
||
assert isinstance(result, tuple) | ||
assert len(result) == 2 | ||
|
||
symbols = result[0] | ||
for symbol in symbols: | ||
del symbol["deprecated"] | ||
del symbol["kind"] | ||
del symbol["location"]["uri"] | ||
|
||
assert symbols == [ | ||
{ | ||
"location": { | ||
"range": { | ||
"end": {"character": 24, "line": 3}, | ||
"start": {"character": 6, "line": 3}, | ||
}, | ||
}, | ||
"name": "CopyOrDeleteDialog", | ||
}, | ||
{ | ||
"containerName": "CopyOrDeleteDialog", | ||
"location": { | ||
"range": { | ||
"end": {"character": 26, "line": 4}, | ||
"start": {"character": 8, "line": 4}, | ||
}, | ||
}, | ||
"name": "CopyOrDeleteDialog", | ||
}, | ||
{ | ||
"containerName": "CopyOrDeleteDialog", | ||
"location": { | ||
"range": { | ||
"end": {"character": 14, "line": 7}, | ||
"start": {"character": 9, "line": 7}, | ||
}, | ||
}, | ||
"name": "build", | ||
}, | ||
] |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is good to start with now, but can we maybe add tests for any other commonly used lsp features as well? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
""" | ||
This file contains tests for running the Python Language Server: jedi-language-server | ||
""" | ||
|
||
from multilspy import SyncLanguageServer | ||
from multilspy.multilspy_config import Language | ||
from tests.test_utils import create_test_context | ||
from pathlib import PurePath | ||
|
||
|
||
def test_sync_multilspy_dart() -> None: | ||
""" | ||
Test the working of multilspy with python repository - black | ||
""" | ||
code_language = Language.DART | ||
params = { | ||
"code_language": code_language, | ||
"repo_url": "https://github.com/simonoppowa/OpenNutriTracker/", | ||
"repo_commit": "2df39185bdd822dec6a0e521f4c14e3eab6b0805", | ||
} | ||
with create_test_context(params) as context: | ||
lsp = SyncLanguageServer.create( | ||
context.config, context.logger, context.source_directory | ||
) | ||
|
||
# All the communication with the language server must be performed inside the context manager | ||
# The server process is started when the context manager is entered and is terminated when the context manager is exited. | ||
with lsp.start_server(): | ||
result = lsp.request_references( | ||
file_path=str( | ||
PurePath("lib/features/add_meal/presentation/add_meal_screen.dart") | ||
), | ||
line=19, | ||
column=5, | ||
) | ||
|
||
for item in result: | ||
del item["uri"] | ||
del item["absolutePath"] | ||
|
||
assert result == [ | ||
{ | ||
"range": { | ||
"end": {"character": 21, "line": 20}, | ||
"start": {"character": 8, "line": 20}, | ||
}, | ||
"relativePath": "lib/features/add_meal/presentation/add_meal_screen.dart", | ||
}, | ||
{ | ||
"range": { | ||
"end": {"character": 21, "line": 23}, | ||
"start": {"character": 8, "line": 23}, | ||
}, | ||
"relativePath": "lib/features/add_meal/presentation/add_meal_screen.dart", | ||
}, | ||
{ | ||
"range": { | ||
"end": {"character": 53, "line": 26}, | ||
"start": {"character": 40, "line": 26}, | ||
}, | ||
"relativePath": "lib/features/add_meal/presentation/add_meal_screen.dart", | ||
}, | ||
] |
Uh oh!
There was an error while loading. Please reload this page.