(defun dojo-print-point () (interactive) (message "Point: %s" (point)) ) (defun dojo-get-prefix-start-point() ; (let* ((result ; (or (ac-prefix-symbol) (save-excursion ; (message "searching dot: [%s]" (buffer-substring (1- (point)) (point))) (if (string= (buffer-substring (1- (point)) (point)) ".") (progn ; (message "Dot.") (point)) (let* ((search-result (re-search-backward "[^[:alnum:]^\\.]" nil t)) ) ; (message "search-result %s, point %d" search-result (point)) (if search-result (1+ (point)) nil ) ) )) ; (message "Result of dojo-get-prefix-start-point: %s" result) ; result) ) (defun dojo-get-current-symbol-path-tokens () (let ((path (dojo-get-current-symbol-path)) ) (split-string path "\\.") ) ) (defun dojo-get-current-symbol-path () (save-excursion (let* ((original-point (point)) (search-result (re-search-backward "[^[:alnum:]^\\.]" nil t)) ) (buffer-substring (1+ (point)) original-point) ) ) ) (defun dojo-get-current-completion-path () "Returns the part of the symbol path at point relevant for completion, i.e. either the complete symbol (if it contains no dot), or otherwise its substring starting with the last dot. Examples: For 'foo' return 'foo', for 'foo.bar' return '.bar', for 'foo().bar' return '.bar', for 'foo.bar.cde' return '.cde', for 'foo.' return '.'." (let* ((current-symbol-path (dojo-get-current-symbol-path)) (last-dot-index (last-index-of current-symbol-path "\\.")) ) ; (log-completion (format "get-current-completion-path: current-symbol-path [%s], last-dot-index [%s]" current-symbol-path last-dot-index)) (if last-dot-index (substring current-symbol-path last-dot-index) current-symbol-path ) ) ) (defun dojo-get-last-prop-path-token (path) (let* ((tokens (split-string path ".")) (number-of-tokens (length tokens)) ) (if (> number-of-tokens 0) (nth (1- number-of-tokens) tokens) nil ) ) ) ;(defun dojo-get-current-symbol-path () ; "Determines the symbol path before point, i.e. the text between ; the last occurrence of a non-alphanumeric, and non-dot character ; before point, and the character at point (both exclusive). ; Splits it along occurrences of the dot '.' and returns the ; corresponding list. If it ends (or starts, but that case would ; be rather strange) with a dot, the last (first) token in the ; list is the empty string. ; Example: Buffer contains '[].concat(this.strokeStyles);', ; point is at the first 's' of strokeStyles, ; The relevant text this function will find is 'this.', ; the return value will be ['this' '']." ; (save-excursion ; (let* ((original-point (point)) ; (search-result (re-search-backward "[^[:alnum:]^\\.]" nil t)) ; ) ; (split-string (buffer-substring (1+ (point)) original-point) "\\.") ; ) ; ) ;) (defun dojo-get-ancestor-path (&optional pos) "Returns the js2-node at the given position, and all its ancestors, in a list. The first element of the list is the root node of the AST, the last one the node at pos. If pos is omitted, it defaults to (1- point) (reason: If you type something, say 'bar', then the corresponding node spans from the 'b' to the 'r'. But point is at the character after the 'r', i.e. if we would search for point, then we would not find the node we just want to complete, but something else typically somewhere upwards in the node tree." (if (null pos) (setq pos (point)) ) (let* ((ancestor-path ()) (curr-node (js2-node-at-point (1- pos))) ) (while curr-node (push curr-node ancestor-path) (setq curr-node (js2-node-parent curr-node)) ) ancestor-path ) ) (defun dojo-print-ancestor-path (ancestor-path) (log-completion 0 "===== Ancestor path =====") (let ((n 0)) (dolist (node ancestor-path) (log-completion 0 (format "[%s]: Node [%s]" n (js2-node-short-name node))) (setq n (1+ n)))) (log-completion 0 "=========================") ) (defun dojo-get-next-non-whitespace-column (limit) (save-excursion (let* ((search-result (re-search-forward "[^[:space:]]" limit t)) ) (if search-result (progn (log-indent (format "--- get-next-non-whitespace-column: limit = [%s]; found character %s at [%s], point = [%s]" limit (char-to-string (char-after (point))) (current-column) (point))) (1- (current-column)) ) (progn nil ) ) ) ) ) (defun dojo-get-first-non-whitespace-column-on-line () (save-excursion (beginning-of-line) (dojo-get-next-non-whitespace-column (line-end-position)) ) ) ;(defun dojo-is-whitespace-line () ;) (defun dojo-js-get-line-comment-position () (save-excursion (beginning-of-line) (if (search-forward "//" (line-end-position) t) (progn (backward-char 2) (point)) nil) ) ) (defun dojo-common-source-get-line-start (pos) (save-excursion (goto-char pos) (move-beginning-of-line nil) (point) ) ) (defun dojo-common-source-get-line-end (pos) (save-excursion (goto-char pos) (move-end-of-line nil) (point) ) ) (defun dojo-common-source-get-prev-line-comment-end () (save-excursion (let* (line-comment-pos (search-backward "//" nil t)) (if (not (null line-comment-pos)) (progn (move-end-of-line nil) (point)) nil)))) (defun dojo-js-get-start-column-of-our-line-comment () (let* ((curr-column (current-column)) (curr-point (point)) (beginning-of-comment-column (save-excursion (beginning-of-line) (let ((beginning-of-comment-position (search-forward "//" (dojo-get-end-of-line-position) t)) ) (if (not (null beginning-of-comment-position)) (- (current-column) 2) nil ) ) )) ) ; (message "curr-column %s, beginning-of-comment-column %s" curr-column beginning-of-comment-column) (log-indent (format "About to return from dojo-js-get-start-column-of-our-line-comment, column = [%s]" beginning-of-comment-column)) (if beginning-of-comment-column beginning-of-comment-column nil ) ) ) (defun dojo-js-get-start-pos-of-our-block-comment (find-own-comment) (let* ((last-prev-end-pos (save-excursion (search-backward "*/" nil t))) ; TODO: If a /* is inside a comment, this will catch it, and not the beginning of the comment. (last-prev-start-pos (save-excursion (if find-own-comment (goto-char (+ (point) 2))) (search-backward "/*" nil t))) ) ; (log-indent (format "last-prev-end-pos [%s]; last-prev-start-pos [%s]" last-prev-end-pos last-prev-start-pos)) (log-indent (format "About to return from dojo-js-get-start-pos-of-our-block-comment, last-prev-end-pos = [%s], last-prev-start-pos = [%s]" last-prev-end-pos last-prev-start-pos)) (if (or (null last-prev-end-pos) (> last-prev-start-pos last-prev-end-pos)) last-prev-start-pos nil ) ) ) (defun dojo-js-get-end-pos-of-our-block-comment () (let* ((first-next-start-pos (save-excursion (goto-char (1+ (point))) (search-forward "/*" nil t))) (first-next-end-pos (save-excursion (goto-char (1- (point))) (search-forward "*/" nil t))) ) (if (not (null first-next-start-pos)) (setq first-next-start-pos (- first-next-start-pos 2)) ) (log-indent (format "first-next-start-pos [%s]; first-next-end-pos [%s]" first-next-start-pos first-next-end-pos)) (if (or (null first-next-start-pos) (and (not (null first-next-end-pos)) (< first-next-end-pos first-next-start-pos))) first-next-end-pos nil ) ) ) ; TODO: Does not support /* inside block comments. I.e., a block comment like /* /**/ will not be recognized. (defun dojo-js-is-point-inside-block-comment (last-prev-start-pos first-next-end-pos) (log-indent (format "last-prev-start-pos [%s]; first-next-end-pos [%s]" last-prev-start-pos first-next-end-pos)) (let ((p (point)) ) (and (not (null last-prev-start-pos)) (not (null first-next-end-pos)) (>= p last-prev-start-pos) (<= p first-next-end-pos)) ) ) (defun dojo-js-is-object-member-line (position) ; Function returning wether the line at the given position might be an object member line. ; Might be, because of my unability to set up a proper elisp RegExp for this. ; But as the next thing to do is starting parsing in a more detailed way, this is no real harm either... ; TODO: Implement a more sophisticated test for this than just testing for the precence of a colon... (save-excursion (goto-char position) (string-match-p ".*:.*" (thing-at-point 'line)) ) ) (defun dojo-js-backward-process-slash-for-comment (backward-scan-state) (if (dojo-indent-scan-state-slash-found backward-scan-state) (log-indent "[WARNING] Second slash found during backward scan, although we meant to process line comments separately.") (setf (dojo-indent-scan-state-slash-found backward-scan-state) t) ) ) (defun dojo-js-backward-process-star-for-comment (backward-scan-state) (if (dojo-indent-scan-state-slash-found backward-scan-state) ; End of a block comment (progn (let* ((prev-line (line-number-at-pos)) (block-comment-start-pos (dojo-js-get-start-pos-of-our-block-comment nil)) ) (while block-comment-start-pos (goto-char block-comment-start-pos) (setq block-comment-start-pos (dojo-js-get-start-pos-of-our-block-comment nil)) ) (setf (dojo-indent-scan-state-slash-found backward-scan-state) nil) (let* ((new-line (line-number-at-pos)) (number-of-lines (dojo-indent-scan-state-number-of-lines backward-scan-state)) ) (setf number-of-lines (+ number-of-lines (- prev-line new-line))) (setf (dojo-indent-scan-state-end-of-line-code-position backward-scan-state) (point)) ) ) ) ) ) (defun dojo-js-backward-process-newline-for-comment (backward-scan-state) (let* ((line-comment-pos (dojo-js-get-line-comment-position)) ) (if line-comment-pos (goto-char line-comment-pos) ) ) ) (defun dojo-js-backward-process-round-bracket-open (backward-scan-state) "Default function for processing a opening round bracket while stepping backward. It just records the bracket level in the scan state. If more work is needed, a custom function needs to be implemented." (log-indent "Processing (") (if (> (dojo-indent-scan-state-round-bracket-level backward-scan-state) 0) (progn (decf (dojo-indent-scan-state-round-bracket-level backward-scan-state)) ) ) nil ) (defun dojo-js-backward-process-round-bracket-close (backward-scan-state) "Default function for processing a closing round bracket while stepping backward. It just records the bracket level in the scan state. If more work is needed, a custom function needs to be implemented." (log-indent "Processing )") (incf (dojo-indent-scan-state-round-bracket-level backward-scan-state)) nil ) (defun dojo-js-backward-process-square-bracket-open (backward-scan-state) "Default function for processing a opening square bracket while stepping backward. It just records the bracket level in the scan state. If more work is needed, a custom function needs to be implemented." (log-indent "Processing [") (if (> (dojo-indent-scan-state-square-bracket-level backward-scan-state) 0) (progn (decf (dojo-indent-scan-state-square-bracket-level backward-scan-state)) ) ) nil ) (defun dojo-js-backward-process-square-bracket-close (backward-scan-state) "Default function for processing a closing square bracket while stepping backward. It just records the bracket level in the scan state. If more work is needed, a custom function needs to be implemented." (log-indent "Processing ]") (incf (dojo-indent-scan-state-square-bracket-level backward-scan-state)) nil ) (defun dojo-js-backward-process-curly-bracket-open (backward-scan-state) "Default function for processing a opening curly bracket while stepping backward. It just records the bracket level in the scan state. If more work is needed, a custom function needs to be implemented." (log-indent "Processing {") (if (> (dojo-indent-scan-state-curly-bracket-level backward-scan-state) 0) (progn (decf (dojo-indent-scan-state-curly-bracket-level backward-scan-state)) ) ) nil ) (defun dojo-js-backward-process-curly-bracket-close (backward-scan-state) "Default function for processing a closing curly bracket while stepping backward. It just records the bracket level in the scan state. If more work is needed, a custom function needs to be implemented." (log-indent "Processing }") (incf (dojo-indent-scan-state-curly-bracket-level backward-scan-state)) nil ) (defun dojo-js-backward-scan (backward-scan-state continue-function char-table) ; Function performing a character by character backward scan in source code, ; executing a custom function for each entry of the given char-table. ; Maintaining the given backward-scan-state is mainly the task of the custom ; functions, however start-char and start-position are set by this function, ; and the slash-found flag is cleared once something different than a slash ; is found. ; Comment detection and skipping should by made by calling the functions ; dojo-js-backward-process-(slash/star/newline)-for-comment in the corresponding ; functions for slash/star/newline. ; The custom functions mapped to entries of the char-table must take the ; backward-scan-state (a dojo-indent-scan-state) as parameter, and return a result. ; The scan proceeds as long as the given continue-function, feeded with that ; result, returns t. Once it returns nil, the last result is returned by this ; function. (let ((result nil) ) (setf (dojo-indent-scan-state-start-char backward-scan-state) (char-after)) (setf (dojo-indent-scan-state-start-position backward-scan-state) (point)) (while (and (> (point) (point-min)) (funcall continue-function result)) (backward-char) (let* ((c (char-after)) (fct (char-table-range char-table c)) ) ; If we found something that is neither a star, nor a slash, clear the slash-found flag (if (and (not (char-equal c ?\*)) (not (char-equal c ?\/))) (setf (dojo-indent-scan-state-slash-found backward-scan-state) nil) ) ; If we pass a newline, go into mode END_OF_LINE_SECTION. If we find the last non-whitespace, non-comment and non- ; newline character on the line, clear that mode, and record that character if we are at the line before the line where we started. (cond ((char-equal c ?\n) (setf (dojo-indent-scan-state-state backward-scan-state) "END_OF_LINE_SECTION")) ((and (string= (dojo-indent-scan-state-state backward-scan-state) "END_OF_LINE_SECTION") (not (or (char-equal c ?\s) (char-equal c ?\t) (char-equal c ?\/) (char-equal c ?\*)))) (if (= (dojo-indent-scan-state-number-of-lines backward-scan-state) 1) (setf (dojo-indent-scan-state-last-prev-line-char backward-scan-state) c) ) (setf (dojo-indent-scan-state-state backward-scan-state) nil)) ) (if fct (setq result (funcall fct backward-scan-state)) ) ) ) result ) ) (defun dojo-goto-indendation-in-line () "Moves point to the first non-whitespace character in the current line, and returns t, if such a character exists. Moves point to the beginning of the line, and returns nil, if no such character exists (i.e. line containing only whitespace)." (beginning-of-line) (let* ((end-of-line-position (save-excursion (end-of-line) (point))) (search-result (re-search-forward "[^[:space:]]" end-of-line-position t)) ) (if search-result (backward-char) ) (not (null search-result)) ) ) (defun dojo-indent (level) (dotimes (n level) (dotimes (m dojo-indent-width) (insert " ") ) ) ) (defun dojo-add-function (name params) (interactive "sName of function: \nsParameters: ") (move-beginning-of-line nil) (if (not (is-prev-line-empty 1)) (progn (open-line 1) (next-line) ) ) (dojo-indent 2) (insert (concat name " : function(" params ") {")) (newline) (dojo-indent 2) (insert "},") (move-beginning-of-line nil) (if (not (is-next-line-empty 1)) (progn (next-line) (open-line 1) (previous-line) ) ) (open-line 1) (dojo-indent 3) ) (provide 'dojo-common-source)