com.ansorgit.plugins.bash.editor.codecompletion.BashCompletionContributor.java Source code

Java tutorial

Introduction

Here is the source code for com.ansorgit.plugins.bash.editor.codecompletion.BashCompletionContributor.java

Source

/*
 * Copyright 2010 Joachim Ansorg, mail@ansorg-it.com
 * File: BashCompletionContributor.java, Class: BashCompletionContributor
 * Last modified: 2013-02-03
 *
 * Licensed 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 com.ansorgit.plugins.bash.editor.codecompletion;

import com.ansorgit.plugins.bash.lang.lexer.BashTokenTypes;
import com.ansorgit.plugins.bash.lang.psi.api.word.BashWord;
import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionInitializationContext;
import com.intellij.codeInsight.completion.OffsetMap;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.TokenSet;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;

import static com.intellij.codeInsight.completion.CompletionInitializationContext.IDENTIFIER_END_OFFSET;
import static com.intellij.codeInsight.completion.CompletionInitializationContext.START_OFFSET;

/**
 * Bash completion contributor.
 */
public class BashCompletionContributor extends CompletionContributor {
    private final static TokenSet endTokens = TokenSet.create(BashTokenTypes.STRING_END,
            BashTokenTypes.RIGHT_CURLY);
    private final static TokenSet wordTokens = TokenSet.create(BashTokenTypes.WORD);

    public BashCompletionContributor() {
        new VariableNameCompletionProvider().addTo(this);
        new CommandNameCompletionProvider().addTo(this);
        new AbsolutePathCompletionProvider().addTo(this);
        new ShebangPathCompletionProvider().addTo(this);
        new DynamicPathCompletionProvider().addTo(this);
        new BashKeywordCompletionProvider().addTo(this);
    }

    @Override
    public void beforeCompletion(@NotNull CompletionInitializationContext context) {
        context.setDummyIdentifier("ZZZ");
    }

    @Override
    public void duringCompletion(@NotNull CompletionInitializationContext context) {
        fixComposedWordEndOffset(context);
    }

    /**
     * Fix the replace offset, for composed bash words use the full composed range
     *
     * @param context The current invocation context
     */
    protected void fixComposedWordEndOffset(CompletionInitializationContext context) {
        PsiElement element = context.getFile().findElementAt(context.getStartOffset());
        if (element == null) {
            return;
        }

        OffsetMap offsetMap = context.getOffsetMap();

        //if the completion is like "$<caret>" then element is the string end marker
        // in that case set the end before the end marker
        if (endTokens.contains(element.getNode().getElementType())) {
            offsetMap.addOffset(START_OFFSET, element.getTextOffset());
            offsetMap.addOffset(IDENTIFIER_END_OFFSET, element.getTextOffset());
            return;
        }

        if (wordTokens.contains(element.getNode().getElementType())) {
            if (fixReplacementOffsetInString(element, offsetMap)) {
                return;
            }
        }

        //try the parent if it's not already a BashWord
        if (!(element instanceof BashWord)) {
            element = element.getParent();
        }

        if (element instanceof BashWord) {
            offsetMap.addOffset(START_OFFSET, element.getTextOffset());

            // https://code.google.com/p/bashsupport/issues/detail?id=51
            // a completion at the end of a line with an open string is parsed as string until the first quote in the next line(s)
            // in the case of a newline character in the string the end offset is set to the end of the string to avoid aggressive string replacements

            if (!fixReplacementOffsetInString(element, offsetMap)) {
                offsetMap.addOffset(IDENTIFIER_END_OFFSET, element.getTextRange().getEndOffset());
            }

        }
    }

    /**
     * In a completion like "$abc<caret> def" the element is a word psi lead element, in this case to not expand the autocompletion after the whitespace character
     * https://code.google.com/p/bashsupport/issues/detail?id=51
     * a completion at the end of a line with an open string is parsed as string until the first quote in the next line(s)
     * in the case of a newline character in the string the end offset is set to the end of the string to avoid aggressive string replacements
     */
    private boolean fixReplacementOffsetInString(PsiElement element, OffsetMap offsetMap) {
        int endCharIndex = StringUtils.indexOfAny(element.getText(), new char[] { '\n', ' ' });
        if (endCharIndex > 0) {
            offsetMap.addOffset(IDENTIFIER_END_OFFSET, element.getTextOffset() + endCharIndex);
            return true;
        }

        return false;
    }
}