/
DocumentAutoFormatter.java
161 lines (140 loc) · 5.04 KB
/
DocumentAutoFormatter.java
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
159
160
161
/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2016 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.sarl.lang.ui.editor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.formatter.IContentFormatter;
import org.eclipse.xtext.ui.editor.model.IXtextDocument;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.xbase.lib.Exceptions;
/** A service that enables to do auto-formatting when a document changed.
*
* <p>FIXME: Remove if Xtext accept the patch https://github.com/eclipse/xtext-eclipse/pull/63
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public class DocumentAutoFormatter implements IDocumentAutoFormatter {
private Collection<RegionFormattingRequest> formattingRequests = Collections.synchronizedList(new ArrayList<>(1));
private IDocumentListener autoFormatListener;
private IXtextDocument document;
private IContentFormatter contentFormatter;
@Override
public void bind(IXtextDocument document, IContentFormatter contentFormatter) {
assert document != null;
assert contentFormatter != null;
this.document = document;
this.contentFormatter = contentFormatter;
}
@Override
public synchronized void beginAutoFormat() {
if (this.document != null && this.autoFormatListener == null) {
this.formattingRequests.clear();
this.autoFormatListener = new IDocumentListener() {
@Override
public void documentAboutToBeChanged(DocumentEvent event) {
//
}
@SuppressWarnings("synthetic-access")
@Override
public void documentChanged(DocumentEvent event) {
if (!Strings.isEmpty(event.getText())
&& event.getDocument() instanceof IXtextDocument) {
DocumentAutoFormatter.this.formattingRequests.add(new RegionFormattingRequest(
(IXtextDocument) event.getDocument(), event.getOffset(), event.getText().length()));
}
}
};
this.document.addDocumentListener(this.autoFormatListener);
}
}
@Override
public void endAutoFormat() {
final Collection<RegionFormattingRequest> requests;
synchronized (this) {
requests = this.formattingRequests;
this.formattingRequests = Collections.synchronizedList(new ArrayList<>(1));
if (this.autoFormatListener != null) {
final IDocumentListener listener = this.autoFormatListener;
this.autoFormatListener = null;
if (this.document != null) {
this.document.removeDocumentListener(listener);
}
}
}
if (this.contentFormatter != null) {
for (final RegionFormattingRequest request : requests) {
formatRegion(request.document, request.offset, request.length);
}
}
}
/** Called for formatting a region.
*
* @param document the document to format.
* @param offset the offset of the text to format.
* @param length the length of the text.
*/
protected void formatRegion(IXtextDocument document, int offset, int length) {
try {
final int startRegionOffset = document.getLineInformationOfOffset(
previousSiblingChar(document, offset)).getOffset();
final IRegion endLine = document.getLineInformationOfOffset(offset + length);
final int endRegionOffset = endLine.getOffset() + endLine.getLength();
final int regionLength = endRegionOffset - startRegionOffset;
for (final IRegion region : document.computePartitioning(startRegionOffset, regionLength)) {
this.contentFormatter.format(document, region);
}
} catch (BadLocationException exception) {
Exceptions.sneakyThrow(exception);
}
}
private static int previousSiblingChar(IXtextDocument document, int offset) throws BadLocationException {
int off = offset - 1;
while (off >= 0 && Character.isWhitespace(document.getChar(off))) {
--off;
}
return off;
}
/** Request for formatting a region.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("checkstyle:visibilitymodifier")
private static class RegionFormattingRequest {
public final IXtextDocument document;
public final int offset;
public final int length;
RegionFormattingRequest(IXtextDocument document, int offset, int length) {
this.document = document;
this.offset = offset;
this.length = length;
}
}
}