File: workers.py

package info (click to toggle)
turing 0.11-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,340 kB
  • sloc: python: 106,582; xml: 101; makefile: 53; sh: 29
file content (246 lines) | stat: -rw-r--r-- 8,151 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# -*- coding: utf-8 -*-
"""
This module contains the worker functions/classes used on the server side.

A worker is a function or a callable which receive one single argument (the
decoded json object) and returns a tuple made up of a status (bool) and a
response object (json serializable).

A worker is always tightly coupled with its caller, so are the data.

.. warning::
    This module should keep its dependencies as low as possible and fully
    supports python2 syntax. This is badly needed since the server might be run
    with a python2 interpreter. We don't want to force the user to install all
    the pyqode dependencies twice (if the user choose to run the server with
    python2, which might happen in pyqode.python to support python2 syntax).

"""
import logging
import re
import sys
import traceback


def echo_worker(data):
    """
    Example of worker that simply echoes back the received data.

    :param data: Request data dict.
    :returns: True, data
    """
    print('echo worker running')
    return data


class CodeCompletionWorker(object):
    """
    This is the worker associated with the code completion mode.

    The worker does not actually do anything smart, the real work of collecting
    code completions is accomplished by the completion providers (see the
    :class:`pyqode.core.backend.workers.CodeCompletionWorker.Provider`
    interface) listed in
    :attr:`pyqode.core.backend.workers.CompletionWorker.providers`.

    Completion providers must be installed on the CodeCompletionWorker
    at the beginning of the main server script, e.g.::

        from pyqode.core.backend import CodeCompletionWorker
        CodeCompletionWorker.providers.insert(0, MyProvider())
    """
    #: The list of code completion provider to run on each completion request.
    providers = []

    class Provider(object):
        """
        This class describes the expected interface for code completion
        providers.

        You can inherit from this class but this is not required as long as you
        implement a ``complete`` method which returns the list of completions
        and have the expected signature::

            def complete(self, code, line, column, path, encoding, prefix):
                pass

        """

        def complete(self, code, line, column, path, encoding, prefix):
            """
            Returns a list of completions.

            A completion is dictionary with the following keys:

                - 'name': name of the completion, this the text displayed and
                  inserted when the user select a completion in the list
                - 'icon': an optional icon file name
                - 'tooltip': an optional tooltip string

            :param code: code string
            :param line: line number (0 based)
            :param column: column number (0 based)
            :param path: file path
            :param encoding: file encoding
            :param prefix: completion prefix (text before cursor)

            :returns: A list of completion dicts as described above.
            :rtype: list
            """
            raise NotImplementedError()

    def __call__(self, data):
        """
        Do the work (this will be called in the child process by the
        SubprocessServer).
        """
        code = data['code']
        line = data['line']
        column = data['column']
        path = data['path']
        encoding = data['encoding']
        prefix = data['prefix']
        req_id = data['request_id']
        completions = []
        for prov in CodeCompletionWorker.providers:
            try:
                results = prov.complete(
                    code, line, column, path, encoding, prefix)
                completions.append(results)
                if len(completions):
                    break
            except:
                sys.stderr.write('Failed to get completions from provider %r'
                                 % prov)
                exc1, exc2, exc3 = sys.exc_info()
                traceback.print_exception(exc1, exc2, exc3, file=sys.stderr)
        return [(line, column, req_id)] + completions


class DocumentWordsProvider(object):
    """
    Provides completions based on the document words
    """
    words = {}

    # word separators
    separators = [
        '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '+', '{',
        '}', '|', ':', '"', "'", "<", ">", "?", ",", ".", "/", ";", '[',
        ']', '\\', '\n', '\t', '=', '-', ' '
    ]

    @staticmethod
    def split(txt, seps):
        """
        Splits a text in a meaningful list of words based on a list of word
        separators (define in pyqode.core.settings)

        :param txt: Text to split
        :param seps: List of words separators
        :return: A **set** of words found in the document (excluding
            punctuations, numbers, ...)
        """
        # replace all possible separators with a default sep
        default_sep = seps[0]
        for sep in seps[1:]:
            if sep:
                txt = txt.replace(sep, default_sep)
        # now we can split using the default_sep
        raw_words = txt.split(default_sep)
        words = set()
        for word in raw_words:
            # w = w.strip()
            if word.replace('_', '').isalpha():
                words.add(word)
        return sorted(words)

    def complete(self, code, *args):
        """
        Provides completions based on the document words.

        :param code: code to complete
        :param args: additional (unused) arguments.
        """
        completions = []
        for word in self.split(code, self.separators):
            completions.append({'name': word})
        return completions


def finditer_noregex(string, sub, whole_word):
    """
    Search occurrences using str.find instead of regular expressions.

    :param string: string to parse
    :param sub: search string
    :param whole_word: True to select whole words only
    """
    start = 0
    while True:
        start = string.find(sub, start)
        if start == -1:
            return
        if whole_word:
            if start:
                pchar = string[start - 1]
            else:
                pchar = ' '
            try:
                nchar = string[start + len(sub)]
            except IndexError:
                nchar = ' '
            if nchar in DocumentWordsProvider.separators and \
                    pchar in DocumentWordsProvider.separators:
                yield start
            start += len(sub)
        else:
            yield start
            start += 1


def findalliter(string, sub, regex=False, case_sensitive=False,
                whole_word=False):
    """
    Generator that finds all occurrences of ``sub`` in  ``string``
    :param string: string to parse
    :param sub: string to search
    :param regex: True to search using regex
    :param case_sensitive: True to match case, False to ignore case
    :param whole_word: True to returns only whole words
    :return:
    """
    if not sub:
        return
    if regex:
        flags = re.MULTILINE
        if not case_sensitive:
            flags |= re.IGNORECASE
        for val in re.finditer(sub, string, flags):
            yield val.span()
    else:
        if not case_sensitive:
            string = string.lower()
            sub = sub.lower()
        for val in finditer_noregex(string, sub, whole_word):
            yield val, val + len(sub)


def findall(data):
    """
    Worker that finds all occurrences of a given string (or regex)
    in a given text.

    :param data: Request data dict::
        {
            'string': string to search in text
            'sub': input text
            'regex': True to consider string as a regular expression
            'whole_word': True to match whole words only.
            'case_sensitive': True to match case, False to ignore case
        }
    :return: list of occurrence positions in text
    """
    return list(findalliter(
        data['string'], data['sub'], regex=data['regex'],
        whole_word=data['whole_word'], case_sensitive=data['case_sensitive']))