1'''
2This module contains template variables (added through the templates engine).
3
4Users should be able to create their own templates by adding an additional path in the scripts
5and creating any file that starts with "pytemplate" within that directory.
6
7E.g.: clients could get all the methods in the class and use them to complete the template, make
8custom date formatting, etc (only limited by your imagination as any class from Pydev could be
9imported and used here).
10
11The only annoyance is that changes aren't automatically applied, so, one must (in a Pydev Editor) use:
12
13Ctrl + 2 + --clear-templates-cache
14
15to clear the cache so that any changed files regarding the templates are (re)evaluated (the only
16other way to get the changes applied is restarting eclipse).
17
18The concept is the same as the default scripting engine in pydev. The only difference is that it'll
19only get files starting with 'pytemplate', so, it's also worth checking
20http://pydev.org/manual_articles_scripting.html
21
22context passed as parameter: org.python.pydev.editor.codecompletion.templates.PyDocumentTemplateContext
23'''
24
25import time
26
27import template_helper
28
29if False:
30    #Variables added externally by the runner of this module.
31    py_context_type = org.python.pydev.editor.templates.PyContextType  # @UndefinedVariable
32
33
34#===================================================================================================
35# _CreateSelection
36#===================================================================================================
37def _CreateSelection(context):
38    '''
39    Created method so that it can be mocked on tests.
40    '''
41    selection = context.createPySelection()
42    return selection
43
44#===================================================================================================
45# _IsGrammar3
46#===================================================================================================
47def _IsGrammar3(context):
48    if context is None:
49        return False  #Default is Python 2
50    from org.python.pydev.core import IGrammarVersionProvider
51    if context.getGrammarVersion() >= IGrammarVersionProvider.GRAMMAR_PYTHON_VERSION_3_0:
52        return True
53    return False
54
55
56#===============================================================================
57# ISO-8601 Dates
58#===============================================================================
59def GetISODate(context):
60    return time.strftime("%Y-%m-%d")
61
62template_helper.AddTemplateVariable(py_context_type, 'isodate', 'ISO-8601 Ymd date', GetISODate)
63
64def GetISODateString1(context):
65    return time.strftime("%Y-%m-%d %H:%M")
66
67template_helper.AddTemplateVariable(py_context_type, 'isodatestr', 'ISO-8601 Ymd HM date', GetISODateString1)
68
69def GetISODateString2(context):
70    return time.strftime("%Y-%m-%d %H:%M:%S")
71
72template_helper.AddTemplateVariable(py_context_type, 'isodatestr2', 'ISO-8601 Ymd HMS date', GetISODateString2)
73
74
75#===================================================================================================
76# GetModuleName
77#===================================================================================================
78def GetModuleName(context):
79    return context.getModuleName()
80
81template_helper.AddTemplateVariable(py_context_type, 'module', 'Current module', GetModuleName)
82
83
84#===================================================================================================
85# _GetCurrentASTPath
86#===================================================================================================
87def _GetCurrentASTPath(context, reverse=False):
88    '''
89    @return: ArrayList(SimpleNode)
90    '''
91    FastParser = context.getFastParserClass()  # from org.python.pydev.parser.fastparser import FastParser
92    selection = _CreateSelection(context)
93    ret = FastParser.parseToKnowGloballyAccessiblePath(
94        context.getDocument(), selection.getStartLineIndex())
95    if reverse:
96        from java.util import Collections  # @UnresolvedImport
97        Collections.reverse(ret)
98
99    return ret
100
101
102#===================================================================================================
103# GetQualifiedNameScope
104#===================================================================================================
105def GetQualifiedNameScope(context):
106    NodeUtils = context.getNodeUtilsClass()  # from org.python.pydev.parser.visitors import NodeUtils
107
108    ret = ''
109    for stmt in _GetCurrentASTPath(context):
110        if ret:
111            ret += '.'
112        ret += NodeUtils.getRepresentationString(stmt)
113    return ret
114
115
116template_helper.AddTemplateVariable(
117    py_context_type, 'current_qualified_scope', 'Current qualified scope.', GetQualifiedNameScope)
118
119
120
121#===================================================================================================
122# _GetCurrentClassStmt
123#===================================================================================================
124def _GetCurrentClassStmt(context):
125    NodeUtils = context.getNodeUtilsClass()  #from org.python.pydev.parser.visitors import NodeUtils
126    ClassDef = context.getClassDefClass()  # from org.python.pydev.parser.jython.ast import ClassDef
127
128    for stmt in _GetCurrentASTPath(context, True):
129        if isinstance(stmt, ClassDef):
130            return stmt
131    return None
132
133
134#===================================================================================================
135# GetCurrentClass
136#===================================================================================================
137def GetCurrentClass(context):
138    NodeUtils = context.getNodeUtilsClass()  #from org.python.pydev.parser.visitors import NodeUtils
139    ClassDef = context.getClassDefClass()  # from org.python.pydev.parser.jython.ast import ClassDef
140
141    stmt = _GetCurrentClassStmt(context)
142    if stmt is not None:
143        return NodeUtils.getRepresentationString(stmt)
144
145    return ''
146
147
148template_helper.AddTemplateVariable(py_context_type, 'current_class', 'Current class', GetCurrentClass)
149
150
151#===================================================================================================
152# GetPydevdFileLocation
153#===================================================================================================
154def GetPydevdFileLocation(context):
155    from org.python.pydev.debug.ui.launching import PythonRunnerConfig  # @UnresolvedImport
156    return PythonRunnerConfig.getDebugScript()
157
158template_helper.AddTemplateVariable(
159    py_context_type, 'pydevd_file_location', 'pydevd.py File Location', GetPydevdFileLocation)
160
161#===================================================================================================
162# GetPydevdDirLocation
163#===================================================================================================
164def GetPydevdDirLocation(context):
165    from org.python.pydev.debug.ui.launching import PythonRunnerConfig  # @UnresolvedImport
166    import os
167    return os.path.split(PythonRunnerConfig.getDebugScript())[0]
168
169template_helper.AddTemplateVariable(
170    py_context_type, 'pydevd_dir_location', 'pydevd.py Directory Location', GetPydevdDirLocation)
171
172
173
174#===================================================================================================
175# GetCurrentMethod
176#===================================================================================================
177def GetCurrentMethod(context):
178    NodeUtils = context.getNodeUtilsClass()  #from org.python.pydev.parser.visitors import NodeUtils
179    FunctionDef = context.getFunctionDefClass()  # from org.python.pydev.parser.jython.ast import FunctionDef
180
181    for stmt in _GetCurrentASTPath(context, True):
182        if isinstance(stmt, FunctionDef):
183            return NodeUtils.getRepresentationString(stmt)
184    return ''
185
186
187
188template_helper.AddTemplateVariable(py_context_type, 'current_method', 'Current method', GetCurrentMethod)
189
190
191#===================================================================================================
192# _GetPreviousOrNextClassOrMethod
193#===================================================================================================
194def _GetPreviousOrNextClassOrMethod(context, searchForward):
195    NodeUtils = context.getNodeUtilsClass()  #from org.python.pydev.parser.visitors import NodeUtils
196    FastParser = context.getFastParserClass()  #from org.python.pydev.parser.fastparser import FastParser
197    doc = context.getDocument()
198    selection = _CreateSelection(context)
199    startLine = selection.getStartLineIndex()
200
201    found = FastParser.firstClassOrFunction(doc, startLine, searchForward, context.isCythonFile())
202    if found:
203        return NodeUtils.getRepresentationString(found)
204    return ''
205
206
207
208#===================================================================================================
209# GetPreviousClassOrMethod
210#===================================================================================================
211def GetPreviousClassOrMethod(context):
212    return _GetPreviousOrNextClassOrMethod(context, False)
213
214template_helper.AddTemplateVariable(
215    py_context_type, 'prev_class_or_method', 'Previous class or method', GetPreviousClassOrMethod)
216
217#===================================================================================================
218# GetNextClassOrMethod
219#===================================================================================================
220def GetNextClassOrMethod(context):
221    return _GetPreviousOrNextClassOrMethod(context, True)
222
223template_helper.AddTemplateVariable(
224    py_context_type, 'next_class_or_method', 'Next class or method', GetNextClassOrMethod)
225
226
227
228#===================================================================================================
229# GetSuperclass
230#===================================================================================================
231def GetSuperclass(context):
232    selection = _CreateSelection(context)
233    stmt = _GetCurrentClassStmt(context)
234    BadLocationException = context.getBadLocationExceptionClass()  # from org.eclipse.jface.text import BadLocationException
235    if stmt is not None:
236        doc = context.getDocument()
237        name = stmt.name
238        nameStartOffset = selection.getAbsoluteCursorOffset(name.beginLine - 1, name.beginColumn - 1)
239        nameStartOffset += len(name.id)
240
241        found_start = False
242        i = 0
243        contents = ''
244        while True:
245            try:
246                c = doc.get(nameStartOffset + i, 1)
247                i += 1
248
249                if c == '(':
250                    found_start = True
251
252                elif c in (')', ':'):
253                    break
254
255                elif c in ('\r', '\n', ' ', '\t'):
256                    pass
257
258                elif c == '#':  #skip comments
259                    while c not in ('\r', '\n'):
260                        c = doc.get(nameStartOffset + i, 1)
261                        i += 1
262
263
264                else:
265                    if found_start:
266                        contents += c
267
268            except BadLocationException:
269                return ''  #Seems the class declaration is not properly finished as we're now out of bounds in the doc.
270
271        if ',' in contents:
272            ret = []
273            for c in contents.split(','):
274                ret.append(c.strip())
275            return ret
276
277        return contents.strip()
278
279    return ''
280
281template_helper.AddTemplateVariable(
282    py_context_type, 'superclass', 'Superclass of the current class', GetSuperclass)
283