-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for multiple workspaces (#160)
* Add support for multiple workspaces Previously, the language server only knew about a single workspace root, so if your editor was using [WorkspaceFolders](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_workspaceFolders) the server would just pick the first one, and not load any others. This commit allows the server to load multiple workspaces. The primary challenge was handling state changes to individual workspaces independently. We use client-side file watchers and the `didChangeWatchedFiles` notification to make sure projects are up to date with new and deleted Smithy files, and any changes to build files (i.e. smithy-build.json). `didChangeWatchedFiles` sends a flat list of file events - not partitioned by workspace - so we have to figure out which projects each change applies to. This is done by creating a `PathMatcher` for each project's smithy files and build files, then matching on each file event. This way, we can apply changes to each individual project, rather than reloading everything. Selectors were also updated to select from all available projects. * Fix file patterns for windows The file patterns we were using for telling the client which files to watch, and to match file events in `didChangeWatchedFiles` to projects, were not working properly on windows because they didn't use the correct file separator.
- Loading branch information
1 parent
779ef70
commit 5ef6b14
Showing
14 changed files
with
1,041 additions
and
289 deletions.
There are no files selected for viewing
238 changes: 130 additions & 108 deletions
238
src/main/java/software/amazon/smithy/lsp/SmithyLanguageServer.java
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
src/main/java/software/amazon/smithy/lsp/project/ProjectChanges.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package software.amazon.smithy.lsp.project; | ||
|
||
import java.util.Set; | ||
|
||
/** | ||
* File changes to a {@link Project}. | ||
* | ||
* @param changedBuildFileUris The uris of changed build files | ||
* @param createdSmithyFileUris The uris of created Smithy files | ||
* @param deletedSmithyFileUris The uris of deleted Smithy files | ||
*/ | ||
public record ProjectChanges( | ||
Set<String> changedBuildFileUris, | ||
Set<String> createdSmithyFileUris, | ||
Set<String> deletedSmithyFileUris | ||
) { | ||
/** | ||
* @return Whether there are any changed build files | ||
*/ | ||
public boolean hasChangedBuildFiles() { | ||
return !changedBuildFileUris.isEmpty(); | ||
} | ||
|
||
/** | ||
* @return Whether there are any changed Smithy files | ||
*/ | ||
public boolean hasChangedSmithyFiles() { | ||
return !createdSmithyFileUris.isEmpty() || !deletedSmithyFileUris.isEmpty(); | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
src/main/java/software/amazon/smithy/lsp/project/ProjectFilePatterns.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package software.amazon.smithy.lsp.project; | ||
|
||
import java.io.File; | ||
import java.nio.file.FileSystems; | ||
import java.nio.file.Path; | ||
import java.nio.file.PathMatcher; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
/** | ||
* Utility methods for creating file patterns corresponding to meaningful | ||
* paths of a {@link Project}, such as sources and build files. | ||
*/ | ||
public final class ProjectFilePatterns { | ||
private static final int BUILD_FILE_COUNT = 2 + ProjectConfigLoader.SMITHY_BUILD_EXTS.length; | ||
|
||
private ProjectFilePatterns() { | ||
} | ||
|
||
/** | ||
* @param project The project to get watch patterns for | ||
* @return A list of glob patterns used to watch Smithy files in the given project | ||
*/ | ||
public static List<String> getSmithyFileWatchPatterns(Project project) { | ||
return Stream.concat(project.sources().stream(), project.imports().stream()) | ||
.map(path -> getSmithyFilePattern(path, true)) | ||
.toList(); | ||
} | ||
|
||
/** | ||
* @param project The project to get a path matcher for | ||
* @return A path matcher that can check if Smithy files belong to the given project | ||
*/ | ||
public static PathMatcher getSmithyFilesPathMatcher(Project project) { | ||
String pattern = Stream.concat(project.sources().stream(), project.imports().stream()) | ||
.map(path -> getSmithyFilePattern(path, false)) | ||
.collect(Collectors.joining(",")); | ||
return FileSystems.getDefault().getPathMatcher("glob:{" + pattern + "}"); | ||
} | ||
|
||
/** | ||
* @param project The project to get the watch pattern for | ||
* @return A glob pattern used to watch build files in the given project | ||
*/ | ||
public static String getBuildFilesWatchPattern(Project project) { | ||
Path root = project.root(); | ||
String buildJsonPattern = escapeBackslashes(root.resolve(ProjectConfigLoader.SMITHY_BUILD).toString()); | ||
String projectJsonPattern = escapeBackslashes(root.resolve(ProjectConfigLoader.SMITHY_PROJECT).toString()); | ||
|
||
List<String> patterns = new ArrayList<>(BUILD_FILE_COUNT); | ||
patterns.add(buildJsonPattern); | ||
patterns.add(projectJsonPattern); | ||
for (String buildExt : ProjectConfigLoader.SMITHY_BUILD_EXTS) { | ||
patterns.add(escapeBackslashes(root.resolve(buildExt).toString())); | ||
} | ||
|
||
return "{" + String.join(",", patterns) + "}"; | ||
} | ||
|
||
/** | ||
* @param project The project to get a path matcher for | ||
* @return A path matcher that can check if a file is a build file belonging to the given project | ||
*/ | ||
public static PathMatcher getBuildFilesPathMatcher(Project project) { | ||
// Watch pattern is the same as the pattern used for matching | ||
String pattern = getBuildFilesWatchPattern(project); | ||
return FileSystems.getDefault().getPathMatcher("glob:" + pattern); | ||
} | ||
|
||
// When computing the pattern used for telling the client which files to watch, we want | ||
// to only watch .smithy/.json files. We don't need in the PathMatcher pattern (and it | ||
// is impossible anyway because we can't have a nested pattern). | ||
private static String getSmithyFilePattern(Path path, boolean isWatcherPattern) { | ||
String glob = path.toString(); | ||
if (glob.endsWith(".smithy") || glob.endsWith(".json")) { | ||
return escapeBackslashes(glob); | ||
} | ||
|
||
if (!glob.endsWith(File.separator)) { | ||
glob += File.separator; | ||
} | ||
glob += "**"; | ||
|
||
if (isWatcherPattern) { | ||
glob += ".{smithy,json}"; | ||
} | ||
|
||
return escapeBackslashes(glob); | ||
} | ||
|
||
// In glob patterns, '\' is an escape character, so it needs to escaped | ||
// itself to work as a separator (i.e. for windows) | ||
private static String escapeBackslashes(String pattern) { | ||
return pattern.replace("\\", "\\\\"); | ||
} | ||
} |
Oops, something went wrong.