下面列出了怎么用org.eclipse.lsp4j.FileChangeType的API类实例代码及写法,或者点击链接到github查看源代码。
/**
* Called when a file is changed. Notifies the server if this file was watched.
*
* @param file The file
*/
static void fileChanged(VirtualFile file) {
if (!FileUtils.isFileSupported(file)) {
return;
}
String uri = FileUtils.VFSToURI(file);
if (uri == null) {
return;
}
ApplicationUtils.invokeAfterPsiEvents(() -> {
EditorEventManager manager = EditorEventManagerBase.forUri(uri);
if (manager != null) {
manager.documentSaved();
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
FileUtils.projectToUri(p), FileChangeType.Changed));
} else {
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
FileUtils.projectToUri(p), FileChangeType.Changed));
}
});
}
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
// TODO: Set watched files to client. Note: Client may have performance issues with lots of folders to watch.
final List<URI> dirtyFiles = new ArrayList<>();
final List<URI> deletedFiles = new ArrayList<>();
for (FileEvent fileEvent : params.getChanges()) {
URI uri = uriExtensions.toUri(fileEvent.getUri());
String fileName = uri.lastSegment();
boolean skipFile = fileName.equals(ProjectStatePersister.FILENAME);
if (!skipFile && isSourceFile(uri)) {
FileChangeType changeType = fileEvent.getType();
if (changeType == FileChangeType.Deleted) {
deletedFiles.add(uri);
} else {
dirtyFiles.add(uri);
}
}
}
if (!dirtyFiles.isEmpty() || !deletedFiles.isEmpty()) {
runBuildable("didChangeWatchedFiles", () -> workspaceManager.didChangeFiles(dirtyFiles, deletedFiles));
}
}
/**
* Shows that the resource is not validated during incremental build
*/
@Test
public void testIncrementalBuildNoValidation() {
StringConcatenation _builder = new StringConcatenation();
_builder.append("type Test {");
_builder.newLine();
_builder.append(" ");
_builder.append("NonExisting foo");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this.writeFile("MyType1.testlang", _builder);
this.initialize();
Assert.assertTrue(IterableExtensions.join(this.getDiagnostics().entrySet(), ","), this.getDiagnostics().isEmpty());
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("type NonExisting {");
_builder_1.newLine();
_builder_1.append("}");
_builder_1.newLine();
final String path = this.writeFile("MyType2.testlang", _builder_1);
WorkspaceService _workspaceService = this.languageServer.getWorkspaceService();
FileEvent _fileEvent = new FileEvent(path, FileChangeType.Created);
DidChangeWatchedFilesParams _didChangeWatchedFilesParams = new DidChangeWatchedFilesParams(Collections.<FileEvent>unmodifiableList(CollectionLiterals.<FileEvent>newArrayList(_fileEvent)));
_workspaceService.didChangeWatchedFiles(_didChangeWatchedFilesParams);
Assert.assertTrue(IterableExtensions.join(this.getDiagnostics().entrySet(), ","), this.getDiagnostics().isEmpty());
}
@Test
public void testIncrementalDeletion() {
StringConcatenation _builder = new StringConcatenation();
_builder.append("type Test {");
_builder.newLine();
_builder.append(" ");
_builder.append("NonExisting foo");
_builder.newLine();
_builder.append("}");
_builder.newLine();
final String path = this.writeFile("MyType1.testlang", _builder);
this.initialize();
this.assertEquals("Couldn\'t resolve reference to TypeDeclaration \'NonExisting\'.",
IterableExtensions.<Diagnostic>head(IterableExtensions.<List<Diagnostic>>head(this.getDiagnostics().values())).getMessage());
this.deleteFile("MyType1.testlang");
WorkspaceService _workspaceService = this.languageServer.getWorkspaceService();
FileEvent _fileEvent = new FileEvent(path, FileChangeType.Deleted);
DidChangeWatchedFilesParams _didChangeWatchedFilesParams = new DidChangeWatchedFilesParams(Collections.<FileEvent>unmodifiableList(CollectionLiterals.<FileEvent>newArrayList(_fileEvent)));
_workspaceService.didChangeWatchedFiles(_didChangeWatchedFilesParams);
Assert.assertTrue(IterableExtensions.<List<Diagnostic>>head(this.getDiagnostics().values()).isEmpty());
}
/**
* Called when a file is moved. Notifies the server if this file was watched.
*
* @param event The file move event
*/
static void fileMoved(VirtualFileMoveEvent event) {
try {
VirtualFile file = event.getFile();
if (!FileUtils.isFileSupported(file)) {
return;
}
String newFileUri = FileUtils.VFSToURI(file);
String oldParentUri = FileUtils.VFSToURI(event.getOldParent());
if (newFileUri == null || oldParentUri == null) {
return;
}
String oldFileUri = String.format("%s/%s", oldParentUri, event.getFileName());
ApplicationUtils.invokeAfterPsiEvents(() -> {
// Notifies the language server.
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(oldFileUri,
FileUtils.projectToUri(p), FileChangeType.Deleted));
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(newFileUri,
FileUtils.projectToUri(p), FileChangeType.Created));
FileUtils.findProjectsFor(file).forEach(p -> {
// Detaches old file from the wrappers.
Set<LanguageServerWrapper> wrappers = IntellijLanguageClient.getAllServerWrappersFor(FileUtils.projectToUri(p));
if (wrappers != null) {
wrappers.forEach(wrapper -> wrapper.disconnect(oldFileUri, FileUtils.projectToUri(p)));
}
// Re-open file to so that the new editor will be connected to the language server.
FileEditorManager fileEditorManager = FileEditorManager.getInstance(p);
ApplicationUtils.invokeLater(() -> {
fileEditorManager.closeFile(file);
fileEditorManager.openFile(file, true);
});
});
});
} catch (Exception e) {
LOG.warn("LSP file move event failed due to :", e);
}
}
/**
* Called when a file is deleted. Notifies the server if this file was watched.
*
* @param file The file
*/
static void fileDeleted(VirtualFile file) {
if (!FileUtils.isFileSupported(file)) {
return;
}
String uri = FileUtils.VFSToURI(file);
if (uri == null) {
return;
}
ApplicationUtils.invokeAfterPsiEvents(() -> {
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
FileUtils.projectToUri(p), FileChangeType.Deleted));
});
}
/**
* Called when a file is created. Notifies the server if needed.
*
* @param file The file
*/
static void fileCreated(VirtualFile file) {
if (!FileUtils.isFileSupported(file)) {
return;
}
String uri = FileUtils.VFSToURI(file);
if (uri != null) {
ApplicationUtils.invokeAfterPsiEvents(() -> {
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(uri,
FileUtils.projectToUri(p), FileChangeType.Created));
});
}
}
private static void changedConfiguration(String uri, String projectUri, FileChangeType typ) {
ApplicationUtils.pool(() -> {
DidChangeWatchedFilesParams params = getDidChangeWatchedFilesParams(uri, typ);
Set<LanguageServerWrapper> wrappers = IntellijLanguageClient.getAllServerWrappersFor(projectUri);
if (wrappers == null) {
return;
}
for (LanguageServerWrapper wrapper : wrappers) {
if (wrapper.getRequestManager() != null
&& wrapper.getStatus() == ServerStatus.INITIALIZED) {
wrapper.getRequestManager().didChangeWatchedFiles(params);
}
}
});
}
/**
* Change a non-opened file <u>on disk</u> and notify the LSP server.
* <p>
* Use method {@link #changeOpenedFile(String, Pair...)} instead if the file was previously opened with one of the
* {@link #openFile(FileURI) #openFile()} methods.
*
* @param modification
* a function returning the desired new content when given the file's current content on disk.
*/
protected void changeNonOpenedFile(String moduleName, Function<String, String> modification) {
FileURI fileURI = getFileURIFromModuleName(moduleName);
if (isOpen(fileURI)) {
Assert.fail("file is open: " + fileURI);
}
// 1) change on disk
changeFileOnDiskWithoutNotification(fileURI, modification);
// 2) notify LSP server
List<FileEvent> fileEvents = Collections.singletonList(
new FileEvent(fileURI.toString(), FileChangeType.Changed));
DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams(fileEvents);
languageServer.didChangeWatchedFiles(params);
}
private CHANGE_TYPE toChangeType(FileChangeType vtype) {
switch (vtype) {
case Created:
return CHANGE_TYPE.CREATED;
case Changed:
return CHANGE_TYPE.CHANGED;
case Deleted:
return CHANGE_TYPE.DELETED;
default:
throw new UnsupportedOperationException();
}
}
@Test
public void testDiscardStaleWorkingCopies() throws Exception {
IJavaProject javaProject = newEmptyProject();
IFolder src = javaProject.getProject().getFolder("src");
IPackageFragmentRoot srcRoot = javaProject.getPackageFragmentRoot(src);
IPackageFragment mypack = srcRoot.createPackageFragment("mypack", true, null);
// @formatter:off
String contents = "package mypack;\n" +
"public class Foo {" +
"}\n";
// @formatter:on
ICompilationUnit unit = mypack.createCompilationUnit("Foo.java", contents, true, null);
openDocument(unit, contents, 1);
assertTrue(unit.isWorkingCopy());
String oldUri = JDTUtils.getFileURI(srcRoot.getResource());
String parentUri = JDTUtils.getFileURI(src);
String newUri = oldUri.replace("mypack", "mynewpack");
File oldPack = mypack.getResource().getLocation().toFile();
File newPack = new File(oldPack.getParent(), "mynewpack");
Files.move(oldPack, newPack);
assertTrue(unit.isWorkingCopy());
DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams(Arrays.asList(
new FileEvent(newUri, FileChangeType.Created),
new FileEvent(parentUri, FileChangeType.Changed),
new FileEvent(oldUri, FileChangeType.Deleted)
));
new WorkspaceEventsHandler(projectsManager, javaClient, lifeCycleHandler).didChangeWatchedFiles(params);
assertFalse(unit.isWorkingCopy());
}
@Test
public void testDeleteProjectFolder() throws Exception {
importProjects("maven/multimodule3");
IProject module2 = ProjectUtils.getProject("module2");
assertTrue(module2 != null && module2.exists());
String projectUri = JDTUtils.getFileURI(module2);
FileUtils.deleteDirectory(module2.getLocation().toFile());
assertTrue(module2.exists());
clientRequests.clear();
DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams(Arrays.asList(
new FileEvent(projectUri, FileChangeType.Deleted)
));
new WorkspaceEventsHandler(projectsManager, javaClient, lifeCycleHandler).didChangeWatchedFiles(params);
waitForBackgroundJobs();
assertFalse(module2.exists());
List<PublishDiagnosticsParams> diags = getClientRequests("publishDiagnostics");
assertEquals(9L, diags.size());
assertEndsWith(diags.get(0).getUri(), "/module2");
assertEndsWith(diags.get(1).getUri(), "/multimodule3");
assertEndsWith(diags.get(2).getUri(), "/multimodule3/pom.xml");
assertEndsWith(diags.get(3).getUri(), "/module2/pom.xml");
assertEquals(0L, diags.get(3).getDiagnostics().size());
assertEndsWith(diags.get(4).getUri(), "/module2");
assertEquals(0L, diags.get(4).getDiagnostics().size());
assertEndsWith(diags.get(5).getUri(), "/App.java");
assertEquals(0L, diags.get(5).getDiagnostics().size());
assertEndsWith(diags.get(6).getUri(), "/AppTest.java");
assertEquals(0L, diags.get(6).getDiagnostics().size());
assertEndsWith(diags.get(7).getUri(), "/multimodule3");
assertEndsWith(diags.get(8).getUri(), "/multimodule3/pom.xml");
}
@Test
public void testOpenedDocumentShadowsPersistedFile() {
StringConcatenation _builder = new StringConcatenation();
_builder.append("type Test {");
_builder.newLine();
_builder.append(" ");
_builder.append("NonExisting foo");
_builder.newLine();
_builder.append("}");
_builder.newLine();
final String firstFile = this.writeFile("MyType1.testlang", _builder);
this.initialize();
this.assertEquals("Couldn\'t resolve reference to TypeDeclaration \'NonExisting\'.", IterableExtensions.<Diagnostic>head(this.getDiagnostics().get(firstFile)).getMessage());
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("type Foo {");
_builder_1.newLine();
_builder_1.append("}");
_builder_1.newLine();
final String path = this.writeFile("MyType2.testlang", _builder_1);
WorkspaceService _workspaceService = this.languageServer.getWorkspaceService();
FileEvent _fileEvent = new FileEvent(path, FileChangeType.Created);
DidChangeWatchedFilesParams _didChangeWatchedFilesParams = new DidChangeWatchedFilesParams(
Collections.<FileEvent>unmodifiableList(CollectionLiterals.<FileEvent>newArrayList(_fileEvent)));
_workspaceService.didChangeWatchedFiles(_didChangeWatchedFilesParams);
this.assertEquals("Couldn\'t resolve reference to TypeDeclaration \'NonExisting\'.", IterableExtensions.<Diagnostic>head(this.getDiagnostics().get(firstFile)).getMessage());
StringConcatenation _builder_2 = new StringConcatenation();
_builder_2.append("type NonExisting {");
_builder_2.newLine();
_builder_2.append("}");
_builder_2.newLine();
this.open(path, _builder_2.toString());
Assert.assertNull(IterableExtensions.<Diagnostic>head(this.getDiagnostics().get(firstFile)));
this.close(path);
this.assertEquals("Couldn\'t resolve reference to TypeDeclaration \'NonExisting\'.", IterableExtensions.<Diagnostic>head(this.getDiagnostics().get(firstFile)).getMessage());
}
@Test
public void testIncrementalBuildWithError() {
StringConcatenation _builder = new StringConcatenation();
_builder.append("type Test {");
_builder.newLine();
_builder.append(" ");
_builder.append("NonExisting foo");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this.writeFile("MyType1.testlang", _builder);
this.initialize();
this.assertEquals("Couldn\'t resolve reference to TypeDeclaration \'NonExisting\'.", IterableExtensions.<Diagnostic>head(IterableExtensions.<List<Diagnostic>>head(this.getDiagnostics().values())).getMessage());
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("type NonExisting {");
_builder_1.newLine();
_builder_1.append("}");
_builder_1.newLine();
final String path = this.writeFile("MyType2.testlang", _builder_1);
WorkspaceService _workspaceService = this.languageServer.getWorkspaceService();
FileEvent _fileEvent = new FileEvent(path, FileChangeType.Created);
DidChangeWatchedFilesParams _didChangeWatchedFilesParams = new DidChangeWatchedFilesParams(Collections.<FileEvent>unmodifiableList(CollectionLiterals.<FileEvent>newArrayList(_fileEvent)));
_workspaceService.didChangeWatchedFiles(_didChangeWatchedFilesParams);
Assert.assertNotNull(this.getDiagnostics().get(path));
final Function1<List<Diagnostic>, Boolean> _function = (List<Diagnostic> it) -> {
return Boolean.valueOf(it.isEmpty());
};
Assert.assertTrue(IterableExtensions.join(this.getDiagnostics().values(), ","), IterableExtensions.<List<Diagnostic>>forall(this.getDiagnostics().values(), _function));
}
@Test
public void testTwoFilesDeleteClose() {
final String fileURI = this.writeFile("Foo.testlang", "");
this.initialize();
final String referencingFileURI = this.getVirtualFile("Bar.testlang");
StringConcatenation _builder = new StringConcatenation();
_builder.append("type Bar {");
_builder.newLine();
_builder.append(" ");
_builder.append("Foo foo");
_builder.newLine();
_builder.append("}");
_builder.newLine();
this.open(referencingFileURI, _builder.toString());
Assert.assertFalse("Bar.testlang references missing type Foo from Foo.testlang: expect error",
this.getDiagnostics().get(referencingFileURI).isEmpty());
this.open(fileURI, "type Foo {}");
Assert.assertTrue("Bar.testlang references type Foo from Foo.testlang: expect no error",
this.getDiagnostics().get(referencingFileURI).isEmpty());
this.deleteFile(fileURI);
WorkspaceService _workspaceService = this.languageServer.getWorkspaceService();
FileEvent _fileEvent = new FileEvent(fileURI, FileChangeType.Deleted);
DidChangeWatchedFilesParams _didChangeWatchedFilesParams = new DidChangeWatchedFilesParams(Collections.<FileEvent>unmodifiableList(CollectionLiterals.<FileEvent>newArrayList(_fileEvent)));
_workspaceService.didChangeWatchedFiles(_didChangeWatchedFilesParams);
Assert.assertTrue("delete file on disk: expect no error", this.getDiagnostics().get(referencingFileURI).isEmpty());
this.close(fileURI);
Assert.assertFalse("close deleted file: expect error", this.getDiagnostics().get(referencingFileURI).isEmpty());
}
/**
* Evaluate the params and deduce the respective build command.
*/
protected Buildable toBuildable(DidChangeWatchedFilesParams params) {
List<URI> dirtyFiles = new ArrayList<>();
List<URI> deletedFiles = new ArrayList<>();
params.getChanges().stream()
.map((fileEvent) -> Pair.of(uriExtensions.toUri(fileEvent.getUri()), fileEvent.getType()))
.filter(pair -> !workspaceManager.isDocumentOpen(pair.getKey())).forEach(pair -> {
if (pair.getValue() == FileChangeType.Deleted) {
deletedFiles.add(pair.getKey());
} else {
dirtyFiles.add(pair.getKey());
}
});
return workspaceManager.didChangeFiles(dirtyFiles, deletedFiles);
}
/**
* Called when a file is renamed. Notifies the server if this file was watched.
*
* @param oldFileName The old file name
* @param newFileName the new file name
*/
static void fileRenamed(String oldFileName, String newFileName) {
ApplicationUtils.invokeAfterPsiEvents(() -> {
try {
// Getting the right file is not trivial here since we only have the file name. Since we have to iterate over
// all opened projects and filter based on the file name.
Set<VirtualFile> files = Arrays.stream(ProjectManager.getInstance().getOpenProjects())
.flatMap(p -> Arrays.stream(searchFiles(newFileName, p)))
.map(PsiFile::getVirtualFile)
.collect(Collectors.toSet());
for (VirtualFile file : files) {
if (!FileUtils.isFileSupported(file)) {
continue;
}
String newFileUri = FileUtils.VFSToURI(file);
String oldFileUri = newFileUri.replace(file.getName(), oldFileName);
// Notifies the language server.
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(oldFileUri,
FileUtils.projectToUri(p), FileChangeType.Deleted));
FileUtils.findProjectsFor(file).forEach(p -> changedConfiguration(newFileUri,
FileUtils.projectToUri(p), FileChangeType.Created));
FileUtils.findProjectsFor(file).forEach(p -> {
// Detaches old file from the wrappers.
Set<LanguageServerWrapper> wrappers = IntellijLanguageClient.getAllServerWrappersFor(FileUtils.projectToUri(p));
if (wrappers != null) {
wrappers.forEach(wrapper -> {
// make these calls first since the disconnect might stop the LS client if its last file.
wrapper.getRequestManager().didChangeWatchedFiles(
getDidChangeWatchedFilesParams(oldFileUri, FileChangeType.Deleted));
wrapper.getRequestManager().didChangeWatchedFiles(
getDidChangeWatchedFilesParams(newFileUri, FileChangeType.Created));
wrapper.disconnect(oldFileUri, FileUtils.projectToUri(p));
});
}
// Todo - Stop opening files with the same file name.
// Re-open file to so that the new editor will be connected to the language server.
FileEditorManager fileEditorManager = FileEditorManager.getInstance(p);
ApplicationUtils.invokeLater(() -> {
fileEditorManager.closeFile(file);
fileEditorManager.openFile(file, true);
});
});
}
} catch (Exception e) {
LOG.warn("LSP file rename event failed due to : ", e);
}
});
}
@NotNull
private static DidChangeWatchedFilesParams getDidChangeWatchedFilesParams(String fileUri, FileChangeType typ) {
List<FileEvent> event = new ArrayList<>();
event.add(new FileEvent(fileUri, typ));
return new DidChangeWatchedFilesParams(event);
}
private void didChangedWatchedFiles(XMLLanguageServer ls, File file) {
List<FileEvent> changes = new ArrayList<>();
changes.add(new FileEvent(file.toURI().toString(), FileChangeType.Changed));
DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams(changes);
ls.getWorkspaceService().didChangeWatchedFiles(params);
}
private void createSourcePathWatcher()
{
try
{
sourcePathWatcher = FileSystems.getDefault().newWatchService();
}
catch (IOException e)
{
System.err.println("Failed to get watch service for source paths.");
e.printStackTrace(System.err);
}
sourcePathWatcherThread = new Thread()
{
public void run()
{
while(true)
{
WatchKey watchKey = null;
try
{
//pause the thread while there are no changes pending,
//for better performance
watchKey = sourcePathWatcher.take();
}
catch(InterruptedException e)
{
return;
}
List<FileEvent> changes = new ArrayList<>();
while (watchKey != null)
{
for (WorkspaceFolder folder : workspaceFolderManager.getWorkspaceFolders())
{
WorkspaceFolderData folderData = workspaceFolderManager.getWorkspaceFolderData(folder);
if(!folderData.sourceOrLibraryPathWatchKeys.containsKey(watchKey))
{
continue;
}
Path path = folderData.sourceOrLibraryPathWatchKeys.get(watchKey);
for (WatchEvent<?> event : watchKey.pollEvents())
{
WatchEvent.Kind<?> kind = event.kind();
Path childPath = (Path) event.context();
childPath = path.resolve(childPath);
if(java.nio.file.Files.isDirectory(childPath))
{
if(kind.equals(StandardWatchEventKinds.ENTRY_CREATE))
{
//if a new directory has been created under
//an existing that we're already watching,
//then start watching the new one too.
watchNewSourceOrLibraryPath(childPath, folderData);
}
}
FileChangeType changeType = FileChangeType.Changed;
if(kind.equals(StandardWatchEventKinds.ENTRY_CREATE))
{
changeType = FileChangeType.Created;
}
else if(kind.equals(StandardWatchEventKinds.ENTRY_DELETE))
{
changeType = FileChangeType.Deleted;
}
changes.add(new FileEvent(childPath.toUri().toString(), changeType));
}
boolean valid = watchKey.reset();
if (!valid)
{
folderData.sourceOrLibraryPathWatchKeys.remove(watchKey);
}
}
//keep handling new changes until we run out
watchKey = sourcePathWatcher.poll();
}
if (changes.size() > 0)
{
//convert to DidChangeWatchedFilesParams and pass
//to didChangeWatchedFiles, as if a notification
//had been sent from the client.
DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams();
params.setChanges(changes);
didChangeWatchedFiles(params);
}
}
}
};
sourcePathWatcherThread.start();
}
public FileEvent(@NonNull final String uri, @NonNull final FileChangeType type) {
this.uri = Preconditions.<String>checkNotNull(uri, "uri");
this.type = Preconditions.<FileChangeType>checkNotNull(type, "type");
}
/**
* The change type.
*/
@Pure
@NonNull
public FileChangeType getType() {
return this.type;
}
/**
* The change type.
*/
public void setType(@NonNull final FileChangeType type) {
this.type = Preconditions.checkNotNull(type, "type");
}