├── .abbrev_defs ├── .gitignore ├── .mc-lists.el ├── .yasnippets ├── c++-mode │ ├── .yas-parents │ ├── .yas-setup │ ├── beginend │ ├── cerr │ ├── cin │ ├── cit │ ├── class │ ├── copy_constructor │ ├── cout │ ├── doc │ ├── enum │ ├── eulerangles │ ├── exists │ ├── fori │ ├── formatidx │ ├── hpp │ ├── imshow │ ├── io │ ├── it │ ├── map │ ├── rotation │ ├── savepbm │ ├── std │ ├── timer │ ├── tostring │ └── try ├── cc-mode │ ├── do │ ├── for │ ├── if │ ├── ifdef │ ├── inc │ ├── inv │ ├── main │ ├── once │ ├── struct │ ├── typedef │ └── while ├── cmake-mode │ ├── cmake_minimum_required │ ├── copy │ ├── dependency │ ├── foreach │ ├── function │ ├── if │ ├── ifelse │ ├── macro │ ├── message │ ├── option │ └── std11 ├── conf-mode │ └── host ├── d-mode │ ├── do │ ├── doc │ ├── for │ ├── foreach │ ├── if │ ├── io │ ├── main │ ├── pure │ ├── read │ ├── sif │ ├── unittest │ └── while ├── emacs-lisp-mode │ ├── char │ ├── defun │ ├── dirlocals │ ├── ifu │ └── join ├── gitconfig-mode │ └── pull ├── make-mode │ └── hello ├── org-mode │ ├── example │ ├── fullpage │ ├── notes │ ├── revealjs │ └── src ├── python-mode │ ├── arg │ ├── doc │ ├── for │ ├── from │ ├── if │ ├── ife │ ├── ifmain │ ├── import │ ├── matplotlib │ ├── parse_args │ ├── script │ ├── try │ ├── while │ └── with ├── sh-mode │ ├── #! │ ├── argn │ ├── base │ ├── dir │ ├── ext │ ├── for │ ├── guard │ ├── if │ ├── ife │ ├── ifne0 │ ├── inc │ ├── name │ └── root └── snippet-mode │ ├── field │ └── mirror ├── README.org ├── emacs-init.org ├── ideas.org ├── init.el └── tutorial.org /.abbrev_defs: -------------------------------------------------------------------------------- 1 | ;;-*-coding: utf-8;-*- 2 | (define-abbrev-table 'global-abbrev-table 3 | '( 4 | ("char" "(char-to-string (+ ?x ))" nil :count 0) 5 | ("date" "(format-time-string \"%d, %b, %Y\")" nil :count 0) 6 | ("nocaps" "setxkbmap -option \"ctrl:nocaps\"" nil :count 0) 7 | ("today" "(format-time-string \"%d, %b, %Y\")" nil :count 0) 8 | ("units" "(calc-eval (math-convert-units (calc-eval \"29ft + 8in\" 'raw) (calc-eval \"m\" 'raw)))" nil :count 0) 9 | )) 10 | 11 | (define-abbrev-table 'python-mode-abbrev-table 12 | '( 13 | ("class" "class" nil :count 0) 14 | ("def" "def" nil :count 0) 15 | ("for" "for" nil :count 0) 16 | ("if" "if" nil :count 0) 17 | ("try" "try" nil :count 0) 18 | ("while" "while" nil :count 0) 19 | )) 20 | 21 | (define-abbrev-table 'shell-mode-abbrev-table 22 | '( 23 | ("ccache" "CC=/usr/lib/ccache/gcc CXX=/usr/lib/ccache/g++" nil :count 0) 24 | ("cmake" "cmake -GNinja .." nil :count 0) 25 | ("convert-crop" "convert in.jpg -crop '745x745+0+70' out.jpg" nil :count 0) 26 | ("convert-gif" "convert -delay 50 *.jpg out.gif" nil :count 0) 27 | ("convert-random" "convert -size 1280x720 xc: +noise Random random.png" nil :count 0) 28 | ("convert-scale" "convert in.jpg -scale 1280x720! out.jpg" nil :count 0) 29 | ("convert-side-by-side" "convert image1.png image2.png +append output.png" nil :count 0) 30 | ("du" "du -sh * | sort -hr" nil :count 0) 31 | ("ffmpeg-add-audio" "ffmpeg -i video.avi -i audio.mp3 -codec copy -shortest output.avi" nil :count 0) 32 | ("ffmpeg-crop" "ffmpeg -i in.mp4 -b:v 10000k -r 24 -filter_complex \"[0:v:0]crop=w=100:h=100:x=12:y=34\" out.mp4" nil :count 0) 33 | ("ffmpeg-cut" "ffmpeg -i input.mkv -ss 00:00:30.0 -c copy -t 00:00:10.0 output.mkv" nil :count 0) 34 | ("ffmpeg-from-images" "ffmpeg -i %04d.jpg -b:v 10000k -r 24 -f mp4 out.mp4" nil :count 0) 35 | ("ffmpeg-grab" "ffmpeg -f alsa -ac 2 -i pulse -f x11grab -r 30 -s 1920x1080 -i :0.0 -acodec pcm_s16le -vcodec libx264 -preset ultrafast -crf 0 -y output.mkv" nil :count 0) 36 | ("ffmpeg-scale" "ffmpeg -i in.mp4 -b:v 10000k -vf scale=320:240 out.mp4" nil :count 0) 37 | ("ffmpeg-side-by-side" "ffmpeg -i left.mp4 -i right.mp4 -b:v 10000k -r 24 -filter_complex \"[0:v:0]pad=iw*2:ih[bg]; [bg][1:v:0]overlay=w\" out.mp4" nil :count 0) 38 | ("ffmpeg-to-images" "ffmpeg -i in.mp4 -f image2 %04d.jpg" nil :count 0) 39 | ("ffmpeg-top-bottom" "ffmpeg -i top.mp4 -i bottom.mp4 -b:v 10000k -r 24 -filter_complex \"[0:v]pad=iw:ih*2[bg]; [bg][1:v]overlay=0:h\" out.mp4" nil :count 0) 40 | ("git-fetch-pr" "git fetch origin pull/ID/head:BRANCHNAME" nil :count 0) 41 | ("ln" "ln -s target link" nil :count 0) 42 | ("mencoder-concat" "mencoder -oac copy -ovc copy -idx -o output.mp4 *.mp4" nil :count 0) 43 | ("pip-install-recursive" "find . -name \"requirements.txt\" -type f -exec pip install -r '{}' ';'" nil :count 0) 44 | ("ssh-keygen" "ssh-keygen -t rsa -C \"your_email@example.com\"" nil :count 0) 45 | )) 46 | 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | backups 2 | auto-save-list 3 | eln-cache 4 | url 5 | elpa 6 | emacs-init.el 7 | ido.last 8 | smex-items 9 | -------------------------------------------------------------------------------- /.mc-lists.el: -------------------------------------------------------------------------------- 1 | ;; This file is automatically generated by the multiple-cursors extension. 2 | ;; It keeps track of your preferences for running commands with multiple cursors. 3 | 4 | (setq mc/cmds-to-run-for-all 5 | '( 6 | backward-sexp 7 | c-electric-backspace 8 | c-electric-colon 9 | c-electric-delete-forward 10 | c-electric-lt-gt 11 | c-electric-paren 12 | c-electric-semi&comma 13 | c-electric-slash 14 | c-electric-star 15 | c-end-of-statement 16 | c-indent-line-or-region 17 | capitalize-dispatch 18 | comint-delchar-or-maybe-eof 19 | comment-dwim 20 | comment-dwim-or-comment-region-as-kill 21 | delete-indentation 22 | dired-jump-to-top 23 | dired-next-line 24 | double-quote-word 25 | downcase-dispatch 26 | dp-self-insert-command 27 | electric-newline-and-maybe-indent 28 | end-of-buffer 29 | eval-dispatch 30 | fixup-whitespace 31 | forward-sexp 32 | hippie-expand-ido 33 | increment-decimal-number-at-point 34 | indent-for-tab-command 35 | isearch-abort 36 | join-following-line 37 | kill-region 38 | kmacro-end-or-call-macro 39 | markdown-cycle 40 | markdown-exdent-or-delete 41 | move-text-up 42 | mwim-beginning-of-code-or-line 43 | mwim-end-of-line-or-code 44 | nxml-electric-slash 45 | open-line-indent 46 | org-backward-sentence 47 | org-beginning-of-line 48 | org-cycle 49 | org-delete-char 50 | org-end-of-line 51 | org-force-self-insert 52 | org-forward-sentence 53 | org-kill-line 54 | org-return-indent 55 | org-self-insert-command 56 | org-yank 57 | orgtbl-hijacker-command-102 58 | orgtbl-self-insert-command 59 | paredit-splice-sexp 60 | python-indent-dedent-line-backspace 61 | python-indent-electric-colon 62 | sgml-slash 63 | sh-assignment 64 | smart-beginning-of-line 65 | sp--self-insert-command 66 | sp-remove-active-pair-overlay 67 | sp-self-insert-command 68 | sp-wrap-cancel 69 | transpose-words 70 | upcase-dispatch 71 | wdired-downcase-word 72 | wrap-region-trigger 73 | yaml-electric-backspace 74 | yas-expand 75 | zap-up-to-char 76 | )) 77 | 78 | (setq mc/cmds-to-run-once 79 | '( 80 | compile 81 | describe-key 82 | dired-details-hide 83 | dired-toggle-read-only 84 | edebug-next-mode 85 | ediff-next-difference 86 | find-tag 87 | ido-find-file 88 | ido-switch-buffer 89 | ignore 90 | isearch-printing-char 91 | ispell-word 92 | keyboard-escape-quit 93 | mc/align 94 | mc/dwim 95 | mc/mark-all-dispatch 96 | org-list-repair 97 | overwrite-mode 98 | python-shell-send-buffer 99 | smart-isearch-backward 100 | smart-isearch-forward 101 | smex 102 | toggle-input-method 103 | universal-argument-minus 104 | vimgolf-submit 105 | wdired-abort-changes 106 | wdired-finish-edit 107 | )) 108 | -------------------------------------------------------------------------------- /.yasnippets/c++-mode/.yas-parents: -------------------------------------------------------------------------------- 1 | cc-mode 2 | -------------------------------------------------------------------------------- /.yasnippets/c++-mode/.yas-setup: -------------------------------------------------------------------------------- 1 | (defun sanitize-to-identifier (str) 2 | (mapconcat 'identity (split-string str "[][ ~`!@#$%^&*()+={}|\:;\"'<,>.?/]+") "_")) 3 | 4 | (defun simplify-nested-name (str) 5 | (car (last (split-string str ":")))) 6 | -------------------------------------------------------------------------------- /.yasnippets/c++-mode/beginend: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : v.begin(), v.end() 3 | # key: beg 4 | # -- 5 | ${1:v}.begin(), $1.end() -------------------------------------------------------------------------------- /.yasnippets/c++-mode/cerr: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: cerr 3 | # key: cerr 4 | # -- 5 | std::cerr << $0 << std::endl; -------------------------------------------------------------------------------- /.yasnippets/c++-mode/cin: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: cin 3 | # key: cin 4 | # -- 5 | std::cin >> $0; -------------------------------------------------------------------------------- /.yasnippets/c++-mode/cit: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: const iterator 3 | # key: cit 4 | # -- 5 | ${1:std::vector}::const_iterator $0it; -------------------------------------------------------------------------------- /.yasnippets/c++-mode/class: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: class 3 | # key: class 4 | # -- 5 | class $0 6 | { 7 | }; 8 | -------------------------------------------------------------------------------- /.yasnippets/c++-mode/copy_constructor: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: common copy constructor and assignment 3 | # key: copy_constructor 4 | # -- 5 | $1::${1:$(simplify-nested-name yas-text)}(const $1 &other) 6 | { 7 | (*this) = other; 8 | } 9 | $1& $1::operator=(const $1 &other) 10 | { 11 | if (this != &other) 12 | { 13 | memcpy(this, &other, sizeof(*this)); 14 | } 15 | 16 | return *this; 17 | } -------------------------------------------------------------------------------- /.yasnippets/c++-mode/cout: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: cout 3 | # key: cout 4 | # -- 5 | std::cout << $0 << std::endl; -------------------------------------------------------------------------------- /.yasnippets/c++-mode/doc: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: doc 3 | # key: doc 4 | # -- 5 | /** 6 | * $0 7 | */ -------------------------------------------------------------------------------- /.yasnippets/c++-mode/enum: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: enum 3 | # key: enum 4 | # -- 5 | enum $0 6 | { 7 | }; -------------------------------------------------------------------------------- /.yasnippets/c++-mode/eulerangles: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: convert between Euler angles and 3x3 rotation matrix 3 | # key: eulerangles 4 | # -- 5 | /** this conversion uses NASA standard aeroplane conventions as described on page: 6 | * http://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm 7 | * Coordinate System: right hand 8 | * Positive angle: right hand 9 | * Order of euler angles: heading first, then attitude, then bank 10 | * matrix row column ordering: 11 | * [m00 m01 m02] 12 | * [m10 m11 m12] 13 | * [m20 m21 m22]*/ 14 | cv::Matx33f euler2mat(const cv::Vec3f &euler) 15 | { 16 | double heading = euler[0]; 17 | double attitude = euler[1]; 18 | double bank = euler[2]; 19 | 20 | // Assuming the angles are in radians. 21 | double ch = cos(heading); 22 | double sh = sin(heading); 23 | double ca = cos(attitude); 24 | double sa = sin(attitude); 25 | double cb = cos(bank); 26 | double sb = sin(bank); 27 | 28 | double m00 = ch * ca; 29 | double m01 = sh*sb - ch*sa*cb; 30 | double m02 = ch*sa*sb + sh*cb; 31 | double m10 = sa; 32 | double m11 = ca*cb; 33 | double m12 = -ca*sb; 34 | double m20 = -sh*ca; 35 | double m21 = sh*sa*cb + ch*sb; 36 | double m22 = -sh*sa*sb + ch*cb; 37 | 38 | return cv::Matx33f(m00, m01, m02, 39 | m10, m11, m12, 40 | m20, m21, m22); 41 | } 42 | 43 | /** this conversion uses conventions as described on page: 44 | * http://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm 45 | * Coordinate System: right hand 46 | * Positive angle: right hand 47 | * Order of euler angles: heading first, then attitude, then bank 48 | * matrix row column ordering: 49 | * [m00 m01 m02] 50 | * [m10 m11 m12] 51 | * [m20 m21 m22]*/ 52 | cv::Vec3f mat2euler(const cv::Matx33f &m) { 53 | // Assuming the angles are in radians. 54 | if (m(1,0) > 0.998) { // singularity at north pole 55 | double heading = atan2(m(0,2), m(2,2)); 56 | double attitude = M_PI/2; 57 | double bank = 0; 58 | return cv::Vec3f(heading, attitude, bank); 59 | } 60 | if (m(1,0) < -0.998) { // singularity at south pole 61 | double heading = atan2(m(0,2), m(2,2)); 62 | double attitude = -M_PI/2; 63 | double bank = 0; 64 | return cv::Vec3f(heading, attitude, bank); 65 | } 66 | 67 | double heading = atan2(-m(2,0), m(0,0)); 68 | double attitude = asin ( m(1,0)); 69 | double bank = atan2(-m(1,2), m(1,1)); 70 | 71 | return cv::Vec3f(heading, attitude, bank); 72 | } -------------------------------------------------------------------------------- /.yasnippets/c++-mode/exists: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: checks whether the file exists 3 | # key: exists 4 | # -- 5 | #include 6 | static bool exists(const std::string &filename) 7 | { 8 | std::ifstream infile(filename.c_str()); 9 | bool ret = infile.good(); 10 | infile.close(); 11 | return ret; 12 | } -------------------------------------------------------------------------------- /.yasnippets/c++-mode/fori: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: fori 3 | # key: fori 4 | # -- 5 | for (${1:auto}${1:$(and (> (length yas-text) 0) " ")}${2:it} = ${3:vec}.begin(); $2 != $3.end(); ++$2) 6 | { 7 | $0 8 | } 9 | -------------------------------------------------------------------------------- /.yasnippets/c++-mode/formatidx: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: formatidx 3 | # key: formatidx 4 | # -- 5 | #include 6 | #include 7 | std::string format_idx(size_t idx) 8 | { 9 | std::ostringstream oss; 10 | oss << std::setw(4) << std::setfill('0') << idx; 11 | return oss.str(); 12 | } -------------------------------------------------------------------------------- /.yasnippets/c++-mode/hpp: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: hpp 3 | # key: hpp 4 | # -- 5 | #include "`(file-name-nondirectory (file-name-sans-extension (buffer-file-name)))`.hpp" -------------------------------------------------------------------------------- /.yasnippets/c++-mode/imshow: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: imshow 3 | # key: imshow 4 | # -- 5 | cv::imshow("$1", $1); 6 | cv::waitKey(0$0); -------------------------------------------------------------------------------- /.yasnippets/c++-mode/io: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: io 3 | # key: io 4 | # -- 5 | #include -------------------------------------------------------------------------------- /.yasnippets/c++-mode/it: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: iterator 3 | # key: it 4 | # -- 5 | ${1:std::vector}::iterator $0it; -------------------------------------------------------------------------------- /.yasnippets/c++-mode/map: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: map 3 | # key: map 4 | # -- 5 | std::map<${1:from}, ${2:to}> $0; -------------------------------------------------------------------------------- /.yasnippets/c++-mode/rotation: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: rotation 3 | # key: rotation 4 | # -- 5 | //--------------------------------------------------------------------------- 6 | // This function performs an axis/angle rotation. (x,y,z) is any 7 | // vector on the axis. For greater speed, always use a unit vector 8 | // (length = 1). In this version, we will assume an arbitrary 9 | // length and normalize. 10 | void axis_angle2mat(double x, double y, double z, double theta, 11 | double mat[9]) 12 | { 13 | double length; 14 | double c,s,t; 15 | 16 | // normalize 17 | length = sqrt(x*x + y*y + z*z); 18 | 19 | // too close to 0, can't make a normalized vector 20 | if (length < .000001) 21 | return; 22 | 23 | x /= length; 24 | y /= length; 25 | z /= length; 26 | 27 | // do the trig 28 | c = cos(theta); 29 | s = sin(theta); 30 | t = 1 - c; 31 | 32 | // build the rotation matrix 33 | mat[0] = t*x*x + c; 34 | mat[1] = t*x*y - s*z; 35 | mat[2] = t*x*z + s*y; 36 | 37 | mat[3] = t*y*x + s*z; 38 | mat[4] = t*y*y + c; 39 | mat[5] = t*y*z - s*x; 40 | 41 | mat[6] = t*z*x - s*y; 42 | mat[7] = t*z*y + s*x; 43 | mat[8] = t*z*z + c; 44 | } 45 | -------------------------------------------------------------------------------- /.yasnippets/c++-mode/savepbm: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: save image in pbm ascii format 3 | # key: savepbm 4 | # -- 5 | std::vector params; 6 | params.push_back(CV_IMWRITE_PXM_BINARY); 7 | params.push_back(0); 8 | cv::imwrite("image.pbm", image, params); -------------------------------------------------------------------------------- /.yasnippets/c++-mode/std: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: std 3 | # key: std 4 | # -- 5 | using namespace std; -------------------------------------------------------------------------------- /.yasnippets/c++-mode/timer: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: wrap with timer macros 3 | # key: timer 4 | # -- 5 | NVX_TIMER(${1:$(sanitize-to-identifier yas-text)}, \"$1\"); 6 | $0 7 | NVX_TIMEROFF(${1:$(sanitize-to-identifier yas-text)}); 8 | -------------------------------------------------------------------------------- /.yasnippets/c++-mode/tostring: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: generic to_string function 3 | # key: to_string 4 | # -- 5 | #include 6 | template 7 | std::string to_string(const T& obj) 8 | { 9 | std::ostringstream oss; 10 | oss << obj; 11 | return oss.str(); 12 | } -------------------------------------------------------------------------------- /.yasnippets/c++-mode/try: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: try 3 | # key: try 4 | # -- 5 | try 6 | { 7 | } 8 | catch ($0) 9 | { 10 | } -------------------------------------------------------------------------------- /.yasnippets/cc-mode/do: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : do { ... } while (...) 3 | # key: do 4 | # -- 5 | do 6 | { 7 | } while ($0); -------------------------------------------------------------------------------- /.yasnippets/cc-mode/for: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: for 3 | # key: for 4 | # 5 | # my goodness... 6 | # -- 7 | for (${1:int}${1:$(and (> (length yas-text) 0) " ")}${2:i = 0}; ${2:$(string-trim (car (split-string yas-text "=")))} ${3:< N}; ${2:$(concat (string-trim (car (split-string yas-text "="))))}${3:$(if (> (length (split-string yas-text ">")) 1) "--" "++")}) 8 | { 9 | $0 10 | } -------------------------------------------------------------------------------- /.yasnippets/cc-mode/if: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : if (...) { ... } 3 | # key: if 4 | # -- 5 | if ($0) 6 | { 7 | } -------------------------------------------------------------------------------- /.yasnippets/cc-mode/ifdef: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: ifdef 3 | # key: ifdef 4 | # -- 5 | #ifdef ${1:MACRO} 6 | 7 | $0 8 | 9 | #endif // $1 -------------------------------------------------------------------------------- /.yasnippets/cc-mode/inc: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : #include "..." 3 | # key : inc 4 | # -- 5 | #include "$0" -------------------------------------------------------------------------------- /.yasnippets/cc-mode/inv: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : matrix_inverse 3 | # key: inv 4 | # -- 5 | template 6 | void matrix_inverse(T in[9], T out[9]) 7 | { 8 | // row-major storage 9 | 10 | T det = 11 | in(0) * (in[4] * in[8] - in[7] * in[5]) - 12 | in(1) * (in[3] * in[8] - in[5] * in[6]) + 13 | in(2) * (in[3] * in[7] - in[4] * in[6]); 14 | 15 | T invdet = 1.0 / det; 16 | 17 | out[0] = (in[4] * in[8] - in[7] * in[5]) * invdet; 18 | out[1] = (in[2] * in[7] - in[1] * in[8]) * invdet; 19 | out[2] = (in[1] * in[5] - in[2] * in[4]) * invdet; 20 | out[3] = (in[5] * in[6] - in[3] * in[8]) * invdet; 21 | out[4] = (in[0] * in[8] - in[2] * in[6]) * invdet; 22 | out[5] = (in[3] * in[2] - in[0] * in[5]) * invdet; 23 | out[6] = (in[3] * in[7] - in[6] * in[4]) * invdet; 24 | out[7] = (in[6] * in[1] - in[0] * in[7]) * invdet; 25 | out[8] = (in[0] * in[4] - in[3] * in[1]) * invdet; 26 | } -------------------------------------------------------------------------------- /.yasnippets/cc-mode/main: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: main 3 | # key: main 4 | # -- 5 | int main(int argc, char *argv[]) 6 | { 7 | $0 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /.yasnippets/cc-mode/once: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : #ifndef XXX; #define XXX; #endif 3 | # key: once 4 | # -- 5 | #ifndef ${1:`(upcase (concat "__" (file-name-nondirectory (file-name-sans-extension (buffer-file-name))) "_" (file-name-extension (buffer-file-name)) "__"))`} 6 | #define $1 7 | 8 | $0 9 | 10 | #endif /* $1 */ 11 | -------------------------------------------------------------------------------- /.yasnippets/cc-mode/struct: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : struct ... { ... } 3 | # key: struct 4 | # -- 5 | struct $0 6 | { 7 | }; -------------------------------------------------------------------------------- /.yasnippets/cc-mode/typedef: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: typedef 3 | # key: typedef 4 | # -- 5 | typedef ${1:type} ${2:alias}; -------------------------------------------------------------------------------- /.yasnippets/cc-mode/while: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: while 3 | # key: while 4 | # -- 5 | while ($0) 6 | { 7 | } -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/cmake_minimum_required: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: cmake_minimum_required 3 | # key: min 4 | # -- 5 | cmake_minimum_required(VERSION ${1:2.6}) -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/copy: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: copy 3 | # key: copy 4 | # -- 5 | add_custom_target(copy_public_resources ALL 6 | COMMAND "\${CMAKE_COMMAND}" -E copy_directory "\${CMAKE_CURRENT_SOURCE_DIR}/public/" "\${CMAKE_CURRENT_BINARY_DIR}/public/" 7 | COMMENT "Copy public resources to runtime output directory") 8 | -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/dependency: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: adds explicit dependency to a source file 3 | # key: dependency 4 | # contributor: http://stackoverflow.com/questions/6545879/cmake-dependency-on-defined-preprocessor-header-file 5 | # -- 6 | set_property(SOURCE ${1:source} APPEND PROPERTY OBJECT_DEPENDS "${2:dependency}") -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/foreach: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: foreach 3 | # key: for 4 | # -- 5 | foreach(${1:item} \${${2:array}}) 6 | $0 7 | endforeach() -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/function: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: function 3 | # key: fun 4 | # -- 5 | function ($0) 6 | endfunction() -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/if: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: if 3 | # key: if 4 | # -- 5 | if($0) 6 | endif() -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/ifelse: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: ifelse 3 | # key: ife 4 | # -- 5 | if($0) 6 | else() 7 | endif() -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/macro: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: macro 3 | # key: macro 4 | # -- 5 | macro($0) 6 | endmacro() -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/message: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: message 3 | # key: msg 4 | # -- 5 | message(${1:STATUS }"$0") -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/option: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: option 3 | # key: opt 4 | # -- 5 | option(${1:OPT} "${2:docstring}" ${3:value}) -------------------------------------------------------------------------------- /.yasnippets/cmake-mode/std11: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: std11 3 | # key: std11 4 | # -- 5 | set(CMAKE_CXX_STANDARD 11) -------------------------------------------------------------------------------- /.yasnippets/conf-mode/host: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : example entry in ~/.ssh/config 3 | # key: host 4 | # expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil)) 5 | # -- 6 | Host sergei-ws 7 | User sergei 8 | HostName 192.168.0.80 9 | -------------------------------------------------------------------------------- /.yasnippets/d-mode/do: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : do { ... } while (...) 3 | # key: do 4 | # -- 5 | do 6 | { 7 | } while ($0); -------------------------------------------------------------------------------- /.yasnippets/d-mode/doc: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: doc 3 | # key: doc 4 | # -- 5 | /** 6 | * $0 7 | */ -------------------------------------------------------------------------------- /.yasnippets/d-mode/for: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: for 3 | # key: for 4 | # 5 | # my goodness... 6 | # -- 7 | for (${1:size_t}${1:$(and (> (length yas-text) 0) " ")}${2:i = 0}; ${2:$(string-trim (car (split-string yas-text "=")))} ${3:< N}; ${2:$(concat (string-trim (car (split-string yas-text "="))))}${3:$(if (> (length (split-string yas-text ">")) 1) "--" "++")}) 8 | { 9 | $0 10 | } -------------------------------------------------------------------------------- /.yasnippets/d-mode/foreach: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: foreach 3 | # key: fore 4 | # -- 5 | foreach (${1:ref}${1:$(and (> (length yas-text) 0) " ")}${2:i}; $0) 6 | { 7 | } -------------------------------------------------------------------------------- /.yasnippets/d-mode/if: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : if (...) { ... } 3 | # key: if 4 | # -- 5 | if ($0) 6 | { 7 | } -------------------------------------------------------------------------------- /.yasnippets/d-mode/io: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : import std.stdio; 3 | # key: io 4 | # -- 5 | import std.stdio; -------------------------------------------------------------------------------- /.yasnippets/d-mode/main: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: main 3 | # key: main 4 | # -- 5 | int main(string[] args) 6 | { 7 | $0 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /.yasnippets/d-mode/pure: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : pure nothrow @safe @nogc 3 | # key: pure 4 | # -- 5 | pure nothrow @safe @nogc$0 -------------------------------------------------------------------------------- /.yasnippets/d-mode/read: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : useful generic read function 3 | # key: read 4 | # -- 5 | T read(T)() 6 | { 7 | T t; 8 | readf(" %s", &t); 9 | return t; 10 | } -------------------------------------------------------------------------------- /.yasnippets/d-mode/sif: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : static if (...) { ... } 3 | # key: sif 4 | # -- 5 | static if ($0) 6 | { 7 | } -------------------------------------------------------------------------------- /.yasnippets/d-mode/unittest: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : unittest ... { ... } 3 | # key: unit 4 | # -- 5 | unittest 6 | { 7 | $0 8 | } -------------------------------------------------------------------------------- /.yasnippets/d-mode/while: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: while 3 | # key: while 4 | # -- 5 | while ($0) 6 | { 7 | } -------------------------------------------------------------------------------- /.yasnippets/emacs-lisp-mode/char: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: generate successive chars 3 | # key: char 4 | # -- 5 | (char-to-string (+ ?x $0)) -------------------------------------------------------------------------------- /.yasnippets/emacs-lisp-mode/defun: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: defun 3 | # key: def 4 | # -- 5 | (defun ${1:fun} (${2:args}) 6 | ${3:(interactive "P")} 7 | $0) -------------------------------------------------------------------------------- /.yasnippets/emacs-lisp-mode/dirlocals: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: dirlocals 3 | # key: dirlocals 4 | # -- 5 | ((d-mode . ((indent-tabs-mode . t) 6 | (tab-width . 4) 7 | (c-basic-offset . 4) 8 | (fill-column . 80)))) -------------------------------------------------------------------------------- /.yasnippets/emacs-lisp-mode/ifu: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: if universal argument is provided 3 | # key: ifu 4 | # -- 5 | (if (equal current-prefix-arg '(4)) 6 | $0) -------------------------------------------------------------------------------- /.yasnippets/emacs-lisp-mode/join: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: join list of strings 3 | # key: join 4 | # -- 5 | (mapconcat 'identity $0 " ") -------------------------------------------------------------------------------- /.yasnippets/gitconfig-mode/pull: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: fetch github pull requests 3 | # key: pull 4 | # -- 5 | fetch = +refs/pull/*/head:refs/remotes/origin/pull/* -------------------------------------------------------------------------------- /.yasnippets/make-mode/hello: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: hello 3 | # key: hello 4 | # -- 5 | hellomake: hellomake.c hellofunc.c 6 | gcc -o hellomake hellomake.c hellofunc.c -I. 7 | -------------------------------------------------------------------------------- /.yasnippets/org-mode/example: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: example 3 | # key: example 4 | # -- 5 | #+BEGIN_EXAMPLE 6 | $0 7 | #+END_EXAMPLE 8 | -------------------------------------------------------------------------------- /.yasnippets/org-mode/fullpage: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: fullpage 3 | # key: fullpage 4 | # -- 5 | #+LATEX_HEADER: \usepackage[cm]{fullpage} -------------------------------------------------------------------------------- /.yasnippets/org-mode/notes: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: notes 3 | # key: notes 4 | # -- 5 | #+BEGIN_NOTES 6 | $0 7 | #+END_NOTES 8 | -------------------------------------------------------------------------------- /.yasnippets/org-mode/revealjs: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: revealjs 3 | # key: revealjs 4 | # -- 5 | #+TITLE: Rocky IV 6 | #+DATE: (format-time-string "%Y-%m-%d") 7 | #+AUTHOR: Ivan Drago 8 | #+EMAIL: ivan.drago@rocky.com 9 | 10 | #+OPTIONS: toc:nil num:nil ^:nil reveal_mathjax:t 11 | #+REVEAL_ROOT: ./reveal.js 12 | #+REVEAL_THEME: league 13 | 14 | * Image 15 | 16 | #+ATTR_HTML: :align center :width 50% 17 | [[https://www.google.com/images/srpr/logo11w.png]] 18 | 19 | * Code block and notes 20 | 21 | #+BEGIN_SRC cpp 22 | int main(int argc, char *argv[]) 23 | { 24 | return 0; 25 | } 26 | #+END_SRC 27 | 28 | #+BEGIN_NOTES 29 | - Press 's' to show speaker notes (works only in local server mode) 30 | - Press 'b' to fade out everything 31 | #+END_NOTES 32 | 33 | * $\LaTeX$ 34 | 35 | $distance = \frac{f_x \cdot baseline}{disparity}$ 36 | 37 | * Fragment attributes 38 | 39 | #+ATTR_REVEAL: :frag (grow fade-out roll-in current-visible shrink highlight-current-blue highlight-green) 40 | - grow 41 | - fade-out 42 | - roll-in 43 | - current-visible 44 | - shrink 45 | - highlight-current-blue 46 | - highlight-green 47 | 48 | * Two columns 49 | 50 | #+REVEAL_HTML:
51 | 52 | [[https://www.google.com/images/srpr/logo11w.png]] 53 | 54 | #+REVEAL_HTML:
55 | 56 | #+REVEAL_HTML:
57 | 58 | That's a really nice image on the left that I find very interesting. I can even 59 | make up a good amount of text about it not touching the essence and disregarding 60 | the content of the image completely. So nice it is! 61 | 62 | #+REVEAL_HTML:
63 | -------------------------------------------------------------------------------- /.yasnippets/org-mode/src: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: src 3 | # key: src 4 | # -- 5 | #+BEGIN_SRC ${1:emacs-lisp} 6 | $0 7 | #+END_SRC 8 | -------------------------------------------------------------------------------- /.yasnippets/python-mode/arg: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: arg 3 | # key: arg 4 | # group: argparser 5 | # -- 6 | parser.add_argument('-$1', '--$2', 7 | $0) 8 | -------------------------------------------------------------------------------- /.yasnippets/python-mode/doc: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: doc 3 | # key: doc 4 | # -- 5 | """$0 6 | """ -------------------------------------------------------------------------------- /.yasnippets/python-mode/for: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: for ... in ... : ... 3 | # key: for 4 | # group : control structure 5 | # -- 6 | for ${var} in ${collection}: 7 | $0 -------------------------------------------------------------------------------- /.yasnippets/python-mode/from: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: from 3 | # key: from 4 | # group : general 5 | # -- 6 | from ${1:lib} import ${2:funs} -------------------------------------------------------------------------------- /.yasnippets/python-mode/if: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: if 3 | # key: if 4 | # group : control structure 5 | # -- 6 | if ${1:cond}: 7 | $0 8 | -------------------------------------------------------------------------------- /.yasnippets/python-mode/ife: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: ife 3 | # key: ife 4 | # group : control structure 5 | # -- 6 | if $1: 7 | $2 8 | else: 9 | $0 10 | -------------------------------------------------------------------------------- /.yasnippets/python-mode/ifmain: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: ifmain 3 | # key: ifmain 4 | # -- 5 | if __name__ == '__main__': 6 | ${1:main()} -------------------------------------------------------------------------------- /.yasnippets/python-mode/import: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: import 3 | # key: import 4 | # group : general 5 | # -- 6 | import ${1:lib}${2: as ${3:alias}} 7 | $0 -------------------------------------------------------------------------------- /.yasnippets/python-mode/matplotlib: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: matplotlib simple app 3 | # key: matplotlib 4 | # group : general 5 | # -- 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | 9 | # evenly sampled time at 200ms intervals 10 | t = np.arange(0., 5., 0.2) 11 | 12 | # red dashes, blue squares and green triangles 13 | plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^') 14 | plt.show() 15 | -------------------------------------------------------------------------------- /.yasnippets/python-mode/parse_args: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: parse_args 3 | # key: pargs 4 | # group: argparser 5 | # -- 6 | def parse_arguments(): 7 | parser = argparse.ArgumentParser(description='$1') 8 | $0 9 | return parser.parse_args() -------------------------------------------------------------------------------- /.yasnippets/python-mode/script: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: script 3 | # key: script 4 | # -- 5 | #!/usr/bin/env python 6 | 7 | def main(): 8 | pass 9 | 10 | if __name__ == '__main__': 11 | main() 12 | -------------------------------------------------------------------------------- /.yasnippets/python-mode/try: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: try 3 | # key: try 4 | # -- 5 | try: 6 | $0 7 | except ${1:Exception ex}: 8 | pass -------------------------------------------------------------------------------- /.yasnippets/python-mode/while: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: while 3 | # key: while 4 | # group: control structure 5 | # -- 6 | while ${1:True}: 7 | $0 -------------------------------------------------------------------------------- /.yasnippets/python-mode/with: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: with 3 | # key: with 4 | # group : control structure 5 | # -- 6 | with open(fname) as f: 7 | content = f.readlines() -------------------------------------------------------------------------------- /.yasnippets/sh-mode/#!: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: shebang 3 | # key: #! 4 | # -- 5 | #!/usr/bin/env bash -------------------------------------------------------------------------------- /.yasnippets/sh-mode/argn: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: request a specific number of arguments 3 | # key: argn 4 | # -- 5 | if [[ $# -lt $1 ]] ; then echo "Please, provide at least $1 argument(s)" ; exit 1 ; fi -------------------------------------------------------------------------------- /.yasnippets/sh-mode/base: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: base name of a file 3 | # key: base 4 | # -- 5 | $\{${1:VAR}%.*\} -------------------------------------------------------------------------------- /.yasnippets/sh-mode/dir: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: directory of the script 3 | # key: directory 4 | # -- 5 | DIR="$( cd "$( dirname "\${BASH_SOURCE[0]}" )" && pwd )" -------------------------------------------------------------------------------- /.yasnippets/sh-mode/ext: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: extension of a file 3 | # key: ext 4 | # -- 5 | $\{${1:VAR}##*.\} 6 | -------------------------------------------------------------------------------- /.yasnippets/sh-mode/for: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: for loop 3 | # key: for 4 | # -- 5 | for ${1:var} in $0 6 | do 7 | done -------------------------------------------------------------------------------- /.yasnippets/sh-mode/guard: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: must haves in a shell script 3 | # key: guard 4 | # -- 5 | set -e # exit on error 6 | set -u # disallow unset variables usage -------------------------------------------------------------------------------- /.yasnippets/sh-mode/if: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: if 3 | # key: if 4 | # -- 5 | if [ $0 ]; then 6 | fi 7 | -------------------------------------------------------------------------------- /.yasnippets/sh-mode/ife: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: ife 3 | # key: ife 4 | # -- 5 | if [ $0 ]; then 6 | else 7 | fi 8 | -------------------------------------------------------------------------------- /.yasnippets/sh-mode/ifne0: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: triggers if previous command returned non-zero 3 | # key: ifne0 4 | # -- 5 | if [ $? -ne 0 ]; then 6 | exit 125 # skip code for git bisect 7 | fi -------------------------------------------------------------------------------- /.yasnippets/sh-mode/inc: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: increment 3 | # key: inc 4 | # -- 5 | ${1:count}=$(($1 + 1)) -------------------------------------------------------------------------------- /.yasnippets/sh-mode/name: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: base name of a file without path 3 | # key: name 4 | # -- 5 | $(basename "$${1:VAR}") 6 | -------------------------------------------------------------------------------- /.yasnippets/sh-mode/root: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name: require root rights 3 | # key: root 4 | # -- 5 | if [[ $(id -u) -ne 0 ]] ; then echo "Please, run as sudo" ; exit 1 ; fi -------------------------------------------------------------------------------- /.yasnippets/snippet-mode/field: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : ${ ... } field 3 | # key: field 4 | # -- 5 | \${${1:n}:${2:\$(lisp-fn)}} -------------------------------------------------------------------------------- /.yasnippets/snippet-mode/mirror: -------------------------------------------------------------------------------- 1 | # -*- mode: snippet -*- 2 | # name : ${ ... } mirror 3 | # key: mirror 4 | # -- 5 | \${${1:n}:${2:\$(lisp-fn yas-text)}} -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | emacs-init.org -------------------------------------------------------------------------------- /ideas.org: -------------------------------------------------------------------------------- 1 | * Отдельные методички 2 | ** Шпаргалка 3 | ** Как поставить dmd 4 | - dired (открыть директорию в ido) - должен быть в самом начале! 5 | - M-& 6 | - пишем программу - комментарии, M-q 7 | - компилируем 8 | - создаем репозиторий, смотрим в магит 9 | - выкладываем на гитхаб 10 | - Таги 11 | ** Пишем презентацию в орг-моде (в маркдауне) 12 | ** Ссылки на конфиг 13 | 14 | - Google, Lingvo, C-c C-o 15 | - Выполнение лиспа 16 | - Редактирование под рутом 17 | - remote-term 18 | - проверка правописания 19 | 20 | ** Разное 21 | - контекстное авто-дополнение 22 | - починить нумерованый список 23 | - ввод Unicode символов 24 | - re-builder 25 | - M-; 26 | - M-g g 27 | - C-x C-q 28 | - C-o, M-j 29 | - Парные скобочки, удаление 30 | - M-p, M-n 31 | - M-c, M-u, M-l 32 | - C-x C-o, M-\ 33 | - hexl-mode 34 | - M-| 35 | - Регистры 36 | - редактирование в диред 37 | - grep, wgrep 38 | - calc 39 | - rectangular regions 40 | - Дополнительные курсоры 41 | В начале данного обзора я выразил мысль, что редактирование текста - давно 42 | решенная задача. Однако даже в, казалось бы, самых понятных областях нет-нет, да 43 | и произойдет какое-нибудь открытие. Одним из относительно недавних примеров 44 | такого открытия является идея использования /дополнительных курсоров/ для 45 | одновременного редактирования текста сразу в нескольких местах. Т.е. в буфере 46 | создаются, например, три курсора и последующие команды выполняются для каждого 47 | из них: жмем =C-b= - все три курсора сдвигаются влево, жмем =M-d= - удаляются 3 48 | слова - справа от каждого курсора. 49 | 50 | Трудно сказать, кто был её первооткрывателем и когда она впервые была 51 | реализована. Но более или менее широкое распространение она получила около 2010 52 | года. Такая запоздалая адаптация, наверное, связана с тем, что идея кажется 53 | слишком уж экзотической. Плюс, функциональность дополнительных курсоров во 54 | многом перекрывается с функциональностью клавиатурных макросов, интерактивной 55 | замены и /прямоугольных регионов/ (которые я ещё не затрагивал в рамках данного 56 | обзора). 57 | 58 | Признаться, мне и самому поначалу казалось, что ничего хорошего из неё 59 | получиться не может. Но прошло совсем немного времени и теперь я с трудом 60 | представляю, как без неё можно обходиться. 61 | 62 | Начнем знакомство с дополнительными курсорами на простом примере. 63 | -------------------------------------------------------------------------------- /init.el: -------------------------------------------------------------------------------- 1 | ;; initialization 2 | (require 'package) 3 | (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) 4 | (setq package-enable-at-startup nil) 5 | (package-initialize) 6 | 7 | ;; list of required packages 8 | (setq required-packages 9 | '( 10 | browse-kill-ring 11 | cmake-mode 12 | d-mode 13 | dummyparens 14 | expand-region 15 | smex 16 | string-edit-at-point 17 | window-numbering 18 | markdown-mode 19 | magit 20 | move-text 21 | multiple-cursors 22 | mwim 23 | org-pomodoro 24 | toc-org 25 | treesit-auto 26 | paredit 27 | unfill 28 | yaml-mode 29 | yasnippet 30 | wgrep 31 | )) 32 | 33 | ;; install external packages 34 | (require 'cl-lib) 35 | (map-y-or-n-p 36 | "Package %s is missing. Install? " 37 | '(lambda (package) 38 | (when (not package-archive-contents) 39 | (package-refresh-contents)) 40 | (package-install package)) 41 | (cl-remove-if 'package-installed-p required-packages) 42 | '("package" "packages" "install")) 43 | 44 | (setq vc-follow-symlinks t) 45 | 46 | (require 'ob-tangle) 47 | 48 | (org-babel-load-file (concat (file-name-directory load-file-name) "emacs-init.org")) 49 | -------------------------------------------------------------------------------- /tutorial.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Об одном текстовом редакторе 2 | #+AUTHOR: Сергей Носов 3 | #+EMAIL: sergei.nosov@gmail.com 4 | #+LATEX_HEADER: \usepackage[T2A]{fontenc} 5 | #+LATEX_HEADER: \usepackage[russian]{babel} 6 | #+LATEX_HEADER: \usepackage[cm]{fullpage} 7 | #+LATEX_HEADER: \usepackage[num,english]{isodate} 8 | #+LATEX_HEADER: \addto{\captionsenglish}{\renewcommand*{\contentsname}{Содержание}} 9 | 10 | * Введение 11 | 12 | В данном репозитории находится конфигурация редактора [[https://www.gnu.org/software/emacs/][Emacs]], которую я использую 13 | в своей профессиональной деятельности. В первую очередь она включает в себя 14 | разработку программного обеспечения, а также всё, что с этим связано - 15 | исследование, администрирование, тестирование, отладка, написание статей, 16 | презентаций, документации и т.п. 17 | 18 | Ниже приведен обзор возможностей Emacs и того, как ими пользоваться. Изложение в 19 | нём структурировано следующим образом: 20 | 21 | - Те, кому просто нужен текстовый редактор, могут ограничить чтение секциями 22 | "Установка", "Обозначения" и "Начало работы" (совсем небольшими по объему). 23 | - Тем, кто уже начал использовать Emacs и хотел бы узнать побольше о его 24 | основных возможностях, к прочтению рекомендуются все разделы. Думаю, что на их 25 | освоение уйдет один дождливый или морозный выходной. 26 | - Те, кто овладел основными возможностями Emacs и хочет узнать о более 27 | интересных способах его использования, могут найти полезные ссылки в 28 | заключении. 29 | 30 | * Содержание :TOC:noexport: 31 | - [[#введение][Введение]] 32 | - [[#установка][Установка]] 33 | - [[#обозначения][Обозначения]] 34 | - [[#начало-работы][Начало работы]] 35 | - [[#минимальные-требования][Минимальные требования]] 36 | - [[#слепая-печать][Слепая печать]] 37 | - [[#caps-lock---третий-ctrl][Caps Lock - третий Ctrl]] 38 | - [[#переключение-языка-на-superwin-space][Переключение языка на Super(Win)-Space]] 39 | - [[#файловый-менеджер][Файловый менеджер]] 40 | - [[#базовые-операции][Базовые операции]] 41 | - [[#работа-со-словами-и-другими-структурными-единицами][Работа со словами и другими структурными единицами]] 42 | - [[#окна-и-буферы][Окна и буферы]] 43 | - [[#определения][Определения]] 44 | - [[#список-буферов][Список буферов]] 45 | - [[#работа-с-окнами][Работа с окнами]] 46 | - [[#строка-состояния][Строка состояния]] 47 | - [[#вырезатьвставить][Вырезать/Вставить]] 48 | - [[#выделение-регионов][Выделение регионов]] 49 | - [[#базовые-операции-1][Базовые операции]] 50 | - [[#дополнительные-операции-вырезания][Дополнительные операции вырезания]] 51 | - [[#префиксный-аргумент][Префиксный аргумент]] 52 | - [[#численный-аргумент][Численный аргумент]] 53 | - [[#отрицательный-аргумент][Отрицательный аргумент]] 54 | - [[#универсальный-аргумент][Универсальный аргумент]] 55 | - [[#откат][Откат]] 56 | - [[#навигация-и-поиск][Навигация и поиск]] 57 | - [[#навигация][Навигация]] 58 | - [[#поиск-в-буфере][Поиск в буфере]] 59 | - [[#поиск-во-множестве-файлов][Поиск во множестве файлов]] 60 | - [[#замена][Замена]] 61 | - [[#клавиатурные-макросы][Клавиатурные макросы]] 62 | - [[#заключение][Заключение]] 63 | - [[#вопросы-и-ответы][Вопросы и ответы]] 64 | - [[#для-кого-предназначен-этот-обзор][Для кого предназначен этот обзор?]] 65 | - [[#так-ли-уж-важно-уметь-печатать-вслепую][Так ли уж важно уметь печатать вслепую?]] 66 | - [[#зачем-менять-caps-lock-на-ctrl][Зачем менять Caps Lock на Ctrl?]] 67 | - [[#что-такое-регулярные-выражения][Что такое регулярные выражения?]] 68 | 69 | * Установка 70 | 71 | Для установки Emacs, пользователи ОС [[http://www.ubuntu.com/][Ubuntu]] могут воспользоваться следующей 72 | командой: 73 | 74 | #+BEGIN_SRC sh 75 | sudo apt-get install emacs24 76 | #+END_SRC 77 | 78 | Для установки предлагаемой конфигурации: 79 | 80 | #+BEGIN_SRC sh 81 | git clone https://github.com/snosov1/dot-emacs.git ~/.emacs.d 82 | #+END_SRC 83 | 84 | Для запуска нужно в терминале выполнить команду =emacs=, либо воспользоваться 85 | любым другим способом, предоставляемым операционной системой. 86 | 87 | * Обозначения 88 | 89 | Единственное, что нужно обговорить перед тем, как перейти непосредственно к 90 | работе - обозначения комбинаций клавиш: 91 | 92 | 1. =С-= обозначает =Ctrl=. 93 | 2. =M-= обозначает =Alt=. Пользователи продукции Apple могут не найти 94 | у себя такой кнопки, её место (насколько мне известно) занимает клавиша =Cmd= 95 | и именно она функционирует в роли =M-=. 96 | 3. =S-= обозначает =Shift=. 97 | 98 | Эти символы участвуют в обозначении комбинаций клавиш, например: 99 | - =C-n= означает =Ctrl-n= 100 | - =C-x C-f= означает, что надо нажать =Ctrl-x= и потом =Ctrl-f= 101 | (=Ctrl= можно не отпускать между нажатиями =x= и =f=) 102 | - =C-c f= - означает, что нужно нажать =Ctrl-c= и потом (с отпущенным =Ctrl=) 103 | нажать =f= 104 | 105 | Самые часто используемые команды, такие как перемещение курсора, как правило, 106 | выполняются нажатием одного модификатора и одной буквенной клавиши. При этом 107 | буквенный символ чаще всего является мнемоническим, например, =C-n= - 108 | переместить курсор на следующую строчку (next line). Для менее частых, но тоже 109 | важных команд, как правило, используется префикс =C-x=, например, =C-x C-f= - 110 | открыть файл (find file). Для схожих по частоте и важности команд, определенных 111 | пользователем, используется префикс =C-c=, например, =C-c C-o= - открыть файл 112 | (или интернет-адрес), путь к которому находится под курсором. 113 | 114 | Теперь можно начинать! 115 | 116 | * Начало работы 117 | 118 | При первом запуске Emacs предложит установить недостающие /пакеты/. Чтобы 119 | согласиться на установку всего, что нужно, требуется нажать =!=. После этого 120 | откроется т.н. черновой /буфер/, в котором уже можно что-нибудь напечатать. 121 | 122 | Предлагаю попробовать набрать небольшой абзац - уверен, что всё получится без 123 | дополнительных объяснений. Большинство распространенных команд и сочетаний 124 | работают "как обычно". 125 | 126 | Единственное, возможно, кто-то привык использовать кнопки =Ctrl-x=, =Ctrl-c= и 127 | =Ctrl-v= при редактировании. В Emacs эти комбинации выполняют совсем другие 128 | функции. Подробнее я расскажу о том, как устроены копирование и вставка в Emacs 129 | позже, а первое время можно просто использовать следующие аналоги: 130 | 131 | - =C-w=, либо =S-= - /вырезать/ 132 | - =M-w=, либо =С-S-= - /копировать/ 133 | - =C-y=, либо =S-= - /вставить/ 134 | 135 | Комбинации, указанные вторыми, являются довольно традиционными и работают в 136 | большинстве современных редакторов. А комбинации, укзанные первыми, являются 137 | традиционными для Emacs. 138 | 139 | На первый взгляд они могут показаться довольно странными, например, =C-y= трудно 140 | нажать одной рукой, но если обе руки лежат на клавиатуре - то, в целом, они 141 | самые обыкновенные. А поскольку отучиться пользоваться мышкой - второй по 142 | важности пункт для улучшения качества работы (после слепой печати), то это даже 143 | играет на пользу. 144 | 145 | Теперь попробуем открыть какой-нибудь файл. Для этого используем комбинацию =C-x 146 | C-f=. Внизу, в т.н. /минибуфере/ появится имя текущей директории и начало списка 147 | находящихся в ней файлов. 148 | 149 | По мере набора имени файла будут оставаться только те варианты, которые 150 | соответствуют набранным символам. Например, можно набрать "rdme", и если в 151 | директории есть файл с именем =Readme.txt= (регистр не учитывается), то он 152 | останется в числе кандидатов. При наборе можно пропускать символы, но порядок 153 | должен оставаться тем же, что и в имени файла, т.е. если, набрать "drme", то 154 | =Readme.txt= уже пропадет из списка кандидатов. 155 | 156 | При открытии файла работают следующие команды: 157 | 158 | - =Enter= - открыть подсвеченный файл или зайти в директорию 159 | - =Backspace= - вверх на одну директорию 160 | - =C-s= - следующий кандидат в списке 161 | - =C-r= - предыдущий кандидат в списке 162 | - =C-f= - переход к "простому" вводу имени файла (в частности, 163 | необходим для создания новых файлов) 164 | - =~/= - домашняя директория 165 | - =/-<символ>= - корневая директория 166 | 167 | После внесения изменений, файл можно сохранить командой =C-x C-s= (save 168 | file). Сохранить его с другим именем можно командой =C-x C-w= (write file). 169 | 170 | Для простого поиска по файлу используются сочетания: 171 | 172 | - =C-s= и =C-r= - поиск вперед и назад, соответственно (повторные нажатия 173 | переводят курсор к очередному кандидату) 174 | - =C-g= или =ESC= - отмена поиска и возврат курсора в исходную позицию 175 | - =C-m= или =Enter= - выход из поиска 176 | 177 | Для выхода из Emacs используется сочетание =C-x C-c=. 178 | 179 | Если вдруг после каких-то нажатий произошло нечто страшное - стали происходить 180 | непонятные события, попытаться вернуть всё на свои места можно следующими 181 | способами: 182 | 183 | - Для отмены последних редактирований (т.н. undo) можно воспользоваться 184 | сочетанием =C-z= или равнозначным ему =C-/= (подробнее об отмене позже). 185 | - В остальных случаях можно попробовать нажать =C-g=, что для большинства команд 186 | означает "отмена" (либо "усиленный" вариант отмены - =ESC=). 187 | 188 | И последнее. При переключении языка ввода на русский, можно заметить, что 189 | практически все разобранные в этой секции комбинации перестают работать - внизу 190 | появляются сообщения вроде =C-ы is undefined=. В принципе, из этого сообщения 191 | можно понять, что происходит, но остается вопрос, что делать. Ответ прост - для 192 | переключения языка в Emacs нужно использовать комбинацию =C-\= - таким образом 193 | язык переключается не на уровне системы, а на уровне Emacs. Т.е. в Emacs 194 | попадают команды с латинскими буквами, но после того, как было нажато =C-\=, 195 | Emacs будет переводить символы латинского алфавита в соответствующие (в смысле 196 | раскладок QWERTY-ЙЦУКЕН) символы русского алфавита. 197 | 198 | Обогащенные этими знаниями должны быть в состоянии пользоваться Emacs в 199 | повседневной жизни вместо своего прошлого любимого текстового редактора, 200 | практически не изменяя старым привычкам. Дальше пойдут бонусы. 201 | 202 | * Минимальные требования 203 | 204 | Этот раздел написан для тех читателей, которые собираются всерьез подойти к 205 | изучению Emacs. В целом, без выполнения требований этого раздела можно сразу 206 | начать им пользоваться, как обычным текстовым редактором - большинство часто 207 | используемых команд можно найти в общепринятых или легко доступных местах. 208 | Наиболее значимые исключения составляют клавиши Ctrl-x, Ctrl-c и Ctrl-v, о чем 209 | было сказано в разделе "Начало работы". 210 | 211 | Тем же, кто полон решимости стать суровым профессионалом, а не жить бестолковым 212 | балбесом, позвольте выразить свое глубочайшее почтение и огласить весь список. 213 | 214 | ** Слепая печать 215 | 216 | Ты должен научиться печатать вслепую. Точка. Точнее, восклицательный знак. 217 | 218 | Переоценить важность этого навыка невозможно. Никакие программы, инструменты, 219 | авто-дополнения, подсказки, интуитивно понятные интерфейсы не улучшат качество 220 | твоей работы так, как слепая печать. 221 | 222 | Конечно, без неё можно прожить - но выглядеть это будет, как если бы ты ходил, 223 | привязав правую ногу к левой. 224 | 225 | Самый простой способ научиться - пройти курс [[http://ergosolo.ru/]["Соло на клавиатуре"]]. Первым делом 226 | нужно пройти английский курс, после этого, по желанию - русский. 227 | 228 | Если ты уже умеешь печатать вслепую - отлично. Если ты только начал учиться 229 | слепой печати, то, в принципе, можно начинать работать, только помни - даже если 230 | ты ещё не умеешь печатать не глядя, держи обе руки на клавиатуре в правильном 231 | положении. 232 | 233 | Если же ты не собираешься учиться печатать вслепую - можешь делать всё, что 234 | угодно. Ты - обречен. 235 | 236 | ** Caps Lock - третий Ctrl 237 | 238 | Нужно изменить конфигурацию клавиатуры, чтобы Caps Lock выступал в роли третьего 239 | Ctrl'а. С первого взгляда такой поворот событий может показаться диким, но это 240 | обязательно нужно сделать. 241 | 242 | Все современные операционные системы (кроме Windows, если там ничего не 243 | изменилось) имеют для этого соответствующую галочку в настройках клавиатуры. 244 | 245 | Счастливым обладателям ОС Windows придется несколько сложнее. В Windows 7 246 | работал следующий способ, который за годы, возможно, утратил свою актуальность: 247 | 248 | - Создать файл, с расширением =.reg=, например =capstoctrl.reg= 249 | - Открыть его при помощи блокнота (или Emacs'a, если он уже установлен) и 250 | заполнить его следующим содержимым: 251 | #+BEGIN_EXAMPLE 252 | Windows Registry Editor Version 5.00 253 | 254 | [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] 255 | "Scancode Map"=hex:00,00,00,00,00,00,00,00,02,00,00,00,1d,00,3a,00,00,00,00,00 256 | #+END_EXAMPLE 257 | - Щелкнуть по нему два раза мышью и согласиться на внесение изменений в реестр 258 | 259 | ** Переключение языка на Super(Win)-Space 260 | 261 | И последнее. Рекомендую привыкнуть переключать языки (с русского на английский и 262 | наоборот) сочетанием Super(Win)-Space, иначе говоря, "Кнопка Виндоуз"-Пробел. 263 | А другие возможности для переключения языка лучше отключить. 264 | 265 | Дело в том, что в Emacs время от времени придется использовать сочетания, 266 | предусматривающие одновременное нажатие Ctrl-Alt, Ctrl-Shift и Shift-Alt. И если 267 | какая-то из этих комбинаций также переключает язык - то время от времени он 268 | будет нечаянно переключаться. 269 | 270 | Насколько мне известно, "яблочные" системы используют аналогичное сочетание, с 271 | каких-то пор в Windows оно работает по умолчанию. Ubuntu тоже, кажется, следует 272 | моде. Нечасто в нашем деле можно встретить такое единение - предлагаю 273 | присоединиться. 274 | 275 | * Файловый менеджер 276 | 277 | Есть довольно известный в определенных кругах анекдот: "Из Emacs получилась бы 278 | отличная операционная система, если бы в нём был нормальный текстовый редактор". 279 | Я не буду подробно объяснять в чем же, собственно говоря, юмор. Вместо этого я 280 | расскажу о встроенном в Emacs файловом менеджере. 281 | 282 | Вообще, файловый менеджер - это самый первый инструмент для работы на 283 | компьютере, с которым я познакомился. В те далекие годы, когда я не знал, как 284 | написать даже самую простую программу, я, тем не менее, умел открыть голубой 285 | экран Norton Commander'a и стремительно носиться по файлам и папкам без 286 | использования мышки - в чем и была главная функция файлового менеджера. 287 | 288 | Для этих же целей в Emacs имеется свой собственный файловый менеджер - Dired 289 | (directory editor). Открыть его можно нажатием =C-x C-j= (dired jump), при этом 290 | мы окажемся в папке, в которой находится редактируемый в данный момент файл. 291 | Если нажать =C-x C-j=, уже находясь в dired, то это перебросит нас на директорию 292 | выше - гораздо более удобная альтернатива беготне до строки с двумя точками. 293 | 294 | Единственное, что стоит ещё отметить, это то, что Dired не обновляет своё 295 | содержимое автоматически. Т.е. если в какую-то директорию, открытую в Dired, 296 | скопировать файл или создать в ней новый файл, то отображаемое содержимое 297 | директории не изменится. Для того, чтобы обновить содержимое, используется 298 | кнопка =g=. 299 | 300 | На этом, признаться, я хочу закончить знакомство с dired, поскольку, на мой 301 | взгляд, дальнейшая работа с ним не вызовет трудностей даже у самого 302 | незамутненного пользователя. 303 | 304 | Однако, смею заверить, что это лишь вершина айсберга - возможности dired гораздо 305 | более широки. Dired - на удивление мощный, гибкий и гармоничный менеджер - 306 | особенно элегантный на фоне своих аналогов - Norton Commander'a, FAR'a, Total 307 | Commander'a и проч. Но разговор об этом я буду вести после того, как опишу 308 | другие базовые возможности Emacs. 309 | 310 | * Базовые операции 311 | 312 | Начнем привыкать к хорошему с базовых вещей. Во-первых, нужно забыть про 313 | стрелочки для перемещения курсора: 314 | 315 | - =C-n= - вниз (next line) 316 | - =C-p= - вверх (previous line) 317 | - =C-f= - вперед (forward char) 318 | - =C-b= - назад (backward char) 319 | 320 | Любое перемещение рук с их рабочего положения - к стрелочкам, 321 | PgUp-ам/PgDown-ам/Home-ам/End-ам - это работа от локтя, которая плохо 322 | автоматизируется и менее энергоэффективна, чем работа пальцами. Поэтому в первую 323 | очередь мы будем переучиваться использовать буквенные клавиши для выполнения 324 | частых операций. 325 | 326 | Вот эквиваленты других часто используемых команд: 327 | 328 | - =C-a= - в начало строки (=Home=) 329 | - =C-e= - в конец строки (=End=) 330 | - =C-v= - вниз на величину экрана (=PgDown=) 331 | - =M-v= - вверх на величину экрана (=PgUp=) 332 | - =M->= - в конец буфера 333 | - =M-<= - в начало буфера 334 | - =C-h= - удалить символ слева от курсора (=Backspace=) 335 | - =C-d= - удалить символ справа от курсора (=Delete=) 336 | - =С-j= - перевод строки 337 | 338 | Можно было бы сказать, что =С-j= - замена клавиши =Enter=, но это будет не 339 | совсем корректно. Если задуматься, то =Enter=, вообще говоря, выполняет 2 340 | функции - перевод строки и "ввод". Например, если ты набираешь строку поиска в 341 | Гугл, то, нажав =Enter=, ты выполняешь поиск, а не переводишь строку, т.е. в 342 | зависимости от ситуации, =Enter= ведет себя тем или иным образом. 343 | 344 | В Emacs эти две функции разнесены на разные кнопки. 95 процентов времени 345 | используется именно =C-j= - и для перевода строки, и как "ввод" - в тех случаях, 346 | когда это не вызывает двусмысленности. Одним из наиболее важных примеров, когда 347 | действия =C-j= и =C-m= отличаются, является процесс открытия файлов. Если после 348 | нажатия =C-x C-f= текущим выбором является директория (а не файл), то нажатие 349 | =C-m= приведет к тому, что мы продолжим выбор файлов, но уже внутри этой 350 | директории. А нажатие =C-j= приведет к тому, что текущая директория будет 351 | открыта в Dired. 352 | 353 | Отдельно хочу отметить замечательную комбинацию =C-l=. При первом нажатии, она 354 | устанавливает содержимое буфера так, чтобы курсор находился в самом центре 355 | окна. При повторном нажатии, содержимое меняется, чтобы курсор оказался в самом 356 | верху, а при третьем - в самом низу. Очень полезная и часто используемая 357 | функция. 358 | 359 | * Работа со словами и другими структурными единицами 360 | 361 | Работать в редакторе с отдельными символами примерно так же эффективно, как 362 | умножение заменять сложением. Как правило, человек не оперирует в голове 363 | отдельными символами, а оперирует структурными единицами - словами, 364 | предложениями, абзацами, что в языках программирования соответствует 365 | идентификаторам, выражениям и блокам (либо функциям). Поэтому гораздо удобнее 366 | пользоваться командами, которые оперируют с этими же структурными единицами. 367 | 368 | Если в посимвольных командах использовать клавишу =M-=, то это позволит 369 | оперировать более сложными единицами: 370 | 371 | - =M-f= - следующее слово (forward word) 372 | - =M-b= - предыдущее слово (backward word) 373 | - =M-a= - в начало предложения (выражения в языках программирования) 374 | - =M-e= - в конец предложения (выражения) 375 | - =M-h= - вырезать слово слева от курсора 376 | - =M-d= - вырезать слово справа от курсора 377 | 378 | Некоторые из этих команд могут быть ещё больше "усилены" добавлением 379 | =C-=. Например, для перемещения по сбалансированным скобкам, в Emacs 380 | используются команды: 381 | 382 | - =C-M-f= - следующее "скобочное выражение" (forward sexp) 383 | - =C-M-b= - предыдущее "скобочное выражение" (backward sexp) 384 | 385 | Эти функции работают почти также, как и функции "следующее слово" и "предыдущее 386 | слово", с тем исключением, что они расценивают выражение в круглых, фигурных или 387 | прямоугольных скобках, а также строки в кавычках, за одну единицу. Т.е. если 388 | перед курсором открывающаяся скобка и ты нажмешь =C-M-f=, то курсор переместится 389 | к закрывающей скобке. Вывести курсор за пределы скобок, в которых он находится, 390 | при помощи этих функций нельзя. 391 | 392 | - =С-M-a= - в начало абзаца (функции в языках программирования) 393 | - =С-M-e= - в конец абзаца (функции) 394 | 395 | Абзацами в тексте называются группы символов, разделенные пустой строкой. В 396 | языках программирования иногда тоже бывает удобно перемещаться по таким группам, 397 | для этого там используются сочетания: 398 | 399 | - =С-M-p= - предыдущая пустая строка (previous paragraph) 400 | - =С-M-n= - следующая пустая строка (next paragraph) 401 | 402 | Отмечу, что для обычного текста эти сочетания по функциональности ничем не 403 | отличаются от перемещения между абзацами. 404 | 405 | Таким образом, в Emacs выделяются следующие текстовые единицы: 406 | 407 | - символы и строки (префикс =С-=) 408 | - слова и предложения (префикс =M-=) 409 | - скобочные выражения (префикс =С-M-=) 410 | - абзацы (выражения и функции в языках программирования) (префикс =С-M-=) 411 | 412 | При разговоре о скобочных выражениях необходимо также отметить команду =C-S-h= 413 | (splice sexp). Она несколько выбивается из рассматриваемого ряда по 414 | функциональности (а потому и по форме "аккорда" - использованием Shift вместо 415 | Alt), но тем не менее слишком важна, чтобы не упомянуть её. Указанная комбинация 416 | удаляет обрамляющие символы скобочного выражения, внутри которого находится 417 | курсор. Т.е. если курсор находится внутри цитаты, заключенной в кавычки, то 418 | =C-S-h= удаляет обе - открывающую и закрывающую. Эта команда позволяет легко 419 | следить за тем, чтобы скобки (и кавычки) всегда были сбалансированы. 420 | 421 | В завершение этой секции, я приведу последнюю команду, которая используется для 422 | работы с языковыми единицами, и которую нечасто встретишь в других редакторах: 423 | 424 | - =C-t= - поменять буквы слева и справа от курсора местами (transpose chars) 425 | - =M-t= - поменять слова слева и справа от курсора местами (transpose words) 426 | 427 | Не сказать, что эти функции используются очень часто, но лично у меня на душе 428 | становится теплее, когда нет-нет да и получится их использовать. Кроме того, эти 429 | функции обладают интересным свойством, если, например, использовать =M-t= 430 | несколько раз подряд, то это будет иметь эффект, как будто ты "тащишь" слово 431 | вперед по тексту. 432 | 433 | * Окна и буферы 434 | ** Определения 435 | 436 | Современные приложения - браузеры, редакторы и т.п. - позволяют пользователю 437 | открыть несколько т.н. /вкладок/. Например, если ты гуляешь по интернету, то в 438 | браузере у тебя одновременно открыты ВКонтакте, Твиттер, Фейсбук, Ю-тюб и ещё 439 | много чего, чтобы ты, не дай Бог, не пропустил момент, когда кто-то пришлет тебе 440 | веселую картинку или ролик. 441 | 442 | В текстовых редакторах можно открыть сразу несколько файлов и переключаться 443 | между ними по мере необходимости - например, если ты выборочно копируешь текст 444 | из одного файла в другой. 445 | 446 | В Emacs таких вкладок нету, но дело ведь не во вкладках. Важно то, что они 447 | позволяют делать и как они позволяют организовать работу. Поэтому вместо них в 448 | Emacs предусмотрен другой механизм для схожей функциональности, который я сейчас 449 | опишу. Но прежде сделаю одну оговорку. 450 | 451 | Как и в приведенном примере, многие функции Emacs имеют более или менее 452 | устоявшиеся аналоги в других программах. И у значительной части людей эта 453 | непохожесть Emacs'a на то, что они видели ранее, вызывает, как минимум, вопросы, 454 | а у кого-то даже отторжение. 455 | 456 | Зачастую, причины, по которым в Emacs что-то сделано определенным образом, 457 | являются чисто историческими. Например, поскольку на UNIX-терминалах 1970-х 458 | годов не было ни мышек, ни даже графических интерфейсов, придумать и реализовать 459 | вкладки в их современном виде тогда не пришло бы никому в голову. 460 | 461 | Резонно заметить, что исторические причины едва ли являются хорошим обоснованием 462 | целесообразности того или иного решения. Но если исторически сложившееся 463 | решение, как минимум, предоставляет тот же функционал, то, на мой взгляд, если к 464 | этому добавить ещё и пройденное испытание временем, измена своим привычкам 465 | становится вполне оправданной. 466 | 467 | Этим я хочу сказать, что как только тебе в голову начнут залезать предательские 468 | мысли о том, что что-то в Emacs делается "не так, как должно бы" - гони их 469 | прочь. Скорее всего, в тебе просто говорит привычка и нежелание учиться и 470 | переучиваться. Практически во всех случаях после непродолжительного 471 | использования и размышления становится понятно, что предложенное решение 472 | является разумным, целостным, продуманным и вполне годным. 473 | 474 | Конечно, идеальных решений не существует и, поразмыслив над какой-то проблемой, 475 | возможно, ты только ещё больше убедишься в том, что решать её надо по-другому. В 476 | этом случае мой совет такой - если ты пользуешься Emacs'ом меньше полугода - 477 | просто прикуси губу и попытайся работать так, "как задумано композитором" (с) 478 | Chet Atkins. 479 | 480 | А если ты считаешь себя продвинутым пользователем Emacs, то это хороший повод 481 | для того, чтобы научиться настраивать его под свои нужды. Можно сказать, что 482 | Emacs расширяем до бесконечности - его всегда можно заставить вести себя в 483 | точности так, как ты хочешь. Во многом, именно эта особенность и обеспечила ему 484 | такую долгую и счастливую жизнь. Подробнее я расскажу о том, как это делается, 485 | ближе к концу обзора. 486 | 487 | А теперь вернемся к работе с окнами и буферами. 488 | 489 | В Emacs есть 3 основных понятия, связанные с организацией рабочего 490 | пространства - это /фрейм/ (frame), /окно/ (window) и /буфер/ (buffer). 491 | 492 | Рассмотрим диаграмму, на которой изображен пример рабочей сессии в Emacs. 493 | 494 | #+BEGIN_EXAMPLE 495 | +-------------------------------------------------------------------------------------+ 496 | | emacs@sergei-MS-7758 | 497 | +-------------------------------------------------------------------------------------+ 498 | | File Edit Options Buffers Tools Org Tbl Help | 499 | +------------------------------------------+------------------------------------------+ 500 | | #!/usr/bin/env rdmd | * Работа со словами и абзацами | 501 | | // Computes average line length for | | 502 | | // standard input. | Работать в редакторе с отдельными символа→ 503 | | import std.stdio; | эффективно, как умножение заменять сложен→ 504 | | | оперирует в голове отдельными символами, → 505 | | void main() { | единицами - словами, предложениями, абзац→ 506 | | ulong lines = 0; | программирования соответствует идентифика→ 507 | | double sumLength = 0; | (либо функциям). Поэтому гораздо удобнее → 508 | | foreach (line; stdin.byLine()) { | которые оперируют с этими же структурными→ 509 | | ++lines; | | 510 | | sumLength += line.length; | Если в посимвольных командах использовать→ 511 | | } | позволит оперировать более сложными едини→ 512 | | writeln("Average line length: ", | | 513 | | lines ? sumLength / lines : 0); | - =M-f= - следующее слово (forward word) → 514 | | } | - =M-b= - предыдущее слово (backward word→ 515 | | | - =M-a= - в начало предложения (выражения→ 516 | | | - =M-e= - в конец предложения (выражения)→ 517 | +------------------------------------------+------------------------------------------+ 518 | |1 U:--- lc.d All L12 (D/l hs Abbrev) |2:U:**- README.org 84% L472 | 519 | +------------------------------------------+------------------------------------------+ 520 | | * Установка... | 521 | | * Предисловие... | 522 | | * Минимальные требования | 523 | | | 524 | | Список того, что требуется от читателя - совсем небольшой - но очень | 525 | | важный: | 526 | | | 527 | | - Во-первых, ты должен научиться печатать вслепую. Переоценить | 528 | | важность этого навыка невозможно. Никакие программы, инструменты, | 529 | | авто-дополнения, подсказки, интуитивно понятные интерфейсы не улучшат | 530 | | | 531 | | Конечно же, в целом, без этого можно прожить - но выглядеть это будет | 532 | +-------------------------------------------------------------------------------------+ 533 | |3 U:**- README.org 2% L120 (Org Ind ARev) | 534 | +-------------------------------------------------------------------------------------+ 535 | | | 536 | +-------------------------------------------------------------------------------------+ 537 | #+END_EXAMPLE 538 | 539 | Всё, что изображено на приведенной диаграмме помещено в одном 540 | фрейме. Т.е. фрейм - это самая вместительная сущность в Emacs. Новый фрейм 541 | создается выполнением команды =emacs= в терминале. 542 | 543 | Внутри фрейма могут создаваться окна - контейнеры, отвечающие за его 544 | "геометрическую организацию". На приведенной диаграмме окна пронумерованы - их 545 | номера записаны в самом начале т.н. /строки состояния/ (modline) - =1 U:--- lc.d 546 | <...>=. 547 | 548 | В каждом окне отображен какой-либо буфер. О буфере можно упрощенно думать, как 549 | об открытом файле (в Emacs бывают не только файловые буферы, но в рамках данного 550 | вопроса они ничем существенным не отличаются). 551 | 552 | Ещё раз обращаю внимание, что окна - чисто геометрические сущности, а буферы 553 | наполняют их содержанием. 554 | 555 | Например, в первом окне отображен буфер, соответствующий файлу "lc.d", что 556 | отражено в строке состояния. А буфер, соответствующий файлу "README.org" 557 | отображен сразу в двух окнах - втором и третьем, причем отображают они разные 558 | части файла. Но поскольку это один и тот же буфер, его изменеие в одном окне 559 | влияет на содержимое другого. 560 | 561 | ** Список буферов 562 | 563 | Для того, чтобы создать буфер, нужно просто открыть файл. Как уже оговаривалось, 564 | сделать это можно командой =C-x C-f=. 565 | 566 | Для переключения между буферами используется комбинация =C-=, для закрытия 567 | буфера - =C-x k=. 568 | 569 | Если во время выбора буфера или файла ты вдруг передумал открывать что-либо, то 570 | можно нажать =C-g=. Повторюсь, что эта комбинация означает "отмена" не только в 571 | этом случае, но и для большинства нетривиальных команд Emacs. 572 | 573 | Открыв несколько файлов, можно получить список всех буферов с помощью клавиш 574 | =C-x C-b=, который выглядит примерно следующим образом: 575 | 576 | #+BEGIN_EXAMPLE 577 | MR Name Size Mode Filename/Process 578 | -- ---- ---- ---- ---------------- 579 | [ org ] 580 | README.org 36003 Org ~/.dev-setup/dot-emacs/README.org 581 | [ dired ] 582 | [ D ] 583 | [ C/C++ ] 584 | [ magit ] 585 | [ Markdown ] 586 | [ emacs ] 587 | * *Messages* 554 Fundamental 588 | [ shell commands ] 589 | [ Default ] 590 | * *shell* 25 Shell (shell run) ~/ 591 | .emacs 44231 Emacs-Lisp ~/.dev-setup/dot-emacs/.emacs 592 | *scratch* 0 Emacs-Lisp 593 | config 337 Conf[Space] ~/.ssh/config 594 | *% *Compile-Log* 102 Special 595 | 596 | 7 buffers 81252 4 files, 1 process 597 | #+END_EXAMPLE 598 | 599 | В этом списке можно навести курсор на строчку с именем буфера и нажать =Enter=, 600 | либо =C-m= для того, чтобы открыть соответствующий буфер. 601 | 602 | Разберем, что указано в столбцах этого списка. 603 | 604 | Расшифровка загадочного названия первого столбца - "Modified, Read-only". Если 605 | буфер имеет несохраненные изменения, то первый символ в этом столбце - "*". Если 606 | буфер нельзя редактировать, то второй символ в этом столбце - "%". 607 | 608 | Во втором столбце указано имя буфера, в третьем - размер содержимого буфера в 609 | байтах. 610 | 611 | В четвертом столбце указан основной /режим/ (mode) буфера. Существуют, например, 612 | режимы для редактирования файлов с программами на языках С++, D, Python и т.д.; 613 | есть режимы для редактирования HTML, LaTex; есть также специальные режимы, 614 | которые предназначены не для редактирования файлов, а для взаимодействия с 615 | другими программами, например, для просмотра директорий или выполнения команд в 616 | терминале. 617 | 618 | Основной режим определяет способы редактирования и отображения буфера. Например, 619 | в языке программирования С++ есть такие ключевые слова, как inline, const, 620 | class, struct и др. И если открыть файл с программой на С++, то эти слова 621 | выделятся специальным цветом. А в языке Python, например, слова inline, const и 622 | struct не являются ключевыми, в то время как слова class, in, elif и др. - 623 | являются. Для того, чтобы выделить ключевые слова корректно, буферы с файлами на 624 | языках C++ и Python будут открыты в разных режимах, каждый со своими 625 | представлениями о том, какие слова считать ключевыми. 626 | 627 | Как правило, режим, в котором открывается буфер определяется по расширению 628 | файла. Например, в приведенном списке буферов, файл "README.org" открыт в режиме 629 | Org, предназначенном для редактирования файлов с одноименной разметкой. 630 | 631 | В последнем столбце указан полный путь до файла либо имя процесса, с которым 632 | связан буфер. 633 | 634 | Также в списке буферов присутствуют горизонтальные разделители в квадратных 635 | скобках (например, "[С/C++]"), они объединяют файлы в группы по каким-то общим 636 | признакам. Состав групп и используемые признаки могут настраиваться, но мы не 637 | будем на этом сейчас останавливаться. 638 | 639 | ** Работа с окнами 640 | 641 | Нередко при работе требуется, чтобы перед глазами одновременно было несколько 642 | буферов или разные части одного и того же буфера. Для этого в Emacs и 643 | предназначены окна. 644 | 645 | Для работы с ними используются следующие команды: 646 | 647 | - Создание 648 | - =C-x 2= - разделить текущее окно по горизонтали 649 | - =C-x 3= - разделить текущее окно по вертикали 650 | - Уничтожение 651 | - =C-x 1= - уничтожить все окна, кроме текущего 652 | - =C-x 0= - уничтожить текущее окно 653 | - Переход между окнами 654 | - =M-1=, =M-2=, =M-3= и т.д. - переход в окно с указанным номером 655 | - =C-x o= - переход в следующее окно (other window) 656 | 657 | Пользуясь командами для создания и уничтожения, можно строить довольно 658 | замысловатые конструкции из окон. Однако, лично у меня 95 процентов времени 659 | открыто либо одно, либо два окна. 660 | 661 | Такой подход разительно отличается от того, что предлагают практически все 662 | современные "интегрированные среды разработки" (IDE). Рабочее пространство в 663 | них, как правило, ужасно захламлено. Одновременно там отображается редактор 664 | кода, дерево файловой системы, панели со всевозможными настройками, функциями и 665 | проч. Думаю, что во многом по этой причине, я практически не встречал людей, 666 | которые при работе в IDE открывают файлы одновременно в двух окнах, а 667 | предпочитают переключаться между вкладками. 668 | 669 | По моему же опыту, случаи, когда одновременно нужно смотреть сразу в три и более 670 | мест встречаются, но довольно редки. Поэтому все эти дополнительные панели 671 | просто создают бардак. Приятно посмотреть на рабочее место иного художника или 672 | архитектора, когда все инструменты аккуратно лежат на своих местах и находятся 673 | под рукой; когда на столе практически ничего нет и он предоставлен только листу 674 | бумаги. И, наоборот, берет оторопь, когда видишь "творческий беспорядок", 675 | заключающийся в том, что рабочий стол завален инструментами, лист положить 676 | просто негде, карандаши и бумага разбросаны по комнате, а художник грязными 677 | руками пытается изобразить шедевр, сидя на полу. 678 | 679 | На мой взгляд, рабочее место человека является прямым отражением того, что у 680 | него происходит в голове. И если рабочее место человека - это непонятная свалка, 681 | то и в голове у него точно такая же свалка. Нарисовать в таких условиях картину 682 | в стиле "героиновый сон" и сказать, что художник "так видит", наверное, 683 | можно. Но вот создать архитектурный проект "на века", наверное, уже нельзя. 684 | 685 | В этом свете очень кстати приходится то, что управление буферами и окнами в 686 | Emacs обеспечивается парой элементарных команд. Даже если у тебя есть склонность 687 | к плохой организации (у меня, например, эта склонность проступает очень даже 688 | выпукло), твоё рабочее пространство всё равно будет довольно аккуратным, потому 689 | что поддерживать порядок в Emacs проще, чем наводить беспорядок. 690 | 691 | В конце отмечу, что поскольку чаще всего одновременно я использую не больше двух 692 | окон, то переключаюсь между ними я при помощи комбинации =C-x o=, что позволяет 693 | не держать в голове номер текущего окна. Кроме того, при наличии двух окон, 694 | полезными оказываются следующие команды: 695 | 696 | - =C-c f= - поменять вертикальное разделение на горизонтальное и 697 | наоборот (flip windows) 698 | - =C-c s= - поменять местами буферы, отображаемые в окнах (swap 699 | buffers) 700 | 701 | ** Строка состояния 702 | 703 | Единственное, что осталось не до конца разобрано в этой секции - формат строки 704 | состояния. Она присутствует внизу каждого окна и, как следует из названия, 705 | содержит информацию о текущем состоянии окна. 706 | 707 | #+BEGIN_EXAMPLE 708 | 3 U:**- README.org 2% L120 (Org Ind ARev) 709 | #+END_EXAMPLE 710 | 711 | Разберем её слева направо. 712 | 713 | - =3= - номер окна 714 | - =U= - кодировка текущего буфера; в данном случае - UTF-8 715 | - =:= - разделитель 716 | - =**-= - 3 символа, описывающие состояние буфера; возможные значения: 717 | - первый символ: 718 | - =-= или =*= - буфер доступен для редактирования 719 | - =%= - буфер доступен только для чтения 720 | - второй символ: 721 | - =-= - все изменения буфера сохранены 722 | - =*= - в буфере есть несохраненные изменения 723 | - третий символ: 724 | - =-= - буфер является локальным, т.е. соответствует файлу или 725 | процессу на том же компьютере, на котором запущен Emacs 726 | - =@= - буфер является удаленным, т.е. соответствует файлу или 727 | процессу на удаленном сервере 728 | - =README.org= - имя буфера 729 | - =2%= - позиция окна в буфере; 2 процента означают, что отображаемый в окне 730 | текст находится почти в самом начале буфера; также вместо числа процентов 731 | может быть указано: =Top= - окно отображает самое начало буфера, =Bot= - окно 732 | отображает самый конец буфера, =All= - окно отображает буфер целиком 733 | - =L120= - символ =L= и номер строки, на которой находится курсор 734 | - =(Org Ind ARev)= - перечень режимов, работающих в этом буфере; 735 | первым всегда указан основной режим, после чего указан неполный перечень 736 | дополнительных режимов 737 | * Вырезать/Вставить 738 | ** Выделение регионов 739 | 740 | Важной функцией любого редактора является работа с областями текста, которые в 741 | Emacs называются /регионами/. Для выделения регионов многие люди используют 742 | мышку, более продвинутые - используют стрелочки с зажатой клавишей Shift. В 743 | Emacs оба эти способа тоже работают, однако считаются неоптимальными. 744 | 745 | Для выделения произвольного региона нужно нажать =C-SPC=, по-русски говоря, 746 | Ктрл-Пробел. После этого при изменении положения курсора начнет выделяться 747 | регион между текущим положением и положением, где находился курсор во время 748 | нажатия =C-SPC=. 749 | 750 | Для выделения всего буфера используется сочетание =C-x h= (mark whole buffer). 751 | 752 | Для снятия выделения используется сочетание =C-g=, которое, как говорилось в 753 | самом начале, для большинства команд обозначает "отмена". 754 | 755 | Кроме такого способа, Emacs также предлагает выделение структурных единиц при 756 | помощи комбинации =C-==. Разберем её работу на примере следующего отрывка: 757 | 758 | #+BEGIN_EXAMPLE 759 | "А смею спросить, - продолжал он, - зачем изволили вы перейти из гвардии в 760 | гарнизон?" Я отвечал, что такова была воля начальства. "Чаятельно, за 761 | неприличные гвардии офицеру поступки", - продолжал неутомимый 762 | вопрошатель. "Полно врать пустяки, - сказала ему капитанша, - ты видишь, 763 | молодой человек с дороги устал; ему не до тебя... (держи-ка руки 764 | прямее...). А ты, мой батюшка, - продолжала она, обращаясь ко мне, - не 765 | печалься, что тебя упекли в наше захолустье. Не ты первый, не ты 766 | последний. Стерпится, слюбится." (А.С. Пушкин, "Капитанская дочка") 767 | #+END_EXAMPLE 768 | 769 | Допустим, курсор находится в середине последнего слова - "дочка". При 770 | последовательных нажатиях =C-== будут выделены следующие регионы: 771 | 772 | - дочка 773 | - Капитанская дочка 774 | - "Капитанская дочка" 775 | - А.С. Пушкин, "Капитанская дочка" 776 | - (А.С. Пушкин, "Капитанская дочка") 777 | - При очередном нажатии отрывок будет выделен целиком. 778 | 779 | Т.е. =C-== осуществляет последовательное /расширение региона/ (expand 780 | region). Эта функция пытается увеличить выделенный регион, раздвигая его границы 781 | к началу и концу структурных единиц, вмещающих текущее выделение. В приведенном 782 | примере сначала выделяется слово, потом то, что находится внутри кавычек, потом 783 | захватываются сами кавычки, потом - внутренность скобок, потом - сами скобки и, 784 | наконец, весь фрагмент. 785 | 786 | Структурными единицами являются: 787 | 788 | - слова 789 | - внутренности скобок и кавычек 790 | - внутренности скобок и кавычек вместе с обрамляющими символами 791 | - абзацы 792 | - весь буфер 793 | 794 | Кроме того, в зависимости от основного режима, в буфере могут быть определены 795 | другие структурные единицы, например, выражения и функции в языках 796 | программирования. 797 | 798 | Интересным следствием правил расширения региона также является то, что если 799 | поставить курсор перед открывающейся скобкой или после закрывающейся 800 | (соответствующая пара скобок при этом подсветится) - при нажатии =C-==, скобки 801 | будут выделены вместе со всем содержимым. 802 | 803 | ** Базовые операции 804 | 805 | Итак, допустим регион выделен - но что же с ним делать? Список основных действий 806 | с регионами таков: 807 | 808 | 1) При нажатии печатных символов на клавиатуре регион будет удалён и набранные 809 | символы появятся на его месте. 810 | 2) При нажатии клавиш =C-d= или =C-h= и регион будет просто удален. 811 | 3) При нажатии на символы открывающихся скобок - "(", "{", "[", а также символ 812 | кавычки, регион будет /обернут/ (wrapped) - набранный символ вставится в 813 | начало региона, а соответствующий закрывающий символ - в конец. 814 | 4) При нажатии =M-w= регион будет скопирован. 815 | 5) При нажатии =C-w= регион будет вырезан. 816 | 817 | Список можно было бы назвать самым обычным, если бы не особенности копирования и 818 | вставки в Emacs. В англоязычной документации для этих действий даже специально 819 | употребляются слова kill/yank, вместо традиционных cut/paste. Я не придумывал 820 | специальные русские термины для того, чтобы подчеркнуть эту разницу, поэтому 821 | употребляю общеизвестные вырезать/вставить, хотя, наверное, это и не совсем 822 | корректно. 823 | 824 | Главное отличие рассматриваемых команд в Emacs от традиционных редакторов в том, 825 | что вырезаемые данные записываются в последовательность, называемую /кольцо 826 | вставки/ (kill ring). Т.е. в каждый момент времени у пользователя есть 827 | возможность вставить не только самый последний вырезанный регион, а также и 828 | любой другой, находящийся в кольце. 829 | 830 | Как говорилось ранее, вставка последнего вырезанного региона осуществляется с 831 | помощью =C-y=. Если следующей после нажатия =C-y= выполнить команду =M-y=, то 832 | только что вставленный регион будет заменен своим предшественником из кольца 833 | вставки. 834 | 835 | Я нахожу такой подход крайне полезным и удобным - можно не переживать, что 836 | вырезанные данные потеряются после следующего копирования. 837 | 838 | В некоторых ситуациях (в основном, когда нужно найти что-то вырезанное давно), 839 | удобно просмотреть содержимое кольца вставки. Для этого используется команда 840 | =C-x C-y=. После того, как требуемые регион найден, его можно вставить нажатием 841 | =C-m= (=Enter=). 842 | 843 | Поскольку хранить абсолютно все вырезанные регионы нецелесообразно (они могут 844 | занимать слишком много места), то выбранная структура хранения этих регионов - 845 | именно кольцо. По умолчанию, его размер - 60 регионов. Т.е. 60 первых вырезанных 846 | регионов будут записаны в кольцо друг за другом, а при вырезании следующего 847 | (61-го) региона, из кольца будет удален самый старый (1-ый) регион, а 61-ый 848 | будет записан вместо него и т.д. 849 | 850 | ** Дополнительные операции вырезания 851 | *** Вырезание строк 852 | 853 | В Emacs некоторые структурные единицы можно вырезать, предварительно не выделяя 854 | их в регион. Одной из главных таких единиц является строка. 855 | 856 | Вырезать текст от курсора до конца строки можно с помощью команды =C-k=. Обращаю 857 | внимание, что при этом символ переноса строки не удаляется. Для того, чтобы его 858 | удалить требуется повторно нажать =C-k=. Однако зачастую это не совсем приводит 859 | к желаемому результату. 860 | 861 | Допустим, мы редактируем следующий отрывок: 862 | 863 | #+BEGIN_SRC d 864 | if (supported) 865 | { 866 | performOperation(first_argument, 867 | second_argument); 868 | } 869 | #+END_SRC 870 | 871 | Мы хотим, чтобы круглые скобки находились на одной строке. Для этого, можно 872 | поставить курсор после запятой и нажать =C-k=. Поскольку мы уже находимся в 873 | конце строки, то будет удален (условно невидимый) символ перевода строки и 874 | отрывок примет новый вид: 875 | 876 | #+BEGIN_SRC d 877 | if (supported) 878 | { 879 | performOperation(first_argument, second_argument); 880 | } 881 | #+END_SRC 882 | 883 | Следующая строка (с текстом =second_argument);=) попала на текущую, но поскольку 884 | перед началом буквенных символов в этой строке присутствовал также отступ из 885 | пробельных символов, то и он благополучно попал на текущую строку. 886 | 887 | Для того, чтобы с ним расправиться можно нажать =M-\= (fixup whitespace) - эта 888 | команда превратит любое количество пробелов вокруг курсора в один. 889 | 890 | Но есть и другой способ - вместо нажатия =C-k=, можно нажать =M-j= (join 891 | following line). Эта команда как бы "подтягивает" текст следующей строчки на 892 | текущую, после чего отрывок выглядит так: 893 | 894 | #+BEGIN_SRC d 895 | if (supported) 896 | { 897 | performOperation(first_argument, second_argument); 898 | } 899 | #+END_SRC 900 | 901 | =M-j= - очень удобная команда - использовать её, кстати, можно не только когда 902 | курсор находится в конце строки (с тем же результатом), но я отвлекся от главной 903 | темы этого раздела - вырезания. 904 | 905 | Итак, команда =C-k= удаляет строку от курсора и до её конца, а если курсор уже 906 | находится в конце, то удаляется символ перевода строки. Поговорим ещё об 907 | интересных особенностях этой команды. 908 | 909 | Если её выполнить несколько раз подряд и потом осуществить вставку, то можно 910 | заметить, что вставлены будут все вырезанные строки, а не только самая 911 | последняя. Это обусловлено тем, что в Emacs действует следующее правило: если 912 | вырезающей команде предшествовала другая вырезающая команда, то вместо создания 913 | новой записи в кольце вставки, вырезанный регион приписывается к последней 914 | записи. 915 | 916 | Т.е. если 6 раз подряд нажать =C-k=, то будет вырезано три полных строки с 917 | символами перевода строк и при следующем нажатии =C-y=, будут вставлены все 3 918 | строки. 919 | 920 | *** Вырезание слов 921 | 922 | Внимательный читатель мог обратить внимание, что операции =M-d= и =M-h= не 923 | удаляют, а вырезают соответствующие слова. Впрочем, в Emacs вообще практически 924 | все операции, удаляющие текст длиннее одного символа, являются операциями 925 | вырезания, что позволяет "не терять" содержательные куски. Значит, после 926 | использования указанных команд слова можно вставить при помощи =C-y=. Кроме 927 | того, на них также распространяется описанное только что правило - 928 | последовательные исполнения этих команд складируют вырезанные слова в первом 929 | элементе кольца вставки. 930 | 931 | Пытливый читатель может заметить, что эта функциональность перекрывается с 932 | выделением регионов, и резонно задать вопрос - а что лучше/эффективнее 933 | использовать - =C-SPC=, =M-f=, =M-f=, =M-f=, =C-w= или =M-d=, =M-d=, =M-d= и 934 | почему вообще существует 2 способа сделать одно и то же? 935 | 936 | Причина здесь, как часто бывает, историческая. Мы все давно привыкли к 937 | использованию регионов, но было время, когда их использование не было так 938 | распространено. На старых терминалах у символов нельзя было изменять фон, а 939 | значит - нельзя было "подсветить" выделенный регион. Т.е. использовать регионы в 940 | Emacs можно было точно так же, как и сейчас, но увидеть выделенный регион было 941 | нельзя, что было несколько неудобно. Я предполагаю, что именно этот факт и 942 | явился главной причиной того, почему вырезание и вставка в Emacs работают так, 943 | как работают. Такой подход позволяет альтернативным способом визуализировать то, 944 | что происходит - вместо выделения региона, куски текста вырезались. Сегодня, 945 | когда вопрос о цвете фона символов стоит не так остро, наверное, проще всегда 946 | использовать регионы, если нужно вырезать больше одного слова (во всяком случае 947 | я делаю так в 95% случаев). 948 | 949 | Казалось бы, выделять регион можно и когда требуется вырезать всего одно слово, 950 | воспользовавшись комбинациями =C-==, =C-w=. Но, во-первых, всё-таки в голове это 951 | проходит по двум пунктам - "выделить слово и вырезать", вместо - "вырезать 952 | слово", во-вторых, это и две комбинации вместо одной, ну и, в-третьих, есть одно 953 | отличие в работе этих команд от =M-d= и =M-h=, которое позволяет им очень удачно 954 | дополнять друг друга. 955 | 956 | В программировании часто используется т.н. "верблюжий" стиль (camel case) 957 | именования функций, переменных и т.п. - разные слова в имени начинаются с 958 | заглавных букв, например - LongFunctionName или longFunctionName. 959 | 960 | Так вот, комбинация =C-==, =C-w= вырезает всё имя функции целиком, а команды 961 | =M-d= и =M-h= вырезают "подслова" в имени. 962 | 963 | *** Вырезание до символа 964 | 965 | Как уже было сказано, чаще всего для вырезания лично я использую выделение 966 | региона и в сегодняшних реалиях наличие большого количества специальных команд в 967 | Emacs на этот счет, наверное, несколько утратило актуальность. 968 | 969 | Но тем не менее, я всё-таки хочу рассказать о последней специальной команде, 970 | которая довольно часто пригождается - =M-z= (zap to char). 971 | 972 | Допустим, курсор находится в середине предложения и мы хотим вырезать все 973 | символы до его конца, начиная с позиции курсора. Для этого нужно нажать =M-z=, 974 | после чего будет предложено ввести символ, до которого нужно осуществлять 975 | вырезание. В нашем случае это точка. После её нажатия, будут вырезаны все 976 | символы между текущим положением курсора и ближайшей точкой, включая её. 977 | 978 | Для того, чтобы оставить точку, можно воспользоваться командой =M-Z= (zap up to 979 | char), которая во всём эквивалента =M-z=, кроме того, что не вырезает указанный 980 | символ. 981 | 982 | * Префиксный аргумент 983 | 984 | Сейчас я хочу коротко рассмотреть, наверное, не самый жизненно важный вопрос, но 985 | вряд ли для него найдется лучшее место. 986 | 987 | ** Численный аргумент 988 | 989 | Начну с того, на чем закончился предыдущий раздел - команды =M-z=, которая 990 | вырезает все символы, начиная с текущей позиции курсора до первого появления 991 | укзанного символа (включая сам символ). 992 | 993 | Допустим, мы с её помощью хотим удалить деепричастный оборот в предложении 994 | "Убедившись, что понять этого он не может, ему стало скучно (Л. Толстой)". 995 | Деепричастный оборот расположен в самом начале предложения и заканичается 996 | запятой после слова "может". Соответственно, для того, чтобы его удалить, можно 997 | расположить курсор в начале предложения, нажать =M-z=, запятую - таким образом 998 | вырежется текст до первой запятой ("Убедившись,") - а потом опять =M-z= и 999 | запятую - чтобы вырезать весь необходимый текст. Т.е. для достижения цели нам 1000 | пришлось 2 раза подряд выполнить одну и ту же команду. 1001 | 1002 | В таких ситуациях удобно передать исполняемой команде /префиксный аргумент/. В 1003 | рассмотреном примере вместо того, чтобы два раза выполнить одну и ту же команду, 1004 | можно выполнить =C-2 M-z= и нажать запятую, для достижения того же результата. 1005 | 1006 | Т.е. любой команде в Emacs можно передать префиксный аргумент нажатием =C-= 1007 | перед самой командой. В подавляющем большинстве случаев это будет сигналом к 1008 | тому, что указанную команду нужно выполнить == раз. 1009 | 1010 | Один пример использования префиксного аргумента (вкупе с командой =M-z=) уже был 1011 | рассмотрен. Вот другие примеры: 1012 | 1013 | - =C-3 M-Z= - вырезать текст до третьего появления указанного символа (исключая 1014 | сам символ) 1015 | - =С-8 0 /= или =С-8 С-0 /= - вставить 80 символов '/' 1016 | - =C-5 C-n= - спустить курсор на 5 строчек вниз 1017 | - =C-6 C-k= - вырезать 6 строк 1018 | 1019 | Последний пример требует определенного пояснения. Если 6 раз подряд выполнить 1020 | команду =C-k=, то вырезаны будут всего 3 строчки, потому что первое нажатие 1021 | вырежет текст до конца строки; второе - символ конца строки; третье, по аналогии 1022 | с первым - текст до конца строки и т.д. Однако, если нажать =C-6 C-k=, то 1023 | вырезаны будут именно 6 строк. 1024 | 1025 | Связано это с тем, что, вообще говоря, правило о том, что префиксный аргумент 1026 | означает количество раз, которое нужно повторить следующую команду не является 1027 | строгим. Т.е. этот функционал реализован не на уровне Emacs, а на уровне самих 1028 | функций. И описанное правило является всего лишь соглашением, которому должны 1029 | следовать "правильные" функции. 1030 | 1031 | В случае с функцией вырезания строки, она позволяет себе некоторую вольность - 1032 | вырезать именно столько строк, сколько указано префиксным аргументом, а не 1033 | делить его пополам. И в данном случае эта вольность является вполне уместной, 1034 | потому что функция ведет себя вполне ожидаемо и адекватно. 1035 | 1036 | Для некоторых команд предписание "выполнись N раз подряд" не имеет особого 1037 | смысла. Например, функция =C-l= располагает содержимое буфера так, чтобы курсор 1038 | оказался в центре окна, последовательное нажатие располагает содержимое, чтобы 1039 | курсор оказался вверху, потом - внизу, а потом опять в центре - и так по кругу. 1040 | Особого смысла предоставлять возможность выполнить эту команду произвольное 1041 | число раз подряд нету, поскольку, фактически, у неё всего 3 возможных исхода. 1042 | 1043 | В таких случаях авторы функций, как правило, стараются подобрать для префиксного 1044 | аргумента какое-нибудь более или менее осмысленное значение. Функция =C-l=, 1045 | например, если ей передать N в качестве аргумента, расположит содержимое буфера 1046 | так, чтобы курсор находился на (N+1)-ой строчке окна. 1047 | 1048 | ** Отрицательный аргумент 1049 | 1050 | Будучи математиком по образованию и профессии, могу предположить, что 1051 | разработчиками Emacs двигала та же мотивация, что и Диофантом, когда они стали 1052 | использовать отрицательные префиксные аргументы. Объяснить с бытовой точки 1053 | зрения, что значит выполнить команду минус 3 раза не очень просто, но слишком уж 1054 | гармонично всё выстраивается, если наплевать на эту бытовую точку зрения! 1055 | 1056 | К примеру, если выполнить =C-- 3 C-n=, то курсор переместится на три строки 1057 | вверх, несмотря на то, что мы выполнили команду перевода курсора вниз. В погоне 1058 | за красивыми математическими параллелями можно даже отметить, что после 1059 | выполнения, команды курсор попадает в такую точку, что если в ней выполнить =C-3 1060 | C-n=, то он окажется в исходном положении! 1061 | 1062 | Аналогично, если выполнить =C-- M-z= (минус в качестве префиксного аргумента 1063 | эквивалентен минус единице), то будет вырезан кусок текста с текущего положения 1064 | до ближайшего указанного символа *перед* курсором. 1065 | 1066 | Как и в случае с положительными префиксными аргументами, какого-то строгого 1067 | правила, что делать с отрицательными аргументами нет. Но негласная 1068 | договорённость состоит в том, что они должны модифицировать выполнение команды 1069 | точно так же, как и положительные, только наоборот. 1070 | 1071 | ** Универсальный аргумент 1072 | 1073 | Помимо численных префиксных аргументов, функциям можно передать т.н. 1074 | /универсальный аргумент/ при помощи префикса =C-u=. 1075 | 1076 | Если численный аргумент чаще всего означает "выполнить следующую команду N раз", 1077 | то универсальный аргумент имеет несколько значений. 1078 | 1079 | Во-первых, если команда не обрабатывает его особенно, то он соответствует 1080 | численному аргументу, равному 4. Почему именно четырем сказать трудно, но с 1081 | другой стороны, а почему бы и нет?! Например, =C-u C-m= - вставить 4 пустых 1082 | строки, =C-u C-u C-m= - вставить 16 пустых строк, =C-u C-u C-u C-m= - вставить 1083 | 64 пустых строки и т.д. Естественно, точные значения, получаемые при помощи 1084 | универсального аргумента (степени четверки) не очень полезны, но можно 1085 | относиться к этим величинам, как к качественным: 1086 | 1087 | - один аргумент - "повтори несколько раз", 1088 | - два аргумента - "повтори с дюжину раз", 1089 | - три аргумента - "повтори с полсотни раз", 1090 | - четыре аргумента - "повтори с сотню раз" и т.д. 1091 | 1092 | Во-вторых, универсальный аргумент может сигнализировать команде "выполнись, но 1093 | не как обычно". Что именно значит "не как обычно" каждая команда решает 1094 | по-своему. В качестве примера, рассмотрим команду =C-y=, ради чего я и завел 1095 | разговор о префиксных аргументах именно сейчас. 1096 | 1097 | Допустим, у нас есть следующий кусок кода: 1098 | 1099 | #+BEGIN_SRC d 1100 | int fun() 1101 | { 1102 | if (supported) 1103 | { 1104 | performFirstOperation(first_argument, second_argument); 1105 | } 1106 | 1107 | performSecondOperation(); 1108 | performThirdOperation(); 1109 | } 1110 | #+END_SRC 1111 | 1112 | И мы решили, что и вторую и третью операцию нужно выполнять, только при условии 1113 | =supported=. Т.е. мы хотим преобразовать этот код в следующий: 1114 | 1115 | #+BEGIN_SRC d 1116 | int fun() 1117 | { 1118 | if (supported) 1119 | { 1120 | performFirstOperation(first_argument, second_argument); 1121 | performSecondOperation(); 1122 | performThirdOperation(); 1123 | } 1124 | } 1125 | #+END_SRC 1126 | 1127 | Очевидное решение было бы следующим - вырезать строчки 1128 | 1129 | #+BEGIN_SRC d 1130 | performSecondOperation(); 1131 | performThirdOperation(); 1132 | #+END_SRC 1133 | 1134 | и вставить их куда надо - что может быть проще?! Однако, тут имеется тонкий 1135 | момент. В какое положение нужно поставить курсор для выделения региона и в какое 1136 | положение его нужно поставить для вставки? 1137 | 1138 | Допустим, мы поставим курсор перед первой буквой "p" и вставим ровно в том 1139 | положении, где она должна оказаться. Тогда (в "обычном" редакторе) мы получим 1140 | следующую картину: 1141 | 1142 | #+BEGIN_SRC d 1143 | int fun() 1144 | { 1145 | if (supported) 1146 | { 1147 | performFirstOperation(first_argument, second_argument); 1148 | performSecondOperation(); 1149 | performThirdOperation(); 1150 | } 1151 | } 1152 | #+END_SRC 1153 | 1154 | Другой вариант - поставим курсор в начале строки с вызовом функции 1155 | =performFirstOperation()=, а при вставке - в начало строки, где мы хотим 1156 | расположить вырезанный кусок, тогда получим: 1157 | 1158 | #+BEGIN_SRC d 1159 | int fun() 1160 | { 1161 | if (supported) 1162 | { 1163 | performFirstOperation(first_argument, second_argument); 1164 | performSecondOperation(); 1165 | performThirdOperation(); 1166 | } 1167 | } 1168 | #+END_SRC 1169 | 1170 | И, вообще говоря, как бы мы ни старались - в "обычном" редакторе вставленный 1171 | текст всегда будет выровнен неправильно. А значит, после вставки его придется 1172 | ещё и форматировать. 1173 | 1174 | В Emacs же эта проблема не стоит. Вставленный текст будет выровнен 1175 | автоматически. Т.е. где бы ты ни расположил курсор для вырезания (в начале 1176 | строки или начале текста) и где бы ты ни расположил курсор для вставки - 1177 | результат будет выглядеть "как надо". 1178 | 1179 | Это настолько удобная и сама собой разумеющаяся функция, что я привык к ней ещё 1180 | до того, как начал пользоваться, и постоянно ругался вслух, когда приходилось 1181 | выравнивать только что вставленный текст. 1182 | 1183 | Но в редких случаях, такое выравнивание будет играть не на руку - регион нужно 1184 | просто вставить "как есть". Тогда можно передать команде вставки универсальный 1185 | аргумент - =C-u C-y= - и текст будет вставлен без автоматического выравнивания. 1186 | 1187 | * Откат 1188 | 1189 | Как известно, не ошибается тот, кто ничего не делает, поэтому Emacs 1190 | предоставляет возможность откатить последние действия. Для этого можно 1191 | использовать сочетание =C-z= (как и в других редакторах), либо =С-/=. Лично я 1192 | использую =C-/=, потому что его удобнее нажимать. 1193 | 1194 | В целом, эта тема не стоила бы отдельного раздела, если бы мы не ошибались во 1195 | время исправления наших ошибок - жмешь =C-/= столько раз, сколько нужно, пока 1196 | все ошибки не исчезнут. Но что делать, если ты нажал =C-/= лишнего и теперь тебе 1197 | нужно вернуть всё, как было перед последним нажатием? Для ответа на этот простой 1198 | вопрос придется довольно подробно разобраться, как работает система откатов в 1199 | Emacs. 1200 | 1201 | Рассмотрим такой пример: первой командой мы ввели слово "Береги ", второй - 1202 | "деньги ", третьей - "смолоду". Не очень важно, как мы это делали - мы могли как 1203 | набирать слова по отдельным символам, так и вставлять их различными способами 1204 | (не обо всех из которых я успел сказать) - главное, что мы выполнили три команды 1205 | вставки. 1206 | 1207 | Проиллюстрируем это следующей диаграммой: 1208 | 1209 | #+BEGIN_EXAMPLE 1210 | o (пустой буфер) 1211 | | 1212 | | 1213 | o Береги (вставка) 1214 | | 1215 | | 1216 | o Береги деньги (вставка) 1217 | | 1218 | | 1219 | x Береги деньги смолоду (вставка) 1220 | #+END_EXAMPLE 1221 | 1222 | Теперь, допустим, мы пересмотрели свои ценности и захотели исправить ошибку во 1223 | втором слове. Для этого выполняем два раза откат и приходим к следующей картине: 1224 | 1225 | #+BEGIN_EXAMPLE 1226 | o (пустой буфер) 1227 | | 1228 | | 1229 | x Береги (вставка) 1230 | | 1231 | | 1232 | o 1233 | | 1234 | | 1235 | o 1236 | #+END_EXAMPLE 1237 | 1238 | Однако, эта схема не совсем верна с точки зрения внутреннего устройства Emacs, 1239 | поскольку он считает произведенный откат за очередные изменения буфера: 1240 | 1241 | #+BEGIN_EXAMPLE 1242 | o (пустой буфер) 1243 | | 1244 | | 1245 | o Береги (вставка) 1246 | | 1247 | | 1248 | o Береги деньги (вставка) 1249 | | 1250 | | 1251 | o Береги деньги смолоду (вставка) 1252 | | 1253 | | 1254 | o Береги деньги (откат) 1255 | | 1256 | | 1257 | x Береги (откат) 1258 | #+END_EXAMPLE 1259 | 1260 | Но тем не менее, мы ведь всё-таки выполняем откат, поэтому правильнее было бы, 1261 | изобразить эту же схему несколько иначе: 1262 | 1263 | #+BEGIN_EXAMPLE 1264 | (пустой буфер) o 1265 | | 1266 | | 1267 | Береги (вставка) o x (откат) 1268 | | | 1269 | | | 1270 | Береги деньги (вставка) o o (откат) 1271 | | / 1272 | |/ 1273 | Береги деньги смолоду (вставка) o 1274 | #+END_EXAMPLE 1275 | 1276 | С первого взгляда может показаться, что относиться к откату, как к новым 1277 | изменениям буфера - странная затея. Но попробуем рассмотреть ситуацию, когда мы 1278 | вносим изменения в буфер после выполнения отката. 1279 | 1280 | В случае "обычного" редактора, который позволяет двигаться по истории только 1281 | вперед или назад, история выглядела бы следующим образом: 1282 | 1283 | #+BEGIN_EXAMPLE 1284 | (пустой буфер) o 1285 | | 1286 | | 1287 | Береги (вставка) o 1288 | .\ 1289 | . \ 1290 | Береги деньги (вставка) o o Береги честь (вставка) 1291 | . 1292 | . 1293 | Береги деньги смолоду (вставка) o 1294 | #+END_EXAMPLE 1295 | 1296 | Обращаю внимание, что оригинальная ветвь канула в лету и вернуть её из истории 1297 | уже не получится. В Emacs же, напротив, все узлы остаются в истории: 1298 | 1299 | #+BEGIN_EXAMPLE 1300 | (пустой буфер) o 1301 | | 1302 | | 1303 | Береги (вставка) o o (откат) 1304 | | |\ 1305 | | | \ 1306 | Береги деньги (вставка) o o | (откат) 1307 | | / | 1308 | |/ | 1309 | Береги деньги смолоду (вставка) o | 1310 | | 1311 | | 1312 | x Береги честь (вставка) 1313 | #+END_EXAMPLE 1314 | 1315 | Единственный вопрос, что нужно сделать, если мы пересмотрели свои ценности 1316 | второй раз и захотели вернуть первоначальный вариант пословицы? Ничего 1317 | особенного - точно так же жмем =C-/=. В результате чего получаем: 1318 | 1319 | #+BEGIN_EXAMPLE 1320 | (пустой буфер) o 1321 | | 1322 | | 1323 | Береги (вставка) o o (откат) o (откат) 1324 | | |\ /| 1325 | | | \ / | 1326 | Береги деньги (вставка) o o | / o (откат) 1327 | | / | / | 1328 | |/ | / | 1329 | Береги деньги смолоду (вставка) o | / x 1330 | | / 1331 | |/ 1332 | o Береги честь (вставка) 1333 | #+END_EXAMPLE 1334 | 1335 | Как, надеюсь, видно из рисунка, буфер можно вернуть в любое состояние, нажав 1336 | =C-/= достаточное количество раз. 1337 | 1338 | На мой взгляд, описанная система довольно проста и прозрачна. Рассмотрим только 1339 | единственный тонкий момент. Вспомним, когда история выглядела следующим образом: 1340 | 1341 | #+BEGIN_EXAMPLE 1342 | (пустой буфер) o 1343 | | 1344 | | 1345 | Береги (вставка) o x (откат) 1346 | | | 1347 | | | 1348 | Береги деньги (вставка) o o (откат) 1349 | | / 1350 | |/ 1351 | Береги деньги смолоду (вставка) o 1352 | #+END_EXAMPLE 1353 | 1354 | Если теперь нажать =C-/= (предполагая, что мы уже два раза нажали =C-/=, для 1355 | выполнения откатов и больше ничего не делали), то буфер перейдет в начальное 1356 | состояние: 1357 | 1358 | #+BEGIN_EXAMPLE 1359 | (пустой буфер) o x (откат) 1360 | | | 1361 | | | 1362 | Береги (вставка) o o (откат) 1363 | | | 1364 | | | 1365 | Береги деньги (вставка) o o (откат) 1366 | | / 1367 | |/ 1368 | Береги деньги смолоду (вставка) o 1369 | #+END_EXAMPLE 1370 | 1371 | Но, что если мы хотим выполнить не очередной откат, а пойти по истории в 1372 | обратную сторону? Т.е. как-то сигнализировать, что мы хотим изменить 1373 | направление, в котором мы шагаем по истории. В прошлый раз мы вставили слово 1374 | "честь" и после этого очередные нажатия =C-/= вели нас в прошлое. 1375 | 1376 | Правило заключается в следующем: любая команда, не являющаяся откатом, выступает 1377 | в роли такой "поворотной точки". Пока мы жмем =C-/= - мы наращиваем точки 1378 | "отката" в истории. А как только мы сделали что-то другое, то следующие нажатия 1379 | =C-/= поведут нас в прошлое уже от наращённых точек. 1380 | 1381 | Вся эта великая теория может показаться довольно запутанной, что вдвойне 1382 | печально, учитывая, что построена она ради такой, казалось бы, простой функции, 1383 | как откат. Однако, не стоит отчаиваться, если ты не понял ни единого слова или 1384 | даже не хочешь вникать. Из всей этой теории следуют два очень простых 1385 | практических совета. И, в сущности, запомнить можно только их: 1386 | 1387 | 1. Для того, чтобы вернуться к предыдущему состоянию буфера нужно жать =C-/= до 1388 | наступления желаемого эффекта. 1389 | 2. Если ты "промотал" лишку, то нужно нажать =C-g= - для смены направления 1390 | движения по истории - и опять жать =C-/=. 1391 | 1392 | * Навигация и поиск 1393 | ** Навигация 1394 | 1395 | В самом начале я уже рассказал о том, что поиск в буфере можно осуществлять с 1396 | помощью комбинаций =C-s= и =C-r=. Сейчас я хочу поподробнее рассказать о них, о 1397 | поиске вообще и не только. 1398 | 1399 | Вопрос, который мог бы возникнуть при знакомстве с этими функциями - почему им 1400 | отданы такие короткие и замечательные комбинации? =C-s= даже двигает с 1401 | насиженного места функцию сохранения, претендуя на большую популярность, что 1402 | несколько удивительно. Ведь поиск, как правило, довольно тяжеловесная процедура 1403 | в других редакторах. Для ввода строки открывается отдельное окошко, в нем 1404 | имеется ряд галочек и кнопочек, поэтому лишний раз пользоваться им не будешь. 1405 | 1406 | В Emacs же, поиск, наоборот, ненавязчив - комбинации находятся под рукой, 1407 | переходы к найденным словам происходят быстро, редактировать можно 1408 | сразу. Никакие лишние элементы управления не появляются - подходящий текст и 1409 | текущий кандидат выделяются заметно, но опять же, ненавязчиво. Всё это как бы 1410 | приглашает пользоваться поиском чаще, но с первого взгляда не очень очевидно 1411 | зачем. 1412 | 1413 | Ответ, однако же, довольно прост - поиск в Emacs используется, в частности, и 1414 | для навигации по буферу. Наблюдательный читатель уже мог заметить, что Emacs 1415 | предоставляет широкие возможности "отрезать столько, сколько надо" - можно 1416 | работать с символами, со словами, с абзацами, с блоками, с регионами и т.д. 1417 | 1418 | Точно так же и здесь - с помощью поиска в Emacs можно прыгнуть ровно в то место, 1419 | в которое нужно. Допустим, например, что курсор находится в нижней части 1420 | буфера. Чтобы прыгнуть ближе к верхнему краю - ты можешь промотать экран с 1421 | помощью =M-v=, добежать до нужной строчки с помощью =C-n=, =C-p= или, двигаясь 1422 | по абзацам, с помощью =C-M-n=, =C-M-p=. 1423 | 1424 | А можешь нажать =C-s= или =C-r= и начать набирать то слово, к которому ты хочешь 1425 | переместить курсор - пары символов для этого, как правило, достаточно. Процесс 1426 | перехода при этом значительно упрощается. 1427 | 1428 | Кроме того, после выхода из поиска, в месте его старта Emacs оставляет /маркер/ 1429 | (=mark=) и если после редактирования в какой-то момент ты захочешь "вернуться 1430 | обратно", то комбинация =C-M-\= возвратит тебя к этому маркеру. Последующие 1431 | нажатия =C-M-\= будут возвращать тебя ко всё более ранним маркерам. Таким 1432 | способом можно пробежать все позиции в буфере, в которых ты уже 1433 | был. (Справедливости ради нужно заметить, что по умолчанию Emacs хранит не все, 1434 | а только 16 последних маркеров). 1435 | 1436 | Кстати, выставляются маркеры не только командой поиска, но и многими другими 1437 | командами, которые потенциально перемещают курсор на большие расстояния, 1438 | например, переходы в начало и конец буфера (=M-<= и =M->=), переход к строке с 1439 | заданным номером (=M-g M-g=), а также командой =C-SPC=. 1440 | 1441 | Сама по себе, команда =C-M-\=, выполняет переходы между маркерами только в 1442 | пределах одного буфера, но её можно использовать и для перехода между маркерами 1443 | во всех буферах. Для этого ей нужно всего лишь передать универсальный аргумент 1444 | =C-u C-M-\=. 1445 | 1446 | ** Поиск в буфере 1447 | 1448 | Использование поиска для навигации по буферу, возможно, интересно и свежо. Но, 1449 | наверное, неплохо бы было рассказать подробнее и о собственно поиске, потому что 1450 | пока я затронул только самые базовые его возможности. 1451 | 1452 | Итак, поиск по буферу можно осуществлять при помощи комбинаций =C-s= и =C-r= для 1453 | поиска "вперед" и "назад", соответственно. Одно приятное дополнение к простому 1454 | поиску состоит в том, что если перед его началом выделить слово (или любой 1455 | другой регион, умещающийся на одной строке), то оно будет использовано в 1456 | качестве искомой строки. 1457 | 1458 | Кроме того, Emacs хранит историю поисковых кандидатов и с помощью нажатия =M-n= 1459 | и =M-p= во время поиска можно их найти. К слову сказать, =M-n= и =M-p= - 1460 | практически всегда выполняют функцию выбора кандидатов из истории, когда это 1461 | уместно. 1462 | 1463 | Следующий момент касается чувствительности поиска к регистру. По умолчанию, 1464 | поиск не различает большие и маленькие буквы и есть по крайней мере 2 способа 1465 | это изменить. Наиболее удобный - если в искомой строке присутствует хотя бы одна 1466 | заглавная буква - он автоматически становится чувствительным к регистру. Этот 1467 | способ будет хорошо работать всегда, когда в искомой строке есть заглавные 1468 | буквы. Если же их нет, то можно явно включить чувствительность с помощью 1469 | комбинации =M-s c= (case sensitivity). 1470 | 1471 | Другая возможность поиска - показать список всех кандидатов сразу. Сделать это 1472 | можно, нажав =M-s o= (occurrences) и введя искомое выражение. Та же самая 1473 | комбинация сработает, если её нажать во время поиска. Тогда она уже не будет 1474 | явно спрашивать строку, а использует текущую. 1475 | 1476 | Если функции =M-s o= передать численный аргумент, то каждый кандидат появится не 1477 | просто со строкой, в которой он находится, но и со строками, находящимися до и 1478 | после нее. Количество дополнительных строк определяется численным аргументом. 1479 | 1480 | В открывшемся буфере можно будет при помощи комбинации =C-m= (или =Enter=) 1481 | переходить к соответствующей строке в исходном буфере. Кроме того, список 1482 | кандидатов можно дополнительно сузить, выполнив =M-s o= в буфере со списком ещё 1483 | раз. Получается довольно элегантно, не так ли?! А всё потому, что в Emacs 1484 | действует негласное правило - относиться ко всем буферам, как к обычным 1485 | текстовым файлам. Но то ли ещё будет! 1486 | 1487 | Комбинация =C-x C-q= в буфере со списком кандидатов переведет его в режим 1488 | редактирования. Т.е. текст в нем можно будет править обычными средствами, а 1489 | изменения будут перенесены в исходный буфер (при помощи той же комбинации =C-x 1490 | C-q=). Нужно ли говорить, что это крайне удобная функция, которая позволяет 1491 | эффективно редактировать разные частей буфера одновременно. 1492 | 1493 | ** Поиск во множестве файлов 1494 | 1495 | Уже описанных возможностей вполне хватает для большинства бытовых поисковых нужд 1496 | при редактировании. Но, как можно заметить, они осуществляют поиск лишь в одном 1497 | файле. Если же требуется найти что-то в большем количестве файлов, то для этого 1498 | можно использовать известную утилиту =grep=. 1499 | 1500 | Насколько мне известно, в операционных системах Windows эта утилита не 1501 | установлена по умолчанию, т.е. её требуется установить отдельно (а также команду 1502 | =find=, которая идет с =grep= рука об руку). Конечно, это не очень приятно, но я 1503 | решительно убежден, что операционные системы без утилиты =grep= не имеют права 1504 | на жизнь, и поэтому оставляю за собой право игнорировать их существование. В 1505 | поддержку своей позиции замечу, что Оксфордский Словарь Английского Языка 1506 | содержит определение слова =grep= и как существительного, и как глагола с 1507 | 2003-го года. А слово =google=, например, появилось в нем тремя годами 1508 | позже. Отсюда можно сделать осторожный вывод, что система, не умеющая грепать, 1509 | более бесполезна, чем система, не умеющая гуглить. 1510 | 1511 | Отложив в сторону лингво-философские вопросы, перейдем, наконец, к поиску в 1512 | нескольких файлах. В Emacs для этого используется комбинация =C-F= (при нажатии 1513 | удобнее использовать правый =Shift=). В минибуфере появится следующая строка: 1514 | 1515 | #+BEGIN_EXAMPLE 1516 | find . -type f -exec grep -nHi -e <курсор> {} + 1517 | #+END_EXAMPLE 1518 | 1519 | Возможно она выглядит страшновато для непривычного человека, но использовать её 1520 | совсем несложно. На месте курсора нужно просто написать искомую строку (если она 1521 | содержит пробелы, то её нужно заключить в кавычки) и нажать =Enter=. 1522 | 1523 | После этого в открывшемся буфере появятся результаты поиска во всех файлах 1524 | текущей директории. =C-m= (или =Enter=) точно так же будет открывать 1525 | соответствующие строчки в файлах, а =C-x C-q= - переводить буфер в режим 1526 | редактирования и обратно. 1527 | 1528 | Мне не хотелось бы дальше распространяться на тему поиска, потому что подробный 1529 | разговор о нем мог бы по размеру затмить все остальные части обзора. Поэтому в 1530 | заключение я просто приведу краткий список возможностей поисковых утилит с 1531 | небольшими пояснениями, а за деталями предлагаю обратиться к другим источникам 1532 | (документации =find= и =grep=, например). 1533 | 1534 | - =grep= можно запустить без использования =find=, но всегда нужно передавать 1535 | аргумент =-n=, например, =grep "искомая строка" -nr .= 1536 | - Для учета регистра нужно удалить аргумент =-i= при запуске =grep=, например, 1537 | =find . -type f -exec grep -nH -e "искомая строка" {} += 1538 | - Искомая строка утилиты =grep= интерпретируется, как /регулярное выражение/, 1539 | что значительно расширяет возможности поиска. Подробнее о регулярных 1540 | выражениях можно прочитать в соответствующем разделе секции вопросов и 1541 | ответов. 1542 | - Набор файлов, в которых будет производиться поиск может быть изменен при 1543 | помощи аргументов утилиты =find=, например, =find . -type f -name '*.[ch]' 1544 | -exec grep -nHi -e include {} += будет искать строку =include= только в файлах 1545 | с расширениями =.c= или =.h=. 1546 | - =C-u C-F= выполнит команду =git-grep=, вместо связки =find + grep=. 1547 | 1548 | ** Замена 1549 | 1550 | Замена - действие, обычно идущее рука об руку с поиском. В программировании 1551 | часто, например, приходится переименовать переменную или функцию. Для этой 1552 | задачи в Emacs есть две команды - =M-%= и =C-M-%=. Отличие меджу ними состоит 1553 | лишь в том, используется для поиска обычная строка или регулярное выражение. 1554 | 1555 | После нажатия соответствующей комбинации сначала нужно ввести искомую строку, а 1556 | потом замену для нее. После чего курсор будет переходить к очередному кандидату 1557 | и спрашивать, требуется ли осуществить замену. Для положительного ответа нужно 1558 | нажать =y=, для отрицательного - =n=. Для того, чтобы автоматически ответить =y= 1559 | для всех кандидатов, нужно нажать =!=. 1560 | 1561 | Приведенные комбинации успешно выполняют свои функции, однако лично я 1562 | предпочитаю вместо них использовать редактируемые буферы кандидатов поиска, 1563 | описанные в предшествующих разделах. Может показаться, что редактировать 1564 | кандидаты по одному - утомительно, и это действительно так. Но в следующих двух 1565 | разделах я опишу возможности, которые позволяют очень эффективно проводить 1566 | однотипные редактирования в буфере. 1567 | 1568 | * Клавиатурные макросы 1569 | 1570 | Не думаю, что сильно ошибусь, сказав, что автоматизация является основным 1571 | элементом в деятельности человека, направленной на достижение какого-то 1572 | результата. Чем большую часть работы удается автоматизировать, тем быстрее и 1573 | качественнее она будет сделана. 1574 | 1575 | Поэтому естественно, что Emacs, будучи серьезным инструментом, обладает богатыми 1576 | средствами автоматизации самых различных задач. Реализация одного из наиболее 1577 | доступных методов опирается на простейший принцип - "делай, как я". 1578 | 1579 | Рассмотрим такой пример. Допустим, у нас в тексте имеется такая таблица: 1580 | 1581 | #+BEGIN_EXAMPLE 1582 | 0 1 0 1 0 0 0 1583 | 1 1 0 0 1 1 1 1584 | 0 1 0 0 1 1 0 1585 | 1 1 0 0 1 1 0 1586 | 0 1 0 1 0 1 0 1587 | 1 0 0 1 1 0 1 1588 | 0 1 0 0 0 0 0 1589 | #+END_EXAMPLE 1590 | 1591 | Те, кто знают, что такое матрица смежности графа, могут думать, что это одна из 1592 | их представительниц. Те, кто не знают - могут придать этому набору нулей и 1593 | единиц любое другое значение. 1594 | 1595 | Я же предлагаю подумать над такой задачей - как все элементы на "главной" 1596 | диагонали превратить в единицы? Т.е. сделать так, чтобы таблица выглядела 1597 | следующим образом: 1598 | 1599 | #+BEGIN_EXAMPLE 1600 | 1 1 0 1 0 0 0 1601 | 1 1 0 0 1 1 1 1602 | 0 1 1 0 1 1 0 1603 | 1 1 0 1 1 1 0 1604 | 0 1 0 1 1 1 0 1605 | 1 0 0 1 1 1 1 1606 | 0 1 0 0 0 0 1 1607 | #+END_EXAMPLE 1608 | 1609 | Конечно, это всегда можно сделать вручную - таблица всего лишь 7 на 7. Но этот 1610 | пример игрушечный, а что если нам потребовалось бы выполнить подобные однотипные 1611 | действия 100 или больше раз? Неплохо бы этот процесс автоматизировать. 1612 | 1613 | Замена - наверное, единственная из рассмотренных функций, которая как-то могла 1614 | бы нам в этом помочь. Но для того, чтобы она сработала, нам нужно написать 1615 | строчку или регулярное выражение, которое соответствовало бы диагональным 1616 | элементам, что не представляется возможным. 1617 | 1618 | Давайте тогда просто прикинем, как бы мы решали эту задачу вручную. Для начала 1619 | мы встали бы перед самым первым символом, удалили его, вставили 1, перешли на 1620 | следующую строку и сдвинулись на 1 символ вправо, оказавшись, таким образом, 1621 | перед вторым символом второй строки. После чего мы бы удалили и этот символ, 1622 | вставили 1, перешли на следующую строку и сдвинулись вправо, оказавшись, перед 1623 | третьим символом третьей строки. 1624 | 1625 | Т.е. последовательность команд выглядела бы следующим образом: 1626 | 1627 | #+BEGIN_EXAMPLE 1628 | M-< C-d 1 C-n C-f C-d 1 C-n C-f C-d 1 C-n C-f C-d 1 C-n C-f ... 1629 | #+END_EXAMPLE 1630 | 1631 | Для наглядности, разделим эти команды на группы символами =|=: 1632 | 1633 | #+BEGIN_EXAMPLE 1634 | M-< | C-d 1 C-n C-f | C-d 1 C-n C-f | C-d 1 C-n C-f | C-d 1 C-n C-f | ... 1635 | #+END_EXAMPLE 1636 | 1637 | Нетрудно видеть, что команды во второй и последующих группах одни и те 1638 | же. Соответственно, если бы у нас была возможность каким-то образом 1639 | сохранить/запомнить последовательность этих команд, а потом повторить их нужное 1640 | количество раз - это сильно облегчило бы нам жизнь. 1641 | 1642 | В Emacs для этой цели существует механизм т.н. /клавиатурных макросов/. В 1643 | приведенном примере им можно воспользоваться так: 1644 | 1645 | #+BEGIN_EXAMPLE 1646 | M-< | C-d 1 C-n C-f | | | | ... 1647 | #+END_EXAMPLE 1648 | 1649 | Т.е. перед первым выполнением повторяющейся группы команд нужно нажать ==, 1650 | обозначив тем самым начало записи клавиатурного макроса. Команды, которые будут 1651 | выполнены после этого, будут сохраняться до нажатия ==, означающего конец 1652 | записи макроса. Последующие нажатия == выполнят все записанные команды. 1653 | 1654 | Думаю, все согласятся, что дело теперь обстоит значительно лучше. Единственное, 1655 | жать == 6 раз подряд после записи макроса тоже как-то не с руки. Но здесь, 1656 | как можно было догадаться, нам поможет префиксный аргумент - если передать его 1657 | команде ==, то макрос выполнится указанное количество раз: 1658 | 1659 | #+BEGIN_EXAMPLE 1660 | M-< | C-d 1 C-n C-f | С-6 1661 | #+END_EXAMPLE 1662 | 1663 | Клавиатурные макросы - казалось бы, простейшая идея, но она обладает большим 1664 | потенциалом. 1665 | 1666 | Во-первых, при записи макроса можно (и нужно) пользоваться операциями для работы 1667 | с семантическими единицами. Например, если бы значения в таблице были не просто 1668 | цифрами 0 или 1, а многозначными числами, то удаление с помощью =C-d= было бы 1669 | затруднительным - ведь выполнить =C-d= нужно столько раз, сколько в числе 1670 | цифр. Но если вместо =C-d= использовать =M-d= - всё прекрасно отработает. 1671 | 1672 | Во-вторых, совсем необязательно выполнять макрос сразу же после того, как он 1673 | записан. В приведенном примере это не очень актуально, но вообще - можно 1674 | записать какую-то последовательность действий и потом выполнять её в разных 1675 | местах, перемещая курсор между запусками. 1676 | 1677 | В-третьих, использовать макросы можно не только для редактирования текстов, но и 1678 | для любых других действий - в конце концов, это ведь всего лишь повторение 1679 | нажатий клавиш. Представим, например, что мы находимся в директории с исходными 1680 | кодами программы, каждый из которых содержит "заголовок" с лицензией, e-mail'ом 1681 | автора и т.п. Допустим, нам нужно поменять год лицензии на текущий и старый 1682 | e-mail - на новый. Для этого можно перейти к верхнему файлу в директории, начать 1683 | запись макроса, открыть файл, выполнить нужные правки, сохранить его, перейти 1684 | обратно в =dired= (=C-x C-j=), спуститься на строчку вниз - к следующему файлу и 1685 | закончить запись макроса. Последующие его выполнения будут обновлять заголовки 1686 | очередных файлов. Неплохо? Более того, если передать 0 в качестве префиксного 1687 | аргумента команде ==, макрос будет повторяться "пока возможно". В данном 1688 | примере выполнение прервется, когда мы попытаемся перейти к следующему файлу, 1689 | находясь на последней строчке. 1690 | 1691 | Итак, клавиатурные макросы - простой и гибкий инструмент для автоматизации 1692 | разнообразных задач в Emacs. По началу его использование может показаться 1693 | непривычным и несколько странноватым, но со временем запись макросов не будет 1694 | составлять особого труда - руки привыкнуть пользоваться командами для работы со 1695 | словами и более крупными единицами, пропадет желание записать "как можно более 1696 | короткий" макрос, будет не страшно допускать и исправлять ошибки во время 1697 | записи макроса. 1698 | 1699 | Конечно, будучи довольно примитивным, этот механизм имеет ряд ограничений и 1700 | недостатков, но доступность, понятность и простота делают его прекрасным 1701 | инструментом, особенно для начинающих. 1702 | 1703 | * Заключение 1704 | 1705 | На этом, собственно, мне и хотелось бы закончить этот обзор и с радостью 1706 | сообщить, что освоившие его граждане могут с уверенностью считать себя 1707 | кандидатами в мастера клавиатуры третьего разряда. Теперь неуклюжие попытки 1708 | товарищей писать и программировать, размахивая мышкой перед экраном и бегая 1709 | двумя пальцами по клавиатуре, будут вызывать лишь жалостливое сострадание. 1710 | 1711 | Но как и после любого значительного достижения, у многих внутри может возникнуть 1712 | ощущение пустоты, связанное с внезапно возникшей неопределенностью. Буквально 1713 | только что путь к успеху был так ясен и понятен, а что делать теперь? А теперь 1714 | можно заняться содержательной работой и продолжать изучать Emacs в процессе. 1715 | 1716 | Официальное название Emacs читается примерно так: "Расширяемый, настраиваемый, 1717 | самодокументируемый, интерактивный экранный редактор". Программа проверки 1718 | орфографии обратила мое внимание на слово "самодокументируемый", а я хочу 1719 | обратить на него внимание читателя. 1720 | 1721 | Одна из фундаментальных особенностей Emacs заключается в том, что он имеет 1722 | превосходную встроенную документацию для всех функций. В качестве примера, 1723 | предлагаю нажать = k C-f=. Эта комбинация откроет буфер, в котором подробно 1724 | изложено, что за функция выполняется при нажатии =C-f=, а именно - 1725 | =forward-char=. Аналогичным образом можно узнать назначение любой комбинации - 1726 | нужно всего лишь набрать перед ней = k=. 1727 | 1728 | Использовать эту возможность хорошо в том случае, если ты забыл, что именно 1729 | делает та или иная комбинация. Однако возможен и обратный сценарий - ты помнишь, 1730 | что есть такая функция =forward-char=, но не помнишь, какой комбинацией она 1731 | вызывается. В этом случае нужно нажать = f forward-char C-m= и будет открыт 1732 | тот же буфер с документацией, в котором сверху написано, что её можно вызвать 1733 | при помощи =C-f=. Обращаю внимание, что по мере набора названия функции 1734 | (=forward-char=), можно нажимать клавишу == и Emacs будет предлагать 1735 | возможные продолжения, основываясь на списке всех имеющихся функций. 1736 | 1737 | Также, если имя функции известно, то её можно вызвать по имени. Для этого нужно 1738 | нажать =M-x forward-char С-m= (здесь работают те же правила, что и при смене 1739 | буфера). В том случае, если её можно вызвать при помощи какой-либо комбинации, 1740 | сообщение об этом появится в /строке сообщений/ (в самом низу фрейма). 1741 | 1742 | Отдельно хочу отметить, что не всем функциям в Emacs отведены клавиатурные 1743 | комбинации. Соответственно, вызвать их иначе, как с помощью =M-x=, не получится 1744 | (если быть откровенным, то, конечно, получится - но другие способы ещё 1745 | сложнее). Например, в Emacs есть замечательная функция =align-regexp= без 1746 | собственной клавиатурной комбинации, которая легко позволяет превратить вот 1747 | такой регион: 1748 | 1749 | #+BEGIN_EXAMPLE 1750 | object.width = 30; 1751 | object.height = 150; 1752 | object.temperature = 300; 1753 | #+END_EXAMPLE 1754 | 1755 | вот в такой: 1756 | 1757 | #+BEGIN_EXAMPLE 1758 | object.width = 30; 1759 | object.height = 150; 1760 | object.temperature = 300; 1761 | #+END_EXAMPLE 1762 | 1763 | Всё, что для этого нужно сделать - это выделить все три строчки в регион и 1764 | нажать =M-x align-regexp C-m = C-m=. 1765 | 1766 | Уверен, что все эти возможности вызывают у большинства читателей такой же 1767 | искренний восторг, как и у меня. Однако, приливы радости может несколько 1768 | омрачать тот факт, что совсем непонятно, откуда можно узнать о том, какие в 1769 | Emacs есть функции и как они называются. Но не стоит отчаиваться, потому что 1770 | решение для этой проблемы тоже есть. 1771 | 1772 | Во-первых, стоит твердо запомнить, что в Emacs *возможно всё*. И либо функция 1773 | для того, что ты хочешь уже есть, либо её можно написать. Если я и передергиваю 1774 | в этом заявлении, то только самую малость. Всё, что можно сделать при помощи 1775 | символов на экране (и даже больше) - можно сделать в Emacs. Вот лишь несколько 1776 | примеров: 1777 | 1778 | - В Emacs есть встроенная игра Гомоку (известная также, как "крестики-нолики", 1779 | "5 в ряд" и т.д.) - =M-x gomoku= 1780 | - Веб-браузер =M-x eww= и E-mail клиенты 1781 | - Возможен просмотр pdf-файлов, картинок и проч. (не уверен на счет музыки и 1782 | видео, хотя и не исключаю) 1783 | 1784 | Во-вторых, в каждом буфере можно нажать = m= и в открывшейся справке будет 1785 | указано, какие функции доступны в текущем буфере (в соответствии с включенными в 1786 | нём режимами). Например, если нажать = m= в буфере Dired, то можно узнать, 1787 | что в нём можно выделять файлы при помощи =m= (mark) и снимать выделение при 1788 | помощи =u= или =U= (unmark). Выделенные файлы можно скопировать в другую 1789 | директорию при помощи =C= (copy), либо переместить - при помощи =R= 1790 | (rename). Удалить их можно при помощи =D= и т.д. 1791 | 1792 | В-третьих, при помощи комбинации = a= можно искать функции примерно 1793 | догадываясь об их названии. Например, найти описанную функцию =algin-regexp= 1794 | можно было бы предположив, что выполнять такое действие должна функция, в 1795 | названии которой есть слово =align=. Т.е. мы пишем = a align= и видим список 1796 | функций, в названии которых есть слово =align=, и по их описанию находим нужную 1797 | (для того, чтобы открыть полное описание функции нужно нажать =C-m=, находясь на 1798 | её имени). 1799 | 1800 | Ну и, в-четвертых, есть множество обучающих видео и статей, подобных этому 1801 | обзору, которые рассказывают о различных способах использования Emacs для 1802 | решения прикладных задач разного рода. Из них можно черпать идеи и делать свою 1803 | работу более приятной и эффективной. 1804 | 1805 | В частности, я в скором времени собираюсь написать статьи на следующие темы: 1806 | 1807 | - Специальные возможности Emacs (взаимодействие с операционной системой, более 1808 | "продвинутые" возможности редактирования и т.д.) 1809 | - Расширение и настройка Emacs 1810 | - Как начать программировать в Emacs 1811 | - Как работать с большими программными проектами в Emacs 1812 | - Как создавать презентации в Emacs 1813 | 1814 | Надеюсь, дорогой товарищ, этим обзором мне удалось заинтересовать тебя и 1815 | показать с новой и интересной стороны такую, казалось бы, посредственную тему, 1816 | как редактирование текстов. Желаю тебе успехов и свершений во всех твоих 1817 | начинаниях. Я буду очень признателен за любые отзывы, вопросы и комментарии - по 1818 | поводу и без. Будем помогать друг другу становиться лучше! 1819 | 1820 | * Вопросы и ответы 1821 | ** Для кого предназначен этот обзор? 1822 | 1823 | Возможно, ты вообще никогда ничего не программировал в своей жизни и при работе 1824 | с компьютером ограничивался браузером и меню компьютерных игр, а теперь тебе 1825 | захотелось написать свою собственную игру или сайт. Возможно, ты что-то 1826 | программировал в школе или университете, глядя в голубой экран с текстом на 1827 | языке Паскаль и вот сейчас в твоих висках неожиданно застучали воспоминания и 1828 | огнём вспыхнули дерзкие мысли. Возможно, ты программировал на C# в Visual Studio 1829 | под Windows и чувствуешь, что настоящая жизнь проходит мимо и отправился её 1830 | искать. 1831 | 1832 | Во всех этих случаях, а также, если ты программист / администратор / публицист / 1833 | ..., который вдруг осознал, что написание и перестановка символов на экране 1834 | занимает значительную часть твоего дня. Если ты задумался, почему ты вынужден 1835 | ставить десяток программ - каждую со своим текстовым редактором разной степени 1836 | убогости, для не очень-то разных задач. Если тебе непонятно, почему ты должен к 1837 | ним привыкать, воевать с ними и вообще пользоваться ими, то ты зашел по адресу. 1838 | 1839 | Думаю, очевидно, что редактирование текста - первейшая задача при работе за 1840 | компьютером и было бы странно, если бы она до сих пор не была решена. Ко 1841 | всеобщей радости, решили её давно - созданием Emacs. 1842 | 1843 | А также попутно решили вопрос с тем, как человек может удобно организовать и 1844 | обработку созданного текста. Языки программирования приходят и уходят, а Emacs 1845 | остается. Языки вёрстки - LaTeX, HTML, Markdown - приходят и уходят - а Emacs 1846 | остается. Операционные системы приходят и уходят, а Emacs - прекрасно работает 1847 | на любой из них. 1848 | 1849 | Кому-то может показаться, что я передергиваю и бессовестно пользуюсь рекламными 1850 | приемами. Однако, судите сами - за недолгую историю компьютеров и программного 1851 | обеспечения, мы уже привыкли к тому, что новые идеи и решения устаревают, едва 1852 | успев появиться. Поэтому, на мой взгляд, программа, рожденная в 1970-х годах, 1853 | которая до сих пор жива и работает, которую используют сотни тысяч людей по сей 1854 | день, как минимум, внушает интерес и доверие - по всей видимости, что-то в ней 1855 | сделано как следует. 1856 | 1857 | ** Так ли уж важно уметь печатать вслепую? 1858 | 1859 | Гораздо важнее, чем ты думаешь. 1860 | 1861 | На мой взгляд, совершенно очевидно, что это мега-полезный навык. А все 1862 | отговорки - не выдерживают малейшей критики. 1863 | 1864 | Главный аргумент, почему люди не учатся печатать вслепую - "у меня и так всё 1865 | нормально". Естественно, ничего у них не нормально. 1866 | 1867 | Когда речь заходит о слепой печати - в первую очередь люди думают о скорости 1868 | набора. И те, кто научился двумя пальцами набирать по 100 знаков в минуту, 1869 | говорят, что они и так хорошо справляются - быстрее не надо. 1870 | 1871 | Действительно, со стороны, скорость - самый заметный и внешне привлекательный 1872 | аспект - но с практической точки зрения он интересен разве только 1873 | стенографисткам. Средний программист, к примеру, в день набирает около 100 (ста) 1874 | строк кода. Скорость, с которой он их наберет даже тыкая двумя пальцами по 1875 | клавиатуре - всего лишь небольшая толика 8ми-часового рабочего дня. 1876 | 1877 | Может показаться, что 100 строк в день - заниженная оценка, но на самом деле она 1878 | легко объясняется. Как правило, программист гораздо больше думает о том, /что/ 1879 | нужно написать, чем непосредственно пишет. То же самое можно сказать и о 1880 | писателе или журналисте. Человек не может сочинять текст на скорости 300 1881 | символов в минуту. Нужно подбирать слова, перечитывать написанное, переставлять 1882 | абзацы и предложения - именно это занимает большую часть времени. 1883 | 1884 | Поэтому в навыке слепой печати намного важнее то, что человек перестает думать о 1885 | том, *КАК* он печатает и у него освобождается время для обдумывания того, *ЧТО* 1886 | нужно напечатать. 1887 | 1888 | При печати двумя пальцами, человеку нужно перевести взгляд с монитора на 1889 | клавиатуру, а потом обратно, нужно найти буквы на клавиатуре. В результате фраза 1890 | "Добрый день" в голове превращается в "Lj,hs-блин, язык не 1891 | переключил,del,del,del,del,del-Д-о-б-р-ы-ы-ы-где-ы?-ы-й д-е-н-ь!". И к тому 1892 | времени, как человек её набрал он уже забыл, что хотел написать в письме. 1893 | 1894 | Представь, также что ты вынужден был бы думать о том, как и когда делать вдохи и 1895 | выдохи - с большими шансами ты вообще не смог бы думать больше ни о чем другом, 1896 | а думал только как бы не задохнуться. 1897 | 1898 | Таким образом, главный плюс слепой печати - среди прочих неоспоримых и важных 1899 | преимуществ - заключается в том, что печать для тебя становится навыком, 1900 | записанным на подкорке мозга - ты просто перестаешь о нем думать - как о ходьбе, 1901 | дыхании и проч. 1902 | 1903 | Второй аргумент, почему люди не учатся печатать вслепую - "у меня нет времени на 1904 | обучение". 1905 | 1906 | Этот аргумент ещё мощнее первого. "Вслепую я печатаю в три раза медленнее, 1907 | сбиваюсь и забываю клавиши, а мне нужно срочно писать курсовую/диплом/проект на 1908 | работе/...". 1909 | 1910 | Прохождение курса "Соло на клавиатуре" в среднем занимает у человека 40 (сорок) 1911 | часов работы за клавиатурой (всё время занимает немногим больше). Наверное, 1912 | что-то с тобой не так, если для обретения навыка, который качественно улучшит 1913 | твою профессиональную деятельность до конца жизни, у тебя нет сорока часов. 40 1914 | часов - это один месяц по 2 часа в день, неделя - по 6 часов в день. Сколько 1915 | времени ты посвящаешь чтению новостей, просмотру YouTube, прослушиванию музыки и 1916 | т.п.? 1917 | 1918 | Кроме того, представь, что тебе полгода и ты научился ползать по квартире. В год 1919 | родители тебя заставляют ходить - а ты им объясняешь, что можешь доползти куда 1920 | хочешь и тебе непонятно, зачем нужно ходить - перемещаешься ты гораздо 1921 | медленнее, постоянно падаешь, быстро устаешь. И где бы ты был, если бы в 20 лет 1922 | по-прежнему ползал и не умел ходить? 1923 | 1924 | Третий аргумент. Многим кажется, что овладеть слепой печатью очень сложно. 1925 | Действительно, задуматься только - надо же запомнить порядка 30 клавиш, а если 1926 | печатать на 2х языках - то порядка 70. Их все нужно ПОСТОЯННО ПОМНИТЬ. 1927 | 1928 | Главный секрет здесь - думать надо меньше. Я не зря всё время провожу аналогии с 1929 | ходьбой, дыханием и подобными действиями - все они управляются не сознанием, а 1930 | подсознанием. В процессе обучения - ты действительно будешь задумываться, где 1931 | расположена какая-то клавиша. Но эта информация очень быстро запишется под корку 1932 | и не будет требовать вообще никаких умственных усилий. Вообще никаких. Клавиши 1933 | будут нажиматься чисто машинально. 1934 | 1935 | Кстати сказать, это ещё и положительно сказывается на количестве ошибок. В моем 1936 | детстве был мультфильм про сороконожку, которую спросили, как она управляется со 1937 | своими ногами, она ничего толком не ответила и ушла, но задумалась. И когда она 1938 | стала думать, как ей шагать - ноги у неё стали заплетаться и она постоянно 1939 | падала, а когда она отвлеклась, то спокойно пошла, как раньше. 1940 | 1941 | И последний анекдот. После того, как я прошел курс обучения на английском языке, 1942 | по-русски я всё ещё печатал глядя на клавиатуру. Но я привык держать руки 1943 | правильно и однажды во время печати меня осенило, что я уже около часа печатаю 1944 | по-русски и ни разу не поглядел на клавиатуру. Я стал придумывать слова, а мои 1945 | пальцы сами их набирали. Причем, для того, чтобы вспомнить, где находится 1946 | какая-то конкретная клавиша, мне приходилось подумать секунд 5, а текст 1947 | набирался совершенно непринужденно - при условии, что я думал о тексте, а не о 1948 | клавишах. 1949 | 1950 | Надеюсь, этот прием поможет и тебе, мой ленивый читатель. 1951 | 1952 | ** Зачем менять Caps Lock на Ctrl? 1953 | 1954 | Во-первых, используешь ты Emacs или нет, Caps Lock - абсолютно бесполезная 1955 | кнопка, которая занимает одну из самых удобных позиций на клавиатуре. Объяснить 1956 | это чем-то кроме исторического недоразумения невозможно. 1957 | 1958 | Вообще, раскладка клавиатуры, которая повсеместно используется сегодня - т.н. 1959 | QWERTY - сама по себе является историческим недоразумением. Она была придумана 1960 | во времена печатных машинок и одним из главных факторов, повлиявших на её 1961 | окончательный вид было то, что механические молоточки, которые выбивали символы 1962 | на бумаге, не должны были цепляться друг за друга и застревать. Для этого буквы, 1963 | которые в тексте часто встречаются слитно, старались развести как можно дальше 1964 | друг от друга. 1965 | 1966 | Такие метрики, как частота использования клавиш, частота использования разных 1967 | пальцев, частота чередования рук, практически не учитывались при разработке 1968 | QWERTY - её просто делали такой, чтобы механическая машинка могла работать. 1969 | 1970 | Одной из первых раскладок, которая попыталась исправить это недоразумение была 1971 | Dvorak - и сегодня именно она является второй самой используемой раскладкой. При 1972 | её разработке как раз учитывались все те факторы, которые я перечислил - самые 1973 | часто используемые символы поместили на средний ряд, постарались, чтобы часто 1974 | встречающиеся сочетания двух букв как можно чаще набирались разными руками и 1975 | т.д. А работоспособность механической машинки не учитывалась вовсе, потому что 1976 | их вытеснили клавиатуры. 1977 | 1978 | Для набора текста раскладка Dvorak по всем параметрам лучше QWERTY. Все мировые 1979 | рекорды скорости до недавнего времени ставились только на ней. QWERTY не было 1980 | даже близко в рекордных таблицах. 1981 | 1982 | И по уму, все уже давно должны были перейти на Dvorak, но реальность диктовала 1983 | свои условия - куда бы ты ни пришел - везде стоят только QWERTY-клавиатуры, 1984 | операционные системы не поддерживают других раскладок или их не очень просто 1985 | настроить. Плюс - многие полезные комбинации кнопок, например, откат последнего 1986 | действия, вырезать, копировать, вставить, располагаются на Ctrl-Z, Ctrl-X, 1987 | Ctrl-C, Ctrl-V, и если сменить раскладку - то они разлетятся по всей клавиатуре. 1988 | 1989 | Для преодоления этих трудностей относительно недавно была придумана раскладка 1990 | Colemak - которая сравнима по ключевым параметрам с Dvorak, но гораздо больше 1991 | похожа на QWERTY, чем Dvorak. В частности, названные клавиши - Z, X, C, V - 1992 | вообще остались на тех же местах. Всё это, плюс - поддержка современными 1993 | операционными системами, плюс - активная реклама, сделали Colemak третьей по 1994 | популярности на сегодняшний день. 1995 | 1996 | Но для чего я это рассказываю? А для того, что создатели Colemak тоже заметили, 1997 | что Caps Lock - это бесполезная кнопка на отличном месте. И на её место они 1998 | посадили Backspace. Печатальщики-пьюристы, наверное, раскритиковали бы такое 1999 | решение, дескать, "настоящему печатальщику не нужен Backspace, потому что он не 2000 | совершает ошибок". Но, на мой взгляд, решение это, в целом, хорошее. И не 2001 | пользуйся я Emacs'ом - поступил бы точно так же. Однако самая часто используемая 2002 | не-буквенная клавиша при работе в Emacs - Ctrl, поэтому именно он заслуживает 2003 | самого удобного положения. А вопрос с Backspace'ом там решен по-другому. 2004 | 2005 | Кроме того, раз уж мы рассматриваем вопрос в историческом контексте, то на 2006 | старых клавиатурах для Unix-овых терминалов Ctrl располагался именно на месте 2007 | Caps Lock'a. Либо, на некоторых вариантах - на месте нынешнего Alt'a - тоже в 2008 | легко досягаемой позиции. Что, собственно, и мотивировало его частое 2009 | использование в редакторах того времени, к которым относится Emacs. 2010 | ** Что такое регулярные выражения? 2011 | 2012 | Поиск по умолчанию ищет точное (с возможной поправкой на регистр) совпадение 2013 | строки поиска с какой-либо подстрокой в текущем буфере. Но бывают случаи, когда 2014 | требуется найти строку, имеющую определенный вид. 2015 | 2016 | Рассмотрим такой пример. В Emacs есть встроенный легковесный язык разметки - 2017 | Org. Он используется для верстки простых документов, которым не требуется 2018 | сложное форматирование, как, например, этот обзор. 2019 | 2020 | Org позволяет выделять заголовки и подзаголовки разных уровней, вставлять 2021 | примеры кода и изображения, выделять текст жирным, курсивом и т.п. Текст, 2022 | сверстанный в Org можно экспортировать в множество других форматов, например, 2023 | PDF или HTML. Но в отличие от более серьезных языков верстки, таких как LaTeX 2024 | или тот же HTML, его исходный код содержит минимум вспомогательных 2025 | синтаксических элементов. Всё это позволяет легко и быстро писать документы, 2026 | заботясь, в основном, об их содержании, а не о внешнем виде. 2027 | 2028 | Но вернемся к вопросу поиска. В языке Org заголовки секций документа начинаются 2029 | с некоторого количества звездочек: заголовки верхнего уровня - с одной, 2030 | подзаголовки - с двух и т.д.: 2031 | 2032 | #+BEGIN_EXAMPLE 2033 | ,* Навигация и поиск 2034 | 2035 | ,** Навигация 2036 | 2037 | В самом начале я уже рассказал о том, что поиск в буфере можно осуществлять с 2038 | помощью комбинаций =C-s= и =C-r=. Сейчас я хочу поподробнее рассказать о них, о 2039 | поиске вообще и не только. 2040 | 2041 | ,** Поиск 2042 | 2043 | Использование поиска для навигации по буферу, возможно, интересно и свежо. Но, 2044 | наверное, неплохо бы было рассказать подробнее и о собственно поиске, потому что 2045 | пока я затронул только самые базовые его возможности. 2046 | 2047 | ,*** Дополнительные удобства 2048 | 2049 | Итак, "простой" поиск по буферу можно осуществлять при помощи комбинаций =C-s= и 2050 | =C-r= для поиска "вперед" и "назад", соответственно. 2051 | 2052 | ,*** Регулярные выражения 2053 | 2054 | Уже описанные расширения функциональности, конечно, важны, но они являются 2055 | скорее "косметическими". В этой секции я хочу рассказать о качественно 2056 | отличающихся возможностях поиска. 2057 | 2058 | ,* Разное 2059 | 2060 | В разделе "Разное" я планирую рассказать о разном, после того, как закончу 2061 | отсальные разделы. 2062 | #+END_EXAMPLE 2063 | 2064 | Что делать, если я хочу перейти к заголовку текущего раздела? С одной стороны, 2065 | его можно найти по названию, но для этого нужно держать в голове название 2066 | раздела, в котором ты находишься, что не всегда возможно, и уж точно совсем не 2067 | удобно. С другой стороны, можно попробовать найти заголовок по звездочкам, но 2068 | звездочки могут встречаться в тексте и сами по себе и для выделения текста 2069 | жирным. Однако, если звездочки встречаются в самом начале строки, то эта строка 2070 | обязательно является заголовком. Соответственно, нам нужно как-то ограничить 2071 | поиск, чтобы он выделял звездочки только в начале строки. 2072 | 2073 | Для того, чтобы обогатить возможности поиска строк в различных текстах, 2074 | человечество придумало и использует т.н. /регулярные выражения/ (regular 2075 | expressions). Фактически, это специальный язык, который позволяет выражать более 2076 | сложные условия, по сравнению с точным совпадением символов. 2077 | 2078 | В случае с заголовками, регулярное выражение =*=, как и строка обычного поиска, 2079 | будет совпадать с любой звездочкой в тексте. Но выражение =^*= будет совпадать 2080 | не с подстрокой, состоящей из двух символов =^= и =*=, а с одной звездочкой, 2081 | стоящей в начале строки. Потому что символ =^=, находящийся в начале регулярного 2082 | выражения, является специальным обозначением начала строки. 2083 | 2084 | Т.е. для перехода к текущему подзаголовку можно поступить следующим образом: 2085 | 2086 | 1. нажать =C-r= для перехода в режим поиска 2087 | 2. нажать =M-s r= для перехода в режим поиска по регулярным выражениям 2088 | 3. ввести =^*= для перехода предыдущему подзаголовку 2089 | 2090 | Обращаю внимание, что если бы мы не перешли в режим поиска по регулярным 2091 | выражениям, то искалась бы подстрока =^*=, а не заголовочные звездочки. 2092 | 2093 | Пойдем дальше. Что, если нам нужно найти заголовок первого уровня? Нетрудно 2094 | догадаться, что это можно сделать при помощи выражения =^*<пробел>=. Звездочки 2095 | заголовков более низких уровней не будут с ним совпадать, поскольку их больше 2096 | одной. 2097 | 2098 | Разумно было бы предположить, что если мы хотим найти заголовки второго уровня, 2099 | то это можно было бы сделать при помощи выражения =^**<пробел>=. Однако, оно 2100 | будет находить начало *каждой* строки, а не только начальные звездочки 2101 | заголовков. 2102 | 2103 | Дело в том, что символ =*= тоже имеет специальное значение в регулярных 2104 | выражениях. Он соответствует условию "0 или более повторений предыдущего 2105 | выражения". Т.е. выражение =^**<пробел>= можно прочитать так: "начало строки, за 2106 | которым следует 0 или более повторений символа *, после чего следует пробел". 2107 | 2108 | Чтобы вторая звездочка рассматривалась поиском, как обычная звездочка, а не 2109 | специальный символ, перед ней нужно поставить /экранирующий символ/ (escape 2110 | symbol) - =\=. Выражение будет выглядеть следующим образом: =^*\*<пробел>=. И 2111 | оно уже будет искать заголовки второго уровня. По аналогии, можно искать 2112 | заголовки третьего уровня: =^*\*\*<пробел>= и т.д. 2113 | 2114 | У внимательного читателя может возникнуть вопрос - если звездочка имеет 2115 | специальное значение, то почему нам не приходилось экранировать её, в выражении 2116 | =^*=? Дело в том, что в этом конкретном выражении, она стоит в таком положении, 2117 | что её специальное значение не имеет смысла. Действительно, что значит "0 или 2118 | более повторений начала строки"? Начало у каждой строки всегда одно - "начAл" не 2119 | бывает 0 или 2. А в тех случаях, когда значение специального символа не имеет 2120 | смысла, то он рассматривается как обычный символ, без дополнительного 2121 | экранирования. Аналогично, например, символ =^=, появившийся не в начале 2122 | выражения, будет рассматриваться, как обычный. 2123 | 2124 | Мне не хотелось бы дальше распространяться на тему регулярных выражений, потому 2125 | что, во-первых, о них уже написано огромное количество материалов, в которых они 2126 | разбираются во всех деталях. А, во-вторых, потому что, на мой взгляд эта тема, 2127 | несмотря на всю свою важность, не стоит долгого разговора. Самый правильный 2128 | способ учиться пользоваться регулярными выражениями, на мой взгляд - применение 2129 | их в ежедневной работе. Большого смысла в том, чтобы специально разбирать 2130 | бесконечное число различных конструкций нет, потому что применяются они не так 2131 | часто, и всё равно очень быстро вылетают из головы. 2132 | 2133 | Поэтому в заключение я лишь приведу список наиболее простых и важных 2134 | конструкций: 2135 | 2136 | - =.= (точка) - специальный символ, на месте которого может стоять любой другой 2137 | символ (ровно один), кроме перевода строки. Например, выражение =a.b= 2138 | совпадает с любым набором ровно из трех букв, который начинается на =a= и 2139 | заканчивается на =b=. 2140 | 2141 | - =*= (звездочка) - оператор, обозначающий, что предыдущее подвыражение может 2142 | быть повторено 0 или более раз. Например, =o*= совпадает с любым числом 2143 | повторений символа =o=, включая ни одного. 2144 | 2145 | =*= всегда относится к самому короткому предшествующему выражению. Например, 2146 | =fo*=, будет совпадать со строками =f=, =fo=, =foo=, и т.д., но не =fofo=. 2147 | 2148 | Кроме того, =*= является "жадным" оператором, т.е. он захватит максимально 2149 | возможное количество совпадающих подвыражений. Например, =fo*.= при поиске в 2150 | тексте =foooooob=, будет совпадать со всем текстом. 2151 | 2152 | - =+= (плюс) - оператор, аналогичный =*= за единственным исключением - 2153 | предыдущее подвыражение должно быть повторено хотя бы один раз. Например, 2154 | =ca+r= будет совпадать со строками =car= и =caaaar=, но не будет совпадать с 2155 | =cr=, тогда как =ca*r= будет совпадать со всеми тремя строками. 2156 | 2157 | - =?= (вопросительный знак) - оператор, аналогичный =*= за единственным 2158 | исключением - предыдущее подвыражение может повторяться 0 или 1 раз. Например, 2159 | =ca?r= будет совпадать со строками =car= и =cr= и никакими другими. 2160 | 2161 | - =*?=, =+?=, =??= - "ленивые" ("нежадные") версии соответствующих операторов. В 2162 | отличие от своих "жадных" аналогов, они будут захватывать минимально возможное 2163 | количество символов. Например, =fo*?.= при поиске в тексте =foooooob=, будет 2164 | совпадать с подстрокой =fo=, а не всем текстом (в данном случае оператор =*?= 2165 | захватывает минимально возможное число символов - 0). 2166 | 2167 | - =[ ... ]= (символы, заключенные в квадратные скобки) - соответствуют условию 2168 | "один из". Например, =c[ad]r= совпадает со строками =car= или =cdr= и никакими 2169 | другими. =c[ad]*r= совпадает с =сr=, =car=, =cdr=, =caddaar=, и т.п. 2170 | 2171 | Также в скобках можно указывать интервалы, используя символ =-=. Например, 2172 | =[a-zA-Z]= будет совпадать с любым буквенным символом латинского алфавита в 2173 | верхнем или нижнем регистре. 2174 | 2175 | Для того, чтобы включить в множество символ =]=, его нужно указать самым 2176 | первым (в этом случае потеряется смысл его специального значения, как 2177 | закрывающей скобки). Аналогично, символ =-= можно указать первым или последним 2178 | символом в множестве. Например, =[]-]= совпадает со строками =]= и =-= и 2179 | никакими другими. 2180 | 2181 | Если первым символом после открывающей скобки является =^=, то смысл 2182 | конструкции меняется на противоположный - "ни один из". Например, =[^a-zA-Z]= 2183 | будет совпадать с любым символом, *не* являющимся буквой латинского алфавита в 2184 | верхнем или нижнем регистре. 2185 | 2186 | - =^= (шляпа) - специальный символ, совпадающий с началом строки. Выполняет 2187 | специальную функцию только если является первым символом регулярного 2188 | выражения. Например, =^a= будет совпадать только с символом =a=, находящимся в 2189 | начале строки. 2190 | 2191 | - =$= (доллар) - аналогично =^=, но совпадает с концом строки, а не 2192 | началом. Например, =x+$= совпадает с подстрокой из одного или более =x='ов, 2193 | находящихся в конце строки. 2194 | 2195 | На этом, пожалуй, список основных функций и операторов в регулярных выражениях 2196 | заканчивается. Есть ещё функции, позволяющие выражать различные единицы, 2197 | например, "буква", "цифра", "пробельный символ", "слово" и т.п. Есть оператор 2198 | "или" и многое другое. За более полным описанием регулярных выражений можно 2199 | обратиться, например, к документации, встроенной в сам Emacs: = i=, в 2200 | открывшемся буфере перейти по ссылкам "Emacs", затем "Search", затем "Regexps", 2201 | либо "Regexp Backslash". 2202 | --------------------------------------------------------------------------------