1/**************************************************************************/
2/* gdscript_extend_parser.h */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#ifndef GDSCRIPT_EXTEND_PARSER_H
32#define GDSCRIPT_EXTEND_PARSER_H
33
34#include "../gdscript_parser.h"
35#include "godot_lsp.h"
36
37#include "core/variant/variant.h"
38
39#ifndef LINE_NUMBER_TO_INDEX
40#define LINE_NUMBER_TO_INDEX(p_line) ((p_line)-1)
41#endif
42#ifndef COLUMN_NUMBER_TO_INDEX
43#define COLUMN_NUMBER_TO_INDEX(p_column) ((p_column)-1)
44#endif
45
46#ifndef SYMBOL_SEPERATOR
47#define SYMBOL_SEPERATOR "::"
48#endif
49
50#ifndef JOIN_SYMBOLS
51#define JOIN_SYMBOLS(p_path, name) ((p_path) + SYMBOL_SEPERATOR + (name))
52#endif
53
54typedef HashMap<String, const lsp::DocumentSymbol *> ClassMembers;
55
56/**
57 * Represents a Position as used by GDScript Parser. Used for conversion to and from `lsp::Position`.
58 *
59 * Difference to `lsp::Position`:
60 * * Line & Char/column: 1-based
61 * * LSP: both 0-based
62 * * Tabs are expanded to columns using tab size (`text_editor/behavior/indent/size`).
63 * * LSP: tab is single char
64 *
65 * Example:
66 * ```gdscript
67 * →→var my_value = 42
68 * ```
69 * `_` is at:
70 * * Godot: `column=12`
71 * * using `indent/size=4`
72 * * Note: counting starts at `1`
73 * * LSP: `character=8`
74 * * Note: counting starts at `0`
75 */
76struct GodotPosition {
77 int line;
78 int column;
79
80 GodotPosition(int p_line, int p_column) :
81 line(p_line), column(p_column) {}
82
83 lsp::Position to_lsp(const Vector<String> &p_lines) const;
84 static GodotPosition from_lsp(const lsp::Position p_pos, const Vector<String> &p_lines);
85
86 bool operator==(const GodotPosition &p_other) const {
87 return line == p_other.line && column == p_other.column;
88 }
89
90 String to_string() const {
91 return vformat("(%d,%d)", line, column);
92 }
93};
94
95struct GodotRange {
96 GodotPosition start;
97 GodotPosition end;
98
99 GodotRange(GodotPosition p_start, GodotPosition p_end) :
100 start(p_start), end(p_end) {}
101
102 lsp::Range to_lsp(const Vector<String> &p_lines) const;
103 static GodotRange from_lsp(const lsp::Range &p_range, const Vector<String> &p_lines);
104
105 bool operator==(const GodotRange &p_other) const {
106 return start == p_other.start && end == p_other.end;
107 }
108
109 String to_string() const {
110 return vformat("[%s:%s]", start.to_string(), end.to_string());
111 }
112};
113
114class ExtendGDScriptParser : public GDScriptParser {
115 String path;
116 Vector<String> lines;
117
118 lsp::DocumentSymbol class_symbol;
119 Vector<lsp::Diagnostic> diagnostics;
120 List<lsp::DocumentLink> document_links;
121 ClassMembers members;
122 HashMap<String, ClassMembers> inner_classes;
123
124 lsp::Range range_of_node(const GDScriptParser::Node *p_node) const;
125
126 void update_diagnostics();
127
128 void update_symbols();
129 void update_document_links(const String &p_code);
130 void parse_class_symbol(const GDScriptParser::ClassNode *p_class, lsp::DocumentSymbol &r_symbol);
131 void parse_function_symbol(const GDScriptParser::FunctionNode *p_func, lsp::DocumentSymbol &r_symbol);
132
133 Dictionary dump_function_api(const GDScriptParser::FunctionNode *p_func) const;
134 Dictionary dump_class_api(const GDScriptParser::ClassNode *p_class) const;
135
136 const lsp::DocumentSymbol *search_symbol_defined_at_line(int p_line, const lsp::DocumentSymbol &p_parent, const String &p_symbol_name = "") const;
137
138 Array member_completions;
139
140public:
141 _FORCE_INLINE_ const String &get_path() const { return path; }
142 _FORCE_INLINE_ const Vector<String> &get_lines() const { return lines; }
143 _FORCE_INLINE_ const lsp::DocumentSymbol &get_symbols() const { return class_symbol; }
144 _FORCE_INLINE_ const Vector<lsp::Diagnostic> &get_diagnostics() const { return diagnostics; }
145 _FORCE_INLINE_ const ClassMembers &get_members() const { return members; }
146 _FORCE_INLINE_ const HashMap<String, ClassMembers> &get_inner_classes() const { return inner_classes; }
147
148 Error get_left_function_call(const lsp::Position &p_position, lsp::Position &r_func_pos, int &r_arg_index) const;
149
150 String get_text_for_completion(const lsp::Position &p_cursor) const;
151 String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_required = false) const;
152 String get_identifier_under_position(const lsp::Position &p_position, lsp::Range &r_range) const;
153 String get_uri() const;
154
155 /**
156 * `p_symbol_name` gets ignored if empty. Otherwise symbol must match passed in named.
157 *
158 * Necessary when multiple symbols at same line for example with `func`:
159 * `func handle_arg(arg: int):`
160 * -> Without `p_symbol_name`: returns `handle_arg`. Even if parameter (`arg`) is wanted.
161 * With `p_symbol_name`: symbol name MUST match `p_symbol_name`: returns `arg`.
162 */
163 const lsp::DocumentSymbol *get_symbol_defined_at_line(int p_line, const String &p_symbol_name = "") const;
164 const lsp::DocumentSymbol *get_member_symbol(const String &p_name, const String &p_subclass = "") const;
165 const List<lsp::DocumentLink> &get_document_links() const;
166
167 const Array &get_member_completions();
168 Dictionary generate_api() const;
169
170 Error parse(const String &p_code, const String &p_path);
171};
172
173#endif // GDSCRIPT_EXTEND_PARSER_H
174