From e06276d10b0bd9104f661987d6ece72db6b1c922 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Sat, 23 Aug 2025 13:11:20 +0200 Subject: [PATCH 01/21] feat: Implemented needed models. --- src/malls/lsp/models.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/malls/lsp/models.py b/src/malls/lsp/models.py index 1ae03bd..c7c0175 100644 --- a/src/malls/lsp/models.py +++ b/src/malls/lsp/models.py @@ -2677,3 +2677,11 @@ class CompletionContext: trigger_character: str | None = None model_config = base_config + + +class HoverParams(TextDocumentPositionParams, WorkDoneProgressParams): + """ + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#hoverParams + """ + + model_config = base_config From 40a40902fb057088fe3c232f7ff655c9b064abdd Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Sat, 23 Aug 2025 13:37:36 +0200 Subject: [PATCH 02/21] chore: Return node instead of text. --- src/malls/ts/utils.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/malls/ts/utils.py b/src/malls/ts/utils.py index 3cf7a4c..be93980 100644 --- a/src/malls/ts/utils.py +++ b/src/malls/ts/utils.py @@ -1162,7 +1162,7 @@ def find_meta_comment_category_declaration(node: Node) -> list: """ meta_info = [] for children in node.children_by_field_name("meta"): - meta_info.append(children.child_by_field_name("info").text.strip(b'"')) + meta_info.append(children) return meta_info @@ -1174,7 +1174,7 @@ def find_meta_comment_asset_declaration(node: Node) -> list: """ meta_info = [] for children in node.children_by_field_name("meta"): - meta_info.append(children.child_by_field_name("info").text.strip(b'"')) + meta_info.append(children) return meta_info @@ -1186,7 +1186,7 @@ def find_meta_comment_attack_step(node: Node) -> list: """ meta_info = [] for children in node.children_by_field_name("meta"): - meta_info.append(children.child_by_field_name("info").text.strip(b'"')) + meta_info.append(children) return meta_info @@ -1209,7 +1209,7 @@ def find_meta_comment_asset_variable( meta_info = [] for children in asset.children_by_field_name("meta"): - meta_info.append(children.child_by_field_name("info").text.strip(b'"')) + meta_info.append(children) return meta_info @@ -1252,7 +1252,7 @@ def find_meta_comment_asset_variable_subsitution( # otherwise get the meta corresponding to that asset meta_info = [] for children in asset.children_by_field_name("meta"): - meta_info.append(children.child_by_field_name("info").text.strip(b'"')) + meta_info.append(children) return meta_info @@ -1275,7 +1275,7 @@ def find_meta_comment_asset_expr( # otherwise get the meta corresponding to that asset meta_info = [] for children in asset.children_by_field_name("meta"): - meta_info.append(children.child_by_field_name("info").text.strip(b'"')) + meta_info.append(children) return meta_info @@ -1299,7 +1299,7 @@ def find_meta_comment_association( # otherwise get the meta corresponding to that asset meta_info = [] for children in result_node.children_by_field_name("meta"): - meta_info.append(children.child_by_field_name("info").text.strip(b'"')) + meta_info.append(children) return meta_info @@ -1377,7 +1377,7 @@ def find_comments_function( # sort captures by row sorted_comments = sorted(captures["comment_node"], key=lambda item: item.start_point.row) - comments = [sorted_comments[0].text] + comments = [sorted_comments[0]] previous_row = sorted_comments[0].end_point.row for comment_node in sorted_comments[1:]: @@ -1392,11 +1392,11 @@ def find_comments_function( # if the comment is in a consecutive row, # we keep it if current_row == previous_row + 1: - comments.append(comment_node.text) + comments.append(comment_node) previous_row = current_row # update row else: # otherwise, restart the count - comments = [comment_node.text] + comments = [comment_node] previous_row = comment_node.end_point.row return comments if previous_row == start_row - 1 else [] From 14e83728a8d33965a3741898cb364a95104bdfce Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Sat, 23 Aug 2025 13:37:51 +0200 Subject: [PATCH 03/21] tests: Take into account nodes are returned. --- tests/unit/test_find_comments_for_symbol_function.py | 4 +++- tests/unit/test_find_meta_comment_function.py | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_find_comments_for_symbol_function.py b/tests/unit/test_find_comments_for_symbol_function.py index f2aeca2..ec8739f 100644 --- a/tests/unit/test_find_comments_for_symbol_function.py +++ b/tests/unit/test_find_comments_for_symbol_function.py @@ -54,6 +54,8 @@ def test_find_comments_for_symbol_function(mal_find_comments_for_symbol_function assert cursor.node.type == "identifier" # we use sets to ensure order does not matter - returned_comments = find_comments_function(cursor.node, cursor.node.text, doc_uri, storage) + returned_comments = [ + x.text for x in find_comments_function(cursor.node, cursor.node.text, doc_uri, storage) + ] assert set(returned_comments) == set(comments) diff --git a/tests/unit/test_find_meta_comment_function.py b/tests/unit/test_find_meta_comment_function.py index 839b69b..a26dd5e 100644 --- a/tests/unit/test_find_meta_comment_function.py +++ b/tests/unit/test_find_meta_comment_function.py @@ -59,6 +59,8 @@ def test_find_meta_comment_function(mal_find_meta_comment_function, point, comme assert cursor.node.type == "identifier" # we use sets to ensure order does not matter - returned_comments = find_meta_comment_function(cursor.node, cursor.node.text, doc_uri, storage) - + returned_comments = [ + x.child_by_field_name("info").text.strip(b'"') + for x in find_meta_comment_function(cursor.node, cursor.node.text, doc_uri, storage) + ] assert set(returned_comments) == set(comments) From 2f39e6afff94da4720b22f29659cb149ca26018e Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Sat, 23 Aug 2025 13:38:09 +0200 Subject: [PATCH 04/21] feat: Function to return markdown with hover info. --- src/malls/lsp/utils.py | 49 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/malls/lsp/utils.py b/src/malls/lsp/utils.py index 7216146..1746fc8 100644 --- a/src/malls/lsp/utils.py +++ b/src/malls/lsp/utils.py @@ -4,11 +4,13 @@ import tree_sitter_mal as ts_mal from pylsp_jsonrpc.endpoint import Endpoint -from tree_sitter import Language, Parser +from tree_sitter import Language, Node, Parser from uritools import urisplit from ..ts.utils import ( INCLUDED_FILES_QUERY, + find_comments_function, + find_meta_comment_function, find_symbols_in_current_scope, lsp_to_tree_sitter_position, query_for_error_nodes, @@ -124,3 +126,48 @@ def get_completion_list(doc: Document, pos: Position) -> list: ] return completion_list + + +def build_markdown_meta_comments(meta_comments: list[Node]): + markdown = "## Meta comments\n" + for meta_comment in meta_comments: + meta_id = meta_comment.child_by_field_name("id") + meta_info = meta_comment.child_by_field_name("info") + markdown += f"- **{meta_id}**: {meta_info}\n" + return markdown + + +def build_markdown_comments(meta_comments: list[Node]): + markdown = "## Comments\n" + for meta_comment in meta_comments: + meta_id = meta_comment.child_by_field_name("id") + meta_info = meta_comment.child_by_field_name("info") + markdown += f"- **{meta_id}**: {meta_info}\n" + return markdown + + +def get_hover_info_list(doc: Document, pos: Position, storage: dict) -> list: + # convert LSP position to TS point + point = lsp_to_tree_sitter_position(doc.text, pos) + + # get the symbol in that position + cursor = doc.tree.walk() + while cursor.goto_first_child_for_point(point) is not None: + continue + + node = cursor.node + if node.type != "identifier": + return [] # we can only find comments for identifiers + + # TODO write better hover info + markdown = "# Symbol Info" + + # get meta comments + meta_comments = find_meta_comment_function(node, node.text, doc.uri, storage) + markdown = build_markdown_meta_comments(meta_comments) + + # get regular comments + comments = find_comments_function(node, node.text, doc.uri, storage) + markdown += build_markdown_comments(comments) + + return markdown From 1c61910d7f6c1300e249622beab35cebc20ac8c5 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Sat, 23 Aug 2025 13:38:55 +0200 Subject: [PATCH 05/21] feat: Implement handler for textDocument hover. --- src/malls/mal_lsp.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/malls/mal_lsp.py b/src/malls/mal_lsp.py index b7acadd..fdcc800 100644 --- a/src/malls/mal_lsp.py +++ b/src/malls/mal_lsp.py @@ -9,7 +9,7 @@ from .lsp import enums, models from .lsp.classes import Document -from .lsp.enums import ErrorCodes, PositionEncodingKind, TraceValue +from .lsp.enums import ErrorCodes, MarkupKind, PositionEncodingKind, TraceValue from .lsp.fsm import LifecycleFSM from .lsp.utils import ( get_completion_list, @@ -417,3 +417,21 @@ def m_text_document__completion(self, **params: dict | None) -> None: # `If a CompletionItem[] is provided it is interpreted to # be complete. So it is the same as { isIncomplete: false, items }` return completion_list + + def m_text_document__hover(self, **params: dict | None) -> None | dict: + # validate parameters + hover = models.HoverParams(**params) if params else None + if hover is None: + return None # parameters are wrong + + # obtain relevant parameters + doc_uri = uri_to_path(hover.text_document.uri) + position = hover.position + + # get completion list + hover_content = get_completion_list(self.__files[doc_uri], position) + + return { + "kind": MarkupKind.Markdown, + "value": hover_content, + } From f1e6431f71ea9a02f6af1b3d22b40aaac9d16bb3 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Sun, 24 Aug 2025 17:47:31 +0200 Subject: [PATCH 06/21] fix: Update capabilities and fix response. --- src/malls/mal_lsp.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/malls/mal_lsp.py b/src/malls/mal_lsp.py index fdcc800..a986467 100644 --- a/src/malls/mal_lsp.py +++ b/src/malls/mal_lsp.py @@ -13,6 +13,7 @@ from .lsp.fsm import LifecycleFSM from .lsp.utils import ( get_completion_list, + get_hover_info, path_to_uri, recursive_parsing, send_diagnostics, @@ -108,6 +109,7 @@ def capabilities(self, client_capabilities: models.ClientCapabilities | None = N }, "definitionProvider": True, "completionProvider": {}, + "hoverProvider": True, } log.debug("Server capabilities: %s", capabilities) @@ -429,9 +431,11 @@ def m_text_document__hover(self, **params: dict | None) -> None | dict: position = hover.position # get completion list - hover_content = get_completion_list(self.__files[doc_uri], position) + hover_content = get_hover_info(self.__files[doc_uri], position, self.__files) return { - "kind": MarkupKind.Markdown, - "value": hover_content, + "contents": { + "kind": MarkupKind.Markdown, + "value": hover_content, + } } From 580586c4b7e4b947c4014502f07195f8295bed95 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Sun, 24 Aug 2025 17:47:45 +0200 Subject: [PATCH 07/21] chore: Better markdown response. --- src/malls/lsp/utils.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/malls/lsp/utils.py b/src/malls/lsp/utils.py index 1746fc8..fe03c22 100644 --- a/src/malls/lsp/utils.py +++ b/src/malls/lsp/utils.py @@ -129,24 +129,22 @@ def get_completion_list(doc: Document, pos: Position) -> list: def build_markdown_meta_comments(meta_comments: list[Node]): - markdown = "## Meta comments\n" + markdown = "## **Meta comments**\n" for meta_comment in meta_comments: - meta_id = meta_comment.child_by_field_name("id") - meta_info = meta_comment.child_by_field_name("info") + meta_id = meta_comment.child_by_field_name("id").text.decode() + meta_info = meta_comment.child_by_field_name("info").text.decode() markdown += f"- **{meta_id}**: {meta_info}\n" return markdown -def build_markdown_comments(meta_comments: list[Node]): - markdown = "## Comments\n" - for meta_comment in meta_comments: - meta_id = meta_comment.child_by_field_name("id") - meta_info = meta_comment.child_by_field_name("info") - markdown += f"- **{meta_id}**: {meta_info}\n" +def build_markdown_comments(comments: list[Node]): + markdown = "## **Comments**\n" + for comment in comments: + markdown += f"- {comment.text.decode()}\n" return markdown -def get_hover_info_list(doc: Document, pos: Position, storage: dict) -> list: +def get_hover_info(doc: Document, pos: Position, storage: dict) -> str: # convert LSP position to TS point point = lsp_to_tree_sitter_position(doc.text, pos) @@ -157,14 +155,16 @@ def get_hover_info_list(doc: Document, pos: Position, storage: dict) -> list: node = cursor.node if node.type != "identifier": - return [] # we can only find comments for identifiers + return "" # we can only find comments for identifiers # TODO write better hover info - markdown = "# Symbol Info" + markdown = "\n# Symbol Info\n" # get meta comments meta_comments = find_meta_comment_function(node, node.text, doc.uri, storage) - markdown = build_markdown_meta_comments(meta_comments) + markdown += build_markdown_meta_comments(meta_comments) + + markdown += "---\n" # get regular comments comments = find_comments_function(node, node.text, doc.uri, storage) From c9121679ffde1aa0bc57ad25e80e00e0dd1314f8 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Sun, 24 Aug 2025 19:45:18 +0200 Subject: [PATCH 08/21] chore: Sanitize comments. --- src/malls/lsp/utils.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/malls/lsp/utils.py b/src/malls/lsp/utils.py index fe03c22..b142295 100644 --- a/src/malls/lsp/utils.py +++ b/src/malls/lsp/utils.py @@ -137,10 +137,21 @@ def build_markdown_meta_comments(meta_comments: list[Node]): return markdown +def sanitize_comment(comment: str): + sanitized_comment = "" + for line in comment: + line = line.lstrip("/*") + line = line.lstrip("*") + line = line.lstrip("//") + line = line.rstrip("*/") + sanitized_comment += line if line != "\n" else "" + return sanitized_comment + + def build_markdown_comments(comments: list[Node]): markdown = "## **Comments**\n" for comment in comments: - markdown += f"- {comment.text.decode()}\n" + markdown += f"- {sanitize_comment(comment.text.decode())}\n" return markdown From 1477859e908fd55924160ab777a6431e6eb88edc Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Wed, 27 Aug 2025 16:01:06 +0200 Subject: [PATCH 09/21] fix: Update test to include hover capability. --- tests/fixtures/init_exit.out.lsp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fixtures/init_exit.out.lsp b/tests/fixtures/init_exit.out.lsp index 77f5141..f555d6c 100644 --- a/tests/fixtures/init_exit.out.lsp +++ b/tests/fixtures/init_exit.out.lsp @@ -1,7 +1,7 @@ -Content-Length: 210 +Content-Length: 231 Content-Type: application/vscode-jsonrpc; charset=utf8 -{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"positionEncoding":"utf-16","textDocumentSync":{"openClose":true,"change":1},"definitionProvider":true,"completionProvider":{}},"serverInfo":{"name":"mal-ls"}}}Content-Length: 123 +{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"positionEncoding":"utf-16","textDocumentSync":{"openClose":true,"change":1},"definitionProvider":true,"completionProvider":{},"hoverProvider":true},"serverInfo":{"name":"mal-ls"}}}Content-Length: 123 Content-Type: application/vscode-jsonrpc; charset=utf8 {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Must wait for `initalized` notification before other requests."}} From 98b0e148b4e71e60f85627db7eb19cc0d6d13f1f Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 15:49:30 +0200 Subject: [PATCH 10/21] tests: textDocument hover. --- tests/fixtures/mal/hover_document.mal | 77 +++++++++ tests/integration/test_hover.py | 225 ++++++++++++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 tests/fixtures/mal/hover_document.mal create mode 100644 tests/integration/test_hover.py diff --git a/tests/fixtures/mal/hover_document.mal b/tests/fixtures/mal/hover_document.mal new file mode 100644 index 0000000..9c64dd5 --- /dev/null +++ b/tests/fixtures/mal/hover_document.mal @@ -0,0 +1,77 @@ +#id: "org.mal-lang.testAnalyzer" +#version:"0.0.0" + +// should be ignored + +// category comment +category Example +developer info: "dev cat" +modeler info: "mod cat" +{ + //should be ignored + + // asset1 comment + abstract asset Asset1 + developer info: "dev asset" + modeler info: "mod asset" + { + let var = c.b + // attack_step comment + | compromise + developer info: "dev attack_step" + modeler info: "mod attack_step" + -> var().destroy + + // attack_step comment2 + | attack + -> c.b.h.attack4, + c.b.h[Asset5].attack5 + } + + // should be ignored + abstract asset + // asset3 comment + Asset3 + developer info: "dev asset3" + modeler info: "mod asset3" + { + + } + + // asset2 comment + asset Asset2 extends Asset3 + { + | destroy + } + + asset Asset4 + developer info: "dev asset4" + modeler info: "mod asset4" + { + & attack4 + } + + /* + * MULTI-LINE + */ + asset Asset5 extends Asset4 + developer info: "dev asset5" + modeler info: "mod asset5" + { + // attack_step comment3 + & attack5 + developer info: "dev attack_step_5" + modeler info: "mod attack_step_5" + // should be ignored + } + +} +associations +{ + // association1 comment + Asset1 [a] * <-- L --> * [c] Asset2 developer info: "some info" + // association2 comment + Asset2 [d] 1 <-- M --> 1 [e] Asset2 + Asset3 [b] 1 <-- N --> 1 [f] Asset2 + Asset3 [g] 1 <-- O --> 1 [h] Asset4 +} diff --git a/tests/integration/test_hover.py b/tests/integration/test_hover.py new file mode 100644 index 0000000..0d560ec --- /dev/null +++ b/tests/integration/test_hover.py @@ -0,0 +1,225 @@ +import io +import typing +from pathlib import Path + +import pytest +import tree_sitter_mal as ts_mal +from tree_sitter import Language, Parser + +from ..util import build_rpc_message_stream, get_lsp_json, server_output + +pytest_plugins = ["tests.fixtures.lsp.conftest"] + +comments_in_category = { + "comments": ["// category comment"], + "meta": { + "developer": "\"dev cat\"", + "modeler": "\"mod cat\"", + } +} +comments_in_asset_1 = { + "comments": ["// asset1 comment"], + "meta": { + "developer": "\"dev asset\"", + "modeler": "\"mod asset\"" + } +} +comments_in_attack_step1 = { + "comments": ["// attack_step comment"], + "meta": { + "developer": "\"dev attack_step\"", + "modeler": "\"mod attack_step\"" + } +} +comments_in_attack_step2 = { + "comments": ["// attack_step comment2"], + "meta": { + } +} +comments_in_asset_3 = { + "comments": ["// asset3 comment"], + "meta": { + "developer": "\"dev asset3\"", + "modeler": "\"mod asset3\"" + } +} +comments_in_asset_2 = { + "comments": ["// asset2 comment"], + "meta": { + } +} +comments_in_asset_4 = { + "comments": [], + "meta": { + "developer": "\"dev asset4\"", + "modeler": "\"mod asset4\"" + } +} +comments_in_asset_5 = { + "comments": [ + """/* + * MULTI-LINE + */""" + ], + "meta": { + "developer": "\"dev asset5\"", + "modeler": "\"mod asset5\"" + } +} +comments_in_attack_step3 = { + "comments": ["// attack_step comment3"], + "meta": { + "developer": "\"dev attack_step_5\"", + "modeler": "\"mod attack_step_5\"" + } +} +comments_in_association = { + "comments": ["// association1 comment"], + "meta": { + "developer": "\"some info\"", + } +} +comments_in_association2 = { + "comments": ["// association2 comment"], + "meta": { + } +} + +parameters = [ + ((6, 11), comments_in_category), + ((13, 20), comments_in_asset_1), + ((19, 15), comments_in_attack_step1), + ((25, 12), comments_in_attack_step2), + ((33, 7), comments_in_asset_3), + ((41, 13), comments_in_asset_2), + ((46, 13), comments_in_asset_4), + ((56, 13), comments_in_asset_5), + ((61, 13), comments_in_attack_step3), + ((71, 21), comments_in_association), + ((73, 21), comments_in_association2), +] + +parameter_names = [ + "comments_in_category", + "comments_in_asset_1", + "comments_in_attack_step1", + "comments_in_attack_step2", + "comments_in_asset_3", + "comments_in_asset_2", + "comments_in_asset_4", + "comments_in_asset_5", + "comments_in_attack_step3", + "comments_in_association", + "comments_in_association2", +] + +pytest_plugins = ["tests.fixtures.mal"] + +def sanitize_comment(comment: str): + sanitized_comment = "" + for line in comment: + line = line.lstrip("/*") + line = line.lstrip("*") + line = line.lstrip("//") + line = line.rstrip("*/") + sanitized_comment += line if line != "\n" else "" + return sanitized_comment + +def build_comment(comments: dict): + markdown = "\n# Symbol Info\n" + markdown += "## **Meta comments**\n" + for meta_id, meta_info in comments['meta'].items(): + markdown += f"- **{meta_id}**: {meta_info}\n" + markdown += "---\n" + markdown += "## **Comments**\n" + for comment in comments['comments']: + markdown += f"- {sanitize_comment(comment)}\n" + return markdown + + +@pytest.fixture +def open_hover_document_notification( + client_notifications: list[dict], + client_messages: list[dict], + mal_hover_document: io.BytesIO, + mal_hover_document_uri: str, +) -> dict: + """ + Sends a didOpen notification bound to the MAL fixture file hover_document. + """ + message = { + "jsonrpc": "2.0", + "method": "textDocument/didOpen", + "params": { + "textDocument": { + "uri": mal_hover_document_uri, + "languageId": "mal", + "version": 0, + "text": mal_hover_document.read().decode("utf8"), + } + }, + } + client_notifications.append(message) + client_messages.append(message) + return message + + +@pytest.fixture +def hover_request( + client_requests: list[dict], client_messages: list[dict], mal_hover_document_uri: str +) -> typing.Callable[[(int, int)], dict]: + def make(position: (int, int)): + line, character = position + message = { + "id": len(client_requests), + "jsonrpc": "2.0", + "method": "textDocument/hover", + "params": { + "textDocument": { + "uri": mal_hover_document_uri, + }, + "position": { + "line": line, + "character": character, + }, + }, + } + client_requests.append(message) + client_messages.append(message) + return message + + return make + + +@pytest.fixture +def hover_client_messages( + client_messages: list[dict], + initalize_request, + initalized_notification, + open_hover_document_notification, + hover_request: typing.Callable[[(int, int)], dict], +) -> typing.Callable[[(int, int)], io.BytesIO]: # noqa: E501 + def make(position: (int, int)) -> io.BytesIO: + hover_request(position) + return build_rpc_message_stream(client_messages) + + return make + + +@pytest.mark.parametrize("location,comments", parameters, ids=parameter_names) +def test_hover( + location: (int, int), + comments: dict, + hover_client_messages: typing.Callable[[(int, int)], io.BytesIO], +): + # send to server + fixture = hover_client_messages(location) + output, *_ = server_output(fixture) + + output.seek(0) + response = get_lsp_json(output) + response = get_lsp_json(output) + + assert build_comment(comments) == response['result']['contents']['value'] + + output.close() From f535f8a51ac56b4addad7e28b31f414636e6a20e Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 15:53:38 +0200 Subject: [PATCH 11/21] fix: Function returns node, not text. --- tests/unit/test_find_meta_comment_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_find_meta_comment_function.py b/tests/unit/test_find_meta_comment_function.py index a437945..9c1947d 100644 --- a/tests/unit/test_find_meta_comment_function.py +++ b/tests/unit/test_find_meta_comment_function.py @@ -71,6 +71,6 @@ def test_find_meta_comment_function( assert cursor.node.type == "identifier" # we use sets to ensure order does not matter - comments = find_meta_comment_function(cursor.node, cursor.node.text, doc_uri, storage) + comments = [x.child_by_field_name('info').text.strip(b"\"") for x in find_meta_comment_function(cursor.node, cursor.node.text, doc_uri, storage)] assert set(comments) == expected_comments From 5c9b99076d1fc03d50e5ad9c88c71338476c3f05 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 16:01:52 +0200 Subject: [PATCH 12/21] fix: Include hover capability. --- tests/fixtures/lsp/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fixtures/lsp/conftest.py b/tests/fixtures/lsp/conftest.py index 26468c7..14d0578 100644 --- a/tests/fixtures/lsp/conftest.py +++ b/tests/fixtures/lsp/conftest.py @@ -129,6 +129,7 @@ def initalize_response( "textDocumentSync": {"openClose": True, "change": 1}, "definitionProvider": True, "completionProvider": {}, + "hoverProvider": True, }, # TODO: Replace with values from an actual server instance (e.g. via instance.server_info()) # noqa: E501 "serverInfo": {"name": "mal-ls"}, From 1ed86f6931df616c34fa8724eee60c197ca5a5dc Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 16:02:22 +0200 Subject: [PATCH 13/21] fix: Linter errors. --- src/malls/lsp/utils.py | 2 +- tests/integration/test_hover.py | 71 ++++++------------- tests/unit/test_find_meta_comment_function.py | 5 +- 3 files changed, 25 insertions(+), 53 deletions(-) diff --git a/src/malls/lsp/utils.py b/src/malls/lsp/utils.py index 990249e..d6ef43d 100644 --- a/src/malls/lsp/utils.py +++ b/src/malls/lsp/utils.py @@ -166,7 +166,7 @@ def get_hover_info(doc: Document, pos: Position, storage: dict) -> str: node = cursor.node if node.type != "identifier": - return "" # we can only find comments for identifiers + return "" # we can only find comments for identifiers # TODO write better hover info markdown = "\n# Symbol Info\n" diff --git a/tests/integration/test_hover.py b/tests/integration/test_hover.py index 0d560ec..eda1616 100644 --- a/tests/integration/test_hover.py +++ b/tests/integration/test_hover.py @@ -1,10 +1,7 @@ import io import typing -from pathlib import Path import pytest -import tree_sitter_mal as ts_mal -from tree_sitter import Language, Parser from ..util import build_rpc_message_stream, get_lsp_json, server_output @@ -13,77 +10,47 @@ comments_in_category = { "comments": ["// category comment"], "meta": { - "developer": "\"dev cat\"", - "modeler": "\"mod cat\"", - } + "developer": '"dev cat"', + "modeler": '"mod cat"', + }, } comments_in_asset_1 = { "comments": ["// asset1 comment"], - "meta": { - "developer": "\"dev asset\"", - "modeler": "\"mod asset\"" - } + "meta": {"developer": '"dev asset"', "modeler": '"mod asset"'}, } comments_in_attack_step1 = { "comments": ["// attack_step comment"], - "meta": { - "developer": "\"dev attack_step\"", - "modeler": "\"mod attack_step\"" - } -} -comments_in_attack_step2 = { - "comments": ["// attack_step comment2"], - "meta": { - } + "meta": {"developer": '"dev attack_step"', "modeler": '"mod attack_step"'}, } +comments_in_attack_step2 = {"comments": ["// attack_step comment2"], "meta": {}} comments_in_asset_3 = { "comments": ["// asset3 comment"], - "meta": { - "developer": "\"dev asset3\"", - "modeler": "\"mod asset3\"" - } -} -comments_in_asset_2 = { - "comments": ["// asset2 comment"], - "meta": { - } + "meta": {"developer": '"dev asset3"', "modeler": '"mod asset3"'}, } +comments_in_asset_2 = {"comments": ["// asset2 comment"], "meta": {}} comments_in_asset_4 = { "comments": [], - "meta": { - "developer": "\"dev asset4\"", - "modeler": "\"mod asset4\"" - } + "meta": {"developer": '"dev asset4"', "modeler": '"mod asset4"'}, } comments_in_asset_5 = { "comments": [ - """/* + """/* * MULTI-LINE */""" ], - "meta": { - "developer": "\"dev asset5\"", - "modeler": "\"mod asset5\"" - } + "meta": {"developer": '"dev asset5"', "modeler": '"mod asset5"'}, } comments_in_attack_step3 = { "comments": ["// attack_step comment3"], - "meta": { - "developer": "\"dev attack_step_5\"", - "modeler": "\"mod attack_step_5\"" - } + "meta": {"developer": '"dev attack_step_5"', "modeler": '"mod attack_step_5"'}, } comments_in_association = { "comments": ["// association1 comment"], "meta": { - "developer": "\"some info\"", - } -} -comments_in_association2 = { - "comments": ["// association2 comment"], - "meta": { - } + "developer": '"some info"', + }, } +comments_in_association2 = {"comments": ["// association2 comment"], "meta": {}} parameters = [ ((6, 11), comments_in_category), @@ -115,6 +82,7 @@ pytest_plugins = ["tests.fixtures.mal"] + def sanitize_comment(comment: str): sanitized_comment = "" for line in comment: @@ -125,14 +93,15 @@ def sanitize_comment(comment: str): sanitized_comment += line if line != "\n" else "" return sanitized_comment + def build_comment(comments: dict): markdown = "\n# Symbol Info\n" markdown += "## **Meta comments**\n" - for meta_id, meta_info in comments['meta'].items(): + for meta_id, meta_info in comments["meta"].items(): markdown += f"- **{meta_id}**: {meta_info}\n" markdown += "---\n" markdown += "## **Comments**\n" - for comment in comments['comments']: + for comment in comments["comments"]: markdown += f"- {sanitize_comment(comment)}\n" return markdown @@ -220,6 +189,6 @@ def test_hover( response = get_lsp_json(output) response = get_lsp_json(output) - assert build_comment(comments) == response['result']['contents']['value'] + assert build_comment(comments) == response["result"]["contents"]["value"] output.close() diff --git a/tests/unit/test_find_meta_comment_function.py b/tests/unit/test_find_meta_comment_function.py index 9c1947d..8d67ad8 100644 --- a/tests/unit/test_find_meta_comment_function.py +++ b/tests/unit/test_find_meta_comment_function.py @@ -71,6 +71,9 @@ def test_find_meta_comment_function( assert cursor.node.type == "identifier" # we use sets to ensure order does not matter - comments = [x.child_by_field_name('info').text.strip(b"\"") for x in find_meta_comment_function(cursor.node, cursor.node.text, doc_uri, storage)] + comments = [ + x.child_by_field_name("info").text.strip(b'"') + for x in find_meta_comment_function(cursor.node, cursor.node.text, doc_uri, storage) + ] assert set(comments) == expected_comments From 26f97a574fb163ac90a438edc3704405dce5de87 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 17:05:08 +0200 Subject: [PATCH 14/21] chore: Move to markdown files. --- tests/integration/test_hover.py | 80 +++++++-------------------------- 1 file changed, 16 insertions(+), 64 deletions(-) diff --git a/tests/integration/test_hover.py b/tests/integration/test_hover.py index eda1616..b9adbd4 100644 --- a/tests/integration/test_hover.py +++ b/tests/integration/test_hover.py @@ -5,65 +5,18 @@ from ..util import build_rpc_message_stream, get_lsp_json, server_output -pytest_plugins = ["tests.fixtures.lsp.conftest"] - -comments_in_category = { - "comments": ["// category comment"], - "meta": { - "developer": '"dev cat"', - "modeler": '"mod cat"', - }, -} -comments_in_asset_1 = { - "comments": ["// asset1 comment"], - "meta": {"developer": '"dev asset"', "modeler": '"mod asset"'}, -} -comments_in_attack_step1 = { - "comments": ["// attack_step comment"], - "meta": {"developer": '"dev attack_step"', "modeler": '"mod attack_step"'}, -} -comments_in_attack_step2 = {"comments": ["// attack_step comment2"], "meta": {}} -comments_in_asset_3 = { - "comments": ["// asset3 comment"], - "meta": {"developer": '"dev asset3"', "modeler": '"mod asset3"'}, -} -comments_in_asset_2 = {"comments": ["// asset2 comment"], "meta": {}} -comments_in_asset_4 = { - "comments": [], - "meta": {"developer": '"dev asset4"', "modeler": '"mod asset4"'}, -} -comments_in_asset_5 = { - "comments": [ - """/* - * MULTI-LINE - */""" - ], - "meta": {"developer": '"dev asset5"', "modeler": '"mod asset5"'}, -} -comments_in_attack_step3 = { - "comments": ["// attack_step comment3"], - "meta": {"developer": '"dev attack_step_5"', "modeler": '"mod attack_step_5"'}, -} -comments_in_association = { - "comments": ["// association1 comment"], - "meta": { - "developer": '"some info"', - }, -} -comments_in_association2 = {"comments": ["// association2 comment"], "meta": {}} - parameters = [ - ((6, 11), comments_in_category), - ((13, 20), comments_in_asset_1), - ((19, 15), comments_in_attack_step1), - ((25, 12), comments_in_attack_step2), - ((33, 7), comments_in_asset_3), - ((41, 13), comments_in_asset_2), - ((46, 13), comments_in_asset_4), - ((56, 13), comments_in_asset_5), - ((61, 13), comments_in_attack_step3), - ((71, 21), comments_in_association), - ((73, 21), comments_in_association2), + (6, 11), + (13, 20), + (19, 15), + (25, 12), + (33, 7), + (41, 13), + (46, 13), + (56, 13), + (61, 13), + (71, 21), + (73, 21), ] parameter_names = [ @@ -80,9 +33,6 @@ "comments_in_association2", ] -pytest_plugins = ["tests.fixtures.mal"] - - def sanitize_comment(comment: str): sanitized_comment = "" for line in comment: @@ -175,12 +125,14 @@ def make(position: (int, int)) -> io.BytesIO: return make -@pytest.mark.parametrize("location,comments", parameters, ids=parameter_names) +@pytest.mark.parametrize("location,markdown_file", zip(parameters,parameter_names), ids=parameter_names) def test_hover( + request: pytest.FixtureRequest, location: (int, int), - comments: dict, + markdown_file: str, hover_client_messages: typing.Callable[[(int, int)], io.BytesIO], ): + file_fixture: typing.BinaryIO = request.getfixturevalue(f"markdown_{markdown_file}") # send to server fixture = hover_client_messages(location) output, *_ = server_output(fixture) @@ -189,6 +141,6 @@ def test_hover( response = get_lsp_json(output) response = get_lsp_json(output) - assert build_comment(comments) == response["result"]["contents"]["value"] + assert file_fixture.read().decode() == response["result"]["contents"]["value"] output.close() From e23fb856b194222152fb6abf0f7c853ab8cf1312 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 17:05:20 +0200 Subject: [PATCH 15/21] chore: Create markdown fixtures. --- tests/fixtures/markdown/comments_in_asset_1.md | 8 ++++++++ tests/fixtures/markdown/comments_in_asset_2.md | 6 ++++++ tests/fixtures/markdown/comments_in_asset_3.md | 8 ++++++++ tests/fixtures/markdown/comments_in_asset_4.md | 7 +++++++ tests/fixtures/markdown/comments_in_asset_5.md | 8 ++++++++ tests/fixtures/markdown/comments_in_association.md | 7 +++++++ tests/fixtures/markdown/comments_in_association2.md | 6 ++++++ tests/fixtures/markdown/comments_in_attack_step1.md | 8 ++++++++ tests/fixtures/markdown/comments_in_attack_step2.md | 6 ++++++ tests/fixtures/markdown/comments_in_attack_step3.md | 8 ++++++++ tests/fixtures/markdown/comments_in_category.md | 8 ++++++++ 11 files changed, 80 insertions(+) create mode 100644 tests/fixtures/markdown/comments_in_asset_1.md create mode 100644 tests/fixtures/markdown/comments_in_asset_2.md create mode 100644 tests/fixtures/markdown/comments_in_asset_3.md create mode 100644 tests/fixtures/markdown/comments_in_asset_4.md create mode 100644 tests/fixtures/markdown/comments_in_asset_5.md create mode 100644 tests/fixtures/markdown/comments_in_association.md create mode 100644 tests/fixtures/markdown/comments_in_association2.md create mode 100644 tests/fixtures/markdown/comments_in_attack_step1.md create mode 100644 tests/fixtures/markdown/comments_in_attack_step2.md create mode 100644 tests/fixtures/markdown/comments_in_attack_step3.md create mode 100644 tests/fixtures/markdown/comments_in_category.md diff --git a/tests/fixtures/markdown/comments_in_asset_1.md b/tests/fixtures/markdown/comments_in_asset_1.md new file mode 100644 index 0000000..2059488 --- /dev/null +++ b/tests/fixtures/markdown/comments_in_asset_1.md @@ -0,0 +1,8 @@ + +# Symbol Info +## **Meta comments** +- **developer**: "dev asset" +- **modeler**: "mod asset" +--- +## **Comments** +- asset1 comment diff --git a/tests/fixtures/markdown/comments_in_asset_2.md b/tests/fixtures/markdown/comments_in_asset_2.md new file mode 100644 index 0000000..704d33c --- /dev/null +++ b/tests/fixtures/markdown/comments_in_asset_2.md @@ -0,0 +1,6 @@ + +# Symbol Info +## **Meta comments** +--- +## **Comments** +- asset2 comment diff --git a/tests/fixtures/markdown/comments_in_asset_3.md b/tests/fixtures/markdown/comments_in_asset_3.md new file mode 100644 index 0000000..6480a0d --- /dev/null +++ b/tests/fixtures/markdown/comments_in_asset_3.md @@ -0,0 +1,8 @@ + +# Symbol Info +## **Meta comments** +- **developer**: "dev asset3" +- **modeler**: "mod asset3" +--- +## **Comments** +- asset3 comment diff --git a/tests/fixtures/markdown/comments_in_asset_4.md b/tests/fixtures/markdown/comments_in_asset_4.md new file mode 100644 index 0000000..4dacd96 --- /dev/null +++ b/tests/fixtures/markdown/comments_in_asset_4.md @@ -0,0 +1,7 @@ + +# Symbol Info +## **Meta comments** +- **developer**: "dev asset4" +- **modeler**: "mod asset4" +--- +## **Comments** diff --git a/tests/fixtures/markdown/comments_in_asset_5.md b/tests/fixtures/markdown/comments_in_asset_5.md new file mode 100644 index 0000000..783cf6a --- /dev/null +++ b/tests/fixtures/markdown/comments_in_asset_5.md @@ -0,0 +1,8 @@ + +# Symbol Info +## **Meta comments** +- **developer**: "dev asset5" +- **modeler**: "mod asset5" +--- +## **Comments** +- MULTI-LINE diff --git a/tests/fixtures/markdown/comments_in_association.md b/tests/fixtures/markdown/comments_in_association.md new file mode 100644 index 0000000..6603bde --- /dev/null +++ b/tests/fixtures/markdown/comments_in_association.md @@ -0,0 +1,7 @@ + +# Symbol Info +## **Meta comments** +- **developer**: "some info" +--- +## **Comments** +- association1 comment diff --git a/tests/fixtures/markdown/comments_in_association2.md b/tests/fixtures/markdown/comments_in_association2.md new file mode 100644 index 0000000..b32a34d --- /dev/null +++ b/tests/fixtures/markdown/comments_in_association2.md @@ -0,0 +1,6 @@ + +# Symbol Info +## **Meta comments** +--- +## **Comments** +- association2 comment diff --git a/tests/fixtures/markdown/comments_in_attack_step1.md b/tests/fixtures/markdown/comments_in_attack_step1.md new file mode 100644 index 0000000..ad1cf02 --- /dev/null +++ b/tests/fixtures/markdown/comments_in_attack_step1.md @@ -0,0 +1,8 @@ + +# Symbol Info +## **Meta comments** +- **developer**: "dev attack_step" +- **modeler**: "mod attack_step" +--- +## **Comments** +- attack_step comment diff --git a/tests/fixtures/markdown/comments_in_attack_step2.md b/tests/fixtures/markdown/comments_in_attack_step2.md new file mode 100644 index 0000000..308a443 --- /dev/null +++ b/tests/fixtures/markdown/comments_in_attack_step2.md @@ -0,0 +1,6 @@ + +# Symbol Info +## **Meta comments** +--- +## **Comments** +- attack_step comment2 diff --git a/tests/fixtures/markdown/comments_in_attack_step3.md b/tests/fixtures/markdown/comments_in_attack_step3.md new file mode 100644 index 0000000..fb93b8c --- /dev/null +++ b/tests/fixtures/markdown/comments_in_attack_step3.md @@ -0,0 +1,8 @@ + +# Symbol Info +## **Meta comments** +- **developer**: "dev attack_step_5" +- **modeler**: "mod attack_step_5" +--- +## **Comments** +- attack_step comment3 diff --git a/tests/fixtures/markdown/comments_in_category.md b/tests/fixtures/markdown/comments_in_category.md new file mode 100644 index 0000000..0ab1735 --- /dev/null +++ b/tests/fixtures/markdown/comments_in_category.md @@ -0,0 +1,8 @@ + +# Symbol Info +## **Meta comments** +- **developer**: "dev cat" +- **modeler**: "mod cat" +--- +## **Comments** +- category comment From bffa8382063c93d78ee096977cf38ea9fca46fce Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 17:05:48 +0200 Subject: [PATCH 16/21] fix: Linter errors. --- tests/integration/test_hover.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_hover.py b/tests/integration/test_hover.py index b9adbd4..32297fb 100644 --- a/tests/integration/test_hover.py +++ b/tests/integration/test_hover.py @@ -33,6 +33,7 @@ "comments_in_association2", ] + def sanitize_comment(comment: str): sanitized_comment = "" for line in comment: @@ -125,7 +126,9 @@ def make(position: (int, int)) -> io.BytesIO: return make -@pytest.mark.parametrize("location,markdown_file", zip(parameters,parameter_names), ids=parameter_names) +@pytest.mark.parametrize( + "location,markdown_file", zip(parameters, parameter_names), ids=parameter_names +) def test_hover( request: pytest.FixtureRequest, location: (int, int), From 3477ffc8b9307caf083fe3ad1ac37cc64f361e60 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 17:54:12 +0200 Subject: [PATCH 17/21] chore: Update markdown. --- src/malls/lsp/utils.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/malls/lsp/utils.py b/src/malls/lsp/utils.py index d6ef43d..dccaaa7 100644 --- a/src/malls/lsp/utils.py +++ b/src/malls/lsp/utils.py @@ -129,7 +129,7 @@ def get_completion_list(doc: Document, pos: Position) -> list: def build_markdown_meta_comments(meta_comments: list[Node]): - markdown = "## **Meta comments**\n" + markdown = "" for meta_comment in meta_comments: meta_id = meta_comment.child_by_field_name("id").text.decode() meta_info = meta_comment.child_by_field_name("info").text.decode() @@ -149,7 +149,7 @@ def sanitize_comment(comment: str): def build_markdown_comments(comments: list[Node]): - markdown = "## **Comments**\n" + markdown = "" for comment in comments: markdown += f"- {sanitize_comment(comment.text.decode())}\n" return markdown @@ -169,16 +169,19 @@ def get_hover_info(doc: Document, pos: Position, storage: dict) -> str: return "" # we can only find comments for identifiers # TODO write better hover info - markdown = "\n# Symbol Info\n" # get meta comments + meta_title = "## **Meta comments**\n" meta_comments = find_meta_comment_function(node, node.text, doc.uri, storage) - markdown += build_markdown_meta_comments(meta_comments) + meta_markdown = build_markdown_meta_comments(meta_comments) - markdown += "---\n" # get regular comments + comments_title = "## **Comments**\n" comments = find_comments_function(node, node.text, doc.uri, storage) - markdown += build_markdown_comments(comments) + comments_markdown = build_markdown_comments(comments) - return markdown + if meta_markdown and comments_markdown: + return meta_title+meta_markdown+"---\n"+comments_title+comments_markdown + + return meta_title+meta_markdown if meta_markdown else comments_title+comments_markdown From 275e05dd57bcbcf09af33fc94f6f1324777208d3 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 17:54:24 +0200 Subject: [PATCH 18/21] tests: Update markdown. --- tests/fixtures/markdown/comments_in_asset_1.md | 2 -- tests/fixtures/markdown/comments_in_asset_2.md | 4 ---- tests/fixtures/markdown/comments_in_asset_3.md | 2 -- tests/fixtures/markdown/comments_in_asset_4.md | 4 ---- tests/fixtures/markdown/comments_in_asset_5.md | 2 -- tests/fixtures/markdown/comments_in_association.md | 2 -- tests/fixtures/markdown/comments_in_association2.md | 4 ---- tests/fixtures/markdown/comments_in_attack_step1.md | 2 -- tests/fixtures/markdown/comments_in_attack_step2.md | 4 ---- tests/fixtures/markdown/comments_in_attack_step3.md | 2 -- tests/fixtures/markdown/comments_in_category.md | 2 -- 11 files changed, 30 deletions(-) diff --git a/tests/fixtures/markdown/comments_in_asset_1.md b/tests/fixtures/markdown/comments_in_asset_1.md index 2059488..bdf4a44 100644 --- a/tests/fixtures/markdown/comments_in_asset_1.md +++ b/tests/fixtures/markdown/comments_in_asset_1.md @@ -1,5 +1,3 @@ - -# Symbol Info ## **Meta comments** - **developer**: "dev asset" - **modeler**: "mod asset" diff --git a/tests/fixtures/markdown/comments_in_asset_2.md b/tests/fixtures/markdown/comments_in_asset_2.md index 704d33c..6c26ab7 100644 --- a/tests/fixtures/markdown/comments_in_asset_2.md +++ b/tests/fixtures/markdown/comments_in_asset_2.md @@ -1,6 +1,2 @@ - -# Symbol Info -## **Meta comments** ---- ## **Comments** - asset2 comment diff --git a/tests/fixtures/markdown/comments_in_asset_3.md b/tests/fixtures/markdown/comments_in_asset_3.md index 6480a0d..d37b21e 100644 --- a/tests/fixtures/markdown/comments_in_asset_3.md +++ b/tests/fixtures/markdown/comments_in_asset_3.md @@ -1,5 +1,3 @@ - -# Symbol Info ## **Meta comments** - **developer**: "dev asset3" - **modeler**: "mod asset3" diff --git a/tests/fixtures/markdown/comments_in_asset_4.md b/tests/fixtures/markdown/comments_in_asset_4.md index 4dacd96..1f58359 100644 --- a/tests/fixtures/markdown/comments_in_asset_4.md +++ b/tests/fixtures/markdown/comments_in_asset_4.md @@ -1,7 +1,3 @@ - -# Symbol Info ## **Meta comments** - **developer**: "dev asset4" - **modeler**: "mod asset4" ---- -## **Comments** diff --git a/tests/fixtures/markdown/comments_in_asset_5.md b/tests/fixtures/markdown/comments_in_asset_5.md index 783cf6a..6a4f9e8 100644 --- a/tests/fixtures/markdown/comments_in_asset_5.md +++ b/tests/fixtures/markdown/comments_in_asset_5.md @@ -1,5 +1,3 @@ - -# Symbol Info ## **Meta comments** - **developer**: "dev asset5" - **modeler**: "mod asset5" diff --git a/tests/fixtures/markdown/comments_in_association.md b/tests/fixtures/markdown/comments_in_association.md index 6603bde..fc48fef 100644 --- a/tests/fixtures/markdown/comments_in_association.md +++ b/tests/fixtures/markdown/comments_in_association.md @@ -1,5 +1,3 @@ - -# Symbol Info ## **Meta comments** - **developer**: "some info" --- diff --git a/tests/fixtures/markdown/comments_in_association2.md b/tests/fixtures/markdown/comments_in_association2.md index b32a34d..a12fa68 100644 --- a/tests/fixtures/markdown/comments_in_association2.md +++ b/tests/fixtures/markdown/comments_in_association2.md @@ -1,6 +1,2 @@ - -# Symbol Info -## **Meta comments** ---- ## **Comments** - association2 comment diff --git a/tests/fixtures/markdown/comments_in_attack_step1.md b/tests/fixtures/markdown/comments_in_attack_step1.md index ad1cf02..8576560 100644 --- a/tests/fixtures/markdown/comments_in_attack_step1.md +++ b/tests/fixtures/markdown/comments_in_attack_step1.md @@ -1,5 +1,3 @@ - -# Symbol Info ## **Meta comments** - **developer**: "dev attack_step" - **modeler**: "mod attack_step" diff --git a/tests/fixtures/markdown/comments_in_attack_step2.md b/tests/fixtures/markdown/comments_in_attack_step2.md index 308a443..3927889 100644 --- a/tests/fixtures/markdown/comments_in_attack_step2.md +++ b/tests/fixtures/markdown/comments_in_attack_step2.md @@ -1,6 +1,2 @@ - -# Symbol Info -## **Meta comments** ---- ## **Comments** - attack_step comment2 diff --git a/tests/fixtures/markdown/comments_in_attack_step3.md b/tests/fixtures/markdown/comments_in_attack_step3.md index fb93b8c..06edc15 100644 --- a/tests/fixtures/markdown/comments_in_attack_step3.md +++ b/tests/fixtures/markdown/comments_in_attack_step3.md @@ -1,5 +1,3 @@ - -# Symbol Info ## **Meta comments** - **developer**: "dev attack_step_5" - **modeler**: "mod attack_step_5" diff --git a/tests/fixtures/markdown/comments_in_category.md b/tests/fixtures/markdown/comments_in_category.md index 0ab1735..21a9c2f 100644 --- a/tests/fixtures/markdown/comments_in_category.md +++ b/tests/fixtures/markdown/comments_in_category.md @@ -1,5 +1,3 @@ - -# Symbol Info ## **Meta comments** - **developer**: "dev cat" - **modeler**: "mod cat" From 15b39788abbfd38570f3934f17c0406a342612a9 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 17:54:55 +0200 Subject: [PATCH 19/21] fix: Linter errors. --- src/malls/lsp/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/malls/lsp/utils.py b/src/malls/lsp/utils.py index dccaaa7..a58a156 100644 --- a/src/malls/lsp/utils.py +++ b/src/malls/lsp/utils.py @@ -175,13 +175,12 @@ def get_hover_info(doc: Document, pos: Position, storage: dict) -> str: meta_comments = find_meta_comment_function(node, node.text, doc.uri, storage) meta_markdown = build_markdown_meta_comments(meta_comments) - # get regular comments comments_title = "## **Comments**\n" comments = find_comments_function(node, node.text, doc.uri, storage) comments_markdown = build_markdown_comments(comments) if meta_markdown and comments_markdown: - return meta_title+meta_markdown+"---\n"+comments_title+comments_markdown + return meta_title + meta_markdown + "---\n" + comments_title + comments_markdown - return meta_title+meta_markdown if meta_markdown else comments_title+comments_markdown + return meta_title + meta_markdown if meta_markdown else comments_title + comments_markdown From 67d684d77cc3fd883a11d55fb416adee12ab4678 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 17:58:43 +0200 Subject: [PATCH 20/21] chore: Remove bullet points. --- src/malls/lsp/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/malls/lsp/utils.py b/src/malls/lsp/utils.py index a58a156..dd2da83 100644 --- a/src/malls/lsp/utils.py +++ b/src/malls/lsp/utils.py @@ -151,7 +151,7 @@ def sanitize_comment(comment: str): def build_markdown_comments(comments: list[Node]): markdown = "" for comment in comments: - markdown += f"- {sanitize_comment(comment.text.decode())}\n" + markdown += f"{sanitize_comment(comment.text.decode())}\n" return markdown From 1a68cb051678ddc85165a990948473c88d7bd2b3 Mon Sep 17 00:00:00 2001 From: Tomas Fonseca Date: Thu, 28 Aug 2025 17:58:54 +0200 Subject: [PATCH 21/21] tests: Remove bullet points. --- tests/fixtures/markdown/comments_in_asset_1.md | 2 +- tests/fixtures/markdown/comments_in_asset_2.md | 2 +- tests/fixtures/markdown/comments_in_asset_3.md | 2 +- tests/fixtures/markdown/comments_in_asset_5.md | 2 +- tests/fixtures/markdown/comments_in_association.md | 2 +- tests/fixtures/markdown/comments_in_association2.md | 2 +- tests/fixtures/markdown/comments_in_attack_step1.md | 2 +- tests/fixtures/markdown/comments_in_attack_step2.md | 2 +- tests/fixtures/markdown/comments_in_attack_step3.md | 2 +- tests/fixtures/markdown/comments_in_category.md | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/fixtures/markdown/comments_in_asset_1.md b/tests/fixtures/markdown/comments_in_asset_1.md index bdf4a44..4137472 100644 --- a/tests/fixtures/markdown/comments_in_asset_1.md +++ b/tests/fixtures/markdown/comments_in_asset_1.md @@ -3,4 +3,4 @@ - **modeler**: "mod asset" --- ## **Comments** -- asset1 comment + asset1 comment diff --git a/tests/fixtures/markdown/comments_in_asset_2.md b/tests/fixtures/markdown/comments_in_asset_2.md index 6c26ab7..e3efd00 100644 --- a/tests/fixtures/markdown/comments_in_asset_2.md +++ b/tests/fixtures/markdown/comments_in_asset_2.md @@ -1,2 +1,2 @@ ## **Comments** -- asset2 comment + asset2 comment diff --git a/tests/fixtures/markdown/comments_in_asset_3.md b/tests/fixtures/markdown/comments_in_asset_3.md index d37b21e..3f413cd 100644 --- a/tests/fixtures/markdown/comments_in_asset_3.md +++ b/tests/fixtures/markdown/comments_in_asset_3.md @@ -3,4 +3,4 @@ - **modeler**: "mod asset3" --- ## **Comments** -- asset3 comment + asset3 comment diff --git a/tests/fixtures/markdown/comments_in_asset_5.md b/tests/fixtures/markdown/comments_in_asset_5.md index 6a4f9e8..9a34141 100644 --- a/tests/fixtures/markdown/comments_in_asset_5.md +++ b/tests/fixtures/markdown/comments_in_asset_5.md @@ -3,4 +3,4 @@ - **modeler**: "mod asset5" --- ## **Comments** -- MULTI-LINE + MULTI-LINE diff --git a/tests/fixtures/markdown/comments_in_association.md b/tests/fixtures/markdown/comments_in_association.md index fc48fef..695ee13 100644 --- a/tests/fixtures/markdown/comments_in_association.md +++ b/tests/fixtures/markdown/comments_in_association.md @@ -2,4 +2,4 @@ - **developer**: "some info" --- ## **Comments** -- association1 comment + association1 comment diff --git a/tests/fixtures/markdown/comments_in_association2.md b/tests/fixtures/markdown/comments_in_association2.md index a12fa68..c00f84c 100644 --- a/tests/fixtures/markdown/comments_in_association2.md +++ b/tests/fixtures/markdown/comments_in_association2.md @@ -1,2 +1,2 @@ ## **Comments** -- association2 comment + association2 comment diff --git a/tests/fixtures/markdown/comments_in_attack_step1.md b/tests/fixtures/markdown/comments_in_attack_step1.md index 8576560..1b241c6 100644 --- a/tests/fixtures/markdown/comments_in_attack_step1.md +++ b/tests/fixtures/markdown/comments_in_attack_step1.md @@ -3,4 +3,4 @@ - **modeler**: "mod attack_step" --- ## **Comments** -- attack_step comment + attack_step comment diff --git a/tests/fixtures/markdown/comments_in_attack_step2.md b/tests/fixtures/markdown/comments_in_attack_step2.md index 3927889..64382e7 100644 --- a/tests/fixtures/markdown/comments_in_attack_step2.md +++ b/tests/fixtures/markdown/comments_in_attack_step2.md @@ -1,2 +1,2 @@ ## **Comments** -- attack_step comment2 + attack_step comment2 diff --git a/tests/fixtures/markdown/comments_in_attack_step3.md b/tests/fixtures/markdown/comments_in_attack_step3.md index 06edc15..04a7b36 100644 --- a/tests/fixtures/markdown/comments_in_attack_step3.md +++ b/tests/fixtures/markdown/comments_in_attack_step3.md @@ -3,4 +3,4 @@ - **modeler**: "mod attack_step_5" --- ## **Comments** -- attack_step comment3 + attack_step comment3 diff --git a/tests/fixtures/markdown/comments_in_category.md b/tests/fixtures/markdown/comments_in_category.md index 21a9c2f..4cd7f80 100644 --- a/tests/fixtures/markdown/comments_in_category.md +++ b/tests/fixtures/markdown/comments_in_category.md @@ -3,4 +3,4 @@ - **modeler**: "mod cat" --- ## **Comments** -- category comment + category comment