diff options
| author | Fabián Ezequiel Gallina | 2012-08-09 00:30:37 -0300 |
|---|---|---|
| committer | Fabián Ezequiel Gallina | 2012-08-09 00:30:37 -0300 |
| commit | 489af14fa55c1774367460c3279c0897f0ea9f2c (patch) | |
| tree | a9efd774007b5a6a2e1ca48b0a4df051f33cb775 /lisp/progmodes/python.el | |
| parent | b5e94c8e466ea583b3518cda8d5219baad2ac427 (diff) | |
| download | emacs-489af14fa55c1774367460c3279c0897f0ea9f2c.tar.gz emacs-489af14fa55c1774367460c3279c0897f0ea9f2c.zip | |
* progmodes/python.el: Enhancements to forward-sexp.
(python-nav-forward-sexp): Rename from
python-nav-forward-sexp-function.
(python-nav--forward-sexp, python-nav--backward-sexp): New
functions.
Diffstat (limited to 'lisp/progmodes/python.el')
| -rw-r--r-- | lisp/progmodes/python.el | 239 |
1 files changed, 167 insertions, 72 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 58d984f8d4d..c65f2a2d595 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el | |||
| @@ -54,7 +54,7 @@ | |||
| 54 | ;; `python-nav-beginning-of-statement', `python-nav-end-of-statement', | 54 | ;; `python-nav-beginning-of-statement', `python-nav-end-of-statement', |
| 55 | ;; `python-nav-beginning-of-block' and `python-nav-end-of-block' are | 55 | ;; `python-nav-beginning-of-block' and `python-nav-end-of-block' are |
| 56 | ;; included but no bound to any key. At last but not least the | 56 | ;; included but no bound to any key. At last but not least the |
| 57 | ;; specialized `python-nav-forward-sexp-function' allows easy | 57 | ;; specialized `python-nav-forward-sexp' allows easy |
| 58 | ;; navigation between code blocks. | 58 | ;; navigation between code blocks. |
| 59 | 59 | ||
| 60 | ;; Shell interaction: is provided and allows you to execute easily any | 60 | ;; Shell interaction: is provided and allows you to execute easily any |
| @@ -1249,83 +1249,178 @@ backward to previous block." | |||
| 1249 | (and (goto-char starting-pos) nil) | 1249 | (and (goto-char starting-pos) nil) |
| 1250 | (and (not (= (point) starting-pos)) (point-marker))))) | 1250 | (and (not (= (point) starting-pos)) (point-marker))))) |
| 1251 | 1251 | ||
| 1252 | (defun python-nav-forward-sexp-function (&optional arg) | 1252 | (defun python-nav-lisp-forward-sexp-safe (&optional arg) |
| 1253 | "Safe version of standard `forward-sexp'. | ||
| 1254 | When ARG > 0 move forward, else if ARG is < 0." | ||
| 1255 | (or arg (setq arg 1)) | ||
| 1256 | (let ((forward-sexp-function nil) | ||
| 1257 | (paren-regexp | ||
| 1258 | (if (> arg 0) (python-rx close-paren) (python-rx open-paren))) | ||
| 1259 | (search-fn | ||
| 1260 | (if (> arg 0) #'re-search-forward #'re-search-backward))) | ||
| 1261 | (condition-case nil | ||
| 1262 | (forward-sexp arg) | ||
| 1263 | (error | ||
| 1264 | (while (and (funcall search-fn paren-regexp nil t) | ||
| 1265 | (python-syntax-context 'paren))))))) | ||
| 1266 | |||
| 1267 | (defun python-nav--forward-sexp () | ||
| 1268 | "Move to forward sexp." | ||
| 1269 | (case (python-syntax-context-type) | ||
| 1270 | (string | ||
| 1271 | ;; Inside of a string, get out of it. | ||
| 1272 | (while (and (re-search-forward "[\"']" nil t) | ||
| 1273 | (python-syntax-context 'string)))) | ||
| 1274 | (comment | ||
| 1275 | ;; Inside of a comment, just move forward. | ||
| 1276 | (python-util-forward-comment)) | ||
| 1277 | (paren | ||
| 1278 | (python-nav-lisp-forward-sexp-safe 1)) | ||
| 1279 | (t | ||
| 1280 | (if (and (not (eobp)) | ||
| 1281 | (= (syntax-class (syntax-after (point))) 4)) | ||
| 1282 | ;; Looking an open-paren | ||
| 1283 | (python-nav-lisp-forward-sexp-safe 1) | ||
| 1284 | (let ((block-starting-pos | ||
| 1285 | (save-excursion (python-nav-beginning-of-block))) | ||
| 1286 | (block-ending-pos | ||
| 1287 | (save-excursion (python-nav-end-of-block))) | ||
| 1288 | (next-block-starting-pos | ||
| 1289 | (save-excursion (python-nav-forward-block)))) | ||
| 1290 | (cond | ||
| 1291 | ((not block-starting-pos) | ||
| 1292 | ;; Not inside a block, move to closest one. | ||
| 1293 | (and next-block-starting-pos | ||
| 1294 | (goto-char next-block-starting-pos))) | ||
| 1295 | ((= (point) block-starting-pos) | ||
| 1296 | ;; Point is at beginning of block | ||
| 1297 | (if (and next-block-starting-pos | ||
| 1298 | (< next-block-starting-pos block-ending-pos)) | ||
| 1299 | ;; Beginning of next block is closer than current's | ||
| 1300 | ;; end, move to it. | ||
| 1301 | (goto-char next-block-starting-pos) | ||
| 1302 | (goto-char block-ending-pos))) | ||
| 1303 | ((= block-ending-pos (point)) | ||
| 1304 | ;; Point is at end of current block | ||
| 1305 | (let ((parent-block-end-pos | ||
| 1306 | (save-excursion | ||
| 1307 | (python-util-forward-comment) | ||
| 1308 | (python-nav-beginning-of-block) | ||
| 1309 | (python-nav-end-of-block)))) | ||
| 1310 | (if (and parent-block-end-pos | ||
| 1311 | (or (not next-block-starting-pos) | ||
| 1312 | (> next-block-starting-pos parent-block-end-pos))) | ||
| 1313 | ;; If the parent block ends before next block | ||
| 1314 | ;; starts move to it. | ||
| 1315 | (goto-char parent-block-end-pos) | ||
| 1316 | (and next-block-starting-pos | ||
| 1317 | (goto-char next-block-starting-pos))))) | ||
| 1318 | (t (python-nav-end-of-block)))))))) | ||
| 1319 | |||
| 1320 | (defun python-nav--backward-sexp () | ||
| 1321 | "Move to backward sexp." | ||
| 1322 | (case (python-syntax-context-type) | ||
| 1323 | (string | ||
| 1324 | ;; Inside of a string, get out of it. | ||
| 1325 | (while (and (re-search-backward "[\"']" nil t) | ||
| 1326 | (python-syntax-context 'string)))) | ||
| 1327 | (comment | ||
| 1328 | ;; Inside of a comment, just move backward. | ||
| 1329 | (python-util-forward-comment -1)) | ||
| 1330 | (paren | ||
| 1331 | ;; Handle parens like we are lisp. | ||
| 1332 | (python-nav-lisp-forward-sexp-safe -1)) | ||
| 1333 | (t | ||
| 1334 | (let* ((block-starting-pos | ||
| 1335 | (save-excursion (python-nav-beginning-of-block))) | ||
| 1336 | (block-ending-pos | ||
| 1337 | (save-excursion (python-nav-end-of-block))) | ||
| 1338 | (prev-block-ending-pos | ||
| 1339 | (save-excursion (when (python-nav-backward-block) | ||
| 1340 | (python-nav-end-of-block)))) | ||
| 1341 | (prev-block-parent-ending-pos | ||
| 1342 | (save-excursion | ||
| 1343 | (when prev-block-ending-pos | ||
| 1344 | (goto-char prev-block-ending-pos) | ||
| 1345 | (python-util-forward-comment) | ||
| 1346 | (python-nav-beginning-of-block) | ||
| 1347 | (python-nav-end-of-block))))) | ||
| 1348 | (if (and (not (bobp)) | ||
| 1349 | (= (syntax-class (syntax-after (1- (point)))) 5)) | ||
| 1350 | ;; Char before point is a paren closing char, handle it | ||
| 1351 | ;; like we are lisp. | ||
| 1352 | (python-nav-lisp-forward-sexp-safe -1) | ||
| 1353 | (cond | ||
| 1354 | ((not block-ending-pos) | ||
| 1355 | ;; Not in and ending pos, move to end of previous block. | ||
| 1356 | (and (python-nav-backward-block) | ||
| 1357 | (python-nav-end-of-block))) | ||
| 1358 | ((= (point) block-ending-pos) | ||
| 1359 | ;; In ending pos, we need to search backwards for the | ||
| 1360 | ;; closest point looking the list of candidates from here. | ||
| 1361 | (let ((candidates)) | ||
| 1362 | (dolist (name | ||
| 1363 | '(prev-block-parent-ending-pos | ||
| 1364 | prev-block-ending-pos | ||
| 1365 | block-ending-pos | ||
| 1366 | block-starting-pos)) | ||
| 1367 | (when (and (symbol-value name) | ||
| 1368 | (< (symbol-value name) (point))) | ||
| 1369 | (add-to-list 'candidates (symbol-value name)))) | ||
| 1370 | (goto-char (apply 'max candidates)))) | ||
| 1371 | ((> (point) block-ending-pos) | ||
| 1372 | ;; After an ending position, move to it. | ||
| 1373 | (goto-char block-ending-pos)) | ||
| 1374 | ((= (point) block-starting-pos) | ||
| 1375 | ;; On a block starting position. | ||
| 1376 | (if (not (> (point) (or prev-block-ending-pos (point)))) | ||
| 1377 | ;; Point is after the end position of the block that | ||
| 1378 | ;; wraps the current one, just move a block backward. | ||
| 1379 | (python-nav-backward-block) | ||
| 1380 | ;; If we got here we are facing a case like this one: | ||
| 1381 | ;; | ||
| 1382 | ;; try: | ||
| 1383 | ;; return here() | ||
| 1384 | ;; except Exception as e: | ||
| 1385 | ;; | ||
| 1386 | ;; Where point is on the "except" and must move to the | ||
| 1387 | ;; end of "here()". | ||
| 1388 | (goto-char prev-block-ending-pos) | ||
| 1389 | (let ((parent-block-ending-pos | ||
| 1390 | (save-excursion | ||
| 1391 | (python-nav-forward-sexp) | ||
| 1392 | (and (not (looking-at (python-rx block-start))) | ||
| 1393 | (point))))) | ||
| 1394 | (when (and parent-block-ending-pos | ||
| 1395 | (> parent-block-ending-pos prev-block-ending-pos)) | ||
| 1396 | ;; If we got here we are facing a case like this one: | ||
| 1397 | ;; | ||
| 1398 | ;; except ImportError: | ||
| 1399 | ;; if predicate(): | ||
| 1400 | ;; processing() | ||
| 1401 | ;; here() | ||
| 1402 | ;; except AttributeError: | ||
| 1403 | ;; | ||
| 1404 | ;; Where point is on the "except" and must move to | ||
| 1405 | ;; the end of "here()". Without this extra step we'd | ||
| 1406 | ;; just get to the end of processing(). | ||
| 1407 | (goto-char parent-block-ending-pos))))) | ||
| 1408 | (t | ||
| 1409 | (if (and prev-block-ending-pos (< prev-block-ending-pos (point))) | ||
| 1410 | (goto-char prev-block-ending-pos) | ||
| 1411 | (python-nav-beginning-of-block))))))))) | ||
| 1412 | |||
| 1413 | (defun python-nav-forward-sexp (&optional arg) | ||
| 1253 | "Move forward across one block of code. | 1414 | "Move forward across one block of code. |
| 1254 | With ARG, do it that many times. Negative arg -N means | 1415 | With ARG, do it that many times. Negative arg -N means |
| 1255 | move backward N times." | 1416 | move backward N times." |
| 1256 | (interactive "^p") | 1417 | (interactive "^p") |
| 1257 | (or arg (setq arg 1)) | 1418 | (or arg (setq arg 1)) |
| 1258 | (while (> arg 0) | 1419 | (while (> arg 0) |
| 1259 | (let ((block-starting-pos | 1420 | (python-nav--forward-sexp) |
| 1260 | (save-excursion (python-nav-beginning-of-block))) | 1421 | (setq arg (1- arg))) |
| 1261 | (block-ending-pos | ||
| 1262 | (save-excursion (python-nav-end-of-block))) | ||
| 1263 | (next-block-starting-pos | ||
| 1264 | (save-excursion (python-nav-forward-block)))) | ||
| 1265 | (cond ((not block-starting-pos) | ||
| 1266 | (python-nav-forward-block)) | ||
| 1267 | ((= (point) block-starting-pos) | ||
| 1268 | (if (or (not next-block-starting-pos) | ||
| 1269 | (< block-ending-pos next-block-starting-pos)) | ||
| 1270 | (python-nav-end-of-block) | ||
| 1271 | (python-nav-forward-block))) | ||
| 1272 | ((= block-ending-pos (point)) | ||
| 1273 | (let ((parent-block-end-pos | ||
| 1274 | (save-excursion | ||
| 1275 | (python-util-forward-comment) | ||
| 1276 | (python-nav-beginning-of-block) | ||
| 1277 | (python-nav-end-of-block)))) | ||
| 1278 | (if (and parent-block-end-pos | ||
| 1279 | (or (not next-block-starting-pos) | ||
| 1280 | (> next-block-starting-pos parent-block-end-pos))) | ||
| 1281 | (goto-char parent-block-end-pos) | ||
| 1282 | (python-nav-forward-block)))) | ||
| 1283 | (t (python-nav-end-of-block)))) | ||
| 1284 | (setq arg (1- arg))) | ||
| 1285 | (while (< arg 0) | 1422 | (while (< arg 0) |
| 1286 | (let* ((block-starting-pos | 1423 | (python-nav--backward-sexp) |
| 1287 | (save-excursion (python-nav-beginning-of-block))) | ||
| 1288 | (block-ending-pos | ||
| 1289 | (save-excursion (python-nav-end-of-block))) | ||
| 1290 | (prev-block-ending-pos | ||
| 1291 | (save-excursion (when (python-nav-backward-block) | ||
| 1292 | (python-nav-end-of-block)))) | ||
| 1293 | (prev-block-parent-ending-pos | ||
| 1294 | (save-excursion | ||
| 1295 | (when prev-block-ending-pos | ||
| 1296 | (goto-char prev-block-ending-pos) | ||
| 1297 | (python-util-forward-comment) | ||
| 1298 | (python-nav-beginning-of-block) | ||
| 1299 | (python-nav-end-of-block))))) | ||
| 1300 | (cond ((not block-ending-pos) | ||
| 1301 | (and (python-nav-backward-block) | ||
| 1302 | (python-nav-end-of-block))) | ||
| 1303 | ((= (point) block-ending-pos) | ||
| 1304 | (let ((candidates)) | ||
| 1305 | (dolist (name | ||
| 1306 | '(prev-block-parent-ending-pos | ||
| 1307 | prev-block-ending-pos | ||
| 1308 | block-ending-pos | ||
| 1309 | block-starting-pos)) | ||
| 1310 | (when (and (symbol-value name) | ||
| 1311 | (< (symbol-value name) (point))) | ||
| 1312 | (add-to-list 'candidates (symbol-value name)))) | ||
| 1313 | (goto-char (apply 'max candidates)))) | ||
| 1314 | ((> (point) block-ending-pos) | ||
| 1315 | (python-nav-end-of-block)) | ||
| 1316 | ((= (point) block-starting-pos) | ||
| 1317 | (if (not (> (point) (or prev-block-ending-pos (point)))) | ||
| 1318 | (python-nav-backward-block) | ||
| 1319 | (goto-char prev-block-ending-pos) | ||
| 1320 | (let ((parent-block-ending-pos | ||
| 1321 | (save-excursion | ||
| 1322 | (python-nav-forward-sexp-function) | ||
| 1323 | (and (not (looking-at (python-rx block-start))) | ||
| 1324 | (point))))) | ||
| 1325 | (when (and parent-block-ending-pos | ||
| 1326 | (> parent-block-ending-pos prev-block-ending-pos)) | ||
| 1327 | (goto-char parent-block-ending-pos))))) | ||
| 1328 | (t (python-nav-beginning-of-block)))) | ||
| 1329 | (setq arg (1+ arg)))) | 1424 | (setq arg (1+ arg)))) |
| 1330 | 1425 | ||
| 1331 | 1426 | ||
| @@ -2848,7 +2943,7 @@ if that value is non-nil." | |||
| 2848 | (set (make-local-variable 'parse-sexp-ignore-comments) t) | 2943 | (set (make-local-variable 'parse-sexp-ignore-comments) t) |
| 2849 | 2944 | ||
| 2850 | (set (make-local-variable 'forward-sexp-function) | 2945 | (set (make-local-variable 'forward-sexp-function) |
| 2851 | 'python-nav-forward-sexp-function) | 2946 | 'python-nav-forward-sexp) |
| 2852 | 2947 | ||
| 2853 | (set (make-local-variable 'font-lock-defaults) | 2948 | (set (make-local-variable 'font-lock-defaults) |
| 2854 | '(python-font-lock-keywords nil nil nil nil)) | 2949 | '(python-font-lock-keywords nil nil nil nil)) |