8
8
import os
9
9
import re
10
10
import traceback
11
+ from multiprocessing import Pool
11
12
from pathlib import Path
12
13
13
14
from fortls .intrinsics import (
@@ -284,108 +285,11 @@ def serve_initialize(self, request):
284
285
params .get ("rootUri" ) or params .get ("rootPath" ) or ""
285
286
)
286
287
self .source_dirs .add (self .root_path )
287
- # Check for config file
288
- config_path = os .path .join (self .root_path , ".fortls" )
289
- config_exists = os .path .isfile (config_path )
290
- if config_exists :
291
- try :
292
- with open (config_path , "r" ) as jsonfile :
293
- # Allow for jsonc C-style commnets
294
- # jsondata = "".join(
295
- # line for line in jsonfile if not line.startswith("//")
296
- # )
297
- # config_dict = json.loads(jsondata)
298
- config_dict = json .load (jsonfile )
299
-
300
- # Exclude paths (directories & files)
301
- # with glob resolution
302
- for path in config_dict .get ("excl_paths" , []):
303
- self .excl_paths .update (set (resolve_globs (path , self .root_path )))
304
-
305
- # Source directory paths (directories)
306
- # with glob resolution
307
- source_dirs = config_dict .get ("source_dirs" , [])
308
- for path in source_dirs :
309
- self .source_dirs .update (
310
- set (
311
- only_dirs (
312
- resolve_globs (path , self .root_path ),
313
- self .post_messages ,
314
- )
315
- )
316
- )
317
- # Keep all directories present in source_dirs but not excl_paths
318
- self .source_dirs = {
319
- i for i in self .source_dirs if i not in self .excl_paths
320
- }
288
+ self .__config_logger (request )
289
+ self .__load_config_file ()
290
+ self .__load_intrinsics ()
291
+ self .__add_source_dirs ()
321
292
322
- self .excl_suffixes = config_dict .get ("excl_suffixes" , [])
323
- self .lowercase_intrinsics = config_dict .get (
324
- "lowercase_intrinsics" , self .lowercase_intrinsics
325
- )
326
- self .debug_log = config_dict .get ("debug_log" , self .debug_log )
327
- self .disable_diagnostics = config_dict .get (
328
- "disable_diagnostics" , self .disable_diagnostics
329
- )
330
- self .pp_suffixes = config_dict .get ("pp_suffixes" , None )
331
- self .pp_defs = config_dict .get ("pp_defs" , {})
332
- for path in config_dict .get ("include_dirs" , []):
333
- self .include_dirs .extend (
334
- only_dirs (
335
- resolve_globs (path , self .root_path ), self .post_messages
336
- )
337
- )
338
- self .max_line_length = config_dict .get (
339
- "max_line_length" , self .max_line_length
340
- )
341
- self .max_comment_line_length = config_dict .get (
342
- "max_comment_line_length" , self .max_comment_line_length
343
- )
344
- if isinstance (self .pp_defs , list ):
345
- self .pp_defs = {key : "" for key in self .pp_defs }
346
-
347
- except FileNotFoundError :
348
- msg = "Error settings file '.fortls' not found"
349
- self .post_messages .append ([1 , msg ])
350
- log .error (msg )
351
-
352
- except ValueError :
353
- msg = "Error while parsing '.fortls' settings file"
354
- self .post_messages .append ([1 , msg ])
355
- log .error (msg )
356
-
357
- # Setup logging
358
- if self .debug_log and (self .root_path != "" ):
359
- logging .basicConfig (
360
- filename = os .path .join (self .root_path , "fortls_debug.log" ),
361
- level = logging .DEBUG ,
362
- filemode = "w" ,
363
- )
364
- log .debug ("REQUEST %s %s" , request .get ("id" ), request .get ("method" ))
365
- self .post_messages .append ([3 , "FORTLS debugging enabled" ])
366
- # Load intrinsics
367
- set_keyword_ordering (True ) # Always sort intrinsics
368
- if self .lowercase_intrinsics :
369
- set_lowercase_intrinsics ()
370
- (
371
- self .statements ,
372
- self .keywords ,
373
- self .intrinsic_funs ,
374
- self .intrinsic_mods ,
375
- ) = load_intrinsics ()
376
- for module in self .intrinsic_mods :
377
- self .obj_tree [module .FQSN ] = [module , None ]
378
- # Set object settings
379
- set_keyword_ordering (self .sort_keywords )
380
- # Recursively add sub-directories that only match Fortran extensions
381
- if len (self .source_dirs ) == 1 :
382
- self .source_dirs = set ()
383
- for root , dirs , files in os .walk (self .root_path ):
384
- # Match not found
385
- if not list (filter (FORTRAN_EXT_REGEX .search , files )):
386
- continue
387
- if root not in self .source_dirs and root not in self .excl_paths :
388
- self .source_dirs .add (str (Path (root ).resolve ()))
389
293
# Initialize workspace
390
294
self .workspace_init ()
391
295
#
@@ -412,9 +316,6 @@ def serve_initialize(self, request):
412
316
if self .notify_init :
413
317
self .post_messages .append ([3 , "FORTLS initialization complete" ])
414
318
return {"capabilities" : server_capabilities }
415
- # "workspaceSymbolProvider": True,
416
- # "streaming": False,
417
- # }
418
319
419
320
def serve_workspace_symbol (self , request ):
420
321
def map_types (type ):
@@ -460,7 +361,7 @@ def serve_document_symbols(self, request):
460
361
def map_types (type , in_class = False ):
461
362
if type == 1 :
462
363
return 2
463
- elif ( type == 2 ) or ( type == 3 ):
364
+ elif type in ( 2 , 3 ):
464
365
if in_class :
465
366
return 6
466
367
else :
@@ -1526,27 +1427,9 @@ def update_workspace_file(
1526
1427
return True , None
1527
1428
1528
1429
def workspace_init (self ):
1529
- # Get filenames
1530
- file_list = []
1531
- for src_dir in self .source_dirs :
1532
- for f in os .listdir (src_dir ):
1533
- p = os .path .join (src_dir , f )
1534
- # Process only files
1535
- if not os .path .isfile (p ):
1536
- continue
1537
- # File extension must match supported extensions
1538
- if not FORTRAN_EXT_REGEX .search (f ):
1539
- continue
1540
- # File cannot be in excluded paths/files
1541
- if p in self .excl_paths :
1542
- continue
1543
- # File cannot have an excluded extension
1544
- if any (f .endswith (ext ) for ext in set (self .excl_suffixes )):
1545
- continue
1546
- file_list .append (p )
1547
- # Process files
1548
- from multiprocessing import Pool
1549
1430
1431
+ file_list = self .__get_source_files ()
1432
+ # Process files
1550
1433
pool = Pool (processes = self .nthreads )
1551
1434
results = {}
1552
1435
for filepath in file_list :
@@ -1588,6 +1471,165 @@ def serve_default(self, request):
1588
1471
code = - 32601 , message = "method {} not found" .format (request ["method" ])
1589
1472
)
1590
1473
1474
+ def __load_config_file (self ) -> None :
1475
+ """Loads the configuration file for the Language Server"""
1476
+
1477
+ # Check for config file
1478
+ config_fname = ".fortls"
1479
+ config_path = os .path .join (self .root_path , config_fname )
1480
+ if not os .path .isfile (config_path ):
1481
+ return None
1482
+
1483
+ try :
1484
+ with open (config_path , "r" ) as jsonfile :
1485
+ # Allow for jsonc C-style commnets
1486
+ # jsondata = "".join(
1487
+ # line for line in jsonfile if not line.startswith("//")
1488
+ # )
1489
+ # config_dict = json.loads(jsondata)
1490
+ config_dict = json .load (jsonfile )
1491
+
1492
+ # Exclude paths (directories & files)
1493
+ # with glob resolution
1494
+ for path in config_dict .get ("excl_paths" , []):
1495
+ self .excl_paths .update (set (resolve_globs (path , self .root_path )))
1496
+
1497
+ # Source directory paths (directories)
1498
+ # with glob resolution
1499
+ source_dirs = config_dict .get ("source_dirs" , [])
1500
+ for path in source_dirs :
1501
+ self .source_dirs .update (
1502
+ set (
1503
+ only_dirs (
1504
+ resolve_globs (path , self .root_path ),
1505
+ self .post_messages ,
1506
+ )
1507
+ )
1508
+ )
1509
+ # Keep all directories present in source_dirs but not excl_paths
1510
+ self .source_dirs = {
1511
+ i for i in self .source_dirs if i not in self .excl_paths
1512
+ }
1513
+
1514
+ self .excl_suffixes = config_dict .get ("excl_suffixes" , [])
1515
+ self .lowercase_intrinsics = config_dict .get (
1516
+ "lowercase_intrinsics" , self .lowercase_intrinsics
1517
+ )
1518
+ self .debug_log = config_dict .get ("debug_log" , self .debug_log )
1519
+ self .disable_diagnostics = config_dict .get (
1520
+ "disable_diagnostics" , self .disable_diagnostics
1521
+ )
1522
+ self .pp_suffixes = config_dict .get ("pp_suffixes" , None )
1523
+ self .pp_defs = config_dict .get ("pp_defs" , {})
1524
+ for path in config_dict .get ("include_dirs" , []):
1525
+ self .include_dirs .extend (
1526
+ only_dirs (
1527
+ resolve_globs (path , self .root_path ), self .post_messages
1528
+ )
1529
+ )
1530
+ self .max_line_length = config_dict .get (
1531
+ "max_line_length" , self .max_line_length
1532
+ )
1533
+ self .max_comment_line_length = config_dict .get (
1534
+ "max_comment_line_length" , self .max_comment_line_length
1535
+ )
1536
+ if isinstance (self .pp_defs , list ):
1537
+ self .pp_defs = {key : "" for key in self .pp_defs }
1538
+
1539
+ except FileNotFoundError :
1540
+ msg = f"Error settings file '{ config_fname } ' not found"
1541
+ self .post_messages .append ([1 , msg ])
1542
+ log .error (msg )
1543
+
1544
+ except ValueError :
1545
+ msg = f"Error while parsing '{ config_fname } ' settings file"
1546
+ self .post_messages .append ([1 , msg ])
1547
+ log .error (msg )
1548
+
1549
+ def __add_source_dirs (self ) -> None :
1550
+ """Will recursively add all subdirectories that contain Fortran
1551
+ source files only if the option `source_dirs` has not been specified
1552
+ in the configuration file or no configuration file is present
1553
+ """
1554
+ # Recursively add sub-directories that only match Fortran extensions
1555
+ if len (self .source_dirs ) != 1 :
1556
+ return None
1557
+ if self .root_path not in self .source_dirs :
1558
+ return None
1559
+ self .source_dirs = set ()
1560
+ for root , dirs , files in os .walk (self .root_path ):
1561
+ # Match not found
1562
+ if not list (filter (FORTRAN_EXT_REGEX .search , files )):
1563
+ continue
1564
+ if root not in self .source_dirs and root not in self .excl_paths :
1565
+ self .source_dirs .add (str (Path (root ).resolve ()))
1566
+
1567
+ def __get_source_files (self ) -> list [str ]:
1568
+ """Get all the source files present in `self.source_dirs`,
1569
+ exclude any files found in `self.excl_paths`^ and ignore
1570
+ any files ending with `self_excl_suffixes`.
1571
+
1572
+ ^: the only case where this has not allready happened is when
1573
+ `source_dirs` is not specified or a configuration file is not present
1574
+
1575
+ Returns
1576
+ -------
1577
+ list[str]
1578
+ List of source Fortran source files
1579
+ """
1580
+ # Get filenames
1581
+ file_list = []
1582
+ excl_suffixes = set (self .excl_suffixes )
1583
+ for src_dir in self .source_dirs :
1584
+ for f in os .listdir (src_dir ):
1585
+ p = os .path .join (src_dir , f )
1586
+ # Process only files
1587
+ if not os .path .isfile (p ):
1588
+ continue
1589
+ # File extension must match supported extensions
1590
+ if not FORTRAN_EXT_REGEX .search (f ):
1591
+ continue
1592
+ # File cannot be in excluded paths/files
1593
+ if p in self .excl_paths :
1594
+ continue
1595
+ # File cannot have an excluded extension
1596
+ if any (f .endswith (ext ) for ext in excl_suffixes ):
1597
+ continue
1598
+ file_list .append (p )
1599
+ return file_list
1600
+
1601
+ def __config_logger (self , request ) -> None :
1602
+ """Configures the logger to save Language Server requests/responses to a file
1603
+ the logger will by default output to the main (stderr, stdout) channels.
1604
+ """
1605
+
1606
+ if not self .debug_log :
1607
+ return None
1608
+ if not self .root_path :
1609
+ return None
1610
+
1611
+ fname = "fortls_debug.log"
1612
+ fname = os .path .join (self .root_path , fname )
1613
+ logging .basicConfig (filename = fname , level = logging .DEBUG , filemode = "w" )
1614
+ log .debug ("REQUEST %s %s" , request .get ("id" ), request .get ("method" ))
1615
+ self .post_messages .append ([3 , "FORTLS debugging enabled" ])
1616
+
1617
+ def __load_intrinsics (self ) -> None :
1618
+ # Load intrinsics
1619
+ set_keyword_ordering (True ) # Always sort intrinsics
1620
+ if self .lowercase_intrinsics :
1621
+ set_lowercase_intrinsics ()
1622
+ (
1623
+ self .statements ,
1624
+ self .keywords ,
1625
+ self .intrinsic_funs ,
1626
+ self .intrinsic_mods ,
1627
+ ) = load_intrinsics ()
1628
+ for module in self .intrinsic_mods :
1629
+ self .obj_tree [module .FQSN ] = [module , None ]
1630
+ # Set object settings
1631
+ set_keyword_ordering (self .sort_keywords )
1632
+
1591
1633
1592
1634
class JSONRPC2Error (Exception ):
1593
1635
def __init__ (self , code , message , data = None ):
0 commit comments