Permalink
Browse files

implement flexible constraints checkers using decorators. fix doxygen…

… comment style when move comments.
  • Loading branch information...
1 parent 8dddce4 commit 114eedf93517dd45d6b9e46a2a4903a14af287bc @zaufi committed Jun 11, 2012
Showing with 151 additions and 33 deletions.
  1. +1 −1 CHANGES.md
  2. +7 −1 CMakeLists.txt
  3. +31 −16 commentar.py
  4. +14 −6 format.py
  5. +40 −0 libkatepate/common.py
  6. +57 −8 libkatepate/decorators.py
  7. +1 −1 libkatepate/ui.py
View
@@ -10,7 +10,7 @@ Version 0.5 (2012-06-10)
* improvements in C++ expansions
* add passive popups w/ native look and feel (as for me it's better than
custom popups in Pate, which is looks ugly w/ my color scheme)
-* use decorators to disable (show popup w/ alert) actions by document type,
+* use decorators to disable (show popup w/ alert) actions by various constraints,
for example commenting text block w/ `#if 0' have sense only for C/C++
documents
View
@@ -61,7 +61,13 @@ set(CMAKE_INSTALL_PREFIX "${KDE4_DATA_DIR}/kate/pate")
include(PythonMacros)
set(LIBKATEPATE_DIR libkatepate)
-set(LIBKATEPATE_SOURCES ${LIBKATEPATE_DIR}/__init__.py ${LIBKATEPATE_DIR}/ui.py)
+set(
+ LIBKATEPATE_SOURCES
+ ${LIBKATEPATE_DIR}/__init__.py
+ ${LIBKATEPATE_DIR}/common.py
+ ${LIBKATEPATE_DIR}/decorators.py
+ ${LIBKATEPATE_DIR}/ui.py
+ )
python_install(${CMAKE_INSTALL_PREFIX}/${LIBKATEPATE_DIR} ${LIBKATEPATE_SOURCES})
set(PLUGIN_SOURCES format.py block.py commentar.py expand/expand.py)
View
@@ -58,8 +58,10 @@
import kate
import kate.gui
import re
+# TODO Is it really bad to import in such way? (even here?)
from PyKDE4.ktexteditor import KTextEditor
-from libkatepate.decorators import cpp_only
+from libkatepate.decorators import restrict_doc_type, check_constraints, comment_char_must_be_known
+from libkatepate.common import getCommentStyleForDoc
if 'commentar:comment-position' not in kate.configuration:
@@ -75,17 +77,12 @@
BLOCK_END_SEARCH_RE = re.compile('^\s*#\s*endif.*$')
BLOCK_ELSE_ENDIF_MATCH_RE = re.compile('^\s*#\s*(endif|else).*$')
BLOCK_START_GET_COND_RE = re.compile('^\s*#\s*(if((n)?def)?)\s+(.*)\s*$')
-COMMENT_STRING = {
- 'Python' : '# '
- , 'CMake' : '# '
- , 'Bash' : '# '
- , 'C++' : '//'
-}
def isApplicableMime():
return str(kate.activeDocument().mimeType()).find('c++') != -1
+
def insertTextBlock(document, line, text):
"""Put lines from a list into the current position (line) in a document
"""
@@ -94,6 +91,7 @@ def insertTextBlock(document, line, text):
document.insertLine(line, l)
line += 1
+
#
# Build a list of tuples (start, end, elseif, is_comment) for all #if/#elseif/#endif blocks
# in a document. If block contains #endif 3rd element will point its line, -1 otherwise.
@@ -131,6 +129,7 @@ def buildIfEndifMap(document):
return blockRanges
+
def locateBlock(currentLine, blockRanges, ignoreComments = False):
"""Find an index of a current block
@@ -150,6 +149,7 @@ def locateBlock(currentLine, blockRanges, ignoreComments = False):
c += 1
return idx;
+
def processLine(line, commentCh):
result = []
column = COMMENT_POS
@@ -167,6 +167,9 @@ def processLine(line, commentCh):
else:
# Move comment to the line above
column = len(before) - len(before.lstrip())
+ # NOTE Try to fix Doxygen comment on the fly: '///<' or '//!<' --> '///'
+ if after[1] == '<' and (after[0] == '!' or after[0] == '/'):
+ after = '/' + after[2:]
result.append(' ' * column + commentCh + after.rstrip())
result.append(before_s)
else:
@@ -208,7 +211,10 @@ def processLine(line, commentCh):
result.append(' ' * COMMENT_POS + commentCh + ' ')
return (result, column + len(commentCh) + 1)
+
@kate.action('Inline Comment', shortcut='Alt+D', menu='Edit')
+@check_constraints
+@comment_char_must_be_known()
def commentar():
"""Append or align an inlined comment to COMMENT_POS for the current line or the selection.
@@ -217,7 +223,7 @@ def commentar():
document = kate.activeDocument()
view = kate.activeView()
currentPosition = view.cursorPosition()
- commentCh = COMMENT_STRING.get(document.highlightingMode(), '# ')
+ commentCh = getCommentStyleForDoc(document)
if view.selection():
selectedText = view.selectionText().split('\n')
@@ -260,13 +266,15 @@ def commentar():
@kate.action('Move Comment Above', shortcut='Meta+Left')
+@check_constraints
+@comment_char_must_be_known()
def moveAbove():
"""Move inlined comment before the current line at same align
"""
document = kate.activeDocument()
view = kate.activeView()
currentPosition = view.cursorPosition()
- commentCh = COMMENT_STRING.get(document.highlightingMode(), '# ')
+ commentCh = getCommentStyleForDoc(document)
if not view.selection():
insertionText = list()
@@ -315,11 +323,13 @@ def moveAbove():
# (if line below still has no one)
#
@kate.action('Move Comment Inline', shortcut='Meta+Right')
+@check_constraints
+@comment_char_must_be_known()
def moveInline():
document = kate.activeDocument()
view = kate.activeView()
currentPosition = view.cursorPosition()
- commentCh = COMMENT_STRING.get(document.highlightingMode(), '# ')
+ commentCh = getCommentStyleForDoc(document)
if not view.selection():
insertionText = []
@@ -328,7 +338,7 @@ def moveInline():
# Split a line before and after a comment
(before, comment, after) = currentLine.partition(commentCh)
- # Is there is some text on a line?
+ # Is there some text on a line?
if bool(before.strip()):
return # Aha... move cursor co comment u stupid bastard!
else:
@@ -383,7 +393,8 @@ def moveInline():
@kate.action('Comment Block w/ `#if0`', shortcut='Meta+D', menu='Edit')
-@cpp_only
+@check_constraints
+@restrict_doc_type('C++')
def commentBlock():
view = kate.activeView()
@@ -408,7 +419,8 @@ def commentBlock():
@kate.action('Toggle `#if0/#if1` Block', shortcut='Meta+Shift+D', menu='Edit')
-@cpp_only
+@check_constraints
+@restrict_doc_type('C++')
def toggleBlock():
document = kate.activeDocument()
view = kate.activeView()
@@ -439,7 +451,8 @@ def toggleBlock():
@kate.action('Remove `#if 0` Block', shortcut='Meta+R', menu='Edit')
-@cpp_only
+@check_constraints
+@restrict_doc_type('C++')
def removeBlock():
document = kate.activeDocument()
view = kate.activeView()
@@ -483,7 +496,8 @@ def removeBlock():
@kate.action('Select Current Block', shortcut='Meta+S', menu='Edit')
-@cpp_only
+@check_constraints
+@restrict_doc_type('C++')
def selectBlock():
document = kate.activeDocument()
view = kate.activeView()
@@ -607,7 +621,8 @@ def turnFromBlockComment():
@kate.action('Transform Doxygen Comments', shortcut='Meta+X', menu='Edit')
-@cpp_only
+@check_constraints
+@restrict_doc_type('C++')
def toggleDoxyComment():
document = kate.activeDocument()
view = kate.activeView()
View
@@ -302,19 +302,23 @@ def boostFormat():
try:
r, nestedRanges, breakPositions = getRangeTopology(',')
except LookupError as error:
- ui.popup("Failed to parse C++ expression", str(error))
+ ui.popup("Failed to parse C++ expression", str(error), "face-sad")
return
if r.isEmpty(): # Is range empty?
- ui.popup("Failed to parse C++ expression", "Didn't found anything to format. Sorry ;-(")
+ ui.popup(
+ "Failed to parse C++ expression"
+ , "Didn't found anything to format. Sorry..."
+ , "face-sad"
+ )
return # Nothing interesting wasn't found...
# Rescan the range w/ ';' as breaker added if current range is a `for` statement
if document.line(r.start().line())[0:r.start().column() - 1].rstrip().endswith('for'):
try:
r, nestedRanges, breakPositions = getRangeTopology(',;')
except LookupError as error:
- ui.popup("Failed to parse C++ expression", str(error))
+ ui.popup("Failed to parse C++ expression", str(error), "face-sad")
return
# Going to format a text whithin a selected range
@@ -350,19 +354,23 @@ def boostFormat():
try:
r, nestedRanges, breakPositions = getRangeTopology(',')
except LookupError as error:
- ui.popup("Failed to parse C++ expression", str(error))
+ ui.popup("Failed to parse C++ expression", str(error), "face-sad")
return
if r.isEmpty(): # Is range empty?
- ui.popup("Failed to parse C++ expression", "Didn't found anything to format. Sorry ;-(")
+ ui.popup(
+ "Failed to parse C++ expression"
+ , "Didn't found anything to format. Sorry"
+ , "face-sad"
+ )
return # Nothing interesting wasn't found...
# Rescan the range w/ ';' as breaker added if current range is a `for` statement
if document.line(r.start().line())[0:r.start().column() - 1].rstrip().endswith('for'):
try:
r, nestedRanges, breakPositions = getRangeTopology(',;')
except LookupError as error:
- ui.popup("Failed to parse C++ expression", str(error))
+ ui.popup("Failed to parse C++ expression", str(error), "face-sad")
return
# Going to unformat a text whithin a selected range
View
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2010-2012 by Alex Trubov <i.zaufi@gmail.com>
+#
+#
+# This software is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This software is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this software. If not, see <http://www.gnu.org/licenses/>.
+#
+
+''' Reusable code for Kate/Pâté plugins: general purpose shared code '''
+
+import kate
+
+_COMMENT_STRINGS_MAP = {
+ 'Python' : '# '
+ , 'Perl' : '# '
+ , 'CMake' : '# '
+ , 'Bash' : '# '
+ , 'C++' : '//'
+}
+
+
+def isKnownCommentStyle(docType):
+ ''' Check if we know how to comment a line in a given document type '''
+ return docType in _COMMENT_STRINGS_MAP
+
+def getCommentStyleForDoc(document):
+ ''' Get single line comment string for a document '''
+ assert(document.highlightingMode() in _COMMENT_STRINGS_MAP)
+ return _COMMENT_STRINGS_MAP[document.highlightingMode()]
View
@@ -19,17 +19,66 @@
''' Reusable code for Kate/Pâté plugins: C++ document types related common code '''
+import functools
import kate
from libkatepate import ui
+from libkatepate import common
+def append_constraint(action, constraint):
+ if not hasattr(action, 'constraints'):
+ action.constraints = []
+ print("*** append_constraint: c=" + repr(constraint))
+ action.constraints.append(constraint)
-def cpp_only(action):
- ''' Decorator to enable @kate.action only for C++ documents
- '''
- def checker(**params):
+
+def check_constraints(action):
+ ''' Decorator to evaluate constraints assigned to a given action '''
+ def checker(*args, **kw):
document = kate.activeDocument()
- if document.highlightingMode() == 'C++':
- return action(**params)
- else:
- ui.popup("Alert", "This action have sense <b>only</b> for C++ documents!")
+ # TODO Why this shit^W`if` doesn't work? WTF?!
+ #if not hasattr(action, 'constraints') or reduce(lambda i, j: i(document) and j(document), action.constraints)():
+ if hasattr(action, 'constraints'):
+ for c in action.constraints:
+ if not c(document):
+ return
+ return action(*args, **kw)
return checker
+
+
+def restrict_doc_type(docType):
+ def restrict_doc_type_decorator(action):
+ # TODO Investgate required why in opposite params order
+ # keyword arguments can't pass through `partial` binder
+ # WTF?? WTF!!
+ def doc_type_checker(doc_type, document):
+ if document.highlightingMode() != doc_type:
+ ui.popup(
+ "Alert"
+ , "This action have sense <b>only</b> for " + doc_type + " documents!"
+ , "face-wink"
+ )
+ return False
+ return True
+ binded_predicate = functools.partial(doc_type_checker, docType)
+ append_constraint(action, binded_predicate)
+ return action
+ return restrict_doc_type_decorator
+
+
+def comment_char_must_be_known(dummy = None):
+ def comment_char_known_decorator(action):
+ # TODO Same shit here as for restrict_doc_type!
+ def comment_char_checker(dummy, document):
+ doc_type = document.highlightingMode()
+ result = common.isKnownCommentStyle(doc_type)
+ if not result:
+ ui.popup(
+ "Oops!"
+ , "Don't know how comments look like for " + doc_type + " documents!"
+ , "face-uncertain"
+ )
+ return result
+ binded_predicate = functools.partial(comment_char_checker, dummy)
+ append_constraint(action, binded_predicate)
+ return action
+ return comment_char_known_decorator
View
@@ -32,7 +32,7 @@ def popup(caption, text, iconName = None, iconSize = 16):
'''
parentWidget = kate.mainWindow()
if iconName:
- icon = KIcon ("dialog-cancel").pixmap(QSize(iconSize, iconSize))
+ icon = KIcon (iconName).pixmap(QSize(iconSize, iconSize))
KPassivePopup.message(caption, text, icon, parentWidget)
else:
KPassivePopup.message(caption, text, parentWidget)

0 comments on commit 114eedf

Please sign in to comment.