diff --git a/hdl_checker/base_server.py b/hdl_checker/base_server.py index bf12af5..341b6b4 100644 --- a/hdl_checker/base_server.py +++ b/hdl_checker/base_server.py @@ -55,6 +55,7 @@ from hdl_checker.static_check import getStaticMessages from hdl_checker.types import ( BuildFlagScope, + ConfigFileOrigin, RebuildInfo, RebuildLibraryUnit, RebuildPath, @@ -91,7 +92,9 @@ else: JSONDecodeError = json.decoder.JSONDecodeError -WatchedFile = NamedTuple("WatchedFile", (("path", Path), ("last_read", float))) +WatchedFile = NamedTuple( + "WatchedFile", (("path", Path), ("last_read", float), ("origin", ConfigFileOrigin)) +) class BaseServer(object): # pylint: disable=useless-object-inheritance @@ -159,8 +162,8 @@ def _clearLruCaches(self): for meth in self._cached_methods: meth.cache_clear() - def setConfig(self, filename): - # type: (Union[Path, str]) -> None + def setConfig(self, filename, origin): + # type: (Union[Path, str], ConfigFileOrigin) -> None """ Sets the configuration file. Calling this method will only trigger a configuration update if the given file name is different what was @@ -177,7 +180,7 @@ def setConfig(self, filename): else: return - self.config_file = WatchedFile(path, mtime) + self.config_file = WatchedFile(path, mtime, origin) _logger.debug("Set config to %s", self.config_file) def _updateConfigIfNeeded(self): @@ -199,14 +202,17 @@ def _updateConfigIfNeeded(self): if self.config_file.last_read >= file_mtime: return - # Don't let the user hanging if adding sources is taking too long. Also - # need to notify when adding is done. + # Don't let the user hanging if adding sources is taking too long + # when there's no config file. Also need to notify when adding is + # done. timer = Timer( _HOW_LONG_IS_TOO_LONG, self._handleUiInfo, args=(_HOW_LONG_IS_TOO_LONG_MSG,), ) - timer.start() + + if self.config_file.origin is ConfigFileOrigin.generated: + timer.start() try: config = json.load(open(str(self.config_file.path))) @@ -215,7 +221,9 @@ def _updateConfigIfNeeded(self): self.configure(config) - self.config_file = WatchedFile(self.config_file.path, file_mtime) + self.config_file = WatchedFile( + self.config_file.path, file_mtime, self.config_file.origin + ) _logger.debug("Updated config file to %s", self.config_file) timer.cancel() @@ -362,7 +370,9 @@ def _setupIfNeeded(self): return # Force config file out of to date to trigger reparsing - self.config_file = WatchedFile(self.config_file.path, 0) + self.config_file = WatchedFile( + self.config_file.path, 0, self.config_file.origin + ) def clean(self): # type: (...) -> Any diff --git a/hdl_checker/handlers.py b/hdl_checker/handlers.py index a86fd86..5977add 100644 --- a/hdl_checker/handlers.py +++ b/hdl_checker/handlers.py @@ -27,10 +27,10 @@ import bottle # type: ignore from hdl_checker import __version__ as version -from hdl_checker import types as t # pylint: disable=unused-import from hdl_checker.base_server import BaseServer from hdl_checker.builders.fallback import Fallback from hdl_checker.path import Path +from hdl_checker.types import ConfigFileOrigin from hdl_checker.utils import terminateProcess _logger = logging.getLogger(__name__) @@ -88,7 +88,7 @@ def _getServerByProjectFile(project_file): try: project = Server(root_dir=root_dir) if project_file is not None: - project.setConfig(project_file) + project.setConfig(project_file, origin=ConfigFileOrigin.user) _logger.debug("Created new project server for '%s'", project_file) except (IOError, OSError): _logger.info("Failed to create checker, reverting to fallback") diff --git a/hdl_checker/lsp.py b/hdl_checker/lsp.py index 26d6e4a..f544797 100644 --- a/hdl_checker/lsp.py +++ b/hdl_checker/lsp.py @@ -24,7 +24,6 @@ from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Union import six - from pyls import lsp as defines # type: ignore from pyls._utils import debounce # type: ignore from pyls.python_ls import PythonLanguageServer # type: ignore @@ -44,7 +43,7 @@ tAnyDesignUnit, ) from hdl_checker.path import Path, TemporaryPath -from hdl_checker.types import Location, MarkupKind +from hdl_checker.types import ConfigFileOrigin, Location, MarkupKind from hdl_checker.utils import getTemporaryFilename, logCalls, onNewReleaseFound _logger = logging.getLogger(__name__) @@ -251,7 +250,7 @@ def _onConfigUpdate(self, options): path = self._getProjectFilePath(options) try: - self.checker.setConfig(path) + self.checker.setConfig(path, origin=ConfigFileOrigin.user) return except UnknownParameterError as exc: _logger.info("Failed to read config from %s: %s", path, exc) @@ -274,7 +273,7 @@ def _onConfigUpdate(self, options): # Write this to a file and tell the server to use it auto_project_file = getTemporaryFilename(AUTO_PROJECT_FILE_NAME) json.dump(config, open(auto_project_file, "w")) - self.checker.setConfig(auto_project_file) + self.checker.setConfig(auto_project_file, origin=ConfigFileOrigin.generated) def _getProjectFilePath(self, options=None): # type: (...) -> str diff --git a/hdl_checker/tests/test_base_server.py b/hdl_checker/tests/test_base_server.py index 739a4a6..83351dd 100644 --- a/hdl_checker/tests/test_base_server.py +++ b/hdl_checker/tests/test_base_server.py @@ -65,6 +65,7 @@ from hdl_checker.path import Path from hdl_checker.types import ( BuildFlagScope, + ConfigFileOrigin, FileType, Location, RebuildLibraryUnit, @@ -141,7 +142,7 @@ def test(handle_ui_info): open(source, "w").write("") project = DummyServer(_Path(path)) - project.setConfig(Path(config)) + project.setConfig(_Path(config), origin=ConfigFileOrigin.generated) # Get messages of anything to trigger reading the config project.getMessagesByPath(Path(source)) @@ -151,6 +152,34 @@ def test(handle_ui_info): removeIfExists(path) + @it.should( # type: ignore + "not warn when setup is taking too long if the user provides the config file" + ) + @patch("hdl_checker.base_server._HOW_LONG_IS_TOO_LONG", 0.1) + @patch.object( + hdl_checker.base_server.BaseServer, "configure", lambda *_: time.sleep(0.5) + ) + @patch("hdl_checker.tests.DummyServer._handleUiInfo") + def test(handle_ui_info): + + path = tempfile.mkdtemp() + + config = p.join(path, "config.json") + source = p.join(path, "source.vhd") + + # Make sure the files exist + open(config, "w").write("") + open(source, "w").write("") + + project = DummyServer(_Path(path)) + project.setConfig(Path(config), origin=ConfigFileOrigin.user) + # Get messages of anything to trigger reading the config + project.getMessagesByPath(Path(source)) + + handle_ui_info.assert_not_called() + + removeIfExists(path) + @it.should( # type: ignore "not warn when setup takes less than _HOW_LONG_IS_TOO_LONG" ) @@ -166,7 +195,7 @@ def test(handle_ui_info): open(source, "w").write("") project = DummyServer(_Path(path)) - project.setConfig(Path(config)) + project.setConfig(Path(config), origin=ConfigFileOrigin.user) # Get messages of anything to trigger reading the config project.getMessagesByPath(Path(source)) @@ -220,7 +249,7 @@ def setup(): def test(): project = DummyServer(_Path("nonexisting")) with it.assertRaises(FileNotFoundError): - project.setConfig(str(it.project_file)) + project.setConfig(str(it.project_file), origin=ConfigFileOrigin.user) with it.having("no project file at all"): @@ -308,7 +337,7 @@ def setup(): } ) - it.project.setConfig(it.config_file) + it.project.setConfig(it.config_file, origin=ConfigFileOrigin.user) @it.should("use MockBuilder builder") # type: ignore def test(): @@ -344,7 +373,7 @@ def test(): "hdl_checker.base_server.WatchedFile.__init__", side_effect=[None] ) as watched_file: old = it.project.config_file - it.project.setConfig(it.config_file) + it.project.setConfig(it.config_file, origin=ConfigFileOrigin.user) it.project._updateConfigIfNeeded() it.assertEqual(it.project.config_file, old) watched_file.assert_not_called() @@ -430,7 +459,10 @@ def test(handle_ui_warning): @patch("hdl_checker.base_server.json.dump") def test(_): with PatchBuilder(): - it.project.setConfig(Path(p.join(TEST_PROJECT, "vimhdl.prj"))) + it.project.setConfig( + Path(p.join(TEST_PROJECT, "vimhdl.prj")), + origin=ConfigFileOrigin.user, + ) it.project._updateConfigIfNeeded() entity_a = _SourceMock( @@ -614,7 +646,10 @@ def setup(handle_ui_info): with PatchBuilder(): it.project = DummyServer(_Path(TEST_TEMP_PATH)) - it.project.setConfig(Path(p.join(TEST_PROJECT, "vimhdl.prj"))) + it.project.setConfig( + Path(p.join(TEST_PROJECT, "vimhdl.prj")), + origin=ConfigFileOrigin.user, + ) it.project._updateConfigIfNeeded() handle_ui_info.assert_called_once_with("Added 10 sources") diff --git a/hdl_checker/types.py b/hdl_checker/types.py index dcd25c3..cae7f84 100644 --- a/hdl_checker/types.py +++ b/hdl_checker/types.py @@ -98,3 +98,8 @@ class MarkupKind(Enum): # A location range within a source file Range = NamedTuple("Range", (("start", Location), ("end", Optional[Location]))) + +class ConfigFileOrigin(str, Enum): + "Specifies tracked design unit types" + user = "user" + generated = "generated"