comparison runtime/ftplugin/ocaml.vim @ 20:4ac1dce8dd5e v7.0012

updated for version 7.0012
author vimboss
date Mon, 26 Jul 2004 12:53:41 +0000
parents 3fc0f57ecb91
children 862863033fdd
comparison
equal deleted inserted replaced
19:a81bc802c17c 20:4ac1dce8dd5e
1 " Vim settings file 1 " Vim settings file
2 " Language: OCaml 2 " Language: OCaml
3 " Maintainers: Mike Leary <leary@nwlink.com> 3 " Maintainers: Mike Leary <leary@nwlink.com>
4 " Markus Mottl <markus@oefai.at> 4 " Markus Mottl <markus@oefai.at>
5 " URL: http://www.ai.univie.ac.at/~markus/vim/ftplugin/ocaml.vim 5 " Stefano Zacchiroli <zack@bononia.it>
6 " Last Change: 2003 May 11 6 " URL: http://www.oefai.at/~markus/vim/ftplugin/ocaml.vim
7 " 2001 Nov 01 - added local bindings for inserting 7 " Last Change: 2004 Apr 12 - better .ml/.mli-switching without Python (SZ)
8 " type holes using 'assert false' (MM) 8 " 2003 Nov 21 - match_words-patterns and .ml/.mli-switching (MM)
9 " 2001 Oct 02 - insert spaces in line comments (MM) 9 " 2003 Oct 16 - re-entered variable 'did_ocaml_dtypes' (MM)
10 " 2003 Oct 15 - added Stefano Zacchirolis (SZ) Python-code for
11 " displaying type annotations (MM)
10 12
11 " Only do these settings when not done yet for this buffer 13 " Only do these settings when not done yet for this buffer
12 if exists("b:did_ftplugin") 14 if exists("b:did_ftplugin")
13 finish 15 finish
14 endif 16 endif
45 47
46 if !hasmapto('<Plug>Abbrev') 48 if !hasmapto('<Plug>Abbrev')
47 iabbrev <buffer> ASS (assert false) 49 iabbrev <buffer> ASS (assert false)
48 endif 50 endif
49 endif 51 endif
52
53 " Let % jump between structure elements (due to Issac Trotts)
54 let b:mw='\<let\>:\<and\>:\(\<in\>\|;;\),'
55 let b:mw=b:mw . '\<if\>:\<then\>:\<else\>,\<do\>:\<done\>,'
56 let b:mw=b:mw . '\<\(object\|sig\|struct\|begin\)\>:\<end\>'
57 let b:match_words=b:mw
58
59 " switching between interfaces (.mli) and implementations (.ml)
60 if !exists("g:did_ocaml_switch")
61 let g:did_ocaml_switch = 1
62 map ,s :call OCaml_switch(0)<CR>
63 map ,S :call OCaml_switch(1)<CR>
64 fun OCaml_switch(newwin)
65 if (match(bufname(""), "\\.mli$") >= 0)
66 let fname = substitute(bufname(""), "\\.mli$", ".ml", "")
67 if (a:newwin == 1)
68 exec "new " . fname
69 else
70 exec "arge " . fname
71 endif
72 elseif (match(bufname(""), "\\.ml$") >= 0)
73 let fname = bufname("") . "i"
74 if (a:newwin == 1)
75 exec "new " . fname
76 else
77 exec "arge " . fname
78 endif
79 endif
80 endfun
81 endif
82
83 " Vim support for OCaml 3.07 .annot files (requires Vim with python support)
84 "
85 " Executing OCamlPrintType(<mode>) function will display in the Vim bottom
86 " line(s) the type of an ocaml value getting it from the corresponding .annot
87 " file (if any). If Vim is in visual mode, <mode> should be "visual" and the
88 " selected ocaml value correspond to the highlighted text, otherwise (<mode>
89 " can be anything else) it corresponds to the literal found at the current
90 " cursor position.
91 "
92 " .annot files are parsed lazily the first time OCamlPrintType is invoked; is
93 " also possible to force the parsing using the OCamlParseAnnot() function.
94 "
95 " Hitting the <F3> key will cause OCamlPrintType function to be invoked with
96 " the right argument depending on the current mode (visual or not).
97 "
98 " Copyright (C) <2003> Stefano Zacchiroli <zack@bononia.it>
99 "
100 " Created: Wed, 01 Oct 2003 18:16:22 +0200 zack
101 " LastModified: Mon, 06 Oct 2003 11:05:39 +0200 zack
102 "
103 " This program is free software; you can redistribute it and/or modify
104 " it under the terms of the GNU General Public License as published by
105 " the Free Software Foundation; either version 2 of the License, or
106 " (at your option) any later version.
107 "
108 " This program is distributed in the hope that it will be useful,
109 " but WITHOUT ANY WARRANTY; without even the implied warranty of
110 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
111 " GNU General Public License for more details.
112 "
113 " You should have received a copy of the GNU General Public License
114 " along with this program; if not, write to the Free Software
115 " Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
116 "
117
118 if !has("python")
119 echo "Python support not found: OCaml .annot support disabled"
120 finish
121 endif
122
123 if !exists("g:did_ocaml_dtypes")
124 let g:did_ocaml_dtypes = 1
125 else
126 finish
127 endif
128
129 python << EOF
130
131 import re
132 import os
133 import string
134 import time
135 import vim
136
137 debug = False
138
139 class AnnExc(Exception):
140 def __init__(self, reason):
141 self.reason = reason
142
143 no_annotations = AnnExc("No type annotations (.annot) file found")
144 annotation_not_found = AnnExc("No type annotation found for the given text")
145 def malformed_annotations(lineno):
146 return AnnExc("Malformed .annot file (line = %d)" % lineno)
147
148 class Annotations:
149 """
150 .annot ocaml file representation
151
152 File format (copied verbatim from caml-types.el)
153
154 file ::= block *
155 block ::= position <SP> position <LF> annotation *
156 position ::= filename <SP> num <SP> num <SP> num
157 annotation ::= keyword open-paren <LF> <SP> <SP> data <LF> close-paren
158
159 <SP> is a space character (ASCII 0x20)
160 <LF> is a line-feed character (ASCII 0x0A)
161 num is a sequence of decimal digits
162 filename is a string with the lexical conventions of O'Caml
163 open-paren is an open parenthesis (ASCII 0x28)
164 close-paren is a closed parenthesis (ASCII 0x29)
165 data is any sequence of characters where <LF> is always followed by
166 at least two space characters.
167
168 - in each block, the two positions are respectively the start and the
169 - end of the range described by the block.
170 - in a position, the filename is the name of the file, the first num
171 is the line number, the second num is the offset of the beginning
172 of the line, the third num is the offset of the position itself.
173 - the char number within the line is the difference between the third
174 and second nums.
175
176 For the moment, the only possible keyword is \"type\"."
177 """
178
179 def __init__(self):
180 self.__filename = None # last .annot parsed file
181 self.__ml_filename = None # as above but s/.annot/.ml/
182 self.__timestamp = None # last parse action timestamp
183 self.__annot = {}
184 self.__re = re.compile(
185 '^"[^"]+"\s+(\d+)\s+(\d+)\s+(\d+)\s+"[^"]+"\s+(\d+)\s+(\d+)\s+(\d+)$')
186
187 def __parse(self, fname):
188 try:
189 f = open(fname)
190 line = f.readline() # position line
191 lineno = 1
192 while (line != ""):
193 m = self.__re.search(line)
194 if (not m):
195 raise malformed_annotations(lineno)
196 line1 = int(m.group(1))
197 col1 = int(m.group(3)) - int(m.group(2))
198 line2 = int(m.group(4))
199 col2 = int(m.group(6)) - int(m.group(5))
200 line = f.readline() # "type(" string
201 lineno += 1
202 if (line == ""): raise malformed_annotations(lineno)
203 type = []
204 line = f.readline() # type description
205 lineno += 1
206 if (line == ""): raise malformed_annotations(lineno)
207 while line != ")\n":
208 type.append(string.strip(line))
209 line = f.readline()
210 lineno += 1
211 if (line == ""): raise malformed_annotations(lineno)
212 type = string.join(type, "\n")
213 self.__annot[(line1, col1), (line2, col2)] = type
214 line = f.readline() # position line
215 f.close()
216 self.__filename = fname
217 self.__ml_filename = re.sub("\.annot$", ".ml", fname)
218 self.__timestamp = int(time.time())
219 except IOError:
220 raise no_annotations
221
222 def parse(self):
223 annot_file = re.sub("\.ml$", ".annot", vim.current.buffer.name)
224 self.__parse(annot_file)
225
226 def get_type(self, (line1, col1), (line2, col2)):
227 if debug:
228 print line1, col1, line2, col2
229 if vim.current.buffer.name == None:
230 raise no_annotations
231 if vim.current.buffer.name != self.__ml_filename or \
232 os.stat(self.__filename).st_mtime > self.__timestamp:
233 self.parse()
234 try:
235 return self.__annot[(line1, col1), (line2, col2)]
236 except KeyError:
237 raise annotation_not_found
238
239 word_char_RE = re.compile("^[\w.]$")
240
241 # TODO this function should recognize ocaml literals, actually it's just an
242 # hack that recognize continuous sequences of word_char_RE above
243 def findBoundaries(line, col):
244 """ given a cursor position (as returned by vim.current.window.cursor)
245 return two integers identify the beggining and end column of the word at
246 cursor position, if any. If no word is at the cursor position return the
247 column cursor position twice """
248 left, right = col, col
249 line = line - 1 # mismatch vim/python line indexes
250 (begin_col, end_col) = (0, len(vim.current.buffer[line]) - 1)
251 try:
252 while word_char_RE.search(vim.current.buffer[line][left - 1]):
253 left = left - 1
254 except IndexError:
255 pass
256 try:
257 while word_char_RE.search(vim.current.buffer[line][right + 1]):
258 right = right + 1
259 except IndexError:
260 pass
261 return (left, right)
262
263 annot = Annotations() # global annotation object
264
265 def printOCamlType(mode):
266 try:
267 if mode == "visual": # visual mode: lookup highlighted text
268 (line1, col1) = vim.current.buffer.mark("<")
269 (line2, col2) = vim.current.buffer.mark(">")
270 else: # any other mode: lookup word at cursor position
271 (line, col) = vim.current.window.cursor
272 (col1, col2) = findBoundaries(line, col)
273 (line1, line2) = (line, line)
274 begin_mark = (line1, col1)
275 end_mark = (line2, col2 + 1)
276 print annot.get_type(begin_mark, end_mark)
277 except AnnExc, exc:
278 print exc.reason
279
280 def parseOCamlAnnot():
281 try:
282 annot.parse()
283 except AnnExc, exc:
284 print exc.reason
285
286 EOF
287
288 fun OCamlPrintType(current_mode)
289 if (a:current_mode == "visual")
290 python printOCamlType("visual")
291 else
292 python printOCamlType("normal")
293 endif
294 endfun
295
296 fun OCamlParseAnnot()
297 python parseOCamlAnnot()
298 endfun
299
300 map <F3> :call OCamlPrintType("normal")<RETURN>
301 vmap <F3> :call OCamlPrintType("visual")<RETURN>