def preceding_trg_from_pos(self, buf, pos, curr_pos, preceding_trg_terminators=None, DEBUG=False): # DEBUG = True if DEBUG: print "pos: %d" % (pos,) print "ch: %r" % (buf.accessor.char_at_pos(pos),) print "curr_pos: %d" % (curr_pos,) if not self.gocode_present: return if pos != curr_pos and self._last_trg_type == "names": # The last trigger type was a 3-char trigger "names", we must try # triggering from the same point as before to get other available # trigger types defined at the same poisition or before. trg = ProgLangTriggerIntelMixin.preceding_trg_from_pos( self, buf, pos + 2, curr_pos, preceding_trg_terminators, DEBUG=DEBUG ) else: trg = ProgLangTriggerIntelMixin.preceding_trg_from_pos( self, buf, pos, curr_pos, preceding_trg_terminators, DEBUG=DEBUG ) names_trigger = None style = None if pos > 0: accessor = buf.accessor if pos == curr_pos: # We actually care about whats left of the cursor. pos -= 1 style = accessor.style_at_pos(pos) if DEBUG: style_names = buf.style_names_from_style_num(style) print " style: %s (%s)" % (style, ", ".join(style_names)) if style in (1, 2): ac = AccessorCache(accessor, pos) prev_pos, prev_ch, prev_style = ac.getPrecedingPosCharStyle(style) if prev_style is not None and (pos - prev_pos) > 3: # We need at least 3 character for proper completion handling. names_trigger = self.trg_from_pos(buf, prev_pos + 4, implicit=False) if DEBUG: print "trg: %r" % (trg,) print "names_trigger: %r" % (names_trigger,) print "last_trg_type: %r" % (self._last_trg_type,) if names_trigger: if not trg: trg = names_trigger # Two triggers, choose the best one. elif trg.pos == names_trigger.pos: if self._last_trg_type != "names": # The names trigger gets priority over the other trigger # types, unless the previous trigger was also a names trg. trg = names_trigger elif trg.pos < names_trigger.pos: trg = names_trigger if trg: self._last_trg_type = trg.type return trg
def preceding_trg_from_pos(self, buf, pos, curr_pos, preceding_trg_terminators=None, DEBUG=False): #DEBUG = True if DEBUG: log.debug("pos: %d", pos) log.debug("ch: %r", buf.accessor.char_at_pos(pos)) log.debug("curr_pos: %d", curr_pos) if pos != curr_pos and self._last_trg_type == "names": # The last trigger type was a 3-char trigger "names", we must try # triggering from the same point as before to get other available # trigger types defined at the same poisition or before. trg = ProgLangTriggerIntelMixin.preceding_trg_from_pos( self, buf, pos+2, curr_pos, preceding_trg_terminators, DEBUG=DEBUG) else: trg = ProgLangTriggerIntelMixin.preceding_trg_from_pos( self, buf, pos, curr_pos, preceding_trg_terminators, DEBUG=DEBUG) names_trigger = None style = None if pos > 0: accessor = buf.accessor if pos == curr_pos: # We actually care about whats left of the cursor. pos -= 1 style = accessor.style_at_pos(pos) if DEBUG: style_names = buf.style_names_from_style_num(style) log.debug(" style: %s (%s)", style, ", ".join(style_names)) if style in (1,2): ac = AccessorCache(accessor, pos) prev_pos, prev_ch, prev_style = ac.getPrecedingPosCharStyle(style) if prev_style is not None and (pos - prev_pos) > 3: # We need at least 3 character for proper completion handling. names_trigger = self.trg_from_pos(buf, prev_pos + 4, implicit=False) if DEBUG: log.debug("trg: %r", trg) log.debug("names_trigger: %r", names_trigger) log.debug("last_trg_type: %r", self._last_trg_type) if names_trigger: if not trg: trg = names_trigger # Two triggers, choose the best one. elif trg.pos == names_trigger.pos: if self._last_trg_type != "names": # The names trigger gets priority over the other trigger # types, unless the previous trigger was also a names trg. trg = names_trigger elif trg.pos < names_trigger.pos: trg = names_trigger if trg: self._last_trg_type = trg.type return trg
def preceding_trg_from_pos(self, buf, pos, curr_pos): DEBUG = False # not using 'logging' system, because want to be fast #DEBUG = True # not using 'logging' system, because want to be fast if DEBUG: print "\npreceding_trg_from_pos -- pos: %d, curr_pos: %d" % ( pos, curr_pos, ) if isinstance(buf, UDLBuffer): styleClassifier = UDLCSSStyleClassifier else: styleClassifier = StraightCSSStyleClassifier ac = AccessorCache(buf.accessor, curr_pos+1, fetchsize=50) currTrg = self._trg_from_pos(buf, (curr_pos == pos) and pos or pos+1, implicit=False, DEBUG=DEBUG, ac=ac, styleClassifier=styleClassifier) if DEBUG: print " currTrg: %r" % (currTrg, ) # If we're not looking for a previous trigger, or else the current # trigger position is for a calltip, then do not look any further. if (pos == curr_pos) or (currTrg and currTrg.form == TRG_FORM_CALLTIP): return currTrg # Else, work our way backwards from pos. ac.resetToPosition(pos+1) p, ch, style = ac.getPrevPosCharStyle() if DEBUG: print " preceding_trg_from_pos: p: %r, ch: %r, style: %r" % (p, ch, style) min_p = max(0, p - 200) ignore_styles = styleClassifier.comment_styles + \ styleClassifier.string_styles + \ styleClassifier.number_styles while p > min_p and styleClassifier.is_css_style(style): p, ch, style = ac.getPrecedingPosCharStyle(style, ignore_styles=ignore_styles, max_look_back=100) if DEBUG: print " preceding_trg_from_pos: Trying preceding p: %r, ch: %r, style: %r" % (p, ch, style) if ch and (_isident(ch) or ch in ":( \t"): trg = self._trg_from_pos(buf, p+1, implicit=False, DEBUG=DEBUG, ac=ac, styleClassifier=styleClassifier) if trg is not None: if DEBUG: print "trg: %r" % (trg, ) if currTrg is not None: if currTrg.type != trg.type: if DEBUG: print " Next trigger is a different type, ending search" return None elif currTrg.form != trg.form: return trg elif DEBUG: print " Found same trigger again, continuing " \ "looking for a different trigger" else: return trg return None
def trg_from_pos(self, buf, pos, implicit=True, lang=None): log.debug("trg_from_pos(pos=%r)", pos) if pos < 2: return None accessor = buf.accessor last_pos = pos - 1 last_char = accessor.char_at_pos(last_pos) if last_char == ".": # must be "complete-object-members" or None log.debug(" triggered 'complete-object-members'") return Trigger(self.lang, TRG_FORM_CPLN, "object-members", pos, implicit) elif last_char == "(": log.debug(" triggered 'calltip-call-signature'") return Trigger(self.lang, TRG_FORM_CALLTIP, "call-signature", pos, implicit) elif last_char in "'\"`" or last_char == "@": # Check if it's an import log.debug(" checking for import statement") ac = AccessorCache(accessor, pos, fetchsize=100) prev_style = accessor.style_at_pos(last_pos - 1) if prev_style == SCE_C_STRING: # It's the end of a string then, not what we are looking for. return False # Bug in Komodo 8 - peek at the previous style to ensure the cache # is primed. No needed in Komodo 9 onwards. p, ch, style = ac.peekPrevPosCharStyle() log.debug(" p1 %r, ch %r, style %r", p, ch, style) loops_left = 100 while loops_left: loops_left -= 1 p, ch, style = ac.getPrecedingPosCharStyle(style) log.debug(" p %r, ch %r, style %r", p, ch, style) if p is None: break if style == SCE_C_WORD: p, text = ac.getTextBackWithStyle(style) log.debug(" p %r, text %r", p, text) if text == "import": log.debug(" triggered 'complete-imports'") return Trigger(self.lang, TRG_FORM_CPLN, "imports", pos, implicit) break elif style not in ( SCE_C_DEFAULT, SCE_C_OPERATOR, SCE_C_STRING, SCE_C_COMMENT, SCE_C_COMMENTDOC, SCE_C_COMMENTLINE, ): break log.debug(" triggered 'complete-any'") return Trigger(self.lang, TRG_FORM_CPLN, "any", pos, implicit)
def test_basics(self): content = "This is my test buffer\r\nSecond line\r\nThird line\r\n" styles = "1111011011011110111111 2 21111110001111 2 21111101111 2 2".replace(" ", "") ta = _TestAccessor(content, list(map(int, styles))) pos = len(content) - 2 ac = AccessorCache(ta, pos) #ac._debug = True for i in range(2): assert(ac.getPrevPosCharStyle() == (pos-1, "e", 1)) assert(ac.getPrecedingPosCharStyle(1) == (pos-5, " ", 0)) assert(ac.getPrecedingPosCharStyle(0) == (pos-6, "d", 1)) assert(ac.getPrecedingPosCharStyle(1) == (pos-11, "\n", 2)) assert(ac.getPrecedingPosCharStyle() == (pos-13, "e", 1)) assert(ac.getTextBackWithStyle(1) == (pos-16, "line")) assert(ac.getPrevPosCharStyle() == (pos-17, " ", 0)) assert(ac.getPrecedingPosCharStyle(0) == (pos-20, "d", 1)) if i == 0: ac.resetToPosition(pos) assert(ac.getCurrentPosCharStyle() == (pos-20, "d", 1)) #print pos #print ac.getSucceedingPosCharStyle() assert(ac.getNextPosCharStyle() == (pos-19, " ", 0)) assert(ac.getSucceedingPosCharStyle() == (pos-16, "l", 1)) assert(ac.getTextForwardWithStyle(1) == (pos-13, "line")) assert(ac.getNextPosCharStyle() == (pos-12, "\r", 2)) assert(ac.getNextPosCharStyle() == (pos-11, "\n", 2)) assert(ac.getSucceedingPosCharStyle(2) == (pos-10, "T", 1)) assert(ac.getSucceedingPosCharStyle() == (pos-5, " ", 0)) assert(ac.getSucceedingPosCharStyle() == (pos-4, "l", 1)) assert(ac.getSucceedingPosCharStyle() == (pos, "\r", 2)) assert(ac.getNextPosCharStyle() == (pos+1, "\n", 2)) # Bug: http://bugs.activestate.com/show_bug.cgi?id=64227 # Ensure text_range uses correct parameters in boundary situations ac.resetToPosition(3) assert(ac.getTextBackWithStyle(1)[1] == "This") ac.resetToPosition(len(content) - 2) assert(ac.getTextForwardWithStyle(2)[1] == "\r\n")
def test_basics(self): content = "This is my test buffer\r\nSecond line\r\nThird line\r\n" styles = "1111011011011110111111 2 21111110001111 2 21111101111 2 2".replace(" ", "") ta = _TestAccessor(content, map(int, styles)) pos = len(content) - 2 ac = AccessorCache(ta, pos) #ac._debug = True for i in range(2): assert(ac.getPrevPosCharStyle() == (pos-1, "e", 1)) assert(ac.getPrecedingPosCharStyle(1) == (pos-5, " ", 0)) assert(ac.getPrecedingPosCharStyle(0) == (pos-6, "d", 1)) assert(ac.getPrecedingPosCharStyle(1) == (pos-11, "\n", 2)) assert(ac.getPrecedingPosCharStyle() == (pos-13, "e", 1)) assert(ac.getTextBackWithStyle(1) == (pos-16, "line")) assert(ac.getPrevPosCharStyle() == (pos-17, " ", 0)) assert(ac.getPrecedingPosCharStyle(0) == (pos-20, "d", 1)) if i == 0: ac.resetToPosition(pos) assert(ac.getCurrentPosCharStyle() == (pos-20, "d", 1)) #print pos #print ac.getSucceedingPosCharStyle() assert(ac.getNextPosCharStyle() == (pos-19, " ", 0)) assert(ac.getSucceedingPosCharStyle() == (pos-16, "l", 1)) assert(ac.getTextForwardWithStyle(1) == (pos-13, "line")) assert(ac.getNextPosCharStyle() == (pos-12, "\r", 2)) assert(ac.getNextPosCharStyle() == (pos-11, "\n", 2)) assert(ac.getSucceedingPosCharStyle(2) == (pos-10, "T", 1)) assert(ac.getSucceedingPosCharStyle() == (pos-5, " ", 0)) assert(ac.getSucceedingPosCharStyle() == (pos-4, "l", 1)) assert(ac.getSucceedingPosCharStyle() == (pos, "\r", 2)) assert(ac.getNextPosCharStyle() == (pos+1, "\n", 2)) # Bug: http://bugs.activestate.com/show_bug.cgi?id=64227 # Ensure text_range uses correct parameters in boundary situations ac.resetToPosition(3) assert(ac.getTextBackWithStyle(1)[1] == "This") ac.resetToPosition(len(content) - 2) assert(ac.getTextForwardWithStyle(2)[1] == "\r\n")
def trg_from_pos(self, buf, pos, implicit=True, lang=None): log.debug("trg_from_pos(pos=%r)", pos) if pos < 2: return None accessor = buf.accessor last_pos = pos - 1 last_char = accessor.char_at_pos(last_pos) if last_char == '.': # must be "complete-object-members" or None log.debug(" triggered 'complete-object-members'") return Trigger(self.lang, TRG_FORM_CPLN, "object-members", pos, implicit) elif last_char == '(': log.debug(" triggered 'calltip-call-signature'") return Trigger(self.lang, TRG_FORM_CALLTIP, "call-signature", pos, implicit) elif last_char in '\'"`' or last_char == "@": # Check if it's an import log.debug(" checking for import statement") ac = AccessorCache(accessor, pos, fetchsize=100) prev_style = accessor.style_at_pos(last_pos - 1) if prev_style == SCE_C_STRING: # It's the end of a string then, not what we are looking for. return False # Bug in Komodo 8 - peek at the previous style to ensure the cache # is primed. No needed in Komodo 9 onwards. p, ch, style = ac.peekPrevPosCharStyle() log.debug(" p1 %r, ch %r, style %r", p, ch, style) loops_left = 100 while loops_left: loops_left -= 1 p, ch, style = ac.getPrecedingPosCharStyle(style) log.debug(" p %r, ch %r, style %r", p, ch, style) if p is None: break if style == SCE_C_WORD: p, text = ac.getTextBackWithStyle(style) log.debug(" p %r, text %r", p, text) if text == "import": log.debug(" triggered 'complete-imports'") return Trigger(self.lang, TRG_FORM_CPLN, "imports", pos, implicit) break elif style not in (SCE_C_DEFAULT, SCE_C_OPERATOR, SCE_C_STRING, SCE_C_COMMENT, SCE_C_COMMENTDOC, SCE_C_COMMENTLINE): break log.debug(" triggered 'complete-any'") return Trigger(self.lang, TRG_FORM_CPLN, "any", pos, implicit)
def _trg_from_pos(self, buf, pos, implicit=True, DEBUG=False, ac=None, styleClassifier=None): #DEBUG = True # not using 'logging' system, because want to be fast if DEBUG: print "\n----- CSS _trg_from_pos(pos=%r, implicit=%r) -----"\ % (pos, implicit) try: if pos == 0: return None if ac is None: ac = AccessorCache(buf.accessor, pos, fetchsize=50) else: ac.resetToPosition(pos) # Ensure this variable is initialized as False, it is used by UDL # for checking if the css style is inside of a html tag, example: # <p style="mycss: value;" /> # When it's found that it is such a case, this value is set True ac.is_html_style_attribute = False last_pos, last_char, last_style = ac.getPrevPosCharStyle() if DEBUG: print " _trg_from_pos:: last_pos: %s" % last_pos print " last_char: %r" % last_char print " last_style: %s" % last_style # The easy ones are triggering after any of '#.[: '. # For speed, let's get the common ' ' out of the way. The only # trigger on space is 'complete-property-values'. if styleClassifier.is_default(last_style): if DEBUG: print " _trg_from_pos:: Default style: %d, ch: %r" % (last_style, last_char) # This may not even be a property-value, but at this stage we # don't care, as it will get worked out later in the # asynchronous call async_eval_at_trg(). return Trigger("CSS", TRG_FORM_CPLN, "property-values", pos, implicit, extra={"ac": ac}) elif styleClassifier.is_operator(last_style, ac): # anchors if DEBUG: print " _trg_from_pos:: OPERATOR style" if last_char == '#': return Trigger("CSS", TRG_FORM_CPLN, "anchors", pos, implicit, extra={"ac": ac}) elif last_char == ':': try: p, ch, style = ac.getPrevPosCharStyle(ignore_styles=styleClassifier.ignore_styles) if DEBUG: print " _trg_from_pos:: Looking at p: %d, ch: %r, style: %d" % (p, ch, style) except IndexError: style = None if DEBUG: print " _trg_from_pos:: style: %r" % (style) if style is None or \ not styleClassifier.is_identifier(style, ac): #if style is None or \ # not styleClassifier.is_css_style(style) or \ # styleClassifier.is_class(style, ac): # complete for pseudo-class-names return Trigger("CSS", TRG_FORM_CPLN, "pseudo-class-names", pos, implicit, extra={"ac": ac}) else: #if styleClassifier.is_identifier(style, ac): # calltip for property-values return Trigger("CSS", TRG_FORM_CALLTIP, "property-values", pos, implicit, extra={"ac": ac}) # class-names elif last_char == '.': return Trigger("CSS", TRG_FORM_CPLN, "class-names", pos, implicit, extra={"ac": ac}) # at-rule elif last_char == '@': #p, ch, style = ac.getPrevPosCharStyle(ignore_styles=styleClassifier.comment_styles) # XXX - Should check not beyond first rule set # - Should check not within a rule block. return Trigger("CSS", TRG_FORM_CPLN, "at-rule", pos, implicit, extra={"ac": ac}) elif last_char == '/': try: p, ch, style = ac.getPrevPosCharStyle() except IndexError: pass else: if ch == "<": # Looks like start of closing '</style>' # tag. While typing this the styling will # still be in the CSS range. return Trigger(buf.m_lang, TRG_FORM_CPLN, "end-tag", pos, implicit) # tag-names elif styleClassifier.is_tag(last_style, ac): # We trigger on tag names of specified length >= 1 char if DEBUG: print " _trg_from_pos:: TAG style" p, ch, style = last_pos, last_char, last_style try: while p >= 0: if DEBUG: print " _trg_from_pos:: Looking at p: %d, ch: %r, style: %d" % (p, ch, style) if not _isident(ch): p += 1 break elif style != last_style: if DEBUG: print " _trg_from_pos:: Current style is not a tag: %d" % (style) return None p, ch, style = ac.getPrevPosCharStyle() except IndexError: p = 0 return Trigger("CSS", TRG_FORM_CPLN, "tag-names", p, implicit, extra={"ac": ac}) elif styleClassifier.is_identifier(last_style, ac): if DEBUG: print " _trg_from_pos:: IDENTIFIER style" # property-names #print "here", accessor.text_range(0, pos) # We trigger on identifier names with any length >= 1 char pos = last_pos while pos >= 0: pos, ch, style = ac.getPrevPosCharStyle() if not _isident(ch): break elif style != last_style: return None return Trigger("CSS", TRG_FORM_CPLN, "property-names", pos+1, implicit, extra={"ac": ac}) elif styleClassifier.is_value(last_style, ac): p, ch, style = ac.getPrevPosCharStyle(ignore_styles=styleClassifier.comment_styles) if DEBUG: print " _trg_from_pos:: VALUE style" print " _trg_from_pos:: p: %s" % p print " _trg_from_pos:: ch: %r" % ch print " _trg_from_pos:: style: %s" % style ac.dump() # Implicit triggering only happens on a whitespace character # after any one of these ":,%) " characters # Note: last_char can be a value style yet also be whitespace # in straight CSS. if last_char in WHITESPACE: return Trigger("CSS", TRG_FORM_CPLN, "property-values", last_pos+1, implicit, extra={"ac": ac}) elif ch in WHITESPACE or ch in ":,%)": # Check to ensure this is not a pseudo-class! Bug: # http://bugs.activestate.com/show_bug.cgi?id=71073 if ch == ":": # Last style must be an identifier then! pp, pch, pstyle = ac.getPrevPosCharStyle( ignore_styles=styleClassifier.ignore_styles) if DEBUG: print "pp: %d, pch: %r, pstyle: %d" % (pp, pch, pstyle) if not styleClassifier.is_identifier(pstyle, ac): # This is likely a pseudo-class definition then, # no trigger here. if DEBUG: print "pseudo-class style found, no trigger." return None return Trigger("CSS", TRG_FORM_CPLN, "property-values", p+1, implicit, extra={"ac": ac}) # For explicit, we can also be inside a property already if not implicit and _isident(ch): # If there is already part of a value there, we need to move # the trigger point "p" to the start of the value. while _isident(ch): p, ch, style = ac.getPrevPosCharStyle() return Trigger("CSS", TRG_FORM_CPLN, "property-values", p+1, implicit, extra={"ac": ac}) return None elif styleClassifier.is_default(last_style): if DEBUG: print " _trg_from_pos:: Default style: %d, ch: %r" % (last_style, last_char) p, ch, style = ac.getPrecedingPosCharStyle(last_style) while style in styleClassifier.identifier_styles: p, ch, style = ac.getPrecedingPosCharStyle(style) if styleClassifier.is_operator(style) and ch in ":,)": return Trigger("CSS", TRG_FORM_CPLN, "property-values", p+1, implicit, extra={"ac": ac}) elif DEBUG: print " _trg_from_pos:: Unknown style: %d, ch: %r" % (last_style, last_char) # XXX "at-property-names" - Might be used later #elif last_style == SCE_CSS_DIRECTIVE: # # property-names # # We trigger on identifier names with length == 3 # #print "here", accessor.text_range(0, pos) # if pos >= 4 and accessor.char_at_pos(pos - 4) == ' ' and \ # self._is_ident_of_length(accessor, pos, length=3): # # We are good for completion # if DEBUG: # print "Got a trigger for 'at-property-names'" # return Trigger("CSS", TRG_FORM_CPLN, "at-property-names", # pos-3, implicit, extra={"ac": ac}) except IndexError: # Wen't out of range of buffer before we found anything useful pass if DEBUG: print "----- CSS trg_from_pos() -----" return None