http://ea.jetbrains.com/browser/ea_problems/17317
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInsight / editorActions / JoinLinesHandler.java
blob3e10c20828f7abd3ea448ab99137347b75d2c78f
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Created by IntelliJ IDEA.
19 * User: max
20 * Date: May 20, 2002
21 * Time: 6:21:42 PM
22 * To change template for new class use
23 * Code Style | Class Templates options (Tools | IDE Options).
25 package com.intellij.codeInsight.editorActions;
27 import com.intellij.ide.DataManager;
28 import com.intellij.openapi.actionSystem.DataContext;
29 import com.intellij.openapi.actionSystem.PlatformDataKeys;
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.editor.Editor;
32 import com.intellij.openapi.editor.LogicalPosition;
33 import com.intellij.openapi.editor.ScrollType;
34 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
35 import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
36 import com.intellij.openapi.editor.ex.DocumentEx;
37 import com.intellij.openapi.extensions.Extensions;
38 import com.intellij.openapi.project.Project;
39 import com.intellij.psi.PsiComment;
40 import com.intellij.psi.PsiDocumentManager;
41 import com.intellij.psi.PsiElement;
42 import com.intellij.psi.PsiFile;
43 import com.intellij.psi.codeStyle.CodeStyleManager;
44 import com.intellij.psi.util.PsiTreeUtil;
45 import com.intellij.util.IncorrectOperationException;
47 public class JoinLinesHandler extends EditorWriteActionHandler {
48 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.editorActions.JoinLinesHandler");
49 private final EditorActionHandler myOriginalHandler;
51 public JoinLinesHandler(EditorActionHandler originalHandler) {
52 myOriginalHandler = originalHandler;
55 public void executeWriteAction(final Editor editor, final DataContext dataContext) {
56 if (!(editor.getDocument() instanceof DocumentEx)) {
57 myOriginalHandler.execute(editor, dataContext);
58 return;
60 final DocumentEx doc = (DocumentEx)editor.getDocument();
61 final Project project = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(editor.getContentComponent()));
63 LogicalPosition caretPosition = editor.getCaretModel().getLogicalPosition();
65 final PsiDocumentManager docManager = PsiDocumentManager.getInstance(project);
66 PsiFile psiFile = docManager.getPsiFile(doc);
68 if (psiFile == null) {
69 myOriginalHandler.execute(editor, dataContext);
70 return;
73 int startLine = caretPosition.line;
74 int endLine = startLine + 1;
75 if (editor.getSelectionModel().hasSelection()) {
76 startLine = doc.getLineNumber(editor.getSelectionModel().getSelectionStart());
77 endLine = doc.getLineNumber(editor.getSelectionModel().getSelectionEnd());
78 if (doc.getLineStartOffset(endLine) == editor.getSelectionModel().getSelectionEnd()) endLine--;
81 int caretRestoreOffset = -1;
82 for (int i = startLine; i < endLine; i++) {
83 if (i >= doc.getLineCount() - 1) break;
84 int lineEndOffset = doc.getLineEndOffset(startLine);
86 docManager.commitDocument(doc);
87 CharSequence text = doc.getCharsSequence();
88 int firstNonSpaceOffsetInNextLine = doc.getLineStartOffset(startLine + 1);
89 while (firstNonSpaceOffsetInNextLine < text.length() - 1 && (text.charAt(firstNonSpaceOffsetInNextLine) == ' ' || text.charAt(firstNonSpaceOffsetInNextLine) == '\t')) {
90 firstNonSpaceOffsetInNextLine++;
92 PsiElement elementAtNextLineStart = psiFile.findElementAt(firstNonSpaceOffsetInNextLine);
93 boolean isNextLineStartsWithComment = isCommentElement(elementAtNextLineStart);
95 int lastNonSpaceOffsetInStartLine = lineEndOffset;
96 while (lastNonSpaceOffsetInStartLine > 0 &&
97 (text.charAt(lastNonSpaceOffsetInStartLine - 1) == ' ' || text.charAt(lastNonSpaceOffsetInStartLine - 1) == '\t')) {
98 lastNonSpaceOffsetInStartLine--;
100 int elemOffset = lastNonSpaceOffsetInStartLine > doc.getLineStartOffset(startLine) ? lastNonSpaceOffsetInStartLine - 1 : -1;
101 PsiElement elementAtStartLineEnd = elemOffset == -1 ? null : psiFile.findElementAt(elemOffset);
102 boolean isStartLineEndsWithComment = isCommentElement(elementAtStartLineEnd);
104 if (lastNonSpaceOffsetInStartLine == doc.getLineStartOffset(startLine)) {
105 doc.deleteString(doc.getLineStartOffset(startLine), firstNonSpaceOffsetInNextLine);
107 int indent = -1;
108 try {
109 docManager.commitDocument(doc);
110 indent = CodeStyleManager.getInstance(project).adjustLineIndent(psiFile, startLine == 0 ? 0 : doc.getLineStartOffset(startLine));
112 catch (IncorrectOperationException e) {
113 LOG.error(e);
116 if (caretRestoreOffset == -1) {
117 caretRestoreOffset = indent;
120 continue;
123 doc.deleteString(lineEndOffset, lineEndOffset + doc.getLineSeparatorLength(startLine));
125 text = doc.getCharsSequence();
126 int start = lineEndOffset - 1;
127 int end = lineEndOffset;
128 while (start > 0 && (text.charAt(start) == ' ' || text.charAt(start) == '\t')) start--;
129 while (end < doc.getTextLength() && (text.charAt(end) == ' ' || text.charAt(end) == '\t')) end++;
131 // Check if we're joining splitted string literal.
132 docManager.commitDocument(doc);
135 int rc = -1;
136 for(JoinLinesHandlerDelegate delegate: Extensions.getExtensions(JoinLinesHandlerDelegate.EP_NAME)) {
137 rc = delegate.tryJoinLines(doc, psiFile, start, end);
138 if (rc != -1) break;
140 docManager.doPostponedOperationsAndUnblockDocument(doc);
142 if (rc != -1) {
143 if (caretRestoreOffset == -1) caretRestoreOffset = rc;
144 continue;
147 if (caretRestoreOffset == -1) caretRestoreOffset = start == lineEndOffset ? start : start + 1;
150 if (isStartLineEndsWithComment && isNextLineStartsWithComment) {
151 if (text.charAt(end) == '*' && end < text.length() && text.charAt(end + 1) != '/') {
152 end++;
153 while (end < doc.getTextLength() && (text.charAt(end) == ' ' || text.charAt(end) == '\t')) end++;
155 else if (text.charAt(end) == '/') {
156 end += 2;
157 while (end < doc.getTextLength() && (text.charAt(end) == ' ' || text.charAt(end) == '\t')) end++;
160 doc.replaceString(start == lineEndOffset ? start : start + 1, end, " ");
161 continue;
164 while (end < doc.getTextLength() && (text.charAt(end) == ' ' || text.charAt(end) == '\t')) end++;
165 doc.replaceString(start == lineEndOffset ? start : start + 1, end, " ");
167 if (start <= doc.getLineStartOffset(startLine)) {
168 try {
169 docManager.commitDocument(doc);
170 CodeStyleManager.getInstance(project).adjustLineIndent(psiFile, doc.getLineStartOffset(startLine));
172 catch (IncorrectOperationException e) {
173 LOG.error(e);
177 int prevLineCount = doc.getLineCount();
179 docManager.commitDocument(doc);
180 try {
181 CodeStyleManager.getInstance(project).reformatText(psiFile, start + 1, end);
183 catch (IncorrectOperationException e) {
184 LOG.error(e);
187 if (prevLineCount < doc.getLineCount()) {
188 docManager.doPostponedOperationsAndUnblockDocument(doc);
189 end = doc.getLineEndOffset(startLine) + doc.getLineSeparatorLength(startLine);
190 start = end - doc.getLineSeparatorLength(startLine);
191 int addedLinesCount = doc.getLineCount() - prevLineCount - 1;
192 while (end < doc.getTextLength() &&
193 (text.charAt(end) == ' ' || text.charAt(end) == '\t' || text.charAt(end) == '\n' && addedLinesCount > 0)) {
194 if (text.charAt(end) == '\n') addedLinesCount--;
195 end++;
197 doc.replaceString(start, end, " ");
200 docManager.commitDocument(doc);
203 if (editor.getSelectionModel().hasSelection()) {
204 editor.getCaretModel().moveToOffset(editor.getSelectionModel().getSelectionEnd());
206 else if (caretRestoreOffset != -1) {
207 editor.getCaretModel().moveToOffset(caretRestoreOffset);
208 editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
209 editor.getSelectionModel().removeSelection();
213 private static boolean isCommentElement(final PsiElement element) {
214 return element != null && PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null;