JaroslavTulach commented on a change in pull request #2812: URL: https://github.com/apache/netbeans/pull/2812#discussion_r601576786
########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * + * @author Dusan Balek + */ +public final class Completion { + + static { + CompletionAccessor.setDefault(new CompletionAccessor() { + @Override + public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, + List<Character> commitCharacters, Command command) { + return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters, command); + } + }); + } + + private final String label; + private final Kind kind; + private final List<Tag> tags; + private final CompletableFuture<String> detail; + private final CompletableFuture<String> documentation; + private final boolean preselect; + private final String sortText; + private final String filterText; + private final String insertText; + private final TextFormat insertTextFormat; + private final TextEdit textEdit; + private final CompletableFuture<List<TextEdit>> additionalTextEdits; + private final List<Character> commitCharacters; + private final Command command; + + private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, Review comment: `private` and accessible via accessor. Great. ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Command.java ########## @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.List; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; + +/** + * Represents a reference to a command. + * + * @author Dusan Balek + */ +public final class Command { Review comment: One of the comments when discussing the richness of this LSP API with you and @jlahoda was a weak definition of `Command`. Unless I am mistaken it is not yet used anywhere - maybe we should delay making it public. I've just realized we already have an _abstract command system_ API in NetBeans - we have [intents and their actions](https://bits.netbeans.org/12.0/javadoc/org-netbeans-api-intent/org/netbeans/api/intent/Intent.html)! Could we use `Intent` instead of `Command`? Or can we hide `Command` for now and think about the `Intent`/`Command` relationship more thoughtfully? ########## File path: ide/api.lsp/nbproject/project.properties ########## @@ -0,0 +1,3 @@ +is.autoload=true Review comment: There seems to be a missing license in this file. It is good this module is an autoload. ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * Review comment: I'd add `@since` tag to every class, possibly also visible method. ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * + * @author Dusan Balek + */ +public final class Completion { + + static { + CompletionAccessor.setDefault(new CompletionAccessor() { + @Override + public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, + List<Character> commitCharacters, Command command) { + return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters, command); + } + }); + } + + private final String label; + private final Kind kind; + private final List<Tag> tags; + private final CompletableFuture<String> detail; + private final CompletableFuture<String> documentation; + private final boolean preselect; + private final String sortText; + private final String filterText; + private final String insertText; + private final TextFormat insertTextFormat; + private final TextEdit textEdit; + private final CompletableFuture<List<TextEdit>> additionalTextEdits; + private final List<Character> commitCharacters; + private final Command command; + + private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, + TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters, Command command) { + this.label = label; + this.kind = kind; + this.tags = tags; + this.detail = detail; + this.documentation = documentation; + this.preselect = preselect; + this.sortText = sortText; + this.filterText = filterText; + this.insertText = insertText; + this.insertTextFormat = insertTextFormat; + this.textEdit = textEdit; + this.additionalTextEdits = additionalTextEdits; + this.commitCharacters = commitCharacters; + this.command = command; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public String getLabel() { + return label; + } + + /** + * The kind of this completion. + */ + @CheckForNull + public Kind getKind() { + return kind; + } + + /** + * Tags for this completion. + */ + @CheckForNull + public List<Tag> getTags() { + return tags != null ? Collections.unmodifiableList(tags) : null; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @CheckForNull + public CompletableFuture<String> getDetail() { + return detail; + } + + /** + * A human-readable string that represents a doc-comment. An HTML format is + * supported. + */ + @CheckForNull + public CompletableFuture<String> getDocumentation() { + return documentation; + } + + /** + * Select this completion when showing. + */ + public boolean isPreselect() { + return preselect; + } + + /** + * A string that should be used when comparing this completion with other + * completions. When {@code null} the label is used as the sort text. + */ + @CheckForNull + public String getSortText() { + return sortText; + } + + /** + * A string that should be used when filtering a set of completions. + * When {@code null} the label is used as the filter. + */ + @CheckForNull + public String getFilterText() { + return filterText; + } + + /** + * A string that should be inserted into a document when selecting + * this completion. When {@code null} the label is used as the insert text. + */ + @CheckForNull + public String getInsertText() { + return insertText; + } + + /** + * The format of the insert text. The format applies to both the + * {@code insertText} property and the {@code newText} property of a provided + * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}. + */ + @CheckForNull + public TextFormat getInsertTextFormat() { + return insertTextFormat; + } + + /** + * An edit which is applied to a document when selecting this completion. + * When an edit is provided the value of {@code insertText} is ignored. + * The range of the edit must be a single line range and it must + * contain the position at which completion has been requested. + */ + @CheckForNull + public TextEdit getTextEdit() { + return textEdit; + } + + /** + * A list of additional text edits that are applied when selecting this + * completion. Edits must not overlap (including the same insert position) + * with the main edit nor with themselves. + * Additional text edits should be used to change text unrelated to the + * current cursor position (for example adding an import statement at the + * top of the file if the completion item will insert an unqualified type). + */ + @CheckForNull + public CompletableFuture<List<TextEdit>> getAdditionalTextEdits() { + return additionalTextEdits; + } + + /** + * A list of characters that when pressed while this completion is + * active will accept it first and then type that character. + */ + @CheckForNull + public List<Character> getCommitCharacters() { + return commitCharacters != null ? Collections.unmodifiableList(commitCharacters) : null; + } + + /** + * A command that is executed after inserting this completion. + */ + @CheckForNull + public Command getCommand() { + return command; + } + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. + * + * @param doc a text document + * @param offset an offset inside the text document + * @param context an optional completion context + * @param consumer an operation accepting collected completions + * + * @return true if the list of collected completion is complete Review comment: What does it mean _complete_? Btw. are can the completion be computed asynchronously or is it guaranteed the `consumer` gets all `Completion` instances before this method returns? Does a concept of _completeness_ have any sense when the `consumer` is populated asynchronously? ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * + * @author Dusan Balek + */ +public final class Completion { + + static { + CompletionAccessor.setDefault(new CompletionAccessor() { + @Override + public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, + List<Character> commitCharacters, Command command) { + return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters, command); + } + }); + } + + private final String label; + private final Kind kind; + private final List<Tag> tags; + private final CompletableFuture<String> detail; + private final CompletableFuture<String> documentation; + private final boolean preselect; + private final String sortText; + private final String filterText; + private final String insertText; + private final TextFormat insertTextFormat; + private final TextEdit textEdit; + private final CompletableFuture<List<TextEdit>> additionalTextEdits; + private final List<Character> commitCharacters; + private final Command command; + + private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, + TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters, Command command) { + this.label = label; + this.kind = kind; + this.tags = tags; + this.detail = detail; + this.documentation = documentation; + this.preselect = preselect; + this.sortText = sortText; + this.filterText = filterText; + this.insertText = insertText; + this.insertTextFormat = insertTextFormat; + this.textEdit = textEdit; + this.additionalTextEdits = additionalTextEdits; + this.commitCharacters = commitCharacters; + this.command = command; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public String getLabel() { + return label; + } + + /** + * The kind of this completion. + */ + @CheckForNull + public Kind getKind() { + return kind; + } + + /** + * Tags for this completion. + */ + @CheckForNull + public List<Tag> getTags() { + return tags != null ? Collections.unmodifiableList(tags) : null; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @CheckForNull + public CompletableFuture<String> getDetail() { + return detail; + } + + /** + * A human-readable string that represents a doc-comment. An HTML format is + * supported. + */ + @CheckForNull + public CompletableFuture<String> getDocumentation() { + return documentation; + } + + /** + * Select this completion when showing. + */ + public boolean isPreselect() { + return preselect; + } + + /** + * A string that should be used when comparing this completion with other + * completions. When {@code null} the label is used as the sort text. + */ + @CheckForNull + public String getSortText() { + return sortText; + } + + /** + * A string that should be used when filtering a set of completions. + * When {@code null} the label is used as the filter. + */ + @CheckForNull + public String getFilterText() { + return filterText; + } + + /** + * A string that should be inserted into a document when selecting + * this completion. When {@code null} the label is used as the insert text. + */ + @CheckForNull + public String getInsertText() { + return insertText; + } + + /** + * The format of the insert text. The format applies to both the + * {@code insertText} property and the {@code newText} property of a provided + * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}. + */ + @CheckForNull + public TextFormat getInsertTextFormat() { + return insertTextFormat; + } + + /** + * An edit which is applied to a document when selecting this completion. + * When an edit is provided the value of {@code insertText} is ignored. + * The range of the edit must be a single line range and it must + * contain the position at which completion has been requested. + */ + @CheckForNull + public TextEdit getTextEdit() { + return textEdit; + } + + /** + * A list of additional text edits that are applied when selecting this + * completion. Edits must not overlap (including the same insert position) + * with the main edit nor with themselves. + * Additional text edits should be used to change text unrelated to the + * current cursor position (for example adding an import statement at the + * top of the file if the completion item will insert an unqualified type). + */ + @CheckForNull + public CompletableFuture<List<TextEdit>> getAdditionalTextEdits() { + return additionalTextEdits; + } + + /** + * A list of characters that when pressed while this completion is + * active will accept it first and then type that character. + */ + @CheckForNull + public List<Character> getCommitCharacters() { + return commitCharacters != null ? Collections.unmodifiableList(commitCharacters) : null; + } + + /** + * A command that is executed after inserting this completion. + */ + @CheckForNull + public Command getCommand() { + return command; + } + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. + * Review comment: This is the most important method around. It would deserve a `{@codesnippet ....}` example inserting here a code with [codesnippet4javadoc](https://github.com/jtulach/codesnippet4javadoc) doclet. ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * + * @author Dusan Balek + */ +public final class Completion { + + static { + CompletionAccessor.setDefault(new CompletionAccessor() { + @Override + public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, + List<Character> commitCharacters, Command command) { + return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters, command); + } + }); + } + + private final String label; + private final Kind kind; + private final List<Tag> tags; + private final CompletableFuture<String> detail; + private final CompletableFuture<String> documentation; + private final boolean preselect; + private final String sortText; + private final String filterText; + private final String insertText; + private final TextFormat insertTextFormat; + private final TextEdit textEdit; + private final CompletableFuture<List<TextEdit>> additionalTextEdits; + private final List<Character> commitCharacters; + private final Command command; + + private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, + TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters, Command command) { + this.label = label; + this.kind = kind; + this.tags = tags; + this.detail = detail; + this.documentation = documentation; + this.preselect = preselect; + this.sortText = sortText; + this.filterText = filterText; + this.insertText = insertText; + this.insertTextFormat = insertTextFormat; + this.textEdit = textEdit; + this.additionalTextEdits = additionalTextEdits; + this.commitCharacters = commitCharacters; + this.command = command; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public String getLabel() { + return label; + } + + /** + * The kind of this completion. + */ + @CheckForNull + public Kind getKind() { + return kind; + } + + /** + * Tags for this completion. + */ + @CheckForNull + public List<Tag> getTags() { + return tags != null ? Collections.unmodifiableList(tags) : null; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @CheckForNull + public CompletableFuture<String> getDetail() { + return detail; + } + + /** + * A human-readable string that represents a doc-comment. An HTML format is + * supported. + */ + @CheckForNull + public CompletableFuture<String> getDocumentation() { + return documentation; + } + + /** + * Select this completion when showing. + */ + public boolean isPreselect() { + return preselect; + } + + /** + * A string that should be used when comparing this completion with other + * completions. When {@code null} the label is used as the sort text. + */ + @CheckForNull + public String getSortText() { + return sortText; + } + + /** + * A string that should be used when filtering a set of completions. + * When {@code null} the label is used as the filter. + */ + @CheckForNull + public String getFilterText() { + return filterText; + } + + /** + * A string that should be inserted into a document when selecting + * this completion. When {@code null} the label is used as the insert text. + */ + @CheckForNull + public String getInsertText() { + return insertText; + } + + /** + * The format of the insert text. The format applies to both the + * {@code insertText} property and the {@code newText} property of a provided + * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}. + */ + @CheckForNull + public TextFormat getInsertTextFormat() { + return insertTextFormat; + } + + /** + * An edit which is applied to a document when selecting this completion. + * When an edit is provided the value of {@code insertText} is ignored. + * The range of the edit must be a single line range and it must + * contain the position at which completion has been requested. + */ + @CheckForNull + public TextEdit getTextEdit() { + return textEdit; + } + + /** + * A list of additional text edits that are applied when selecting this + * completion. Edits must not overlap (including the same insert position) + * with the main edit nor with themselves. + * Additional text edits should be used to change text unrelated to the + * current cursor position (for example adding an import statement at the + * top of the file if the completion item will insert an unqualified type). + */ + @CheckForNull + public CompletableFuture<List<TextEdit>> getAdditionalTextEdits() { + return additionalTextEdits; + } + + /** + * A list of characters that when pressed while this completion is + * active will accept it first and then type that character. + */ + @CheckForNull + public List<Character> getCommitCharacters() { + return commitCharacters != null ? Collections.unmodifiableList(commitCharacters) : null; + } + + /** + * A command that is executed after inserting this completion. + */ + @CheckForNull + public Command getCommand() { + return command; + } + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. + * + * @param doc a text document + * @param offset an offset inside the text document + * @param context an optional completion context + * @param consumer an operation accepting collected completions + * + * @return true if the list of collected completion is complete + */ + public static boolean collect(@NonNull Document doc, int offset, @NullAllowed Context context, @NonNull Consumer<Completion> consumer) { + boolean isComplete = true; + MimePath mimePath = MimePath.parse(DocumentUtilities.getMimeType(doc)); + for (CompletionCollector collector : MimeLookup.getLookup(mimePath).lookupAll(CompletionCollector.class)) { + isComplete &= collector.collectCompletions(doc, offset, context, consumer); + } + return isComplete; + } + + /** + * Contains additional information about the context in which a request for + * collections completions is triggered. + */ + public static final class Context { + + private final TriggerKind triggerKind; + private final String triggerCharacter; + + public Context(@NonNull TriggerKind triggerKind, @NullAllowed String triggerCharacter) { + this.triggerKind = triggerKind; + this.triggerCharacter = triggerCharacter; + } + + /** + * How the completion was triggered. + */ + @NonNull + public TriggerKind getTriggerKind() { + return triggerKind; + } + + /** + * The trigger character (a single character) that has trigger code complete. + * Is undefined if {@code triggerKind != TriggerKind.TriggerCharacter}. + */ + @CheckForNull + public String getTriggerCharacter() { + return triggerCharacter; + } + } + + public enum TriggerKind { + + /** + * Completion was triggered by typing an identifier (24x7 code + * complete), manual invocation (e.g Ctrl+Space) or via API. + */ + Invoked(1), + + /** + * Completion was triggered by a trigger character. + */ + TriggerCharacter(2), + + /** + * Completion was re-triggered as the current completion list is incomplete. + */ + TriggerForIncompleteCompletions(3); + + private final int value; + + private TriggerKind(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static TriggerKind forValue(int value) { + TriggerKind[] allValues = TriggerKind.values(); + if (value < 1 || value > allValues.length) { + throw new IllegalArgumentException("Illegal enum value: " + value); + } + return allValues[value - 1]; + } + } + + public static enum Kind { + Review comment: Add one kind: `None(0)` and you can use `ordinal()` instead of `getValue()` and `values()[ordinal]`... ########## File path: ide/api.lsp/src/org/netbeans/spi/lsp/CompletionCollector.java ########## @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.spi.lsp; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.lsp.Command; +import org.netbeans.api.lsp.Completion; +import org.netbeans.api.lsp.TextEdit; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.editor.mimelookup.MimeLocation; + +/** + * Interface for computing and collecting completions. Clients can use this interface + * to collect completions and send them for presentation outside of NetBeans using + * the Language Server Protocol. Implementations of the interface should be registered + * in MimeLookup. + * + * @author Dusan Balek + */ +@MimeLocation(subfolderName = "CompletionCollectors") +public interface CompletionCollector { + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. Review comment: I assume all `Completion` instances has to be provided to `consumer` before this method returns. ########## File path: ide/api.lsp/src/org/netbeans/modules/lsp/CompletionAccessor.java ########## @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.lsp; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.lsp.Command; +import org.netbeans.api.lsp.Completion; +import org.netbeans.api.lsp.TextEdit; +import org.openide.util.Exceptions; +import org.openide.util.Parameters; + + +public abstract class CompletionAccessor { + + private static volatile CompletionAccessor DEFAULT; + + public static synchronized CompletionAccessor getDefault() { + CompletionAccessor instance = DEFAULT; + if (instance == null) { + Class<?> c = Completion.class; + try { + Class.forName(c.getName(), true, c.getClassLoader()); + instance = DEFAULT; + assert instance != null; + } catch (Exception ex) { + Exceptions.printStackTrace(ex); + } + } + return instance; + } + + public static void setDefault(@NonNull final CompletionAccessor accessor) { + Parameters.notNull("accessor", accessor); //NOI18N Review comment: assert `DEFAULT` is `null` otherwise throw an error. An alternative is to remove this method and assign `DEFAULT = this` in the constructor. ########## File path: ide/api.lsp/src/org/netbeans/spi/lsp/CompletionCollector.java ########## @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.spi.lsp; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.lsp.Command; +import org.netbeans.api.lsp.Completion; +import org.netbeans.api.lsp.TextEdit; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.editor.mimelookup.MimeLocation; + +/** + * Interface for computing and collecting completions. Clients can use this interface + * to collect completions and send them for presentation outside of NetBeans using + * the Language Server Protocol. Implementations of the interface should be registered + * in MimeLookup. + * + * @author Dusan Balek + */ +@MimeLocation(subfolderName = "CompletionCollectors") +public interface CompletionCollector { + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. + * + * @param doc a text document + * @param offset an offset inside the text document + * @param context an optional completion context + * @param consumer an operation accepting collected completions + * + * @return true if the list of collected completion is complete + */ + public boolean collectCompletions(@NonNull Document doc, int offset, @NullAllowed Completion.Context context, @NonNull Consumer<Completion> consumer); + + /** + * Creates a builder for {@link Completion} instances + * + * @param label the label of the completion + * @return newly created builder + */ + public static Builder newBuilder(@NonNull String label) { + return new Builder(label); + } + + public static final class Builder { + + private String label; + private Completion.Kind kind; + private List<Completion.Tag> tags; + private CompletableFuture<String> detail; + private CompletableFuture<String> documentation; + private boolean preselect; + private String sortText; + private String filterText; + private String insertText; + private Completion.TextFormat insertTextFormat; + private TextEdit textEdit; + private CompletableFuture<List<TextEdit>> additionalTextEdits; + private List<Character> commitCharacters; + private Command command; + + private Builder(@NonNull String label) { + this.label = label; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public Builder label(@NonNull String label) { + this.label = label; + return this; + } + + /** + * The kind of this completion. + */ + @NonNull + public Builder kind(@NonNull Completion.Kind kind) { + this.kind = kind; + return this; + } + + /** + * Adds tag for this completion. + */ + @NonNull + public Builder addTag(@NonNull Completion.Tag tag) { + if (this.tags == null) { + this.tags = new ArrayList<>(); + } + this.tags.add(tag); + return this; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @NonNull + public Builder detail(@NonNull CompletableFuture<String> detail) { + this.detail = detail; + return this; + } + + /** + * A human-readable string that represents a doc-comment. An HTML format + * is supported. + */ + @NonNull + public Builder documentation(@NonNull CompletableFuture<String> documentation) { + this.documentation = documentation; + return this; + } + + /** + * Select this completion when showing. + */ + @NonNull + public Builder preselect(boolean preselect) { + this.preselect = preselect; + return this; + } + + /** + * A string that should be used when comparing this completion with other + * completions. When {@code null} the label is used as the sort text. + */ + @NonNull + public Builder sortText(@NonNull String sortText) { + this.sortText = sortText; + return this; + } + + /** + * A string that should be used when filtering a set of completions. + * When {@code null} the label is used as the filter. + */ + @NonNull + public Builder filterText(@NonNull String filterText) { + this.filterText = filterText; + return this; + } + + /** + * A string that should be inserted into a document when selecting + * this completion. When {@code null} the label is used as the insert text. + */ + @NonNull + public Builder insertText(@NonNull String insertText) { + this.insertText = insertText; + return this; + } + + /** + * The format of the insert text. The format applies to both the + * {@code insertText} property and the {@code newText} property of a provided + * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}. + */ + @NonNull + public Builder insertTextFormat(@NonNull Completion.TextFormat insertTextFormat) { Review comment: I suggest to join the methods `insertText` and its format into one with two parameters. ########## File path: ide/api.lsp/src/org/netbeans/spi/lsp/CompletionCollector.java ########## @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.spi.lsp; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.lsp.Command; +import org.netbeans.api.lsp.Completion; +import org.netbeans.api.lsp.TextEdit; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.editor.mimelookup.MimeLocation; + +/** + * Interface for computing and collecting completions. Clients can use this interface + * to collect completions and send them for presentation outside of NetBeans using + * the Language Server Protocol. Implementations of the interface should be registered + * in MimeLookup. + * + * @author Dusan Balek + */ +@MimeLocation(subfolderName = "CompletionCollectors") +public interface CompletionCollector { + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. + * + * @param doc a text document + * @param offset an offset inside the text document + * @param context an optional completion context + * @param consumer an operation accepting collected completions + * + * @return true if the list of collected completion is complete + */ + public boolean collectCompletions(@NonNull Document doc, int offset, @NullAllowed Completion.Context context, @NonNull Consumer<Completion> consumer); + + /** + * Creates a builder for {@link Completion} instances + * + * @param label the label of the completion + * @return newly created builder + */ + public static Builder newBuilder(@NonNull String label) { + return new Builder(label); + } + + public static final class Builder { + + private String label; + private Completion.Kind kind; + private List<Completion.Tag> tags; + private CompletableFuture<String> detail; + private CompletableFuture<String> documentation; + private boolean preselect; + private String sortText; + private String filterText; + private String insertText; + private Completion.TextFormat insertTextFormat; + private TextEdit textEdit; + private CompletableFuture<List<TextEdit>> additionalTextEdits; + private List<Character> commitCharacters; + private Command command; + + private Builder(@NonNull String label) { + this.label = label; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public Builder label(@NonNull String label) { + this.label = label; + return this; + } + + /** + * The kind of this completion. + */ + @NonNull + public Builder kind(@NonNull Completion.Kind kind) { + this.kind = kind; + return this; + } + + /** + * Adds tag for this completion. + */ + @NonNull + public Builder addTag(@NonNull Completion.Tag tag) { + if (this.tags == null) { + this.tags = new ArrayList<>(); + } + this.tags.add(tag); + return this; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @NonNull + public Builder detail(@NonNull CompletableFuture<String> detail) { + this.detail = detail; + return this; + } + + /** + * A human-readable string that represents a doc-comment. An HTML format + * is supported. + */ + @NonNull + public Builder documentation(@NonNull CompletableFuture<String> documentation) { + this.documentation = documentation; + return this; + } + + /** + * Select this completion when showing. + */ + @NonNull + public Builder preselect(boolean preselect) { + this.preselect = preselect; + return this; + } + + /** + * A string that should be used when comparing this completion with other + * completions. When {@code null} the label is used as the sort text. + */ + @NonNull + public Builder sortText(@NonNull String sortText) { + this.sortText = sortText; + return this; + } + + /** + * A string that should be used when filtering a set of completions. + * When {@code null} the label is used as the filter. + */ + @NonNull + public Builder filterText(@NonNull String filterText) { + this.filterText = filterText; + return this; + } + + /** + * A string that should be inserted into a document when selecting + * this completion. When {@code null} the label is used as the insert text. + */ + @NonNull + public Builder insertText(@NonNull String insertText) { + this.insertText = insertText; + return this; + } + + /** + * The format of the insert text. The format applies to both the + * {@code insertText} property and the {@code newText} property of a provided + * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}. + */ + @NonNull + public Builder insertTextFormat(@NonNull Completion.TextFormat insertTextFormat) { + this.insertTextFormat = insertTextFormat; + return this; + } + + /** + * An edit which is applied to a document when selecting this completion. + * When an edit is provided the value of {@code insertText} is ignored. + * The range of the edit must be a single line range and it must + * contain the position at which completion has been requested. + */ + @NonNull + public Builder textEdit(@NonNull TextEdit textEdit) { + this.textEdit = textEdit; + return this; + } + + /** + * A list of additional text edits that are applied when selecting this + * completion. Edits must not overlap (including the same insert position) + * with the main edit nor with themselves. + * Additional text edits should be used to change text unrelated to the + * current cursor position (for example adding an import statement at the + * top of the file if the completion item will insert an unqualified type). + */ + @NonNull + public Builder additionalTextEdits(@NonNull CompletableFuture<List<TextEdit>> additionalTextEdits) { + this.additionalTextEdits = additionalTextEdits; + return this; + } + + /** + * Adds character that when pressed while this completion is active will + * accept it first and then type that character. + */ + @NonNull + public Builder addCommitCharacter(@NonNull Character commitCharacter) { Review comment: `char` ########## File path: ide/api.lsp/src/org/netbeans/spi/lsp/CompletionCollector.java ########## @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.spi.lsp; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.lsp.Command; +import org.netbeans.api.lsp.Completion; +import org.netbeans.api.lsp.TextEdit; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.editor.mimelookup.MimeLocation; + +/** + * Interface for computing and collecting completions. Clients can use this interface + * to collect completions and send them for presentation outside of NetBeans using + * the Language Server Protocol. Implementations of the interface should be registered + * in MimeLookup. + * + * @author Dusan Balek + */ +@MimeLocation(subfolderName = "CompletionCollectors") +public interface CompletionCollector { + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. + * + * @param doc a text document + * @param offset an offset inside the text document + * @param context an optional completion context + * @param consumer an operation accepting collected completions + * + * @return true if the list of collected completion is complete + */ + public boolean collectCompletions(@NonNull Document doc, int offset, @NullAllowed Completion.Context context, @NonNull Consumer<Completion> consumer); + + /** + * Creates a builder for {@link Completion} instances + * + * @param label the label of the completion + * @return newly created builder + */ + public static Builder newBuilder(@NonNull String label) { + return new Builder(label); + } + + public static final class Builder { + + private String label; + private Completion.Kind kind; + private List<Completion.Tag> tags; + private CompletableFuture<String> detail; + private CompletableFuture<String> documentation; + private boolean preselect; + private String sortText; + private String filterText; + private String insertText; + private Completion.TextFormat insertTextFormat; + private TextEdit textEdit; + private CompletableFuture<List<TextEdit>> additionalTextEdits; + private List<Character> commitCharacters; + private Command command; + + private Builder(@NonNull String label) { + this.label = label; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public Builder label(@NonNull String label) { + this.label = label; + return this; + } + + /** + * The kind of this completion. + */ + @NonNull + public Builder kind(@NonNull Completion.Kind kind) { + this.kind = kind; + return this; + } + + /** + * Adds tag for this completion. + */ + @NonNull + public Builder addTag(@NonNull Completion.Tag tag) { + if (this.tags == null) { + this.tags = new ArrayList<>(); + } + this.tags.add(tag); + return this; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @NonNull + public Builder detail(@NonNull CompletableFuture<String> detail) { + this.detail = detail; + return this; + } + + /** + * A human-readable string that represents a doc-comment. An HTML format + * is supported. + */ + @NonNull + public Builder documentation(@NonNull CompletableFuture<String> documentation) { + this.documentation = documentation; + return this; + } + + /** + * Select this completion when showing. + */ + @NonNull + public Builder preselect(boolean preselect) { + this.preselect = preselect; + return this; + } + + /** + * A string that should be used when comparing this completion with other + * completions. When {@code null} the label is used as the sort text. + */ + @NonNull + public Builder sortText(@NonNull String sortText) { + this.sortText = sortText; + return this; + } + + /** + * A string that should be used when filtering a set of completions. + * When {@code null} the label is used as the filter. + */ + @NonNull + public Builder filterText(@NonNull String filterText) { + this.filterText = filterText; + return this; + } + + /** + * A string that should be inserted into a document when selecting + * this completion. When {@code null} the label is used as the insert text. + */ + @NonNull + public Builder insertText(@NonNull String insertText) { + this.insertText = insertText; + return this; + } + + /** + * The format of the insert text. The format applies to both the + * {@code insertText} property and the {@code newText} property of a provided + * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}. + */ + @NonNull + public Builder insertTextFormat(@NonNull Completion.TextFormat insertTextFormat) { + this.insertTextFormat = insertTextFormat; + return this; + } + + /** + * An edit which is applied to a document when selecting this completion. + * When an edit is provided the value of {@code insertText} is ignored. + * The range of the edit must be a single line range and it must + * contain the position at which completion has been requested. + */ + @NonNull + public Builder textEdit(@NonNull TextEdit textEdit) { + this.textEdit = textEdit; + return this; + } + + /** + * A list of additional text edits that are applied when selecting this + * completion. Edits must not overlap (including the same insert position) + * with the main edit nor with themselves. + * Additional text edits should be used to change text unrelated to the + * current cursor position (for example adding an import statement at the + * top of the file if the completion item will insert an unqualified type). + */ + @NonNull + public Builder additionalTextEdits(@NonNull CompletableFuture<List<TextEdit>> additionalTextEdits) { Review comment: As said previously, I am not sure why this is lazy value and `textEdit` is eager value. ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * + * @author Dusan Balek + */ +public final class Completion { + + static { + CompletionAccessor.setDefault(new CompletionAccessor() { + @Override + public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, + List<Character> commitCharacters, Command command) { + return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters, command); + } + }); + } + + private final String label; + private final Kind kind; + private final List<Tag> tags; + private final CompletableFuture<String> detail; + private final CompletableFuture<String> documentation; + private final boolean preselect; + private final String sortText; + private final String filterText; + private final String insertText; + private final TextFormat insertTextFormat; + private final TextEdit textEdit; + private final CompletableFuture<List<TextEdit>> additionalTextEdits; + private final List<Character> commitCharacters; + private final Command command; + + private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, + TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters, Command command) { + this.label = label; + this.kind = kind; + this.tags = tags; + this.detail = detail; + this.documentation = documentation; + this.preselect = preselect; + this.sortText = sortText; + this.filterText = filterText; + this.insertText = insertText; + this.insertTextFormat = insertTextFormat; + this.textEdit = textEdit; + this.additionalTextEdits = additionalTextEdits; + this.commitCharacters = commitCharacters; + this.command = command; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public String getLabel() { + return label; + } + + /** + * The kind of this completion. + */ + @CheckForNull + public Kind getKind() { + return kind; + } + + /** + * Tags for this completion. + */ + @CheckForNull + public List<Tag> getTags() { + return tags != null ? Collections.unmodifiableList(tags) : null; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @CheckForNull + public CompletableFuture<String> getDetail() { Review comment: I am not sure `CompletableFuture` is the right API here? I would be more in favor of [CompletionStage](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/CompletionStage.html) which is also used in Eclipse Lsp4j classes. ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * + * @author Dusan Balek + */ +public final class Completion { + + static { + CompletionAccessor.setDefault(new CompletionAccessor() { + @Override + public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, + List<Character> commitCharacters, Command command) { + return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters, command); + } + }); + } + + private final String label; + private final Kind kind; + private final List<Tag> tags; + private final CompletableFuture<String> detail; + private final CompletableFuture<String> documentation; + private final boolean preselect; + private final String sortText; + private final String filterText; + private final String insertText; + private final TextFormat insertTextFormat; + private final TextEdit textEdit; + private final CompletableFuture<List<TextEdit>> additionalTextEdits; + private final List<Character> commitCharacters; + private final Command command; + + private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, + TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters, Command command) { + this.label = label; + this.kind = kind; + this.tags = tags; + this.detail = detail; + this.documentation = documentation; + this.preselect = preselect; + this.sortText = sortText; + this.filterText = filterText; + this.insertText = insertText; + this.insertTextFormat = insertTextFormat; + this.textEdit = textEdit; + this.additionalTextEdits = additionalTextEdits; + this.commitCharacters = commitCharacters; + this.command = command; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public String getLabel() { + return label; + } + + /** + * The kind of this completion. + */ + @CheckForNull + public Kind getKind() { + return kind; + } + + /** + * Tags for this completion. + */ + @CheckForNull + public List<Tag> getTags() { + return tags != null ? Collections.unmodifiableList(tags) : null; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @CheckForNull + public CompletableFuture<String> getDetail() { + return detail; + } + + /** + * A human-readable string that represents a doc-comment. An HTML format is + * supported. + */ + @CheckForNull + public CompletableFuture<String> getDocumentation() { + return documentation; + } + + /** + * Select this completion when showing. + */ + public boolean isPreselect() { + return preselect; + } + + /** + * A string that should be used when comparing this completion with other + * completions. When {@code null} the label is used as the sort text. + */ + @CheckForNull + public String getSortText() { + return sortText; + } + + /** + * A string that should be used when filtering a set of completions. + * When {@code null} the label is used as the filter. + */ + @CheckForNull + public String getFilterText() { + return filterText; + } + + /** + * A string that should be inserted into a document when selecting + * this completion. When {@code null} the label is used as the insert text. + */ + @CheckForNull + public String getInsertText() { + return insertText; + } + + /** + * The format of the insert text. The format applies to both the + * {@code insertText} property and the {@code newText} property of a provided + * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}. + */ + @CheckForNull + public TextFormat getInsertTextFormat() { + return insertTextFormat; + } + + /** + * An edit which is applied to a document when selecting this completion. + * When an edit is provided the value of {@code insertText} is ignored. Review comment: I know this is the behavior in the specification, but cannot we join the `insertText` (with `insertTextFormat`) and `getTextEdit` into one? E.g. regardless what one uses in the _builder_ always return a `textedit`. ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * + * @author Dusan Balek + */ +public final class Completion { + + static { + CompletionAccessor.setDefault(new CompletionAccessor() { + @Override + public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, + List<Character> commitCharacters, Command command) { + return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters, command); + } + }); + } + + private final String label; + private final Kind kind; + private final List<Tag> tags; + private final CompletableFuture<String> detail; + private final CompletableFuture<String> documentation; + private final boolean preselect; + private final String sortText; + private final String filterText; + private final String insertText; + private final TextFormat insertTextFormat; + private final TextEdit textEdit; + private final CompletableFuture<List<TextEdit>> additionalTextEdits; + private final List<Character> commitCharacters; + private final Command command; + + private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, + TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters, Command command) { + this.label = label; + this.kind = kind; + this.tags = tags; + this.detail = detail; + this.documentation = documentation; + this.preselect = preselect; + this.sortText = sortText; + this.filterText = filterText; + this.insertText = insertText; + this.insertTextFormat = insertTextFormat; + this.textEdit = textEdit; + this.additionalTextEdits = additionalTextEdits; + this.commitCharacters = commitCharacters; + this.command = command; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public String getLabel() { + return label; + } + + /** + * The kind of this completion. + */ + @CheckForNull + public Kind getKind() { + return kind; + } + + /** + * Tags for this completion. + */ + @CheckForNull + public List<Tag> getTags() { + return tags != null ? Collections.unmodifiableList(tags) : null; Review comment: Immutable. Good. ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * + * @author Dusan Balek + */ +public final class Completion { + + static { + CompletionAccessor.setDefault(new CompletionAccessor() { + @Override + public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, + List<Character> commitCharacters, Command command) { + return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters, command); + } + }); + } + + private final String label; + private final Kind kind; + private final List<Tag> tags; + private final CompletableFuture<String> detail; + private final CompletableFuture<String> documentation; + private final boolean preselect; + private final String sortText; + private final String filterText; + private final String insertText; + private final TextFormat insertTextFormat; + private final TextEdit textEdit; + private final CompletableFuture<List<TextEdit>> additionalTextEdits; + private final List<Character> commitCharacters; + private final Command command; + + private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, + TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters, Command command) { + this.label = label; + this.kind = kind; + this.tags = tags; + this.detail = detail; + this.documentation = documentation; + this.preselect = preselect; + this.sortText = sortText; + this.filterText = filterText; + this.insertText = insertText; + this.insertTextFormat = insertTextFormat; + this.textEdit = textEdit; + this.additionalTextEdits = additionalTextEdits; + this.commitCharacters = commitCharacters; + this.command = command; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public String getLabel() { + return label; + } + + /** + * The kind of this completion. + */ + @CheckForNull + public Kind getKind() { + return kind; + } + + /** + * Tags for this completion. + */ + @CheckForNull + public List<Tag> getTags() { + return tags != null ? Collections.unmodifiableList(tags) : null; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @CheckForNull + public CompletableFuture<String> getDetail() { + return detail; + } + + /** + * A human-readable string that represents a doc-comment. An HTML format is + * supported. + */ + @CheckForNull + public CompletableFuture<String> getDocumentation() { + return documentation; + } + + /** + * Select this completion when showing. + */ + public boolean isPreselect() { + return preselect; + } + + /** + * A string that should be used when comparing this completion with other + * completions. When {@code null} the label is used as the sort text. + */ + @CheckForNull + public String getSortText() { + return sortText; + } + + /** + * A string that should be used when filtering a set of completions. + * When {@code null} the label is used as the filter. + */ + @CheckForNull + public String getFilterText() { + return filterText; + } + + /** + * A string that should be inserted into a document when selecting + * this completion. When {@code null} the label is used as the insert text. + */ + @CheckForNull + public String getInsertText() { + return insertText; + } + + /** + * The format of the insert text. The format applies to both the + * {@code insertText} property and the {@code newText} property of a provided + * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}. + */ + @CheckForNull + public TextFormat getInsertTextFormat() { + return insertTextFormat; + } + + /** + * An edit which is applied to a document when selecting this completion. + * When an edit is provided the value of {@code insertText} is ignored. + * The range of the edit must be a single line range and it must + * contain the position at which completion has been requested. + */ + @CheckForNull + public TextEdit getTextEdit() { + return textEdit; + } + + /** + * A list of additional text edits that are applied when selecting this + * completion. Edits must not overlap (including the same insert position) + * with the main edit nor with themselves. + * Additional text edits should be used to change text unrelated to the + * current cursor position (for example adding an import statement at the + * top of the file if the completion item will insert an unqualified type). + */ + @CheckForNull + public CompletableFuture<List<TextEdit>> getAdditionalTextEdits() { Review comment: Why is `textEdit` property and `additionalTextEdits` a lazy property? Btw. cannot these two be merged into `textEdits` returning of `List<TextEdit>`? ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * + * @author Dusan Balek + */ +public final class Completion { + + static { + CompletionAccessor.setDefault(new CompletionAccessor() { + @Override + public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, + List<Character> commitCharacters, Command command) { + return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters, command); + } + }); + } + + private final String label; + private final Kind kind; + private final List<Tag> tags; + private final CompletableFuture<String> detail; + private final CompletableFuture<String> documentation; + private final boolean preselect; + private final String sortText; + private final String filterText; + private final String insertText; + private final TextFormat insertTextFormat; + private final TextEdit textEdit; + private final CompletableFuture<List<TextEdit>> additionalTextEdits; + private final List<Character> commitCharacters; + private final Command command; + + private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, + TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters, Command command) { + this.label = label; + this.kind = kind; + this.tags = tags; + this.detail = detail; + this.documentation = documentation; + this.preselect = preselect; + this.sortText = sortText; + this.filterText = filterText; + this.insertText = insertText; + this.insertTextFormat = insertTextFormat; + this.textEdit = textEdit; + this.additionalTextEdits = additionalTextEdits; + this.commitCharacters = commitCharacters; + this.command = command; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public String getLabel() { + return label; + } + + /** + * The kind of this completion. + */ + @CheckForNull + public Kind getKind() { + return kind; + } + + /** + * Tags for this completion. + */ + @CheckForNull + public List<Tag> getTags() { + return tags != null ? Collections.unmodifiableList(tags) : null; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @CheckForNull + public CompletableFuture<String> getDetail() { + return detail; + } + + /** + * A human-readable string that represents a doc-comment. An HTML format is + * supported. + */ + @CheckForNull + public CompletableFuture<String> getDocumentation() { + return documentation; + } + + /** + * Select this completion when showing. + */ + public boolean isPreselect() { + return preselect; + } + + /** + * A string that should be used when comparing this completion with other + * completions. When {@code null} the label is used as the sort text. + */ + @CheckForNull + public String getSortText() { + return sortText; + } + + /** + * A string that should be used when filtering a set of completions. + * When {@code null} the label is used as the filter. + */ + @CheckForNull + public String getFilterText() { + return filterText; + } + + /** + * A string that should be inserted into a document when selecting + * this completion. When {@code null} the label is used as the insert text. + */ + @CheckForNull + public String getInsertText() { + return insertText; + } + + /** + * The format of the insert text. The format applies to both the + * {@code insertText} property and the {@code newText} property of a provided + * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}. + */ + @CheckForNull + public TextFormat getInsertTextFormat() { + return insertTextFormat; + } + + /** + * An edit which is applied to a document when selecting this completion. + * When an edit is provided the value of {@code insertText} is ignored. + * The range of the edit must be a single line range and it must + * contain the position at which completion has been requested. + */ + @CheckForNull + public TextEdit getTextEdit() { + return textEdit; + } + + /** + * A list of additional text edits that are applied when selecting this + * completion. Edits must not overlap (including the same insert position) + * with the main edit nor with themselves. + * Additional text edits should be used to change text unrelated to the + * current cursor position (for example adding an import statement at the + * top of the file if the completion item will insert an unqualified type). + */ + @CheckForNull + public CompletableFuture<List<TextEdit>> getAdditionalTextEdits() { + return additionalTextEdits; + } + + /** + * A list of characters that when pressed while this completion is + * active will accept it first and then type that character. + */ + @CheckForNull + public List<Character> getCommitCharacters() { + return commitCharacters != null ? Collections.unmodifiableList(commitCharacters) : null; + } + + /** + * A command that is executed after inserting this completion. + */ + @CheckForNull + public Command getCommand() { + return command; + } + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. + * + * @param doc a text document + * @param offset an offset inside the text document + * @param context an optional completion context + * @param consumer an operation accepting collected completions + * + * @return true if the list of collected completion is complete + */ + public static boolean collect(@NonNull Document doc, int offset, @NullAllowed Context context, @NonNull Consumer<Completion> consumer) { + boolean isComplete = true; + MimePath mimePath = MimePath.parse(DocumentUtilities.getMimeType(doc)); + for (CompletionCollector collector : MimeLookup.getLookup(mimePath).lookupAll(CompletionCollector.class)) { + isComplete &= collector.collectCompletions(doc, offset, context, consumer); + } + return isComplete; + } + + /** + * Contains additional information about the context in which a request for + * collections completions is triggered. + */ + public static final class Context { + + private final TriggerKind triggerKind; + private final String triggerCharacter; + + public Context(@NonNull TriggerKind triggerKind, @NullAllowed String triggerCharacter) { + this.triggerKind = triggerKind; + this.triggerCharacter = triggerCharacter; + } + + /** + * How the completion was triggered. + */ + @NonNull + public TriggerKind getTriggerKind() { + return triggerKind; + } + + /** + * The trigger character (a single character) that has trigger code complete. + * Is undefined if {@code triggerKind != TriggerKind.TriggerCharacter}. + */ + @CheckForNull + public String getTriggerCharacter() { Review comment: Character should be `char`, not `String` in Java. ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/Completion.java ########## @@ -0,0 +1,405 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.lsp.CompletionCollector; + +/** + * Represents a completion proposal. + * + * @author Dusan Balek + */ +public final class Completion { + + static { + CompletionAccessor.setDefault(new CompletionAccessor() { + @Override + public Completion createCompletion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, + List<Character> commitCharacters, Command command) { + return new Completion(label, kind, tags, detail, documentation, preselect, sortText, filterText, insertText, insertTextFormat, textEdit, additionalTextEdits, commitCharacters, command); + } + }); + } + + private final String label; + private final Kind kind; + private final List<Tag> tags; + private final CompletableFuture<String> detail; + private final CompletableFuture<String> documentation; + private final boolean preselect; + private final String sortText; + private final String filterText; + private final String insertText; + private final TextFormat insertTextFormat; + private final TextEdit textEdit; + private final CompletableFuture<List<TextEdit>> additionalTextEdits; + private final List<Character> commitCharacters; + private final Command command; + + private Completion(String label, Kind kind, List<Tag> tags, CompletableFuture<String> detail, CompletableFuture<String> documentation, + boolean preselect, String sortText, String filterText, String insertText, TextFormat insertTextFormat, + TextEdit textEdit, CompletableFuture<List<TextEdit>> additionalTextEdits, List<Character> commitCharacters, Command command) { + this.label = label; + this.kind = kind; + this.tags = tags; + this.detail = detail; + this.documentation = documentation; + this.preselect = preselect; + this.sortText = sortText; + this.filterText = filterText; + this.insertText = insertText; + this.insertTextFormat = insertTextFormat; + this.textEdit = textEdit; + this.additionalTextEdits = additionalTextEdits; + this.commitCharacters = commitCharacters; + this.command = command; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public String getLabel() { + return label; + } + + /** + * The kind of this completion. + */ + @CheckForNull + public Kind getKind() { + return kind; + } + + /** + * Tags for this completion. + */ + @CheckForNull + public List<Tag> getTags() { + return tags != null ? Collections.unmodifiableList(tags) : null; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @CheckForNull + public CompletableFuture<String> getDetail() { + return detail; + } + + /** + * A human-readable string that represents a doc-comment. An HTML format is + * supported. + */ + @CheckForNull + public CompletableFuture<String> getDocumentation() { + return documentation; + } + + /** + * Select this completion when showing. + */ + public boolean isPreselect() { + return preselect; + } + + /** + * A string that should be used when comparing this completion with other + * completions. When {@code null} the label is used as the sort text. + */ + @CheckForNull + public String getSortText() { + return sortText; + } + + /** + * A string that should be used when filtering a set of completions. + * When {@code null} the label is used as the filter. + */ + @CheckForNull + public String getFilterText() { + return filterText; + } + + /** + * A string that should be inserted into a document when selecting + * this completion. When {@code null} the label is used as the insert text. + */ + @CheckForNull + public String getInsertText() { + return insertText; + } + + /** + * The format of the insert text. The format applies to both the + * {@code insertText} property and the {@code newText} property of a provided + * {@code textEdit}. If omitted defaults to {@link TextFormat#PlainText}. + */ + @CheckForNull + public TextFormat getInsertTextFormat() { + return insertTextFormat; + } + + /** + * An edit which is applied to a document when selecting this completion. + * When an edit is provided the value of {@code insertText} is ignored. + * The range of the edit must be a single line range and it must + * contain the position at which completion has been requested. + */ + @CheckForNull + public TextEdit getTextEdit() { + return textEdit; + } + + /** + * A list of additional text edits that are applied when selecting this + * completion. Edits must not overlap (including the same insert position) + * with the main edit nor with themselves. + * Additional text edits should be used to change text unrelated to the + * current cursor position (for example adding an import statement at the + * top of the file if the completion item will insert an unqualified type). + */ + @CheckForNull + public CompletableFuture<List<TextEdit>> getAdditionalTextEdits() { + return additionalTextEdits; + } + + /** + * A list of characters that when pressed while this completion is + * active will accept it first and then type that character. + */ + @CheckForNull + public List<Character> getCommitCharacters() { + return commitCharacters != null ? Collections.unmodifiableList(commitCharacters) : null; + } + + /** + * A command that is executed after inserting this completion. + */ + @CheckForNull + public Command getCommand() { + return command; + } + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. + * + * @param doc a text document + * @param offset an offset inside the text document + * @param context an optional completion context + * @param consumer an operation accepting collected completions + * + * @return true if the list of collected completion is complete + */ + public static boolean collect(@NonNull Document doc, int offset, @NullAllowed Context context, @NonNull Consumer<Completion> consumer) { + boolean isComplete = true; + MimePath mimePath = MimePath.parse(DocumentUtilities.getMimeType(doc)); + for (CompletionCollector collector : MimeLookup.getLookup(mimePath).lookupAll(CompletionCollector.class)) { + isComplete &= collector.collectCompletions(doc, offset, context, consumer); + } + return isComplete; + } + + /** + * Contains additional information about the context in which a request for + * collections completions is triggered. + */ + public static final class Context { + + private final TriggerKind triggerKind; + private final String triggerCharacter; + + public Context(@NonNull TriggerKind triggerKind, @NullAllowed String triggerCharacter) { + this.triggerKind = triggerKind; + this.triggerCharacter = triggerCharacter; + } + + /** + * How the completion was triggered. + */ + @NonNull + public TriggerKind getTriggerKind() { + return triggerKind; + } + + /** + * The trigger character (a single character) that has trigger code complete. + * Is undefined if {@code triggerKind != TriggerKind.TriggerCharacter}. + */ + @CheckForNull + public String getTriggerCharacter() { + return triggerCharacter; + } + } + + public enum TriggerKind { + + /** + * Completion was triggered by typing an identifier (24x7 code + * complete), manual invocation (e.g Ctrl+Space) or via API. + */ + Invoked(1), + + /** + * Completion was triggered by a trigger character. + */ + TriggerCharacter(2), + + /** + * Completion was re-triggered as the current completion list is incomplete. + */ + TriggerForIncompleteCompletions(3); + + private final int value; + + private TriggerKind(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static TriggerKind forValue(int value) { Review comment: Each `enum` by default gets ``` public static TriggerKind valueOf(String name); ``` method. Isn't `forValue` confusing? Shouldn't the `forValue` method also be called `valueOf(int)`? Btw. there also is `Enum.ordinal()` which in this case is going to be one off to `getValue()`... ########## File path: ide/api.lsp/src/org/netbeans/api/lsp/HyperlinkLocation.java ########## @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.api.lsp; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.lsp.HyperlinkLocationAccessor; +import org.netbeans.spi.lsp.HyperlinkLocationProvider; +import org.openide.filesystems.FileObject; + +/** + * Represents the target location of a hyperlink. Location is a range inside a + * file object, such as a line inside a text file. + * + * @author Dusan Balek + */ +public final class HyperlinkLocation { + + static { + HyperlinkLocationAccessor.setDefault(new HyperlinkLocationAccessor() { Review comment: Interesting. It is usually enough to have one accessor per (pair of) package(s). Having more isn't a problem however. ########## File path: ide/api.lsp/src/org/netbeans/spi/lsp/CompletionCollector.java ########## @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.spi.lsp; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.lsp.Command; +import org.netbeans.api.lsp.Completion; +import org.netbeans.api.lsp.TextEdit; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.editor.mimelookup.MimeLocation; + +/** + * Interface for computing and collecting completions. Clients can use this interface + * to collect completions and send them for presentation outside of NetBeans using + * the Language Server Protocol. Implementations of the interface should be registered + * in MimeLookup. + * + * @author Dusan Balek + */ +@MimeLocation(subfolderName = "CompletionCollectors") +public interface CompletionCollector { + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. + * + * @param doc a text document + * @param offset an offset inside the text document + * @param context an optional completion context + * @param consumer an operation accepting collected completions + * + * @return true if the list of collected completion is complete + */ + public boolean collectCompletions(@NonNull Document doc, int offset, @NullAllowed Completion.Context context, @NonNull Consumer<Completion> consumer); + + /** + * Creates a builder for {@link Completion} instances + * + * @param label the label of the completion + * @return newly created builder + */ + public static Builder newBuilder(@NonNull String label) { + return new Builder(label); + } + + public static final class Builder { + + private String label; + private Completion.Kind kind; + private List<Completion.Tag> tags; + private CompletableFuture<String> detail; + private CompletableFuture<String> documentation; + private boolean preselect; + private String sortText; + private String filterText; + private String insertText; + private Completion.TextFormat insertTextFormat; + private TextEdit textEdit; + private CompletableFuture<List<TextEdit>> additionalTextEdits; + private List<Character> commitCharacters; + private Command command; + + private Builder(@NonNull String label) { + this.label = label; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public Builder label(@NonNull String label) { + this.label = label; + return this; + } + + /** + * The kind of this completion. + */ + @NonNull + public Builder kind(@NonNull Completion.Kind kind) { + this.kind = kind; + return this; + } + + /** + * Adds tag for this completion. + */ + @NonNull + public Builder addTag(@NonNull Completion.Tag tag) { + if (this.tags == null) { + this.tags = new ArrayList<>(); Review comment: Cumulative! Nice. ########## File path: ide/api.lsp/src/org/netbeans/spi/lsp/CompletionCollector.java ########## @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.spi.lsp; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.lsp.Command; +import org.netbeans.api.lsp.Completion; +import org.netbeans.api.lsp.TextEdit; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.editor.mimelookup.MimeLocation; + +/** + * Interface for computing and collecting completions. Clients can use this interface + * to collect completions and send them for presentation outside of NetBeans using + * the Language Server Protocol. Implementations of the interface should be registered + * in MimeLookup. + * + * @author Dusan Balek + */ +@MimeLocation(subfolderName = "CompletionCollectors") +public interface CompletionCollector { + + /** + * Computes and collects completions for a document at a given offset. + * This method is called outside of AWT to collect completions and + * send them via the Language Server Protocol to client for display. + * + * @param doc a text document + * @param offset an offset inside the text document + * @param context an optional completion context + * @param consumer an operation accepting collected completions + * + * @return true if the list of collected completion is complete + */ + public boolean collectCompletions(@NonNull Document doc, int offset, @NullAllowed Completion.Context context, @NonNull Consumer<Completion> consumer); + + /** + * Creates a builder for {@link Completion} instances + * + * @param label the label of the completion + * @return newly created builder + */ + public static Builder newBuilder(@NonNull String label) { + return new Builder(label); + } + + public static final class Builder { + + private String label; + private Completion.Kind kind; + private List<Completion.Tag> tags; + private CompletableFuture<String> detail; + private CompletableFuture<String> documentation; + private boolean preselect; + private String sortText; + private String filterText; + private String insertText; + private Completion.TextFormat insertTextFormat; + private TextEdit textEdit; + private CompletableFuture<List<TextEdit>> additionalTextEdits; + private List<Character> commitCharacters; + private Command command; + + private Builder(@NonNull String label) { + this.label = label; + } + + /** + * The label of this completion. By default also the text that is inserted + * when selecting this completion. + */ + @NonNull + public Builder label(@NonNull String label) { + this.label = label; + return this; + } + + /** + * The kind of this completion. + */ + @NonNull + public Builder kind(@NonNull Completion.Kind kind) { + this.kind = kind; + return this; + } + + /** + * Adds tag for this completion. + */ + @NonNull + public Builder addTag(@NonNull Completion.Tag tag) { + if (this.tags == null) { + this.tags = new ArrayList<>(); + } + this.tags.add(tag); + return this; + } + + /** + * A human-readable string with additional information + * about this completion, like type or symbol information. + */ + @NonNull + public Builder detail(@NonNull CompletableFuture<String> detail) { Review comment: I complained about use of `CompletableFuture` in the API and here I am going to complain as well. In my opinion here we don't want `CompletableFuture` at all. Ideally we want to register _some computation_ that gets triggered when `Completion.getDetail()` is called. My favorite signature is: ``` public Builder detail(Executor whereToRunTheSupplier, Supplier<String> computeTheValueWhenNeeded); ``` the API part would then be ``` CompletionStage<String> getDetail() { return CompletableFuture.supplyAsync(computeTheValueWhenNeeded, whereToRunTheSupplier); } ``` preferrably without allowing one to cast to `CompletableFuture` and also keeping the first instances, once computation is started. What do you think @sdedic? ########## File path: ide/api.lsp/src/org/netbeans/spi/lsp/CompletionCollector.java ########## @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.spi.lsp; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.api.lsp.Command; +import org.netbeans.api.lsp.Completion; +import org.netbeans.api.lsp.TextEdit; +import org.netbeans.modules.lsp.CompletionAccessor; +import org.netbeans.spi.editor.mimelookup.MimeLocation; + +/** + * Interface for computing and collecting completions. Clients can use this interface + * to collect completions and send them for presentation outside of NetBeans using + * the Language Server Protocol. Implementations of the interface should be registered + * in MimeLookup. Review comment: Example of registration with `{@codesnipptet}` would be nice. ########## File path: ide/api.lsp/src/org/netbeans/spi/lsp/HyperlinkLocationProvider.java ########## @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.spi.lsp; + +import org.netbeans.api.lsp.HyperlinkLocation; +import java.util.concurrent.CompletableFuture; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.modules.lsp.HyperlinkLocationAccessor; +import org.netbeans.spi.editor.mimelookup.MimeLocation; +import org.openide.filesystems.FileObject; + +/** + * Interface for resolving hyperlink locations. Implementations of the interface + * should be registered in MimeLookup. Review comment: Example with `@codesnippet` would be nice. ########## File path: ide/api.lsp/src/org/netbeans/spi/lsp/HyperlinkLocationProvider.java ########## @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.spi.lsp; + +import org.netbeans.api.lsp.HyperlinkLocation; +import java.util.concurrent.CompletableFuture; +import javax.swing.text.Document; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.modules.lsp.HyperlinkLocationAccessor; +import org.netbeans.spi.editor.mimelookup.MimeLocation; +import org.openide.filesystems.FileObject; + +/** + * Interface for resolving hyperlink locations. Implementations of the interface + * should be registered in MimeLookup. + * + * @author Dusan Balek + */ +@MimeLocation(subfolderName = "HyperlinkLocationProviders") +public interface HyperlinkLocationProvider { + + /** + * Resolves a hyperlink at the given document offset and returns its + * target location. + * + * @param doc document on which to operate. + * @param offset offset within document + * @return target location + */ + CompletableFuture<HyperlinkLocation> getHyperlinkLocation(@NonNull Document doc, int offset); + + public static HyperlinkLocation createHyperlinkLocation(@NonNull FileObject fileObject, int startOffset, int endOffset) { Review comment: Missing javadoc/example. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected] For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists
