Skip to content
This repository has been archived by the owner on Sep 18, 2023. It is now read-only.

Commit

Permalink
Merge pull request #17 from aditosoftware/#2016181_logging_lsp
Browse files Browse the repository at this point in the history
#2016181 logging lsp
  • Loading branch information
rH4rtinger authored Mar 8, 2023
2 parents 2c29f3c + 86eb904 commit f3e78c6
Show file tree
Hide file tree
Showing 5 changed files with 327 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package de.adito.aditoweb.nbm.nodejs.impl.actions;

import de.adito.aditoweb.nbm.nodejs.impl.ls.TypeScriptLanguageServerProvider;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.editor.BaseAction;
import org.netbeans.modules.lsp.client.LSPBindings;
import org.netbeans.modules.lsp.client.spi.ServerRestarter;
import org.openide.awt.*;

import javax.swing.*;
import javax.swing.text.JTextComponent;
import java.awt.event.ActionEvent;

/**
* Action for restarting the LSP in javascript and typescript files.
*
* @author r.hartinger, 07.03.2023
*/
@ActionReferences({
@ActionReference(path = "Editors/text/typescript/Toolbars/Default", position = 20220),
@ActionReference(path = "Editors/text/javascript/Toolbars/Default", position = 20220)
})
@ActionID(category = "adito/editor/toolbar", id = "de.adito.aditoweb.nbm.nodejs.impl.actions.RestartLSP")
@ActionRegistration(displayName = "Restart LSP",
iconBase = "de/adito/aditoweb/nbm/nodejs/impl/actions/restart.svg", lazy = false)
public class RestartLSPAction extends BaseAction
{

/**
* Creates a new instance.
*/
public RestartLSPAction()
{
super("Restart LSP");
putValue(BaseAction.ICON_RESOURCE_PROPERTY, "de/adito/aditoweb/nbm/nodejs/impl/actions/restart.svg");
putValue(Action.SHORT_DESCRIPTION, "Restart the server that provides autocomplete, go-to actions and other features for js/ts files");
}

@Override
public void actionPerformed(ActionEvent evt, JTextComponent target)
{
String mimeType = (String) target.getDocument().getProperty("mimeType");
if (mimeType != null)
{
TypeScriptLanguageServerProvider typeScriptLanguageServerProvider = MimeLookup.getLookup(mimeType).lookup(TypeScriptLanguageServerProvider.class);
if (typeScriptLanguageServerProvider != null)
{
ServerRestarter serverRestarter = typeScriptLanguageServerProvider.getServerRestarter();
if (serverRestarter != null)
{
synchronized (LSPBindings.class)
{
typeScriptLanguageServerProvider.stopServer(serverRestarter);
}
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import de.adito.aditoweb.nbm.nodejs.impl.*;
import de.adito.notification.INotificationFacade;
import io.reactivex.rxjava3.disposables.Disposable;
import lombok.Getter;
import org.jetbrains.annotations.*;
import org.netbeans.api.editor.mimelookup.*;
import org.netbeans.modules.lsp.client.LanguageServerProviderAccessor;
import org.netbeans.modules.lsp.client.spi.*;
import org.openide.util.*;

import java.io.*;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.*;
Expand All @@ -29,6 +30,10 @@ public class TypeScriptLanguageServerProvider implements LanguageServerProvider
private final _NotificationHandler notificationHandler = new _NotificationHandler();
private Disposable currentDisposable;

@Getter
@Nullable
private ServerRestarter serverRestarter;

@Override
public LanguageServerDescription startServer(@NotNull Lookup pLookup)
{
Expand Down Expand Up @@ -61,6 +66,7 @@ private Optional<LanguageServerDescription> _startServer(@Nullable ServerRestart
LOGGER.log(Level.INFO, "Starting TypeScript Language Server");

// restarts
serverRestarter = pServerRestarter;
_handleRestartOnChange(pServerRestarter);

// execute
Expand Down Expand Up @@ -116,24 +122,32 @@ private void _handleRestartOnChange(@Nullable ServerRestarter pServerRestarter)
if (pServerRestarter != null)
currentDisposable = NodeJSInstaller.observeInstallation()
.skip(1) // ignore initial value, we only want real changes
.subscribe(pTime -> {
LOGGER.info("Restarting TypeScript Language Server");
.subscribe(pTime -> stopServer(pServerRestarter));
}

// Stop Server
Optional<LanguageServerDescription> currentServer = currentRef.get();
if (currentServer != null && currentServer.isPresent()) //NOSONAR null is a valid value, even it is not recommended
_stopServer(currentServer.get());
/**
* Stops the server via the given server restarter.
*
* @param pServerRestarter the server restarter with which the LSP should be stopped
*/
public void stopServer(@NotNull ServerRestarter pServerRestarter)
{
LOGGER.info("Restarting TypeScript Language Server");

// Trigger Restart
try
{
pServerRestarter.restart();
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, "", e);
}
});
// Stop Server
Optional<LanguageServerDescription> currentServer = currentRef.get();
if (currentServer != null && currentServer.isPresent()) //NOSONAR null is a valid value, even it is not recommended
_stopServer(currentServer.get());

// Trigger Restart
try
{
pServerRestarter.restart();
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, "", e);
}
}

/**
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package de.adito.aditoweb.nbm.nodejs.impl.actions;

import de.adito.aditoweb.nbm.nodejs.impl.ls.TypeScriptLanguageServerProvider;
import org.jetbrains.annotations.*;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
import org.mockito.*;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.modules.lsp.client.spi.ServerRestarter;
import org.openide.util.Lookup;

import javax.swing.text.*;
import java.awt.event.ActionEvent;
import java.util.*;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.*;

/**
* Test class for {@link RestartLSPAction}.
*
* @author r.hartinger, 07.03.2023
*/
class RestartLSPActionTest
{

/**
* Tests that a new {@link RestartLSPAction} is created with having the correct key - values.
*/
@Test
void testConstructor()
{
RestartLSPAction restartLSPAction = new RestartLSPAction();

Map<String, Object> expected = Map.of("Name", "Restart LSP",
"IconResource", "de/adito/aditoweb/nbm/nodejs/impl/actions/restart.svg",
"ShortDescription", "Restart the server that provides autocomplete, go-to actions and other features for js/ts files");

Map<String, Object> actual = new HashMap<>();
// Transforming all key - values in the restartAction
Arrays.stream(restartLSPAction.getKeys())
.filter(Objects::nonNull)
// keys should be strings, so we need to transform them
.filter(String.class::isInstance)
.map(String.class::cast)
// finding the value to each key and add it to the actual map
.forEach(pKey -> {
Object value = restartLSPAction.getValue(pKey);
actual.put(pKey, value);
});

assertEquals(expected, actual);
}

/**
* Tests the method {@link RestartLSPAction#actionPerformed(ActionEvent, JTextComponent)}
*/
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ActionPerformed
{

/**
* @return the arguments for {@link #shouldActionPerformed(int, ServerRestarter, String)}
*/
@NotNull
private Stream<Arguments> shouldActionPerformed()
{
ServerRestarter serverRestarter = Mockito.spy(ServerRestarter.class);

return Stream.of(
// no mime type -> will not call restart
Arguments.of(0, null, null),
Arguments.of(0, serverRestarter, null),

// mime type is there, but the method will not find any lsp
Arguments.of(0, null, "text/json"),
Arguments.of(0, serverRestarter, "text/json"),

// correct mime type, LSP can be restarted, if the server restarter is there
Arguments.of(0, null, "text/javascript"),
Arguments.of(0, null, "text/typescript"),
Arguments.of(1, serverRestarter, "text/javascript"),
Arguments.of(1, serverRestarter, "text/typescript")
);
}

/**
* Tests if the action is only performed, when an LSP is found, and it contains a ServerRestarter.
* In these tests, the {@link TypeScriptLanguageServerProvider} will only be found, if the mime type ends with {@code script} (e.g. typescript or javascript).
*
* @param pCallStopServer how often {@link TypeScriptLanguageServerProvider#stopServer(ServerRestarter)} will be called.
* @param pServerRestarter the {@link ServerRestarter} that should be returned by {@link TypeScriptLanguageServerProvider#getServerRestarter()}
* @param pMimeType the mime type that should be returned by the document in the {@link JTextComponent}
*/
@ParameterizedTest
@MethodSource
void shouldActionPerformed(int pCallStopServer, @Nullable ServerRestarter pServerRestarter, @Nullable String pMimeType)
{
Document document = Mockito.spy(Document.class);
Mockito.doReturn(pMimeType).when(document).getProperty("mimeType");

JTextComponent textComponent = Mockito.mock(JTextComponent.class);
Mockito.doReturn(document).when(textComponent).getDocument();

RestartLSPAction restartLSPAction = new RestartLSPAction();

try (MockedStatic<MimeLookup> mimeLookupMockedStatic = Mockito.mockStatic(MimeLookup.class))
{
TypeScriptLanguageServerProvider typeScriptLanguageServerProvider = Mockito.spy(TypeScriptLanguageServerProvider.class);
Mockito.doReturn(pServerRestarter).when(typeScriptLanguageServerProvider).getServerRestarter();

Lookup lookup = Mockito.mock(Lookup.class);
Mockito.doReturn(typeScriptLanguageServerProvider).when(lookup).lookup(TypeScriptLanguageServerProvider.class);

// return the correct lookup when the mime type ends with script
mimeLookupMockedStatic.when(() -> MimeLookup.getLookup(endsWith("script"))).thenReturn(lookup);
// return a lookup with nothing in it, when the mime type ends with json
mimeLookupMockedStatic.when(() -> MimeLookup.getLookup(endsWith("json"))).thenReturn(Mockito.mock(Lookup.class));

restartLSPAction.actionPerformed(null, textComponent);

Mockito.verify(typeScriptLanguageServerProvider, Mockito.times(pCallStopServer)).stopServer(any());
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,6 @@ void shouldDownloadSuccessfully() throws Exception

// check
Assertions.assertTrue(invalidDownloads.isEmpty(), invalidDownloads::toString);
Assertions.assertTrue(validDownloads.containsAll(availableVersions), invalidDownloads::toString);
Assertions.assertTrue(validDownloads.containsAll(availableVersions), () -> validDownloads + " should contain all " + availableVersions);
}
}

0 comments on commit f3e78c6

Please sign in to comment.