1 // Copyright (C) 2011-2018 ycmd contributors
2 //
3 // This file is part of ycmd.
4 //
5 // ycmd is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // ycmd is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with ycmd. If not, see <http://www.gnu.org/licenses/>.
17
18 #include "PythonSupport.h"
19 #include "Candidate.h"
20 #include "Repository.h"
21 #include "Result.h"
22 #include "Utils.h"
23
24 #include <utility>
25 #include <vector>
26
27 namespace YouCompleteMe {
28
29 namespace {
30
CandidatesFromObjectList(const pybind11::list & candidates,pybind11::str candidate_property,size_t num_candidates)31 std::vector< const Candidate * > CandidatesFromObjectList(
32 const pybind11::list& candidates,
33 pybind11::str candidate_property,
34 size_t num_candidates ) {
35 std::vector< std::string > candidate_strings( num_candidates );
36 auto it = candidate_strings.begin();
37
38 if ( !PyUnicode_GET_LENGTH( candidate_property.ptr() ) ) {
39 for ( size_t i = 0; i < num_candidates; ++i ) {
40 *it++ = GetUtf8String( PyList_GET_ITEM( candidates.ptr(), i ) );
41 }
42 } else {
43 for ( size_t i = 0; i < num_candidates; ++i ) {
44 auto element = PyDict_GetItem( PyList_GET_ITEM( candidates.ptr(), i ),
45 candidate_property.ptr() );
46 *it++ = GetUtf8String( element );
47 }
48 }
49
50 return Repository< Candidate >::Instance().GetElements(
51 std::move( candidate_strings ) );
52 }
53
54 } // unnamed namespace
55
56
FilterAndSortCandidates(const pybind11::list & candidates,pybind11::str candidate_property,std::string & query,const size_t max_candidates)57 pybind11::list FilterAndSortCandidates(
58 const pybind11::list& candidates,
59 pybind11::str candidate_property,
60 std::string& query,
61 const size_t max_candidates ) {
62
63 auto num_candidates = size_t( PyList_GET_SIZE( candidates.ptr() ) );
64 std::vector< const Candidate * > repository_candidates =
65 CandidatesFromObjectList( candidates,
66 std::move( candidate_property ),
67 num_candidates );
68
69 std::vector< ResultAnd< size_t > > result_and_objects;
70 {
71 pybind11::gil_scoped_release unlock;
72 Word query_object( std::move( query ) );
73
74 for ( size_t i = 0; i < num_candidates; ++i ) {
75 const Candidate *candidate = repository_candidates[ i ];
76
77 if ( candidate->IsEmpty() || !candidate->ContainsBytes( query_object ) ) {
78 continue;
79 }
80
81 Result result = candidate->QueryMatchResult( query_object );
82
83 if ( result.IsSubsequence() ) {
84 result_and_objects.emplace_back( result, i );
85 }
86 }
87
88 PartialSort( result_and_objects, max_candidates );
89 }
90
91 pybind11::list filtered_candidates( result_and_objects.size() );
92 for ( size_t i = 0; i < result_and_objects.size(); ++i ) {
93 auto new_candidate = PyList_GET_ITEM(
94 candidates.ptr(),
95 result_and_objects[ i ].extra_object_ );
96 Py_INCREF( new_candidate );
97 PyList_SET_ITEM( filtered_candidates.ptr(), i, new_candidate );
98 }
99
100 return filtered_candidates;
101 }
102
103
GetUtf8String(pybind11::handle value)104 std::string GetUtf8String( pybind11::handle value ) {
105 // If already a unicode or string (or something derived from it)
106 // pybind will already convert to utf8 when converting to std::string.
107 // For `bytes` the contents are left untouched:
108 if ( PyUnicode_CheckExact( value.ptr() ) ) {
109 ssize_t size = 0;
110 const char* buffer = nullptr;
111 buffer = PyUnicode_AsUTF8AndSize( value.ptr(), &size );
112 return { buffer, static_cast< size_t >( size ) };
113 }
114 if ( PyBytes_CheckExact( value.ptr() ) ) {
115 ssize_t size = 0;
116 char* buffer = nullptr;
117 PyBytes_AsStringAndSize( value.ptr(), &buffer, &size );
118 return { buffer, static_cast< size_t >( size ) };
119 }
120
121 // Otherwise go through Python's built-in `str`.
122 pybind11::str keep_alive( value );
123 ssize_t size = 0;
124 const char* buffer =
125 PyUnicode_AsUTF8AndSize( keep_alive.ptr(), &size );
126 return { buffer, static_cast< size_t >( size ) };
127 }
128
129 } // namespace YouCompleteMe
130