Skip to content

Commit 9e73460

Browse files
committed
Simplifies self.serve_initialise
1 parent 64d7273 commit 9e73460

File tree

1 file changed

+167
-125
lines changed

1 file changed

+167
-125
lines changed

fortls/langserver.py

Lines changed: 167 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import os
99
import re
1010
import traceback
11+
from multiprocessing import Pool
1112
from pathlib import Path
1213

1314
from fortls.intrinsics import (
@@ -284,108 +285,11 @@ def serve_initialize(self, request):
284285
params.get("rootUri") or params.get("rootPath") or ""
285286
)
286287
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()
321292

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()))
389293
# Initialize workspace
390294
self.workspace_init()
391295
#
@@ -412,9 +316,6 @@ def serve_initialize(self, request):
412316
if self.notify_init:
413317
self.post_messages.append([3, "FORTLS initialization complete"])
414318
return {"capabilities": server_capabilities}
415-
# "workspaceSymbolProvider": True,
416-
# "streaming": False,
417-
# }
418319

419320
def serve_workspace_symbol(self, request):
420321
def map_types(type):
@@ -460,7 +361,7 @@ def serve_document_symbols(self, request):
460361
def map_types(type, in_class=False):
461362
if type == 1:
462363
return 2
463-
elif (type == 2) or (type == 3):
364+
elif type in (2, 3):
464365
if in_class:
465366
return 6
466367
else:
@@ -1526,27 +1427,9 @@ def update_workspace_file(
15261427
return True, None
15271428

15281429
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
15491430

1431+
file_list = self.__get_source_files()
1432+
# Process files
15501433
pool = Pool(processes=self.nthreads)
15511434
results = {}
15521435
for filepath in file_list:
@@ -1588,6 +1471,165 @@ def serve_default(self, request):
15881471
code=-32601, message="method {} not found".format(request["method"])
15891472
)
15901473

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+
15911633

15921634
class JSONRPC2Error(Exception):
15931635
def __init__(self, code, message, data=None):

0 commit comments

Comments
 (0)