When writing the code, I used prototype based OO — mainly to provide a more complex example for my previous raving.
Marks in vim
In vim you can mark a location in a file so you can quickly jump back there. A mark consists of a position (line/column) and an identifier (a single letter). Every buffer has its own set of lowercase marks (letters a–z) that exist only as long as the buffer exists. There is also a set of global marks using capital letters. These marks are persistent and there is only one set.
More info can be found at
:help mark-motions
.Enter "named marks"
In the IRC chat, the guy wanted the same functionality as the uppercase marks, but with more descriptive names. Which is fair enough IMO since "A" doesn't really tell you anything about the position it jumps to.
So named marks are global, persistent marks with descriptive names.
The code
OMG check this out:
1 "start the named mark prototype
2 let s:NamedMark = {}
3
4 "the file the marks are stored in
5 let s:NamedMark.Filename = expand('~/.namedMarks')
6
7 "constructor
8 function! s:NamedMark.New(name, column, line, path)
9 if a:name =~ ' '
10 throw "IllegalNamedmarkNameError illegal name:" . a:name
11 endif
12
13 let newNamedMark = copy(self)
14 let newNamedMark.name = a:name
15 let newNamedMark.column = a:column
16 let newNamedMark.line = a:line
17 let newNamedMark.path = a:path
18 return newNamedMark
19 endfunction
20
21 "lazy load and cache all named marks
22 function! s:NamedMark.All()
23 if !exists("s:NamedMark.AllMarks")
24 let s:NamedMark.AllMarks = s:NamedMark.Read()
25 endif
26 return s:NamedMark.AllMarks
27 endfunction
28
29 "create and add a new mark to the list
30 function! s:NamedMark.Add(name, column, line, path)
31
32 try
33 "if the mark already exists, just update it
34 let mark = s:NamedMark.FindFor(a:name)
35 let mark.column = a:column
36 let mark.line = a:line
37 let mark.path = a:path
38
39 catch /NamedMarkNotFoundError/
40 let newMark = s:NamedMark.New(a:name, a:column, a:line, a:path)
41 call add(s:NamedMark.All(), newMark)
42
43 finally
44 call s:NamedMark.Write()
45 endtry
46 endfunction
47
48 "find the mark with the given name
49 function! s:NamedMark.FindFor(name)
50 for i in s:NamedMark.All()
51 if i.name == a:name
52 return i
53 endif
54 endfor
55 throw "NamedMarkNotFoundError no mark found for name: \"".a:name.'"'
56 endfunction
57
58 "get a list of all mark names
59 function! s:NamedMark.Names()
60 let names = []
61 for i in s:NamedMark.All()
62 call add(names, i.name)
63 endfor
64 return names
65 endfunction
66
67 "delete this mark
68 function! s:NamedMark.delete()
69 call remove(s:NamedMark.All(), index(s:NamedMark.All(), self))
70 call s:NamedMark.Write()
71 endfunction
72
73 "go to this mark
74 function! s:NamedMark.recall()
75 exec "edit " . self.path
76 call cursor(self.line, self.column)
77 endfunction
78
79 "read the marks from the filesystem and return the list
80 function! s:NamedMark.Read()
81 let marks = []
82 if filereadable(s:NamedMark.Filename)
83 let lines = readfile(s:NamedMark.Filename)
84 for i in lines
85 let name = substitute(i, '^\(.\{-}\) \d\{-} \d\{-} .*$', '\1', '')
86 let column = substitute(i, '^.\{-} \(\d\{-}\) \d\{-} .*$', '\1', '')
87 let line = substitute(i, '^.\{-} \d\{-} \(\d\{-}\) .*$', '\1', '')
88 let path = substitute(i, '^.\{-} \d\{-} \d\{-} \(.*\)$', '\1', '')
89
90 let namedMark = s:NamedMark.New(name, column, line, path)
91 call add(marks, namedMark)
92 endfor
93 endif
94 return marks
95 endfunction
96
97 "write all named marks to the filesystem
98 function! s:NamedMark.Write()
99 let lines = []
100 for i in s:NamedMark.All()
101 call add(lines, i.name .' '. i.column .' '. i.line .' '. i.path)
102 endfor
103 call writefile(lines, s:NamedMark.Filename)
104 endfunction
105
106 "NM command, adds a new named mark
107 command! -nargs=1
108 \ NM call s:NamedMark.Add('<args>', col("."), line("."), expand("%:p"))
109
110 "RM command, recalls a named mark
111 command! -nargs=1 -complete=customlist,s:CompleteNamedMarks
112 \ RM call s:NamedMark.FindFor('<args>').recall()
113
114 "DeleteNamedMark command
115 command! -nargs=1 -complete=customlist,s:CompleteNamedMarks
116 \ DeleteNamedMark call s:NamedMark.FindFor('<args>').delete()
117
118 "used by the above commands for cmd line completion
119 function! s:CompleteNamedMarks(A,L,P)
120 return filter(s:NamedMark.Names(), 'v:val =~ "^' . a:A . '"')
121 endfunction
lol, so... wtf does that do?
From a users perspective, if you were to shove this code in your vimrc, or in a plugin, it would provide three commands:
- NM: create a new named mark, e.g.
:NM main-function
would create themain-function
mark and bind it to the current file and cursor position. If that mark already existed, it would be overwritten - RM: recall a previous mark, e.g.
:RM main-function
would jump the cursor back to the same file and position wheremain-function
was created - DeleteNamedMark: delete a named mark, e.g.
:DeleteNamedMark main-function
would removemain-function
from the mark list.
From a programming perspective, the code defines one prototype object (lines 1–104), the three commands (lines 106–116), and a completion function for two of the commands (lines 118–121).
If I had to jack off to one part of this code, it would be the prototype object. It contains:
- Seven class methods and two class variables which are used to maintain the list of named marks, including reading/writing to the filesystem.
- Two instance methods for deleting and recalling marks.
- Four instance variables specifying the position and name of the mark.
If I was actually going to turn this into a plugin, I would want to flesh it out a bit and add, for example, better error handling and a command similar to the existing
:marks
command to list named marks.
vim makes my peepee come to attention!
ReplyDeletehey !
ReplyDeleteI was on #vim sometimes, asking some help about a problem that's look AFAIK impossible to solve with vim too. Maybe you'll have an idea. The problem appear while I try to make switch a emac's friends (nobody's perfect) who use this feature.
Long story short, it consist in highlighting with var scoping awareness. ( ~ local and "global")
a brief example (esp. look at var "name")
1class MyClass {
2
3 String name = "Foo";
4
5 void printName(){
6 cout << name << "\n";
7 }
8
9 void printNameBar(){
A String name = "Bar";
B cout << name << "\n";
C }
D}
well (sorry for poor numbering). so, the aim is to have name in line 3 and 6 in the same color and in a different color on line A and B. An idea ?
(I apologize too for my poor english.)
AFAIK this isnt possible :(
Delete