Translate this page...

BeanDev: Code Completion abhängig vom Java Context

Wenn man für Java Quelltext Code Completion hinzufügen möchte, steht man schnell vor der Frage, wie man das Swing-Document orientierte Code Completion mit der Java Infrastruktur verknüpft.

Geertjan hat in seinem Blogeintrag gezeigt, dass man das über eine extra Service lösen kann. Es wird eine eigenständiger Service als CaretAwareJavaSourceTaskFactory registriert. Dieser gibt über einen globalen statischen Wert bekannt, ob an der aktuelen Cursor-Position die Code Completion-Liste mit eigenen Werten versehen werden soll. Sein CompletionProvider schnappt sich diesen globalen Wert und füllt dann die CompletionItem-Liste, oder auch nicht.

Die lockere Verbindung mit unterschiedlichen Tasks funktioniert, aber ich habe da so meine Befürchtungen, dass das nicht immer deterministisch ist, wenn zwei nebenläufige Prozesse sich über eine globale Variable unterhalten wollen.

Glücklicherweise spricht nichts dagegen im eigenen CompletionProvider selbst einen ActionTask zu starten, um die Analyse direkt in die Hand zu nehmen.

Ein CompletionProvider muss per createTask einen CompletionTask zurückgeben. Dieser Task wird immer dann aufgerufen, wenn der Anwender die Code Completion manuell aktiviert (oder die Code Completion automatisch ausgelöst wird).

Der CompletionTask ist i.d.R. immer eine AsyncCompletionQuery die mehrere Methoden überschreiben kann. Die Methode

public void query (CompletionResultSet completionResultSet, Document document, int caretOffset);

füllt das CompletionResultSet mit eigenen Einträgen. Es gibt nun verschiedene Eingangsparameter, die eine solche Liste begrenzen. Zum einem die aktuelle Benutzereingabe und zum anderen der Scope. Mit Scope ist gemeint, wo die Eingabe gerade durchgeführt wird.

Es macht nicht immer Sinn die eigenen Completion-Items der Liste hinzuzufügen, weil die Eingabeposition im Programmcode überhaupt nicht zu den möglichen Completion Items passt.

Also muss man den semantischen und syntaktischen Kontext prüfen, bevor man den Anwender mit einer sonst überfüllten Liste von Ersetzungswerten vollpumpt.

Hier dient wieder die Hilfsklasse JavaSource, um aus dem Document einen Zugriff auf den Parser zu bekommen. Für ein eigenes Color-Code Completion-Modul habe ich folgende simple Programmierung:

JavaSource js = JavaSource.forDocument(component.getDocument());
js.runUserActionTask(ccpt = new ColorCompletionPreparerTask(component), false);

Die Klasse ColorCompletionPreparerTask enthält eine kleine run-Methode, die die aktuelle Cursor-Position versucht zu analysieren.

  public void run(CompilationController ci) throws Exception {
    ci.toPhase(Phase.ELEMENTS_RESOLVED);
    TreePath tp = ci.getTreeUtilities().pathFor(component.getCaretPosition());

    Kind kind = tp.getLeaf().getKind();
    if (kind == Kind.METHOD_INVOCATION) {
      valid = true;
    }

    Element parent = getIdentifierElement(ci, tp);
    if (parent != null) {
      if (parent.asType().toString().endsWith("Color")) {
        valid = true;
      }
    }

    Element element = ci.getTrees().getElement(tp);

    if (element != null) {
      switch (element.getKind()) {
        case CLASS: {
          if (kind != Kind.METHOD_INVOCATION) {
            valid = false;
          }
          break;
        }
      }
    }
  }

Dazu gibt es noch eine Hilfsmethode getIdentifierElement(...):

  private Element getIdentifierElement(CompilationInfo ci, TreePath identifier) {
    Kind kind = identifier.getLeaf().getKind();
    if (kind == Kind.IDENTIFIER) {
      TreePath parent = identifier.getParentPath();
      Element e = ci.getTrees().getElement(parent);
      if (e != null && e.getKind() == ElementKind.FIELD) {
        return e;
      }
    }
    if (kind == Kind.VARIABLE) {
      Element e = ci.getTrees().getElement(identifier);
      if (e != null && (e.getKind() == ElementKind.LOCAL_VARIABLE || e.getKind() == ElementKind.FIELD)) {
        return e;
      }
    }
    if (kind == Kind.ASSIGNMENT) {
      // ?
    }
    return null;
  }

Die Klasse gibt per isValid() den Status zurück, der im CompletionProvider abgefragt werden kann. Wenn der Wert true ist, wird die Completion-List mit einigen Color.* Konstanten und weiteren Color-Werten gefüllt. Ein Icon zeigt die Farbe in der Completion List auch an, damit man einen besseren Eindruck davon hat.

Allerdings ist der ColorCompletionPreparerTask recht simpel und erfasst nicht alle Möglichkeiten (zum Beispiel innerhalb von Ausdrücken oder bei Zuweisungen, bei denen der Typ an anderer Stelle deklariert wurde).

Das dies nicht sehr trivial ist, zeigt der Sourcecode von JavaCompletionProvider, den man sich hier mal betrachten kann

Beste Grüße,  Josch.

 

 

http://www.netbeans-forum.de/ Da werden Sie geholfen:
Das deutsche NetBeans Forum

      NetBeans Dream Team Member

Hinterlasse eine Nachricht

Meine Informationen merken

CAPTCHA Bild zum Spamschutz 

[Valid RSS]