@@ -600,7 +600,7 @@ executable found in PATH.")
600
600
(goto-char end)))))))
601
601
602
602
(defun haskell-ident-at-point ()
603
- " Return the identifier under point, or nil if none found.
603
+ " Return the identifier near point going backward or nil if none found.
604
604
May return a qualified name."
605
605
(let ((reg (haskell-ident-pos-at-point)))
606
606
(when reg
@@ -618,44 +618,107 @@ May return a qualified name."
618
618
(cons start end)))))))
619
619
620
620
(defun haskell-ident-pos-at-point ()
621
- " Return the span of the identifier under point, or nil if none found.
622
- May return a qualified name."
623
- (save-excursion
624
- ; ; Skip whitespace if we're on it. That way, if we're at "map ", we'll
625
- ; ; see the word "map".
626
- (if (and (not (eobp ))
627
- (eq ? (char-syntax (char-after ))))
628
- (skip-chars-backward " \t " ))
629
-
630
- (let ((case-fold-search nil ))
631
- (cl-multiple-value-bind (start end)
632
- (list
633
- (progn (skip-syntax-backward " w_" ) (point ))
634
- (progn (skip-syntax-forward " w_" ) (point )))
635
- ; ; If we're looking at a module ID that qualifies further IDs, add
636
- ; ; those IDs.
637
- (goto-char start)
638
- (while (and (looking-at " [[:upper:]]" ) (eq (char-after end) ?. )
639
- ; ; It's a module ID that qualifies further IDs.
640
- (goto-char (1+ end))
641
- (save-excursion
642
- (when (not (zerop (skip-syntax-forward
643
- (if (looking-at " \\ s_" ) " _" " w'" ))))
644
- (setq end (point ))))))
645
- ; ; If we're looking at an ID that's itself qualified by previous
646
- ; ; module IDs, add those too.
647
- (goto-char start)
648
- (if (eq (char-after ) ?. ) (forward-char 1 )) ; Special case for "."
649
- (while (and (eq (char-before ) ?. )
650
- (progn (forward-char -1 )
651
- (not (zerop (skip-syntax-backward " w'" ))))
652
- (skip-syntax-forward " '" )
653
- (looking-at " [[:upper:]]" ))
654
- (setq start (point )))
655
- ; ; This is it.
656
- (unless (= start end)
621
+ " Return the span of the identifier near point going backward.
622
+ Returns nil if no identifier found or point is inside string or
623
+ comment. May return a qualified name."
624
+ (when (not (nth 8 (syntax-ppss )))
625
+ ; ; Do not handle comments and strings
626
+ (let (start end)
627
+ ; ; Initial point position is non-deterministic, it may occur anywhere
628
+ ; ; inside identifier span, so the approach is:
629
+ ; ; - first try go left and find left boundary
630
+ ; ; - then try go right and find right boundary
631
+ ; ;
632
+ ; ; In both cases assume the longest path, e.g. when going left take into
633
+ ; ; account than point may occur at the end of identifier, when going right
634
+ ; ; take into account that point may occur at the beginning of identifier.
635
+ ; ;
636
+ ; ; We should handle `.` character very careful because it is heavily
637
+ ; ; overloaded. Examples of possible cases:
638
+ ; ; Control.Monad.>>= -- delimiter
639
+ ; ; Control.Monad.when -- delimiter
640
+ ; ; Data.Aeson..: -- delimiter and operator symbol
641
+ ; ; concat.map -- composition function
642
+ ; ; .? -- operator symbol
643
+ (save-excursion
644
+ ; ; First, skip whitespace if we're on it, moving point to last
645
+ ; ; identifier char. That way, if we're at "map ", we'll see the word
646
+ ; ; "map".
647
+ (when (and (looking-at (rx eol))
648
+ (not (bolp )))
649
+ (backward-char ))
650
+ (when (and (not (eobp ))
651
+ (eq (char-syntax (char-after )) ? ))
652
+ (skip-chars-backward " \t " )
653
+ (backward-char ))
654
+ ; ; Now let's try to go left.
655
+ (save-excursion
656
+ (if (not (haskell-mode--looking-at-varsym))
657
+ ; ; Looking at non-operator char, this is quite simple
658
+ (progn
659
+ (skip-syntax-backward " w_" )
660
+ ; ; Remember position
661
+ (setq start (point )))
662
+ ; ; Looking at operator char.
663
+ (while (and (not (bobp ))
664
+ (haskell-mode--looking-at-varsym))
665
+ ; ; skip all operator chars backward
666
+ (setq start (point ))
667
+ (backward-char ))
668
+ ; ; Extra check for case when reached beginning of the buffer.
669
+ (when (haskell-mode--looking-at-varsym)
670
+ (setq start (point ))))
671
+ ; ; Slurp qualification part if present. If identifier is qualified in
672
+ ; ; case of non-operator point will stop before `.` dot, but in case of
673
+ ; ; operator it will stand at `.` delimiting dot. So if we're looking
674
+ ; ; at `.` let's step one char forward and try to get qualification
675
+ ; ; part.
676
+ (goto-char start)
677
+ (when (looking-at-p (rx " ." ))
678
+ (forward-char ))
679
+ (let ((pos (haskell-mode--skip-qualification-backward)))
680
+ (when pos
681
+ (setq start pos))))
682
+ ; ; Finally, let's try to go right.
683
+ (save-excursion
684
+ ; ; Try to slurp qualification part first.
685
+ (skip-syntax-forward " w_" )
686
+ (setq end (point ))
687
+ (while (and (looking-at (rx " ." upper))
688
+ (not (zerop (progn (forward-char )
689
+ (skip-syntax-forward " w_" )))))
690
+ (setq end (point )))
691
+ ; ; If point was at non-operator we already done, otherwise we need an
692
+ ; ; extra check.
693
+ (while (haskell-mode--looking-at-varsym)
694
+ (forward-char )
695
+ (setq end (point ))))
696
+ (when (not (= start end))
657
697
(cons start end))))))
658
698
699
+ (defun haskell-mode--looking-at-varsym ()
700
+ " Return t when point stands at operator symbol."
701
+ (when (not (eobp ))
702
+ (let ((lex (haskell-lexeme-classify-by-first-char (char-after ))))
703
+ (or (eq lex 'varsym )
704
+ (eq lex 'consym )))))
705
+
706
+ (defun haskell-mode--skip-qualification-backward ()
707
+ " Skip qualified part of identifier backward.
708
+ Expects point stands *after* delimiting dot.
709
+ Returns beginning position of qualified part or nil if no qualified part found."
710
+ (when (not (and (bobp )
711
+ (looking-at (rx bol))))
712
+ (let ((case-fold-search nil )
713
+ pos)
714
+ (while (and (eq (char-before ) ?. )
715
+ (progn (backward-char )
716
+ (not (zerop (skip-syntax-backward " w'" ))))
717
+ (skip-syntax-forward " '" )
718
+ (looking-at " [[:upper:]]" ))
719
+ (setq pos (point )))
720
+ pos)))
721
+
659
722
(defun haskell-delete-indentation (&optional arg )
660
723
" Like `delete-indentation' but ignoring Bird-style \" >\" ."
661
724
(interactive " *P" )
0 commit comments