Skip to content

Commit c0495e6

Browse files
committed
feat(vscode): new keywords tree view in the explorer sidebar
1 parent 3fa09a7 commit c0495e6

File tree

6 files changed

+614
-55
lines changed

6 files changed

+614
-55
lines changed

package.json

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@
8383
}
8484
}
8585
},
86+
"views": {
87+
"explorer": [
88+
{
89+
"id": "robotcode.keywordsTreeView",
90+
"name": "Keywords",
91+
"contextualTitle": "Robot Framework",
92+
"icon": "icons/svg/robot_light.svg",
93+
"when": "resourceLangId == robotframework"
94+
}
95+
]
96+
},
8697
"tomlValidation": [
8798
{
8899
"fileMatch": "robot.toml",
@@ -335,7 +346,7 @@
335346
"args": {
336347
"text": " "
337348
},
338-
"when": "editorLangId == robotframework && editorTextFocus && config.robotcode.editor.4SpacesTab && !editorHasSelection && !inSnippetMode && !suggestWidgetVisible && !inlineSuggestionVisible"
349+
"when": "editorTextFocus && editorLangId == robotframework && config.robotcode.editor.4SpacesTab && !editorHasSelection && !inSnippetMode && !suggestWidgetVisible && !inlineSuggestionVisible"
339350
}
340351
],
341352
"configuration": [
@@ -807,6 +818,24 @@
807818
"title": "Select Execution Profiles",
808819
"category": "RobotCode",
809820
"command": "robotcode.selectExecutionProfiles"
821+
},
822+
{
823+
"title": "Refresh",
824+
"command": "robotcode.keywordsTreeView.refresh",
825+
"enablement": "resourceLangId == robotframework && view == robotcode.keywordsTreeView",
826+
"icon": "$(refresh)"
827+
},
828+
{
829+
"title": "Insert Keyword",
830+
"command": "robotcode.keywordsTreeView.insertKeyword",
831+
"enablement": "resourceLangId == robotframework && view == robotcode.keywordsTreeView",
832+
"icon": "$(insert)"
833+
},
834+
{
835+
"title": "Show Documentation",
836+
"command": "robotcode.keywordsTreeView.showDocumentation",
837+
"enablement": "resourceLangId == robotframework && view == robotcode.keywordsTreeView",
838+
"icon": "$(book)"
810839
}
811840
],
812841
"menus": {
@@ -833,6 +862,29 @@
833862
"group": "robotcode@2",
834863
"when": "resourceLangId == robotframework && resourceExtname == .robot && !isInDiffEditor"
835864
}
865+
],
866+
"view/title": [
867+
{
868+
"command": "robotcode.keywordsTreeView.refresh",
869+
"when": "view == robotcode.keywordsTreeView",
870+
"group": "navigation"
871+
}
872+
],
873+
"view/item/context": [
874+
{
875+
"command": "robotcode.keywordsTreeView.insertKeyword",
876+
"when": "view == robotcode.keywordsTreeView && viewItem == keyword",
877+
"group": "inline"
878+
},
879+
{
880+
"command": "robotcode.keywordsTreeView.showDocumentation",
881+
"when": "view == robotcode.keywordsTreeView",
882+
"group": "inline"
883+
},
884+
{
885+
"command": "robotcode.keywordsTreeView.showDocumentation",
886+
"when": "view == robotcode.keywordsTreeView"
887+
}
836888
]
837889
},
838890
"breakpoints": [
@@ -1343,4 +1395,4 @@
13431395
"webpack": "^5.89.0",
13441396
"webpack-cli": "^5.1.4"
13451397
}
1346-
}
1398+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
from dataclasses import dataclass
2+
from typing import TYPE_CHECKING, Any, List, Optional
3+
4+
from robotcode.core.lsp.types import TextDocumentIdentifier
5+
from robotcode.core.utils.dataclasses import CamelSnakeMixin
6+
from robotcode.core.utils.logging import LoggingDescriptor
7+
from robotcode.jsonrpc2.protocol import rpc_method
8+
from robotcode.robot.diagnostics.model_helper import ModelHelper
9+
10+
from .protocol_part import RobotLanguageServerProtocolPart
11+
12+
if TYPE_CHECKING:
13+
from ..protocol import RobotLanguageServerProtocol
14+
15+
16+
@dataclass(repr=False)
17+
class GetDocumentImportsParams(CamelSnakeMixin):
18+
text_document: TextDocumentIdentifier
19+
20+
21+
@dataclass(repr=False)
22+
class Keyword(CamelSnakeMixin):
23+
name: str
24+
id: Optional[str]
25+
signature: Optional[str] = None
26+
documentation: Optional[str] = None
27+
28+
29+
@dataclass(repr=False)
30+
class DocumentImport(CamelSnakeMixin):
31+
name: str
32+
alias: Optional[str]
33+
id: Optional[str]
34+
type: Optional[str]
35+
documentation: Optional[str] = None
36+
keywords: Optional[List[Keyword]] = None
37+
38+
39+
@dataclass(repr=False)
40+
class GetDocumentationUrl(CamelSnakeMixin):
41+
text_document: TextDocumentIdentifier
42+
import_id: Optional[str] = None
43+
keyword_id: Optional[str] = None
44+
45+
46+
class RobotKeywordsTreeViewPart(RobotLanguageServerProtocolPart, ModelHelper):
47+
_logger = LoggingDescriptor()
48+
49+
def __init__(self, parent: "RobotLanguageServerProtocol") -> None:
50+
super().__init__(parent)
51+
52+
@rpc_method(name="robot/keywordsview/getDocumentImports", param_type=GetDocumentImportsParams, threaded=True)
53+
@_logger.call
54+
async def _get_document_imports(
55+
self,
56+
text_document: TextDocumentIdentifier,
57+
*args: Any,
58+
**kwargs: Any,
59+
) -> Optional[List[DocumentImport]]:
60+
document = self.parent.documents.get(text_document.uri)
61+
if document is None:
62+
return None
63+
64+
namespace = self.parent.documents_cache.get_namespace(document)
65+
66+
result = []
67+
68+
for _k, v in namespace.get_libraries().items():
69+
result.append(
70+
DocumentImport(
71+
name=v.name,
72+
alias=v.alias,
73+
id=str(hash(v)),
74+
type="library",
75+
documentation=v.library_doc.to_markdown(add_signature=False),
76+
keywords=[
77+
Keyword(
78+
l.name,
79+
str(hash(l)),
80+
l.parameter_signature(),
81+
l.to_markdown(add_signature=False),
82+
)
83+
for l in v.library_doc.keywords.values()
84+
],
85+
)
86+
)
87+
for _k, v in namespace.get_resources().items():
88+
result.append(
89+
DocumentImport(
90+
name=v.name,
91+
alias=None,
92+
id=str(hash(v)),
93+
type="resource",
94+
documentation=v.library_doc.to_markdown(add_signature=False),
95+
keywords=[
96+
Keyword(l.name, str(hash(l)), l.parameter_signature(), l.to_markdown(add_signature=False))
97+
for l in v.library_doc.keywords.values()
98+
],
99+
)
100+
)
101+
102+
return result
103+
104+
@rpc_method(name="robot/keywordsview/getDocumentKeywords", param_type=GetDocumentImportsParams, threaded=True)
105+
@_logger.call
106+
async def _get_document_keywords(
107+
self,
108+
text_document: TextDocumentIdentifier,
109+
*args: Any,
110+
**kwargs: Any,
111+
) -> Optional[List[Keyword]]:
112+
document = self.parent.documents.get(text_document.uri)
113+
if document is None:
114+
return None
115+
116+
namespace = self.parent.documents_cache.get_namespace(document)
117+
118+
return [
119+
Keyword(l.name, str(hash(l)), l.parameter_signature(), l.to_markdown(add_signature=False))
120+
for l in namespace.get_library_doc().keywords.values()
121+
]
122+
123+
@rpc_method(name="robot/keywordsview/getDocumentationUrl", param_type=GetDocumentationUrl, threaded=True)
124+
@_logger.call
125+
async def _get_documentation_url(
126+
self,
127+
text_document: TextDocumentIdentifier,
128+
import_id: Optional[str] = None,
129+
keyword_id: Optional[str] = None,
130+
*args: Any,
131+
**kwargs: Any,
132+
) -> Optional[str]:
133+
document = self.parent.documents.get(text_document.uri)
134+
if document is None:
135+
return None
136+
137+
namespace = self.parent.documents_cache.get_namespace(document)
138+
139+
keyword_name = None
140+
141+
if import_id is None:
142+
if keyword_id is not None:
143+
keyword = next(
144+
(l for l in namespace.get_library_doc().keywords.values() if str(hash(l)) == keyword_id), None
145+
)
146+
if keyword is not None:
147+
keyword_name = keyword.name
148+
149+
return self.parent.robot_code_action_documentation.build_url(
150+
str(document.uri.to_path().name), (), document, namespace, keyword_name
151+
)
152+
153+
entry = next((l for l in namespace.get_libraries().values() if str(hash(l)) == import_id), None)
154+
if entry is None:
155+
entry = next((l for l in namespace.get_resources().values() if str(hash(l)) == import_id), None)
156+
157+
if keyword_id and entry is not None:
158+
keyword = next((l for l in entry.library_doc.keywords.values() if str(hash(l)) == keyword_id), None)
159+
if keyword is not None:
160+
keyword_name = keyword.name
161+
162+
if entry is not None:
163+
return self.parent.robot_code_action_documentation.build_url(
164+
entry.name, entry.args, document, namespace, keyword_name
165+
)
166+
167+
return None

packages/language_server/src/robotcode/language_server/robotframework/protocol.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from .parts.http_server import HttpServerProtocolPart
4343
from .parts.inlay_hint import RobotInlayHintProtocolPart
4444
from .parts.inline_value import RobotInlineValueProtocolPart
45+
from .parts.keywords_treeview import RobotKeywordsTreeViewPart
4546
from .parts.references import RobotReferencesProtocolPart
4647
from .parts.rename import RobotRenameProtocolPart
4748
from .parts.robocop_diagnostics import RobotRoboCopDiagnosticsProtocolPart
@@ -114,6 +115,7 @@ class RobotLanguageServerProtocol(LanguageServerProtocol):
114115
robot_code_action_refactor = ProtocolPartDescriptor(RobotCodeActionRefactorProtocolPart)
115116

116117
robot_debugging_utils = ProtocolPartDescriptor(RobotDebuggingUtilsProtocolPart)
118+
robot_keywords_treeview = ProtocolPartDescriptor(RobotKeywordsTreeViewPart)
117119

118120
http_server = ProtocolPartDescriptor(HttpServerProtocolPart)
119121

vscode-client/extension.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { DebugManager } from "./debugmanager";
33
import { LanguageClientsManager } from "./languageclientsmanger";
44
import { PythonManager } from "./pythonmanger";
55
import { TestControllerManager } from "./testcontrollermanager";
6+
import { KeywordsTreeViewProvider } from "./keywordsTreeViewProvider";
67

78
class TerminalLink extends vscode.TerminalLink {
89
constructor(
@@ -112,6 +113,10 @@ export async function activateAsync(context: vscode.ExtensionContext): Promise<v
112113
await languageClientManger.openUriInDocumentationView(vscode.Uri.file(link.path));
113114
},
114115
}),
116+
vscode.window.registerTreeDataProvider(
117+
"robotcode.keywordsTreeView",
118+
new KeywordsTreeViewProvider(languageClientManger, outputChannel),
119+
),
115120
);
116121

117122
await languageClientManger.refresh();

0 commit comments

Comments
 (0)