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.
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
);
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
);
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
);
109 docManager
.commitDocument(doc
);
110 indent
= CodeStyleManager
.getInstance(project
).adjustLineIndent(psiFile
, startLine
== 0 ?
0 : doc
.getLineStartOffset(startLine
));
112 catch (IncorrectOperationException e
) {
116 if (caretRestoreOffset
== -1) {
117 caretRestoreOffset
= indent
;
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
);
136 for(JoinLinesHandlerDelegate delegate
: Extensions
.getExtensions(JoinLinesHandlerDelegate
.EP_NAME
)) {
137 rc
= delegate
.tryJoinLines(doc
, psiFile
, start
, end
);
140 docManager
.doPostponedOperationsAndUnblockDocument(doc
);
143 if (caretRestoreOffset
== -1) caretRestoreOffset
= rc
;
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) != '/') {
153 while (end
< doc
.getTextLength() && (text
.charAt(end
) == ' ' || text
.charAt(end
) == '\t')) end
++;
155 else if (text
.charAt(end
) == '/') {
157 while (end
< doc
.getTextLength() && (text
.charAt(end
) == ' ' || text
.charAt(end
) == '\t')) end
++;
160 doc
.replaceString(start
== lineEndOffset ? start
: start
+ 1, end
, " ");
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
)) {
169 docManager
.commitDocument(doc
);
170 CodeStyleManager
.getInstance(project
).adjustLineIndent(psiFile
, doc
.getLineStartOffset(startLine
));
172 catch (IncorrectOperationException e
) {
177 int prevLineCount
= doc
.getLineCount();
179 docManager
.commitDocument(doc
);
181 CodeStyleManager
.getInstance(project
).reformatText(psiFile
, start
+ 1, end
);
183 catch (IncorrectOperationException 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
--;
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;