File: emacs.ml

package info (click to toggle)
otags 4.05.1-1
  • links: PTS, VCS
  • area: main
  • in suites: buster, sid
  • size: 424 kB
  • ctags: 356
  • sloc: ml: 1,267; sh: 212; makefile: 194
file content (158 lines) | stat: -rw-r--r-- 4,587 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
(* Otags III
 * 
 * Hendrik Tews Copyright (C) 2010 - 2017
 * 
 * This file is part of "Otags III".
 * 
 * "Otags III" is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * "Otags III" is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License in file COPYING in this or one of the parent
 * directories for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with "Otags III". If not, see
 * <http://www.gnu.org/licenses/>.
 * 
 * write emacs tags files
 * 
 *)

open Global
open Otags_types
open Source_channel
open Fix_location


(* For files with INCLUDE or line directives we suddenly get tags with
 * locations from different files. We cannot tag with the main source file
 * because, for the line directives case, we don't know their location and,
 * for the INCLUDE case, they do not occur there. We therefore maintain 
 * several buffers to which we append tags. They are created as needed 
 * and stored in a hash table, because, for the line directive case, 
 * it is possible that one jumps several times between two files.
 * 
 * This buffers hash table is empty between compilation units. And the 
 * current buffer is some default empty buffer that is never used.
 * 
 * If file = "" then the current_buf, file and ic fields hold only
 * placeholders. Therefore filenames must be different from "".
 *)
type emacs_tag_state = {
  tags_oc : out_channel;
  buffers : (string, Buffer.t) Hashtbl.t;
  mutable current_buf : Buffer.t;
  mutable file : string;
}


(* Dig out the buffer into which the tags for file are written, to
 * append the next tag to the right buffer. If that buffer does not yet
 * exist it is created. 
 *)
let make_current_buffer es file =
  let buf =
    try
      Hashtbl.find es.buffers file
    with
      | Not_found -> 
	let buf = Buffer.create 4095 in
	Hashtbl.add es.buffers file buf;
	buf
  in
  es.current_buf <- buf;
  es.file <- file


let emacs_tag_line line tag line_number char =
  Printf.sprintf "%s\127%s\001%d,%d\n" line tag line_number char


let start_unit es file =
  assert(file <> "");
  assert(Hashtbl.length es.buffers = 0);
  let mod_name = Otags_misc.module_name file in
  let module_tag = emacs_tag_line "" mod_name 1 0 in
  make_current_buffer es file;
  Buffer.add_string es.current_buf module_tag


let write_tag es loc tag = 
  (* Printf.eprintf "%s: %s\n" tag (full_string_of_loc loc); *)
  if es.file <> Otags_misc.file_of_loc loc 
  then 
    make_current_buffer es (Otags_misc.file_of_loc loc);
  let loc = fix_loc loc in
  let line_fragment =
    (* if loc is wrong (eg, because of line directives) cut_out will
     * throw End_of_file.
     *)
    try
      Otags_misc.cut_out
	(get_channel loc)
	loc.Location.loc_start.Lexing.pos_bol
	loc.Location.loc_end.Lexing.pos_cnum
    with
      | End_of_file ->
	 raise
	   (Otags_parsing_error(
		loc,
		(Printf.sprintf
		   ("The parser delivered an invalid location "
		    ^^ "(char position %d-%d),\n"
		    ^^ "maybe there are line directives in the input?")
		   loc.Location.loc_start.Lexing.pos_bol
		   loc.Location.loc_end.Lexing.pos_cnum)))
  in
  Buffer.add_string es.current_buf 
    (emacs_tag_line
       line_fragment
       tag
       loc.Location.loc_start.Lexing.pos_lnum
       loc.Location.loc_start.Lexing.pos_bol)


  (* Buffer, never to be really used, serves as a placeholder for the
   * current_buf field between compilation units.
   *)
let default_empty_buffer = Buffer.create 1


let finish_unit es () =
  Hashtbl.iter 				(* XXX don't write empty buffers *)
    (fun file buf ->
      Printf.fprintf es.tags_oc "\012\n%s,%d\n" 
	(if !relative_file_prefix <> "" && Filename.is_relative file
	 then Filename.concat !relative_file_prefix file
	 else file)
	(Buffer.length buf);
      Buffer.output_buffer es.tags_oc buf;
    )
    es.buffers;
  Hashtbl.clear es.buffers;
  es.current_buf <- default_empty_buffer;
  es.file <- "";
  ()
  

let finish_tagging _es () = ()


let init oc =
  let es = {
    tags_oc = oc;
    buffers = Hashtbl.create 23;
    current_buf = default_empty_buffer;
    file = "";
  }
  in {
    start_unit = start_unit es;
    write_tag = write_tag es;
    finish_unit = finish_unit es;
    finish_tagging = finish_tagging es;
  }