Auto-completion outside an Eclipse editor, API reuse [message #1386179] |
Sat, 14 June 2014 08:04 |
Mathieu Acher Messages: 1 Registered: June 2014 |
Junior Member |
|
|
Hi,
I am seeking a function for completing a portion of code (mainly outside Eclipse).
Roughly speaking, the function will take as input the content of the code and a position (offset) stating where we are in the code.
The function will return a set of possible completions.
Collection<String> complete (String content, int offset)
So far I have investigated how auto-completion is implemented with Eclipse editors generated by Xtext.
Everything is in org.eclipse.xtext.ui.editor.contentassist.
In particular, in CompletionProposalComputer, there is
public ICompletionProposal[] exec(XtextResource resource) throws Exception
and there is a field "offset" (among others).
Everything seems to be fine but unfortunately the code is highly coupled to an Eclipse editor (for instance, ITextViewer).
Same after with the interface IContentProposalProvider (an interface implemented by the generated class of Xtext)
void createProposals(ContentAssistContext context, ICompletionProposalAcceptor acceptor);
Basically part of the API related to content assist (even the class generated by Xtext for exploiting the grammar of a DSL) is highly coupled to an Eclipse editor, precluding a direct reuse in other settings. It does make sense of course, but here are my question.
Are you aware of a simple function (like the one described at the very beginning of the post) for supporting auto-completion?
Is there any attempt to provide such function (e.g., outside Eclipse)?
|
|
|
|
|
|
|
|
|
|
|
Re: Auto-completion outside an Eclipse editor, API reuse [message #1722463 is a reply to message #1722462] |
Fri, 05 February 2016 05:37 |
|
Hi,
if i understand you correct, you have to points
- you dont want to use xtext client side api at all but directly talk to the xtext server
- you want to run the server in stateless mode
you can always send the fulltext to the server if you want to run stateless
Here is a simple example that exactly does this
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="en-us">
<title>Example Web Editor</title>
<link rel="stylesheet" type="text/css" href="xtext/2.9.2-SNAPSHOT/xtext-ace.css"/>
<link rel="stylesheet" type="text/css" href="style.css"/>
<script src="webjars/requirejs/2.1.20/require.min.js"></script>
<script type="text/javascript">
var baseUrl = window.location.pathname;
var fileIndex = baseUrl.indexOf("index.html");
if (fileIndex > 0)
baseUrl = baseUrl.slice(0, fileIndex);
require.config({
baseUrl: baseUrl,
paths: {
"jquery": "webjars/jquery/2.1.4/jquery.min"
}
});
require(["../webjars/jquery/2.1.4/jquery.min"], function() {
jQuery(document).ready(function() {
jQuery("#generate-button").click(function(e){
var text = "Hello World!";
var data = {
resource : "example1.mydsl2",
fullText : $("#mytextarea").val()
};
jQuery.post('http://' + location.host + '/xtext-service/generate', data, function(result){
jQuery("#generator-result").text(result);
});
});
});
});
</script>
</head>
<body>
<textarea id="mytextarea">
Hello Christian!
Hello Roy!
</textarea>
<button id="generate-button" value="Generate" title="Generate">Generate</button>
<div id="generator-result">
</div>
</body>
</html>
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
|
|
|
|
|
|
|
|
|
|
Re: Auto-completion outside an Eclipse editor, API reuse [message #1722892 is a reply to message #1722891] |
Wed, 10 February 2016 07:09 |
|
hi,
no there is nothing. you have to have a look at the server side code XtextServlet, XtextServiceDispatcher, ContentAssistService and the dependencies they use and build up "your" service as well.
as a starting point you may use this quick hack.
(all beyond that you have to build up and find out how to use the api yourself)
import com.google.common.base.Charsets
import com.google.inject.Injector
import java.io.ByteArrayInputStream
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.xtext.util.TextRegion
import org.eclipse.xtext.web.server.IServiceContext
import org.eclipse.xtext.web.server.contentassist.ContentAssistService
import org.eclipse.xtext.web.server.model.IWebDocumentProvider
import org.eclipse.xtext.web.server.model.XtextWebDocumentAccess
import com.google.inject.Provider
import org.eclipse.xtext.web.server.ISession
import org.eclipse.xtext.web.server.model.XtextWebDocument
import org.eclipse.xtext.resource.XtextResource
class Main {
def static void main(String[] args) {
val s = new ISession.NullImpl
val IServiceContext sc = new IServiceContext() {
override getParameter(String key) {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}
override getParameterKeys() {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}
override getSession() {
s
}
}
val Provider<ExecutorService> executorServiceProvider = [Executors.newCachedThreadPool]
val Injector i = new MyDslWebSetup(executorServiceProvider).createInjectorAndDoEMFRegistration()
val rs = i.getInstance(ResourceSet);
val r = rs.createResource(URI.createURI("test.mydsl2")) as XtextResource
r.load(new ByteArrayInputStream('''Hello Hello Christian! Hello Roy!'''.toString.getBytes(Charsets.UTF_8)), null)
val pp = i.getInstance(ContentAssistService)
val dp = i.getInstance(IWebDocumentProvider)
val XtextWebDocument doc = dp.get("dummy.mydsl2", sc)
doc.input = r
val da = i.getInstance(XtextWebDocumentAccess.Factory).create(doc, "-80000000" , true)
val selection = new TextRegion(6, 0)
val result = pp.createProposals(da, selection, 6, 100)
println(result)
}
}
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
|
|
|
|
|
|
|
Re: Auto-completion outside an Eclipse editor, API reuse [message #1722942 is a reply to message #1722938] |
Wed, 10 February 2016 12:04 |
|
e.g. something like
import com.google.common.base.Charsets
import com.google.inject.Binder
import com.google.inject.Guice
import com.google.inject.Injector
import com.google.inject.Module
import com.google.inject.Provider
import com.google.inject.name.Names
import com.google.inject.util.Modules
import java.io.ByteArrayInputStream
import java.util.Arrays
import java.util.HashSet
import java.util.List
import javax.inject.Inject
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.xtend.lib.annotations.Data
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
import org.eclipse.xtend.lib.annotations.ToString
import org.eclipse.xtext.ide.LexerIdeBindings
import org.eclipse.xtext.ide.editor.contentassist.ContentAssistContext
import org.eclipse.xtext.ide.editor.contentassist.ContentAssistEntry
import org.eclipse.xtext.ide.editor.contentassist.IIdeContentProposalAcceptor
import org.eclipse.xtext.ide.editor.contentassist.IdeContentProposalProvider
import org.eclipse.xtext.ide.editor.contentassist.antlr.ContentAssistContextFactory
import org.eclipse.xtext.ide.editor.contentassist.antlr.IContentAssistParser
import org.eclipse.xtext.ide.editor.contentassist.antlr.internal.Lexer
import org.eclipse.xtext.resource.XtextResource
import org.eclipse.xtext.util.ITextRegion
import org.eclipse.xtext.util.TextRegion
import org.xtext.example.mydsl2.MyDslRuntimeModule
import org.xtext.example.mydsl2.MyDslStandaloneSetup
import org.xtext.example.mydsl2.ide.StatemachineWebContentProposalProvider
import org.xtext.example.mydsl2.ide.contentassist.antlr.MyDslParser
import org.xtext.example.mydsl2.ide.contentassist.antlr.internal.InternalMyDslLexer
import java.util.concurrent.Executors
class Main {
def static void main(String[] args) {
try {
val Injector i = new MyDslStandloneCASetup().createInjectorAndDoEMFRegistration()
val rs = i.getInstance(ResourceSet);
val r = rs.createResource(URI.createURI("test.mydsl2")) as XtextResource
r.load(new ByteArrayInputStream('''Hello Hello Christian! Hello Roy!'''.toString.getBytes(Charsets.UTF_8)),
null)
val sca = i.getInstance(StandaloneContentAssistService)
val selection = new TextRegion(6, 0)
val result2 = sca.createProposals(r, r.parseResult.rootNode.text, selection, 6, 1000)
println(result2)
} catch (Exception e) {
e.printStackTrace
}
}
}
class MyDslCAModule implements Module {
override configure(Binder binder) {
binder.bind(IdeContentProposalProvider).to(StatemachineWebContentProposalProvider)
binder.bind(IContentAssistParser).to(MyDslParser)
binder.bind(Lexer).annotatedWith(Names.named(LexerIdeBindings.CONTENT_ASSIST)).to(InternalMyDslLexer);
}
}
@FinalFieldsConstructor
class MyDslStandloneCASetup extends MyDslStandaloneSetup {
override Injector createInjector() {
val runtimeModule = new MyDslRuntimeModule()
return Guice.createInjector(Modules.override(runtimeModule).with(new MyDslCAModule))
}
}
class StandaloneContentAssistService {
@Inject Provider<ContentAssistContextFactory> contextFactoryProvider
@Inject IdeContentProposalProvider proposalProvider
def createProposals(XtextResource resource, String text, ITextRegion selection, int caretOffset, int limit) {
val contexts = getContexts(resource, text, selection, caretOffset)
return createProposals(Arrays.asList(contexts), limit)
}
def private ContentAssistContext[] getContexts(XtextResource resource, String text, ITextRegion selection,
int caretOffset) {
if (caretOffset > text.length)
return #[]
val contextFactory = contextFactoryProvider.get() => [pool = Executors.newCachedThreadPool]
contextFactory.create(text, selection, caretOffset, resource)
}
def private createProposals(List<ContentAssistContext> contexts, int proposalsLimit) {
val result = new ContentAssistResult()
if (!contexts.empty) {
val proposals = new HashSet<Pair<Integer, ContentAssistEntry>>
val acceptor = new IIdeContentProposalAcceptor {
override accept(ContentAssistEntry entry, int priority) {
if (entry.proposal === null)
throw new IllegalArgumentException('proposal must not be null.')
proposals.add(priority -> entry)
}
override canAcceptMoreProposals() {
proposals.size < proposalsLimit
}
}
proposalProvider.createProposals(contexts, acceptor)
result.entries.addAll(proposals.sortWith [ p1, p2 |
val prioResult = p2.key.compareTo(p1.key)
if (prioResult != 0)
return prioResult
val s1 = p1.value.label ?: p1.value.proposal
val s2 = p2.value.label ?: p2.value.proposal
return s1.compareTo(s2)
].map[value])
}
return result
}
}
@Data
@ToString(skipNulls=true)
class ContentAssistResult {
val List<ContentAssistEntry> entries = newArrayList
}
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
|
|
|
|
|
|
|