Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bnd] Language Server for bnd #5861

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions biz.aQute.bnd.lsp.provider/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
<classpathentry kind="src" output="bin" path="src"/>
<classpathentry kind="src" output="bin_test" path="test">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
3 changes: 3 additions & 0 deletions biz.aQute.bnd.lsp.provider/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/bin/
/bin_test/
generated/
23 changes: 23 additions & 0 deletions biz.aQute.bnd.lsp.provider/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>biz.aQute.bnd.lsp.provider</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>bndtools.core.bndbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>bndtools.core.bndnature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
line.separator=\n
517 changes: 517 additions & 0 deletions biz.aQute.bnd.lsp.provider/.settings/org.eclipse.jdt.core.prefs

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions biz.aQute.bnd.lsp.provider/.settings/org.eclipse.jdt.ui.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
cleanup.add_default_serial_version_id=true
cleanup.add_generated_serial_version_id=false
cleanup.add_missing_annotations=true
cleanup.add_missing_deprecated_annotations=true
cleanup.add_missing_methods=false
cleanup.add_missing_nls_tags=false
cleanup.add_missing_override_annotations=true
cleanup.add_missing_override_annotations_interface_methods=true
cleanup.add_serial_version_id=true
cleanup.always_use_blocks=true
cleanup.always_use_parentheses_in_expressions=false
cleanup.always_use_this_for_non_static_field_access=false
cleanup.always_use_this_for_non_static_method_access=false
cleanup.convert_functional_interfaces=true
cleanup.convert_to_enhanced_for_loop=true
cleanup.convert_to_enhanced_for_loop_if_loop_var_used=false
cleanup.correct_indentation=false
cleanup.format_source_code=false
cleanup.format_source_code_changes_only=false
cleanup.insert_inferred_type_arguments=false
cleanup.lazy_logical_operator=false
cleanup.make_local_variable_final=true
cleanup.make_parameters_final=false
cleanup.make_private_fields_final=true
cleanup.make_type_abstract_if_missing_method=false
cleanup.make_variable_declarations_final=false
cleanup.merge_conditional_blocks=false
cleanup.never_use_blocks=false
cleanup.never_use_parentheses_in_expressions=true
cleanup.number_suffix=true
cleanup.objects_equals=true
cleanup.organize_imports=false
cleanup.precompile_regex=true
cleanup.push_down_negation=false
cleanup.qualify_static_field_accesses_with_declaring_class=false
cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
cleanup.qualify_static_member_accesses_with_declaring_class=true
cleanup.qualify_static_method_accesses_with_declaring_class=false
cleanup.remove_private_constructors=true
cleanup.remove_redundant_modifiers=true
cleanup.remove_redundant_semicolons=true
cleanup.remove_redundant_type_arguments=true
cleanup.remove_trailing_whitespaces=true
cleanup.remove_trailing_whitespaces_all=true
cleanup.remove_trailing_whitespaces_ignore_empty=false
cleanup.remove_unnecessary_array_creation=true
cleanup.remove_unnecessary_casts=true
cleanup.remove_unnecessary_nls_tags=true
cleanup.remove_unused_imports=true
cleanup.remove_unused_local_variables=false
cleanup.remove_unused_private_fields=true
cleanup.remove_unused_private_members=true
cleanup.remove_unused_private_methods=true
cleanup.remove_unused_private_types=true
cleanup.simplify_lambda_expression_and_method_ref=true
cleanup.sort_members=false
cleanup.sort_members_all=false
cleanup.use_anonymous_class_creation=false
cleanup.use_autoboxing=false
cleanup.use_blocks=false
cleanup.use_blocks_only_for_return_and_throw=false
cleanup.use_directly_map_method=true
cleanup.use_lambda=true
cleanup.use_parentheses_in_expressions=false
cleanup.use_this_for_non_static_field_access=false
cleanup.use_this_for_non_static_field_access_only_if_necessary=true
cleanup.use_this_for_non_static_method_access=false
cleanup.use_this_for_non_static_method_access_only_if_necessary=true
cleanup.use_type_arguments=false
cleanup.use_unboxing=false
cleanup.use_var=false
cleanup_profile=_bnd
cleanup_settings_version=2
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=_bnd
formatter_settings_version=16
org.eclipse.jdt.ui.exception.name=e
org.eclipse.jdt.ui.gettersetter.use.is=true
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=java;javax;org;com;
org.eclipse.jdt.ui.javadoc=false
org.eclipse.jdt.ui.keywordthis=false
org.eclipse.jdt.ui.ondemandthreshold=99
org.eclipse.jdt.ui.overrideannotation=true
org.eclipse.jdt.ui.staticondemandthreshold=99
org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\n * @return the ${bare_field_name}\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\n * @param ${param} the ${bare_field_name} to set\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\n * ${tags}\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/**\n * \n */</template><template autoinsert\="true" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\n * @author ${user}\n *\n * ${tags}\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\n * \n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\n * ${tags}\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\n * ${see_to_overridden}\n */</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\n * ${tags}\n * ${see_to_target}\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
sp_cleanup.add_missing_annotations=true
sp_cleanup.add_missing_deprecated_annotations=true
sp_cleanup.add_missing_methods=false
sp_cleanup.add_missing_nls_tags=false
sp_cleanup.add_missing_override_annotations=true
sp_cleanup.add_missing_override_annotations_interface_methods=true
sp_cleanup.add_serial_version_id=false
sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_functional_interfaces=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.correct_indentation=false
sp_cleanup.format_source_code=true
sp_cleanup.format_source_code_changes_only=true
sp_cleanup.insert_inferred_type_arguments=false
sp_cleanup.make_local_variable_final=false
sp_cleanup.make_parameters_final=false
sp_cleanup.make_private_fields_final=true
sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=false
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.on_save_use_additional_actions=true
sp_cleanup.organize_imports=true
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_with_declaring_class=true
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
sp_cleanup.remove_redundant_modifiers=true
sp_cleanup.remove_redundant_semicolons=true
sp_cleanup.remove_redundant_type_arguments=false
sp_cleanup.remove_trailing_whitespaces=true
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_casts=true
sp_cleanup.remove_unnecessary_nls_tags=true
sp_cleanup.remove_unused_imports=true
sp_cleanup.remove_unused_local_variables=false
sp_cleanup.remove_unused_private_fields=true
sp_cleanup.remove_unused_private_members=false
sp_cleanup.remove_unused_private_methods=true
sp_cleanup.remove_unused_private_types=true
sp_cleanup.sort_members=false
sp_cleanup.sort_members_all=false
sp_cleanup.use_anonymous_class_creation=false
sp_cleanup.use_blocks=false
sp_cleanup.use_blocks_only_for_return_and_throw=false
sp_cleanup.use_lambda=true
sp_cleanup.use_parentheses_in_expressions=false
sp_cleanup.use_this_for_non_static_field_access=false
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
sp_cleanup.use_this_for_non_static_method_access=false
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
sp_cleanup.use_type_arguments=false
4 changes: 4 additions & 0 deletions biz.aQute.bnd.lsp.provider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Language Server for bnd editor

This project is a proposed implementation for a language server for the bnd editor.

7 changes: 7 additions & 0 deletions biz.aQute.bnd.lsp.provider/bnd.bnd
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-include: ${workspace}/cnf/includes/jdt.bnd

-buildpath: \
org.eclipse.lsp4j,\
org.eclipse.lsp4j.jsonrpc,\
biz.aQute.bndlib
-testpath: junit-jupiter-api
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package biz.aQute.bnd.lsp.provider;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.CompletionParams;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
import org.eclipse.lsp4j.SemanticTokens;
import org.eclipse.lsp4j.SemanticTokensParams;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.services.TextDocumentService;

import aQute.bnd.help.Syntax;
import aQute.bnd.properties.Document;
import aQute.bnd.properties.IDocument;
import aQute.bnd.properties.IRegion;
import aQute.bnd.properties.LineType;
import aQute.bnd.properties.PropertiesLineReader;

public class BNDTextDocumentService implements TextDocumentService {

private final Map<String, IDocument> docs = new ConcurrentHashMap<>();
private Executor executor;

BNDTextDocumentService(Executor executor) {
this.executor = executor;
}

@Override
public void didOpen(DidOpenTextDocumentParams params) {
System.out.println("BNDTextDocumentService.didOpen()");
IDocument model = new Document(params.getTextDocument().getText());
this.docs.put(params.getTextDocument().getUri(), model);
}

@Override
public void didChange(DidChangeTextDocumentParams params) {
IDocument model = new Document(params.getContentChanges().get(0).getText());
this.docs.put(params.getTextDocument().getUri(), model);
}

@Override
public void didClose(DidCloseTextDocumentParams params) {
System.out.println("BNDTextDocumentService.didClose()");
this.docs.remove(params.getTextDocument().getUri());
}

@Override
public void didSave(DidSaveTextDocumentParams params) {
// TODO anything to do here?
System.out.println("BNDTextDocumentService.didSave()");
}

@Override
public CompletableFuture<SemanticTokens> semanticTokensFull(SemanticTokensParams params) {
System.out.println("BNDTextDocumentService.semanticTokensFull()");
return withDocument(params.getTextDocument(), document -> {

int lines = document.getNumberOfLines();
List<IRegion> list = new ArrayList<>(lines);
for (int i = 0; i < lines; i++) {
list.add(document.getLineInformation(i));
}

PropertiesLineReader reader = new PropertiesLineReader(document);
List<Token> tokens = new ArrayList<>();
LineType type;
while ((type = reader.next()) != LineType.eof) {
// TODO https://github.com/eclipse/lsp4e/issues/861 would be
// good to encode
// type/modifier not by plain int...
if (type == LineType.entry) {
String key = reader.key();
IRegion region = reader.region();
tokens.add(new Token(getLine(region, list), 0, key.length(), 0, 0));
} else if (type == LineType.comment) {
// TODO https://github.com/bndtools/bnd/issues/5843
IRegion region = reader.region();
tokens.add(new Token(getLine(region, list), 0, region.getLength(), 1, 0));
}
}
SemanticTokens semanticTokens = new SemanticTokens(new ArrayList<>());
List<Integer> data = semanticTokens.getData();
int lastLine = 0;
int lastStartChar = 0;
// See
// https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-16.md#textDocument_semanticTokens
for (Token token : tokens) {
// TODO https://github.com/eclipse/lsp4e/issues/861 can Token record + encoding
// probably be part of lsp4j?
System.out.println(token);
int lineDelta = token.line() - lastLine;
data.add(lineDelta);
if (lastLine == token.line()) {
data.add(token.startChar() - lastStartChar);
} else {
data.add(token.startChar());
}
data.add(token.length());
data.add(token.tokenType());
data.add(token.tokenModifiers());
lastLine = token.line();
lastStartChar = token.startChar();
}
return semanticTokens;
});
}

private int getLine(IRegion region, List<IRegion> list) {
int s = region.getOffset();
for (int i = 0; i < list.size(); i++) {
IRegion r = list.get(i);
int offsetStart = r.getOffset();
int offsetEnd = offsetStart + r.getLength();
if (s >= offsetStart && s <= offsetEnd) {
return i;
}
}
return -1;
}

@Override
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams params) {
System.out.println("BNDTextDocumentService.completion()");
return withDocument(params.getTextDocument(), document -> {
// TODO prefix
return Either.forRight(new CompletionList(false, Syntax.HELP.values().stream().map(syntax -> {
CompletionItem item = new CompletionItem();
item.setLabel(syntax.getHeader());
item.setInsertText(syntax.getHeader() + ": ");
return item;
}).toList()));
});
}

private <T> CompletableFuture<T> withDocument(TextDocumentIdentifier identifier, DocumentCallable<T> callable) {
IDocument document = docs.get(identifier.getUri());
if (document == null) {
return CompletableFuture.failedFuture(new IllegalStateException());
}
CompletableFuture<T> future = new CompletableFuture<>();
future.completeAsync(() -> {
try {
return callable.call(document);
} catch (Exception e) {
future.completeExceptionally(e);
return null;
}

}, executor);
return future;
}

private interface DocumentCallable<V> {
V call(IDocument document) throws Exception;
}

private static final record Token(int line, int startChar, int length, int tokenType, int tokenModifiers) {
}

}
Loading