Source

ess / lisp / ess-mode.el

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
;;; ess-mode.el --- Support for editing ESS source code

;; Copyright (C) 1989-1994 Doug Bates, Ed Kademan, Frank Ritter, David Smith.
;; Copyright (C) 1997--2004 A.J. Rossini, Rich M. Heiberger, Martin
;;	Maechler, Kurt Hornik, Rodney Sparapani, and Stephen Eglen.

;; Original Author: David Smith <dsmith@stats.adelaide.edu.au>
;; Created: 7 Jan 1994
;; Maintainers: ESS-core <ESS-core@stat.math.ethz.ch>

;; This file is part of ESS

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; This file 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 General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

;;; Commentary:

;; Code for editing ESS source code.

;;; Code:

 ; Requires and autoloads

(require 'ess)

;;; AJR: THIS IS GROSS AND DISGUSTING (but I wrote it).
;;; MM:  and I had to add all other 'ess-eval-*** ...
;;; >>> why not just do the obvious instead of all these ? Namely,
;;; (require 'ess-inf)
;;; ------------------ ?
;;; AJR: The reason is that we ONLY need to load ess-inf for the
;;; functions which are interactive in nature.   We don't want to load
;;; it when we are only editing.

(autoload 'ess-mode-minibuffer-map	"ess-inf" "" nil 'keymap)
(autoload 'ess-read-object-name		"ess-inf" "" nil)
(autoload 'ess-list-object-completions	"ess-inf" "" nil)

(autoload 'ess-eval-linewise		"ess-inf" "" nil)
(autoload 'ess-eval-paragraph		"ess-inf" "" nil)
(autoload 'ess-eval-region		"ess-inf" "" nil)
(autoload 'ess-eval-buffer		"ess-inf" "" nil)
(autoload 'ess-eval-function		"ess-inf" "" nil)
(autoload 'ess-eval-line		"ess-inf" "" nil)
(autoload 'ess-eval-line-and-step	"ess-inf" "" nil)
(autoload 'ess-eval-region-and-go	"ess-inf" "" nil)
(autoload 'ess-eval-buffer-and-go	"ess-inf" "" nil)
(autoload 'ess-eval-function-and-go	"ess-inf" "" nil)
(autoload 'ess-eval-line-and-go		"ess-inf" "" nil)

(autoload 'ess-load-file		"ess-inf" "" nil)
(autoload 'ess-switch-process		"ess-inf" "" nil)
(autoload 'ess-switch-to-ESS		"ess-inf" "" nil)
(autoload 'ess-request-a-process	"ess-inf" "" nil)
(autoload 'get-ess-process		"ess-inf" "" nil)
(autoload 'ess-command			"ess-inf" "" nil)
(autoload 'ess-create-temp-buffer	"ess-inf" "" nil)
(autoload 'ess-display-temp-buffer	"ess-inf" "" nil)
(autoload 'ess-force-buffer-current	"ess-inf" "" nil)
(autoload 'ess-make-buffer-current	"ess-inf" "" nil)
(autoload 'ess-modtime-gt		"ess-inf" "" nil)
(autoload 'ess-object-modtime		"ess-inf" "" nil)
(autoload 'ess-quit			"ess-inf" "" nil)


(defun ess-line-end-position (&optional N)
  (save-excursion
    (end-of-line N)
    (point)))


 ; ESS mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; In this section:
;;;;
;;;; * The major mode ess-mode
;;;; * Commands for ess-mode
;;;; * Code evaluation commands
;;;; * Indenting code and commands
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;*;; Major mode definition

(if ess-eval-map
    nil
  (if (featurep 'xemacs)
      ;; Code for XEmacs
      (setq ess-eval-map (make-keymap))
    ;; else code for GNU Emacs
    (setq ess-eval-map (make-sparse-keymap)))

  (define-key ess-eval-map "\C-r"    'ess-eval-region)
  (define-key ess-eval-map "\M-r"    'ess-eval-region-and-go)
  (define-key ess-eval-map "\C-b"    'ess-eval-buffer)
  (define-key ess-eval-map "\M-b"    'ess-eval-buffer-and-go)
  (define-key ess-eval-map "\C-f"    'ess-eval-function)
  (define-key ess-eval-map "\M-f"    'ess-eval-function-and-go)
  (define-key ess-eval-map "\C-x"    'ess-eval-function)
  (define-key ess-eval-map "\C-n"    'ess-eval-line-and-step)
  (define-key ess-eval-map "\C-j"    'ess-eval-line)
  (define-key ess-eval-map "\M-j"    'ess-eval-line-and-go))


(if ess-mode-map
    nil

  (if (featurep 'xemacs)
      (progn ;; Code for XEmacs
	(setq ess-mode-map (make-keymap))
	(set-keymap-parent ess-mode-map text-mode-map)) ;; was comint?!?
    ;; else code for GNU Emacs
    (setq ess-mode-map (make-sparse-keymap)))

  ;; By popular demand:
  (define-key ess-mode-map "\C-m"	'newline-and-indent); = [RETURN]

  (define-key ess-mode-map "\C-c\C-r"	'ess-eval-region)
  (define-key ess-mode-map "\C-c\M-r"	'ess-eval-region-and-go)
  (define-key ess-mode-map "\C-c\C-b"	'ess-eval-buffer)
  (define-key ess-mode-map "\C-c\M-b"	'ess-eval-buffer-and-go)
  (define-key ess-mode-map "\C-c\C-f"	'ess-eval-function)
  (define-key ess-mode-map "\C-c\M-f"	'ess-eval-function-and-go)
  (define-key ess-mode-map "\M-\C-x"	'ess-eval-function)
  (define-key ess-mode-map "\C-c\C-n"	'ess-eval-line-and-step)
  (define-key ess-mode-map "\C-c\C-j"	'ess-eval-line)
  (define-key ess-mode-map "\C-c\M-j"	'ess-eval-line-and-go)
  (define-key ess-mode-map "\M-\C-a"	'ess-beginning-of-function)
  (define-key ess-mode-map "\M-\C-e"	'ess-end-of-function)
  (define-key ess-mode-map "\C-xnd"	'ess-narrow-to-defun)
  (define-key ess-mode-map "\C-c\C-y"	'ess-switch-to-ESS)
  (define-key ess-mode-map "\C-c\C-z"	'ess-switch-to-end-of-ESS)
  (define-key ess-mode-map "\C-c\C-l"	'ess-load-file)
  (define-key ess-mode-map "\C-c\C-v"	'ess-display-help-on-object)
  (define-key ess-mode-map "\C-c\C-d"	'ess-dump-object-into-edit-buffer)
;(define-key ess-mode-map "\C-c5\C-d"'ess-dump-object-into-edit-buffer-other-frame)
  (define-key ess-mode-map "\C-c\C-s"	'ess-switch-process) ; use a
					; different process for the buffer.
  (define-key ess-mode-map "\C-c\C-t"	'ess-execute-in-tb)
  (define-key ess-mode-map "\C-c\t"	'ess-complete-object-name)
  (define-key ess-mode-map "\M-\t"	'comint-replace-by-expanded-filename)
  (define-key ess-mode-map "\M-?"	'ess-list-object-completions)
  ;; wrong here (define-key ess-mode-map "\C-c\C-k" 'ess-request-a-process)
  (define-key ess-mode-map "\C-c\C-k"	'ess-force-buffer-current)
  (define-key ess-mode-map "\C-c`"	'ess-parse-errors) ; \C-x reserved!
  (define-key ess-mode-map "{"		'ess-electric-brace)
  (define-key ess-mode-map "}"		'ess-electric-brace)
  (define-key ess-mode-map "\e\C-h"	'ess-mark-function)
  (define-key ess-mode-map "\e\C-q"	'ess-indent-exp)
  (define-key ess-mode-map "\177"	'backward-delete-char-untabify)
  (define-key ess-mode-map "\t"		'ess-indent-command)
  (define-key ess-mode-map "\C-c\C-q"	'ess-quit)
  (define-key ess-mode-map "\C-c\C-e"	ess-eval-map))

(require 'noweb-mode)
(easy-menu-define
 ess-mode-menu ess-mode-map
 "Menu for use in `ess-mode'."
 '("ESS" ; ESS-mode
   ["What is this? (beta)"    ess-mouse-me                      t]
   ["Load file"	 ess-load-file t]
   ("Eval and Go"
    ["Eval buffer"	ess-eval-buffer-and-go		  t]
    ["Eval region"	ess-eval-region-and-go		  t]
    ["Eval function"	ess-eval-function-and-go	  t]
    ["Eval line"	ess-eval-line-and-go		  t]
    ["Eval chunk"	ess-eval-chunk-and-go	 noweb-mode]
    ["Eval thread"	ess-eval-thread-and-go	 noweb-mode]
    ["About"		(ess-goto-info "Evaluating code") t]
    )
   ("ESS Eval"
    ["Eval buffer"	ess-eval-buffer			  t]
    ["Eval region"	ess-eval-region			  t]
    ["Eval function"	ess-eval-function		  t]
    ["Enter expression" ess-execute-in-tb		  t]
    ["Eval line"	ess-eval-line			  t]
    ["Eval line & step" ess-eval-line-and-step		  t]
    ["Eval chunk"	ess-eval-chunk		 noweb-mode]
    ["Eval thread"	ess-eval-thread		 noweb-mode]
    ["About"		(ess-goto-info "Evaluating code") t]
    )
   ("Motion..."
    ["Edit new object"		ess-dump-object-into-edit-buffer t]
    ["Goto end of ESS buffer"	ess-switch-to-end-of-ESS	t]
    ["Switch to ESS buffer"	ess-switch-to-ESS		t]
    ["Beginning of function"	ess-beginning-of-function	t]
    ["End of function"		ess-end-of-function		t]
    )
   ("ESS list..."
    ["Backward list"		backward-list			t]
    ["Forward list"		forward-list			t]
    ["Next parenthesis"		down-list			t]
    ["Enclosing parenthesis"	backward-up-list		t]
    ["Backward sexp"		backward-sexp			t]
    ["Forward sexp"		forward-sexp			t]
    ["About"			(Info-goto-node "(Emacs)Lists") t]
    )
   ("ESS Edit"
    ["Complete Filename" comint-replace-by-expanded-filename	t]
    ["Complete Object"	 ess-complete-object-name		t]
    ["Kill sexp"	 kill-sexp				t]
    ["Mark function"	 ess-mark-function			t]
    ["Indent expression" ess-indent-exp				t]
    ["Indent line"	 ess-indent-command			t]
    ["Undo"		 undo					t]
    ["About"		 (ess-goto-info "Edit buffer")		t]
    )
   ("Start Process"
    ;; SJE - :help not yet recognised in XEmacs.
    ["R"     R   t] ;; :help "Start a new R process" :active t
    ["S"     S   t] ;; :help "Start a new S process" :active t
    ["Sqpe" Sqpe ess-microsoft-p] ;; :help "Start a new Sqpe process" :active t
    ["S+6-exisiting" S+6-existing ess-microsoft-p] ;; :help "Access an existing S process" :active t
    ["SAS"   SAS-menu t] ;;  :help "Start a new SAS process" :active t
    ;; The following menu item "Other" is a place-holder that will
    ;; be replaced with the other versions of R and Sqpe that can be run.
    ;; See `ess-r-versions-create' and ess-site.el
    ("Other"
     ["No other R or Sqpe versions" nil nil])
    ["About" 
     (ess-goto-info "Starting up") t]
     ;; :help "Read about starting a new ESS process" :active t]
    )
   ["Switch Process"	ess-switch-process		t]
   "------"
   ["Describe"		describe-mode			t]
   ["About editing" (ess-goto-info "Editing")	t]
   ["Read ESS info" (ess-goto-info "") t]
   ["Send bug report"	ess-submit-bug-report		t]
   ))

(defun SAS-menu ()
  "Start SAS from the menu."
  (interactive)
  (if ess-microsoft-p
      ;; replace with other choices for starting SAS under XEmacs?
      (error "SAS cannot be started this way in ESS on Windows.")
    (SAS)))

(defun ess-mode-xemacs-menu ()
  "Hook to install `ess-mode' menu for XEmacs (w/ easymenu)."
  (if 'ess-mode
        (easy-menu-add ess-mode-menu)
    (easy-menu-remove ess-mode-menu)))

(if (featurep 'xemacs)
    (add-hook 'ess-mode-hook 'ess-mode-xemacs-menu))

(defun ess-mode (&optional alist proc-name)
  "Major mode for editing ESS source.
Optional arg ALIST describes how to customize the editing mode.
Optional arg PROC-NAME is name of associated inferior process.

\\{ess-mode-map}

Extra binding to note:  'ESC C-\\' indent-region.

Entry to this mode runs the hooks in ess-mode-hook.

You can send text to the inferior ESS process from other buffers containing
ESS source.
    `ess-eval-region' sends the current region to the ESS process.
    `ess-eval-buffer' sends the current buffer to the ESS process.
    `ess-eval-function' sends the current function to the ESS process.
    `ess-eval-line' sends the current line to the ESS process.
    `ess-beginning-of-function' and `ess-end-of-function' move the point to
        the beginning and end of the current ESS function.
    `ess-switch-to-ESS' switches the current buffer to the ESS process buffer.
    `ess-switch-to-end-of-ESS' switches the current buffer to the ESS process
        buffer and puts point at the end of it.

    `ess-eval-region-and-go', `ess-eval-buffer-and-go',
        `ess-eval-function-and-go', and `ess-eval-line-and-go' switch to the S
        process buffer after sending their text.

    `ess-load-file' sources a file of commands to the ESS process.

\\[ess-indent-command] indents for ESS code.
\\[backward-delete-char-untabify] converts tabs to spaces as it moves back.
Comments are indented in a similar way to Emacs-lisp mode:
       `###'     beginning of line
       `##'      the same level of indentation as the code
       `#'       the same column on the right, or to the right of such a
                 column if that is not possible.(default value 40).
                 \\[indent-for-comment] command automatically inserts such a
                 `#' in the right place, or aligns such a comment if it is
                 already inserted.
\\[ess-indent-exp] command indents each line of the ESS grouping following point.

Variables controlling indentation style:
 `ess-tab-always-indent'
    Non-nil means TAB in ESS mode should always reindent the current line,
    regardless of where in the line point is when the TAB command is used.
 `ess-auto-newline'
    Non-nil means automatically newline before and after braces inserted in S
    code.
 `ess-indent-level'
    Indentation of ESS statements within surrounding block.
    The surrounding block's indentation is the indentation of the line on
    which the open-brace appears.
 `ess-continued-statement-offset'
    Extra indentation given to a substatement, such as the then-clause of an
    if or body of a while.
 `ess-continued-brace-offset'
    Extra indentation given to a brace that starts a substatement.
    This is in addition to ess-continued-statement-offset.
 `ess-brace-offset'
    Extra indentation for line if it starts with an open brace.
 `ess-arg-function-offset'
    Extra indent for internal substatements of function `foo' that called
    in `arg=foo(...)' form.
   If not number, the statements are indented at open-parenthesis following
   `foo'.
 `ess-expression-offset'
    Extra indent for internal substatements of `expression' that specified
    in `obj <- expression(...)' form.
    If not number, the statements are indented at open-parenthesis following
    `expression'.
 `ess-brace-imaginary-offset'
    An open brace following other text is treated as if it were
    this far to the right of the start of its line.
 `ess-else-offset'
    Extra indentation for line if it starts with `else'.
 `ess-close-brace-offset'
    Extra indentation for closing braces.
 `ess-fancy-comments'
    Non-nil means distinguish between #, ##, and ### for indentation.

Furthermore, \\[ess-set-style] command enables you to set up predefined ess-mode
indentation style. At present, predefined style are `BSD', `GNU', `K&R', `C++',
`CLB' (quoted from C language style)."
  (interactive)
  (kill-all-local-variables) ;; NOTICE THIS!
  (ess-setq-vars-local alist)
  (ess-write-to-dribble-buffer
   (format "(ess-mode-1): ess-language=%s, ess-dialect=%s buf=%s \n"
	   ess-language
	   ess-dialect
	   (current-buffer)))
  ;; (ess-write-to-dribble-buffer
  ;;  (format "(ess-mode-1.2): ess-process= %s \n"
  ;;   (ess-local-process-name ess-local-process-name "none")))
  (ess-write-to-dribble-buffer
   (format "(ess-mode-1.5): alist=%s \n"
	   alist))
  (setq major-mode 'ess-mode)
  (setq mode-name (concat "ESS[" ess-language "]")) ; was ess-dialect
  ;; The following line does the next 20 or so :-).
  (ess-write-to-dribble-buffer
   (format "(ess-mode-1.6): editing-alist=%s \n"
	   ess-mode-editing-alist))
  (ess-setq-vars-local ess-mode-editing-alist)

  (use-local-map ess-mode-map)
  (set-syntax-table ess-mode-syntax-table)

  ;; Keep <tabs> out of the code.
  (make-local-variable 'indent-tabs-mode)
  (setq indent-tabs-mode nil)

  ;;  (make-local-variable 'paragraph-start)
  ;;  (setq paragraph-start (concat "^$\\|" page-delimiter))
  ;;  (make-local-variable 'paragraph-separate)
  ;;  (setq paragraph-separate paragraph-start)
  ;;  (make-local-variable 'paragraph-ignore-fill-prefix)
  ;;  (setq paragraph-ignore-fill-prefix t)
  ;;  (make-local-variable 'indent-line-function)
  ;;  (setq indent-line-function 'ess-indent-line)
  ;;  (make-local-variable 'require-final-newline)
  ;;  (setq require-final-newline t)
  ;;  (make-local-variable 'comment-start)
  ;;  (setq comment-start "#")
  ;;  (make-local-variable 'comment-start-skip)
  ;;  (setq comment-start-skip "#+ *")
  ;;  (make-local-variable 'comment-column)
  ;;  (setq comment-column 40)
  ;;  (make-local-variable 'comment-indent-function)
  ;;  (setq comment-indent-function 'ess-comment-indent)
  ;;  (make-local-variable 'parse-sexp-ignore-comments)
  ;;  (setq parse-sexp-ignore-comments t)
  ;;  (ess-set-style ess-default-style)
  ;;  (make-local-variable 'ess-local-process-name)
  ;;  (make-local-variable 'ess-keep-dump-files)
  (put 'ess-local-process-name 'permanent-local t) ; protect from RCS
  (setq mode-line-process ;; AJR: in future, XEmacs will use modeline-process.
	'(" [" (ess-local-process-name ess-local-process-name "none") "]"))

  (ess-load-object-name-db-file)
  (run-hooks 'ess-mode-hook)
  (ess-write-to-dribble-buffer "\nFinished setting up ESS-mode.\n"))

;;*;; User commands in ess-mode

;;;*;;; Handy commands

(defun ess-execute-in-tb ()
  "Like `ess-execute', but always evaluates in temp buffer."
  (interactive)
  (let ((ess-execute-in-process-buffer nil))
    (call-interactively 'ess-execute)))

;;;*;;; Buffer motion/manipulation commands

(defun ess-beginning-of-function ()
  "Leave (and return) the point at the beginning of the current ESS function."
  (interactive)
  (let ((init-point (point))
 	beg end done)
    ;;DBG (ess-write-to-dribble-buffer "ess-BEG-of-fun:")
    ;; in case we're sitting in a function header:
    (if (search-forward "(" (ess-line-end-position 2) t); at most end of next line
	(forward-char 1))
    (while (not done)
      (if (re-search-backward ess-function-pattern (point-min) t)
 	  nil
 	(goto-char init-point)
 	(error "Point is not in a function."))

      (setq beg (point))
      ;;DBG (ess-write-to-dribble-buffer
      ;;DBG (format "Match,Pt:(%d,%d),%d" (match-beginning 0)(match-end 0) beg))
      (forward-list 1)			; get over arguments
      ;;DBG (ess-write-to-dribble-buffer ":")
      ;; The following used to bomb  "Unbalanced parentheses", n1, n2
      ;; when the above (search-forward "(" ..) wasn't delimited :
      (forward-sexp 1)			; move over braces
      ;;DBG (ess-write-to-dribble-buffer "|")
      (setq end (point))
      (goto-char beg)
      ;; current function must begin and end around point
      (setq done (and (>= end init-point) (<= beg init-point))))
    ;;DBG (ess-write-to-dribble-buffer (format "found beg=%d\n" beg))
    beg))

(defun ess-end-of-function (&optional beginning)
  "Leave the point at the end of the current ESS function.
Optional argument for location of beginning.  Return '(beg end)."
  (interactive)
  (if beginning
      (goto-char beginning)
    (setq beginning (ess-beginning-of-function)))
  (forward-list 1)			; get over arguments
  (forward-sexp 1)			; move over braces
  ;;DBG (ess-write-to-dribble-buffer "ess-END-of-fun: found ok\n")
  (list beginning (point))
  )

;;; Kurt's version, suggested 970306.
(defun ess-mark-function ()
  "Put mark at end of ESS function, point at beginning."
  (interactive)
  (let ((beg (ess-beginning-of-function)))
    (push-mark (point))
    (ess-end-of-function beg)
    (exchange-point-and-mark)))

;; Donated by Stephen Eglen, 2001-08-29:
;; This command is analogous to `narrow-to-defun' (elisp)
;; and `py-narrow-to-defun' (python)."
(defun ess-narrow-to-defun ()
  "Make text outside current function invisible.
If text is already narrowed, this is removed before narrowing to the
current function."
  (interactive)
   ;; if point is not in a function, ess-end-of-function catches the error.
  (save-excursion
    (widen)
    (let* ((beg-end (ess-end-of-function)))
      (narrow-to-region (nth 0 beg-end) (nth 1 beg-end)))))

;;*;; Loading files

(defun ess-check-modifications nil
  "Check whether loading this file would overwrite some ESS objects
which have been modified more recently than this file, and confirm
if this is the case."
  ;; FIXME: this should really cycle through all top-level assignments in
  ;; the buffer
  (and (buffer-file-name) ess-filenames-map
       (let ((sourcemod (nth 5 (file-attributes (buffer-file-name))))
	     (objname))
	 (save-excursion
	   (goto-char (point-min))
	   ;; Get name of assigned object, if we can find it
	   (setq objname
		 (and
		  (re-search-forward
		   "^\\s *\"?\\(\\(\\sw\\|\\s_\\)+\\)\"?\\s *[<_]"
		   nil
		   t)
		  (buffer-substring (match-beginning 1)
				    (match-end 1)))))
	 (and
	  sourcemod			; the file may have been deleted
	  objname			; may not have been able to
					; find name
	  (ess-modtime-gt (ess-object-modtime objname) sourcemod)
	  (not (y-or-n-p

		(format
		 "The ESS object %s is newer than this file. Continue?"
		 objname)))
	  (error "Aborted")))))

(defun ess-check-source (fname)
  "If file FNAME has an unsaved buffer, offer to save it.
Returns t if the buffer existed and was modified, but was not saved."
  (let ((buff (get-file-buffer fname)))
    ;; RMH: Corrections noted below are needed for C-c C-l to work
    ;; correctly when issued from *S* buffer.
    ;; The following barfs since
    ;; 1. `if' does not accept a buffer argument, `not' does.
    ;; 2. (buffer-file-name) is not necessarily defined for *S*
    ;;(if buff
    ;; (let ((deleted (not (file-exists-p (buffer-file-name)))))
    ;; Next 2 lines are RMH's solution:
    (if (not(not buff))
	(let ((deleted (not (file-exists-p fname))))
	  (if (and deleted (not (buffer-modified-p buff)))
	      ;; Buffer has been silently deleted, so silently save
	      (save-excursion
		(set-buffer buff)
		(set-buffer-modified-p t)
		(save-buffer))
	    (if (and (buffer-modified-p buff)
		     (or ess-mode-silently-save
			 (y-or-n-p
			  (format "Save buffer %s first? "
				  (buffer-name buff)))))
		(save-excursion
		  (set-buffer buff)
		  (save-buffer))))
	  (buffer-modified-p buff)))))

(defun ess-parse-errors (showerr)
  "Jump to error in last loaded ESS source file.
With prefix argument, only shows the errors ESS reported."
  (interactive "P")
  (ess-make-buffer-current)
  (let ((errbuff (get-buffer ess-error-buffer-name)))
    (if (not errbuff)
	(error "You need to do a load first!")
      (set-buffer errbuff)
      (goto-char (point-max))
      (if
	  (re-search-backward
	   "^\\(Syntax error: .*\\) at line \\([0-9]*\\), file \\(.*\\)$"
	   nil
	   t)
	  (let* ((filename (buffer-substring (match-beginning 3) (match-end 3)))
		 (fbuffer (get-file-buffer filename))
		 (linenum (string-to-int (buffer-substring (match-beginning 2) (match-end 2))))
		 (errmess (buffer-substring (match-beginning 1) (match-end 1))))
	    (if showerr
		  (ess-display-temp-buffer errbuff)
	      (if fbuffer nil
		(setq fbuffer (find-file-noselect filename))
		(save-excursion
		  (set-buffer fbuffer)
		  (ess-mode)))
	      (pop-to-buffer fbuffer)
	      (goto-line linenum))
	    (princ errmess t))
	(message "Not a syntax error.")
	(ess-display-temp-buffer errbuff)))))

;;*;; ESS code formatting/indentation

;;;*;;; User commands

(defun ess-electric-brace (arg)
  "Insert character and correct line's indentation."
  (interactive "P")
  (let (insertpos)
    (if (and (not arg)
	     (eolp)
	     (or (save-excursion
		   (skip-chars-backward " \t")
		   (bolp))
		 (if ess-auto-newline (progn (ess-indent-line) (newline) t) nil)))
	(progn
	  (insert last-command-char)
	  (ess-indent-line)
	  (if ess-auto-newline
	      (progn
		(newline)
		;; (newline) may have done auto-fill
		(setq insertpos (- (point) 2))
		(ess-indent-line)))
	  (save-excursion
	    (if insertpos (goto-char (1+ insertpos)))
	    (delete-char -1))))
    (if insertpos
	(save-excursion
	  (goto-char insertpos)
	  (self-insert-command (prefix-numeric-value arg)))
      (self-insert-command (prefix-numeric-value arg)))))

(defun ess-indent-command (&optional whole-exp)
  "Indent current line as ESS code, or in some cases insert a tab character.
If `ess-tab-always-indent' is non-nil (the default), always indent
current line.  Otherwise, indent the current line only if point is at
the left margin or in the line's indentation; otherwise insert a tab.
A numeric argument, regardless of its value, means indent rigidly all
the lines of the expression starting after point so that this line
becomes properly indented.  The relative indentation among the lines
of the expression are preserved."
  (interactive "P")
  (if whole-exp
      ;; If arg, always indent this line as S
      ;; and shift remaining lines of expression the same amount.
      (let ((shift-amt (ess-indent-line))
	    beg end)
	(save-excursion
	  (if ess-tab-always-indent
	      (beginning-of-line))
	  (setq beg (point))
	  (backward-up-list 1)
	  (forward-list 1)
	  (setq end (point))
	  (goto-char beg)
	  (forward-line 1)
	  (setq beg (point)))
	(if (> end beg)
	    (indent-code-rigidly beg end shift-amt)))
    (if (and (not ess-tab-always-indent)
	     (save-excursion
	       (skip-chars-backward " \t")
	       (not (bolp))))
	(insert-tab)
      (ess-indent-line))))

(defun ess-indent-exp ()
  "Indent each line of the ESS grouping following point."
  (interactive)
  (let ((indent-stack (list nil))
	(contain-stack (list (point)))
	(case-fold-search nil)
	;; restart
	outer-loop-done	innerloop-done state ostate
	this-indent
	last-sexp
	last-depth
	at-else at-brace
	(opoint (point))
	(next-depth 0))
    (save-excursion
      (forward-sexp 1))
    (save-excursion
      (setq outer-loop-done nil)
      (while (and (not (eobp)) (not outer-loop-done))
	(setq last-depth next-depth)
	;; Compute how depth changes over this line
	;; plus enough other lines to get to one that
	;; does not end inside a comment or string.
	;; Meanwhile, do appropriate indentation on comment lines.
	(setq innerloop-done nil)
	(while (and (not innerloop-done)
		    (not (and (eobp) (setq outer-loop-done t))))
	  (setq ostate state)
	  (setq state (parse-partial-sexp (point) (progn (end-of-line) (point))
					  nil nil state))
	  (setq next-depth (car state))
	  (if (and (car (cdr (cdr state)))
		   (>= (car (cdr (cdr state))) 0))
	      (setq last-sexp (car (cdr (cdr state)))))
	  (if (or (nth 4 ostate))
	      (ess-indent-line))
	  (if (nth 4 state)
	      (and (ess-indent-line)
		   (setcar (nthcdr 4 state) nil)))
	  (if (or (nth 3 state))
	      (forward-line 1)
	    (setq innerloop-done t)))
	(if (<= next-depth 0)
	    (setq outer-loop-done t))
	(if outer-loop-done
	    nil
	  ;; If this line had ..))) (((.. in it, pop out of the levels
	  ;; that ended anywhere in this line, even if the final depth
	  ;; doesn't indicate that they ended.
	  (while (> last-depth (nth 6 state))
	    (setq indent-stack (cdr indent-stack)
		  contain-stack (cdr contain-stack)
		  last-depth (1- last-depth)))
	  (if (/= last-depth next-depth)
	      (setq last-sexp nil))
	  ;; Add levels for any parens that were started in this line.
	  (while (< last-depth next-depth)
	    (setq indent-stack (cons nil indent-stack)
		  contain-stack (cons nil contain-stack)
		  last-depth (1+ last-depth)))
	  (if (null (car contain-stack))
	      (setcar contain-stack (or (car (cdr state))
					(save-excursion (forward-sexp -1)
							(point)))))
	  (forward-line 1)
	  (skip-chars-forward " \t")
	  (if (eolp)
	      nil
	    (if (and (car indent-stack)
		     (>= (car indent-stack) 0))
		;; Line is on an existing nesting level.
		;; Lines inside parens are handled specially.
		(if (/= (char-after (car contain-stack)) ?{)
		    (setq this-indent (car indent-stack))
		  ;; Line is at statement level.
		  ;; Is it a new statement?  Is it an else?
		  ;; Find last non-comment character before this line
		  (save-excursion
		    (setq at-else (looking-at "else\\W"))
		    (setq at-brace (= (following-char) ?{))
		    (ess-backward-to-noncomment opoint)
		    (if (ess-continued-statement-p)
			;; Preceding line did not end in comma or semi;
			;; indent this line  ess-continued-statement-offset
			;; more than previous.
			(progn
			  (ess-backward-to-start-of-continued-exp (car contain-stack))
			  (setq this-indent
				(+ ess-continued-statement-offset (current-column)
				   (if at-brace ess-continued-brace-offset 0))))
		      ;; Preceding line ended in comma or semi;
		      ;; use the standard indent for this level.
		      (if at-else
			  (progn (ess-backward-to-start-of-if opoint)
				 (setq this-indent (+ ess-else-offset
						      (current-indentation))))
			(setq this-indent (car indent-stack))))))
	      ;; Just started a new nesting level.
	      ;; Compute the standard indent for this level.
	      (let ((val (ess-calculate-indent
			   (if (car indent-stack)
			       (- (car indent-stack))))))
		(setcar indent-stack
			(setq this-indent val))))
	    ;; Adjust line indentation according to its contents
	    (if (= (following-char) ?})
		;;(setq this-indent (- this-indent ess-indent-level)))
 		(setq this-indent (+ this-indent
 				     (- ess-close-brace-offset ess-indent-level))))
	    (if (= (following-char) ?{)
		(setq this-indent (+ this-indent ess-brace-offset)))
	    ;; Put chosen indentation into effect.
	    (or (= (current-column) this-indent)
		(= (following-char) ?\#)
		(progn
		  (delete-region (point) (progn (beginning-of-line) (point)))
		  (indent-to this-indent)))
	    ;; Indent any comment following the text.
	    (or (looking-at comment-start-skip)
		(if (re-search-forward comment-start-skip
				       (save-excursion (end-of-line)
						       (point)) t)
		    (progn (indent-for-comment) (beginning-of-line))))))))))
;; (message "Indenting ESS expression...done")

;;;*;;; Support functions for indentation

(defun ess-comment-indent ()
  (if (looking-at "###")
      (current-column)
    (if (looking-at "##")
	(let ((tem (ess-calculate-indent)))
	  (if (listp tem) (car tem) tem))
      (skip-chars-backward " \t")
      (max (if (bolp) 0 (1+ (current-column)))
	   comment-column))))

(defun ess-indent-line ()
  "Indent current line as ESS code.
Return the amount the indentation changed by."
  (let ((indent (ess-calculate-indent nil))
	beg shift-amt
	(case-fold-search nil)
	(pos (- (point-max) (point))))
    (beginning-of-line)
    (setq beg (point))
    (cond ((eq indent nil)
	   (setq indent (current-indentation)))
	  (t
	   (skip-chars-forward " \t")
	   (if (and ess-fancy-comments (looking-at "###"))
	       (setq indent 0))
	   (if (and ess-fancy-comments
		    (looking-at "#")
		    (not (looking-at "##")))
	       (setq indent comment-column)
	     (if (eq indent t) (setq indent 0))
	     (if (listp indent) (setq indent (car indent)))
	     (cond ((and (looking-at "else\\b")
			 (not (looking-at "else\\s_")))
		    (setq indent (save-excursion
				   (ess-backward-to-start-of-if)
				   (+ ess-else-offset (current-indentation)))))
		   ((= (following-char) ?})
		    (setq indent
			  (+ indent
			     (- ess-close-brace-offset ess-indent-level))))
		   ((= (following-char) ?{)
		    (setq indent (+ indent ess-brace-offset)))))))
    (skip-chars-forward " \t")
    (setq shift-amt (- indent (current-column)))
    (if (zerop shift-amt)
	(if (> (- (point-max) pos) (point))
	    (goto-char (- (point-max) pos)))
      (delete-region beg (point))
      (indent-to indent)
      ;; If initial point was within line's indentation,
      ;; position after the indentation.
      ;; Else stay at same point in text.
      (if (> (- (point-max) pos) (point))
	  (goto-char (- (point-max) pos))))
    shift-amt))

(defun ess-calculate-indent (&optional parse-start)
  "Return appropriate indentation for current line as ESS code.
In usual case returns an integer: the column to indent to.
Returns nil if line starts inside a string, t if in a comment."
  (save-excursion
    (beginning-of-line)
    (let ((indent-point (point))
	  (case-fold-search nil)
	  state
	  containing-sexp)
      (if parse-start
	  (goto-char parse-start)
	(beginning-of-defun))
      (while (< (point) indent-point)
	(setq parse-start (point))
	(setq state (parse-partial-sexp (point) indent-point 0))
	(setq containing-sexp (car (cdr state))))
      (cond ((or (nth 3 state) (nth 4 state))
	     ;; return nil or t if should not change this line
	     (nth 4 state))
	    ((null containing-sexp)
	     ;; Line is at top level.  May be data or function definition,
	     (beginning-of-line)
	     (if (and (/= (following-char) ?\{)
		      (save-excursion
			(ess-backward-to-noncomment (point-min))
			(ess-continued-statement-p)))
		 ess-continued-statement-offset
	       0))   ; Unless it starts a function body
	    ((/= (char-after containing-sexp) ?{)
	     ;; line is expression, not statement:
	     ;; indent to just after the surrounding open.
	     (goto-char containing-sexp)
	     (let ((bol (save-excursion (beginning-of-line) (point))))

	       ;; modified by shiba@isac 7.3.1992
	       (cond ((and (numberp ess-expression-offset)
		   (re-search-backward "[ \t]*expression[ \t]*" bol t))
		      ;; This regexp match every "expression".
		      ;; modified by shiba
		      ;;(forward-sexp -1)
		      (beginning-of-line)
		      (skip-chars-forward " \t")
		      ;; End
		      (+ (current-column) ess-expression-offset))
		     ((and (numberp ess-arg-function-offset)
			   (re-search-backward
			    "=[ \t]*\\s\"*\\(\\w\\|\\s_\\)+\\s\"*[ \t]*"
			    bol t))
		      (forward-sexp -1)
		      (+ (current-column) ess-arg-function-offset))
		     ;; "expression" is searched before "=".
		     ;; End
		     (t
		      (progn (goto-char (1+ containing-sexp))
			     (current-column))))))
	    (t
	     ;; Statement level.  Is it a continuation or a new statement?
	     ;; Find previous non-comment character.
	     (goto-char indent-point)
	     (ess-backward-to-noncomment containing-sexp)
	     ;; Back up over label lines, since they don't
	     ;; affect whether our line is a continuation.
	     (while (eq (preceding-char) ?\,)
	       (ess-backward-to-start-of-continued-exp containing-sexp)
	       (beginning-of-line)
	       (ess-backward-to-noncomment containing-sexp))
	     ;; Now we get the answer.
	     (if (ess-continued-statement-p)
		 ;; This line is continuation of preceding line's statement;
		 ;; indent  ess-continued-statement-offset  more than the
		 ;; previous line of the statement.
		 (progn
		   (ess-backward-to-start-of-continued-exp containing-sexp)
		   (+ ess-continued-statement-offset (current-column)
		      (if (save-excursion (goto-char indent-point)
					  (skip-chars-forward " \t")
					  (eq (following-char) ?{))
			  ess-continued-brace-offset 0)))
	       ;; This line starts a new statement.
	       ;; Position following last unclosed open.
	       (goto-char containing-sexp)
	       ;; Is line first statement after an open-brace?
	       (or
		 ;; If no, find that first statement and indent like it.
		 (save-excursion
		   (forward-char 1)
		   (while (progn (skip-chars-forward " \t\n")
				 (looking-at "#"))
		     ;; Skip over comments following openbrace.
		     (forward-line 1))
		   ;; The first following code counts
		   ;; if it is before the line we want to indent.
		   (and (< (point) indent-point)
			(current-column)))
		 ;; If no previous statement,
		 ;; indent it relative to line brace is on.
		 ;; For open brace in column zero, don't let statement
		 ;; start there too.  If ess-indent-level is zero,
		 ;; use ess-brace-offset +
		 ;; ess-continued-statement-offset instead.
		 ;; For open-braces not the first thing in a line,
		 ;; add in ess-brace-imaginary-offset.
		 (+ (if (and (bolp) (zerop ess-indent-level))
			(+ ess-brace-offset ess-continued-statement-offset)
		      ess-indent-level)
		    ;; Move back over whitespace before the openbrace.
		    ;; If openbrace is not first nonwhite thing on the line,
		    ;; add the ess-brace-imaginary-offset.
		    (progn (skip-chars-backward " \t")
			   (if (bolp) 0 ess-brace-imaginary-offset))
		    ;; If the openbrace is preceded by a parenthesized exp,
		    ;; move to the beginning of that;
		    ;; possibly a different line
		    (progn
		      (if (eq (preceding-char) ?\))
			  (forward-sexp -1))
		      ;; Get initial indentation of the line we are on.
		      (current-indentation))))))))))

(defun ess-continued-statement-p ()
  (let ((eol (point)))
    (save-excursion
      (cond ((memq (preceding-char) '(nil ?\, ?\; ?\} ?\{ ?\]))
	     nil)
	    ;; ((bolp))
	    ((= (preceding-char) ?\))
	     (forward-sexp -2)
	     (looking-at "if\\b[ \t]*(\\|function\\b[ \t]*(\\|for\\b[ \t]*(\\|while\\b[ \t]*("))
	    ((progn (forward-sexp -1)
		    (and (looking-at "else\\b\\|repeat\\b")
			 (not (looking-at "else\\s_\\|repeat\\s_"))))
	     (skip-chars-backward " \t")
	     (or (bolp)
		 (= (preceding-char) ?\;)))
	    (t
	     (progn (goto-char eol)
		    (skip-chars-backward " \t")
		    (or (and (> (current-column) 1)
			     (save-excursion (backward-char 1)
					     (looking-at "[-:+*/_><=]")))
			(and (> (current-column) 3)
			     (progn (backward-char 3)
				    (looking-at "%[^ \t]%"))))))))))

(defun ess-backward-to-noncomment (lim)
  (let (opoint stop)
    (while (not stop)
      (skip-chars-backward " \t\n\f" lim)
      (setq opoint (point))
      (beginning-of-line)
      (search-forward "#" opoint 'move)
      (skip-chars-backward " \t#")
      (setq stop (or (/= (preceding-char) ?\n) (<= (point) lim)))
	(if stop (point)
	  (beginning-of-line)))))

(defun ess-backward-to-start-of-continued-exp (lim)
  (if (= (preceding-char) ?\))
      (forward-sexp -1))
  (beginning-of-line)
  (if (<= (point) lim)
      (goto-char (1+ lim)))
  (skip-chars-forward " \t"))

(defun ess-backward-to-start-of-if (&optional limit)
  "Move to the start of the last ``unbalanced'' if."
  (or limit (setq limit (save-excursion (beginning-of-defun) (point))))
  (let ((if-level 1)
	(case-fold-search nil))
    (while (not (zerop if-level))
      (backward-sexp 1)
      (cond ((looking-at "else\\b")
	     (setq if-level (1+ if-level)))
	    ((looking-at "if\\b")
	     (setq if-level (1- if-level)))
	    ((< (point) limit)
	     (setq if-level 0)
	     (goto-char limit))))))

;;;*;;; Predefined indentation styles

(defun ess-set-style (&optional style quiet)
  "Set up the `ess-mode' style variables from the `ess-style' variable
or if STYLE argument is given, use that.  It makes the ESS indentation
style variables buffer local."

  (interactive)
  (let ((ess-styles (mapcar 'car ess-style-alist)))
    (if (interactive-p)
	(setq style
	      (let ((style-string ; get style name with completion
		     (completing-read
		      (format
		       "Set ESS mode indentation style (default %s): "
		       ess-default-style)
		      (vconcat ess-styles)
		      (function (lambda (arg) (memq arg ess-styles))))))
		(if (string-equal "" style-string)
		    ess-default-style
		  (intern style-string)))))
    (setq style (or style ess-style)) ; use ess-style if style is nil
    (make-local-variable 'ess-style)
    (if (memq style ess-styles)
	(setq ess-style style)
      (error (concat "Bad ESS style: " style)))
    (if (not quiet)
	(message "ESS-style: %s" ess-style))
    ; finally, set the indentation style variables making each one local
    (mapcar (function (lambda (ess-style-pair)
			(make-local-variable (car ess-style-pair))
			(set (car ess-style-pair)
			     (cdr ess-style-pair))))
	    (cdr (assq ess-style ess-style-alist)))
    ess-style))

;;*;; Creating and manipulating dump buffers

;;;*;;; The user command

(defun ess-dump-object-into-edit-buffer (object)
  "Edit an ESS object in its own buffer.

Without a prefix argument, this simply finds the file pointed to by
`ess-source-directory'. If this file does not exist, or if a
prefix argument is given, a dump() command is sent to the ESS process to
generate the source buffer."
  (interactive
   (progn
     (require 'ess-inf)
     (ess-force-buffer-current "Process to dump from: ")
     (ess-read-object-name "Object to edit: ")))
  (let* ((dirname (file-name-as-directory
		   (if (stringp ess-source-directory)
		       ess-source-directory
		     (save-excursion
		       (set-buffer
			(process-buffer (get-ess-process
					 ess-local-process-name)))
		       (ess-setq-vars-local ess-customize-alist)
		       (apply ess-source-directory nil)))))
	 (filename (concat dirname (format ess-dump-filename-template object)))
	 (old-buff (get-file-buffer filename)))

    ;; If the directory doesn't exist, offer to create it
    (if (file-exists-p (directory-file-name dirname)) nil
      (if (y-or-n-p	; Approved
	   (format "Directory %s does not exist. Create it? " dirname))
	  (make-directory (directory-file-name dirname))
	(error "Directory %s does not exist." dirname)))

    ;; Three options:
    ;;  (1) Pop to an existing buffer containing the file in question
    ;;  (2) Find an existing file
    ;;  (3) Create a new file by issuing a dump() command to S
    ;; Force option (3) if there is a prefix arg

    (if current-prefix-arg
	(ess-dump-object object filename)
      (if old-buff
	  (progn
	    (pop-to-buffer old-buff)
	    (message "Popped to edit buffer."))
	;; No current buffer containing desired file
	(if (file-exists-p filename)
	    (progn
	      (ess-find-dump-file-other-window filename)
	      (message "Read %s" filename))
	  ;; No buffer and no file
	  (ess-dump-object object filename))))))

(defun ess-dump-object (object filename)
  "Dump the ESS object OBJECT into file FILENAME."
  (let ((complete-dump-command (format inferior-ess-dump-command
				       object filename)))
    (if (file-writable-p filename) nil
      (error "Can't dump %s as %f is not writeable." object filename))

    ;; Make sure we start fresh
    (if (get-file-buffer filename)
	(or (kill-buffer (get-file-buffer filename))
	    (error "Aborted.")))

    (ess-command complete-dump-command)
    (message "Dumped in %s" filename)

    (ess-find-dump-file-other-window filename)

    ;; PD, 1Apr97
    ;;This ensures that the object gets indented according to ess-mode,
    ;;not as the R/S deparser does it. At the same time, it gets rid
    ;;of the mess generated by sending TAB characters to the readline
    ;;functions in R when you eval-buffer-*.
    (indent-region (point-min-marker) (point-max-marker) nil)

    ;; Don't make backups for temporary files; it only causes clutter.
    ;; The ESS object itself is a kind of backup, anyway.
    (if ess-keep-dump-files nil
      (make-local-variable 'make-backup-files)
      (setq make-backup-files nil))

    ;; Don't get confirmation to delete dumped files when loading
    (if (eq ess-keep-dump-files 'check)
	(setq ess-keep-dump-files nil))

    ;; Delete the file if necessary
    (if ess-delete-dump-files
	(delete-file (buffer-file-name)))))

(defun ess-find-dump-file-other-window (filename)
  "Find ESS source file FILENAME in another window."

  (if (file-exists-p filename) nil
    (ess-write-to-dribble-buffer
     (format "%s does not exist. Bad dump, starting fresh." filename)))

  ;; Generate a buffer with the dumped data
  (find-file-other-window filename)
  (ess-mode ess-customize-alist)

  (auto-save-mode 1)		; Auto save in this buffer
  (setq ess-local-process-name ess-current-process-name)

  (if ess-function-template
      (progn
	(goto-char (point-max))
	(if (re-search-backward ess-dumped-missing-re nil t)
	    (progn
	      (replace-match ess-function-template t t)
	      (set-buffer-modified-p nil) ; Don't offer to save if killed now
	      (goto-char (point-min))
	      (condition-case nil
		  ;; This may fail if there are no opens
		  (down-list 1)
		(error nil)))))))

(defun ess-dump-object-into-edit-buffer-other-frame (object)
  "Edit an ESS object in its own frame."
  (switch-to-buffer-other-frame (ess-dump-object-into-edit-buffer object)))

(provide 'ess-mode)

 ; Local variables section

;;; This file is automatically placed in Outline minor mode.
;;; The file is structured as follows:
;;; Chapters:     ^L ;
;;; Sections:    ;;*;;
;;; Subsections: ;;;*;;;
;;; Components:  defuns, defvars, defconsts
;;;              Random code beginning with a ;;;;* comment

;;; Local variables:
;;; mode: emacs-lisp
;;; outline-minor-mode: nil
;;; mode: outline-minor
;;; outline-regexp: "\^L\\|\\`;\\|;;\\*\\|;;;\\*\\|(def[cvu]\\|(setq\\|;;;;\\*"
;;; End:

;;; ess-mode.el ends here
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.