1 package acl2s.plugin.indent;
2
3 import java.util.Arrays;
4 import java.util.HashSet;
5 import java.util.Set;
6
7 import org.eclipse.jface.text.BadLocationException;
8 import org.eclipse.jface.text.IDocument;
9
10 import acl2s.plugin.parse.LispToken;
11 import acl2s.plugin.parse.LispTokens;
12
13 public class DefaultLispIndent implements ILispIndent {
14 protected final LispTokens tokens;
15 protected final IDocument doc;
16
17 public DefaultLispIndent(LispTokens tokens, IDocument doc) {
18 this.tokens = tokens;
19 this.doc = doc;
20 }
21
22 int getExistingBasicIndention(int line) throws BadLocationException {
23 int start = doc.getLineOffset(line);
24 int end = start + doc.getLineLength(line);
25
26 int indent = 0;
27 for (int i = start; i < end; i++) {
28 char ch = doc.getChar(i);
29 if (ch == ' ') {
30 indent++;
31 } else if (ch == '\t') {
32 indent += 4; // fallback; shouldn't be tabs
33 } else if (ch == '\n' || ch == '\r') {
34 return -1; // all space
35 } else {
36 return indent;
37 }
38 }
39 return -1; // all space & eof
40 }
41
42 public int getExistingLineIndention(int line) throws BadLocationException {
43 if (line < 0) return 0;
44 int start = doc.getLineOffset(line);
45 LispToken t = tokens.getToken(tokens.getTokenIdxContaining(start));
46 if (line > 0) {
47 if (t.isLiteralMode(start-1,doc)) {
48 return getExistingLineIndention(line-1);
49 }
50 }
51 int end = start + doc.getLineLength(line);
52
53 // hack to eliminate prompt
54 if (t.type == LispToken.NONE_TOK && t.absoluteEnd() <= end) {
55 start = t.absoluteEnd();
56 }
57
58 int indent = getExistingBasicIndention(line);
59 if (indent >= 0) {
60 return indent;
61 } else {
62 // nothing on line => do previous line
63 return getExistingLineIndention(line - 1);
64 }
65 }
66
67 // called from autoindenter, etc.
68 public int getNextIndentionFrom(int offset) {
69 return getNextIndentionFrom(offset, false);
70 }
71
72 protected int getNextIndentionFrom(int offset, boolean fromParam) {
73 try {
74 int tIdx;
75 if (offset < doc.getLength()) {
76 tIdx = tokens.getTokenIdxContaining(offset);
77 } else {
78 tIdx = tokens.getTokenCount() - 1;
79 }
80 LispToken t = tokens.getToken(tIdx);
81 if (t.offset >= offset) { // if we're only preceded by space in this token
82 if (t.inHashComment(offset - t.offset)) return -2; // no change, autoindent is fine
83 tIdx--;
84 if (tIdx < 0) return 0;
85 t = tokens.getToken(tIdx);
86 } else if (t.isLiteralMode(offset, doc)) { // newline interpreted *not* as whitespace
87 if ((t.type == LispToken.STRING_TOK && doc.getChar(offset-1) == '~')
88 || t.type == LispToken.DOC_TOK) {
89 // indent inside string if doc string or ~\n (as in fmt string)
90 int line = doc.getLineOfOffset(offset);
91 int lstart = doc.getLineOffset(line);
92 while (t.offset < lstart) {
93 // search for non-empty line to indent from
94 // while start of string is before this line
95 int indent = getExistingBasicIndention(line);
96 if (indent >= 0) {
97 return indent;
98 }
99 line--;
100 lstart = doc.getLineOffset(line);
101 }
102 // else we've reached start of string, which is on this line
103 if (t.type == LispToken.DOC_TOK) {
104 return t.offset - lstart; // to conform to ACL2 source conventions
105 } else {
106 return t.offset - lstart + 1;
107 }
108 } else {
109 return -1; // no change, no autoindent
110 }
111 }
112
113 int line = doc.getLineOfOffset(offset);
114 if (!fromParam && t.absoluteEnd() < offset) {
115 line = doc.getLineOfOffset(t.absoluteEnd());
116 }
117 int maxTIdx = tIdx;
118 int lstart = doc.getLineOffset(line);
119 int lstartTIdx = tokens.getTokenIdxContaining(lstart);
120
121 while (lstart > 0 && line > 0 &&
122 tokens.getToken(lstartTIdx).isLiteralMode(lstart-1, doc)) {
123 line--;
124 lstart = doc.getLineOffset(line);
125 lstartTIdx = tokens.getTokenIdxContaining(lstart);
126 }
127
128 // hack to eliminate prompt
129 LispToken st = tokens.getToken(lstartTIdx);
130 if (st.type == LispToken.NONE_TOK) {
131 lstart = st.absoluteEnd();
132 }
133
134 int balance = 0;
135 for (tIdx = maxTIdx; tIdx >= lstartTIdx && balance < 1; tIdx--) {
136 t = tokens.getToken(tIdx);
137 balance += t.parenDelta();
138 if (balance > 0) {
139 break;
140 }
141 }
142
143 if (balance > 0) {
144 int indent = t.absoluteEnd() - lstart;
145 if (tIdx + 1 <= maxTIdx) {
146 LispToken fn = tokens.getToken(tIdx + 1);
147 if (fn.type == LispToken.SYM_TOK) {
148 String fName = doc.get(fn.offset,fn.length);
|
Event do_not_call: |
"java.lang.String.toLowerCase()" implicitly uses the environment's default character set, which might lead to unexpected behavior. Consider using toLowerCase(Locale locale). |
149 fName = fName.toLowerCase();
150 if (fName.startsWith("def") || indentNameSet.contains(fName)) {
151 return indent + 1;
152 }
153 }
154 if ((fn.type == LispToken.SYM_TOK && !(doc.getChar(fn.offset) == ':')) // sym but ot keyword
155 || fn.type == LispToken.STRING_TOK) {
156 if (tIdx + 2 <= maxTIdx || fromParam) { // also a function and >= 1 parameter following
157 LispToken param1 = tokens.getToken(tIdx + 2);
158 indent = param1.offset - lstart;
159 }
160 }
161 }
162 return indent;
163 }
164
165 if (balance == 0) return getExistingLineIndention(line);
166
167 // balance < 0
168 for (tIdx = maxTIdx; tIdx >= lstartTIdx; tIdx--) {
169 t = tokens.getToken(tIdx);
170 if (t.isCParen()) {
171 break;
172 }
173 }
174 tIdx = tokens.matchParenTok(tIdx);
175 if (tIdx < 0) return 0;
176 t = tokens.getToken(tIdx);
177 line = doc.getLineOfOffset(Math.min(offset,t.absoluteEnd()));
178 lstart = doc.getLineOffset(line);
179 return getNextIndentionFrom(t.offset,true); // RECURSION!!
180 } catch (BadLocationException e) {
181 // impossible
182 e.printStackTrace();
183 return 0;
184 }
185 }
186
187 public void indentLine(int line) {
188 try {
189 int docLen = doc.getLength();
190 int start = doc.getLineOffset(line);
191 int space = 0;
192 int semis = 0;
193 boolean allSpace = true;
194 for (;;) {
195 if (start+space >= docLen) break;
196 char ch = doc.getChar(start+space);
197 if (ch != ' ' && ch != '\t') {
198 if (ch != '\r' && ch != '\n') allSpace = false;
199 for (;;) {
200 int i = start+space+semis;
201 if (i >= docLen || doc.getChar(i) != ';') break;
202 semis++;
203 }
204 break;
205 }
206 space++;
207 }
208
209 LispToken t;
210 if (start > 0) {
211 t = tokens.getToken(tokens.getTokenIdxContaining(start - 1));
212 } else if (docLen > 0) {
213 t = tokens.getToken(tokens.getTokenIdxContaining(start));
214 } else {
215 t = null;
216 }
217
218 boolean toBeginning = false;
219 if (t != null &&
220 t.offset >= start + doc.getLineLength(line) && // if the line is all whitespace
221 !allSpace) { // and there's text in the line (comments)
222 if (semis == 0 || semis >= 3) {
223 return; // don't change indent
224 } else {
225 toBeginning = semis == 1;
226 }
227 }
228
229 if (line == 0 || toBeginning) {
230 if (space == 0) return; // no change
231 doc.replace(start, space, "");
232 } else {
233 int indent = 0;
234 indent = getNextIndentionFrom(start-1);
235 if (indent < 0) return; // indent shouldn't be changed (e.g. literal mode)
236 if (indent == space) return; // no change
237 String text = LispAutoIndent.getSpaces(indent);
238 doc.replace(start, space, text);
239 }
240 } catch (BadLocationException e) {
241 e.printStackTrace();
242 }
243 }
244
245 String[] indentNames = new String[] { // def* automatically indented
246 "let", "let*", "if" /*, "cond" NO! */ /*"mv-let", NO! */
247 , "er-let*", "assign", "theorem", "encapsulate"
248 };
249 Set<String> indentNameSet = new HashSet<String>(Arrays.asList(indentNames));
250
251
252 }