|  | 
|  | 1 | +from fortls.regex_patterns import ( | 
|  | 2 | +    DQ_STRING_REGEX, | 
|  | 3 | +    FIXED_COMMENT_LINE_MATCH, | 
|  | 4 | +    FREE_FORMAT_TEST, | 
|  | 5 | +    LINE_LABEL_REGEX, | 
|  | 6 | +    LOGICAL_REGEX, | 
|  | 7 | +    NAT_VAR_REGEX, | 
|  | 8 | +    NUMBER_REGEX, | 
|  | 9 | +    SQ_STRING_REGEX, | 
|  | 10 | +    WORD_REGEX, | 
|  | 11 | +) | 
|  | 12 | + | 
|  | 13 | + | 
|  | 14 | +def expand_name(line, char_poss): | 
|  | 15 | +    """Get full word containing given cursor position""" | 
|  | 16 | +    # The order here is important. | 
|  | 17 | +    # WORD will capture substrings in logical and strings | 
|  | 18 | +    regexs = [LOGICAL_REGEX, SQ_STRING_REGEX, DQ_STRING_REGEX, WORD_REGEX, NUMBER_REGEX] | 
|  | 19 | +    for r in regexs: | 
|  | 20 | +        for num_match in r.finditer(line): | 
|  | 21 | +            if num_match.start(0) <= char_poss and num_match.end(0) >= char_poss: | 
|  | 22 | +                return num_match.group(0) | 
|  | 23 | +    return "" | 
|  | 24 | + | 
|  | 25 | + | 
|  | 26 | +def detect_fixed_format(file_lines): | 
|  | 27 | +    """Detect fixed/free format by looking for characters in label columns | 
|  | 28 | +    and variable declarations before column 6. Treat intersection format | 
|  | 29 | +    files as free format.""" | 
|  | 30 | +    for line in file_lines: | 
|  | 31 | +        if FREE_FORMAT_TEST.match(line): | 
|  | 32 | +            return False | 
|  | 33 | +        tmp_match = NAT_VAR_REGEX.match(line) | 
|  | 34 | +        if tmp_match and tmp_match.start(1) < 6: | 
|  | 35 | +            return False | 
|  | 36 | +        # Trailing ampersand indicates free or intersection format | 
|  | 37 | +        if not FIXED_COMMENT_LINE_MATCH.match(line): | 
|  | 38 | +            line_end = line.split("!")[0].strip() | 
|  | 39 | +            if len(line_end) > 0 and line_end[-1] == "&": | 
|  | 40 | +                return False | 
|  | 41 | +    return True | 
|  | 42 | + | 
|  | 43 | + | 
|  | 44 | +def strip_line_label(line): | 
|  | 45 | +    """Strip leading numeric line label""" | 
|  | 46 | +    match = LINE_LABEL_REGEX.match(line) | 
|  | 47 | +    if match is None: | 
|  | 48 | +        return line, None | 
|  | 49 | +    else: | 
|  | 50 | +        line_label = match.group(1) | 
|  | 51 | +        out_str = line[: match.start(1)] + " " * len(line_label) + line[match.end(1) :] | 
|  | 52 | +        return out_str, line_label | 
|  | 53 | + | 
|  | 54 | + | 
|  | 55 | +def strip_strings(in_line, maintain_len=False): | 
|  | 56 | +    """String string literals from code line""" | 
|  | 57 | + | 
|  | 58 | +    def repl_sq(m): | 
|  | 59 | +        return "'{0}'".format(" " * (len(m.group()) - 2)) | 
|  | 60 | + | 
|  | 61 | +    def repl_dq(m): | 
|  | 62 | +        return '"{0}"'.format(" " * (len(m.group()) - 2)) | 
|  | 63 | + | 
|  | 64 | +    if maintain_len: | 
|  | 65 | +        out_line = SQ_STRING_REGEX.sub(repl_sq, in_line) | 
|  | 66 | +        out_line = DQ_STRING_REGEX.sub(repl_dq, out_line) | 
|  | 67 | +    else: | 
|  | 68 | +        out_line = SQ_STRING_REGEX.sub("", in_line) | 
|  | 69 | +        out_line = DQ_STRING_REGEX.sub("", out_line) | 
|  | 70 | +    return out_line | 
|  | 71 | + | 
|  | 72 | + | 
|  | 73 | +def separate_def_list(test_str): | 
|  | 74 | +    """Separate definition lists, skipping parenthesis and bracket groups | 
|  | 75 | +
 | 
|  | 76 | +    Examples: | 
|  | 77 | +      "var1, var2, var3" -> ["var1", "var2", "var3"] | 
|  | 78 | +      "var, init_var(3) = [1,2,3], array(3,3)" -> ["var", "init_var", "array"] | 
|  | 79 | +    """ | 
|  | 80 | +    stripped_str = strip_strings(test_str) | 
|  | 81 | +    paren_count = 0 | 
|  | 82 | +    def_list = [] | 
|  | 83 | +    curr_str = "" | 
|  | 84 | +    for char in stripped_str: | 
|  | 85 | +        if (char == "(") or (char == "["): | 
|  | 86 | +            paren_count += 1 | 
|  | 87 | +        elif (char == ")") or (char == "]"): | 
|  | 88 | +            paren_count -= 1 | 
|  | 89 | +        elif (char == ",") and (paren_count == 0): | 
|  | 90 | +            curr_str = curr_str.strip() | 
|  | 91 | +            if curr_str != "": | 
|  | 92 | +                def_list.append(curr_str) | 
|  | 93 | +                curr_str = "" | 
|  | 94 | +            elif (curr_str == "") and (len(def_list) == 0): | 
|  | 95 | +                return None | 
|  | 96 | +            continue | 
|  | 97 | +        curr_str += char | 
|  | 98 | +    curr_str = curr_str.strip() | 
|  | 99 | +    if curr_str != "": | 
|  | 100 | +        def_list.append(curr_str) | 
|  | 101 | +    return def_list | 
|  | 102 | + | 
|  | 103 | + | 
|  | 104 | +def find_word_in_line(line, word): | 
|  | 105 | +    """Find Fortran word in line""" | 
|  | 106 | +    i0 = -1 | 
|  | 107 | +    for poss_name in WORD_REGEX.finditer(line): | 
|  | 108 | +        if poss_name.group() == word: | 
|  | 109 | +            i0 = poss_name.start() | 
|  | 110 | +            break | 
|  | 111 | +    return i0, i0 + len(word) | 
|  | 112 | + | 
|  | 113 | + | 
|  | 114 | +def find_paren_match(test_str): | 
|  | 115 | +    """Find matching closing parenthesis by searching forward, | 
|  | 116 | +    returns -1 if no match is found""" | 
|  | 117 | +    paren_count = 1 | 
|  | 118 | +    ind = -1 | 
|  | 119 | +    for (i, char) in enumerate(test_str): | 
|  | 120 | +        if char == "(": | 
|  | 121 | +            paren_count += 1 | 
|  | 122 | +        elif char == ")": | 
|  | 123 | +            paren_count -= 1 | 
|  | 124 | +        if paren_count == 0: | 
|  | 125 | +            return i | 
|  | 126 | +    return ind | 
0 commit comments