diff options
| author | Fabián Ezequiel Gallina | 2012-10-08 02:19:15 -0300 |
|---|---|---|
| committer | Fabián Ezequiel Gallina | 2012-10-08 02:19:15 -0300 |
| commit | 8dbce54cc73d16375b8d233da7271054eb4cda34 (patch) | |
| tree | 05b17501a782d7a8af4ad0a8116e7511084b1731 /lisp/progmodes/python.el | |
| parent | 5acd2b3ece740bbe487e9474f30665317db6f190 (diff) | |
| download | emacs-8dbce54cc73d16375b8d233da7271054eb4cda34.tar.gz emacs-8dbce54cc73d16375b8d233da7271054eb4cda34.zip | |
Enhancements on forward-sexp movement.
* progmodes/python.el (python-nav-beginning-of-statement)
(python-nav-end-of-statement): Return point-marker.
(python-nav-forward-sexp): lisp-like forward-sexp behavior.
(python-info-current-symbol)
(python-info-statement-starts-block-p): Rename from
python-info-beginning-of-block-p.
(python-info-statement-ends-block-p): Rename from
python-info-end-of-block-p.
(python-info-beginning-of-statement-p)
(python-info-end-of-statement-p)
(python-info-beginning-of-block-p, python-info-end-of-block-p):
New functions.
Diffstat (limited to 'lisp/progmodes/python.el')
| -rw-r--r-- | lisp/progmodes/python.el | 276 |
1 files changed, 131 insertions, 145 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ffb2e66ca9d..6b0dc954ca7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el | |||
| @@ -1169,7 +1169,8 @@ Returns nil if point is not in a def or class." | |||
| 1169 | (python-info-line-ends-backslash-p)) | 1169 | (python-info-line-ends-backslash-p)) |
| 1170 | (python-syntax-context 'string) | 1170 | (python-syntax-context 'string) |
| 1171 | (python-syntax-context 'paren)) | 1171 | (python-syntax-context 'paren)) |
| 1172 | (forward-line -1))))) | 1172 | (forward-line -1)))) |
| 1173 | (point-marker)) | ||
| 1173 | 1174 | ||
| 1174 | (defun python-nav-end-of-statement () | 1175 | (defun python-nav-end-of-statement () |
| 1175 | "Move to end of current statement." | 1176 | "Move to end of current statement." |
| @@ -1180,7 +1181,8 @@ Returns nil if point is not in a def or class." | |||
| 1180 | (python-info-line-ends-backslash-p) | 1181 | (python-info-line-ends-backslash-p) |
| 1181 | (python-syntax-context 'string) | 1182 | (python-syntax-context 'string) |
| 1182 | (python-syntax-context 'paren)) | 1183 | (python-syntax-context 'paren)) |
| 1183 | (forward-line 1))))) | 1184 | (forward-line 1)))) |
| 1185 | (point-marker)) | ||
| 1184 | 1186 | ||
| 1185 | (defun python-nav-backward-statement (&optional arg) | 1187 | (defun python-nav-backward-statement (&optional arg) |
| 1186 | "Move backward to previous statement. | 1188 | "Move backward to previous statement. |
| @@ -1295,151 +1297,104 @@ When ARG > 0 move forward, else if ARG is < 0." | |||
| 1295 | (while (and (funcall search-fn paren-regexp nil t) | 1297 | (while (and (funcall search-fn paren-regexp nil t) |
| 1296 | (python-syntax-context 'paren))))))) | 1298 | (python-syntax-context 'paren))))))) |
| 1297 | 1299 | ||
| 1298 | (defun python-nav--forward-sexp () | 1300 | (defun python-nav--forward-sexp (&optional dir) |
| 1299 | "Move to forward sexp." | 1301 | "Move to forward sexp. |
| 1300 | (case (python-syntax-context-type) | 1302 | With positive Optional argument DIR direction move forward, else |
| 1301 | (string | 1303 | backwards." |
| 1302 | ;; Inside of a string, get out of it. | 1304 | (setq dir (or dir 1)) |
| 1303 | (while (and (re-search-forward "[\"']" nil t) | 1305 | (unless (= dir 0) |
| 1304 | (python-syntax-context 'string)))) | 1306 | (let* ((forward-p (if (> dir 0) |
| 1305 | (comment | 1307 | (and (setq dir 1) t) |
| 1306 | ;; Inside of a comment, just move forward. | 1308 | (and (setq dir -1) nil))) |
| 1307 | (python-util-forward-comment)) | 1309 | (re-search-fn (if forward-p |
| 1308 | (paren | 1310 | 're-search-forward |
| 1309 | (python-nav-lisp-forward-sexp-safe 1)) | 1311 | 're-search-backward)) |
| 1310 | (t | 1312 | (context-type (python-syntax-context-type))) |
| 1311 | (if (and (not (eobp)) | 1313 | (cond |
| 1312 | (= (syntax-class (syntax-after (point))) 4)) | 1314 | ((eq context-type 'string) |
| 1313 | ;; Looking an open-paren | 1315 | ;; Inside of a string, get out of it. |
| 1314 | (python-nav-lisp-forward-sexp-safe 1) | 1316 | (while (and (funcall re-search-fn "[\"']" nil t) |
| 1315 | (let ((block-starting-pos | 1317 | (python-syntax-context 'string)))) |
| 1316 | (save-excursion (python-nav-beginning-of-block))) | 1318 | ((eq context-type 'comment) |
| 1317 | (block-ending-pos | 1319 | ;; Inside of a comment, just move forward. |
| 1318 | (save-excursion (python-nav-end-of-block))) | 1320 | (python-util-forward-comment dir)) |
| 1319 | (next-block-starting-pos | 1321 | ((or (eq context-type 'paren) |
| 1320 | (save-excursion (python-nav-forward-block)))) | 1322 | (and forward-p (looking-at (python-rx open-paren))) |
| 1321 | (cond | 1323 | (and (not forward-p) |
| 1322 | ((not block-starting-pos) | 1324 | (eq (syntax-class (syntax-after (1- (point)))) |
| 1323 | ;; Not inside a block, move to closest one. | 1325 | (car (string-to-syntax ")"))))) |
| 1324 | (and next-block-starting-pos | 1326 | ;; Inside a paren or looking at it, lisp knows what to do. |
| 1325 | (goto-char next-block-starting-pos))) | 1327 | (python-nav-lisp-forward-sexp-safe dir)) |
| 1326 | ((= (point) block-starting-pos) | 1328 | (t |
| 1327 | ;; Point is at beginning of block | 1329 | ;; This part handles the lispy feel of |
| 1328 | (if (and next-block-starting-pos | 1330 | ;; `python-nav-forward-sexp'. Knowing everything about the |
| 1329 | (< next-block-starting-pos block-ending-pos)) | 1331 | ;; current context and the context of the next sexp tries to |
| 1330 | ;; Beginning of next block is closer than current's | 1332 | ;; follow the lisp sexp motion commands in a symmetric manner. |
| 1331 | ;; end, move to it. | 1333 | (let* ((context |
| 1332 | (goto-char next-block-starting-pos) | 1334 | (cond |
| 1333 | (goto-char block-ending-pos))) | 1335 | ((python-info-beginning-of-block-p) 'block-start) |
| 1334 | ((= block-ending-pos (point)) | 1336 | ((python-info-end-of-block-p) 'block-end) |
| 1335 | ;; Point is at end of current block | 1337 | ((python-info-beginning-of-statement-p) 'statement-start) |
| 1336 | (let ((parent-block-end-pos | 1338 | ((python-info-end-of-statement-p) 'statement-end))) |
| 1337 | (save-excursion | 1339 | (next-sexp-pos |
| 1338 | (python-util-forward-comment) | 1340 | (save-excursion |
| 1339 | (python-nav-beginning-of-block) | 1341 | (python-nav-lisp-forward-sexp-safe dir) |
| 1340 | (python-nav-end-of-block)))) | 1342 | (point))) |
| 1341 | (if (and parent-block-end-pos | 1343 | (next-sexp-context |
| 1342 | (or (not next-block-starting-pos) | 1344 | (save-excursion |
| 1343 | (> next-block-starting-pos parent-block-end-pos))) | 1345 | (goto-char next-sexp-pos) |
| 1344 | ;; If the parent block ends before next block | 1346 | (cond |
| 1345 | ;; starts move to it. | 1347 | ((python-info-beginning-of-block-p) 'block-start) |
| 1346 | (goto-char parent-block-end-pos) | 1348 | ((python-info-end-of-block-p) 'block-end) |
| 1347 | (and next-block-starting-pos | 1349 | ((python-info-beginning-of-statement-p) 'statement-start) |
| 1348 | (goto-char next-block-starting-pos))))) | 1350 | ((python-info-end-of-statement-p) 'statement-end) |
| 1349 | (t (python-nav-end-of-block)))))))) | 1351 | ((python-info-statement-starts-block-p) 'starts-block) |
| 1352 | ((python-info-statement-ends-block-p) 'ends-block))))) | ||
| 1353 | (if forward-p | ||
| 1354 | (cond ((and (not (eobp)) | ||
| 1355 | (python-info-current-line-empty-p)) | ||
| 1356 | (python-util-forward-comment dir) | ||
| 1357 | (python-nav--forward-sexp dir)) | ||
| 1358 | ((eq context 'block-start) | ||
| 1359 | (python-nav-end-of-block)) | ||
| 1360 | ((eq context 'statement-start) | ||
| 1361 | (python-nav-end-of-statement)) | ||
| 1362 | ((and (memq context '(statement-end block-end)) | ||
| 1363 | (eq next-sexp-context 'ends-block)) | ||
| 1364 | (goto-char next-sexp-pos) | ||
| 1365 | (python-nav-end-of-block)) | ||
| 1366 | ((and (memq context '(statement-end block-end)) | ||
| 1367 | (eq next-sexp-context 'starts-block)) | ||
| 1368 | (goto-char next-sexp-pos) | ||
| 1369 | (python-nav-end-of-block)) | ||
| 1370 | ((memq context '(statement-end block-end)) | ||
| 1371 | (goto-char next-sexp-pos) | ||
| 1372 | (python-nav-end-of-statement)) | ||
| 1373 | (t (goto-char next-sexp-pos))) | ||
| 1374 | (cond ((and (not (bobp)) | ||
| 1375 | (python-info-current-line-empty-p)) | ||
| 1376 | (python-util-forward-comment dir) | ||
| 1377 | (python-nav--forward-sexp dir)) | ||
| 1378 | ((eq context 'block-end) | ||
| 1379 | (python-nav-beginning-of-block)) | ||
| 1380 | ((eq context 'statement-end) | ||
| 1381 | (python-nav-beginning-of-statement)) | ||
| 1382 | ((and (memq context '(statement-start block-start)) | ||
| 1383 | (eq next-sexp-context 'starts-block)) | ||
| 1384 | (goto-char next-sexp-pos) | ||
| 1385 | (python-nav-beginning-of-block)) | ||
| 1386 | ((and (memq context '(statement-start block-start)) | ||
| 1387 | (eq next-sexp-context 'ends-block)) | ||
| 1388 | (goto-char next-sexp-pos) | ||
| 1389 | (python-nav-beginning-of-block)) | ||
| 1390 | ((memq context '(statement-start block-start)) | ||
| 1391 | (goto-char next-sexp-pos) | ||
| 1392 | (python-nav-beginning-of-statement)) | ||
| 1393 | (t (goto-char next-sexp-pos)))))))))) | ||
| 1350 | 1394 | ||
| 1351 | (defun python-nav--backward-sexp () | 1395 | (defun python-nav--backward-sexp () |
| 1352 | "Move to backward sexp." | 1396 | "Move to backward sexp." |
| 1353 | (case (python-syntax-context-type) | 1397 | (python-nav--forward-sexp -1)) |
| 1354 | (string | ||
| 1355 | ;; Inside of a string, get out of it. | ||
| 1356 | (while (and (re-search-backward "[\"']" nil t) | ||
| 1357 | (python-syntax-context 'string)))) | ||
| 1358 | (comment | ||
| 1359 | ;; Inside of a comment, just move backward. | ||
| 1360 | (python-util-forward-comment -1)) | ||
| 1361 | (paren | ||
| 1362 | ;; Handle parens like we are lisp. | ||
| 1363 | (python-nav-lisp-forward-sexp-safe -1)) | ||
| 1364 | (t | ||
| 1365 | (let* ((block-starting-pos | ||
| 1366 | (save-excursion (python-nav-beginning-of-block))) | ||
| 1367 | (block-ending-pos | ||
| 1368 | (save-excursion (python-nav-end-of-block))) | ||
| 1369 | (prev-block-ending-pos | ||
| 1370 | (save-excursion (when (python-nav-backward-block) | ||
| 1371 | (python-nav-end-of-block)))) | ||
| 1372 | (prev-block-parent-ending-pos | ||
| 1373 | (save-excursion | ||
| 1374 | (when prev-block-ending-pos | ||
| 1375 | (goto-char prev-block-ending-pos) | ||
| 1376 | (python-util-forward-comment) | ||
| 1377 | (python-nav-beginning-of-block) | ||
| 1378 | (python-nav-end-of-block))))) | ||
| 1379 | (if (and (not (bobp)) | ||
| 1380 | (= (syntax-class (syntax-after (1- (point)))) 5)) | ||
| 1381 | ;; Char before point is a paren closing char, handle it | ||
| 1382 | ;; like we are lisp. | ||
| 1383 | (python-nav-lisp-forward-sexp-safe -1) | ||
| 1384 | (cond | ||
| 1385 | ((not block-ending-pos) | ||
| 1386 | ;; Not in and ending pos, move to end of previous block. | ||
| 1387 | (and (python-nav-backward-block) | ||
| 1388 | (python-nav-end-of-block))) | ||
| 1389 | ((= (point) block-ending-pos) | ||
| 1390 | ;; In ending pos, we need to search backwards for the | ||
| 1391 | ;; closest point looking the list of candidates from here. | ||
| 1392 | (let ((candidates)) | ||
| 1393 | (dolist (name | ||
| 1394 | '(prev-block-parent-ending-pos | ||
| 1395 | prev-block-ending-pos | ||
| 1396 | block-ending-pos | ||
| 1397 | block-starting-pos)) | ||
| 1398 | (when (and (symbol-value name) | ||
| 1399 | (< (symbol-value name) (point))) | ||
| 1400 | (add-to-list 'candidates (symbol-value name)))) | ||
| 1401 | (goto-char (apply 'max candidates)))) | ||
| 1402 | ((> (point) block-ending-pos) | ||
| 1403 | ;; After an ending position, move to it. | ||
| 1404 | (goto-char block-ending-pos)) | ||
| 1405 | ((= (point) block-starting-pos) | ||
| 1406 | ;; On a block starting position. | ||
| 1407 | (if (not (> (point) (or prev-block-ending-pos (point)))) | ||
| 1408 | ;; Point is after the end position of the block that | ||
| 1409 | ;; wraps the current one, just move a block backward. | ||
| 1410 | (python-nav-backward-block) | ||
| 1411 | ;; If we got here we are facing a case like this one: | ||
| 1412 | ;; | ||
| 1413 | ;; try: | ||
| 1414 | ;; return here() | ||
| 1415 | ;; except Exception as e: | ||
| 1416 | ;; | ||
| 1417 | ;; Where point is on the "except" and must move to the | ||
| 1418 | ;; end of "here()". | ||
| 1419 | (goto-char prev-block-ending-pos) | ||
| 1420 | (let ((parent-block-ending-pos | ||
| 1421 | (save-excursion | ||
| 1422 | (python-nav-forward-sexp) | ||
| 1423 | (and (not (looking-at (python-rx block-start))) | ||
| 1424 | (point))))) | ||
| 1425 | (when (and parent-block-ending-pos | ||
| 1426 | (> parent-block-ending-pos prev-block-ending-pos)) | ||
| 1427 | ;; If we got here we are facing a case like this one: | ||
| 1428 | ;; | ||
| 1429 | ;; except ImportError: | ||
| 1430 | ;; if predicate(): | ||
| 1431 | ;; processing() | ||
| 1432 | ;; here() | ||
| 1433 | ;; except AttributeError: | ||
| 1434 | ;; | ||
| 1435 | ;; Where point is on the "except" and must move to | ||
| 1436 | ;; the end of "here()". Without this extra step we'd | ||
| 1437 | ;; just get to the end of processing(). | ||
| 1438 | (goto-char parent-block-ending-pos))))) | ||
| 1439 | (t | ||
| 1440 | (if (and prev-block-ending-pos (< prev-block-ending-pos (point))) | ||
| 1441 | (goto-char prev-block-ending-pos) | ||
| 1442 | (python-nav-beginning-of-block))))))))) | ||
| 1443 | 1398 | ||
| 1444 | (defun python-nav-forward-sexp (&optional arg) | 1399 | (defun python-nav-forward-sexp (&optional arg) |
| 1445 | "Move forward across one block of code. | 1400 | "Move forward across one block of code. |
| @@ -2891,12 +2846,43 @@ parent defun name." | |||
| 2891 | ".") ".") | 2846 | ".") ".") |
| 2892 | name))))))) | 2847 | name))))))) |
| 2893 | 2848 | ||
| 2894 | (defsubst python-info-beginning-of-block-statement-p () | 2849 | (defun python-info-statement-starts-block-p () |
| 2895 | "Return non-nil if current statement opens a block." | 2850 | "Return non-nil if current statement opens a block." |
| 2896 | (save-excursion | 2851 | (save-excursion |
| 2897 | (python-nav-beginning-of-statement) | 2852 | (python-nav-beginning-of-statement) |
| 2898 | (looking-at (python-rx block-start)))) | 2853 | (looking-at (python-rx block-start)))) |
| 2899 | 2854 | ||
| 2855 | (defun python-info-statement-ends-block-p () | ||
| 2856 | "Return non-nil if point is at end of block." | ||
| 2857 | (let ((end-of-block-pos (save-excursion | ||
| 2858 | (python-nav-end-of-block))) | ||
| 2859 | (end-of-statement-pos (save-excursion | ||
| 2860 | (python-nav-end-of-statement)))) | ||
| 2861 | (and end-of-block-pos end-of-statement-pos | ||
| 2862 | (= end-of-block-pos end-of-statement-pos)))) | ||
| 2863 | |||
| 2864 | (defun python-info-beginning-of-statement-p () | ||
| 2865 | "Return non-nil if point is at beginning of statement." | ||
| 2866 | (= (point) (save-excursion | ||
| 2867 | (python-nav-beginning-of-statement) | ||
| 2868 | (point)))) | ||
| 2869 | |||
| 2870 | (defun python-info-end-of-statement-p () | ||
| 2871 | "Return non-nil if point is at end of statement." | ||
| 2872 | (= (point) (save-excursion | ||
| 2873 | (python-nav-end-of-statement) | ||
| 2874 | (point)))) | ||
| 2875 | |||
| 2876 | (defun python-info-beginning-of-block-p () | ||
| 2877 | "Return non-nil if point is at beginning of block." | ||
| 2878 | (and (python-info-beginning-of-statement-p) | ||
| 2879 | (python-info-statement-starts-block-p))) | ||
| 2880 | |||
| 2881 | (defun python-info-end-of-block-p () | ||
| 2882 | "Return non-nil if point is at end of block." | ||
| 2883 | (and (python-info-end-of-statement-p) | ||
| 2884 | (python-info-statement-ends-block-p))) | ||
| 2885 | |||
| 2900 | (defun python-info-closing-block () | 2886 | (defun python-info-closing-block () |
| 2901 | "Return the point of the block the current line closes." | 2887 | "Return the point of the block the current line closes." |
| 2902 | (let ((closing-word (save-excursion | 2888 | (let ((closing-word (save-excursion |