Skip to content

Commit

Permalink
Merge pull request #6 from nadeeshaan/dev-branch
Browse files Browse the repository at this point in the history
Add completion support while typing
  • Loading branch information
nadeeshaan authored Sep 24, 2018
2 parents 78fe835 + bc33853 commit 350e7ad
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 54 deletions.
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
## Swagger Language Server
Swagger Language Server is currently work in progress
**_(Swagger Language Server is currently work in progress_)**

This is a language server implementation for swagger scripting. Swagger LS supports the LSP v3.7.0

### Supported LSP Features
- Completion

### Build from source

**Prerequisites**
- **Java 1.8** and set **JAVA_HOME** (Will be include the capability to configure through settings in the future)
- **Maven v3.5.3** at least
- **npm 5.6.0** at least

**How to build**
- Execute **mvn clean install** from the project root. This will create the server launcher at <PROJECT_ROOT>/client/launcher
- Go to *<PROJECT_ROOT>/client* and execute command **npm install** and then **npm run package**. This will build the **.vsix** VSCode extention under **_<PROJECT_ROOT>/client_**
- Now you can install the generated extension as usual.

### Editor Configurations
In order to get the auto completion on typing, add the following user settings to **settings.json**

``"editor.quickSuggestions": {
"other": true,
"comments": false,
"strings": true
}``

In order to disable the word based suggestions add the following,

``"editor.wordBasedSuggestions": false``

7 changes: 1 addition & 6 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,14 @@
"contributes": {
"configuration": {
"type": "object",
"title": "swaggerls",
"title": "swagger",
"properties": {
"javahome": {
"type": "string",
"default": "",
"description": "Specifies the path to the java home directory"
}
}
},
"configurationDefaults": {
"[ballerina]": {
"editor.wordBasedSuggestions": false
}
}
},
"scripts": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public void connect(LanguageClient languageClient) {
public CompletableFuture<InitializeResult> initialize(InitializeParams initializeParams) {
final InitializeResult initializeResult = new InitializeResult(new ServerCapabilities());
initializeResult.getCapabilities().setTextDocumentSync(TextDocumentSyncKind.Full);
CompletionOptions completionOptions = new CompletionOptions();
initializeResult.getCapabilities().setCompletionProvider(new CompletionOptions());
return CompletableFuture.supplyAsync(() -> initializeResult);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.ReferenceParams;
import org.eclipse.lsp4j.RenameParams;
import org.eclipse.lsp4j.SignatureHelp;
Expand All @@ -45,10 +44,7 @@
import org.eclipse.lsp4j.services.TextDocumentService;
import org.swagger.langserver.completion.ContentParserUtil;

import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -71,9 +67,7 @@ public SwaggerTextDocumentService() {
return CompletableFuture.supplyAsync(() -> {
List<CompletionItem> completionItems;
try {
Path path = Paths.get(new URI(completionParams.getTextDocument().getUri()));
Position position = completionParams.getPosition();
completionItems = ContentParserUtil.getCompletions(documentManager.getFileContent(path), position);
completionItems = ContentParserUtil.getCompletions(completionParams);
} catch (Exception e) {
completionItems = new ArrayList<>();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import joptsimple.internal.Strings;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.CompletionParams;
import org.eclipse.lsp4j.Position;
import org.swagger.langserver.DocumentManagerImpl;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.MappingNode;
Expand All @@ -34,7 +36,8 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.nio.file.Files;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.ArrayList;
Expand All @@ -57,62 +60,68 @@ private ContentParserUtil() {
private static final String LINE_SEPARATOR = System.lineSeparator();

private static final String SWAGGER_MODEL_PACKAGE = "io.swagger.models";

private static Iterable<Node> getYAMLNodeTree(String content) {
Constructor constructor = new Constructor();
Yaml yaml = new Yaml(constructor);
return yaml.composeAll(new InputStreamReader(new ByteArrayInputStream(content.getBytes())));
}

/**
* Mapping node where the cursor belongs to.
*
* @param line Cursor Line
* @param content Document Content to Parse
*/
public static void getMappingNodeForCursor(int line, String content) {
Iterable<Node> nodes = getYAMLNodeTree(content);
nodes.forEach(node -> {
if (node instanceof MappingNode) {
((MappingNode) node).getValue().forEach(nodeTuple -> {
// TODO: Need Implementation
});
}
});
}

/**
* Get the modified document content.
*
* Note: Here replace the line content at the cursor with spaces to avoid parser issues
*
* @param fileUri Document uri
* @param cursorLine Current cursor line
* @return {@link String} Modified content
* @throws IOException IOException if the file read fails
* @param completionParams Original file content to modify
* @return {@link ModifiedContent} Modified content
* @throws IOException IOException if the file read fails
* @throws URISyntaxException URI Syntax Exception
*/
public String modifyContent(String fileUri, int cursorLine) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(URI.create(fileUri))));
String[] lines = content.split("\\r?\\n");
lines[cursorLine] = lines[cursorLine].replaceAll("\\w", " ");
return Strings.join(lines, LINE_SEPARATOR);
private static ModifiedContent getModifiedContent(CompletionParams completionParams) throws IOException,
URISyntaxException {
Position position = completionParams.getPosition();
int cursorLine = position.getLine();
Path path = Paths.get(new URI(completionParams.getTextDocument().getUri()));
String originalContent = DocumentManagerImpl.getInstance().getFileContent(path);

String[] lines = originalContent.split("\\r?\\n");
lines[cursorLine] = lines[cursorLine].replaceAll("\\S", "");
Position modifiedPosition = new Position(position.getLine(), lines[cursorLine].length());
return new ModifiedContent(Strings.join(lines, LINE_SEPARATOR), modifiedPosition);
}


public static List<CompletionItem> getCompletions(String content, Position position) throws NoSuchMethodException,
InvocationTargetException, IllegalAccessException {

/**
* Get the completion items for the given parameters.
*
* @param completionParams Completion parameters triggered from the client
* @return {@link List} List of completion Items
* @throws NoSuchMethodException Exception while extracting the method from reflection
* @throws InvocationTargetException Exception while accessing the method
* @throws IllegalAccessException Exception while accessing the method
* @throws URISyntaxException Invalid URI
* @throws IOException Error reading file URI
*/
public static List<CompletionItem> getCompletions(CompletionParams completionParams) throws NoSuchMethodException,
InvocationTargetException, IllegalAccessException, URISyntaxException, IOException {

Deque<String> fieldStack = new ArrayDeque<>();
SwaggerDeserializationResult swaggerDeserializationResult = new SwaggerParser().readWithInfo(content);
Constructor constructor = new Constructor();
Yaml yaml = new Yaml(constructor);
Iterable<Node> iterable = yaml.composeAll(new InputStreamReader(new ByteArrayInputStream(content.getBytes())));
Yaml yaml = new Yaml(new Constructor());

ModifiedContent modifiedContent = getModifiedContent(completionParams);
String sourceContent = modifiedContent.getContent();
Position modifiedPosition = modifiedContent.getPosition();


SwaggerDeserializationResult swaggerDeserializationResult = new SwaggerParser().readWithInfo(sourceContent);
Iterable<Node> iterable = yaml.composeAll(new InputStreamReader(
new ByteArrayInputStream(sourceContent.getBytes())));

iterable.forEach(o -> {
FieldIdentifier fieldIdentifier = new FieldIdentifier(position.getLine(), position.getCharacter());
FieldIdentifier fieldIdentifier = new FieldIdentifier(modifiedPosition.getLine(),
modifiedPosition.getCharacter());
fieldIdentifier
.calculateFieldStack(((MappingNode) o).getValue());
if (!fieldIdentifier.getFieldStack().isEmpty()) {
fieldStack.addAll(fieldIdentifier.getFieldStack());
}
});

Swagger swagger = swaggerDeserializationResult.getSwagger();
List<String> fields = new ArrayList<>(fieldStack);
Collections.reverse(fields);
Expand Down Expand Up @@ -157,4 +166,22 @@ private static List<String> getCompletionFields(Swagger swagger, List<String> fi
.filter(field -> !field.getName().equalsIgnoreCase("vendorExtensions"))
.map(Field::getName).collect(Collectors.toList());
}

private static class ModifiedContent {
private String content;
private Position position;

private ModifiedContent(String content, Position position) {
this.content = content;
this.position = position;
}

String getContent() {
return content;
}

Position getPosition() {
return position;
}
}
}

0 comments on commit 350e7ad

Please sign in to comment.