From 15d8264356d0e02a98f388b66020fd1fdae93a16 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Sun, 5 Dec 2010 12:41:38 +0100 Subject: Added emacs config --- emacs.d/sqlplus.el | 5151 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5151 insertions(+) create mode 100644 emacs.d/sqlplus.el (limited to 'emacs.d/sqlplus.el') diff --git a/emacs.d/sqlplus.el b/emacs.d/sqlplus.el new file mode 100644 index 0000000..4d5e7d7 --- /dev/null +++ b/emacs.d/sqlplus.el @@ -0,0 +1,5151 @@ +;;; sqlplus.el --- User friendly interface to SQL*Plus and support for PL/SQL compilation + +;; Copyright (C) 2007, 2008 Peter Karpiuk, Scott Tiger S.A. + +;; Author: Peter Karpiuk +;; Maintainer: Peter Karpiuk +;; Created: 25 Nov 2007 +;; Version 0.9.0 +;; Keywords: sql sqlplus oracle plsql + +;; GNU Emacs 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. + +;; GNU Emacs 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: + +;; Facilitates interaction with Oracle via SQL*Plus (GNU Emacs only). +;; Moreover, this package complements plsql.el (Kahlil Hodgson) +;; upon convenient compilation of PL/SQL source files. +;; +;; This package was inspired by sqlplus-mode.el (Rob Riepel, Peter +;; D. Pezaris, Martin Schwenke), but offers more features: +;; - tables are parsed, formatted and rendered with colors, like in +;; many GUI programs; you can see raw SQL*Plus output also, +;; if you wish +;; - table will be cutted if you try to fetch too many rows +;; (SELECT * FROM MY_MILLION_ROWS_TABLE); current SQL*Plus command +;; will be automatically interrupted under the hood in such cases +;; - you can use many SQL*Plus processes simultaneously, +;; - font locking (especially if you use Emacs>=22), with database +;; object names highlighting, +;; - history (log) of executed commands - see` sqlplus-history-dir` +;; variable, +;; - commands for fetching any database object definition +;; (package, table/index/sequence script) +;; - query result can be shown in HTML, +;; - input buffer for each connection can be saved into file on +;; disconnect and automatically restored on next connect (see +;; 'sqlplus-session-cache-dir' variable); if you place some +;; SQL*Plus commands between '/* init */' and '/* end */' +;; comments in saved input buffer, they will be automatically +;; executed on every connect +;; - if you use plsql.el for editing PL/SQL files, you can compile +;; such sources everytime with C-cC-c; error messages will be +;; parsed and displayed for easy source navigation +;; - M-. or C-mouse-1 on database object name will go to definition +;; in filesystem (use arrow button on toolbar to go back) +;; +;; The following commands should be added to a global initialization +;; file or to any user's .emacs file to conveniently use +;; sqlplus-mode: +;; +;; (require 'sqlplus) +;; (add-to-list 'auto-mode-alist '("\\.sqp\\'" . sqlplus-mode)) +;; +;; If you want PL/SQL support also, try something like this: +;; +;; (require 'plsql) +;; (setq auto-mode-alist +;; (append '(("\\.pls\\'" . plsql-mode) ("\\.pkg\\'" . plsql-mode) +;; ("\\.pks\\'" . plsql-mode) ("\\.pkb\\'" . plsql-mode) +;; ("\\.sql\\'" . plsql-mode) ("\\.PLS\\'" . plsql-mode) +;; ("\\.PKG\\'" . plsql-mode) ("\\.PKS\\'" . plsql-mode) +;; ("\\.PKB\\'" . plsql-mode) ("\\.SQL\\'" . plsql-mode) +;; ("\\.prc\\'" . plsql-mode) ("\\.fnc\\'" . plsql-mode) +;; ("\\.trg\\'" . plsql-mode) ("\\.vw\\'" . plsql-mode) +;; ("\\.PRC\\'" . plsql-mode) ("\\.FNC\\'" . plsql-mode) +;; ("\\.TRG\\'" . plsql-mode) ("\\.VW\\'" . plsql-mode)) +;; auto-mode-alist )) +;; +;; M-x sqlplus will start new SQL*Plus session. +;; +;; C-RET execute command under point +;; S-C-RET execute command under point and show result table in HTML +;; buffer +;; M-RET explain execution plan for command under point +;; M-. or C-mouse-1: find database object definition (table, view +;; index, synonym, trigger, procedure, function, package) +;; in filesystem +;; C-cC-s show database object definition (retrieved from database) +;; +;; Use describe-mode while in sqlplus-mode for further instructions. +;; +;; Many useful commands are defined in orcl-mode minor mode, which is +;; common for input and otput SQL*Plus buffers, as well as PL/SQL +;; buffers. +;; +;; For twiddling, see 'sqlplus' customization group. +;; +;; If you find this package useful, send me a postcard to address: +;; +;; Peter Karpiuk +;; Scott Tiger S.A. +;; ul. Gawinskiego 8 +;; 01-645 Warsaw +;; Poland + +;;; Known bugs: + +;; 1. Result of SQL select command can be messed up if some columns +;; has newline characters. To avoid this, execute SQL*Plus command +;; column truncated +;; before such select + +;;; Code: + +(require 'recentf) +(require 'font-lock) +(require 'cl) +(require 'sql) +(require 'tabify) +(require 'skeleton) + +(defconst sqlplus-revision "$Revision: 1.7 $") + +;;; Variables - + +(defgroup sqlplus nil + "SQL*Plus" + :group 'tools + :version 21) + +(defcustom plsql-auto-parse-errors-flag t + "Non nil means parse PL/SQL compilation results and show them in the compilation buffer." + :group 'sqlplus + :type '(boolean)) + +(defcustom sqlplus-init-sequence-start-regexp "/\\* init \\*/" + "SQL*Plus start of session init command sequence." + :group 'sqlplus + :type '(regexp)) + +(defcustom sqlplus-init-sequence-end-regexp "/\\* end \\*/" + "SQL*Plus end of session init command sequence." + :group 'sqlplus + :type '(regexp)) + +(defcustom sqlplus-explain-plan-warning-regexps '("TABLE ACCESS FULL" "INDEX FULL SCAN") + "SQL*Plus explain plan warning regexps" + :group 'sqlplus + :type '(repeat regexp)) + +(defcustom sqlplus-syntax-faces + '((schema font-lock-type-face nil) + (table font-lock-type-face ("dual")) + (synonym font-lock-type-face nil) + (view font-lock-type-face nil) + (column font-lock-constant-face nil) + (sequence font-lock-type-face nil) + (package font-lock-type-face nil) + (trigger font-lock-type-face nil) + (index font-lock-type-face) nil) + "Font lock configuration for database object names in current schema. +This is alist, and each element looks like (SYMBOL FACE LIST) +where SYMBOL is one of: schema, table, synonym, view, column, +sequence, package, trigger, index. Database objects means only +objects from current schema, so if you want syntax highlighting +for other objects (eg. 'dual' table name), you can explicitly +enumerate them in LIST as strings." + :group 'sqlplus + :tag "Oracle SQL Syntax Faces" + :type '(repeat (list symbol face (repeat string)))) + +(defcustom sqlplus-output-buffer-max-size (* 50 1000 1000) + "Maximum size of SQL*Plus output buffer. +After exceeding oldest results are deleted." + :group 'sqlplus + :tag "SQL*Plus Output Buffer Max Size" + :type '(integer)) + +(defcustom sqlplus-select-result-max-col-width nil + "Maximum width of column in displayed database table, or nil if there is no limit. +If any cell value is longer, it will be cutted and terminated with ellipsis ('...')." + :group 'sqlplus + :tag "SQL*Plus Select Result Max Column Width" + :type '(choice integer (const nil))) + +(defcustom sqlplus-format-output-tables-flag t + "Non-nil means format result if it looks like database table." + :group 'sqlplus + :tag "SQL*Plus Format Output Table" + :type '(boolean)) + +(defcustom sqlplus-kill-processes-without-query-on-exit-flag t + "Non-nil means silently kill all SQL*Plus processes on Emacs exit." + :group 'sqlplus + :tag "SQL*Plus Kill Processes Without Query On Exit" + :type '(boolean)) + +(defcustom sqlplus-multi-output-tables-default-flag t + "Non-nil means render database table as set of adjacent tables so that they occupy all width of output window. +For screen space saving and user comfort." + :group 'sqlplus + :tag "SQL*Plus Multiple Tables In Output by Default" + :type '(boolean)) + +(defcustom sqlplus-source-buffer-readonly-by-default-flag t + "Non-nil means show database sources in read-only buffer." + :group 'sqlplus + :tag "SQL*Plus Source Buffer Read Only By Default" + :type '(boolean)) + +(defcustom sqlplus-command "sqlplus" + "SQL*Plus interpreter program." + :group 'sqlplus + :tag "SQL*Plus Command" + :type '(string)) + +(defcustom sqlplus-history-dir nil + "Directory of SQL*Plus command history (log) files, or nil (dont generate log files). +History file name has format '-history.txt'." + :group 'sqlplus + :tag "SQL*Plus History Dir" + :type '(choice directory (const nil))) + +(defvar sqlplus-session-file-extension "sqp") + +(defcustom sqlplus-session-cache-dir nil + "Directory of SQL*Plus input buffer files, or nil (dont save user session). +Session file name has format '.sqp'" + :group 'sqlplus + :tag "SQL*Plus History Dir" + :type '(choice directory (const nil))) + +(defcustom sqlplus-save-passwords nil + "Non-nil means save passwords between Emacs sessions. (Not implemented yet)." + :group 'sqlplus + :tag "SQL*Plus Save Passwords" + :type '(boolean)) + +(defcustom sqlplus-pagesize 200 + "Approximate number of records in query results. +If result has more rows, it will be cutted and terminated with '. . .' line." + :group 'sqlplus + :tag "SQL*Plus Max Rows Count" + :type '(integer)) + +(defvar sqlplus-default-wrap "on") + +(defcustom sqlplus-initial-strings + (list "set sqlnumber off" + "set tab off" + "set linesize 4000" + "set echo off" + "set newpage 1" + "set space 1" + "set feedback 6" + "set heading on" + "set trimspool off" + (format "set wrap %s" sqlplus-default-wrap) + "set timing on" + "set feedback on") + "Initial commands to send to interpreter. +Customizing this variable is dangerous." + :group 'sqlplus + :tag "SQL*Plus Initial Strings" + :type '(repeat string)) + +(defcustom sqlplus-table-col-separator " | " + "Database table column separator (text-only terminals)." + :group 'sqlplus + :tag "SQL*Plus Table Col Separator" + :type '(string)) + +(defcustom sqlplus-table-col-head-separator "-+-" + "Database table header-column separator (text-only terminals)." + :group 'sqlplus + :tag "SQL*Plus Table Col Separator" + :type '(string)) + +(defcustom sqlplus-html-output-file-name "$HOME/sqlplus_report.html" + "Output file for HTML result." + :group 'sqlplus + :tag "SQL*Plus HTML Output File Name" + :type '(file)) + +(defcustom sqlplus-html-output-encoding "iso-8859-1" + "Encoding for SQL*Plus HTML output." + :group 'sqlplus + :tag "SQL*Plus HTML Output Encoding" + :type '(string)) + +(defcustom sqlplus-html-output-sql t + "Non-nil means put SQL*Plus command in head of HTML result." + :group 'sqlplus + :tag "SQL*Plus HTML Output Encoding" + :type '(choice (const :tag "Elegant" 'elegant) + (const :tag "Simple" t) + (const :tag "No" nil))) + +(defcustom sqlplus-html-output-header (concat (current-time-string) "

") + "HTML header sexp (result must be string)." + :group 'sqlplus + :tag "SQL*Plus HTML Output Header" + :type '(sexp)) + +(defcustom sqlplus-command-highlighting-percentage 7 + "SQL*Plus command highlighting percentage (0-100), only if sqlplus-command-highlighting-style is set." + :group 'sqlplus + :tag "SQL*Plus command highlighting percentage" + :type '(integer)) + +(defcustom sqlplus-command-highlighting-style nil + "How to highlight current command in sqlplus buffer." + :group 'sqlplus + :tag "SQL*Plud command highlighting style" + :type '(choice (const :tag "Fringe" fringe) + (const :tag "Background" background) + (const :tag "Fringe and background" fringe-and-background) + (const :tag "None" nil))) + +(defvar sqlplus-elegant-style window-system) + +(defvar sqlplus-cs nil) + +(defun sqlplus-shine-color (color percent) + (when (equal color "unspecified-bg") + (setq color (if (< percent 0) "white" "black"))) + (apply 'format "#%02x%02x%02x" + (mapcar (lambda (value) + (min 65535 (max 0 (* (+ (/ value 650) percent) 650)))) + (color-values color)))) + +(defvar sqlplus-table-head-face 'sqlplus-table-head-face) +(defface sqlplus-table-head-face + (list + (list '((class mono)) + '(:inherit default :weight bold :inverse-video t)) + (list '((background light)) + (append (list :inherit 'default :background (sqlplus-shine-color (face-background 'default) -70) :foreground (face-background 'default)) + (when (and sqlplus-elegant-style (>= emacs-major-version 22)) '(:box (:style released-button))))) + (list '((background dark)) + (append (list :inherit 'default :background (sqlplus-shine-color (face-background 'default) +70) :foreground (face-background 'default)) + (when (and sqlplus-elegant-style (>= emacs-major-version 22)) '(:box (:style released-button))))) + '(t (:inherit default))) + "Face for table header" + :group 'sqlplus) + +(defvar sqlplus-table-even-rows-face 'sqlplus-table-even-rows-face) +(defface sqlplus-table-even-rows-face + (list + (list '((class mono)) '()) + (list '((type tty)) '()) + (list '((background light)) + (append (list :inherit 'default :background (sqlplus-shine-color (face-background 'default) -20) :overline (face-background 'default)))) + (list '((background dark)) + (append (list :inherit 'default :background (sqlplus-shine-color (face-background 'default) +20) :overline (face-background 'default)))) + '(t ())) + "Face for table even rows" + :group 'sqlplus) + +(defvar sqlplus-table-odd-rows-face 'sqlplus-table-odd-rows-face) +(defface sqlplus-table-odd-rows-face + (list + (list '((class mono)) '(:inherit default)) + (list '((background light)) + (append (list :inherit 'default :background (sqlplus-shine-color (face-background 'default) -30) :overline (face-background 'default)))) + (list '((background dark)) + (append (list :inherit 'default :background (sqlplus-shine-color (face-background 'default) +30) :overline (face-background 'default)))) + '(t (:inherit default))) + "Face for table even rows" + :group 'sqlplus) + +(defvar sqlplus-command-highlight-face 'sqlplus-command-highlight-face) +(defface sqlplus-command-highlight-face + (list + '(((class mono)) ()) + '(((type tty)) ()) + (list '((background light)) + (append (list :background (sqlplus-shine-color (face-background 'default) (- sqlplus-command-highlighting-percentage))))) + (list '((background dark)) + (append (list :background (sqlplus-shine-color (face-background 'default) sqlplus-command-highlighting-percentage)))) + '(t ())) + "Face for highlighting command under point" + :group 'sqlplus) + +(defvar sqlplus-plsql-compilation-results-buffer-name "*PL/SQL Compilation*") + +(defvar sqlplus-fan "|" + "Local in input buffers") +(make-variable-buffer-local 'sqlplus-fan) + +(defvar orcl-mode-map nil + "Keymap used in Orcl mode.") + +(define-minor-mode orcl-mode + "Mode for executing SQL*Plus commands and scrolling results. + +Mode Specific Bindings: + +\\{orcl-mode-map}" + nil ; init value + (" " (:eval sqlplus-fan) " " (:eval (connect-string-to-string))) ; mode indicator + orcl-mode-map ; keymap + ;; body + (setq sqlplus-fan "|") + (unless (assq 'orcl-mode minor-mode-map-alist) + (push (cons 'orcl-mode orcl-mode-map) minor-mode-map-alist))) + +(defvar sqlplus-user-variables (makehash 'equal)) + +(defvar sqlplus-user-variables-history nil) + +(defvar sqlplus-get-source-history nil) + +(defvar sqlplus-process-p nil + "Non-nil (connect string) if current buffer is SQL*Plus process buffer. +Local in process buffer.") +(make-variable-buffer-local 'sqlplus-process-p) + +(defvar sqlplus-command-seq 0 + "Sequence for command id within SQL*Plus connection. +Local in process buffer.") +(make-variable-buffer-local 'sqlplus-command-seq) + +;;; :id - unique command identifier (from sequence, for session) +;;; :sql - content of command +;;; :dont-parse-result - process data online as it comes from sqlplus, with sqlplus-result-online or with :result-function function +;;; :result-function - function for processing sqlplus data; must have signature (context connect-string begin end interrupted); +;;; if nil then it is sqlplus-result-online for :dont-parse-result set to non-nil and sqlplus-process-command-output for :dont-parse-result set to nil +;;; :current-command-input-buffer-name - buffer name from which command was initialized +(defvar sqlplus-command-contexts nil + "Command options list, for current and enqueued commands, in chronological order. +Local in process buffer.") +(make-variable-buffer-local 'sqlplus-command-contexts) + +(defvar sqlplus-connect-string nil + "Local variable with connect-string for current buffer (input buffers, output buffer).") +(make-variable-buffer-local 'sqlplus-connect-string) + +(defvar sqlplus-connect-strings-alist nil + "Connect strings in format (CS . PASSWD), where PASSWD can be nil.") + +(defvar sqlplus-connect-string-history nil) + +(defvar sqlplus-prompt-prefix "SQL[") +(defvar sqlplus-prompt-suffix "]# ") + +(defvar sqlplus-page-separator "@!%#!") + +(defvar sqlplus-repfooter "##%@!") + +(defvar sqlplus-mode-map nil + "Keymap used in SQL*Plus mode.") + +(defvar sqlplus-output-separator "@--" + "String printed between sets of SQL*Plus command output.") + +;;; Markers - + +(defvar sqlplus-buffer-mark (make-marker) + "Marks the current SQL command in the SQL*Plus output buffer. +Local in output buffer.") +(make-variable-buffer-local 'sqlplus-buffer-mark) + +(defvar sqlplus-region-beginning-pos nil + "Marks the beginning of the region to sent to the SQL*Plus process. +Local in input buffer with sqlplus-mode.") +(make-variable-buffer-local 'sqlplus-region-beginning-pos) + +(defvar sqlplus-region-end-pos nil + "Marks the end of the region to sent to the SQL*Plus process. +Local in input buffer with sqlplus-mode.") +(make-variable-buffer-local 'sqlplus-region-end-pos) + +(defvar sqlplus-connections-menu + '("SQL*Plus" + :filter sqlplus-connections-menu) + "Menu for database connections") + +(defconst sqlplus-kill-xpm "\ +/* XPM */ +static char * reload_page_xpm[] = { +\"24 24 100 2\", +\" c None\", +\". c #000000\", +\"+ c #2A5695\", +\"@ c #30609E\", +\"# c #3363A2\", +\"$ c #3969A6\", +\"% c #3D6BA6\", +\"& c #3C68A3\", +\"* c #35619C\", +\"= c #244F8D\", +\"- c #3364A3\", +\"; c #3162A1\", +\"> c #3867A4\", +\", c #3F6DA8\", +\"' c #4672AC\", +\") c #4B76AE\", +\"! c #4E78AF\", +\"~ c #537CB1\", +\"{ c #547DB0\", +\"] c #446BA1\", +\"^ c #2E5D9C\", +\"/ c #234F8C\", +\"( c #214C89\", +\"_ c #244E8C\", +\": c #3A649D\", +\"< c #517BB0\", +\"[ c #517BB1\", +\"} c #4874AD\", +\"| c #6086B7\", +\"1 c #5F84B4\", +\"2 c #4B71A6\", +\"3 c #7B9BC4\", +\"4 c #224C89\", +\"5 c #3865A2\", +\"6 c #406FAB\", +\"7 c #436BA3\", +\"8 c #648ABA\", +\"9 c #4D78AF\", +\"0 c #4B77AE\", +\"a c #6E91BE\", +\"b c #809EC6\", +\"c c #204A87\", +\"d c #4974AF\", +\"e c #2B5590\", +\"f c #6487B5\", +\"g c #678CBB\", +\"h c #3465A4\", +\"i c #84A1C8\", +\"j c #6D8FBA\", +\"k c #4F7AB0\", +\"l c #8BA7CB\", +\"m c #7E9DC5\", +\"n c #83A1C7\", +\"o c #91ACCE\", +\"p c #89A4C9\", +\"q c #8FA9CB\", +\"r c #85A2C7\", +\"s c #90ABCC\", +\"t c #3E6CA8\", +\"u c #87A3C8\", +\"v c #4B6DA1\", +\"w c #91ABCD\", +\"x c #3768A5\", +\"y c #8AA5C9\", +\"z c #2D5690\", +\"A c #204A86\", +\"B c #93ADCE\", +\"C c #7294BF\", +\"D c #6288B9\", +\"E c #86A3C8\", +\"F c #466EA3\", +\"G c #3864A1\", +\"H c #285390\", +\"I c #234E8C\", +\"J c #95AECF\", +\"K c #7493BC\", +\"L c #86A2C7\", +\"M c #7999C3\", +\"N c #5B82B5\", +\"O c #6C8EBB\", +\"P c #4B71A5\", +\"Q c #26508B\", +\"R c #2B5792\", +\"S c #305E9B\", +\"T c #31619F\", +\"U c #7895BD\", +\"V c #819DC3\", +\"W c #688DBB\", +\"X c #6288B8\", +\"Y c #5880B4\", +\"Z c #577FB3\", +\"` c #547DB2\", +\" . c #416FAA\", +\".. c #3564A2\", +\"+. c #577AAB\", +\"@. c #6286B6\", +\"#. c #668BBA\", +\"$. c #507AB0\", +\"%. c #426EA8\", +\"&. c #2F5B97\", +\" \", +\" \", +\" \", +\" . . . . . . . . \", +\" . . + @ # $ % & * . . . . \", +\" . = - ; @ > , ' ) ! ~ { . . . ] . \", +\" . ^ / ( _ . . . : < [ } | 1 2 3 . \", +\" . _ 4 5 6 . . . 7 8 9 0 a b . \", +\" . c d . . . e f g h i . \", +\" . . . . . j k h l . \", +\" . . f m n l o . \", +\" . . . . . . . . \", +\" . . . . . . . . \", +\" . p q q q r . . \", +\" . s , t u v . . . . \", +\" . w x | y z . . . . A . \", +\" . B C 9 D E F . . . G H I . \", +\" . J K L M N C O P . . . Q R S T . \", +\" . U . . . V W X | Y Z ` ) .... \", +\" . . . . +.@.#.N $.%.&.. . \", +\" . . . . . . . . \", +\" \", +\" \", +\" \"}; +" + "XPM format image used as Kill icon") + +(defconst sqlplus-cancel-xpm "\ +/* XPM */ +static char * process_stop_xpm[] = { +\"24 24 197 2\", +\" c None\", +\". c #000000\", +\"+ c #C92B1E\", +\"@ c #DA432F\", +\"# c #E95941\", +\"$ c #F26B50\", +\"% c #ED6047\", +\"& c #DF4A35\", +\"* c #CE3324\", +\"= c #BF1D13\", +\"- c #EA5942\", +\"; c #EF563A\", +\"> c #F14D2C\", +\", c #F1431F\", +\"' c #F23A12\", +\") c #F2421C\", +\"! c #F24D2A\", +\"~ c #F15737\", +\"{ c #F0644A\", +\"] c #CF3121\", +\"^ c #D83828\", +\"/ c #ED5840\", +\"( c #EC3B1C\", +\"_ c #EE310B\", +\": c #F1350C\", +\"< c #F4380D\", +\"[ c #F53A0D\", +\"} c #F53B0D\", +\"| c #F4390D\", +\"1 c #F2360C\", +\"2 c #EF3A15\", +\"3 c #F05A3D\", +\"4 c #E44D37\", +\"5 c #CD2B1E\", +\"6 c #EA4D35\", +\"7 c #E92D0C\", +\"8 c #ED2F0B\", +\"9 c #F0330C\", +\"0 c #F3380D\", +\"a c #F63C0E\", +\"b c #F93F0F\", +\"c c #F9400F\", +\"d c #F73D0E\", +\"e c #F1340C\", +\"f c #EE300B\", +\"g c #EC482C\", +\"h c #E04532\", +\"i c #E84E3A\", +\"j c #E62A0E\", +\"k c #EA2B0A\", +\"l c #F83F0E\", +\"m c #FC4310\", +\"n c #FC4410\", +\"o c #F63B0E\", +\"p c #EB2C0A\", +\"q c #EB5139\", +\"r c #C8251A\", +\"s c #DD3D2E\", +\"t c #E5341D\", +\"u c #E62508\", +\"v c #F9BEB2\", +\"w c #FBCFC5\", +\"x c #F54C23\", +\"y c #F95125\", +\"z c #FDD4CB\", +\"A c #FABFB2\", +\"B c #E83013\", +\"C c #E84F3B\", +\"D c #E54737\", +\"E c #E22007\", +\"F c #E92A09\", +\"G c #FBD2CA\", +\"H c #FFFFFF\", +\"I c #FDDFD9\", +\"J c #F64E24\", +\"K c #FDE0D9\", +\"L c #E72609\", +\"M c #E7452F\", +\"N c #E33D2D\", +\"O c #E11E07\", +\"P c #E52308\", +\"Q c #E82809\", +\"R c #EC3F21\", +\"S c #FCDED8\", +\"T c #F55C37\", +\"U c #FCDFD8\", +\"V c #F04521\", +\"W c #EC2E0A\", +\"X c #E92909\", +\"Y c #E62408\", +\"Z c #E53823\", +\"` c #CE2B1F\", +\" . c #C62018\", +\".. c #E03120\", +\"+. c #E01C06\", +\"@. c #E32107\", +\"#. c #ED4121\", +\"$. c #FEF9F8\", +\"%. c #E72709\", +\"&. c #E42208\", +\"*. c #E32D17\", +\"=. c #D83729\", +\"-. c #CB231B\", +\";. c #DE2A1B\", +\">. c #DE1A06\", +\",. c #EE5135\", +\"'. c #EF5335\", +\"). c #EC2D0A\", +\"!. c #E82709\", +\"~. c #E21F07\", +\"{. c #E02511\", +\"]. c #DC392C\", +\"^. c #BE1612\", +\"/. c #DD2E21\", +\"(. c #DC1705\", +\"_. c #DF1B06\", +\":. c #E42308\", +\"<. c #E93A20\", +\"[. c #FBDDD8\", +\"}. c #EB3D20\", +\"|. c #DF2A18\", +\"1. c #D02A1F\", +\"2. c #DC3328\", +\"3. c #DA1404\", +\"4. c #DD1805\", +\"5. c #E3331E\", +\"6. c #FADCD8\", +\"7. c #FBDCD8\", +\"8. c #EB4C34\", +\"9. c #E6361F\", +\"0. c #DD1905\", +\"a. c #DF2F21\", +\"b. c #C21A14\", +\"c. c #DA3128\", +\"d. c #D81408\", +\"e. c #F7C9C4\", +\"f. c #FADBD8\", +\"g. c #E5341E\", +\"h. c #E5351E\", +\"i. c #F8CEC9\", +\"j. c #DB1505\", +\"k. c #DD3429\", +\"l. c #C31613\", +\"m. c #D9281F\", +\"n. c #D71003\", +\"o. c #D91304\", +\"p. c #F3B5B0\", +\"q. c #F7CDC9\", +\"r. c #E12F1D\", +\"s. c #DF1C06\", +\"t. c #E2301D\", +\"u. c #F4B6B0\", +\"v. c #DC1605\", +\"w. c #DB2317\", +\"x. c #D2271F\", +\"y. c #D1231D\", +\"z. c #D61A10\", +\"A. c #D60F03\", +\"B. c #D81104\", +\"C. c #DB1605\", +\"D. c #D81204\", +\"E. c #D81509\", +\"F. c #DA2F26\", +\"G. c #D52620\", +\"H. c #D51A12\", +\"I. c #D50D03\", +\"J. c #D60E03\", +\"K. c #D6170D\", +\"L. c #D92B23\", +\"M. c #BD100D\", +\"N. c #AB0404\", +\"O. c #CE1D19\", +\"P. c #D6231C\", +\"Q. c #D41008\", +\"R. c #D40B02\", +\"S. c #D40C02\", +\"T. c #D50C03\", +\"U. c #D40E05\", +\"V. c #D62018\", +\"W. c #D4251F\", +\"X. c #B30A09\", +\"Y. c #A20000\", +\"Z. c #BC0F0E\", +\"`. c #D2211E\", +\" + c #D52520\", +\".+ c #D5201A\", +\"++ c #D41A14\", +\"@+ c #D51F19\", +\"#+ c #D62620\", +\"$+ c #D52420\", +\"%+ c #C51614\", +\"&+ c #A30101\", +\"*+ c #A30303\", +\"=+ c #AE0909\", +\"-+ c #BD0E0E\", +\";+ c #B30B0B\", +\">+ c #A30404\", +\" \", +\" . . . . . . . \", +\" . . + @ # $ % & * . . \", +\" . = - ; > , ' ) ! ~ { ] . \", +\" . ^ / ( _ : < [ } | 1 2 3 4 . \", +\" . 5 6 7 8 9 0 a b c d | e f g h . \", +\" . i j k f : [ l m n c o 1 _ p q r . \", +\" . s t u k v w x l m n y z A _ p B C . \", +\" . D E u F G H I J b y K H w f k L M . \", +\" . N O P Q R S H I T K H U V W X Y Z ` . \", +\" . ...+.@.u F #.S H $.H U V 8 k %.&.*.=.. \", +\" . -.;.>.O &.L F ,.$.H $.'.).k !.P ~.{.].. \", +\" . ^./.(._.~.:.<.[.H $.H [.}.L P E +.|.1.. \", +\" . 2.3.4._.5.6.H 7.8.7.H 6.9.~.+.0.a.b.. \", +\" . c.d.3.(.e.H f.g.@.h.6.H i._.4.j.k.. \", +\" . l.m.n.o.p.q.r._.s.s.t.e.u.v.3.w.x.. \", +\" . y.z.A.B.o.j.C.(.(.v.j.3.D.E.F.. \", +\" . G.H.I.J.n.B.B.B.B.n.A.K.L.M.. \", +\" . N.O.P.Q.R.S.T.T.S.U.V.W.X.. \", +\" . Y.Z.`. +.+++@+#+$+%+&+. \", +\" . . . *+=+-+;+>+Y.. . \", +\" . . . . . . \", +\" \", +\" \"}; +" + "XPM format image used as Cancel icon") + +(defconst sqlplus-rollback-xpm "\ +/* XPM */ +static char * rollback_xpm[] = { +\"24 24 228 2\", +\" c None\", +\". c #000000\", +\"+ c #F8F080\", +\"@ c #FEF57B\", +\"# c #FFF571\", +\"$ c #FFF164\", +\"% c #FFED58\", +\"& c #FFE748\", +\"* c #FEDE39\", +\"= c #F8F897\", +\"- c #FFFE96\", +\"; c #FFFA8A\", +\"> c #FFF67C\", +\", c #FFF16E\", +\"' c #FFEC62\", +\") c #FFE956\", +\"! c #FFE448\", +\"~ c #FFE03C\", +\"{ c #FFDD30\", +\"] c #FED821\", +\"^ c #F1CB15\", +\"/ c #FFFC92\", +\"( c #FFFC91\", +\"_ c #FFFC90\", +\": c #FFFB8D\", +\"< c #FFF67D\", +\"[ c #FFEB5E\", +\"} c #FFEA5B\", +\"| c #FFE958\", +\"1 c #FFE855\", +\"2 c #FFE752\", +\"3 c #FDD41C\", +\"4 c #FDD319\", +\"5 c #FDD416\", +\"6 c #FFFF9D\", +\"7 c #FFFF99\", +\"8 c #FFFD94\", +\"9 c #FFFA89\", +\"0 c #FFDC2F\", +\"a c #FED315\", +\"b c #FFD808\", +\"c c #FFFC9F\", +\"d c #FFFE99\", +\"e c #FFDF3B\", +\"f c #F7C909\", +\"g c #F8EA86\", +\"h c #FEFCB7\", +\"i c #FFFDA6\", +\"j c #FFFA91\", +\"k c #FFF681\", +\"l c #FFF171\", +\"m c #FFED64\", +\"n c #FFE44A\", +\"o c #FFE03D\", +\"p c #FEDB2F\", +\"q c #F9D21E\", +\"r c #E9BC0F\", +\"s c #CE9C02\", +\"t c #F3E36A\", +\"u c #FCF899\", +\"v c #FFFCA3\", +\"w c #FEF694\", +\"x c #FFF284\", +\"y c #FFEE71\", +\"z c #FFEA62\", +\"A c #FDDC40\", +\"B c #F8D22F\", +\"C c #F1C61B\", +\"D c #DDAD0A\", +\"E c #CC9A02\", +\"F c #C89500\", +\"G c #F4EA77\", +\"H c #F7EF7F\", +\"I c #FFF16A\", +\"J c #FFEF68\", +\"K c #FFEE66\", +\"L c #FED622\", +\"M c #FED51E\", +\"N c #FED419\", +\"O c #E9B90E\", +\"P c #E7B509\", +\"Q c #D4A202\", +\"R c #CA9700\", +\"S c #F6E67C\", +\"T c #F3E67F\", +\"U c #FCEE7A\", +\"V c #FDEB66\", +\"W c #FEE44E\", +\"X c #FED313\", +\"Y c #FDCA03\", +\"Z c #F2BE01\", +\"` c #D4A60D\", +\" . c #D4A206\", +\".. c #D19C00\", +\"+. c #CF9800\", +\"@. c #E3AF02\", +\"#. c #F9EB81\", +\"$. c #FBF096\", +\"%. c #F9E67C\", +\"&. c #F8DC5F\", +\"*. c #F8D548\", +\"=. c #F9D02D\", +\"-. c #F9C915\", +\";. c #F7C104\", +\">. c #EEB606\", +\",. c #E9B704\", +\"'. c #DEAE08\", +\"). c #414D7B\", +\"!. c #3C5CA2\", +\"~. c #3A65B3\", +\"{. c #3668BB\", +\"]. c #325EAF\", +\"^. c #F3E46E\", +\"/. c #FCFA9B\", +\"(. c #FFF89C\", +\"_. c #FDEC81\", +\":. c #FCE668\", +\"<. c #FDDF4E\", +\"[. c #FCDA3C\", +\"}. c #FCD52E\", +\"|. c #FAD026\", +\"1. c #4662A2\", +\"2. c #465A8D\", +\"3. c #3F6CBA\", +\"4. c #3A68B7\", +\"5. c #2E529E\", +\"6. c #2655AC\", +\"7. c #F0DC69\", +\"8. c #FBF78C\", +\"9. c #FFF880\", +\"0. c #FFF06B\", +\"a. c #FFE03E\", +\"b. c #FFD828\", +\"c. c #FED015\", +\"d. c #F5C40A\", +\"e. c #4B70B4\", +\"f. c #4870B7\", +\"g. c #3C5CA1\", +\"h. c #4070BF\", +\"i. c #3759A0\", +\"j. c #1D469C\", +\"k. c #214493\", +\"l. c #F2DD6C\", +\"m. c #F8EB7E\", +\"n. c #FBEE7A\", +\"o. c #FBE461\", +\"p. c #FADB48\", +\"q. c #FBD631\", +\"r. c #FED10F\", +\"s. c #FECD07\", +\"t. c #F1BD00\", +\"u. c #456AAE\", +\"v. c #4C7ECA\", +\"w. c #487AC8\", +\"x. c #35528F\", +\"y. c #1B4294\", +\"z. c #1B4193\", +\"A. c #F9EA83\", +\"B. c #FCF08E\", +\"C. c #F6E16E\", +\"D. c #F4D559\", +\"E. c #F5CF45\", +\"F. c #F6CB2E\", +\"G. c #F8C611\", +\"H. c #F6C005\", +\"I. c #E8B300\", +\"J. c #4268AE\", +\"K. c #4375C4\", +\"L. c #3F71C1\", +\"M. c #33569B\", +\"N. c #173F94\", +\"O. c #183A8B\", +\"P. c #F3E36E\", +\"Q. c #FCF7A1\", +\"R. c #FEF9A1\", +\"S. c #FEEE7D\", +\"T. c #FCE360\", +\"U. c #FAD946\", +\"V. c #F9D132\", +\"W. c #F8CD26\", +\"X. c #F7CA20\", +\"Y. c #3B589A\", +\"Z. c #395FA9\", +\"`. c #3359A5\", +\" + c #3056A3\", +\".+ c #2B468D\", +\"++ c #0A3897\", +\"@+ c #E6D465\", +\"#+ c #FDFA90\", +\"$+ c #FFF885\", +\"%+ c #FFF074\", +\"&+ c #FFEA60\", +\"*+ c #FFE246\", +\"=+ c #FFDC31\", +\"-+ c #FED51F\", +\";+ c #F7CB14\", +\">+ c #173788\", +\",+ c #063494\", +\"'+ c #E8DE7B\", +\")+ c #FFFA86\", +\"!+ c #FFF26A\", +\"~+ c #FFE84F\", +\"{+ c #FFD415\", +\"]+ c #FDCC04\", +\"^+ c #F3C001\", +\"/+ c #EBB600\", +\"(+ c #E3AF01\", +\"_+ c #D7A100\", +\":+ c #2D3E7F\", +\"<+ c #033396\", +\"[+ c #CFB954\", +\"}+ c #DBC347\", +\"|+ c #DEBF2C\", +\"1+ c #DFB718\", +\"2+ c #DFB206\", +\"3+ c #D6A505\", +\"4+ c #C6970A\", +\"5+ c #B48413\", +\"6+ c #374682\", +\"7+ c #023398\", +\"8+ c #0E3287\", +\"9+ c #253775\", +\"0+ c #05318F\", +\"a+ c #10358B\", +\"b+ c #183888\", +\"c+ c #053495\", +\"d+ c #0E348D\", +\"e+ c #183585\", +\" . . . . . . . \", +\" . . + @ # $ % & * . . . \", +\" . = - ; > , ' ) ! ~ { ] ^ . \", +\". / ( _ : ; < [ } | 1 2 3 4 5 . \", +\". 6 7 8 9 > , ' ) ! ~ 0 ] a b . \", +\". c d 8 9 > , ' ) ! e 0 ] a f . \", +\". g h i j k l m | n o p q r s . \", +\". t u v w x y z 2 A B C D E F . \", +\". G H I J K L M N O P Q R F F . \", +\". S T U V W p X Y Z ` ...+.@.. . . . . \", +\". #.$.%.&.*.=.-.;.>.. . ,.'.. ).!.~.{.].. \", +\". ^./.(._.:.<.[.}.|.. 1.. . 2.3.4.. . 5.6.. \", +\". 7.8.9.0.) a.b.c.d.. e.f.g.h.i.. . j.k.. \", +\". l.m.n.o.p.q.r.s.t.. u.v.w.x.. . y.z.. \", +\". A.B.C.D.E.F.G.H.I.. J.K.L.M.. . N.O.. \", +\". P.Q.R.S.T.U.V.W.X.. Y.Z.`. +.+. . ++. \", +\". @+#+$+%+&+*+=+-+;+. . . . . . . . >+,+. \", +\" . '+)+!+~+{ {+]+^+/+(+_+. . :+<+. \", +\" . . [+}+|+1+2+3+4+5+. . 6+7+8+. \", +\" . . . . . . . . . 9+0+a+. \", +\" . b+c+d+. \", +\" . e+. . \", +\" . \", +\" \"}; +" + "XPM format image used as Rollback icon") + +(defconst sqlplus-commit-xpm "\ +/* XPM */ +static char * commit_xpm[] = { +\"24 24 276 2\", +\" c None\", +\". c #000000\", +\"+ c #FDF57D\", +\"@ c #FFF676\", +\"# c #FFF36C\", +\"$ c #FFF05D\", +\"% c #FFEB51\", +\"& c #FFE445\", +\"* c #FDDC35\", +\"= c #EFEA85\", +\"- c #FBF68D\", +\"; c #FCF482\", +\"> c #FCF178\", +\", c #FCEE6E\", +\"' c #FCEB66\", +\") c #FCE85B\", +\"! c #FCE551\", +\"~ c #FDE147\", +\"{ c #FDDF3D\", +\"] c #FEDD2D\", +\"^ c #FCD621\", +\"/ c #E5BF16\", +\"( c #D8D479\", +\"_ c #FCF587\", +\": c #FAEF78\", +\"< c #FAEA6B\", +\"[ c #FAEA6A\", +\"} c #FAE968\", +\"| c #FAE967\", +\"1 c #FAE865\", +\"2 c #FAE864\", +\"3 c #FDDD3C\", +\"4 c #FED621\", +\"5 c #FFD51D\", +\"6 c #FFD51B\", +\"7 c #FFD519\", +\"8 c #D8B82B\", +\"9 c #FCF790\", +\"0 c #FBF587\", +\"a c #F8EF7D\", +\"b c #F8EC75\", +\"c c #F7E86B\", +\"d c #F8E868\", +\"e c #F9E663\", +\"f c #F9E45A\", +\"g c #F9E253\", +\"h c #F9E04C\", +\"i c #FBDD40\", +\"j c #FBDB38\", +\"k c #FAD933\", +\"l c #FAD529\", +\"m c #FDD810\", +\"n c #FFFD9E\", +\"o c #FFFF9A\", +\"p c #FFFE96\", +\"q c #FFFB8C\", +\"r c #FFF781\", +\"s c #FFF375\", +\"t c #FFEF69\", +\"u c #FFEA5B\", +\"v c #FFE750\", +\"w c #FFE345\", +\"x c #FFDF38\", +\"y c #FFDB2B\", +\"z c #FFD81F\", +\"A c #FFD313\", +\"B c #FBD007\", +\"C c #FBF090\", +\"D c #FFFDAE\", +\"E c #FFFEA2\", +\"F c #FFFA8C\", +\"G c #FFF780\", +\"H c #F6CA11\", +\"I c #E1AF03\", +\"J c #F4E36D\", +\"K c #FCF7A4\", +\"L c #FFFEBB\", +\"M c #FEFAA6\", +\"N c #FFF990\", +\"O c #FFF57E\", +\"P c #FFEE6F\", +\"Q c #FFEB61\", +\"R c #FFE856\", +\"S c #FFE34A\", +\"T c #FBDD44\", +\"U c #F7D535\", +\"V c #EBBF13\", +\"W c #D5A406\", +\"X c #C99500\", +\"Y c #F0DC5F\", +\"Z c #F3E772\", +\"` c #F7EC76\", +\" . c #F6E56D\", +\".. c #F6E369\", +\"+. c #F6E264\", +\"@. c #F5DF5C\", +\"#. c #F3DB53\", +\"$. c #F3D849\", +\"%. c #EFD245\", +\"&. c #ECCE3F\", +\"*. c #E3B91F\", +\"=. c #D3A40B\", +\"-. c #C99600\", +\";. c #C69200\", +\">. c #EED95E\", +\",. c #EDDA60\", +\"'. c #F1DF64\", +\"). c #F2DF5E\", +\"!. c #F2DD57\", +\"~. c #F2D94E\", +\"{. c #F2D644\", +\"]. c #EFD038\", +\"^. c #ECCB34\", +\"/. c #E6C430\", +\"(. c #DFB71F\", +\"_. c #D9AD17\", +\":. c #CC9907\", +\"<. c #C69000\", +\"[. c #D39E00\", +\"}. c #BB1503\", +\"|. c #F9EA7D\", +\"1. c #F6E57A\", +\"2. c #F5E370\", +\"3. c #F5DE62\", +\"4. c #F9DF52\", +\"5. c #FBDB3E\", +\"6. c #FCD526\", +\"7. c #FCCE0F\", +\"8. c #F7C50A\", +\"9. c #EEBA08\", +\"0. c #E2AB03\", +\"a. c #D7A000\", +\"b. c #D59D00\", +\"c. c #DFA901\", +\"d. c #E7B402\", +\"e. c #C91800\", +\"f. c #F6E676\", +\"g. c #FCF4A1\", +\"h. c #FDF096\", +\"i. c #FAE167\", +\"j. c #F7D64F\", +\"k. c #F7CF38\", +\"l. c #F7CB26\", +\"m. c #F6BF0C\", +\"n. c #F1B905\", +\"o. c #ECB309\", +\"p. c #EBB60A\", +\"q. c #F0BF0B\", +\"r. c #F3C206\", +\"s. c #E5B201\", +\"t. c #CF9C01\", +\"u. c #C21602\", +\"v. c #C21703\", +\"w. c #F2E067\", +\"x. c #FBF78F\", +\"y. c #FEF28A\", +\"z. c #FEED74\", +\"A. c #FFE85F\", +\"B. c #FFE24D\", +\"C. c #FFDE3A\", +\"D. c #FED92F\", +\"E. c #FCD325\", +\"F. c #F8CD1A\", +\"G. c #EDBD0A\", +\"H. c #D9A701\", +\"I. c #C79200\", +\"J. c #D11D00\", +\"K. c #EFDA64\", +\"L. c #F7EF7F\", +\"M. c #FCF47F\", +\"N. c #FDEE6C\", +\"O. c #FDE85B\", +\"P. c #FDE249\", +\"Q. c #FDDC36\", +\"R. c #FCD423\", +\"S. c #F9CC14\", +\"T. c #F0C10E\", +\"U. c #E6B507\", +\"V. c #DCA900\", +\"W. c #D29F00\", +\"X. c #C69400\", +\"Y. c #C99200\", +\"Z. c #CC1B02\", +\"`. c #C61A04\", +\" + c #E1CF5F\", +\".+ c #EAD862\", +\"++ c #ECDB63\", +\"@+ c #EFDC5E\", +\"#+ c #EFD955\", +\"$+ c #EFD74D\", +\"%+ c #EFD444\", +\"&+ c #F0D23E\", +\"*+ c #EECE37\", +\"=+ c #E8C731\", +\"-+ c #E0B922\", +\";+ c #D09E03\", +\">+ c #CB9700\", +\",+ c #C39100\", +\"'+ c #C99400\", +\")+ c #E12400\", +\"!+ c #F2E47C\", +\"~+ c #F8ED8C\", +\"{+ c #F4E171\", +\"]+ c #F0D65B\", +\"^+ c #F0D24F\", +\"/+ c #F1CF43\", +\"(+ c #F2CD34\", +\"_+ c #F2C824\", +\":+ c #EEC527\", +\"<+ c #E7BD23\", +\"[+ c #DFAC12\", +\"}+ c #DAA203\", +\"|+ c #E5B202\", +\"1+ c #EDBA01\", +\"2+ c #D69F00\", +\"3+ c #D21E01\", +\"4+ c #D01C00\", +\"5+ c #F2E16A\", +\"6+ c #FBF59D\", +\"7+ c #FEFBAA\", +\"8+ c #FEF084\", +\"9+ c #FCE567\", +\"0+ c #FBDD50\", +\"a+ c #F8D23B\", +\"b+ c #F8CD28\", +\"c+ c #EEB51C\", +\"d+ c #DA8A13\", +\"e+ c #E29A16\", +\"f+ c #EDB111\", +\"g+ c #E5AE08\", +\"h+ c #D19C01\", +\"i+ c #C79400\", +\"j+ c #BF1603\", +\"k+ c #DD2300\", +\"l+ c #E6D261\", +\"m+ c #FCF88C\", +\"n+ c #FFF27A\", +\"o+ c #FFEC6A\", +\"p+ c #FFE655\", +\"q+ c #FFE041\", +\"r+ c #FFDA2B\", +\"s+ c #E49D14\", +\"t+ c #BA4F02\", +\"u+ c #BB6A00\", +\"v+ c #B37102\", +\"w+ c #DD2200\", +\"x+ c #CA1B02\", +\"y+ c #E6DB78\", +\"z+ c #FEFB8B\", +\"A+ c #FFF470\", +\"B+ c #FFEA56\", +\"C+ c #FFE13E\", +\"D+ c #FFDA24\", +\"E+ c #FECF0A\", +\"F+ c #F5BE01\", +\"G+ c #D37800\", +\"H+ c #D72000\", +\"I+ c #C61802\", +\"J+ c #EBD55C\", +\"K+ c #FCE353\", +\"L+ c #FFE33E\", +\"M+ c #FFDB26\", +\"N+ c #FFD20B\", +\"O+ c #FCCB01\", +\"P+ c #F0B900\", +\"Q+ c #D47D00\", +\"R+ c #E42500\", +\"S+ c #EB2900\", +\"T+ c #DF2301\", +\"U+ c #E82700\", +\"V+ c #D31F04\", +\"W+ c #C71F01\", +\"X+ c #EA2800\", +\"Y+ c #E92800\", +\"Z+ c #DD2301\", +\"`+ c #E22501\", +\" . . . . . . . \", +\" . . . + @ # $ % & * . . . \", +\" . = - ; > , ' ) ! ~ { ] ^ / . \", +\". ( _ : < [ } | 1 2 3 4 5 6 7 8 . \", +\". 9 0 a b c d e f g h i j k l m . \", +\". n o p q r s t u v w x y z A B . \", +\". C D E F G s t u v w x y z H I . \", +\". J K L M N O P Q R S T U V W X . \", +\". Y Z ` ...+.@.#.$.%.&.*.=.-.;.. . . \", +\". >.,.'.).!.~.{.].^./.(._.:.<.[.. . }.. \", +\". |.1.2.3.4.5.6.7.8.9.0.a.b.c.d.. . e.. \", +\". f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.. . u.v.. \", +\". w.x.n y.z.A.B.C.D.E.F.G.H.-.I.. . J.. \", +\". K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.. . Z.`.. \", +\". +.+++@+#+$+%+&+*+=+-+;+>+,+'+. . )+. \", +\". !+~+{+]+^+/+(+_+:+<+[+}+|+1+2+. . 3+4+. \", +\". 5+6+7+8+9+0+a+b+c+d+e+f+g+h+i+. j+k+. \", +\". l+m+q n+o+p+q+r+s+. . . t+u+v+. w+x+. \", +\" . y+z+A+B+C+D+E+F+G+. H+. . . I+)+. \", +\" . . J+K+L+M+N+O+P+Q+. R+S+T+U+V+. \", +\" . . . . . . . . . . W+X+Y+. \", +\" . Z+`+. \", +\" . . \", +\" . \"}; +" + "XPM format image used as Commit icon") + +(defconst plsql-prev-mark-xpm "\ +/* XPM */ +static char * go_previous_xpm[] = { +\"24 24 59 1\", +\" c None\", +\". c #000000\", +\"+ c #355D96\", +\"@ c #3C639B\", +\"# c #6E92BF\", +\"$ c #41679D\", +\"% c #6990BE\", +\"& c #6D94C2\", +\"* c #456DA2\", +\"= c #628BBC\", +\"- c #4D7BB4\", +\"; c #6991C0\", +\"> c #4971A6\", +\", c #5D87BA\", +\"' c #4B7BB3\", +\") c #4979B3\", +\"! c #5884B9\", +\"~ c #638CBC\", +\"{ c #638BBC\", +\"] c #6089BA\", +\"^ c #4B73A9\", +\"/ c #5883B8\", +\"( c #4A7AB3\", +\"_ c #618ABB\", +\": c #4C74AB\", +\"< c #547FB5\", +\"[ c #4972A9\", +\"} c #4D79B1\", +\"| c #4171AD\", +\"1 c #4071AD\", +\"2 c #4070AD\", +\"3 c #4171AC\", +\"4 c #4071AC\", +\"5 c #4070AC\", +\"6 c #3F70AC\", +\"7 c #3F70AB\", +\"8 c #406FAC\", +\"9 c #5781B5\", +\"0 c #4A74AC\", +\"a c #3E6CA8\", +\"b c #3465A4\", +\"c c #4E78AF\", +\"d c #446FA8\", +\"e c #4A75AD\", +\"f c #3F6CA6\", +\"g c #3C6BA7\", +\"h c #3B6BA7\", +\"i c #4471AB\", +\"j c #4572AB\", +\"k c #4672AC\", +\"l c #4571AB\", +\"m c #3A68A3\", +\"n c #3B6AA7\", +\"o c #406EA9\", +\"p c #3564A0\", +\"q c #3868A6\", +\"r c #305E9D\", +\"s c #3767A5\", +\"t c #2E5D9B\", +\" \", +\" \", +\" \", +\" .. \", +\" .+. \", +\" .@#. \", +\" .$%&. \", +\" .*=-;......... \", +\" .>,')!~{{{{{~]. \", +\" .^/()))(((((('_. \", +\" .:<)))))))))))),. \", +\" .[}|1123455567589. \", +\" .0abbbbbbbbbbbbc. \", +\" .dabbbbbbbbbbbe. \", +\" .fgbbhijjjjjkl. \", +\" .mnbo......... \", +\" .pqh. \", +\" .rs. \", +\" .t. \", +\" .. \", +\" . \", +\" \", +\" \", +\" \"}; +" + "XPM format image used as Previous Mark icon") + +(defconst plsql-next-mark-xpm "\ +/* XPM */ +static char * go_next_xpm[] = { +\"24 24 63 1\", +\" c None\", +\". c #000000\", +\"+ c #365F97\", +\"@ c #6B8FBE\", +\"# c #41689E\", +\"$ c #6990BF\", +\"% c #466EA4\", +\"& c #678EBD\", +\"* c #4E7DB5\", +\"= c #638CBC\", +\"- c #4B72A7\", +\"; c #5B83B5\", +\"> c #628BBB\", +\", c #5A86BA\", +\"' c #4979B3\", +\") c #4B7AB3\", +\"! c #5E87B9\", +\"~ c #4E76AA\", +\"{ c #5B84B8\", +\"] c #4E7CB5\", +\"^ c #4A7AB3\", +\"/ c #5883B7\", +\"( c #5178AD\", +\"_ c #5982B6\", +\": c #4C7BB4\", +\"< c #537FB5\", +\"[ c #5079AE\", +\"} c #507BB0\", +\"| c #4272AD\", +\"1 c #4070AC\", +\"2 c #3F70AB\", +\"3 c #3F70AC\", +\"4 c #4071AC\", +\"5 c #4171AC\", +\"6 c #4070AD\", +\"7 c #4071AD\", +\"8 c #4171AD\", +\"9 c #4D79B1\", +\"0 c #4E76AD\", +\"a c #4872AA\", +\"b c #3767A5\", +\"c c #3465A4\", +\"d c #3D6CA8\", +\"e c #4C76AD\", +\"f c #2B548E\", +\"g c #446FA8\", +\"h c #3C6BA7\", +\"i c #4772AA\", +\"j c #29528E\", +\"k c #3F6CA6\", +\"l c #4471AB\", +\"m c #4371AB\", +\"n c #3B6BA7\", +\"o c #416EA8\", +\"p c #3F6CA7\", +\"q c #3A69A6\", +\"r c #3C6AA5\", +\"s c #3B6AA5\", +\"t c #3868A6\", +\"u c #3765A2\", +\"v c #3666A3\", +\"w c #32619F\", +\"x c #2F5D9B\", +\" \", +\" \", +\" \", +\" .. \", +\" .+. \", +\" .@#. \", +\" .$$%. \", +\" .........&*=-. \", +\" .;>>>>>>=,')!~. \", +\" .{]^^^^^^''''/(. \", +\" ._:'''''''''''<[. \", +\" .}|12311145677890. \", +\" .abcccccccccccde. \", +\" .gbcccccccccchi. \", +\" .klmlllllhccno. \", +\" .........pcqr. \", +\" .stu. \", +\" .vw. \", +\" .x. \", +\" .. \", +\" . \", +\" \", +\" \", +\" \"}; +" + "XPM format image used as Next Mark icon") + +(defconst sqlplus-kill-image + (create-image sqlplus-kill-xpm 'xpm t)) + +(defconst sqlplus-cancel-image + (create-image sqlplus-cancel-xpm 'xpm t)) + +(defconst sqlplus-commit-image + (create-image sqlplus-commit-xpm 'xpm t)) + +(defconst sqlplus-rollback-image + (create-image sqlplus-rollback-xpm 'xpm t)) + +(defconst plsql-prev-mark-image + (create-image plsql-prev-mark-xpm 'xpm t)) + +(defconst plsql-next-mark-image + (create-image plsql-next-mark-xpm 'xpm t)) + +(defvar sqlplus-mode-syntax-table nil + "Syntax table used while in sqlplus-mode.") + +(defvar sqlplus-suppress-show-output-buffer nil) + +;; Local in input buffers +(defvar sqlplus-font-lock-keywords-1 nil) +(make-variable-buffer-local 'sqlplus-font-lock-keywords-1) +(defvar sqlplus-font-lock-keywords-2 nil) +(make-variable-buffer-local 'sqlplus-font-lock-keywords-2) +(defvar sqlplus-font-lock-keywords-3 nil) +(make-variable-buffer-local 'sqlplus-font-lock-keywords-3) + +(defvar sqlplus-font-lock-defaults '((sqlplus-font-lock-keywords-1 sqlplus-font-lock-keywords-2 sqlplus-font-lock-keywords-3) nil t nil nil)) + +(defvar sqlplus-oracle-extra-builtin-functions-re + (concat "\\b" + (regexp-opt '("acos" "asciistr" "asin" "atan" "atan2" "bfilename" "bin_to_num" "bitand" "cardinality" "cast" "coalesce" "collect" + "compose" "corr" "corr_s" "corr_k" "covar_pop" "covar_samp" "cume_dist" "current_date" "current_timestamp" "cv" + "dbtimezone" "decompose" "dense_rank" "depth" "deref" "empty_blob, empty_clob" "existsnode" "extract" + "extractvalue" "first" "first_value" "from_tz" "group_id" "grouping" "grouping_id" "iteration_number" + "lag" "last" "last_value" "lead" "lnnvl" "localtimestamp" "make_ref" "median" "nanvl" "nchr" "nls_charset_decl_len" + "nls_charset_id" "nls_charset_name" "ntile" "nullif" "numtodsinterval" "numtoyminterval" "nvl2" "ora_hash" "path" + "percent_rank" "percentile_cont" "percentile_disc" "powermultiset" "powermultiset_by_cardinality" "presentnnv" + "presentv" "previous" "rank" "ratio_to_report" "rawtonhex" "ref" "reftohex" "regexp_instr" "regexp_replace" + "regexp_substr" "regr_slope" "regr_intercept" "regr_count" "regr_r2" "regr_avgx" "regr_avgy" "regr_sxx" "regr_syy" + "regr_sxy" "remainder" "row_number" "rowidtonchar" "scn_to_timestamp" "sessiontimezone" "stats_binomial_test" + "stats_crosstab" "stats_f_test" "stats_ks_test" "stats_mode" "stats_mw_test" "stats_one_way_anova" "stats_t_test_one" + "stats_t_test_paired" "stats_t_test_indep" "stats_t_test_indepu" "stats_wsr_test" "stddev_pop" "stddev_samp" + "sys_connect_by_path" "sys_context" "sys_dburigen" "sys_extract_utc" "sys_guid" "sys_typeid" "sys_xmlagg" "sys_xmlgen" + "systimestamp" "timestamp_to_scn" "to_binary_double" "to_binary_float" "to_clob" "to_dsinterval" "to_lob" "to_nchar" + "to_nclob" "to_timestamp" "to_timestamp_tz" "to_yminterval" "treat" "tz_offset" "unistr" "updatexml" "value" "var_pop" + "var_samp" "width_bucket" "xmlagg" "xmlcolattval" "xmlconcat" "xmlelement" "xmlforest" "xmlsequence" "xmltransform") t) + "\\b")) +(defvar sqlplus-oracle-extra-warning-words-re + (concat "\\b" + (regexp-opt '("access_into_null" "case_not_found" "collection_is_null" "rowtype_mismatch" + "self_is_null" "subscript_beyond_count" "subscript_outside_limit" "sys_invalid_rowid") t) + "\\b")) +(defvar sqlplus-oracle-extra-keywords-re + (concat "\\b\\(" + "\\(at\\s-+local\\|at\\s-+time\\s-+zone\\|to\\s-+second\\|to\\s-+month\\|is\\s-+present\\|a\\s-+set\\)\\|" + (regexp-opt '("case" "nan" "infinite" "equals_path" "empty" "likec" "like2" "like4" "member" + "regexp_like" "submultiset" "under_path" "mlslabel") t) + "\\)\\b")) +(defvar sqlplus-oracle-extra-pseudocolumns-re + (concat "\\b" + (regexp-opt '("connect_by_iscycle" "connect_by_isleaf" "versions_starttime" "versions_startscn" + "versions_endtime" "versions_endscn" "versions_xid" "versions_operation" "object_id" "object_value" "ora_rowscn" + "xmldata") t) + "\\b")) +(defvar sqlplus-oracle-plsql-extra-reserved-words-re + (concat "\\b" + (regexp-opt '("array" "at" "authid" "bulk" "char_base" "day" "do" "extends" "forall" "heap" "hour" + "interface" "isolation" "java" "limited" "minute" "mlslabel" "month" "natural" "naturaln" "nocopy" "number_base" + "ocirowid" "opaque" "operator" "organization" "pls_integer" "positive" "positiven" "range" "record" "release" "reverse" + "rowtype" "second" "separate" "space" "sql" "timezone_region" "timezone_abbr" "timezone_minute" "timezone_hour" "year" + "zone") t) + "\\b")) +(defvar sqlplus-oracle-extra-types-re + (concat "\\b" + (regexp-opt '("nvarchar2" "binary_float" "binary_double" "timestamp" "interval" "interval_day" "urowid" "nchar" "clob" "nclob" "bfile") t) + "\\b")) + +(defvar sqlplus-commands-regexp-1 nil) +(defvar sqlplus-commands-regexp-23 nil) +(defvar sqlplus-system-variables-regexp-1 nil) +(defvar sqlplus-system-variables-regexp-23 nil) +(defvar sqlplus-v22-commands-font-lock-keywords-1 nil) +(defvar sqlplus-v22-commands-font-lock-keywords-23 nil) +(defvar font-lock-sqlplus-face nil) + +(defvar sqlplus-output-buffer-keymap nil + "Local in output buffer.") +(make-variable-buffer-local 'sqlplus-output-buffer-keymap) + +(defvar sqlplus-kill-function-inhibitor nil) + +(defvar sqlplus-slip-separator-width 2 + "Only for classic table style.") + +(defvar sqlplus-user-string-history nil) + +(defvar sqlplus-object-types '( "CONSUMER GROUP" "SEQUENCE" "SCHEDULE" "PROCEDURE" "OPERATOR" "WINDOW" + "PACKAGE" "LIBRARY" "PROGRAM" "PACKAGE BODY" "JAVA RESOURCE" "XML SCHEMA" + "JOB CLASS" "TRIGGER" "TABLE" "SYNONYM" "VIEW" "FUNCTION" "WINDOW GROUP" + "JAVA CLASS" "INDEXTYPE" "INDEX" "TYPE" "EVALUATION CONTEXT" )) + +(defvar sqlplus-end-of-source-sentinel "%%@@end-of-source-sentinel@@%%") + +(defconst sqlplus-system-variables + '("appi[nfo]" "array[size]" "auto[commit]" "autop[rint]" "autorecovery" "autot[race]" "blo[ckterminator]" "cmds[ep]" + "colsep" "com[patibility]" "con[cat]" "copyc[ommit]" "copytypecheck" "def[ine]" "describe" "echo" "editf[ile]" + "emb[edded]" "esc[ape]" "feed[back]" "flagger" "flu[sh]" "hea[ding]" "heads[ep]" "instance" "lin[esize]" + "lobof[fset]" "logsource" "long" "longc[hunksize]" "mark[up]" "newp[age]" "null" "numf[ormat]" "num[width]" + "pages[ize]" "pau[se]" "recsep" "recsepchar" "serverout[put]" "shift[inout]" "show[mode]" "sqlbl[anklines]" + "sqlc[ase]" "sqlco[ntinue]" "sqln[umber]" "sqlpluscompat[ibility]" "sqlpre[fix]" "sqlp[rompt]" "sqlt[erminator]" + "suf[fix]" "tab" "term[out]" "ti[me]" "timi[ng]" "trim[out]" "trims[pool]" "und[erline]" "ver[ify]" "wra[p]")) + +(defconst sqlplus-commands + '(("@[@]") + (("/" "r[un]")) + ("acc[ept]" + (font-lock-type-face "num[ber]" "char" "date" "binary_float" "binary_double") + (font-lock-keyword-face "for[mat]" "def[ault]" "[no]prompt" "hide")) + ("a[ppend]") + ("archive log" + (font-lock-keyword-face "list" "stop" "start" "next" "all" "to")) + ("attribute" + (font-lock-keyword-face "ali[as]" "cle[ar]" "for[mat]" "like" "on" "off")) + ("bre[ak]" + (font-lock-keyword-face "on" "row" "report" "ski[p]" "page" "nodup[licates]" "dup[licates]")) + ("bti[tle]" + (font-lock-keyword-face "on" "off") + (font-lock-builtin-face "bold" "ce[nter]" "col" "format" "le[ft]" "r[ight]" "s[kip]" "tab")) + ("c[hange]") + ("cl[ear]" + (font-lock-keyword-face "bre[aks]" "buff[er]" "col[umns]" "comp[utes]" "scr[een]" "sql" "timi[ng]")) + ("col[umn]" + (font-lock-keyword-face "ali[as]" "cle[ar]" "entmap" "on" "off" "fold_a[fter]" "fold_b[efore]" "for[mat]" "hea[ding]" + "jus[tify]" "l[eft]" "c[enter]" "r[ight]" "like" "newl[ine]" "new_v[alue]" "nopri[nt]" "pri[nt]" + "nul[l]" "old_v[alue]" "wra[pped]" "wor[d_wrapped]" "tru[ncated]")) + ("comp[ute]" + (font-lock-keyword-face "lab[el]" "of" "on" "report" "row") + (font-lock-builtin-face "avg" "cou[nt]" "min[imum]" "max[imum]" "num[ber]" "sum" "std" "var[iance]")) + ("conn[ect]" + (font-lock-keyword-face "as" "sysoper" "sysdba")) + ("copy") + ("def[ine]") + ("del" + (font-lock-keyword-face "last")) + ("desc[ribe]") + ("disc[onnect]") + ("ed[it]") + ("exec[ute]") + (("exit" "quit") + (font-lock-keyword-face "success" "failure" "warning" "commit" "rollback")) + ("get" + (font-lock-keyword-face "file" "lis[t]" "nol[ist]")) + ("help") + (("ho[st]" "!" "$")) + ("i[nput]") + ("l[ist]" + (font-lock-keyword-face "last")) + ("passw[ord]") + ("pau[se]") + ("pri[nt]") + ("pro[mpt]") + ("recover" + (font-lock-keyword-face "begin" "end" "backup" "automatic" "from" "logfile" "test" "allow" "corruption" "continue" "default" "cancel" + "standby" "database" "until" "time" "change" "using" "controlfile" "tablespace" "datafile" + "consistent" "with" "[no]parallel" "managed" "disconnect" "session" "[no]timeout" "[no]delay" "next" "no" "expire" + "current" "through" "thread" "sequence" "all" "archivelog" "last" "switchover" "immediate" "[no]wait" + "finish" "skip")) + ("rem[ark]") + ("repf[ooter]" + (font-lock-keyword-face "page" "on" "off") + (font-lock-builtin-face "bold" "ce[nter]" "col" "format" "le[ft]" "r[ight]" "s[kip]" "tab")) + ("reph[eader]" + (font-lock-keyword-face "page" "on" "off") + (font-lock-builtin-face "bold" "ce[nter]" "col" "format" "le[ft]" "r[ight]" "s[kip]" "tab")) + ("sav[e]" + (font-lock-keyword-face "file" "cre[ate]" "rep[lace]" "app[end]")) + ("set" + (font-lock-builtin-face sqlplus-system-variables) + (font-lock-keyword-face "on" "off" "immediate" "trace[only]" "explain" "statistics" "native" "v7" "v8" "all" "linenum" "indent" + "entry" "intermediate" "full" "local" "head" "html" "body" "table" "entmap" "spool" "[pre]format" + "none" "[word_]wrapped" "each" "truncated" "[in]visible" "mixed" "lower" "upper")) + ("sho[w]" + (font-lock-keyword-face "all" "bti[tle]" "err[ors]" "function" "procedure" "package[ body]" "trigger" "view" "type[ body]" + "dimension" "java class" "lno" "parameters" "pno" "recyc[lebin]" "rel[ease]" "repf[ooter]" "reph[eader]" + "sga" "spoo[l]" "sqlcode" "tti[tle]" "user") + (font-lock-builtin-face sqlplus-system-variables)) + ("shutdown" + (font-lock-keyword-face "abort" "immediate" "normal" "transactional" "local")) + ("spo[ol]" + ("cre" "create" "rep" "replace" "app" "append" "off" "out")) + ("sta[rt]") + ("startup" + (font-lock-keyword-face "force" "restrict" "pfile" "quiet" "mount" "open" "nomount" "read" "only" "write" "recover")) + ("store" + (font-lock-keyword-face "set" "cre[ate]" "rep[lace]" "app[end]")) + ("timi[ng]" + (font-lock-keyword-face "start" "show" "stop")) + ("tti[tle]" + (font-lock-keyword-face "tti[tle]" "on" "off") + (font-lock-builtin-face "bold" "ce[nter]" "col" "format" "le[ft]" "r[ight]" "s[kip]" "tab")) + ("undef[ine]") + ("var[iable]" + (font-lock-type-face "number" "[n]char" "byte" "[n]varchar2" "[n]clob" "refcursor" "binary_float" "binary_double")) + ("whenever oserror" + (font-lock-keyword-face "exit" "success" "failure" "commit" "rollback" "continue" "commit" "rollback" "none")) + ("whenever sqlerror" + (font-lock-keyword-face "exit" "success" "failure" "warning" "commit" "rollback" "continue" "none")))) + +(defvar plsql-mode-map nil) + +(defstruct sqlplus-global-struct + font-lock-regexps + objects-alist + side-view-buffer + root-dir +) + +(defvar sqlplus-global-structures (make-hash-table :test 'equal) + "Connect string -> sqlplus-global-struct") + +(defun sqlplus-get-objects-alist (&optional connect-string) + (let ((struct (gethash (car (refine-connect-string (or connect-string sqlplus-connect-string sqlplus-process-p))) + sqlplus-global-structures))) + (when struct + (sqlplus-global-struct-objects-alist struct)))) + +(defun sqlplus-set-objects-alist (objects-alist &optional connect-string) + (let ((struct (gethash (car (refine-connect-string (or connect-string sqlplus-connect-string sqlplus-process-p))) + sqlplus-global-structures))) + (when struct + (setf (sqlplus-global-struct-objects-alist struct) objects-alist)))) + +(defun sqlplus-get-font-lock-regexps (&optional connect-string) + (let ((struct (gethash (car (refine-connect-string (or connect-string sqlplus-connect-string sqlplus-process-p))) + sqlplus-global-structures))) + (when struct + (sqlplus-global-struct-font-lock-regexps struct)))) + +(defun sqlplus-set-font-lock-regexps (font-lock-regexps &optional connect-string) + (let ((struct (gethash (car (refine-connect-string (or connect-string sqlplus-connect-string sqlplus-process-p))) + sqlplus-global-structures))) + (when struct + (setf (sqlplus-global-struct-font-lock-regexps struct) font-lock-regexps)))) + +(defun sqlplus-get-side-view-buffer (&optional connect-string) + (let ((struct (gethash (car (refine-connect-string (or connect-string sqlplus-connect-string sqlplus-process-p))) + sqlplus-global-structures))) + (when struct + (sqlplus-global-struct-side-view-buffer struct)))) + +(defun sqlplus-get-root-dir (&optional connect-string) + (let ((struct (gethash (car (refine-connect-string (or connect-string sqlplus-connect-string sqlplus-process-p))) + sqlplus-global-structures))) + (when struct + (sqlplus-global-struct-root-dir struct)))) + +(defun sqlplus-set-root-dir (root-dir &optional connect-string) + (let ((struct (gethash (car (refine-connect-string (or connect-string sqlplus-connect-string sqlplus-process-p))) + sqlplus-global-structures))) + (when struct + (setf (sqlplus-global-struct-root-dir struct) root-dir)))) + +;;; --- + +(defun sqlplus-initial-strings () + (append sqlplus-initial-strings + (list + (concat "btitle left '" sqlplus-page-separator "'") + (concat "repfooter left '" sqlplus-repfooter "'") + (concat "set pagesize " (number-to-string sqlplus-pagesize))))) + +(defun sqlplus-connect-string-lessp (cs1 cs2) + "Compare two connect strings" + (let ((cs1-pair (split-string cs1 "@")) + (cs2-pair (split-string cs2 "@"))) + (or (string< (cadr cs1-pair) (cadr cs2-pair)) + (and (string= (cadr cs1-pair) (cadr cs2-pair)) + (string< (car cs1-pair) (car cs2-pair)))))) + +(defun sqlplus-divide-connect-strings () + "Returns (active-connect-string-list . inactive-connect-string-list)" + (let* ((active-connect-strings + (sort (delq nil (mapcar (lambda (buffer) + (with-current-buffer buffer + (when (and (eq major-mode 'sqlplus-mode) + sqlplus-connect-string) + (let ((cs (car (refine-connect-string sqlplus-connect-string)))) + (when (and (get-buffer (sqlplus-get-process-buffer-name cs)) + (get-process (sqlplus-get-process-name cs))) + (downcase cs)))))) + (buffer-list))) + 'sqlplus-connect-string-lessp)) + (inactive-connect-strings + (sort (delq nil (mapcar (lambda (pair) + (unless (member (downcase (car pair)) active-connect-strings) (downcase (car pair))) ) + sqlplus-connect-strings-alist)) + 'sqlplus-connect-string-lessp))) + (setq active-connect-strings (remove-duplicates active-connect-strings :test 'equal)) + (setq inactive-connect-strings (remove-duplicates inactive-connect-strings :test 'equal)) + (cons active-connect-strings inactive-connect-strings))) + +(defun sqlplus-connections-menu (menu) + (condition-case err + (let* ((connect-strings-pair (sqlplus-divide-connect-strings)) + (active-connect-strings (car connect-strings-pair)) + (inactive-connect-strings (cdr connect-strings-pair))) + (append + (list ["New connection..." sqlplus t]) + (list ["Tnsnames.ora" sqlplus-find-tnsnames t]) + (list ["Command Line" sqlplus-command-line t]) + (when (eq major-mode 'sqlplus-mode) + (list + "----" + ["Evaluate Statement" sqlplus-send-current sqlplus-connect-string] + ["Explain Statement" sqlplus-explain sqlplus-connect-string] + ["Evaluate Statement (HTML)" sqlplus-send-current-html sqlplus-connect-string] + ["Evaluate Region" sqlplus-send-region (and (mark) sqlplus-connect-string)])) + (when orcl-mode + (list + "----" + ["Send Commit" sqlplus-send-commit sqlplus-connect-string] + ["Send Rollback" sqlplus-send-rollback sqlplus-connect-string] + ["Restart Connection" sqlplus-restart-connection sqlplus-connect-string] + ["Show History" sqlplus-show-history sqlplus-connect-string] + ["Get Source from DB" sqlplus-get-source sqlplus-connect-string] + ["Interrupt Evaluation" sqlplus-send-interrupt sqlplus-connect-string] + ["Compare schema to filesystem" sqlplus-compare-schema-to-filesystem sqlplus-connect-string] + "----" + (list "Output" + ["Show window" sqlplus-buffer-display-window t] + "----" + ["Redisplay" sqlplus-buffer-redisplay-current t] + ["Previous" sqlplus-buffer-prev-command t] + ["Next" sqlplus-buffer-next-command t] + "----" + ["Scroll Right" sqlplus-buffer-scroll-right t] + ["Scroll Left" sqlplus-buffer-scroll-left t] + ["Scroll Down" sqlplus-buffer-scroll-down t] + ["Scroll Up" sqlplus-buffer-scroll-up t] + "----" + ["Bottom" sqlplus-buffer-bottom t] + ["Top" sqlplus-buffer-top t] + "----" + ["Erase" sqlplus-buffer-erase t]) + )) + (when inactive-connect-strings + (append + (list "----") + (list (append (list "Recent Connections") + (mapcar (lambda (connect-string) + (vector connect-string (list 'apply ''sqlplus + (list 'sqlplus-read-connect-string connect-string)) t)) inactive-connect-strings))))) + (when active-connect-strings + (append + (list "----") + (mapcar (lambda (connect-string) + (vector connect-string (list 'apply ''sqlplus + (list 'sqlplus-read-connect-string connect-string)) t)) active-connect-strings))) + )) + (error (message (error-message-string err))))) + +(defun sqlplus-send-commit () + "Send 'commit' command to SQL*Process." + (interactive) + (sqlplus-check-connection) + (sqlplus-execute sqlplus-connect-string "commit;" nil nil)) + +(defun sqlplus-send-rollback () + "Send 'rollback' command to SQL*Process." + (interactive) + (sqlplus-check-connection) + (sqlplus-execute sqlplus-connect-string "rollback;" nil nil)) + +(defun sqlplus-show-history () + "Show command history for current connection." + (interactive) + (sqlplus-check-connection) + (sqlplus-verify-buffer sqlplus-connect-string) + (switch-to-buffer (sqlplus-get-history-buffer sqlplus-connect-string))) + +(defun sqlplus-restart-connection () + "Kill SQL*Plus process and start again." + (interactive) + (sqlplus-check-connection) + (sqlplus-verify-buffer sqlplus-connect-string) + (let ((connect-stringos sqlplus-connect-string)) + (unwind-protect + (progn + (setq sqlplus-kill-function-inhibitor t) + (sqlplus-shutdown connect-stringos t)) + (setq sqlplus-kill-function-inhibitor nil)) + (sqlplus connect-stringos (sqlplus-get-input-buffer-name connect-stringos)))) + +(define-skeleton plsql-begin + "begin..end skeleton" + "" ; interactor + "begin" ?\n + > _ ?\n + "end;" >) + +(define-skeleton plsql-loop + "loop..end loop skeleton" + "" ; interactor + "loop" ?\n + > _ ?\n + "end loop;" >) + +(define-skeleton plsql-if + "if..end if skeleton" + "" ; interactor + "if " _ " then" ?\n + > ?\n + "end if;" >) + +;;; SQLPLUS-mode Keymap - + +(unless orcl-mode-map + (setq orcl-mode-map (make-sparse-keymap)) + (define-key orcl-mode-map "\C-c\C-o" 'sqlplus-buffer-display-window) + (define-key orcl-mode-map "\C-c\C-l" 'sqlplus-buffer-redisplay-current) + (define-key orcl-mode-map "\C-c\C-p" 'sqlplus-buffer-prev-command) + (define-key orcl-mode-map [C-S-up] 'sqlplus-buffer-prev-command) + (define-key orcl-mode-map "\C-c\C-n" 'sqlplus-buffer-next-command) + (define-key orcl-mode-map [C-S-down] 'sqlplus-buffer-next-command) + (define-key orcl-mode-map "\C-c\C-b" 'sqlplus-buffer-scroll-right) + (define-key orcl-mode-map [C-S-left] 'sqlplus-buffer-scroll-right) + (define-key orcl-mode-map "\C-c\C-f" 'sqlplus-buffer-scroll-left) + (define-key orcl-mode-map [C-S-right] 'sqlplus-buffer-scroll-left) + (define-key orcl-mode-map "\C-c\M-v" 'sqlplus-buffer-scroll-down) + (define-key orcl-mode-map "\C-c\C-v" 'sqlplus-buffer-scroll-up) + (define-key orcl-mode-map "\C-c>" 'sqlplus-buffer-bottom) + (define-key orcl-mode-map "\C-c<" 'sqlplus-buffer-top) + (define-key orcl-mode-map "\C-c\C-w" 'sqlplus-buffer-erase) + (define-key orcl-mode-map "\C-c\C-m" 'sqlplus-send-commit) + (define-key orcl-mode-map "\C-c\C-a" 'sqlplus-send-rollback) + (define-key orcl-mode-map "\C-c\C-k" 'sqlplus-restart-connection) + (define-key orcl-mode-map "\C-c\C-t" 'sqlplus-show-history) + (define-key orcl-mode-map "\C-c\C-s" 'sqlplus-get-source) + (define-key orcl-mode-map "\C-c\C-i" 'sqlplus-send-interrupt) + (define-key orcl-mode-map [S-return] 'sqlplus-send-user-string) + (define-key orcl-mode-map [tool-bar sqlplus-restart-connection] + (list 'menu-item "Restart connection" 'sqlplus-restart-connection :image sqlplus-kill-image)) + (define-key orcl-mode-map [tool-bar sqlplus-cancel] + (list 'menu-item "Cancel" 'sqlplus-send-interrupt :image sqlplus-cancel-image)) + (define-key orcl-mode-map [tool-bar sqlplus-rollback] + (list 'menu-item "Rollback" 'sqlplus-send-rollback :image sqlplus-rollback-image)) + (define-key orcl-mode-map [tool-bar sqlplus-commit] + (list 'menu-item "Commit" 'sqlplus-send-commit :image sqlplus-commit-image))) + +(unless sqlplus-mode-map + (setq sqlplus-mode-map (make-sparse-keymap)) + (define-key sqlplus-mode-map "\C-c\C-g" 'plsql-begin) + (define-key sqlplus-mode-map "\C-c\C-q" 'plsql-loop) + (define-key sqlplus-mode-map "\C-c\C-z" 'plsql-if) + (define-key sqlplus-mode-map "\C-c\C-r" 'sqlplus-send-region) + (define-key sqlplus-mode-map [C-return] 'sqlplus-send-current) + (define-key sqlplus-mode-map [M-return] 'sqlplus-explain) + (define-key sqlplus-mode-map "\C-c\C-e" 'sqlplus-send-current) + (define-key sqlplus-mode-map "\C-c\C-j" 'sqlplus-send-current-html) + (define-key sqlplus-mode-map [C-S-return] 'sqlplus-send-current-html) + (define-key sqlplus-mode-map "\M-." 'sqlplus-file-get-source) + (define-key sqlplus-mode-map [C-down-mouse-1] 'sqlplus-mouse-select-identifier) + (define-key sqlplus-mode-map [C-mouse-1] 'sqlplus-file-get-source-mouse) + ) + +(easy-menu-add-item nil nil sqlplus-connections-menu t) + +(unless sqlplus-mode-syntax-table + (setq sqlplus-mode-syntax-table (make-syntax-table)) + (modify-syntax-entry ?/ ". 14" sqlplus-mode-syntax-table) ; comment start + (modify-syntax-entry ?* ". 23" sqlplus-mode-syntax-table) + (modify-syntax-entry ?+ "." sqlplus-mode-syntax-table) + (modify-syntax-entry ?. "." sqlplus-mode-syntax-table) + (modify-syntax-entry ?\" "." sqlplus-mode-syntax-table) + (modify-syntax-entry ?\\ "." sqlplus-mode-syntax-table) + (modify-syntax-entry ?- ". 12b" sqlplus-mode-syntax-table) + (modify-syntax-entry ?\n "> b" sqlplus-mode-syntax-table) + (modify-syntax-entry ?= "." sqlplus-mode-syntax-table) + (modify-syntax-entry ?% "w" sqlplus-mode-syntax-table) + (modify-syntax-entry ?< "." sqlplus-mode-syntax-table) + (modify-syntax-entry ?> "." sqlplus-mode-syntax-table) + (modify-syntax-entry ?& "w" sqlplus-mode-syntax-table) + (modify-syntax-entry ?| "." sqlplus-mode-syntax-table) + (modify-syntax-entry ?_ "w" sqlplus-mode-syntax-table) ; _ is word char + (modify-syntax-entry ?\' "\"" sqlplus-mode-syntax-table)) + +;;; SQL*Plus mode + +(defun connect-string-to-string () + (let ((txt (or (car (refine-connect-string sqlplus-connect-string)) "disconnected")) + (result)) + (if (string-match "^\\(.*?\\)\\(\\w*prod\\w*\\)$" txt) + (if (>= emacs-major-version 22) + (setq result (list (list :propertize (substring txt 0 (match-beginning 2)) 'face '((:foreground "blue"))) + (list :propertize (substring txt (match-beginning 2)) 'face '((:foreground "red")(:weight bold))))) + (setq result (setq txt (propertize txt 'face '((:foreground "blue"))))) + (put-text-property (match-beginning 2) (match-end 2) 'face '((:foreground "red")(:weight bold)) txt)) + (setq result + (if (>= emacs-major-version 22) + (list :propertize txt 'face '((:foreground "blue"))) + (setq txt (propertize txt 'face '((:foreground "blue"))))))) + result)) + +(defun sqlplus-font-lock (type-symbol limit) + (let ((sqlplus-font-lock-regexps (sqlplus-get-font-lock-regexps))) + (when sqlplus-font-lock-regexps + (let ((regexp (gethash type-symbol sqlplus-font-lock-regexps))) + (when regexp + (re-search-forward regexp limit t)))))) + +;; Local in input buffer (sqlplus-mode) +(defvar sqlplus-command-overlay nil) +(make-variable-buffer-local 'sqlplus-command-overlay) +(defvar sqlplus-begin-command-overlay-arrow-position nil) +(make-variable-buffer-local 'sqlplus-begin-command-overlay-arrow-position) +(defvar sqlplus-end-command-overlay-arrow-position nil) +(make-variable-buffer-local 'sqlplus-end-command-overlay-arrow-position) + +(defun sqlplus-highlight-current-sqlplus-command() + (when (and window-system sqlplus-command-highlighting-style) + (let* ((pair (sqlplus-mark-current)) + (begin (and (car pair) (save-excursion (goto-char (car pair)) (skip-chars-forward " \t\n") (point)))) + (end (and (cdr pair) (save-excursion (goto-char (cdr pair)) (skip-chars-backward " \t\n") (beginning-of-line) (point)))) + (point-line-beg (save-excursion (beginning-of-line) (point))) + (overlay-begin begin) + (overlay-end end)) + (when (and begin end) + (when (< end point-line-beg) + (save-excursion (goto-char point-line-beg) (when (eobp) (insert "\n"))) + (setq end point-line-beg) + (setq overlay-end end)) + (when (or (>= begin end) (< (point) begin)) + (when (or (< (point) begin) (> begin end)) + (setq overlay-begin nil + overlay-end nil)) + (setq begin nil + end nil))) + (if (and overlay-begin overlay-end (memq sqlplus-command-highlighting-style '(background fringe-and-background))) + (progn + (setq overlay-end (save-excursion + (goto-char overlay-end) + (beginning-of-line 2) + (point))) + (move-overlay sqlplus-command-overlay overlay-begin overlay-end)) + (move-overlay sqlplus-command-overlay 1 1)) + (if (memq sqlplus-command-highlighting-style '(fringe fringe-and-background)) + (progn + (put 'sqlplus-begin-command-overlay-arrow-position 'overlay-arrow-bitmap 'top-left-angle) + (put 'sqlplus-end-command-overlay-arrow-position 'overlay-arrow-bitmap 'bottom-left-angle) + (set-marker sqlplus-begin-command-overlay-arrow-position begin) + (set-marker sqlplus-end-command-overlay-arrow-position end)) + (set-marker sqlplus-begin-command-overlay-arrow-position nil) + (set-marker sqlplus-end-command-overlay-arrow-position nil))))) + +(defun sqlplus-find-begin-of-sqlplus-command () + (save-excursion + (beginning-of-line) + (while (and (not (bobp)) (save-excursion (end-of-line 0) (skip-chars-backward " \t") (equal (char-before) ?-))) + (beginning-of-line 0)) + (point))) + +(defun sqlplus-find-end-of-sqlplus-command () + (save-excursion + (end-of-line) + (while (progn (skip-chars-backward " \t") (and (not (eobp)) (equal (char-before) ?-))) + (end-of-line 2)) + (point))) + +(defun sqlplus-set-font-lock-emacs-structures-for-level (level mode-symbol) + (let ((result (append sql-mode-oracle-font-lock-keywords + (default-value (cond ((equal level 3) 'sqlplus-font-lock-keywords-3) + ((equal level 2) 'sqlplus-font-lock-keywords-2) + ((equal level 1) 'sqlplus-font-lock-keywords-1) + (t nil)))))) + (when (featurep 'plsql) + (setq result (append (symbol-value 'plsql-oracle-font-lock-fix-re) result))) + (setq result + (append + ;; Names for schemas, tables, synonyms, views, columns, sequences, packages, triggers and indexes + (when (> level 2) + (mapcar (lambda (pair) + (let ((type-symbol (car pair)) + (face (cadr pair))) + (cons (eval `(lambda (limit) (sqlplus-font-lock ',type-symbol limit))) face))) + sqlplus-syntax-faces)) + ;; SQL*Plus + (when (eq mode-symbol 'sqlplus-mode) + (unless sqlplus-commands-regexp-1 + (flet ((first-form-fun (cmds) (mapcar (lambda (name) (car (sqlplus-full-forms name))) cmds)) + (all-forms-fun (cmds) (mapcan 'sqlplus-full-forms cmds)) + (sqlplus-commands-regexp-fun (form-fun cmds) (concat "^" (regexp-opt (funcall form-fun cmds) t) "\\b")) + (sqlplus-system-variables-fun (form-fun vars) (concat "\\b" (regexp-opt (funcall form-fun vars) t) "\\b"))) + (flet ((sqlplus-v22-commands-font-lock-keywords-fun + (form-fun) + (delq nil + (mapcar + (lambda (command-info) + (let* ((names (car command-info)) + (names-list (if (listp names) names (list names))) + (sublists (cdr command-info))) + (when sublists + (append (list (sqlplus-commands-regexp-fun form-fun names-list)) + (mapcar (lambda (sublist) + (let ((face (car sublist)) + (regexp (concat "\\b" + (regexp-opt (mapcan (lambda (name) (sqlplus-full-forms name)) + (mapcan (lambda (elem) + (if (symbolp elem) + (copy-list (symbol-value elem)) + (list elem))) + (cdr sublist))) + t) + "\\b"))) + (list regexp '(sqlplus-find-end-of-sqlplus-command) nil (list 1 face)))) + sublists) + (list '("\\(\\w+\\)" (sqlplus-find-end-of-sqlplus-command) nil (1 font-lock-sqlplus-face))))))) + sqlplus-commands)))) + (let ((commands (mapcan + (lambda (command-info) (let ((names (car command-info))) (if (listp names) (copy-list names) (list names)))) + sqlplus-commands))) + (setq sqlplus-commands-regexp-1 (sqlplus-commands-regexp-fun 'first-form-fun commands)) + (setq sqlplus-commands-regexp-23 (sqlplus-commands-regexp-fun 'all-forms-fun commands)) + (if (<= emacs-major-version 21) + (setq sqlplus-system-variables-regexp-1 (sqlplus-system-variables-fun 'first-form-fun sqlplus-system-variables) + sqlplus-system-variables-regexp-23 (sqlplus-system-variables-fun 'all-forms-fun sqlplus-system-variables)) + (setq sqlplus-v22-commands-font-lock-keywords-1 (sqlplus-v22-commands-font-lock-keywords-fun 'first-form-fun) + sqlplus-v22-commands-font-lock-keywords-23 (sqlplus-v22-commands-font-lock-keywords-fun 'all-forms-fun))))))) + (append (list + ;; Comments (REM command) + (cons "^\\(rem\\)\\b\\(.*?\\)$" '((1 font-lock-keyword-face nil nil) (2 font-lock-comment-face t nil))) + ;; Predefined SQL*Plus variables + (cons (concat "\\b" + (regexp-opt '("_CONNECT_IDENTIFIER" "_DATE" "_EDITOR" "_O_VERSION" "_O_RELEASE" "_PRIVILEGE" + "_SQLPLUS_RELEASE" "_USER") t) + "\\b") + 'font-lock-builtin-face) + ;; SQL*Plus commands (+ shortcuts if level >= 2) + (cons + (concat (if (>= level 2) sqlplus-commands-regexp-23 sqlplus-commands-regexp-1) "\\|^\\(@@\\|@\\|!\\|/\\|\\$\\)" ) + 'font-lock-keyword-face)) + (if (<= emacs-major-version 21) + ;; SQL*Plus system variables (+ shortcuts if level >= 2) + (list (cons (if (>= level 2) sqlplus-system-variables-regexp-23 sqlplus-system-variables-regexp-1) 'font-lock-builtin-face)) + ;; ver. >= 22 + (if (>= level 2) sqlplus-v22-commands-font-lock-keywords-23 sqlplus-v22-commands-font-lock-keywords-1)))) + ; (cons "\\b\\([a-zA-Z$_#0-9]+\\)\\b\\.\\(\\b[a-zA-Z$_#0-9]+\\b\\)" '((1 font-lock-type-face nil nil)(2 font-lock-variable-name-face nil nil))) + (list + ;; Extra Oracle syntax highlighting, not recognized by sql-mode or plsql-mode + (cons sqlplus-oracle-extra-types-re 'font-lock-type-face) + (cons sqlplus-oracle-extra-warning-words-re 'font-lock-warning-face) + (cons sqlplus-oracle-extra-types-re 'font-lock-type-face) + (cons sqlplus-oracle-extra-keywords-re 'font-lock-keyword-face) + (cons sqlplus-oracle-plsql-extra-reserved-words-re 'font-lock-keyword-face) + (if (string-match "XEmacs\\|Lucid" emacs-version) + (cons sqlplus-oracle-extra-pseudocolumns-re 'font-lock-preprocessor-face) + (cons sqlplus-oracle-extra-pseudocolumns-re 'font-lock-builtin-face)) + (if (string-match "XEmacs\\|Lucid" emacs-version) + (cons sqlplus-oracle-extra-builtin-functions-re 'font-lock-preprocessor-face) + (cons sqlplus-oracle-extra-builtin-functions-re 'font-lock-builtin-face)) + ;; SQL*Plus variable names, like '&name' or '&&name' + (cons "\\(\\b&[&a-zA-Z$_#0-9]+\\b\\)" 'font-lock-variable-name-face)) + result + ;; Function calls + (when (>= level 2) + (list (cons "\\b\\(\\([a-zA-Z$_#0-9]+\\b\\)\\.\\)?\\(\\b[a-zA-Z$_#0-9]+\\b\\)\\s-*(" + '((2 font-lock-type-face nil t) + (3 font-lock-function-name-face nil nil))))))) + result)) + +(defun sqlplus-mode nil + "Mode for editing and executing SQL*Plus commands. Entry into this mode runs the hook +'sqlplus-mode-hook'. + +Use \\[sqlplus] to start the SQL*Plus interpreter. + +Just position the cursor on or near the SQL*Plus statement you +wish to send and press '\\[sqlplus-send-current]' to run it and +display the results. + +Mode Specific Bindings: + +\\{sqlplus-mode-map}" + (interactive) + (run-hooks 'change-major-mode-hook) + (setq major-mode 'sqlplus-mode + mode-name "SQL*Plus") + (use-local-map sqlplus-mode-map) + (set-syntax-table sqlplus-mode-syntax-table) + (make-local-variable 'comment-start) + (make-local-variable 'comment-end) + (setq comment-start "/* " + comment-end " */") + (orcl-mode 1) + (setq sqlplus-font-lock-keywords-1 (sqlplus-set-font-lock-emacs-structures-for-level 1 major-mode) + sqlplus-font-lock-keywords-2 (sqlplus-set-font-lock-emacs-structures-for-level 2 major-mode) + sqlplus-font-lock-keywords-3 (sqlplus-set-font-lock-emacs-structures-for-level 3 major-mode)) + (when (featurep 'plsql) + (set (make-local-variable 'indent-line-function) + (lambda () (interactive) (condition-case err (funcall (symbol-function 'plsql-indent)) (error (message "Error: %S" err))))) + (set (make-local-variable 'indent-region-function) 'plsql-indent-region) + (set (make-local-variable 'align-mode-rules-list) 'plsql-align-rules-list)) + (setq font-lock-defaults sqlplus-font-lock-defaults) + (unless sqlplus-connect-string + (let ((potential-connect-string (sqlplus-get-potential-connect-string (buffer-file-name)))) + (when (and potential-connect-string + (get-process (sqlplus-get-process-name potential-connect-string))) + (setq sqlplus-connect-string potential-connect-string)))) + (set (make-local-variable 'font-lock-extend-after-change-region-function) + (lambda (beg end old-len) + (cons (save-excursion (goto-char beg) (sqlplus-find-begin-of-sqlplus-command)) + (save-excursion (goto-char end) (sqlplus-find-end-of-sqlplus-command))))) + (unless font-lock-sqlplus-face + (copy-face 'default 'font-lock-sqlplus-face) + (setq font-lock-sqlplus-face 'font-lock-sqlplus-face)) + (turn-on-font-lock) + (unless frame-background-mode + (setq frame-background-mode (if (< (sqlplus-color-percentage (face-background 'default)) 50) 'dark 'light))) + (setq imenu-generic-expression '((nil "^--[ ]*\\([^;.\n]*\\)" 1))) + ;; if input buffer has sqlplus-mode then prepare it for command under cursor selection + (when (and (eq major-mode 'sqlplus-mode) (null sqlplus-begin-command-overlay-arrow-position)) + (setq sqlplus-begin-command-overlay-arrow-position (make-marker) + sqlplus-end-command-overlay-arrow-position (make-marker) + sqlplus-command-overlay (make-overlay 1 1)) + (overlay-put sqlplus-command-overlay 'face 'sqlplus-command-highlight-face) + (when (and (>= emacs-major-version 22) (not (memq 'sqlplus-begin-command-overlay-arrow-position overlay-arrow-variable-list))) + (push 'sqlplus-begin-command-overlay-arrow-position overlay-arrow-variable-list)) + (when (and (>= emacs-major-version 22) (not (memq 'sqlplus-end-command-overlay-arrow-position overlay-arrow-variable-list))) + (push 'sqlplus-end-command-overlay-arrow-position overlay-arrow-variable-list)) + (add-hook 'pre-command-hook (lambda () + (set-marker sqlplus-begin-command-overlay-arrow-position nil) + (set-marker sqlplus-end-command-overlay-arrow-position nil)) + nil t) + (add-hook 'post-command-hook (lambda () + (sqlplus-clear-mouse-selection) + (set-marker sqlplus-begin-command-overlay-arrow-position nil) + (set-marker sqlplus-end-command-overlay-arrow-position nil)) + nil t)) + (run-hooks 'sqlplus-mode-hook)) + +(defun sqlplus-color-percentage (color) + (truncate (* (/ (/ (reduce '+ (color-values color)) 3.0) 65535.0) 100.0))) + +(defun sqlplus-get-potential-connect-string (file-path) + (when file-path + (let* ((file-name (file-name-nondirectory file-path)) + (extension (file-name-extension file-name)) + (case-fold-search t)) + (when (and extension + (string-match (concat "^" sqlplus-session-file-extension "$") extension) + (string-match "@" file-name)) + (car (refine-connect-string (file-name-sans-extension file-name))))))) + +(defun sqlplus-check-connection () + (if orcl-mode + (unless sqlplus-connect-string + (let* ((potential-connect-string (sqlplus-get-potential-connect-string (buffer-file-name))) + (connect-string (car (sqlplus-read-connect-string nil (or potential-connect-string + (caar (sqlplus-divide-connect-strings))))))) + (sqlplus connect-string (buffer-name)))) + (error "Current buffer is not determined to communicate with Oracle"))) + +;;; Utilitities + +(defun sqlplus-echo-in-buffer (buffer-name string &optional force-display hide-after-head) + "Displays string in the named buffer, creating the buffer if needed. If force-display is true, the buffer will appear +if not already shown." + (let ((buffer (get-buffer buffer-name))) + (when buffer + (if force-display (display-buffer buffer)) + (with-current-buffer buffer + (while (and (> (buffer-size) sqlplus-output-buffer-max-size) + (progn (goto-char (point-min)) + (unless (eobp) (forward-char)) + (re-search-forward (concat "^" (regexp-quote sqlplus-output-separator)) nil t))) + (delete-region 1 (- (point) (length sqlplus-output-separator)))) + + (goto-char (point-max)) + (let ((start-point (point))) + (insert string) + (when hide-after-head + (let ((from-pos (string-match "\n" string)) + (keymap (make-sparse-keymap)) + overlay) + (when from-pos + (setq overlay (make-overlay (+ start-point from-pos) (- (+ start-point (length string)) 2))) + (when (or (not (consp buffer-invisibility-spec)) + (not (assq 'hide-symbol buffer-invisibility-spec))) + (add-to-invisibility-spec '(hide-symbol . t))) + (overlay-put overlay 'invisible 'hide-symbol) + (put-text-property start-point (- (+ start-point (length string)) 2) 'help-echo string) + (put-text-property start-point (- (+ start-point (length string)) 2) 'mouse-face 'highlight) + (put-text-property start-point (- (+ start-point (length string)) 2) 'keymap sqlplus-output-buffer-keymap))))) + (if force-display + (set-window-point (get-buffer-window buffer-name) (point-max))))))) + +(defun sqlplus-verify-buffer (connect-string) + (let ((output-buffer-name (sqlplus-get-output-buffer-name connect-string)) + (process-buffer-name (sqlplus-get-process-buffer-name connect-string))) + (when (not (get-buffer process-buffer-name)) + (sqlplus-shutdown connect-string) + (error "No SQL*Plus session! Use 'M-x sqlplus' to start the SQL*Plus interpreter")) + (unless (get-buffer-process process-buffer-name) + (sqlplus-shutdown connect-string) + (error "Buffer '%s' is not talking to anybody!" output-buffer-name))) + t) + +(defun sqlplus-get-context (connect-string &optional id) + (let ((process-buffer (sqlplus-get-process-buffer-name connect-string))) + (when process-buffer + (with-current-buffer process-buffer + (when id + (while (and sqlplus-command-contexts + (not (equal (sqlplus-get-context-value (car sqlplus-command-contexts) :id) id))) + (setq sqlplus-command-contexts (cdr sqlplus-command-contexts)))) + (car sqlplus-command-contexts))))) + +(defun sqlplus-get-context-value (context var-symbol) + (cdr (assq var-symbol context))) + +(defun sqlplus-set-context-value (context var-symbol value) + (let ((association (assq var-symbol context))) + (if association + (setcdr association value) + (setcdr context (cons (cons var-symbol value) (cdr context)))) + context)) + +(defun sqlplus-mark-current () + "Marks the current SQL for sending to the SQL*Plus process. Marks are placed around a region defined by empty lines." + (let (begin end empty-line-p empty-line-p next-line-included tail-p) + (save-excursion + (beginning-of-line) + (setq empty-line-p (when (looking-at "^[ \t]*\\(\n\\|\\'\\)") (point))) + (setq next-line-included (and empty-line-p (save-excursion (skip-chars-forward " \t\n") (> (current-column) 0)))) + (setq tail-p (and empty-line-p + (or (bobp) (save-excursion (beginning-of-line 0) (looking-at "^[ \t]*\n")))))) + (unless tail-p + (save-excursion + (end-of-line) + (re-search-backward "\\`\\|\n[\r\t ]*\n[^ \t]" nil t) + (skip-syntax-forward "-") + (setq begin (point))) + (save-excursion + (beginning-of-line) + (re-search-forward "\n[\r\t ]*\n[^ \t]\\|\\'" nil t) + (unless (zerop (length (match-string 0))) + (backward-char 1)) + (skip-syntax-backward "-") + (setq end (or (and (not next-line-included) empty-line-p) (point))))) + (cons begin end))) + +;;; Transmission Commands + +(defun sqlplus-send-current (arg &optional html) + "Send the current SQL command(s) to the SQL*Plus process. With argument, show results in raw form." + (interactive "P") + (sqlplus-check-connection) + (when (buffer-file-name) + (condition-case err + (save-buffer) + (error (message (error-message-string err))))) + (let ((region (sqlplus-mark-current))) + (setq sqlplus-region-beginning-pos (car region) + sqlplus-region-end-pos (cdr region))) + (if (and sqlplus-region-beginning-pos sqlplus-region-end-pos) + (sqlplus-send-region arg sqlplus-region-beginning-pos sqlplus-region-end-pos nil html) + (error "Point doesn't indicate any command to execute"))) + +(defun sqlplus-send-current-html (arg) + (interactive "P") + (sqlplus-send-current arg t)) + + +;;; SQLPLUS-Output Buffer Operations - + +(defun sqlplus--show-buffer (connect-string fcn args) + (let* ((output-buffer-name (sqlplus-get-output-buffer-name connect-string))) + (sqlplus-verify-buffer connect-string) + (if sqlplus-suppress-show-output-buffer + (with-current-buffer (get-buffer output-buffer-name) + (if fcn (condition-case err (apply fcn args) (error (message (error-message-string err)))))) + (if (not (eq (window-buffer (selected-window)) (get-buffer output-buffer-name))) + (switch-to-buffer-other-window output-buffer-name)) + (if fcn (condition-case err (apply fcn args) (error (message (error-message-string err)))))))) + +(defun sqlplus-show-buffer (&optional connect-string fcn &rest args) + "Makes the SQL*Plus output buffer visible in the other window." + (interactive) + (setq connect-string (or connect-string sqlplus-connect-string)) + (unless connect-string + (error "Current buffer is disconnected!")) + (let ((output-buffer-name (sqlplus-get-output-buffer-name connect-string))) + (if (and output-buffer-name + (eq (current-buffer) (get-buffer output-buffer-name))) + (sqlplus--show-buffer connect-string fcn args) + (save-excursion + (save-selected-window + (sqlplus--show-buffer connect-string fcn args)))))) + +(fset 'sqlplus-buffer-display-window 'sqlplus-show-buffer) + +(defun sqlplus-buffer-scroll-up (&optional connect-string) + "Scroll-up in the SQL*Plus output buffer window." + (interactive) + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'scroll-up)) + +(defun sqlplus-buffer-scroll-down (&optional connect-string) + "Scroll-down in the SQL*Plus output buffer window." + (interactive) + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'scroll-down)) + +(defun sqlplus-scroll-left (num) + (call-interactively 'scroll-left)) + +(defun sqlplus-scroll-right (num) + (call-interactively 'scroll-right)) + +(defun sqlplus-buffer-scroll-left (num &optional connect-string) + "Scroll-left in the SQL*Plus output buffer window." + (interactive "p") + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'sqlplus-scroll-left (* num (/ (window-width) 2)))) + +(defun sqlplus-buffer-scroll-right (num &optional connect-string) + "Scroll-right in the SQL*Plus output buffer window." + (interactive "p") + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'sqlplus-scroll-right (* num (/ (window-width) 2)))) + +(defun sqlplus-buffer-mark-current (&optional connect-string) + "Mark the current position in the SQL*Plus output window." + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'sqlplus-buffer-make-mark)) + +(defun sqlplus-buffer-make-mark (&optional connect-string) + "Set the sqlplus-buffer-marker." + (setq sqlplus-buffer-mark (copy-marker (point)))) + +(defun sqlplus-buffer-redisplay-current (&optional connect-string) + "Go to the current sqlplus-buffer-mark." + (interactive) + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'sqlplus-goto-mark)) + +(defun sqlplus-goto-mark () + (goto-char sqlplus-buffer-mark) + (recenter 0)) + +(defun sqlplus-buffer-top (&optional connect-string) + "Goto the top of the SQL*Plus output buffer." + (interactive) + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'sqlplus-beginning-of-buffer)) + +(defun sqlplus-beginning-of-buffer nil (goto-char (point-min))) + +(defun sqlplus-buffer-bottom (&optional connect-string) + "Goto the bottom of the SQL*Plus output buffer." + (interactive) + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'sqlplus-end-of-buffer)) + +(defun sqlplus-end-of-buffer nil (goto-char (point-max)) (unless sqlplus-suppress-show-output-buffer (recenter -1))) + +(defun sqlplus-buffer-erase (&optional connect-string) + "Clear the SQL output buffer." + (interactive) + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'erase-buffer)) + +(defun sqlplus-buffer-next-command (&optional connect-string) + "Search for the next command in the SQL*Plus output buffer." + (interactive) + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'sqlplus-next-command)) + +(defun sqlplus-next-command nil + "Search for the next command in the SQL*Plus output buffer." + (cond ((re-search-forward (concat "^" (regexp-quote sqlplus-output-separator)) nil t) + (forward-line 2) + (recenter 0)) + (t (beep) (message "No more commands.")))) + +(defun sqlplus-buffer-prev-command (&optional connect-string) + "Search for the previous command in the SQL*Plus output buffer." + (interactive) + (sqlplus-show-buffer (or connect-string sqlplus-connect-string) 'sqlplus-previous-command)) + +(defun sqlplus-previous-command nil + "Search for the previous command in the SQL*Plus output buffer." + (let ((start (point))) + (re-search-backward (concat "^" (regexp-quote sqlplus-output-separator)) nil t) + (cond ((re-search-backward (concat "^" (regexp-quote sqlplus-output-separator)) nil t) + (forward-line 2) + (recenter 0)) + (t + (message "No more commands.") (beep) + (goto-char start))))) + +(defun sqlplus-send-interrupt nil + "Send an interrupt the the SQL*Plus interpreter process." + (interactive) + (sqlplus-check-connection) + (let ((connect-string sqlplus-connect-string)) + (sqlplus-verify-buffer connect-string) + (interrupt-process (get-process (sqlplus-get-process-name connect-string))))) + + +;;; SQL Interpreter + +(defun refine-connect-string (connect-string &optional no-slash) + "Z connect stringa do SQL*Plusa wycina haslo, tj. np. 'ponaglenia/x@SID' -> ('ponaglenia@SID' . 'x')." + (let (result passwd) + (when connect-string + (setq result + (if (string-match "\\(\\`[^@/]*?\\)/\\([^/@:]*\\)\\(.*?\\'\\)" connect-string) + (progn + (setq passwd (match-string 2 connect-string)) + (concat (match-string 1 connect-string) (match-string 3 connect-string))) + connect-string)) + (when no-slash + (while (string-match "/" result) + (setq result (replace-match "!" nil t result))))) + (cons result passwd))) + +(defun sqlplus-get-output-buffer-name (connect-string) + (concat "*" (car (refine-connect-string connect-string)) "*")) + +(defun sqlplus-get-input-buffer-name (connect-string) + (concat (car (refine-connect-string connect-string)) (concat "." sqlplus-session-file-extension))) + +(defun sqlplus-get-history-buffer-name (connect-string) + (concat " " (car (refine-connect-string connect-string)) "-hist")) + +(defun sqlplus-get-process-buffer-name (connect-string) + (concat " " (car (refine-connect-string connect-string)))) + +(defun sqlplus-get-process-name (connect-string) + (car (refine-connect-string connect-string))) + +(defun sqlplus-read-connect-string (&optional connect-string default-connect-string) + "Ask user for connect string with password, with DEFAULT-CONNECT-STRING proposed. +DEFAULT-CONNECT-STRING nil means first inactive connect-string on sqlplus-connect-strings-alist. +CONNECT-STRING non nil means ask for password only if CONNECT-STRING has no password itself. +Returns (qualified-connect-string refined-connect-string)." + (unless default-connect-string + (let ((inactive-connect-strings (cdr (sqlplus-divide-connect-strings)))) + (setq default-connect-string + (some (lambda (pair) + (when (member (car pair) inactive-connect-strings) (car pair))) + sqlplus-connect-strings-alist)))) + (let* ((cs (downcase (or connect-string + (read-string (format "Connect string%s: " (if default-connect-string (format " [default %s]" default-connect-string) "")) + nil 'sqlplus-connect-string-history default-connect-string)))) + (pair (refine-connect-string cs)) + (refined-cs (car pair)) + (password (cdr pair)) + (was-password password) + (association (assoc refined-cs sqlplus-connect-strings-alist))) + (unless (or password current-prefix-arg) + (setq password (cdr association))) + (unless password + (setq password (read-passwd (format "Password for %s: " cs)))) + (unless was-password + (if (string-match "@" cs) + (setq cs (replace-match (concat "/" password "@") t t cs)) + (setq cs (concat cs "/" password)))) + (list cs refined-cs))) + +(defun sqlplus (connect-string &optional input-buffer-name output-buffer-flag) + "Create SQL*Plus process connected to Oracle according to +CONNECT-STRING, open (or create) input buffer with specified +name (do not create if INPUT-BUFFER-NAME is nil). +OUTPUT-BUFFER-FLAG has meanings: nil or SHOW-OUTPUT-BUFFER - +create output buffer and show it, DONT-SHOW-OUTPUT-BUFFER - +create output buffer but dont show it, DONT-CREATE-OUTPUT-BUFFER +- dont create output buffer" + (interactive (let ((pair (sqlplus-read-connect-string))) + (list (car pair) (concat (cadr pair) (concat "." sqlplus-session-file-extension))))) + (set (make-local-variable 'comment-start-skip) "/\\*+ *\\|--+ *") + (set (make-local-variable 'comment-multi-line) t) + ;; create sqlplus-session-cache-dir if not exists + (when sqlplus-session-cache-dir + (condition-case err + (unless (file-directory-p sqlplus-session-cache-dir) + (make-directory sqlplus-session-cache-dir t)) + (error (message (error-message-string err))))) + (let* ((was-input-buffer (and input-buffer-name (get-buffer input-buffer-name))) + (input-buffer (or was-input-buffer + (when input-buffer-name + (if sqlplus-session-cache-dir + (let ((buf (find-file-noselect + (concat + (file-name-as-directory sqlplus-session-cache-dir) + (car (refine-connect-string connect-string t)) + (concat "." sqlplus-session-file-extension))))) + (condition-case nil + (with-current-buffer buf + (rename-buffer input-buffer-name)) + (error nil)) + buf) + (get-buffer-create input-buffer-name))))) + (output-buffer (or (and (not (eq output-buffer-flag 'dont-create-output-buffer)) + (get-buffer-create (sqlplus-get-output-buffer-name connect-string))) + (get-buffer (sqlplus-get-output-buffer-name connect-string)))) + (process-name (sqlplus-get-process-name connect-string)) + (process-buffer-name (sqlplus-get-process-buffer-name connect-string)) + (was-process (get-process process-name)) + process-created + (process (or was-process + (let (proc) + (puthash (car (refine-connect-string connect-string)) + (make-sqlplus-global-struct :font-lock-regexps (make-hash-table :test 'equal) + :side-view-buffer (when (featurep 'ide-skel) (sqlplus-create-side-view-buffer connect-string))) + sqlplus-global-structures) + ;; push current connect string to the beginning of sqlplus-connect-strings-alist + (let* ((refined-cs (refine-connect-string connect-string))) + (setq sqlplus-connect-strings-alist (delete* (car refined-cs) sqlplus-connect-strings-alist :test 'string= :key 'car)) + (push refined-cs sqlplus-connect-strings-alist)) + (sqlplus-get-history-buffer connect-string) + (when output-buffer + (with-current-buffer output-buffer + (erase-buffer))) + (setq process-created t + proc (start-process process-name process-buffer-name sqlplus-command connect-string)) + (set-process-sentinel proc (lambda (process event) + (let ((proc-buffer (buffer-name (process-buffer process))) + (output-buffer (get-buffer (sqlplus-get-output-buffer-name (process-name process)))) + err-msg + (exited-abnormally (string-match "\\`exited abnormally with code" event))) + (when output-buffer + (with-current-buffer output-buffer + (goto-char (point-max)) + (insert (format "\n%s" event)) + (when exited-abnormally + (setq sqlplus-connect-strings-alist + (delete* (car (refine-connect-string sqlplus-connect-string)) + sqlplus-connect-strings-alist :test 'string= :key 'car)) + (when proc-buffer + (with-current-buffer proc-buffer + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "^ORA-[0-9]+.*$" nil t) + (setq err-msg (match-string 0)))) + (erase-buffer))) + (when err-msg + (insert (concat "\n" err-msg))))))))) + (process-kill-without-query proc (not sqlplus-kill-processes-without-query-on-exit-flag)) + (set-process-filter proc 'sqlplus-process-filter) + (with-current-buffer (get-buffer process-buffer-name) + (setq sqlplus-process-p connect-string)) + proc)))) + (when output-buffer + (with-current-buffer output-buffer + (orcl-mode 1) + (set (make-local-variable 'line-move-ignore-invisible) t) + (setq sqlplus-output-buffer-keymap (make-sparse-keymap) + sqlplus-connect-string connect-string + truncate-lines t) + (define-key sqlplus-output-buffer-keymap "\C-m" (lambda () (interactive) (sqlplus-output-buffer-hide-show))) + (define-key sqlplus-output-buffer-keymap [S-mouse-2] (lambda (event) (interactive "@e") (sqlplus-output-buffer-hide-show))) + (local-set-key [S-return] 'sqlplus-send-user-string))) + (when input-buffer + (with-current-buffer input-buffer + (setq sqlplus-connect-string connect-string))) + ;; if input buffer was created then switch it to sqlplus-mode + (when (and input-buffer (not was-input-buffer)) + (with-current-buffer input-buffer + (unless (eq major-mode 'sqlplus-mode) + (sqlplus-mode))) + (when font-lock-mode (font-lock-mode 1)) + (set-window-buffer (sqlplus-get-workbench-window) input-buffer)) + ;; if process was created then get information for font lock + (when process-created + (sqlplus-execute connect-string nil nil (sqlplus-initial-strings) 'no-echo) + (let ((plsql-font-lock-level (sqlplus-font-lock-value-in-major-mode font-lock-maximum-decoration 'plsql-mode)) + (sqlplus-font-lock-level (sqlplus-font-lock-value-in-major-mode font-lock-maximum-decoration 'sqlplus-mode))) + (when (or (equal plsql-font-lock-level t) (equal sqlplus-font-lock-level t) + (and (numberp plsql-font-lock-level) (>= plsql-font-lock-level 2)) + (and (numberp sqlplus-font-lock-level) (>= sqlplus-font-lock-level 2))) + (sqlplus-hidden-select connect-string + (concat "select distinct column_name, 'COLUMN', ' ' from user_tab_columns where column_name not like 'BIN$%'\n" + "union\n" + "select username, 'SCHEMA', ' ' from all_users where username not like 'BIN$%'\n" + "union\n" + "select object_name, object_type, decode( status, 'INVALID', 'I', ' ' ) from user_objects\n" + "where object_name not like 'BIN$%'\n" + "and object_type in ('VIEW', 'SEQUENCE', 'PACKAGE', 'TRIGGER', 'TABLE', 'SYNONYM', 'INDEX', 'FUNCTION', 'PROCEDURE');") + 'sqlplus-my-handler)))) + (when input-buffer + (save-selected-window + (when (equal (selected-window) (sqlplus-get-side-window)) + (select-window (sqlplus-get-workbench-window))) + (switch-to-buffer input-buffer))) + (let ((saved-window (cons (selected-window) (window-buffer (selected-window)))) + (input-buffer (get-buffer (sqlplus-get-input-buffer-name connect-string)))) + (when (or (eq output-buffer-flag 'show-output-buffer) (null output-buffer-flag)) + (sqlplus-show-buffer connect-string)) + (if (window-live-p (car saved-window)) + (select-window (car saved-window)) + (if (get-buffer-window (cdr saved-window)) + (select-window (get-buffer-window (cdr saved-window))) + (when (and input-buffer + (get-buffer-window input-buffer)) + (select-window (get-buffer-window input-buffer)))))) + ;; executing initial sequence (between /* init */ and /* end */) + (when (and (not was-process) input-buffer) + (with-current-buffer input-buffer + (save-excursion + (goto-char (point-min)) + (when (re-search-forward (concat "^" sqlplus-init-sequence-start-regexp "\\s-*\n\\(\\(.\\|\n\\)*?\\)\n" sqlplus-init-sequence-end-regexp) nil t) + (when (match-string 1) + (sqlplus-send-region nil (match-beginning 1) (match-end 1) t)))))))) + +;; Command under cursor selection mechanism +(when window-system + (run-with-idle-timer 0 t (lambda () (when (eq major-mode 'sqlplus-mode) (sqlplus-highlight-current-sqlplus-command)))) + (run-with-idle-timer 1 t (lambda () + (when (eq major-mode 'sqlplus-mode) + (if (>= (sqlplus-color-percentage (face-background 'default)) 50) + (set-face-attribute 'sqlplus-command-highlight-face nil + :background (sqlplus-shine-color (face-background 'default) (- sqlplus-command-highlighting-percentage))) + (set-face-attribute 'sqlplus-command-highlight-face nil + :background (sqlplus-shine-color (face-background 'default) sqlplus-command-highlighting-percentage))))))) + +(defun sqlplus-output-buffer-hide-show () + (if (and (consp buffer-invisibility-spec) + (assq 'hide-symbol buffer-invisibility-spec)) + (remove-from-invisibility-spec '(hide-symbol . t)) + (add-to-invisibility-spec '(hide-symbol . t))) + (let ((overlay (car (overlays-at (point))))) + (when overlay + (goto-char (overlay-start overlay)) + (beginning-of-line))) + (recenter 0)) + +(defun sqlplus-font-lock-value-in-major-mode (alist mode-symbol) + (if (consp alist) + (cdr (or (assq mode-symbol alist) (assq t alist))) + alist)) + +(defun sqlplus-get-history-buffer (connect-string) + (let* ((history-buffer-name (sqlplus-get-history-buffer-name connect-string)) + (history-buffer (get-buffer history-buffer-name))) + (unless history-buffer + (setq history-buffer (get-buffer-create history-buffer-name)) + (with-current-buffer history-buffer + (setq sqlplus-cs connect-string) + (add-hook 'kill-buffer-hook 'sqlplus-history-buffer-kill-function nil t))) + history-buffer)) + +(defun sqlplus-history-buffer-kill-function () + (when sqlplus-history-dir + (condition-case err + (progn + (unless (file-directory-p sqlplus-history-dir) + (make-directory sqlplus-history-dir t)) + (append-to-file 1 (buffer-size) (concat (file-name-as-directory sqlplus-history-dir) (car (refine-connect-string sqlplus-cs t)) "-hist.txt"))) + (error (message (error-message-string err)))))) + +(defun sqlplus-soft-shutdown (connect-string) + (unless (some (lambda (buffer) + (with-current-buffer buffer + (and sqlplus-connect-string + (equal (car (refine-connect-string sqlplus-connect-string)) + (car (refine-connect-string connect-string)))))) + (buffer-list)) + (sqlplus-shutdown connect-string))) + +(defun sqlplus-shutdown (connect-string &optional dont-kill-input-buffer) + "Kill input, output and process buffer for specified CONNECT-STRING." + (let ((input-buffers (delq nil (mapcar (lambda (buffer) (with-current-buffer buffer + (when (and (eq major-mode 'sqlplus-mode) + (equal (car (refine-connect-string sqlplus-connect-string)) + (car (refine-connect-string connect-string)))) + buffer))) (buffer-list)))) + (output-buffer (get-buffer (sqlplus-get-output-buffer-name connect-string))) + (history-buffer (get-buffer (sqlplus-get-history-buffer-name connect-string))) + (process-buffer (get-buffer (sqlplus-get-process-buffer-name connect-string)))) + (when history-buffer + (kill-buffer history-buffer)) + (when (and process-buffer + (with-current-buffer process-buffer sqlplus-process-p)) + (when (get-process (sqlplus-get-process-name connect-string)) + (delete-process (sqlplus-get-process-name connect-string))) + (kill-buffer process-buffer)) + (when (and output-buffer + (with-current-buffer output-buffer sqlplus-connect-string)) + (when (buffer-file-name output-buffer) + (with-current-buffer output-buffer + (save-buffer))) + (kill-buffer output-buffer)) + (dolist (input-buffer input-buffers) + (when (buffer-file-name input-buffer) + (with-current-buffer input-buffer + (save-buffer))) + (unless dont-kill-input-buffer + (kill-buffer input-buffer))))) + +(defun sqlplus-magic () + (let (bottom-message pos) + (delete-region (point) (progn (beginning-of-line 3) (point))) + (setq bottom-message (buffer-substring (point) (save-excursion (end-of-line) (point)))) + (setq pos (point)) + (when (re-search-forward "^-------" nil t) + (delete-region pos (progn (beginning-of-line 2) (point))) + (while (re-search-forward "|" (save-excursion (end-of-line) (point)) t) + (save-excursion + (backward-char) + (if (or (bolp) (save-excursion (forward-char) (eolp))) + (while (member (char-after) '(?- ?|)) + (delete-char 1) + (sqlplus-next-line)) + (while (member (char-after) '(?- ?|)) + (delete-char 1) + (insert " ") + (backward-char) + (sqlplus-next-line))))) + (beginning-of-line 3) + (re-search-forward "^---" nil t) + (goto-char (match-beginning 0)) + (delete-region (point) (point-max)) + (insert (format "%s\n\n%s\n" sqlplus-repfooter bottom-message)) + ))) + + +(defun sqlplus-process-command-output (context connect-string begin end interrupted) + (let* ((output-buffer-name (sqlplus-get-output-buffer-name connect-string)) + (output-buffer (get-buffer output-buffer-name)) + (process-buffer (sqlplus-get-process-buffer-name connect-string)) + str + error-list show-errors-p + slips-count + (user-function (sqlplus-get-context-value context :user-function)) + (result-function (sqlplus-get-context-value context :result-table-function)) + (last-compiled-file-path (sqlplus-get-context-value context :last-compiled-file-path)) + (compilation-expected (sqlplus-get-context-value context :compilation-expected)) + (columns-count (sqlplus-get-context-value context :columns-count)) + (sql (sqlplus-get-context-value context :sql)) + (original-buffer (current-buffer)) + explain-plan + table-data) + (setq slips-count columns-count) + (with-temp-buffer + (insert-buffer-substring original-buffer begin end) + (goto-char (point-min)) + (while (re-search-forward (concat "\n+" (regexp-quote sqlplus-page-separator) "\n") nil t) + (replace-match "\n")) + (goto-char (point-min)) + (setq str (buffer-string)) + (while (string-match (concat "^" (regexp-quote sqlplus-repfooter) "\n") str) + (setq str (replace-match "" nil t str))) + + ;; compilation errors? + (goto-char (point-min)) + (skip-chars-forward "\n\t ") + (when (and ;;(not (equal (point) (point-max))) + plsql-auto-parse-errors-flag + output-buffer + last-compiled-file-path + (re-search-forward "^\\(LINE/COL\\|\\(SP2\\|CPY\\|ORA\\)-[0-9]\\{4,5\\}:\\|No errors\\|Nie ma b..d.w\\|Keine Fehler\\|No hay errores\\|Identificateur erron\\|Nessun errore\\|N..o h.. erros\\)" nil t)) + (goto-char (point-min)) + (setq error-list (plsql-parse-errors last-compiled-file-path) + show-errors-p compilation-expected)) + + ;; explain? + (let ((case-fold-search t)) + (goto-char (point-min)) + (skip-chars-forward "\n\t ") + (when (and sql + (string-match "^[\n\t ]*explain\\>" sql) + (looking-at "Explained[.]")) + (delete-region (point-min) (point-max)) + (setq str "") + (sqlplus--send connect-string + "select plan_table_output from table(dbms_xplan.display(null, null, 'TYPICAL'));" + nil + 'no-echo + nil))) + + ;; plan table output? + (goto-char (point-min)) + (skip-chars-forward "\n\t ") + (when (and (looking-at "^PLAN_TABLE_OUTPUT\n") + sqlplus-format-output-tables-flag + (not compilation-expected) + (not show-errors-p)) + (sqlplus-magic) ;; TODO + (goto-char (point-min)) + (re-search-forward "^[^\n]+" nil t) + (delete-region (point-min) (progn (beginning-of-line) (point))) + ;; (setq slips-count 1) + (setq explain-plan t) + (setq table-data (save-excursion (sqlplus-parse-output-table interrupted)))) + + ;; query result? + (goto-char (point-min)) + (when (and sqlplus-format-output-tables-flag + (not compilation-expected) + (not table-data) + (not show-errors-p) + (not (re-search-forward "^LINE/COL\\>" nil t))) + (setq table-data (save-excursion (sqlplus-parse-output-table interrupted)))) + (if user-function + (funcall user-function connect-string context (or table-data str)) + (when output-buffer + (with-current-buffer output-buffer + (save-excursion + (goto-char (point-max)) + (cond (show-errors-p + (insert str) + (plsql-display-errors (file-name-directory last-compiled-file-path) error-list) + (let* ((plsql-buf (get-file-buffer last-compiled-file-path)) + (win (when plsql-buf (car (get-buffer-window-list plsql-buf))))) + (when win + (select-window win)))) + ((and table-data + (car table-data)) + (if result-function + (funcall result-function connect-string table-data) + (let ((b (point)) + (warning-regexp (regexp-opt sqlplus-explain-plan-warning-regexps)) + e) + (sqlplus-draw-table table-data slips-count) + (when interrupted (insert ". . .\n")) + (setq e (point)) + (when explain-plan + (save-excursion + (goto-char b) + (while (re-search-forward warning-regexp nil t) + (add-text-properties (match-beginning 0) (match-end 0) + (list 'face (list (cons 'foreground-color "red") (list :weight 'bold) + (get-text-property (match-beginning 0) 'face)))))))))) + (t + (insert str)))))))))) + +(defun sqlplus-result-online (connect-string context string last-chunk) + (let ((output-buffer (sqlplus-get-output-buffer-name connect-string))) + (when output-buffer + (with-current-buffer output-buffer + (save-excursion + (goto-char (point-max)) + (insert string)))))) + +(defvar sqlplus-prompt-regexp (concat "^" (regexp-quote sqlplus-prompt-prefix) "\\([0-9]+\\)" (regexp-quote sqlplus-prompt-suffix))) + +(defvar sqlplus-page-separator-regexp (concat "^" (regexp-quote sqlplus-page-separator))) + +(defun sqlplus-process-filter (process string) + (with-current-buffer (process-buffer process) + (let* ((prompt-safe-len (+ (max (+ (length sqlplus-prompt-prefix) (length sqlplus-prompt-suffix)) (length sqlplus-page-separator)) 10)) + current-context-id filter-input-processed + (connect-string sqlplus-process-p) + (chunk-begin-pos (make-marker)) + (chunk-end-pos (make-marker)) + (prompt-found (make-marker)) + (context (sqlplus-get-context connect-string current-context-id)) + (current-command-input-buffer-name (sqlplus-get-context-value context :current-command-input-buffer-name)) + (current-command-input-buffer-names (when current-command-input-buffer-name (list current-command-input-buffer-name)))) + (set-marker chunk-begin-pos (max 1 (- (point) prompt-safe-len))) + (goto-char (point-max)) + (insert string) + (unless current-command-input-buffer-names + (setq current-command-input-buffer-names + (delq nil (mapcar (lambda (buffer) (with-current-buffer buffer + (when (and (memq major-mode '(sqlplus-mode plsql-mode)) + sqlplus-connect-string + (equal (car (refine-connect-string sqlplus-connect-string)) + (car (refine-connect-string connect-string)))) + buffer))) (buffer-list))))) + ;; fan animation + (dolist (current-command-input-buffer-name current-command-input-buffer-names) + (let ((input-buffer (get-buffer current-command-input-buffer-name))) + (when input-buffer + (with-current-buffer input-buffer + (setq sqlplus-fan + (cond ((equal sqlplus-fan "|") "/") + ((equal sqlplus-fan "/") "-") + ((equal sqlplus-fan "-") "\\") + ((equal sqlplus-fan "\\") "|"))) + (put-text-property 0 (length sqlplus-fan) 'face '((foreground-color . "red")) sqlplus-fan) + (put-text-property 0 (length sqlplus-fan) 'help-echo (sqlplus-get-context-value context :sql) sqlplus-fan) + (force-mode-line-update))))) + (unwind-protect + (while (not filter-input-processed) + (let* ((context (sqlplus-get-context connect-string current-context-id)) + (dont-parse-result (sqlplus-get-context-value context :dont-parse-result)) + (current-command-input-buffer-name (sqlplus-get-context-value context :current-command-input-buffer-name)) + (result-function (sqlplus-get-context-value context :result-function)) + (skip-to-the-end-of-command (sqlplus-get-context-value context :skip-to-the-end-of-command))) + (set-marker prompt-found nil) + (goto-char chunk-begin-pos) + (set-marker chunk-end-pos + (if (or (re-search-forward sqlplus-prompt-regexp nil t) + (re-search-forward "^SQL> " nil t)) + (progn + (set-marker prompt-found (match-end 0)) + (when (match-string 1) + (setq current-context-id (string-to-number (match-string 1)))) + (match-beginning 0)) + (point-max))) + (cond ((and (equal chunk-begin-pos chunk-end-pos) ; at the end of command + (marker-position prompt-found)) + ;; deactivate fan + (dolist (current-command-input-buffer-name current-command-input-buffer-names) + (let ((input-buffer (get-buffer current-command-input-buffer-name))) + (when input-buffer + (with-current-buffer input-buffer + (remove-text-properties 0 (length sqlplus-fan) '(face nil) sqlplus-fan) + (force-mode-line-update))))) + (delete-region 1 prompt-found) + (when dont-parse-result + (funcall (or result-function 'sqlplus-result-online) connect-string context "" t)) + (sqlplus-set-context-value context :skip-to-the-end-of-command nil) + (set-marker chunk-begin-pos 1)) + ((equal chunk-begin-pos chunk-end-pos) + (when dont-parse-result + (delete-region 1 (point-max))) + (setq filter-input-processed t)) + (dont-parse-result + (funcall (or result-function 'sqlplus-result-online) + connect-string + context + (buffer-substring chunk-begin-pos chunk-end-pos) + (marker-position prompt-found)) + (set-marker chunk-begin-pos chunk-end-pos)) + (t + (when (not skip-to-the-end-of-command) + (goto-char (max 1 (- chunk-begin-pos 4010))) + (let ((page-separator-found + (save-excursion (let ((pos (re-search-forward (concat sqlplus-page-separator-regexp "[^-]*\\(^-\\|^ e b) + (buffer-substring b e) + ""))) + (col-preferred-width (string-width col-name))) + ;; (put-text-property 0 (length col-name) 'face '(bold) col-name) + (push (make-col-desc :id col-counter :name col-name :start-pos col-begin + :end-pos col-end :max-width col-max-width :preferred-width col-preferred-width :min-prefix-len col-max-width) + column-infos) + (incf col-counter) + (goto-char ed) + (if (equal (char-after) ?\n) + (progn + (beginning-of-line 3) + (incf record-lines)) + (forward-char))) + (setq finish t))) + (decf record-lines) + (setq column-infos (nreverse column-infos)) + (forward-line -1) + + ;; at the first char of first data cell. + ;; table parsing... + (while (< (point) end) + (let (record last-start-pos) + (dolist (column-info column-infos) + (let ((start-pos (col-desc-start-pos column-info)) + (end-pos (col-desc-end-pos column-info)) + width len value b e l) + (when (and last-start-pos + (<= start-pos last-start-pos)) + (forward-line)) + (setq last-start-pos start-pos) + (move-to-column start-pos) + (setq b (point)) + (move-to-column end-pos) + (setq e (point)) + (move-to-column start-pos) + (setq l (skip-chars-forward " " e)) + (when (and (col-desc-min-prefix-len column-info) + (< l (- e b)) + (< l (col-desc-min-prefix-len column-info))) + (setf (col-desc-min-prefix-len column-info) + (if (looking-at "[0-9]") l nil))) + (move-to-column end-pos) + (skip-chars-backward " " b) + (setq value (if (> (point) b) (buffer-substring b (point)) "")) + (setq len (length value) + width (string-width value)) + (when (and sqlplus-select-result-max-col-width + (> len sqlplus-select-result-max-col-width)) + (setq value (concat (substring value 0 sqlplus-select-result-max-col-width) "...") + len (length value) + width (string-width value))) + (when (> width (col-desc-preferred-width column-info)) + (setf (col-desc-preferred-width column-info) width)) + (when (and (< l (- e b)) + (memq (col-desc-numeric column-info) '(nil y))) + (setf (col-desc-numeric column-info) + (if (string-match "\\` *[-+0-9Ee.,$]+\\'" value) 'y 'n))) + (push value record))) + (forward-line) + (when (> record-lines 1) + (forward-line)) + (setq last-start-pos nil + record (nreverse record)) + (push record rows))) + (setq rows (nreverse rows)) + (list column-infos rows last-msg))))) + +(defun sqlplus-draw-table (lst &optional slips-count) + "SLIPS-COUNT (nil means compute automatically)." + ;; current buffer: SQL*Plus output buffer + (when window-system + (if (>= (sqlplus-color-percentage (face-background 'default)) 50) + (progn + (set-face-attribute 'sqlplus-table-head-face nil + :background (sqlplus-shine-color (face-background 'default) -70) :foreground (face-background 'default)) + (set-face-attribute 'sqlplus-table-even-rows-face nil + :background (sqlplus-shine-color (face-background 'default) -20) :overline (face-background 'default)) + (set-face-attribute 'sqlplus-table-odd-rows-face nil + :background (sqlplus-shine-color (face-background 'default) -30) :overline (face-background 'default))) + (set-face-attribute 'sqlplus-table-head-face nil + :background (sqlplus-shine-color (face-background 'default) +70) :foreground (face-background 'default)) + (set-face-attribute 'sqlplus-table-even-rows-face nil + :background (sqlplus-shine-color (face-background 'default) +20) :overline (face-background 'default)) + (set-face-attribute 'sqlplus-table-odd-rows-face nil + :background (sqlplus-shine-color (face-background 'default) +30) :overline (face-background 'default)))) + (let* ((column-infos (car lst)) + (rows (cadr lst)) + (slip-width 0) + (table-header-height 1) + (table-area-width (1- (let ((side-window (sqlplus-get-side-window))) (if side-window (window-width side-window) (frame-width))))) + ;; may be nil, which means no limit + (table-area-height (let ((side-window (sqlplus-get-side-window))) + (when side-window + (- (window-height side-window) 2 (if mode-line-format 1 0) (if header-line-format 1 0))))) + (column-separator-width (if sqlplus-elegant-style 1.2 (max (length sqlplus-table-col-separator) (length sqlplus-table-col-head-separator)))) + rows-per-slip ;; data rows per slip + (slip-separator-width (if sqlplus-elegant-style 1.5 sqlplus-slip-separator-width)) + (slip-separator (make-string (max 0 (if sqlplus-elegant-style 1 sqlplus-slip-separator-width)) ?\ )) + (last-msg (caddr lst))) + (when sqlplus-elegant-style + (put-text-property 0 1 'display (cons 'space (list :width slip-separator-width)) slip-separator)) + (when (<= table-area-height table-header-height) + (setq table-area-height nil)) + (when (and window-system sqlplus-elegant-style table-area-height (> table-area-height 3)) + ;; overline makes glyph higher... + (setq table-area-height (- table-area-height (round (/ (* 20.0 (- table-area-height 3)) (face-attribute 'default :height)))))) + (when column-infos + (goto-char (point-max)) + (beginning-of-line) + ;; slip width (without separator between slips) + (dolist (col-info column-infos) + (when (col-desc-min-prefix-len col-info) + (setf (col-desc-preferred-width col-info) (max (string-width (col-desc-name col-info)) + (- (col-desc-preferred-width col-info) (col-desc-min-prefix-len col-info))))) + (incf slip-width (+ (col-desc-preferred-width col-info) column-separator-width))) + (when (> slip-width 0) + (setq slip-width (+ (- slip-width column-separator-width) (if sqlplus-elegant-style 1.0 0)))) + ;; computing slip count if not known yet + (unless slips-count + (setq slips-count + (if table-area-height (min (ceiling (/ (float (length rows)) (max 1 (- table-area-height table-header-height 2)))) + (max 1 (floor (/ (float table-area-width) (+ slip-width slip-separator-width))))) + 1))) + (setq slips-count (max 1 (min slips-count (length rows)))) ; slip count <= data rows + (setq rows-per-slip (ceiling (/ (float (length rows)) slips-count))) + (when (> rows-per-slip 0) + (setq slips-count (max 1 (min (ceiling (/ (float (length rows)) rows-per-slip)) slips-count)))) + + (let ((table-begin-point (point))) + (dotimes (slip-no slips-count) + (let ((row-no 0) + (slip-begin-point (point)) + (rows-processed 0)) + ;; column names + (dolist (col-info column-infos) + (let* ((col-name (col-desc-name col-info)) + (spaces (max 0 (- (col-desc-preferred-width col-info) (string-width col-name)))) + (last-col-p (>= (1+ (col-desc-id col-info)) (length column-infos))) + (val (format (if sqlplus-elegant-style " %s%s %s" "%s%s%s") + col-name + (make-string spaces ?\ ) + (if last-col-p "" (if sqlplus-elegant-style " " sqlplus-table-col-separator))))) + (put-text-property 0 (if (or (not sqlplus-elegant-style) last-col-p) (length val) (1- (length val))) + 'face 'sqlplus-table-head-face val) + (when sqlplus-elegant-style + (put-text-property 0 1 'display '(space . (:width 0.5)) val) + (put-text-property (- (length val) (if last-col-p 1 2)) (- (length val) (if last-col-p 0 1)) 'display '(space . (:width 0.5)) val) + (unless last-col-p + (put-text-property (- (length val) 1) (length val) 'display '(space . (:width 0.2)) val))) + (insert val))) + (insert slip-separator) + (insert "\n") + ;; data rows + (while (and (< rows-processed rows-per-slip) + rows) + (let ((row (car rows))) + (setq rows (cdr rows)) + (incf rows-processed) + (let ((col-infos column-infos)) + (dolist (value row) + (let* ((col-info (car col-infos)) + (numeric-p (eq (col-desc-numeric col-info) 'y)) + (min-prefix (col-desc-min-prefix-len col-info))) + (when (and min-prefix + value + (>= (length value) min-prefix)) + (setq value (substring value min-prefix))) + (let* ((spaces (max 0 (- (col-desc-preferred-width col-info) (string-width value)))) + (val (if numeric-p + (format (if sqlplus-elegant-style " %s%s %s" "%s%s%s") + (make-string spaces ?\ ) + value + (if (cdr col-infos) (if sqlplus-elegant-style " " sqlplus-table-col-separator) "")) + (format (if sqlplus-elegant-style " %s%s %s" "%s%s%s") + value + (make-string spaces ?\ ) + (if (cdr col-infos) (if sqlplus-elegant-style " " sqlplus-table-col-separator) ""))))) + (put-text-property 0 (if (and sqlplus-elegant-style (cdr col-infos)) (- (length val) 1) (length val)) + 'face (if (evenp row-no) + 'sqlplus-table-even-rows-face + 'sqlplus-table-odd-rows-face) val) + (when sqlplus-elegant-style + (put-text-property 0 1 'display '(space . (:width 0.5)) val) + (put-text-property (- (length val) (if (cdr col-infos) 2 1)) + (- (length val) (if (cdr col-infos) 1 0)) + 'display '(space . (:width 0.5)) val) + (when (cdr col-infos) + (put-text-property (- (length val) 1) (length val) 'display '(space . (:width 0.2)) val))) + (setq col-infos (cdr col-infos)) + (insert val)))) + (incf row-no) + (insert slip-separator) + (insert "\n")))) + (when (> slip-no 0) + (delete-backward-char 1) + (let ((slip-end-point (point))) + (kill-rectangle slip-begin-point slip-end-point) + (delete-region slip-begin-point (point-max)) + (goto-char table-begin-point) + (end-of-line) + (yank-rectangle) + (goto-char (point-max)) + )))) + (goto-char (point-max)) + (when (and last-msg (> (length last-msg) 0)) + (unless sqlplus-elegant-style (insert "\n")) + (let ((s (format "%s\n\n" (replace-regexp-in-string "\n+" " " last-msg)))) + (when sqlplus-elegant-style + (put-text-property (- (length s) 2) (1- (length s)) 'display '(space . (:height 1.5)) s)) + (insert s))))))) + +(defun sqlplus-send-user-string (str) + (interactive (progn (sqlplus-check-connection) + (if sqlplus-connect-string + (list (read-string "Send to process: " nil 'sqlplus-user-string-history "")) + (error "Works only in SQL*Plus buffer")))) + (let ((connect-string sqlplus-connect-string)) + (sqlplus-verify-buffer connect-string) + (let* ((process (get-process (sqlplus-get-process-name connect-string))) + (output-buffer-name (sqlplus-get-output-buffer-name connect-string))) + (sqlplus-echo-in-buffer output-buffer-name (concat str "\n")) + (send-string process (concat str "\n"))))) + +(defun sqlplus-prepare-update-alist (table-data) + (let ((column-infos (car table-data)) + (rows (cadr table-data)) + (msg (caddr table-data)) + alist) + (dolist (row rows) + (let* ((object-name (car row)) + (object-type (intern (downcase (cadr row)))) + (status (caddr row)) + (regexp-list (cdr (assq object-type alist))) + (pair (cons object-name (equal status "I")))) + (if regexp-list + (setcdr regexp-list (cons pair (cdr regexp-list))) + (setq regexp-list (list pair)) + (setq alist (cons (cons object-type regexp-list) alist))))) + alist)) + +(defun sqlplus-my-update-handler (connect-string table-data) + (let ((alist (sqlplus-prepare-update-alist table-data))) + (when (featurep 'ide-skel) + (funcall 'sqlplus-side-view-update-data connect-string alist)))) + +(defun sqlplus-my-handler (connect-string table-data) + (let ((alist (sqlplus-prepare-update-alist table-data)) + (sqlplus-font-lock-regexps (sqlplus-get-font-lock-regexps connect-string))) + (sqlplus-set-objects-alist alist connect-string) + (when (featurep 'ide-skel) + (funcall 'sqlplus-side-view-update-data connect-string alist)) + (clrhash sqlplus-font-lock-regexps) + (dolist (lst sqlplus-syntax-faces) + (let* ((object-type (car lst)) + (regexp-list (append (caddr lst) (mapcar 'car (cdr (assq object-type alist)))))) + (when regexp-list + (puthash object-type (concat "\\b" (regexp-opt regexp-list t) "\\b") sqlplus-font-lock-regexps)))) + (let ((map sqlplus-font-lock-regexps)) + (mapc (lambda (buffer) + (with-current-buffer buffer + (when (and (memq major-mode '(sqlplus-mode plsql-mode)) + (equal sqlplus-connect-string connect-string)) + (when font-lock-mode (font-lock-mode 1))))) + (buffer-list))))) + +(defun sqlplus-get-source-function (connect-string context string last-chunk) + (let* ((source-text (sqlplus-get-context-value context :source-text)) + (source-type (sqlplus-get-context-value context :source-type)) + (source-name (sqlplus-get-context-value context :source-name)) + (source-extension (sqlplus-get-context-value context :source-extension)) + (name (concat (upcase source-name) "." source-extension)) + finish) + (unless (sqlplus-get-context-value context :finished) + (setq source-text (concat source-text string)) + (sqlplus-set-context-value context :source-text source-text) + (when last-chunk + (if (string-match (regexp-quote sqlplus-end-of-source-sentinel) source-text) + (when (< (length source-text) (+ (length sqlplus-end-of-source-sentinel) 5)) + (setq last-chunk nil + finish "There is no such database object")) + (setq last-chunk nil))) + (when last-chunk + (setq finish t)) + (when finish + (sqlplus-set-context-value context :finished t) + (if (stringp finish) + (message finish) + (with-temp-buffer + (insert source-text) + (goto-char (point-min)) + (re-search-forward (regexp-quote sqlplus-end-of-source-sentinel) nil t) + (replace-match "") + (goto-char (point-max)) + (forward-comment (- (buffer-size))) + (when (equal source-type "TABLE") + (goto-char (point-min)) + (insert (format "table %s\n(\n" source-name)) + (goto-char (point-max)) + (delete-region (re-search-backward "," nil t) (point-max)) + (insert "\n);")) + (insert "\n/\n") + (unless (member source-type '("SEQUENCE" "TABLE" "SYNONYM" "INDEX")) + (insert "show err\n")) + (goto-char (point-min)) + (insert "create " (if (member source-type '("INDEX" "SEQUENCE" "TABLE")) "" "or replace ")) + (setq source-text (buffer-string))) + (with-current-buffer (get-buffer-create name) + (setq buffer-read-only nil) + (erase-buffer) + (insert source-text) + (goto-char (point-min)) + (set-visited-file-name (concat (file-name-as-directory temporary-file-directory) + (concat (make-temp-name (sqlplus-canonize-file-name (concat (upcase source-name) "_") "[$]")) "." source-extension))) + (rename-buffer name) + (condition-case err + (funcall (symbol-function 'plsql-mode)) + (error nil)) + (setq sqlplus-connect-string connect-string + buffer-read-only sqlplus-source-buffer-readonly-by-default-flag) + (save-buffer) + (save-selected-window + (let ((win (selected-window))) + (when (or (equal win (sqlplus-get-side-window)) + (and (fboundp 'ide-skel-side-view-window-p) + (funcall 'ide-skel-side-view-window-p win))) + (setq win (sqlplus-get-workbench-window))) + (set-window-buffer win (current-buffer)))))))))) + +(defun sqlplus-get-source (connect-string name type &optional schema-name) + "Fetch source for database object NAME in current or specified SCHEMA-NAME, and show the source in new buffer. +Possible TYPE values are in 'sqlplus-object-types'." + (interactive (let* ((thing (thing-at-point 'symbol)) + (obj-raw-name (read-string (concat "Object name" (if thing (concat " [default " thing "]") "") ": ") + nil + 'sqlplus-get-source-history (when thing thing))) + (completion-ignore-case t) + (type (completing-read "Object type: " (mapcar (lambda (type) (cons type nil)) sqlplus-object-types) nil t))) + (string-match "^\\(\\([^.]+\\)[.]\\)?\\(.*\\)$" obj-raw-name) + (list sqlplus-connect-string (match-string 3 obj-raw-name) type (match-string 2 obj-raw-name)))) + (setq type (upcase type)) + (let* ((sql + (cond ((equal type "SEQUENCE") + (format (concat "select 'sequence %s' || sequence_name || " + "decode( increment_by, 1, '', ' increment by ' || increment_by ) || " + "case when increment_by > 0 and max_value >= (1.0000E+27)-1 or increment_by < 0 and max_value = -1 then '' " + "else decode( max_value, null, ' nomaxvalue', ' maxvalue ' || max_value) end || " + "case when increment_by > 0 and min_value = 1 or increment_by < 0 and min_value <= (-1.0000E+26)+1 then '' " + "else decode( min_value, null, ' nominvalue', ' minvalue ' || min_value) end || " + "decode( cycle_flag, 'Y', ' cycle', '' ) || " + "decode( cache_size, 20, '', 0, ' nocache', ' cache ' || cache_size ) || " + "decode( order_flag, 'Y', ' order', '' ) " + "from %s where sequence_name = '%s'%s;") + (if schema-name (concat (upcase schema-name) ".") "") + (if schema-name "all_sequences" "user_sequences") + (upcase name) + (if schema-name (format " and sequence_owner = '%s'" (upcase schema-name)) ""))) + ((equal type "TABLE") + (format (concat "select ' ' || column_name || ' ' || data_type || " + "decode( data_type," + " 'VARCHAR2', '(' || to_char( data_length, 'fm9999' ) || ')'," + " 'NUMBER', decode( data_precision," + " null, ''," + " '(' || to_char( data_precision, 'fm9999' ) || decode( data_scale," + " null, ''," + " 0, ''," + " ',' || data_scale ) || ')' )," + " '') || " + "decode( nullable, 'Y', ' not null', '') || ','" + "from all_tab_columns " + "where owner = %s and table_name = '%s' " + "order by column_id;") + (if schema-name (concat "'" (upcase schema-name) "'") "user") + (upcase name))) + ((equal type "SYNONYM") + (format (concat "select " + "decode( owner, 'PUBLIC', 'public ', '' ) || 'synonym ' || " + "decode( owner, 'PUBLIC', '', user, '', owner || '.' ) || synonym_name || ' for ' || " + "decode( table_owner, user, '', table_owner || '.' ) || table_name || " + "decode( db_link, null, '', '@' || db_link ) " + "from all_synonyms where (owner = 'PUBLIC' or owner = %s) and synonym_name = '%s';") + (if schema-name (concat "'" (upcase schema-name) "'") "user") + (upcase name))) + ((equal type "VIEW") + (if schema-name (format "select 'view %s.' || view_name || ' as ', text from all_views where owner = '%s' and view_name = '%s';" + (upcase schema-name) (upcase schema-name) (upcase name)) + (format "select 'view ' || view_name || ' as ', text from user_views where view_name = '%s';" (upcase name)))) + ((or (equal type "PROCEDURE") + (equal type "FUNCTION")) + (if schema-name (format "select text from all_source where owner = '%s' and name = '%s' and type in ('PROCEDURE', 'FUNCTION') order by line;" + (upcase schema-name) (upcase name)) + (format "select text from user_source where name = '%s' and type in ('PROCEDURE', 'FUNCTION') order by line;" + (upcase name)))) + (t + (if schema-name (format "select text from all_source where owner = '%s' and name = '%s' and type = '%s' order by line;" + (upcase schema-name) (upcase name) (upcase type)) + (format "select text from user_source where name = '%s' and type = '%s' order by line;" + (upcase name) (upcase type)))))) + (prolog-commands (list "set echo off" + "set newpage 0" + "set space 0" + "set pagesize 0" + "set feedback off" + "set long 4000" + "set longchunksize 4000" + "set wrap on" + "set heading off" + "set trimspool on" + "set linesize 4000" + "set timing off")) + (extension (if (equal (downcase type) "package") "pks" "sql")) + (source-buffer-name (concat " " (upcase name) "." extension)) + (context-options (list (cons :dont-parse-result 'dont-parse) + (cons :source-text nil) + (cons :source-type type) + (cons :source-name name) + (cons :source-extension extension) + (cons :result-function 'sqlplus-get-source-function)))) + (sqlplus-execute connect-string sql context-options prolog-commands t t) + (sqlplus-execute connect-string (format "select '%s' from dual;" sqlplus-end-of-source-sentinel) context-options prolog-commands t t))) + +(defun sqlplus-canonize-file-name (file-name regexp) + (while (string-match regexp file-name) + (setq file-name (replace-match "!" nil t file-name))) + file-name) + +(defun sqlplus-define-user-variables (string) + (when string + (let (variables-list + define-commands + (index 0)) + (while (setq index (string-match "&+\\(\\(\\sw\\|\\s_\\)+\\)" string index)) + (let ((var-name (match-string 1 string))) + (setq index (+ 2 index)) + (unless (member var-name variables-list) + (push var-name variables-list)))) + (dolist (var-name (reverse variables-list)) + (let* ((default-value (gethash var-name sqlplus-user-variables nil)) + (value (read-string (format (concat "Variable value for %s" (if default-value (format " [default: %s]" default-value) "") ": ") var-name) + nil 'sqlplus-user-variables-history default-value))) + (unless value + (error "There is no value for %s defined" var-name)) + (setq define-commands (cons (format "define %s=%s" var-name value) define-commands)) + (puthash var-name value sqlplus-user-variables))) + define-commands))) + +(defun sqlplus-parse-region (start end) + (let ((sql (buffer-substring start end))) + (save-excursion + ;; Strip whitespace from beginning and end, just to be neat. + (if (string-match "\\`[ \t\n]+" sql) + (setq sql (substring sql (match-end 0)))) + (if (string-match "[ \t\n]+\\'" sql) + (setq sql (substring sql 0 (match-beginning 0)))) + (setq sql (replace-regexp-in-string "^[ \t]*--.*[\n]?" "" sql)) + (when (zerop (length sql)) + (error "Nothing to send")) + ;; Now the string should end with an sqlplus-terminator. + (if (not (string-match "\\(;\\|/\\|[.]\\)\\'" sql)) + (setq sql (concat sql ";")))) + sql)) + +(defun sqlplus-show-html-fun (context connect-string begin end interrupted) + (let ((output-file (expand-file-name (substitute-in-file-name sqlplus-html-output-file-name))) + (sql (sqlplus-get-context-value context :htmlized-html-command)) + (html (buffer-substring begin end)) + (header-html (eval sqlplus-html-output-header))) + (let ((case-fold-search t)) + (while (and (string-match "\\`[ \t\n]*\\(
\\|

\\)?" html) (match-string 0 html) (> (length (match-string 0 html)) 0)) + (setq html (replace-match "" nil t html))) + (when (> (length html) 0) + (sqlplus-execute connect-string "" nil '("set markup html off") 'no-echo 'dont-show-output-buffer) + (find-file output-file) + (erase-buffer) + (insert (concat "\n" + "\n" + " \n" + (sqlplus-get-context-value context :head) "\n" + "\n" + "\n" + (if header-html header-html "") + (if sqlplus-html-output-sql sql "") + "

" + html "\n" + "\n" + "")) + (goto-char (point-min)) + (save-buffer))))) + +(defun sqlplus-refine-html (html remove-entities) + (string-match "\\`\"?\\(\\(.\\|\n\\)*?\\)\"?[ \t\n]*\\'" html) + (setq html (match-string 1 html)) + (if remove-entities + (progn + (while (string-match """ html) (setq html (replace-match "\"" nil t html))) + (while (string-match "<" html) (setq html (replace-match "<" nil t html))) + (while (string-match ">" html) (setq html (replace-match ">" nil t html))) + (while (string-match "&" html) (setq html (replace-match "&" nil t html)))) + (while (string-match "&" html) (setq html (replace-match "&" nil t html))) + (while (string-match ">" html) (setq html (replace-match ">" nil t html))) + (while (string-match "<" html) (setq html (replace-match "<" nil t html))) + (while (string-match "\"" html) (setq html (replace-match """ nil t html)))) + (string-match "\\`\"?\\(\\(.\\|\n\\)*?\\)\"?[ \t\n]*\\'" html) + (setq html (match-string 1 html)) + html) + +(defun sqlplus-show-markup-fun (context connect-string begin end interrupted) + (goto-char begin) + (let ((head "") + (body "") + preformat) + (when (re-search-forward (concat "\\bHEAD\\b[ \t\n]*\\(\\(.\\|\n\\)*\\)[ \t\n]*" + "\\bBODY\\b[ \t\n]*\\(\\(.\\|\n\\)*\\)[ \t\n]*" + "\\bTABLE\\b\\(.\\|\n\\)*PREFORMAT[ \t\n]+\\(ON\\|OFF\\)\\b") nil t) + (setq head (match-string 1) + body (match-string 3) + preformat (string= (downcase (match-string 6)) "on")) + (setq head (sqlplus-refine-html head t) + body (sqlplus-refine-html body t)) + (let ((context-options (list (cons :result-function 'sqlplus-show-html-fun) + (cons :current-command-input-buffer-name (sqlplus-get-context-value context :current-command-input-buffer-name)) + (cons :html-command (sqlplus-get-context-value context :html-command)) + (cons :htmlized-html-command (sqlplus-get-context-value context :htmlized-html-command)) + (cons :head head) + (cons :body body))) + (prolog-commands (list "set wrap on" + (format "set linesize %S" (if preformat (1- (frame-width)) 4000)) + "set pagesize 50000" + "btitle off" + "repfooter off" + "set markup html on"))) + (sqlplus-execute connect-string (sqlplus-get-context-value context :html-command) context-options prolog-commands 'no-echo 'dont-show-output-buffer))))) + +(defun sqlplus-htmlize (begin end) + (let (result) + (when (featurep 'htmlize) + (let* ((htmlize-output-type 'font) + (buffer (funcall (symbol-function 'htmlize-region) begin end))) + (with-current-buffer buffer + (goto-char 1) + (re-search-forward "

[ \t\n]*\\(\\(.\\|\n\\)*?\\)[ \t\n]*
" nil t) + (setq result (concat "
" (match-string 1) "
"))) + (kill-buffer buffer))) + (unless result + (setq result (sqlplus-refine-html (buffer-substring begin end) nil))) + result)) + +(defun sqlplus--send (connect-string sql &optional arg no-echo html start end) + (if html + (let* ((context-options (list (cons :result-function 'sqlplus-show-markup-fun) + (cons :current-command-input-buffer-name (buffer-name)) + (cons :html-command sql) + (cons :htmlized-html-command (if (and (eq sqlplus-html-output-sql 'elegant) (featurep 'htmlize)) + (sqlplus-htmlize start end) + (sqlplus-refine-html sql nil)))))) + (sqlplus-execute connect-string "show markup\n" context-options nil 'no-echo 'dont-show-output-buffer)) + (let* ((no-parse (consp arg)) + (context-options (list (cons :dont-parse-result (consp arg)) + (cons :columns-count (if (integerp arg) + (if (zerop arg) nil arg) + (if sqlplus-multi-output-tables-default-flag nil 1))) + (cons :current-command-input-buffer-name (buffer-name)))) + (prolog-commands (list (format "set wrap %s" (if no-parse "on" sqlplus-default-wrap)) + (format "set linesize %s" (if (consp arg) (1- (frame-width)) 4000)) + (format "set pagesize %S" (if no-parse 50000 sqlplus-pagesize)) + (format "btitle %s" + (if no-parse "off" (concat "left '" sqlplus-page-separator "'"))) + (format "repfooter %s" + (if no-parse "off" (concat "left '" sqlplus-repfooter "'")))))) + (sqlplus-execute connect-string sql context-options prolog-commands no-echo)))) + +(defun sqlplus-explain () + (interactive) + (sqlplus-check-connection) + (when (buffer-file-name) + (condition-case err + (save-buffer) + (error (message (error-message-string err))))) + (let* ((region (sqlplus-mark-current))) + (setq sqlplus-region-beginning-pos (car region) + sqlplus-region-end-pos (cdr region)) + (if (and sqlplus-region-beginning-pos sqlplus-region-end-pos) + (let ((sql (sqlplus-parse-region (car region) (cdr region))) + (case-fold-search t)) + (if (string-match "^[\n\t ]*explain[\n\t ]+plan[\t\t ]+for\\>" sql) + (sqlplus--send sqlplus-connect-string sql nil nil nil) + (setq sql (concat (sqlplus-fontify-string sqlplus-connect-string "explain plan for ") sql)) + (sqlplus--send sqlplus-connect-string sql nil nil nil))) + (error "Point doesn't indicate any command to execute")))) + +(defun sqlplus-send-region (arg start end &optional no-echo html) + "Send a region to the SQL*Plus process." + (interactive "P\nr") + (sqlplus-check-connection) + (sqlplus--send sqlplus-connect-string (sqlplus-parse-region start end) arg no-echo html start end)) + +(defun sqlplus-user-command (connect-string sql result-proc) + (let* ((context-options (list (cons :user-function result-proc) + (cons :columns-count 1))) + (prolog-commands (list (format "set wrap %s" sqlplus-default-wrap) + "set linesize 4000" + "set timing off" + "set pagesize 50000" + "btitle off" + (format "repfooter %s" (concat "left '" sqlplus-repfooter "'"))))) + (sqlplus-execute connect-string sql context-options prolog-commands 'no-echo 'dont-show-output-buffer))) + + +(defun sqlplus-hidden-select (connect-string sql result-proc) + (let* ((context-options (list (cons :result-table-function result-proc) + (cons :columns-count 1))) + (prolog-commands (list (format "set wrap %s" sqlplus-default-wrap) + "set linesize 4000" + "set pagesize 50000" + "btitle off" + (format "repfooter %s" (concat "left '" sqlplus-repfooter "'"))))) + (sqlplus-execute connect-string sql context-options prolog-commands 'no-echo 'dont-show-output-buffer))) + +;; "appi[nfo]" -> '("appinfo" "appi") +(defun sqlplus-full-forms (name) + (if (string-match "\\`\\([^[]*\\)?\\[\\([^]]+\\)\\]\\([^]]*\\)?\\'" name) + (list (replace-match "\\1\\2\\3" t nil name) + (replace-match "\\1\\3" t nil name)) + (list name))) + +(defun sqlplus-get-canonical-command-name (name) + (let ((association (assoc (downcase name) sqlplus-system-variables))) + (if association (cdr association) name))) + + +(defun sqlplus-execute (connect-string sql context-options prolog-commands &optional no-echo dont-show-output-buffer) + (sqlplus-verify-buffer connect-string) + (let* ((process-buffer-name (sqlplus-get-process-buffer-name connect-string)) + (process-buffer (get-buffer process-buffer-name)) + (output-buffer-name (sqlplus-get-output-buffer-name connect-string)) + (echo-prolog (concat "\n" sqlplus-output-separator " " (current-time-string) "\n\n")) + (process (get-buffer-process process-buffer-name)) + set-prolog-commands commands command-no + (history-buffer (sqlplus-get-history-buffer connect-string)) + (defines (sqlplus-define-user-variables sql))) + (setq prolog-commands (append (sqlplus-initial-strings) prolog-commands)) + (when process-buffer + (with-current-buffer process-buffer + (setq command-no sqlplus-command-seq) + (incf sqlplus-command-seq) + (setq context-options (append (list (cons :id command-no) (cons :sql sql)) (copy-list context-options))) + (setq sqlplus-command-contexts (reverse (cons context-options (reverse sqlplus-command-contexts)))))) + ;; move all "set" commands from prolog-commands to set-prolog-commands + (setq prolog-commands (delq nil (mapcar (lambda (command) (if (string-match "^\\s-*[sS][eE][tT]\\s-+" command) + (progn + (setq set-prolog-commands + (append set-prolog-commands + (list (substring command (length (match-string 0 command)))))) + nil) + command)) + prolog-commands))) + ;; remove duplicates commands from prolog-commands (last entries win) + (let (spc-alist) + (dolist (command prolog-commands) + (let* ((name (progn (string-match "^\\S-+" command) (downcase (match-string 0 command)))) + (association (assoc name spc-alist))) + (if (and association (not (equal name "define"))) + (setcdr association command) + (setq spc-alist (cons (cons name command) spc-alist))))) + (setq prolog-commands (mapcar (lambda (pair) (cdr pair)) (reverse spc-alist)))) + + (setq prolog-commands (append prolog-commands defines)) + (setq set-prolog-commands (append (list (format "sqlprompt '%s%S%s'" sqlplus-prompt-prefix command-no sqlplus-prompt-suffix)) set-prolog-commands)) + + ;; remove duplicates from set-prolog-commands (last entries win) + (let (spc-alist) + (dolist (set-command set-prolog-commands) + (let* ((name (progn (string-match "^\\S-+" set-command) (downcase (sqlplus-get-canonical-command-name (match-string 0 set-command))))) + (association (assoc name spc-alist))) + (if association + (setcdr association set-command) + (setq spc-alist (cons (cons name set-command) spc-alist))))) + (setq set-prolog-commands (mapcar (lambda (pair) (cdr pair)) (reverse spc-alist)))) + + (setq commands (concat (mapconcat 'identity (append + (list (concat "set " (mapconcat 'identity set-prolog-commands " "))) + prolog-commands + (list sql)) "\n") + "\n")) + (when history-buffer + (with-current-buffer history-buffer + (goto-char (point-max)) + (insert echo-prolog) + (insert (concat commands "\n")))) + (let ((saved-window (cons (selected-window) (window-buffer (selected-window)))) + (input-buffer (get-buffer (sqlplus-get-input-buffer-name connect-string)))) + (unless no-echo + (sqlplus-echo-in-buffer output-buffer-name echo-prolog) + (let ((old-suppress-show-output-buffer sqlplus-suppress-show-output-buffer)) + (unwind-protect + (save-selected-window + (setq sqlplus-suppress-show-output-buffer dont-show-output-buffer) + (when (and output-buffer-name + (get-buffer output-buffer-name)) + (with-current-buffer (get-buffer output-buffer-name) + (sqlplus-buffer-bottom connect-string) + (sqlplus-buffer-mark-current connect-string)))) + (setq sqlplus-suppress-show-output-buffer old-suppress-show-output-buffer))) + (sqlplus-echo-in-buffer output-buffer-name (concat sql "\n\n") nil t) + (save-selected-window + (unless dont-show-output-buffer + (when (and output-buffer-name + (get-buffer output-buffer-name)) + (with-current-buffer (get-buffer output-buffer-name) + (sqlplus-buffer-redisplay-current connect-string)))))) + (if (window-live-p (car saved-window)) + (select-window (car saved-window)) + (if (get-buffer-window (cdr saved-window)) + (select-window (get-buffer-window (cdr saved-window))) + (when (and input-buffer + (get-buffer-window input-buffer)) + (select-window (get-buffer-window input-buffer)))))) + (send-string process commands))) + +(defun sqlplus-fontify-string (connect-string string) + (let* ((input-buffer-name (sqlplus-get-input-buffer-name connect-string)) + (input-buffer (when input-buffer-name (get-buffer input-buffer-name))) + (result string)) + (when (and input-buffer (buffer-live-p input-buffer)) + (with-current-buffer input-buffer + (save-excursion + (goto-char (point-max)) + (let ((pos (point))) + (insert "\n\n") + (insert string) + (font-lock-fontify-block (+ (count "\n" string) 2)) + (setq result (buffer-substring (+ pos 2) (point-max))) + (delete-region pos (point-max)))))) + result)) + +(defvar plsql-mark-backward-list nil) + +(unless plsql-mode-map + (setq plsql-mode-map (copy-keymap sql-mode-map)) + (define-key plsql-mode-map "\M-." 'sqlplus-file-get-source) + (define-key plsql-mode-map [C-down-mouse-1] 'sqlplus-mouse-select-identifier) + (define-key plsql-mode-map [C-mouse-1] 'sqlplus-file-get-source-mouse) + (define-key plsql-mode-map "\C-c\C-g" 'plsql-begin) + (define-key plsql-mode-map "\C-c\C-q" 'plsql-loop) + (define-key plsql-mode-map "\C-c\C-z" 'plsql-if) + (define-key plsql-mode-map "\C-c\C-c" 'plsql-compile) + (define-key plsql-mode-map [tool-bar plsql-prev-mark] + (list 'menu-item "Previous mark" 'plsql-prev-mark + :image plsql-prev-mark-image + :enable 'plsql-mark-backward-list))) + +(defvar plsql-continue-anyway nil + "Local in input buffer (plsql-mode).") +(make-variable-buffer-local 'plsql-continue-anyway) + +(defun sqlplus-switch-to-buffer (buffer-or-path &optional line-no) + (if (fboundp 'ide-skel-select-buffer) + (funcall 'ide-skel-select-buffer buffer-or-path line-no) + (let ((buffer (or (and (bufferp buffer-or-path) buffer-or-path) + (find-file-noselect buffer-or-path)))) + (switch-to-buffer buffer) + (goto-line line-no)))) + +(defun plsql-prev-mark () + (interactive) + (let (finish) + (while (and plsql-mark-backward-list + (not finish)) + (let* ((marker (pop plsql-mark-backward-list)) + (buffer (marker-buffer marker)) + (point (marker-position marker))) + (set-marker marker nil) + (when (and buffer + (or (not (eq (current-buffer) buffer)) + (not (eql (point) point)))) + (sqlplus-switch-to-buffer buffer) + (goto-char point) + (setq finish t)))) + ;; (message "BACK: %S -- FORWARD: %S" plsql-mark-backward-list plsql-mark-forward-list) + (force-mode-line-update) + (sit-for 0))) + +(defun sqlplus-mouse-select-identifier (event) + (interactive "@e") + (with-selected-window (posn-window (event-start event)) + (save-excursion + (let* ((point (posn-point (event-start event))) + (identifier (progn (goto-char point) (thing-at-point 'symbol))) + (ident-regexp (when identifier (regexp-quote identifier)))) + (push (point-marker) plsql-mark-backward-list) + (when ident-regexp + (save-excursion + (while (not (looking-at ident-regexp)) + (backward-char)) + (sqlplus-mouse-set-selection (current-buffer) (point) (+ (point) (length identifier)) 'highlight))))))) + +(defun sqlplus-file-get-source-mouse (event) + (interactive "@e") + (let (ident) + (with-selected-window (posn-window (event-start event)) + (save-excursion + (goto-char (posn-point (event-start event))) + (setq ident (thing-at-point 'symbol)))) + (sqlplus-file-get-source sqlplus-connect-string ident nil) + (sit-for 0))) + +(defun plsql-compile (&optional arg) + "Save buffer and send its content to SQL*Plus. +You must enter connect-string if buffer is disconnected; with +argument you can change connect-string even for connected +buffer." + (interactive "P") + (let (aborted + exists-show-error-command + (case-fold-search t)) + (save-window-excursion + (save-excursion + ;; ask for "/" and "show err" if absent + (let ((old-point (point)) + show-err-needed + exists-run-command best-point finish) + (goto-char (point-min)) + (setq show-err-needed (let ((case-fold-search t)) + (re-search-forward "create\\([ \t\n]+or[ \t\n]+replace\\)?[ \t\n]+\\(package\\|procedure\\|function\\|trigger\\|view\\|type\\)" nil t))) + (goto-char (point-max)) + (forward-comment (- (buffer-size))) + (re-search-backward "^\\s-*show\\s-+err" nil t) + (forward-comment (- (buffer-size))) + (condition-case nil (forward-char) (error nil)) + (setq best-point (point)) + (goto-char (point-min)) + (setq exists-run-command (re-search-forward "^\\s-*/[^*]" nil t)) + (goto-char (point-min)) + (setq exists-show-error-command (or (not show-err-needed) (re-search-forward "^\\s-*show\\s-+err" nil t))) + (while (and (not plsql-continue-anyway) (or (not exists-run-command) (not exists-show-error-command)) (not finish)) + (goto-char best-point) + (let ((c (read-char + (format "Cannot find %s. (I)nsert it at point, (A)bort, (C)ontinue anyway" + (concat (unless exists-run-command "\"/\"") + (unless (or exists-run-command exists-show-error-command) " and ") + (unless exists-show-error-command "\"show err\"")))))) + (cond ((memq c '(?i ?I)) + (unless exists-run-command (insert "/\n")) + (unless exists-show-error-command (insert "show err\n")) + (setq finish t)) + ((memq c '(?a ?A)) + (setq aborted t + finish t)) + ((memq c '(?c ?C)) + (setq plsql-continue-anyway t) + (setq finish t)))))))) + (unless aborted + (save-buffer) + (let* ((buffer (current-buffer)) + (input-buffer-name (buffer-name)) + (file-path (sqlplus-file-truename (buffer-file-name))) + (compilation-buffer (get-buffer sqlplus-plsql-compilation-results-buffer-name)) + (context-options (list (cons :last-compiled-file-path file-path) + (cons :current-command-input-buffer-name input-buffer-name) + (cons :compilation-expected exists-show-error-command))) + (prolog-commands (list (format "set wrap %s" sqlplus-default-wrap) + "set linesize 4000" + (format "set pagesize %S" sqlplus-pagesize) + (format "btitle %s" (concat "left '" sqlplus-page-separator "'")) + (format "repfooter %s" (concat "left '" sqlplus-repfooter "'"))))) + (when (or (not sqlplus-connect-string) + arg) + (setq sqlplus-connect-string (car (sqlplus-read-connect-string nil (caar (sqlplus-divide-connect-strings)))))) + (sqlplus sqlplus-connect-string nil (when plsql-auto-parse-errors-flag 'dont-show-output-buffer)) + (set-buffer buffer) + (force-mode-line-update) + (when font-lock-mode (font-lock-mode 1)) + (when compilation-buffer + (with-current-buffer compilation-buffer + (let ((inhibit-read-only t)) + (erase-buffer)))) + (setq prolog-commands (append prolog-commands (sqlplus-define-user-variables (buffer-string)))) + (sqlplus-execute sqlplus-connect-string (concat "@" file-path) context-options prolog-commands nil exists-show-error-command))))) + +(defun plsql-parse-errors (last-compiled-file-path) + (let ((file-name (file-name-nondirectory last-compiled-file-path)) + error-list) + (put-text-property 0 (length file-name) 'face 'font-lock-warning-face file-name) + (save-excursion + (when (re-search-forward "^LINE/COL\\>" nil t) + (beginning-of-line 3) + (while (re-search-forward "^\\([0-9]+\\)/\\([0-9]+\\)\\s-*\\(\\(.\\|\n\\)*?\\)[\r\t ]*\n\\([\r\t ]*\\(\n\\|\\'\\)\\|[0-9]+\\)" nil t) + (let ((line-no (match-string 1)) + (column-no (match-string 2)) + (errmsg (match-string 3)) + label) + (goto-char (match-beginning 5)) + (while (string-match "\\s-\\s-+" errmsg) + (setq errmsg (replace-match " " nil t errmsg))) + (put-text-property 0 (length line-no) 'face 'font-lock-variable-name-face line-no) + (put-text-property 0 (length column-no) 'face 'font-lock-variable-name-face column-no) + (setq label (concat file-name ":" line-no ":" column-no ": " errmsg)) + (put-text-property 0 (length label) 'mouse-face 'highlight label) + (push label error-list))))) + (save-excursion + (while (re-search-forward "\\s-\\([0-9]+\\):\n\\(ORA-[0-9]+[^\n]*\\)\n" nil t) + (let ((line-no (match-string 1)) + (errmsg (match-string 2)) + label) + (put-text-property 0 (length line-no) 'face 'font-lock-variable-name-face line-no) + (setq label (concat file-name ":" line-no ": " errmsg)) + (put-text-property 0 (length label) 'mouse-face 'highlight label) + (push label error-list)))) + (save-excursion + (while (re-search-forward "\\(\\(SP2\\|CPY\\)-[0-9]+:[^\n]*\\)\n" nil t) + (let ((errmsg (match-string 1)) + label) + (setq label (concat file-name ":" errmsg)) + (put-text-property 0 (length label) 'mouse-face 'highlight label) + (push label error-list)))) + error-list)) + +(defun plsql-display-errors (dir error-list) + (let ((buffer (get-buffer-create sqlplus-plsql-compilation-results-buffer-name))) + (save-selected-window + (save-excursion + (set-buffer buffer) + (let ((inhibit-read-only t)) + (erase-buffer) + (setq default-directory dir) + (insert (format "cd %s\n" default-directory)) + (insert (format "Compilation results\n")) + (compilation-minor-mode 1) + (dolist (msg (reverse error-list)) + (insert msg) + (insert "\n")) + (insert (format "\n(%s errors)\n" (length error-list)))) + (when (and error-list (fboundp 'compile-reinitialize-errors) (funcall (symbol-function 'compile-reinitialize-errors) t))) + (switch-to-buffer-other-window buffer) + (goto-line 1) + (goto-line 3))))) + + +(defun sqlplus-file-truename (file-name) + (if file-name + (file-truename file-name) + file-name)) + +(defun sqlplus--hidden-buffer-name-p (buffer-name) + (equal (elt buffer-name 0) 32)) + +(defun sqlplus-get-workbench-window () + "Return upper left window" + (if (fboundp 'ide-get-workbench-window) + (funcall (symbol-function 'ide-get-workbench-window)) + (let (best-window) + (dolist (win (copy-list (window-list nil 1))) + (when (not (sqlplus--hidden-buffer-name-p (buffer-name (window-buffer win)))) + (if (null best-window) + (setq best-window win) + (let* ((best-window-coords (window-edges best-window)) + (win-coords (window-edges win))) + (when (or (< (cadr win-coords) (cadr best-window-coords)) + (and (= (cadr win-coords) (cadr best-window-coords)) + (< (car win-coords) (car best-window-coords)))) + (setq best-window win)))))) + ;; (message "BEST-WINDOW: %S" best-window) + best-window))) + +(defun sqlplus-get-side-window () + "Return bottom helper window, or nil if not found" + (if (fboundp 'ide-get-side-window) + (funcall (symbol-function 'ide-get-side-window)) + (let* ((workbench-window (sqlplus-get-workbench-window)) + best-window) + (dolist (win (copy-list (window-list nil 1))) + (when (and (not (sqlplus--hidden-buffer-name-p (buffer-name (window-buffer win)))) + (not (eq win workbench-window))) + (if (null best-window) + (setq best-window win) + (when (> (cadr (window-edges win)) (cadr (window-edges best-window))) + (setq best-window win))))) + best-window))) + +(defvar sqlplus--idle-tasks nil) + +(defun sqlplus--enqueue-task (fun &rest params) + (setq sqlplus--idle-tasks (reverse (cons (cons fun params) (reverse sqlplus--idle-tasks))))) + +(defun sqlplus--execute-tasks () + (dolist (task sqlplus--idle-tasks) + (let ((fun (car task)) + (params (cdr task))) + (condition-case var + (apply fun params) + (error (message (error-message-string var)))))) + (setq sqlplus--idle-tasks nil)) + +(add-hook 'post-command-hook 'sqlplus--execute-tasks) + +(defvar sqlplus-mouse-selection nil) + +(defun sqlplus-mouse-set-selection (buffer begin end mouse-face) + (interactive "@e") + (let ((old-buffer-modified-p (buffer-modified-p))) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (unwind-protect + (put-text-property begin end 'mouse-face mouse-face) + (set-buffer-modified-p old-buffer-modified-p) + (setq sqlplus-mouse-selection (when mouse-face (list buffer begin end)))))))) + +(defun sqlplus-clear-mouse-selection () + (when (and sqlplus-mouse-selection + (eq (event-basic-type last-input-event) 'mouse-1) + (not (memq 'down (event-modifiers last-input-event)))) + (sqlplus-mouse-set-selection (car sqlplus-mouse-selection) (cadr sqlplus-mouse-selection) (caddr sqlplus-mouse-selection) nil))) + +(add-hook 'plsql-mode-hook + (lambda () + (modify-syntax-entry ?. "." sql-mode-syntax-table) + (setq sqlplus-font-lock-keywords-1 (sqlplus-set-font-lock-emacs-structures-for-level 1 major-mode)) + (setq sqlplus-font-lock-keywords-2 (sqlplus-set-font-lock-emacs-structures-for-level 2 major-mode)) + (setq sqlplus-font-lock-keywords-3 (sqlplus-set-font-lock-emacs-structures-for-level 3 major-mode)) + (setq font-lock-defaults '((sqlplus-font-lock-keywords-1 sqlplus-font-lock-keywords-2 sqlplus-font-lock-keywords-3) + nil t ((?_ . "w") (?$ . "w") (?# . "w") (?& . "w")))) + (orcl-mode 1) + (use-local-map plsql-mode-map) ; std + (add-hook 'post-command-hook 'sqlplus-clear-mouse-selection nil t))) + +(setq recentf-exclude (cons (concat "^" (regexp-quote (file-name-as-directory temporary-file-directory))) + (when (boundp 'recentf-exclude) + recentf-exclude))) + +(when (fboundp 'ide-register-persistent-var) + (funcall (symbol-function 'ide-register-persistent-var) 'sqlplus-connect-strings-alist + ;; save proc + (lambda (alist) + (mapcar (lambda (pair) + (if sqlplus-save-passwords + pair + (cons (car pair) nil))) + alist)) + ;; load proc + (lambda (alist) + (setq sqlplus-connect-string-history (mapcar (lambda (pair) (car pair)) alist)) + alist))) + +(defun get-all-dirs (root-dir) + (let ((list-to-see (list root-dir)) + result-list) + (while list-to-see + (let* ((dir (pop list-to-see)) + (children (directory-files dir t))) + (push dir result-list) + (dolist (child children) + (when (and (not (string-match "^[.]+"(file-name-nondirectory child))) + (file-directory-p child)) + (push child list-to-see))))) + result-list)) + +(defun sqlplus-command-line () + (interactive) + (if (comint-check-proc "*SQL*") + (pop-to-buffer "*SQL*") + (let* ((pair (sqlplus-read-connect-string nil (when sqlplus-connect-string (car (refine-connect-string sqlplus-connect-string))))) + (qualified-cs (car pair)) + (refined-cs (cadr pair)) + (password (cdr (refine-connect-string qualified-cs)))) + (if (string-match "^\\([^@]*\\)@\\(.*\\)$" refined-cs) + (let ((old-sql-get-login-fun (symbol-function 'sql-get-login))) + (setq sql-user (match-string 1 refined-cs) + sql-password password + sql-database (match-string 2 refined-cs)) + (unwind-protect + (progn + (fset 'sql-get-login (lambda (&rest whatever) nil)) + (sql-oracle)) + (fset 'sql-get-login old-sql-get-login-fun))) + (error "Connect string must be in form login@sid"))))) + +(defun sqlplus-find-tnsnames () + (interactive) + (let* ((ora-home-dir (or (getenv "ORACLE_HOME") (error "Environment variable ORACLE_HOME not set"))) + found + (list-to-see (list ora-home-dir))) + (while (and (not found) list-to-see) + (let* ((dir (pop list-to-see)) + (children (condition-case nil (directory-files dir t) (error nil)))) + (dolist (child children) + (unless found + (if (string-match "admin.tnsnames\.ora$" child) + (progn + (setq found t) + (find-file child)) + (if (and (not (string-match "^[.]+" (file-name-nondirectory child))) + (file-directory-p child)) + (push child list-to-see))))))) + (unless found + (message "File tnsnames.ora not found")))) + +(defun sqlplus-remove-help-echo (list) + "Remove all HELP-ECHO properties from mode-line format value" + (when (listp list) + (if (eq (car list) :propertize) + (while list + (when (eq (cadr list) 'help-echo) + (setcdr list (cdddr list))) + (setq list (cdr list))) + (dolist (elem list) (sqlplus-remove-help-echo elem))))) + +(when (>= emacs-major-version 22) + (sqlplus-remove-help-echo mode-line-modes)) + +(defun sqlplus-get-project-root-dir (path) + (let ((path (file-truename (substitute-in-file-name path))) + dir) + (if (file-directory-p path) + (progn + (setq path (file-name-as-directory path)) + (setq dir path)) + (setq dir (file-name-as-directory (file-name-directory path)))) + (let ((last-project-dir dir) + (dir-list (split-string dir "/")) + is-project) + (while (directory-files dir t (concat "^" "\\(\\.svn\\|CVS\\)$") t) + (setq is-project t + last-project-dir (file-name-as-directory dir) + dir (file-name-as-directory (file-name-directory (directory-file-name dir))))) + (when is-project + (let ((list (nthcdr (1- (length (split-string last-project-dir "/"))) dir-list))) + (cond ((equal (car list) "trunk") + (setq last-project-dir (concat last-project-dir "trunk/"))) + ((member (car list) '("branches" "tags")) + (setq last-project-dir (concat last-project-dir (car list) "/" (when (cdr list) (concat (cadr list) "/"))))) + (t))) + (setq dir last-project-dir))) + dir)) + +(defvar sqlplus-search-buffer-name "*search*") + +(defvar sqlplus-object-types-regexps + '( + ("TABLE" . "\\bcreate\\s+table\\s+[^(]*?\\b#\\b") + ("VIEW" . "\\bview\\s+.*?\\b#\\b") + ("INDEX" . "\\b(constraint|index)\\s+.*?\\b#\\b") + ("TRIGGER" . "\\btrigger\\s+.*?\\b#\\b") + ("SEQUENCE" . "\\bsequence\\s+.*?\\b#\\b") + ("SYNONYM" . "\\bsynonym\\s+.*?\\b#\\b") + ("SCHEMA" . "\\bcreate\\b.*?\\buser\\b.*?\\b#\\b") + ("PROCEDURE" . "\\b(procedure|function)\\b[^(]*?\\b#\\b") + ("PACKAGE" . "\\bpackage\\s+.*?\\b#\\b"))) + +(defvar sqlplus-root-dir-history nil) + +(defvar sqlplus-compare-report-buffer-name "*Comparation Report*") + +(defun sqlplus-compare-schema-to-filesystem (&optional arg) + (interactive "P") + (let* ((connect-string sqlplus-connect-string) + (objects-alist (sqlplus-get-objects-alist sqlplus-connect-string)) + (report-buffer (get-buffer-create sqlplus-compare-report-buffer-name)) + (types-length (- (length objects-alist) 2)) + (root-dir (or (sqlplus-get-root-dir connect-string) + (sqlplus-set-project-for-connect-string connect-string) + (error "Root dir not set"))) + (counter 0)) + (unless objects-alist + (error "Not ready yet - try again later")) + (save-excursion + (switch-to-buffer-other-window report-buffer)) + (with-current-buffer report-buffer + (let ((inhibit-read-only t)) + (erase-buffer) + (insert (format "%s %s vs. %s\n\n" (current-time-string) (car (refine-connect-string connect-string)) root-dir)) + (sit-for 0))) + (dolist (pair objects-alist) + (let ((type (upcase (format "%s" (car pair)))) + (names (cdr pair))) + (unless (member type '("SCHEMA" "COLUMN")) + (incf counter) + (message (format "%s (%d/%d)..." type counter types-length)) + (dolist (name-pair names) + (let* ((name (car name-pair)) + (grep-result (sqlplus-file-get-source sqlplus-connect-string name type 'batch-mode))) + (with-current-buffer report-buffer + (let ((inhibit-read-only t)) + (goto-char (point-max)) + (cond ((eql (length grep-result) 0) + (insert (format "%s %s: not found\n" type name)) + (sit-for 0)) + ((and arg + (> (length grep-result) 1)) + (insert (format "%s %s:\n" type name)) + (dolist (list grep-result) + (insert (format " %s:%d %s\n" (car list) (cadr list) (caddr list)))) + (sit-for 0)) + (t))))))))) + (message "Done.") + (with-current-buffer report-buffer + (goto-char (point-min))))) + +(defun sqlplus-proj-find-files (dir file-predicate &optional dir-predicate) + (setq dir (file-name-as-directory (file-truename (substitute-in-file-name dir)))) + (let (result-list) + (mapcar (lambda (path) + (if (file-directory-p path) + (when (and (file-accessible-directory-p path) + (or (null dir-predicate) + (funcall dir-predicate path))) + (setq result-list (append result-list (sqlplus-proj-find-files path file-predicate dir-predicate)))) + (when (or (null file-predicate) + (funcall file-predicate path)) + (push path result-list)))) + (delete (concat (file-name-as-directory dir) ".") + (delete (concat (file-name-as-directory dir) "..") + (directory-files dir t nil t)))) + result-list)) + +(defvar sqlplus-proj-ignored-extensions '("semantic.cache")) + +(defun sqlplus-mode-file-regexp-list (mode-symbol-list) + (delq nil (mapcar (lambda (element) + (let ((fun-name (if (listp (cdr element)) (cadr element) (cdr element)))) + (when (memq fun-name mode-symbol-list) (cons (car element) fun-name)))) + auto-mode-alist))) + +(defun sqlplus-find-project-files (root-dir mode-symbol-list predicate) + (let ((obj-file-regexp-list (delq nil (mapcar (lambda (element) + (let ((len (length element))) + (unless (and (> len 0) + (equal (elt element (1- len)) ?/)) + (concat (regexp-quote element) "$")))) + (append sqlplus-proj-ignored-extensions completion-ignored-extensions)))) + (mode-file-regexp-list (sqlplus-mode-file-regexp-list mode-symbol-list))) ; (file-path-regexp . major-mode-function-symbol) + (when (and mode-symbol-list + (not mode-file-regexp-list)) + (error (format "No rules for %s major modes in auto-mode-alist." (mapconcat 'identity mode-symbol-list ", ")))) + (sqlplus-proj-find-files root-dir + (lambda (file-name) + (and (not (string-match "#" file-name)) + (not (string-match "semantic.cache" file-name)) + (or (and (not mode-symbol-list) + (not (some (lambda (regexp) + (string-match regexp file-name)) + obj-file-regexp-list))) + (and mode-symbol-list + (some (lambda (element) + (let ((freg (if (string-match "[$]" (car element)) + (car element) + (concat (car element) "$")))) + (when (string-match freg file-name) + (cdr element)))) + mode-file-regexp-list))) + (or (not predicate) + (funcall predicate file-name)))) + (lambda (dir-path) + (not (string-match "/\\(\\.svn\\|CVS\\)$" dir-path)))))) + + +(defun sqlplus-file-get-source (connect-string object-name object-type &optional batch-mode) + (interactive + (progn + (push (point-marker) plsql-mark-backward-list) + (list sqlplus-connect-string (thing-at-point 'symbol) nil))) + (unless object-name + (error "Nothing to search")) + (let* ((root-dir (or (and (not object-type) + (eq major-mode 'plsql-mode) + (buffer-file-name) + (sqlplus-get-project-root-dir (buffer-file-name))) + (sqlplus-get-root-dir connect-string) + (sqlplus-set-project-for-connect-string connect-string) + (error "Root dir not set"))) + (mode-symbol-list '(plsql-mode sql-mode)) + (files-to-grep (sqlplus-find-project-files root-dir mode-symbol-list nil)) + (temp-file-path (concat (file-name-as-directory temporary-file-directory) (make-temp-name "ide-"))) + (search-buffer (get-buffer sqlplus-search-buffer-name)) + (regexp (let ((index 0) + (len (length object-name)) + result) + (setq result + (if object-type + (let ((type (cond ((equal object-type "FUNCTION") "PROCEDURE") + ((equal object-type "PACKAGE BODY") "PACKAGE") + (t object-type)))) + (cdr (assoc type sqlplus-object-types-regexps))) + (mapconcat 'cdr sqlplus-object-types-regexps "|"))) + (unless result + (error "Not implemented")) + (while (and (< index (length result)) + (string-match "#" result index)) + (setq index (+ (match-beginning 0) len)) + (setq result (replace-match object-name t t result))) + (setq index 0) + (while (and (< index (length result)) + (string-match "[$]\\(\\\\b\\)?" result index)) + (setq index (+ (match-end 0) 1)) + (setq result (replace-match "\\$" t t result))) + result)) + grep-command + grep-result) + (when search-buffer + (with-current-buffer search-buffer + (let ((inhibit-read-only t)) + (erase-buffer)))) + ;; (message "Object type: %S, object name: %S, regexp: %S" object-type object-name regexp) + (with-temp-file temp-file-path + (dolist (path files-to-grep) + (insert (concat "'" path "'\n")))) + (let* ((grep-command (format "cat %s | xargs grep -nHiE -e '%s'" temp-file-path regexp)) + (raw-grep-result (split-string (shell-command-to-string grep-command) "\n" t)) + (grep-result (delq nil (mapcar (lambda (line) + (string-match "^\\(.*?\\):\\([0-9]+\\):\\(.*\\)$" line) + (let* ((path (match-string 1 line)) + (line-no (string-to-number (match-string 2 line))) + (text (match-string 3 line)) + (text2 text) + (syn-table (copy-syntax-table)) + (case-fold-search t)) + (modify-syntax-entry ?$ "w" syn-table) + (modify-syntax-entry ?# "w" syn-table) + (modify-syntax-entry ?_ "w" syn-table) + (with-syntax-table syn-table + (when (and (or (and (not object-type) + (> (length raw-grep-result) 1)) + (equal object-type "SYNONYM")) + (string-match "\\<\\(for\\|from\\|on\\|as\\)\\>" text2)) + (setq text2 (substring text2 0 (match-beginning 0)))) + ;; (message "GREP-RESULT: %s" text2) + (unless (or (not (string-match (concat "\\<" (regexp-quote object-name) "\\>") text2)) + (string-match (concat "\\(--\\|\\\\|\\\\|\\\\|\\\\).*\\<" + (regexp-quote object-name) "\\>") text2) + (and (or (and (not object-type) + (> (length raw-grep-result) 1)) + (equal object-type "TRIGGER")) + (string-match "\\<\\(alter\\|disable\\|enable\\)\\>" text2)) + (and (or (and (not object-type) + (string-match "\\" text2) + current-prefix-arg) + (equal object-type "PACKAGE")) + (string-match "\\" text2)) + (and (or (and (not object-type) + (string-match "\\" text2) + (not current-prefix-arg)) + (equal object-type "PACKAGE BODY")) + (not (string-match "\\" text2))) + (and (not object-type) + (not current-prefix-arg) + (string-match "[.]pks$" path))) + (list path line-no text))))) + raw-grep-result)))) + (if batch-mode + grep-result + (cond ((not grep-result) + (error "Not found")) + ((eql (length grep-result) 1) + (sqlplus-switch-to-buffer (caar grep-result) (cadar grep-result)) + (when connect-string + (setq sqlplus-connect-string connect-string))) + (t + (let ((search-buffer (get-buffer-create sqlplus-search-buffer-name))) + (with-current-buffer search-buffer + (setq buffer-read-only t) + (let ((inhibit-read-only t)) + (setq default-directory root-dir) + (erase-buffer) + (insert "Root dir: ") + (sqlplus-proj-insert-with-face root-dir 'font-lock-keyword-face) + (insert "; Range: ") + (sqlplus-proj-insert-with-face (mapconcat (lambda (sym) (sqlplus-mode-name-stringify sym)) mode-symbol-list ", ") + 'font-lock-keyword-face) + (insert "; Object type: ") + (sqlplus-proj-insert-with-face (or object-type "unspecified") 'font-lock-keyword-face) + (insert "; Object name: ") + (sqlplus-proj-insert-with-face object-name 'font-lock-keyword-face) + (insert "\n\n") + (compilation-minor-mode 1) + (dolist (result grep-result) + (let ((relative-path (concat "./" (file-relative-name (car result) root-dir))) + (line-no (cadr result)) + (text (caddr result))) + (put-text-property 0 (length relative-path) 'mouse-face 'highlight relative-path) + (insert relative-path) + (insert (format ":%S:1 %s\n" line-no text)))) + (insert (format "\n%d matches found." (length grep-result))) + (goto-char (point-min)) + (when (and grep-result (fboundp 'compile-reinitialize-errors) (funcall (symbol-function 'compile-reinitialize-errors) t))) + (switch-to-buffer-other-window search-buffer) + (goto-line 1) + (goto-line 3)))))))))) + +(defun sqlplus-mode-name-stringify (mode-name) + (let ((name (format "%s" mode-name))) + (replace-regexp-in-string "-" " " + (capitalize + (if (string-match "^\\(.*\\)-mode" name) + (match-string 1 name) + name))))) + +(defun sqlplus-proj-insert-with-face (string face) + (let ((point (point))) + (insert string) + (let ((overlay (make-overlay point (point)))) + (overlay-put overlay 'face face)))) + +(defun sqlplus-set-project-for-connect-string (connect-string) + (if (featurep 'ide-skel) + ;; Prepare sqlplus-root-dir-history (file-name-history) for user convenience + ;; 0. previous project root + ;; 1. current editor file project root + ;; 2. previous choices + ;; 3. new project roots + (let* ((prev-proj-root-dir (sqlplus-get-root-dir connect-string)) + (last-sel-window (funcall 'ide-skel-get-last-selected-window)) + (editor-file-proj-root-dir (when last-sel-window + (let* ((buffer (window-buffer last-sel-window)) + (path (and buffer (buffer-file-name buffer))) + (project (and path (car (funcall 'ide-skel-proj-get-project-create path))))) + (when (funcall 'ide-skel-project-p project) + (funcall 'ide-skel-project-root-path project)))))) + (setq sqlplus-root-dir-history + (delete-dups + (delq nil + (mapcar (lambda (dir) + (when dir + (directory-file-name (file-truename (substitute-in-file-name dir))))) + (append + (list editor-file-proj-root-dir prev-proj-root-dir) + sqlplus-root-dir-history + (mapcar (lambda (project) (funcall 'ide-skel-project-root-path project)) + (symbol-value 'ide-skel-projects))))))) + (let* ((file-name-history (cdr sqlplus-root-dir-history)) + (use-file-dialog nil) + (dir (directory-file-name (file-truename (substitute-in-file-name + (read-directory-name (format "Root dir for %s: " (car (refine-connect-string connect-string))) + (car sqlplus-root-dir-history) + (car sqlplus-root-dir-history) + t + nil)))))) + (funcall 'ide-skel-proj-get-project-create dir) + (sqlplus-set-root-dir dir connect-string) + (message (format "Root dir for %s set to %s" (car (refine-connect-string connect-string)) dir)) + dir)) + (let* ((use-file-dialog nil) + (dir (directory-file-name (file-truename (substitute-in-file-name + (read-directory-name (format "Root dir for %s: " (car (refine-connect-string connect-string))) + nil nil t nil)))))) + (sqlplus-set-root-dir dir connect-string) + (message (format "Root dir for %s set to %s" (car (refine-connect-string connect-string)) dir)) + dir))) + +;;; Plugin for ide-skel.el + +(defstruct sqlplus-tab + id + name ; tab name + symbol ; view/sequence/schema/trigger/index/table/package/synonym/procedure + help-string + (display-start 1) ; display-start in side view window + (data nil) ; '(("name" . status)...), where status t means 'invalid' + draw-function ; parameters: sqlplus-tab + click-function ; parameters: event "@e" + (errors-count 0) + (refresh-in-progress t) + update-select) + +(defvar sqlplus-side-view-connect-string nil) +(make-variable-buffer-local 'sqlplus-side-view-connect-string) + +(defvar sqlplus-side-view-active-tab nil) +(make-variable-buffer-local 'sqlplus-side-view-active-tab) + +(defvar sqlplus-side-view-tabset nil) +(make-variable-buffer-local 'sqlplus-side-view-tabset) + +(defface sqlplus-side-view-face '((t :inherit variable-pitch :height 0.8)) + "Default face used in right view" + :group 'sqlplus) + +(defvar sqlplus-side-view-keymap nil) +(unless sqlplus-side-view-keymap + (setq sqlplus-side-view-keymap (make-sparse-keymap)) + (define-key sqlplus-side-view-keymap [mode-line down-mouse-1] 'ignore) + (define-key sqlplus-side-view-keymap [mode-line mouse-1] 'sqlplus-side-view-tab-click)) + +(defun sqlplus-side-view-tab-click (event) + (interactive "@e") + (with-selected-window (posn-window (event-start event)) + (let* ((previous-sel-tab-info (nth sqlplus-side-view-active-tab sqlplus-side-view-tabset)) + (target (posn-string (event-start event))) + (tab-info (get-text-property (cdr target) 'tab-info (car target)))) + (setf (sqlplus-tab-display-start previous-sel-tab-info) (line-number-at-pos (window-start))) + (setq sqlplus-side-view-active-tab (sqlplus-tab-id tab-info)) + (sqlplus-side-view-redraw (current-buffer) t) + (sqlplus-side-view-buffer-mode-line)))) + +(defun sqlplus-side-view-buffer-mode-line () + (let* ((separator (propertize " " + 'face 'header-line + 'display '(space :width 0.2) + 'pointer 'arrow))) + (setq mode-line-format + (concat separator + (mapconcat (lambda (tab) + (let ((face (if (eq (sqlplus-tab-id tab) sqlplus-side-view-active-tab) + 'tabbar-selected + 'tabbar-unselected)) + (help-echo (concat (sqlplus-tab-help-string tab) + (if (> (sqlplus-tab-errors-count tab) 0) + (format "\n(%s error%s)" (sqlplus-tab-errors-count tab) + (if (> (sqlplus-tab-errors-count tab) 1) "s" "")) + "")))) + (propertize (format " %s " (sqlplus-tab-name tab)) + 'local-map sqlplus-side-view-keymap + 'tab-info tab + 'help-echo help-echo + 'mouse-face 'tabbar-highlight + 'face (if (> (sqlplus-tab-errors-count tab) 0) + (list '(foreground-color . "red") face) + face) + 'pointer 'hand))) + sqlplus-side-view-tabset + separator) + separator)))) + +(defun sqlplus-side-view-click-on-default-handler (event) + (interactive "@e") + (with-selected-window (posn-window (event-start event)) + (let* ((posn-point (posn-point (event-start event))) + (object-name (get-text-property posn-point 'object-name)) + (object-type (get-text-property posn-point 'object-type)) + (type (car event))) + (when (eq type 'mouse-3) + (setq type (car (x-popup-menu t (append (list 'keymap object-name) + (list '(sqlplus-refresh-side-view-buffer "Refresh" t)) + (list '(mouse-1 "Get source from Oracle" t)) + (list '(M-mouse-1 "Search source in filesystem" t)) + (list (list 'C-M-mouse-1 (concat "Set root dir for " (car (refine-connect-string sqlplus-side-view-connect-string))) t)) + ))))) + (cond ((eq type 'mouse-1) + (sqlplus-get-source sqlplus-side-view-connect-string object-name object-type)) + ((eq type 'M-mouse-1) + (sqlplus-file-get-source sqlplus-side-view-connect-string object-name object-type)) + ((eq type 'C-M-mouse-1) + (sqlplus-set-project-for-connect-string sqlplus-side-view-connect-string)) + ((eq type nil)) + (t + (condition-case err + (funcall type) + (error nil))))))) + +(defun sqlplus-side-view-click-on-index-handler (event) + (interactive "@e") + (with-selected-window (posn-window (event-start event)) + (let* ((posn-point (posn-point (event-start event))) + (object-name (get-text-property posn-point 'object-name)) + (object-type (get-text-property posn-point 'object-type)) + (type (car event))) + (when (eq type 'mouse-3) + (setq type (car (x-popup-menu t (append (list 'keymap object-name) + (list '(sqlplus-refresh-side-view-buffer "Refresh" t)) + (list '(mouse-1 "Get source from Oracle" t)) + (list '(M-mouse-1 "Search source in filesystem" t)) + (list (list 'C-M-mouse-1 (concat "Set root dir for " (car (refine-connect-string sqlplus-side-view-connect-string))) t)) + ))))) + (cond ((eq type 'mouse-1) + (sqlplus-get-source sqlplus-side-view-connect-string object-name object-type)) + ((eq type 'M-mouse-1) + (sqlplus-file-get-source sqlplus-side-view-connect-string object-name object-type)) + ((eq type 'C-M-mouse-1) + (sqlplus-set-project-for-connect-string sqlplus-side-view-connect-string)) + ((eq type nil)) + (t + (condition-case err + (funcall type) + (error nil))))))) + +(defun sqlplus-side-view-click-on-schema-handler (event) + (interactive "@e") + (with-selected-window (posn-window (event-start event)) + (let* ((posn-point (posn-point (event-start event))) + (object-name (get-text-property posn-point 'object-name)) + (object-type (get-text-property posn-point 'object-type)) + (last-selected-win (funcall 'ide-skel-get-last-selected-window)) + (type (car event))) + (when (eq type 'mouse-3) + (setq type (car (x-popup-menu t (append (list 'keymap object-name) + (list '(sqlplus-refresh-side-view-buffer "Refresh" t)) + (list '(mouse-1 "Connect to schema" t)) + (list '(M-mouse-1 "Search source in filesystem" t)) + (list (list 'C-M-mouse-1 (concat "Set root dir for " (car (refine-connect-string sqlplus-side-view-connect-string))) t)) + ))))) + (cond ((eq type 'mouse-1) + (when (string-match "@.*$" sqlplus-side-view-connect-string) + (let* ((cs (downcase (concat object-name (match-string 0 sqlplus-side-view-connect-string)))) + (pair (sqlplus-read-connect-string cs cs))) + (select-window (or last-selected-win (funcall 'ide-skel-get-editor-window))) + (sqlplus (car pair) (concat (cadr pair) (concat "." sqlplus-session-file-extension)))))) + ((eq type 'M-mouse-1) + (sqlplus-file-get-source sqlplus-side-view-connect-string object-name object-type)) + ((eq type 'C-M-mouse-1) + (sqlplus-set-project-for-connect-string sqlplus-side-view-connect-string)) + ((eq type nil)) + (t + (condition-case err + (funcall type) + (error nil)))) + (select-window (funcall 'ide-skel-get-last-selected-window))))) + +(defun sqlplus-side-view-click-on-table-handler (event) + (interactive "@e") + (with-selected-window (posn-window (event-start event)) + (let* ((posn-point (posn-point (event-start event))) + (object-name (get-text-property posn-point 'object-name)) + (object-type (get-text-property posn-point 'object-type)) + (type (car event))) + (when (eq type 'mouse-3) + (setq type (car (x-popup-menu t (append (list 'keymap object-name) + (list '(sqlplus-refresh-side-view-buffer "Refresh" t)) + (list '(mouse-1 "Show description" t)) + (list '(C-mouse-1 "Select *" t)) + (list '(S-mouse-1 "Get source from Oracle" t)) + (list '(M-mouse-1 "Search source in filesystem" t)) + (list (list 'C-M-mouse-1 (concat "Set root dir for " (car (refine-connect-string sqlplus-side-view-connect-string))) t)) + ))))) + (cond ((eq type 'mouse-1) + (sqlplus-execute sqlplus-side-view-connect-string + (sqlplus-fontify-string sqlplus-side-view-connect-string (format "desc %s;" object-name)) + nil nil)) + ((eq type 'C-mouse-1) + (sqlplus-execute sqlplus-side-view-connect-string + (sqlplus-fontify-string sqlplus-side-view-connect-string (format "select * from %s;" object-name)) + nil nil)) + ((eq type 'S-mouse-1) + (sqlplus-get-source sqlplus-side-view-connect-string object-name object-type)) + ((eq type 'M-mouse-1) + (sqlplus-file-get-source sqlplus-side-view-connect-string object-name object-type)) + ((eq type 'C-M-mouse-1) + (sqlplus-set-project-for-connect-string sqlplus-side-view-connect-string)) + ((eq type nil)) + (t + (condition-case err + (funcall type) + (error nil)))) + (select-window (funcall 'ide-skel-get-last-selected-window))))) + +(defun sqlplus-side-view-click-on-package-handler (event) + (interactive "@e") + (with-selected-window (posn-window (event-start event)) + (let* ((posn-point (posn-point (event-start event))) + (object-name (get-text-property posn-point 'object-name)) + (object-type (get-text-property posn-point 'object-type)) + (type (car event))) + (when (eq type 'mouse-3) + (setq type (car (x-popup-menu t (append (list 'keymap object-name) + (list '(sqlplus-refresh-side-view-buffer "Refresh" t)) + (list '(S-mouse-1 "Get package header from Oracle" t)) + (list '(mouse-1 "Get package body from Oracle" t)) + (list '(S-M-mouse-1 "Search header source in filesystem" t)) + (list '(M-mouse-1 "Search body source in filesystem" t)) + (list (list 'C-M-mouse-1 (concat "Set root dir for " (car (refine-connect-string sqlplus-side-view-connect-string))) t)) + ))))) + (cond ((eq type 'S-mouse-1) + (sqlplus-get-source sqlplus-side-view-connect-string object-name object-type)) + ((eq type 'mouse-1) + (sqlplus-get-source sqlplus-side-view-connect-string object-name "PACKAGE BODY")) + ((eq type 'M-mouse-1) + (sqlplus-file-get-source sqlplus-side-view-connect-string object-name "PACKAGE BODY")) + ((eq type 'S-M-mouse-1) + (sqlplus-file-get-source sqlplus-side-view-connect-string object-name "PACKAGE")) + ((eq type 'C-M-mouse-1) + (sqlplus-set-project-for-connect-string sqlplus-side-view-connect-string)) + ((eq type nil)) + (t + (condition-case err + (funcall type) + (error nil))))))) + +(defun sqlplus-side-view-default-draw-panel (tab-info click-function) + (let ((pairs (sort (sqlplus-tab-data tab-info) + (lambda (pair1 pair2) (string< (car pair1) (car pair2))))) + (type-name (upcase (symbol-name (sqlplus-tab-symbol tab-info))))) + (dolist (pair pairs) + (let* ((label (format " % -100s" (car pair))) + (km (make-sparse-keymap))) + (define-key km [down-mouse-1] 'ignore) + (define-key km [mouse-1] click-function) + (define-key km [C-down-mouse-1] 'ignore) + (define-key km [C-mouse-1] click-function) + (define-key km [S-down-mouse-1] 'ignore) + (define-key km [S-mouse-1] click-function) + (define-key km [down-mouse-3] 'ignore) + (define-key km [mouse-3] click-function) + (setq label (propertize label + 'mouse-face 'ide-skel-highlight-face + 'face (if (cdr pair) + '(sqlplus-side-view-face (foreground-color . "red")) + 'sqlplus-side-view-face) + 'local-map km + 'pointer 'hand + 'object-name (car pair) + 'object-type type-name)) + (insert label) + (insert "\n"))))) + +(defun sqlplus-refresh-side-view-buffer () + (let* ((tab-info (nth sqlplus-side-view-active-tab sqlplus-side-view-tabset)) + (update-select (sqlplus-tab-update-select tab-info))) + (unless (sqlplus-tab-refresh-in-progress tab-info) + (sqlplus-hidden-select sqlplus-side-view-connect-string update-select 'sqlplus-my-update-handler)))) + +(defun sqlplus-get-default-update-select (symbol) + (concat "select object_name, object_type, decode( status, 'INVALID', 'I', ' ' ) from user_objects\n" + "where object_name not like 'BIN$%'\n" + (format "and object_type = '%s';" (upcase (symbol-name symbol))))) + +(defun sqlplus-create-side-view-buffer (connect-string) + (let* ((original-connect-string connect-string) + (connect-string (car (refine-connect-string connect-string))) + (buffer (funcall 'ide-skel-get-side-view-buffer-create + (concat " Ide Skel Right View SQL " connect-string) + 'right "SQL" (concat "SQL Panel for " connect-string) + (lambda (editor-buffer) + (let ((connect-string sqlplus-side-view-connect-string)) + (with-current-buffer editor-buffer + (and connect-string + (equal (car (refine-connect-string sqlplus-connect-string)) + (car (refine-connect-string connect-string))) + ))))))) + (with-current-buffer buffer + (set 'ide-skel-tabbar-menu-function + (lambda () + (let ((tab-info (nth sqlplus-side-view-active-tab sqlplus-side-view-tabset))) + (list + (unless (sqlplus-tab-refresh-in-progress tab-info) + '(sqlplus-refresh-side-view-buffer "Refresh" t)))))) + (setq sqlplus-side-view-connect-string original-connect-string + sqlplus-side-view-active-tab 0 + sqlplus-side-view-tabset + (list + (make-sqlplus-tab :id 0 :name "Tab" :symbol 'table :help-string "Tables" :draw-function 'sqlplus-side-view-default-draw-panel + :update-select (sqlplus-get-default-update-select 'table) + :click-function 'sqlplus-side-view-click-on-table-handler) + (make-sqlplus-tab :id 1 :name "Vie" :symbol 'view :help-string "Views" :draw-function 'sqlplus-side-view-default-draw-panel + :update-select (sqlplus-get-default-update-select 'view) + :click-function 'sqlplus-side-view-click-on-table-handler) + (make-sqlplus-tab :id 2 :name "Idx" :symbol 'index :help-string "Indexes" :draw-function 'sqlplus-side-view-default-draw-panel + :update-select (sqlplus-get-default-update-select 'index) + :click-function 'sqlplus-side-view-click-on-index-handler) + (make-sqlplus-tab :id 3 :name "Tri" :symbol 'trigger :help-string "Triggers" :draw-function 'sqlplus-side-view-default-draw-panel + :update-select (sqlplus-get-default-update-select 'trigger) + :click-function 'sqlplus-side-view-click-on-default-handler) + (make-sqlplus-tab :id 4 :name "Seq" :symbol 'sequence :help-string "Sequences" :draw-function 'sqlplus-side-view-default-draw-panel + :update-select (sqlplus-get-default-update-select 'sequence) + :click-function 'sqlplus-side-view-click-on-default-handler) + (make-sqlplus-tab :id 5 :name "Syn" :symbol 'synonym :help-string "Synonyms" :draw-function 'sqlplus-side-view-default-draw-panel + :update-select (sqlplus-get-default-update-select 'synonym) + :click-function 'sqlplus-side-view-click-on-default-handler) + (make-sqlplus-tab :id 6 :name "Pkg" :symbol 'package :help-string "PL/SQL Packages" :draw-function 'sqlplus-side-view-default-draw-panel + :update-select (sqlplus-get-default-update-select 'package) + :click-function 'sqlplus-side-view-click-on-package-handler) + (make-sqlplus-tab :id 7 :name "Prc" :symbol 'procedure :help-string "PL/SQL Functions & Procedures" :draw-function 'sqlplus-side-view-default-draw-panel + :update-select (concat "select object_name, object_type, decode( status, 'INVALID', 'I', ' ' ) from user_objects\n" + "where object_name not like 'BIN$%'\n" + "and object_type in ('FUNCTION', 'PROCEDURE');") + :click-function 'sqlplus-side-view-click-on-default-handler) + (make-sqlplus-tab :id 8 :name "Sch" :symbol 'schema :help-string "Schemas" :draw-function 'sqlplus-side-view-default-draw-panel + :update-select "select username, 'SCHEMA', ' ' from all_users where username not like 'BIN$%';" + :click-function 'sqlplus-side-view-click-on-schema-handler) + )) + (sqlplus-side-view-buffer-mode-line)) + buffer)) + +(defun sqlplus-side-view-redraw (sql-view-buffer &optional window-start-from-tab-info) + (with-current-buffer sql-view-buffer + (let* ((point (point)) + (tab-info (nth sqlplus-side-view-active-tab sqlplus-side-view-tabset)) + (window-start (when (and (symbol-value 'ide-skel-current-right-view-window) + (eq (window-buffer (symbol-value 'ide-skel-current-right-view-window)) (current-buffer))) + (if window-start-from-tab-info + (sqlplus-tab-display-start tab-info) + (line-number-at-pos (window-start (symbol-value 'ide-skel-current-right-view-window))))))) + (let ((inhibit-read-only t)) + (setq buffer-read-only nil) + (erase-buffer) + (when (sqlplus-tab-draw-function tab-info) + (funcall (sqlplus-tab-draw-function tab-info) tab-info (sqlplus-tab-click-function tab-info)))) + (if window-start + (let ((pos (save-excursion + (goto-line window-start) + (beginning-of-line) + (point)))) + (set-window-start (symbol-value 'ide-skel-current-right-view-window) pos) + (setf (sqlplus-tab-display-start tab-info) window-start)) + (goto-char point) + (beginning-of-line))))) + +(defun sqlplus-side-view-update-data (connect-string alist) + (let* ((connect-string (car (refine-connect-string connect-string))) + (sql-view-buffer (sqlplus-get-side-view-buffer connect-string)) + was-proc) + (when sql-view-buffer + (with-current-buffer sql-view-buffer + (dolist (pair alist) + (let* ((symbol (if (eq (car pair) 'function) 'procedure (car pair))) + (data-list (cdr pair)) + (tab-info (some (lambda (tab) + (when (eq (sqlplus-tab-symbol tab) symbol) + tab)) + sqlplus-side-view-tabset))) + (when tab-info + (setf (sqlplus-tab-refresh-in-progress tab-info) nil) + (setf (sqlplus-tab-data tab-info) + (if (and (eq symbol 'procedure) + was-proc) + (append (sqlplus-tab-data tab-info) (copy-list data-list)) + data-list)) + (when (eq symbol 'procedure) + (setq was-proc t)) + (setf (sqlplus-tab-errors-count tab-info) + (count t (mapcar 'cdr data-list))) + (when (eql sqlplus-side-view-active-tab (sqlplus-tab-id tab-info)) + (sqlplus-side-view-redraw (current-buffer)))))) + (sqlplus-side-view-buffer-mode-line) + (force-mode-line-update))))) + +(defun sqlplus-side-view-window-function (side event &rest list) + (when (and (eq side 'right) + (symbol-value 'ide-skel-current-right-view-window) + (with-current-buffer (symbol-value 'ide-skel-current-editor-buffer) + sqlplus-connect-string)) + (cond ((memq event '(show editor-buffer-changed)) + (let ((sql-view-buffer (sqlplus-get-side-view-buffer (with-current-buffer (symbol-value 'ide-skel-current-editor-buffer) + sqlplus-connect-string)))) + (when sql-view-buffer + (with-current-buffer sql-view-buffer + (set 'ide-skel-tabbar-enabled t) + (funcall 'ide-skel-side-window-switch-to-buffer (symbol-value 'ide-skel-current-right-view-window) sql-view-buffer))))))) + nil) + +(add-hook 'ide-skel-side-view-window-functions 'sqlplus-side-view-window-function) + + +(provide 'sqlplus) + +;;; sqlplus.el ends here -- cgit v1.2.3-54-g00ecf