3
3
import os
4
4
from pathlib import Path
5
5
6
- from fortls .constants import KEYWORD_ID_DICT , KEYWORD_LIST , FRegex , log , sort_keywords
6
+ from fortls .constants import KEYWORD_ID_DICT , KEYWORD_LIST , FRegex , sort_keywords
7
7
from fortls .ftypes import Range
8
8
9
9
@@ -52,6 +52,22 @@ def detect_fixed_format(file_lines: list[str]) -> bool:
52
52
-------
53
53
bool
54
54
True if file_lines are of Fixed Fortran style
55
+
56
+ Examples
57
+ --------
58
+
59
+ >>> detect_fixed_format([' free format'])
60
+ False
61
+
62
+ >>> detect_fixed_format([' INTEGER, PARAMETER :: N = 10'])
63
+ False
64
+
65
+ >>> detect_fixed_format(['C Fixed format'])
66
+ True
67
+
68
+ Lines wih ampersands are not fixed format
69
+ >>> detect_fixed_format(['trailing line & ! comment'])
70
+ False
55
71
"""
56
72
for line in file_lines :
57
73
if FRegex .FREE_FORMAT_TEST .match (line ):
@@ -62,7 +78,7 @@ def detect_fixed_format(file_lines: list[str]) -> bool:
62
78
# Trailing ampersand indicates free or intersection format
63
79
if not FRegex .FIXED_COMMENT .match (line ):
64
80
line_end = line .split ("!" )[0 ].strip ()
65
- if len (line_end ) > 0 and line_end [ - 1 ] == "&" :
81
+ if len (line_end ) > 0 and line_end . endswith ( "&" ) :
66
82
return False
67
83
return True
68
84
@@ -139,18 +155,17 @@ def separate_def_list(test_str: str) -> list[str] | None:
139
155
>>> separate_def_list('var1, var2, var3')
140
156
['var1', 'var2', 'var3']
141
157
142
-
143
158
>>> separate_def_list('var, init_var(3) = [1,2,3], array(3,3)')
144
159
['var', 'init_var(3) = [1,2,3]', 'array(3,3)']
145
160
"""
146
161
stripped_str = strip_strings (test_str )
147
162
paren_count = 0
148
- def_list = []
163
+ def_list : list [ str ] = []
149
164
curr_str = ""
150
165
for char in stripped_str :
151
- if ( char == "(" ) or ( char == "[" ):
166
+ if char in ( "(" , "[" ):
152
167
paren_count += 1
153
- elif ( char == ")" ) or ( char == "]" ):
168
+ elif char in ( ")" , "]" ):
154
169
paren_count -= 1
155
170
elif (char == "," ) and (paren_count == 0 ):
156
171
curr_str = curr_str .strip ()
@@ -233,7 +248,9 @@ def find_paren_match(string: str) -> int:
233
248
return ind
234
249
235
250
236
- def get_line_prefix (pre_lines : list , curr_line : str , col : int , qs : bool = True ) -> str :
251
+ def get_line_prefix (
252
+ pre_lines : list [str ], curr_line : str , col : int , qs : bool = True
253
+ ) -> str :
237
254
"""Get code line prefix from current line and preceding continuation lines
238
255
239
256
Parameters
@@ -252,6 +269,11 @@ def get_line_prefix(pre_lines: list, curr_line: str, col: int, qs: bool = True)
252
269
-------
253
270
str
254
271
part of the line including any relevant line continuations before ``col``
272
+
273
+ Examples
274
+ --------
275
+ >>> get_line_prefix([''], '#pragma once', 0) is None
276
+ True
255
277
"""
256
278
if (curr_line is None ) or (col > len (curr_line )) or (curr_line .startswith ("#" )):
257
279
return None
@@ -293,47 +315,70 @@ def resolve_globs(glob_path: str, root_path: str = None) -> list[str]:
293
315
list[str]
294
316
Expanded glob patterns with absolute paths.
295
317
Absolute paths are used to resolve any potential ambiguity
318
+
319
+ Examples
320
+ --------
321
+
322
+ Relative to a root path
323
+ >>> import os, pathlib
324
+ >>> resolve_globs('test', os.getcwd()) == [str(pathlib.Path(os.getcwd()) / 'test')]
325
+ True
326
+
327
+ Absolute path resolution
328
+ >>> resolve_globs('test') == [str(pathlib.Path(os.getcwd()) / 'test')]
329
+ True
296
330
"""
297
331
# Resolve absolute paths i.e. not in our root_path
298
332
if os .path .isabs (glob_path ) or not root_path :
299
333
p = Path (glob_path ).resolve ()
300
- root = p .root
334
+ root = p .anchor # drive letter + root path
301
335
rel = str (p .relative_to (root )) # contains glob pattern
302
336
return [str (p .resolve ()) for p in Path (root ).glob (rel )]
303
337
else :
304
338
return [str (p .resolve ()) for p in Path (root_path ).resolve ().glob (glob_path )]
305
339
306
340
307
- def only_dirs (paths : list [str ], err_msg : list = [] ) -> list [str ]:
341
+ def only_dirs (paths : list [str ]) -> list [str ]:
308
342
"""From a list of strings returns only paths that are directories
309
343
310
344
Parameters
311
345
----------
312
346
paths : list[str]
313
347
A list containing the files and directories
314
- err_msg : list, optional
315
- A list to append error messages if any, else use log channel, by default []
316
348
317
349
Returns
318
350
-------
319
351
list[str]
320
352
A list containing only valid directories
353
+
354
+ Raises
355
+ ------
356
+ FileNotFoundError
357
+ A list containing all the non existing directories
358
+
359
+ Examples
360
+ --------
361
+
362
+ >>> only_dirs(['./test/', './test/test_source/', './test/test_source/test.f90'])
363
+ ['./test/', './test/test_source/']
364
+
365
+ >>> only_dirs(['/fake/dir/a', '/fake/dir/b', '/fake/dir/c'])
366
+ Traceback (most recent call last):
367
+ FileNotFoundError: /fake/dir/a
368
+ /fake/dir/b
369
+ /fake/dir/c
321
370
"""
322
371
dirs : list [str ] = []
372
+ errs : list [str ] = []
323
373
for p in paths :
324
374
if os .path .isdir (p ):
325
375
dirs .append (p )
326
376
elif os .path .isfile (p ):
327
377
continue
328
378
else :
329
- msg : str = (
330
- f"Directory '{ p } ' specified in Configuration settings file does not"
331
- " exist"
332
- )
333
- if err_msg :
334
- err_msg .append ([2 , msg ])
335
- else :
336
- log .warning (msg )
379
+ errs .append (p )
380
+ if errs :
381
+ raise FileNotFoundError ("\n " .join (errs ))
337
382
return dirs
338
383
339
384
@@ -446,6 +491,9 @@ def get_paren_level(line: str) -> tuple[str, list[Range]]:
446
491
>>> get_paren_level('CALL sub1(arg1(i),arg2')
447
492
('arg1,arg2', [Range(start=10, end=14), Range(start=17, end=22)])
448
493
494
+ >>> get_paren_level('')
495
+ ('', [Range(start=0, end=0)])
496
+
449
497
"""
450
498
if line == "" :
451
499
return "" , [Range (0 , 0 )]
@@ -460,18 +508,18 @@ def get_paren_level(line: str) -> tuple[str, list[Range]]:
460
508
if char == string_char :
461
509
in_string = False
462
510
continue
463
- if ( char == "(" ) or ( char == "[" ):
511
+ if char in ( "(" , "[" ):
464
512
level -= 1
465
513
if level == 0 :
466
514
i1 = i
467
515
elif level < 0 :
468
516
sections .append (Range (i + 1 , i1 ))
469
517
break
470
- elif ( char == ")" ) or ( char == "]" ):
518
+ elif char in ( ")" , "]" ):
471
519
level += 1
472
520
if level == 1 :
473
521
sections .append (Range (i + 1 , i1 ))
474
- elif ( char == "'" ) or ( char == '"' ):
522
+ elif char in ( "'" , '"' ):
475
523
in_string = True
476
524
string_char = char
477
525
if level == 0 :
@@ -508,6 +556,9 @@ def get_var_stack(line: str) -> list[str]:
508
556
509
557
>>> get_var_stack('CALL self%method(this%foo')
510
558
['this', 'foo']
559
+
560
+ >>> get_var_stack('')
561
+ ['']
511
562
"""
512
563
if len (line ) == 0 :
513
564
return ["" ]
0 commit comments