From 45554623f8ba9413c6abb3ebaa447667f0c63817 Mon Sep 17 00:00:00 2001 From: MischaPanch Date: Tue, 28 Oct 2025 11:15:10 +0100 Subject: [PATCH 1/4] Dashboard: added section displaying memories, show path to mode/context on hover [ci skip] --- src/serena/dashboard.py | 12 ++++- src/serena/resources/dashboard/dashboard.js | 50 ++++++++++++++++----- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/serena/dashboard.py b/src/serena/dashboard.py index 0468e0fa..7f90d1e2 100644 --- a/src/serena/dashboard.py +++ b/src/serena/dashboard.py @@ -49,6 +49,7 @@ class ResponseConfigOverview(BaseModel): available_tools: list[dict[str, str | bool]] available_modes: list[dict[str, str | bool]] available_contexts: list[dict[str, str | bool]] + available_memories: list[str] | None class SerenaDashboardAPI: @@ -162,11 +163,12 @@ class SerenaDashboardAPI: context_info = { "name": context.name, "description": context.description, + "path": SerenaAgentContext.get_path(context.name), } # Get active modes modes = self._agent.get_active_modes() - modes_info = [{"name": mode.name, "description": mode.description} for mode in modes] + modes_info = [{"name": mode.name, "description": mode.description, "path": SerenaAgentMode.get_path(mode.name)} for mode in modes] active_mode_names = [mode.name for mode in modes] # Get active tools @@ -203,6 +205,7 @@ class SerenaDashboardAPI: { "name": mode_name, "is_active": mode_name in active_mode_names, + "path": SerenaAgentMode.get_path(mode_name), } ) @@ -214,6 +217,7 @@ class SerenaDashboardAPI: { "name": context_name, "is_active": context_name == context.name, + "path": SerenaAgentContext.get_path(context_name), } ) @@ -223,6 +227,11 @@ class SerenaDashboardAPI: full_stats = self._tool_usage_stats.get_tool_stats_dict() tool_stats_summary = {name: {"num_calls": stats["num_times_called"]} for name, stats in full_stats.items()} + # Get available memories if ReadMemoryTool is active + available_memories = None + if self._agent.tool_is_active("read_memory") and project is not None: + available_memories = project.memories_manager.list_memories() + return ResponseConfigOverview( active_project=project_info, context=context_info, @@ -233,6 +242,7 @@ class SerenaDashboardAPI: available_tools=available_tools, available_modes=available_modes, available_contexts=available_contexts, + available_memories=available_memories, ) def _shutdown(self) -> None: diff --git a/src/serena/resources/dashboard/dashboard.js b/src/serena/resources/dashboard/dashboard.js index b1c53bc3..1c5a34b8 100644 --- a/src/serena/resources/dashboard/dashboard.js +++ b/src/serena/resources/dashboard/dashboard.js @@ -210,9 +210,11 @@ class Dashboard { } displayConfig(config) { - // Check if tools section is currently expanded - const $existingContent = $('#tools-content'); - const wasExpanded = $existingContent.is(':visible'); + // Check if tools and memories sections are currently expanded + const $existingToolsContent = $('#tools-content'); + const $existingMemoriesContent = $('#memories-content'); + const wasToolsExpanded = $existingToolsContent.is(':visible'); + const wasMemoriesExpanded = $existingMemoriesContent.is(':visible'); let html = '
'; @@ -225,17 +227,16 @@ class Dashboard { // Context info html += '
Context:
'; - html += '
' + config.context.name + '
'; + html += '
' + config.context.name + '
'; // Modes info html += '
Active Modes:
'; html += '
'; if (config.modes.length > 0) { - html += '
    '; - config.modes.forEach(function(mode) { - html += '
  • ' + mode.name + '
  • '; + const modeSpans = config.modes.map(function(mode) { + return '' + mode.name + ''; }); - html += '
'; + html += modeSpans.join(', '); } else { html += 'None'; } @@ -247,15 +248,30 @@ class Dashboard { html += '
'; html += '

'; html += 'Active Tools (' + config.active_tools.length + ')'; - html += '▼'; + html += '▼'; html += '

'; - html += '
'; + html += '
'; config.active_tools.forEach(function(tool) { html += '
' + tool + '
'; }); html += '
'; html += '
'; + // Available memories - collapsible (only show if memories exist) + if (config.available_memories && config.available_memories.length > 0) { + html += '
'; + html += '

'; + html += 'Available Memories (' + config.available_memories.length + ')'; + html += '▼'; + html += '

'; + html += '
'; + config.available_memories.forEach(function(memory) { + html += '
' + memory + '
'; + }); + html += '
'; + html += '
'; + } + this.$configDisplay.html(html); // Re-attach collapsible handler for the newly created tools header @@ -267,6 +283,16 @@ class Dashboard { $content.slideToggle(300); $icon.toggleClass('expanded'); }); + + // Re-attach collapsible handler for the newly created memories header + $('#memories-header').click(function() { + const $header = $(this); + const $content = $('#memories-content'); + const $icon = $header.find('.toggle-icon'); + + $content.slideToggle(300); + $icon.toggleClass('expanded'); + }); } displayBasicStats(stats) { @@ -340,7 +366,7 @@ class Dashboard { let html = ''; modes.forEach(function(mode) { const activeClass = mode.is_active ? ' active' : ''; - html += '
' + mode.name + '
'; + html += '
' + mode.name + '
'; }); this.$availableModesDisplay.html(html); @@ -355,7 +381,7 @@ class Dashboard { let html = ''; contexts.forEach(function(context) { const activeClass = context.is_active ? ' active' : ''; - html += '
' + context.name + '
'; + html += '
' + context.name + '
'; }); this.$availableContextsDisplay.html(html); From a49d9a588b1b97a5e2e404117efcfb311e283c19 Mon Sep 17 00:00:00 2001 From: mengjiechen Date: Mon, 27 Oct 2025 16:46:04 +0800 Subject: [PATCH 2/4] feat: Support user's Maven and Gradle configuration files in JDTLS Add detection and usage of user's custom Maven and Gradle configuration files to allow JDTLS to respect user-specific build tool settings. Changes: - Detect and use ~/.m2/settings.xml for Maven user settings - Detect and use ~/.gradle for Gradle user home directory - Set Maven updateSnapshots to false for better performance This enhancement allows JDTLS to: - Use custom Maven repositories and mirrors - Respect proxy and authentication configurations - Apply user-specific build tool settings - Improve initialization performance by disabling automatic snapshot updates The implementation gracefully falls back to None if configuration files don't exist, maintaining backward compatibility with existing setups. --- .../language_servers/eclipse_jdtls.py | 77 ++++++++++++++++++- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/src/solidlsp/language_servers/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py index 912a09d0..e1181902 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -39,8 +39,24 @@ class RuntimeDependencyPaths: class EclipseJDTLS(SolidLanguageServer): - """ + r""" The EclipseJDTLS class provides a Java specific implementation of the LanguageServer class + + You can configure the following options in ls_specific_settings (in serena_config.yml): + - maven_user_settings: Path to Maven settings.xml file (default: ~/.m2/settings.xml) + - gradle_user_home: Path to Gradle user home directory (default: ~/.gradle) + + Note: Gradle wrapper is disabled by default. Projects will use the bundled Gradle distribution. + + Example configuration in ~/.serena/serena_config.yml: + ```yaml + ls_specific_settings: + java: + maven_user_settings: "/home/user/.m2/settings.xml" # Unix/Linux/Mac + # maven_user_settings: 'C:\\Users\\YourName\\.m2\\settings.xml' # Windows (use single quotes!) + gradle_user_home: "/home/user/.gradle" # Unix/Linux/Mac + # gradle_user_home: 'C:\\Users\\YourName\\.gradle' # Windows (use single quotes!) + ``` """ def __init__( @@ -326,6 +342,57 @@ class EclipseJDTLS(SolidLanguageServer): repository_absolute_path = os.path.abspath(repository_absolute_path) repo_uri = pathlib.Path(repository_absolute_path).as_uri() + # Load user's Maven and Gradle configuration paths from ls_specific_settings["java"] + custom_java_settings = self._solidlsp_settings.ls_specific_settings.get(self.get_language_enum_instance(), {}) + + # Maven settings: default to ~/.m2/settings.xml + default_maven_settings = os.path.join(os.path.expanduser("~"), ".m2", "settings.xml") + maven_user_settings = None + if "maven_user_settings" in custom_java_settings: + # User explicitly provided a path + maven_settings_path = custom_java_settings["maven_user_settings"] + if not os.path.exists(maven_settings_path): + error_msg = ( + f"Maven settings file not found: {maven_settings_path}. " + f"Fix: create the file, update path in ~/.serena/serena_config.yml (ls_specific_settings -> java -> maven_user_settings), " + f"or remove the setting to use default (~/.m2/settings.xml)" + ) + self.logger.log(error_msg, logging.ERROR) + raise FileNotFoundError(error_msg) + maven_user_settings = maven_settings_path + self.logger.log(f"Using Maven settings from custom location: {maven_user_settings}", logging.INFO) + elif os.path.exists(default_maven_settings): + maven_user_settings = default_maven_settings + self.logger.log(f"Using Maven settings from default location: {maven_user_settings}", logging.INFO) + else: + self.logger.log( + f"Maven settings not found at default location ({default_maven_settings}), will use JDTLS defaults", logging.INFO + ) + + # Gradle user home: default to ~/.gradle + default_gradle_home = os.path.join(os.path.expanduser("~"), ".gradle") + gradle_user_home = None + if "gradle_user_home" in custom_java_settings: + # User explicitly provided a path + gradle_user_home_path = custom_java_settings["gradle_user_home"] + if not os.path.exists(gradle_user_home_path): + error_msg = ( + f"Gradle user home directory not found: {gradle_user_home_path}. " + f"Fix: create the directory, update path in ~/.serena/serena_config.yml (ls_specific_settings -> java -> gradle_user_home), " + f"or remove the setting to use default (~/.gradle)" + ) + self.logger.log(error_msg, logging.ERROR) + raise FileNotFoundError(error_msg) + gradle_user_home = gradle_user_home_path + self.logger.log(f"Using Gradle user home from custom location: {gradle_user_home}", logging.INFO) + elif os.path.exists(default_gradle_home): + gradle_user_home = default_gradle_home + self.logger.log(f"Using Gradle user home from default location: {gradle_user_home}", logging.INFO) + else: + self.logger.log( + f"Gradle user home not found at default location ({default_gradle_home}), will use JDTLS defaults", logging.INFO + ) + initialize_params = { "locale": "en", "rootPath": repository_absolute_path, @@ -512,7 +579,7 @@ class EclipseJDTLS(SolidLanguageServer): "checkProjectSettingsExclusions": False, "updateBuildConfiguration": "interactive", "maven": { - "userSettings": None, + "userSettings": maven_user_settings, "globalSettings": None, "notCoveredPluginExecutionSeverity": "warning", "defaultMojoExecutionAction": "ignore", @@ -538,7 +605,7 @@ class EclipseJDTLS(SolidLanguageServer): "offline": {"enabled": False}, "arguments": None, "jvmArguments": None, - "user": {"home": None}, + "user": {"home": gradle_user_home}, "annotationProcessing": {"enabled": True}, }, "exclusions": [ @@ -549,7 +616,9 @@ class EclipseJDTLS(SolidLanguageServer): ], "generatesMetadataFilesAtProjectRoot": False, }, - "maven": {"downloadSources": True, "updateSnapshots": True}, + # Set updateSnapshots to False to improve performance and avoid unnecessary network calls + # Snapshots will only be updated when explicitly requested by the user + "maven": {"downloadSources": True, "updateSnapshots": False}, "eclipse": {"downloadSources": True}, "signatureHelp": {"enabled": True, "description": {"enabled": True}}, "implementationsCodeLens": {"enabled": True}, From 04910f866f36461c77b1e6a9dac0586774a26227 Mon Sep 17 00:00:00 2001 From: MischaPanch Date: Tue, 28 Oct 2025 23:51:28 +0100 Subject: [PATCH 3/4] Shutdown dashboard tab on overview page if server is closed --- src/serena/resources/dashboard/dashboard.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/serena/resources/dashboard/dashboard.js b/src/serena/resources/dashboard/dashboard.js index 1c5a34b8..a5c72de9 100644 --- a/src/serena/resources/dashboard/dashboard.js +++ b/src/serena/resources/dashboard/dashboard.js @@ -89,7 +89,10 @@ class Dashboard { // Register event handlers this.$loadButton.click(this.loadLogs.bind(this)); - this.$menuShutdown.click(this.shutdown.bind(this)); + this.$menuShutdown.click(function(e) { + e.preventDefault(); + self.shutdown(); + }); this.$menuToggle.click(this.toggleMenu.bind(this)); this.$themeToggle.click(this.toggleTheme.bind(this)); this.$refreshStats.click(this.loadStats.bind(this)); @@ -184,6 +187,7 @@ class Dashboard { url: '/get_config_overview', type: 'GET', success: function(response) { + self.failureCount = 0; self.configData = response; self.displayConfig(response); self.displayBasicStats(response.tool_stats_summary); @@ -194,6 +198,11 @@ class Dashboard { }, error: function(xhr, status, error) { console.error('Error loading config overview:', error); + self.failureCount++; + if (self.failureCount >= 3) { + console.log('Server appears to be down, closing tab'); + window.close(); + } self.$configDisplay.html('
Error loading configuration
'); self.$basicStatsDisplay.html('
Error loading stats
'); self.$projectsDisplay.html('
Error loading projects
'); From 88caf59033bb03a5f398e52536b07688bd411ee3 Mon Sep 17 00:00:00 2001 From: MischaPanch Date: Wed, 29 Oct 2025 00:06:05 +0100 Subject: [PATCH 4/4] Added callout with link to config instructions and path to project config on hover [ci skip] --- src/serena/dashboard.py | 1 + src/serena/resources/dashboard/dashboard.js | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/serena/dashboard.py b/src/serena/dashboard.py index 7f90d1e2..6b38b38b 100644 --- a/src/serena/dashboard.py +++ b/src/serena/dashboard.py @@ -156,6 +156,7 @@ class SerenaDashboardAPI: project_info = { "name": active_project_name, "language": project.project_config.language.value if project else None, + "path": str(project.project_root) if project else None, } # Get context info diff --git a/src/serena/resources/dashboard/dashboard.js b/src/serena/resources/dashboard/dashboard.js index a5c72de9..e63fdd29 100644 --- a/src/serena/resources/dashboard/dashboard.js +++ b/src/serena/resources/dashboard/dashboard.js @@ -229,7 +229,12 @@ class Dashboard { // Project info html += '
Active Project:
'; - html += '
' + (config.active_project.name || 'None') + '
'; + if (config.active_project.name && config.active_project.path) { + const configPath = config.active_project.path + '/.serena/project.yml'; + html += '
' + config.active_project.name + '
'; + } else { + html += '
' + (config.active_project.name || 'None') + '
'; + } html += '
Language:
'; html += '
' + (config.active_project.language || 'N/A') + '
'; @@ -253,6 +258,12 @@ class Dashboard { html += '
'; + // Configuration help link + html += '
'; + html += '📖'; + html += 'View Configuration Guide'; + html += '
'; + // Active tools - collapsible html += '
'; html += '

';