Lines 42-49
Link Here
|
42 |
package org.netbeans.modules.editor.impl; |
42 |
package org.netbeans.modules.editor.impl; |
43 |
|
43 |
|
44 |
import java.util.ArrayList; |
44 |
import java.util.ArrayList; |
45 |
import java.util.LinkedList; |
|
|
46 |
import java.util.List; |
45 |
import java.util.List; |
|
|
46 |
import java.util.concurrent.atomic.AtomicBoolean; |
47 |
import java.util.logging.Level; |
47 |
import java.util.logging.Level; |
48 |
import java.util.logging.Logger; |
48 |
import java.util.logging.Logger; |
49 |
import java.util.prefs.Preferences; |
49 |
import java.util.prefs.Preferences; |
Lines 54-59
Link Here
|
54 |
import javax.swing.undo.UndoableEdit; |
54 |
import javax.swing.undo.UndoableEdit; |
55 |
import org.netbeans.api.editor.mimelookup.MimeLookup; |
55 |
import org.netbeans.api.editor.mimelookup.MimeLookup; |
56 |
import org.netbeans.api.editor.mimelookup.MimePath; |
56 |
import org.netbeans.api.editor.mimelookup.MimePath; |
|
|
57 |
import org.netbeans.api.editor.mimelookup.MimeRegistration; |
57 |
import org.netbeans.api.editor.settings.SimpleValueNames; |
58 |
import org.netbeans.api.editor.settings.SimpleValueNames; |
58 |
import org.netbeans.editor.GuardedDocument; |
59 |
import org.netbeans.editor.GuardedDocument; |
59 |
import org.netbeans.editor.MarkBlock; |
60 |
import org.netbeans.editor.MarkBlock; |
Lines 62-71
Link Here
|
62 |
import org.netbeans.lib.editor.util.swing.DocumentUtilities; |
63 |
import org.netbeans.lib.editor.util.swing.DocumentUtilities; |
63 |
import org.netbeans.lib.editor.util.swing.PositionRegion; |
64 |
import org.netbeans.lib.editor.util.swing.PositionRegion; |
64 |
import org.netbeans.modules.editor.indent.api.Reformat; |
65 |
import org.netbeans.modules.editor.indent.api.Reformat; |
65 |
import org.netbeans.modules.editor.lib.BeforeSaveTasks; |
|
|
66 |
import org.netbeans.modules.editor.lib2.document.DocumentInternalUtils; |
66 |
import org.netbeans.modules.editor.lib2.document.DocumentInternalUtils; |
67 |
import org.netbeans.modules.editor.lib2.document.ModRootElement; |
67 |
import org.netbeans.modules.editor.lib2.document.ModRootElement; |
68 |
import org.netbeans.modules.editor.lib2.document.TrailingWhitespaceRemoveProcessor; |
68 |
import org.netbeans.spi.editor.document.OnSaveTask; |
69 |
import org.openide.util.Exceptions; |
69 |
import org.openide.util.Exceptions; |
70 |
|
70 |
|
71 |
/** |
71 |
/** |
Lines 73-267
Link Here
|
73 |
* |
73 |
* |
74 |
* @author Miloslav Metelka |
74 |
* @author Miloslav Metelka |
75 |
*/ |
75 |
*/ |
76 |
public class ReformatBeforeSaveTask implements BeforeSaveTasks.Task { |
76 |
public class ReformatBeforeSaveTask implements OnSaveTask { |
77 |
|
|
|
78 |
private static final ReformatBeforeSaveTask INSTANCE = new ReformatBeforeSaveTask(); |
79 |
|
80 |
public static void ensureRegistered() { |
81 |
if (BeforeSaveTasks.getTask(ReformatBeforeSaveTask.class) == null) { |
82 |
BeforeSaveTasks.addTask(INSTANCE); |
83 |
} |
84 |
} |
85 |
|
77 |
|
86 |
// -J-Dorg.netbeans.modules.editor.impl.ReformatAtSaveTask.level=FINE |
78 |
// -J-Dorg.netbeans.modules.editor.impl.ReformatAtSaveTask.level=FINE |
87 |
private static final Logger LOG = Logger.getLogger(ReformatBeforeSaveTask.class.getName()); |
79 |
private static final Logger LOG = Logger.getLogger(ReformatBeforeSaveTask.class.getName()); |
88 |
|
80 |
|
89 |
private ReformatBeforeSaveTask() { |
81 |
private final Document doc; |
|
|
82 |
|
83 |
private Reformat reformat; |
84 |
|
85 |
private boolean modifiedLinesOnly; |
86 |
|
87 |
private List<PositionRegion> guardedBlocks; |
88 |
|
89 |
private int guardedBlockIndex; |
90 |
|
91 |
private Position guardedBlockStartPos; |
92 |
|
93 |
private Position guardedBlockEndPos; |
94 |
|
95 |
private AtomicBoolean canceled = new AtomicBoolean(); |
96 |
|
97 |
ReformatBeforeSaveTask(Document doc) { |
98 |
this.doc = doc; |
90 |
} |
99 |
} |
91 |
|
100 |
|
92 |
@Override |
101 |
@Override |
93 |
public Object lock(Document doc) { |
102 |
public void performTask() { |
|
|
103 |
if (reformat != null) { |
104 |
reformat(); |
105 |
} |
106 |
} |
107 |
|
108 |
@Override |
109 |
public void runLocked(Runnable run) { |
94 |
Preferences prefs = MimeLookup.getLookup(DocumentUtilities.getMimeType(doc)).lookup(Preferences.class); |
110 |
Preferences prefs = MimeLookup.getLookup(DocumentUtilities.getMimeType(doc)).lookup(Preferences.class); |
95 |
if (prefs.getBoolean(SimpleValueNames.ON_SAVE_USE_GLOBAL_SETTINGS, Boolean.TRUE)) { |
111 |
if (prefs.getBoolean(SimpleValueNames.ON_SAVE_USE_GLOBAL_SETTINGS, Boolean.TRUE)) { |
96 |
prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class); |
112 |
prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class); |
97 |
} |
113 |
} |
98 |
String policy = prefs.get(SimpleValueNames.ON_SAVE_REFORMAT, "never"); //NOI18N |
114 |
String policy = prefs.get(SimpleValueNames.ON_SAVE_REFORMAT, "never"); //NOI18N |
99 |
if (!"never".equals(policy)) { //NOI18N |
115 |
if (!"never".equals(policy)) { //NOI18N |
100 |
Reformat reformat = Reformat.get(doc); |
116 |
modifiedLinesOnly = "modified-lines".equals(policy); |
|
|
117 |
reformat = Reformat.get(doc); |
101 |
reformat.lock(); |
118 |
reformat.lock(); |
102 |
LockInfo lockInfo = new LockInfo(reformat, "modified-lines".equals(policy)); |
119 |
try { |
103 |
return lockInfo; |
120 |
run.run(); |
104 |
} |
121 |
} finally { |
105 |
return null; |
122 |
reformat.unlock(); |
106 |
} |
123 |
} |
107 |
|
124 |
} else { |
108 |
@Override |
125 |
run.run(); |
109 |
public void run(Object lock, Document doc, UndoableEdit compoundEdit) { |
|
|
110 |
if (lock != null) { |
111 |
((LockInfo)lock).reformat(doc); |
112 |
} |
126 |
} |
113 |
} |
127 |
} |
114 |
|
128 |
|
115 |
@Override |
129 |
@Override |
116 |
public void unlock(Object lock, Document doc) { |
130 |
public boolean cancel() { |
117 |
if (lock != null) { |
131 |
canceled.set(true); |
118 |
((LockInfo)lock).unlock(); |
132 |
return true; |
|
|
133 |
} |
134 |
|
135 |
void reformat() { |
136 |
ModRootElement modRootElement = ModRootElement.get(doc); |
137 |
if (modRootElement != null) { |
138 |
boolean origEnabled = modRootElement.isEnabled(); |
139 |
modRootElement.setEnabled(false); |
140 |
try { |
141 |
// Read all guarded blocks |
142 |
guardedBlocks = new GapList<PositionRegion>(); |
143 |
if (doc instanceof GuardedDocument) { |
144 |
MarkBlock block = ((GuardedDocument) doc).getGuardedBlockChain().getChain(); |
145 |
while (block != null) { |
146 |
try { |
147 |
guardedBlocks.add(new PositionRegion(doc, block.getStartOffset(), block.getEndOffset())); |
148 |
} catch (BadLocationException ex) { |
149 |
Exceptions.printStackTrace(ex); |
150 |
} |
151 |
block = block.getNext(); |
152 |
} |
153 |
|
154 |
} |
155 |
|
156 |
guardedBlockIndex = 0; |
157 |
fetchNextGuardedBlock(); |
158 |
Element modRootOrDocElement = (modifiedLinesOnly) |
159 |
? modRootElement |
160 |
: DocumentInternalUtils.customElement(doc, 0, doc.getLength()); |
161 |
int modElementCount = modRootOrDocElement.getElementCount(); |
162 |
List<PositionRegion> formatBlocks = new ArrayList<PositionRegion>(modElementCount); |
163 |
for (int i = 0; i < modElementCount; i++) { |
164 |
if (canceled.get()) { |
165 |
return; |
166 |
} |
167 |
Element modElement = modRootOrDocElement.getElement(i); |
168 |
boolean modElementFinished; |
169 |
boolean add; |
170 |
int startOffset = modElement.getStartOffset(); |
171 |
int modElementEndOffset = modElement.getEndOffset(); |
172 |
int endOffset = modElementEndOffset; |
173 |
do { |
174 |
if (guardedBlockStartPos != null) { |
175 |
BlockCompare blockCompare = BlockCompare.get( |
176 |
startOffset, |
177 |
endOffset, |
178 |
guardedBlockStartPos.getOffset(), |
179 |
guardedBlockEndPos.getOffset()); |
180 |
if (blockCompare.before()) { |
181 |
add = true; |
182 |
modElementFinished = true; |
183 |
} else if (blockCompare.after()) { |
184 |
fetchNextGuardedBlock(); |
185 |
add = false; |
186 |
modElementFinished = false; |
187 |
} else if (blockCompare.equal()) { |
188 |
fetchNextGuardedBlock(); |
189 |
add = false; |
190 |
modElementFinished = true; |
191 |
} else if (blockCompare.overlapStart()) { |
192 |
endOffset = guardedBlockStartPos.getOffset(); |
193 |
add = true; |
194 |
modElementFinished = true; |
195 |
} else if (blockCompare.overlapEnd()) { |
196 |
// Skip part covered by guarded block |
197 |
endOffset = guardedBlockEndPos.getOffset(); |
198 |
fetchNextGuardedBlock(); |
199 |
add = false; |
200 |
modElementFinished = false; |
201 |
} else if (blockCompare.contains()) { |
202 |
endOffset = guardedBlockStartPos.getOffset(); |
203 |
add = true; |
204 |
modElementFinished = false; |
205 |
} else if (blockCompare.inside()) { |
206 |
add = false; |
207 |
modElementFinished = true; |
208 |
} else { |
209 |
LOG.info("Unexpected blockCompare=" + blockCompare); |
210 |
add = false; |
211 |
modElementFinished = true; |
212 |
} |
213 |
} else { |
214 |
add = true; |
215 |
modElementFinished = true; |
216 |
} |
217 |
if (add) { |
218 |
try { |
219 |
if (startOffset != endOffset) { |
220 |
PositionRegion block = new PositionRegion(doc, startOffset, endOffset); |
221 |
if (LOG.isLoggable(Level.FINE)) { |
222 |
LOG.fine("Reformat-at-save: add block=" + block); |
223 |
} |
224 |
formatBlocks.add(block); |
225 |
} |
226 |
} catch (BadLocationException ex) { |
227 |
Exceptions.printStackTrace(ex); |
228 |
} |
229 |
} |
230 |
startOffset = endOffset; |
231 |
endOffset = modElementEndOffset; |
232 |
} while (!modElementFinished); |
233 |
} |
234 |
|
235 |
try { |
236 |
for (PositionRegion block : formatBlocks) { |
237 |
if (canceled.get()) { |
238 |
return; |
239 |
} |
240 |
reformat.reformat(block.getStartOffset(), block.getEndOffset()); |
241 |
} |
242 |
} catch (Exception ex) { |
243 |
Exceptions.printStackTrace(ex); |
244 |
} |
245 |
|
246 |
} finally { |
247 |
modRootElement.setEnabled(origEnabled); |
248 |
} |
119 |
} |
249 |
} |
120 |
} |
250 |
} |
121 |
|
251 |
|
122 |
private static final class LockInfo { |
252 |
private void fetchNextGuardedBlock() { |
123 |
|
253 |
if (guardedBlockIndex < guardedBlocks.size()) { |
124 |
final Reformat reformat; |
254 |
PositionRegion guardedBlock = guardedBlocks.get(guardedBlockIndex++); |
|
|
255 |
guardedBlockStartPos = guardedBlock.getStartPosition(); |
256 |
guardedBlockEndPos = guardedBlock.getEndPosition(); |
257 |
} else { |
258 |
guardedBlockEndPos = guardedBlockStartPos = null; |
259 |
} |
260 |
} |
125 |
|
261 |
|
126 |
final boolean modifiedLinesOnly; |
262 |
@MimeRegistration(mimeType="", service=OnSaveTask.Factory.class, position=500) |
127 |
|
263 |
public static final class FactoryImpl implements Factory { |
128 |
List<PositionRegion> guardedBlocks; |
|
|
129 |
|
264 |
|
130 |
int guardedBlockIndex; |
265 |
@Override |
131 |
|
266 |
public OnSaveTask createTask(Context context) { |
132 |
Position guardedBlockStartPos; |
267 |
return new ReformatBeforeSaveTask(context.getDocument()); |
133 |
|
|
|
134 |
Position guardedBlockEndPos; |
135 |
|
136 |
|
137 |
public LockInfo(Reformat reformat, boolean modifiedLinesOnly) { |
138 |
this.reformat = reformat; |
139 |
this.modifiedLinesOnly = modifiedLinesOnly; |
140 |
} |
141 |
|
142 |
void reformat(Document doc) { |
143 |
ModRootElement modRootElement = ModRootElement.get(doc); |
144 |
if (modRootElement != null) { |
145 |
boolean origEnabled = modRootElement.isEnabled(); |
146 |
modRootElement.setEnabled(false); |
147 |
try { |
148 |
// Read all guarded blocks |
149 |
guardedBlocks = new GapList<PositionRegion>(); |
150 |
if (doc instanceof GuardedDocument) { |
151 |
MarkBlock block = ((GuardedDocument)doc).getGuardedBlockChain().getChain(); |
152 |
while (block != null) { |
153 |
try { |
154 |
guardedBlocks.add(new PositionRegion(doc, block.getStartOffset(), block.getEndOffset())); |
155 |
} catch (BadLocationException ex) { |
156 |
Exceptions.printStackTrace(ex); |
157 |
} |
158 |
block = block.getNext(); |
159 |
} |
160 |
|
161 |
} |
162 |
|
163 |
guardedBlockIndex = 0; |
164 |
fetchNextGuardedBlock(); |
165 |
Element modRootOrDocElement = (modifiedLinesOnly) |
166 |
? modRootElement |
167 |
: DocumentInternalUtils.customElement(doc, 0, doc.getLength()); |
168 |
int modElementCount = modRootOrDocElement.getElementCount(); |
169 |
List<PositionRegion> formatBlocks = new ArrayList<PositionRegion>(modElementCount); |
170 |
for (int i = 0; i < modElementCount; i++) { |
171 |
Element modElement = modRootOrDocElement.getElement(i); |
172 |
boolean modElementFinished; |
173 |
boolean add; |
174 |
int startOffset = modElement.getStartOffset(); |
175 |
int modElementEndOffset = modElement.getEndOffset(); |
176 |
int endOffset = modElementEndOffset; |
177 |
do { |
178 |
if (guardedBlockStartPos != null) { |
179 |
BlockCompare blockCompare = BlockCompare.get( |
180 |
startOffset, |
181 |
endOffset, |
182 |
guardedBlockStartPos.getOffset(), |
183 |
guardedBlockEndPos.getOffset()); |
184 |
if (blockCompare.before()) { |
185 |
add = true; |
186 |
modElementFinished = true; |
187 |
} else if (blockCompare.after()) { |
188 |
fetchNextGuardedBlock(); |
189 |
add = false; |
190 |
modElementFinished = false; |
191 |
} else if (blockCompare.equal()) { |
192 |
fetchNextGuardedBlock(); |
193 |
add = false; |
194 |
modElementFinished = true; |
195 |
} else if (blockCompare.overlapStart()) { |
196 |
endOffset = guardedBlockStartPos.getOffset(); |
197 |
add = true; |
198 |
modElementFinished = true; |
199 |
} else if (blockCompare.overlapEnd()) { |
200 |
// Skip part covered by guarded block |
201 |
endOffset = guardedBlockEndPos.getOffset(); |
202 |
fetchNextGuardedBlock(); |
203 |
add = false; |
204 |
modElementFinished = false; |
205 |
} else if (blockCompare.contains()) { |
206 |
endOffset = guardedBlockStartPos.getOffset(); |
207 |
add = true; |
208 |
modElementFinished = false; |
209 |
} else if (blockCompare.inside()) { |
210 |
add = false; |
211 |
modElementFinished = true; |
212 |
} else { |
213 |
LOG.info("Unexpected blockCompare=" + blockCompare); |
214 |
add = false; |
215 |
modElementFinished = true; |
216 |
} |
217 |
} else { |
218 |
add = true; |
219 |
modElementFinished = true; |
220 |
} |
221 |
if (add) { |
222 |
try { |
223 |
if (startOffset != endOffset) { |
224 |
PositionRegion block = new PositionRegion(doc, startOffset, endOffset); |
225 |
if (LOG.isLoggable(Level.FINE)) { |
226 |
LOG.fine("Reformat-at-save: add block=" + block); |
227 |
} |
228 |
formatBlocks.add(block); |
229 |
} |
230 |
} catch (BadLocationException ex) { |
231 |
Exceptions.printStackTrace(ex); |
232 |
} |
233 |
} |
234 |
startOffset = endOffset; |
235 |
endOffset = modElementEndOffset; |
236 |
} while (!modElementFinished); |
237 |
} |
238 |
|
239 |
try { |
240 |
for (PositionRegion block : formatBlocks) { |
241 |
reformat.reformat(block.getStartOffset(), block.getEndOffset()); |
242 |
} |
243 |
} catch (Exception ex) { |
244 |
Exceptions.printStackTrace(ex); |
245 |
} |
246 |
|
247 |
} finally { |
248 |
modRootElement.setEnabled(origEnabled); |
249 |
} |
250 |
} |
251 |
} |
252 |
|
253 |
private void fetchNextGuardedBlock() { |
254 |
if (guardedBlockIndex < guardedBlocks.size()) { |
255 |
PositionRegion guardedBlock = guardedBlocks.get(guardedBlockIndex++); |
256 |
guardedBlockStartPos = guardedBlock.getStartPosition(); |
257 |
guardedBlockEndPos = guardedBlock.getEndPosition(); |
258 |
} else { |
259 |
guardedBlockEndPos = guardedBlockStartPos = null; |
260 |
} |
261 |
} |
262 |
|
263 |
void unlock() { |
264 |
reformat.unlock(); |
265 |
} |
268 |
} |
266 |
|
269 |
|
267 |
} |
270 |
} |