1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package token
6
7import (
8	"go/token"
9	"sync"
10)
11
12// -----------------------------------------------------------------------------
13// File
14
15// A File is a handle for a file belonging to a FileSet.
16// A File has a name, size, line offset table and optionally source code.
17//
18type File struct {
19	*token.File
20	mutex  sync.Mutex // protects source
21	source []string   // optional, used by debugger to show source code. each line does NOT contain the final '\n'
22	line   int        // starting line of this file
23}
24
25// PositionFor returns the Position value for the given file position p.
26// If adjusted is set, the position may be adjusted by position-altering
27// //line comments; otherwise those comments are ignored.
28// p must be a Pos value in f or NoPos.
29//
30func (f *File) PositionFor(p token.Pos, adjusted bool) (pos token.Position) {
31	pos = f.File.PositionFor(p, adjusted)
32	if pos.IsValid() {
33		pos.Line += f.line
34	}
35	return pos
36}
37
38// Position returns the Position value for the given file position p.
39// Calling f.Position(p) is equivalent to calling f.PositionFor(p, true).
40//
41func (f *File) Position(p token.Pos) (pos token.Position) {
42	return f.PositionFor(p, true)
43}
44
45// Source returns the source code for the given file position p, if available.
46//
47func (f *File) Source(p token.Pos) (line string, pos token.Position) {
48	if p != token.NoPos {
49		pos = f.Position(p)
50		if pos.IsValid() {
51			f.mutex.Lock()
52			source := f.source
53			f.mutex.Unlock()
54			line := pos.Line - f.line
55			if line > 0 && line <= len(source) {
56				return source[line-1], pos
57			}
58		}
59	}
60	return "", pos
61}
62
63// SetSource sets the source code for the given file.
64//
65func (f *File) SetSource(source []string) {
66	f.mutex.Lock()
67	f.source = source
68	f.mutex.Unlock()
69}
70
71// SetSourceForContent computes and sets the source code for the given file.
72//
73func (f *File) SetSourceForContent(content []byte) {
74	str := string(content)
75	start, n := 0, len(str)
76	var source []string
77	for i := 0; i < n; i++ {
78		if str[i] == '\n' {
79			source = append(source, str[start:i])
80			// skip '\n'
81			start = i + 1
82		}
83	}
84	if start < n {
85		source = append(source, str[start:])
86	}
87	f.SetSource(source)
88}
89
90// -----------------------------------------------------------------------------
91// FileSet
92
93// A FileSet represents a set of source files.
94// This is a wrapper for go/token.FileSet that adds a starting line offset to each file in the set
95//
96type FileSet struct {
97	token.FileSet
98	filemap map[*token.File]*File
99}
100
101// NewFileSet creates a new file set.
102func NewFileSet() *FileSet {
103	return &FileSet{
104		FileSet: *token.NewFileSet(),
105		filemap: make(map[*token.File]*File),
106	}
107}
108
109// AddFile adds a new file with a given filename, base offset, and file size
110func (s *FileSet) AddFile(filename string, base, size, line int) *File {
111	innerf := s.FileSet.AddFile(filename, base, size)
112	f := &File{File: innerf, line: line}
113	s.filemap[innerf] = f
114	return f
115}
116
117// File returns the file that contains the position p.
118// If no such file is found (for instance for p == NoPos),
119// the result is nil.
120//
121func (s *FileSet) File(p token.Pos) (f *File) {
122	if p != token.NoPos {
123		innerf := s.FileSet.File(p)
124		f = s.filemap[innerf]
125	}
126	return
127}
128
129// PositionFor converts a Pos p in the fileset into a Position value.
130// If adjusted is set, the position may be adjusted by position-altering
131// //line comments; otherwise those comments are ignored.
132// p must be a Pos value in s or NoPos.
133//
134func (s *FileSet) PositionFor(p token.Pos, adjusted bool) (pos token.Position) {
135	if f := s.File(p); f != nil {
136		pos = f.PositionFor(p, adjusted)
137	}
138	return
139}
140
141// Position converts a Pos p in the fileset into a Position value.
142// Calling s.Position(p) is equivalent to calling s.PositionFor(p, true).
143//
144func (s *FileSet) Position(p token.Pos) (pos token.Position) {
145	return s.PositionFor(p, true)
146}
147
148// Source converts a Pos p in the fileset into a line of source code (if available) and a Position value.
149//
150func (s *FileSet) Source(p token.Pos) (line string, pos token.Position) {
151	if f := s.File(p); f != nil {
152		line, pos = f.Source(p)
153	}
154	return
155}
156