File: function.py

package info (click to toggle)
fortran-language-server 3.2.2%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,268 kB
  • sloc: python: 9,688; f90: 1,195; fortran: 30; makefile: 28; ansic: 20
file content (146 lines) | stat: -rw-r--r-- 5,596 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
from __future__ import annotations

from typing import TYPE_CHECKING

from fortls.constants import FUNCTION_TYPE_ID
from fortls.helper_functions import get_keywords

from .subroutine import Subroutine

if TYPE_CHECKING:
    from .ast import FortranAST
    from .variable import Variable


class Function(Subroutine):
    def __init__(
        self,
        file_ast: FortranAST,
        line_number: int,
        name: str,
        args: str = "",
        mod_flag: bool = False,
        keywords: list = None,
        keyword_info: dict = None,
        result_type: str = None,
        result_name: str = None,
    ):
        super().__init__(file_ast, line_number, name, args, mod_flag, keywords)
        self.args: str = args.replace(" ", "").lower()
        self.args_snip: str = self.args
        self.arg_objs: list = []
        self.in_children: list = []
        self.missing_args: list = []
        self.mod_scope: bool = mod_flag
        self.result_name: str = result_name
        self.result_type: str = result_type
        self.result_obj: Variable = None
        self.keyword_info: dict = keyword_info
        # Set the implicit result() name to be the function name
        if self.result_name is None:
            self.result_name = self.name
        # Used in Associated blocks
        if self.keyword_info is None:
            self.keyword_info = {}

    def copy_interface(self, copy_source: Function):
        # Call the parent class method
        child_names = super().copy_interface(copy_source)
        # Return specific options
        self.result_name = copy_source.result_name
        self.result_type = copy_source.result_type
        self.result_obj = copy_source.result_obj
        if (
            copy_source.result_obj is not None
            and copy_source.result_obj.name.lower() not in child_names
        ):
            self.in_children.append(copy_source.result_obj)

    def resolve_link(self, obj_tree):
        self.resolve_arg_link(obj_tree)
        result_var_lower = self.result_name.lower()
        for child in self.children:
            if child.name.lower() == result_var_lower:
                self.result_obj = child
                # Update result value and type
                self.result_name = child.name
                self.result_type = child.get_desc()

    def get_type(self, no_link=False):
        return FUNCTION_TYPE_ID

    def get_desc(self):
        token = "FUNCTION"
        return f"{self.result_type} {token}" if self.result_type else token

    def is_callable(self):
        return False

    def get_hover(self, long: bool = False, drop_arg: int = -1) -> tuple[str, str]:
        """Construct the hover message for a FUNCTION.
        Two forms are produced here the `long` i.e. the normal for hover requests

        [MODIFIERS] FUNCTION NAME([ARGS]) RESULT(RESULT_VAR)
          TYPE, [ARG_MODIFIERS] :: [ARGS]
          TYPE, [RESULT_MODIFIERS] :: RESULT_VAR

        note: intrinsic functions will display slightly different,
        `RESULT_VAR` and its `TYPE` might not always be present

        short form, used when functions are arguments in functions and subroutines:

        FUNCTION NAME([ARGS]) :: ARG_LIST_NAME

        Parameters
        ----------
        long : bool, optional
            toggle between long and short hover results, by default False
        drop_arg : int, optional
            Ignore argument at position `drop_arg` in the argument list, by default -1

        Returns
        -------
        tuple[str, bool]
            String representative of the hover message and the `long` flag used
        """
        fun_sig, _ = self.get_snippet(drop_arg=drop_arg)
        # short hover messages do not include the result()
        fun_sig += f" RESULT({self.result_name})" if long else ""
        keyword_list = get_keywords(self.keywords)
        keyword_list.append("FUNCTION")

        hover_array = [f"{' '.join(keyword_list)} {fun_sig}"]
        hover_array, docs = self.get_docs_full(hover_array, long, drop_arg)
        # Only append the return value if using long form
        if self.result_obj and long:
            # Parse the documentation from the result variable
            arg_doc, doc_str = self.result_obj.get_hover()
            if doc_str is not None:
                docs.append(f"\n**Return:**  \n`{self.result_obj.name}`{doc_str}")
            hover_array.append(arg_doc)
        # intrinsic functions, where the return type is missing but can be inferred
        elif self.result_type and long:
            # prepend type to function signature
            hover_array[0] = f"{self.result_type} {hover_array[0]}"
        return "\n ".join(hover_array), "  \n".join(docs)

    # TODO: fix this
    def get_interface(self, name_replace=None, drop_arg=-1, change_strings=None):
        fun_sig, _ = self.get_snippet(name_replace=name_replace)
        fun_sig += f" RESULT({self.result_name})"
        # XXX:
        keyword_list = []
        if self.result_type:
            keyword_list.append(self.result_type)
        keyword_list += get_keywords(self.keywords)
        keyword_list.append("FUNCTION ")

        interface_array = self.get_interface_array(
            keyword_list, fun_sig, drop_arg, change_strings
        )
        if self.result_obj is not None:
            arg_doc, docs = self.result_obj.get_hover()
            interface_array.append(f"{arg_doc} :: {self.result_obj.name}")
        name = name_replace if name_replace is not None else self.name
        interface_array.append(f"END FUNCTION {name}")
        return "\n".join(interface_array)