KTextEditor::Range IncludeHelperCompletionModel::completionRange(
    KTextEditor::View* view
  , const KTextEditor::Cursor& position
  )
{
    kDebug(DEBUG_AREA) << "cursor: " << position;
    auto line = view->document()->line(position.line());
    auto r = parseIncludeDirective(line, false);
    if (r.m_range.isValid())
    {
        auto start = line.lastIndexOf('/', r.m_range.end().column() - 1);
        kDebug(DEBUG_AREA) << "init start=" << start;
        start = start == -1 ? r.m_range.start().column() : start + 1;
        kDebug(DEBUG_AREA) << "fixed start=" << start;
        KTextEditor::Range range(
            position.line()
          , start
          , position.line()
          , r.m_range.end().column()
          );
        kDebug(DEBUG_AREA) << "selected range: " << range;
        return range;
    }
    kDebug(DEBUG_AREA) << "default select";
#if 0
    return KTextEditor::Range();
#else
    return KTextEditor::CodeCompletionModelControllerInterface3::completionRange(view, position);
#endif
}
/**
 * Initiate completion when there is \c #include on a line (\c m_range
 * in a result of \c parseIncludeDirective() not empty -- i.e. there is some file present)
 * and cursor placed within that range... despite of completeness of the whole line.
 */
bool IncludeHelperCompletionModel::shouldStartCompletion(
    KTextEditor::View* view
  , const QString& inserted_text
  , bool user_insertion
  , const KTextEditor::Cursor& position
  )
{
    kDebug(DEBUG_AREA) << "position=" << position << ", inserted_text=" << inserted_text << ", ui=" << user_insertion;

    m_should_complete = false;
    auto* doc = view->document();                           // get current document
    auto line = doc->line(position.line());                 // get current line
    auto* iface = qobject_cast<KTextEditor::HighlightInterface*>(doc);
    // Do nothing if no highlighting interface or not suitable document or
    // a place within it... (we won't to complete smth in non C++ files or comments for example)
    if (!iface || !isSuitableDocumentAndHighlighting(doc->mimeType(), iface->highlightingModeAt(position)))
        return m_should_complete;

    // Try to parse it...
    auto r = parseIncludeDirective(line, false);
    m_should_complete = r.m_range.isValid();
    if (m_should_complete)
    {
        kDebug(DEBUG_AREA) << "range=" << r.m_range;
        m_should_complete = position.column() >= r.m_range.start().column()
          && position.column() <= r.m_range.end().column();
        if (m_should_complete)
        {
            m_closer = r.close_char();
            kDebug(DEBUG_AREA) << "closer=" << m_closer;
        }
    }
    else if (position.column() == line.length())
    {
        auto text = tryToCompleteIncludeDirective(line.mid(0, position.column()).trimmed());
        m_should_complete = !text.isEmpty();
        if (m_should_complete)
        {
            /// \todo Hardcoded angle bracket! Better to check what file was selected
            /// (from system path or session specific) and replace it accordingly...
            text += QLatin1String(" <");
            auto start = position;
            start.setColumn(0);
            auto range = KTextEditor::Range{start, position};
            view->document()->replaceText(range, text);
        }
    }
    return m_should_complete;
}
/**
 * Search for \c #include directives
 *
 * \invariant Document supposed to have \c KTextEditor::MovingInterface implemented!
 */
void DocumentInfo::scanForHeadersIncluded(const KTextEditor::Range& source_range)
{
    auto* mv_iface = qobject_cast<KTextEditor::MovingInterface*>(m_doc);
    assert("No moving iface for document!" && mv_iface);

    auto range = (source_range == KTextEditor::Range::invalid())
      ? m_doc->documentRange()
      : source_range
      ;

    /**
     * \todo It would be \b cool to have a view class over a document
     * so the following code would be possible:
     * \code
     *  for (const auto& l : DocumentLinesView(range, doc))
     *  {
     *      QString line_str = l;    // auto converstion to strings
     *      int line_no = l.index(); // tracking of the current line number
     *  }
     *  // Or even smth like this
     *  DocumentLinesView dv = { view->document() };
     *  // Get line text by number
     *  QString line_str = dv[line_no];
     * \endcode
     */
    // Search lines and filenames #include'd in a selected range
    for (auto i = range.start().line(); i < range.end().line(); i++)
    {
        auto r = parseIncludeDirective(m_doc->line(i), false);
        if (r.range.isValid())
        {
            r.range.setBothLines(i);
            kDebug(DEBUG_AREA) << "found #include at" << r.range;
            if (!isRangeWithSameLineExists(r.range))
                addRange(
                    mv_iface->newMovingRange(
                        r.range
                      , KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight
                      )
                  , r.type
                  );
            else kDebug(DEBUG_AREA) << "range already registered";
        }
    }
}
Example #4
0
//if-section        (# if / # ifdef / #ifndef )
//control-line      ( #include / etc )
//# non-directive   ( # text newline
//text-line         (text newline )
bool Preprocessor::parseGroupPart(Item *group)
{
    //cout << "parse group part" << endl;
    Q_ASSERT(group->toItemComposite());

    //look up first significant token
    Type token = lookAhead();
    if(token == Token_eof)
        return false;

    //look for '#'
    if(token != Token_preproc)
        return parseTextLine(group);

    //look up first significant token after the '#'
    token = lookAheadSkipHash();
    if(token == Token_eof)
        return false;

    // Check if we are at the end of a group. This is not an neccesarely an
    // error, it happens when we reach an #endif for example.
    if (token == Token_directive_elif || token == Token_directive_else ||
        token == Token_directive_endif)
        return false;

    // if-section?
    if(token == Token_directive_if || token == Token_directive_ifdef ||
        token == Token_directive_ifndef)
        return parseIfSection(group);

    // control-line?
    if (token == Token_directive_define)
        return parseDefineDirective(group);
    if (token ==  Token_directive_undef)
        return parseUndefDirective(group);
    if (token ==  Token_directive_include)
        return parseIncludeDirective(group);
    if (token == Token_directive_error)
        return parseErrorDirective(group);
    if (token ==  Token_directive_pragma)
        return parsePragmaDirective(group);

    return parseNonDirective(group);
}
/**
 * We have to stop \c #include completion when current line would parsed well
 * (i.e. contains complete \c #include expression) or have no \c #include at all.
 */
bool IncludeHelperCompletionModel::shouldAbortCompletion(
    KTextEditor::View* view
  , const KTextEditor::Range& range
  , const QString& current_completion
  )
{
    kDebug(DEBUG_AREA) << "range=" << range << ", current_completion=" << current_completion;
    kDebug(DEBUG_AREA) << "m_should_complete=" << m_should_complete << ", closer=" << m_closer;

    // Get current line
    const auto line = view->document()->line(range.end().line());
    // Try to parse it...
    auto r = parseIncludeDirective(line, false);
    // nothing to complete for lines w/o #include
    const auto need_abort = !r.m_range.isValid()
      || range.end().column() < r.m_range.start().column()
      || range.end().column() > (r.m_range.end().column() + 1)
      ;
    kDebug(DEBUG_AREA) << "result=" << need_abort;
    return need_abort;
}
void IncludeHelperCompletionModel::executeCompletionItem2(
    KTextEditor::Document* document
  , const KTextEditor::Range& word
  , const QModelIndex& index
  ) const
{
    kDebug(DEBUG_AREA) << "rword=" << word;
    auto p = index.row() < m_dir_completions.size()
      ? m_dir_completions[index.row()]
      : m_file_completions[index.row() - m_dir_completions.size()]
      ;
    kDebug(DEBUG_AREA) << "dict=" << p;
    if (!p.endsWith("/"))                                   // Is that dir or file completion?
    {
        // Get line to be replaced and check if #include hase close char...
        auto line = document->line(word.start().line());
        auto r = parseIncludeDirective(line, false);
        if (r.m_range.isValid() && !r.m_is_complete)
            p += r.close_char();
    }
    document->replaceText(word, p);
}
void IncludeHelperCompletionModel::completionInvoked(
    KTextEditor::View* view
  , const KTextEditor::Range& range
  , InvocationType
  )
{
    auto* doc = view->document();
    kDebug(DEBUG_AREA) << range << ", " << doc->text(range);
    const auto& t = doc->line(range.start().line()).left(range.start().column());
    kDebug(DEBUG_AREA) << "text to parse: " << t;
    auto r = parseIncludeDirective(t, false);
    if (r.m_range.isValid())
    {
        m_should_complete = range.start().column() >= r.m_range.start().column()
            && range.start().column() <= r.m_range.end().column();
        if (m_should_complete)
        {
            r.m_range.setBothLines(range.start().line());
            kDebug(DEBUG_AREA) << "parsed range: " << r.m_range;
            m_closer = r.close_char();
            updateCompletionList(doc->text(r.m_range), r.m_type == IncludeStyle::local);
        }
    }
}