mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-23 17:05:26 +02:00
Bug 478938 - Automatically add source files to Qt Project File
Added basic content assist for built-in qmake variables since there are so many of them. Added a bunch of new classes dealing with basic parsing and modification of Qt Project Files. For now the parser is a simple regular expression matcher that finds instances of variables. The modification class supports preservation of indentation and comments that are tied to variables in the project file. Change-Id: I0539458d5c1cf29e6c9c1246e4e717e7cbec1b84 Signed-off-by: Matthew Bastien <mbastien@blackberry.com>
This commit is contained in:
parent
43da8ab7ac
commit
6c3f10576b
14 changed files with 1759 additions and 3 deletions
|
@ -0,0 +1,340 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 QNX Software Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.qt.pro.parser.tests;
|
||||
|
||||
import org.eclipse.cdt.internal.qt.ui.pro.parser.QtProjectFileModifier;
|
||||
import org.eclipse.jface.text.Document;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.junit.Test;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class QtProjectFileModifierTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void test_ReplaceValue_SingleValue() {
|
||||
IDocument document = new Document("SOURCES += main.cpp"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
|
||||
assertTrue(modifier.replaceVariableValue("SOURCES", "main.cpp", "main2.cpp")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
assertEquals("SOURCES += main2.cpp", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ReplaceValue_HasCommentOnMainLine() {
|
||||
IDocument document = new Document("SOURCES += main.cpp # This is a comment"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
|
||||
assertTrue(modifier.replaceVariableValue("SOURCES", "main.cpp", "main2.cpp")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
assertEquals("SOURCES += main2.cpp # This is a comment", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ReplaceValue_HasCommentOnSubsequentLine() {
|
||||
IDocument document = new Document(
|
||||
"SOURCES += main.cpp \\ # This is a comment\n" //$NON-NLS-1$
|
||||
+ " main2.cpp # This is a comment"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
|
||||
assertTrue(modifier.replaceVariableValue("SOURCES", "main2.cpp", "main3.cpp")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\ # This is a comment\n" //$NON-NLS-1$
|
||||
+ " main3.cpp # This is a comment", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ReplaceValue_MatchWholeLineFalse() {
|
||||
IDocument document = new Document("CONFIG = qt debug"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
|
||||
assertTrue(modifier.replaceVariableValue("CONFIG", "debug", "console", false)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
assertEquals(
|
||||
"CONFIG = qt console", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ReplaceValue_DoesNotExist() {
|
||||
IDocument document = new Document("CONFIG = qt debug"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
|
||||
assertFalse(modifier.replaceVariableValue("CONFIG", "console", "debug", false)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
assertEquals(
|
||||
"CONFIG = qt debug", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ReplaceMultilineValue_MatchWholeLineFalse() {
|
||||
IDocument document = new Document(
|
||||
"CONFIG = qt \\\n" //$NON-NLS-1$
|
||||
+ " debug"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
|
||||
assertTrue(modifier.replaceVariableValue("CONFIG", "debug", "console", false)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
assertEquals(
|
||||
"CONFIG = qt \\\n" //$NON-NLS-1$
|
||||
+ " console", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ReplaceMultilineValue() {
|
||||
IDocument document = new Document(
|
||||
"SOURCES += main.cpp \\\n" //$NON-NLS-1$
|
||||
+ " main2.cpp"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
|
||||
assertTrue(modifier.replaceVariableValue("SOURCES", "main2.cpp", "main3.cpp")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\\n" //$NON-NLS-1$
|
||||
+ " main3.cpp", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ReplaceMultilineValue_HasComment() {
|
||||
IDocument document = new Document(
|
||||
"SOURCES += main.cpp \\\n" //$NON-NLS-1$
|
||||
+ " main2.cpp # This is a comment"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
|
||||
assertTrue(modifier.replaceVariableValue("SOURCES", "main2.cpp", "main3.cpp")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\\n" //$NON-NLS-1$
|
||||
+ " main3.cpp # This is a comment", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue() {
|
||||
IDocument document = new Document("SOURCES += main.cpp"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main2.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\\n" //$NON-NLS-1$
|
||||
+ " main2.cpp", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue_NoIndentation() {
|
||||
IDocument document = new Document(
|
||||
"SOURCES += main.cpp \\\n" //$NON-NLS-1$
|
||||
+ "noindent.cpp"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main2.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\\n" //$NON-NLS-1$
|
||||
+ "noindent.cpp \\\n" //$NON-NLS-1$
|
||||
+ "main2.cpp", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue_AlreadyExists() {
|
||||
IDocument document = new Document("SOURCES += main.cpp"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals("SOURCES += main.cpp", document.get()); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue_HasCommentOnMainLine() {
|
||||
IDocument document = new Document("SOURCES += main.cpp # This is a comment"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main2.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\ # This is a comment\n" //$NON-NLS-1$
|
||||
+ " main2.cpp", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue_HasCommentOnSubsequentLine() {
|
||||
IDocument document = new Document(
|
||||
"SOURCES += main.cpp \\ # This is a comment \n" //$NON-NLS-1$
|
||||
+ " main2.cpp # this is a comment\n\n"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main3.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\ # This is a comment \n" //$NON-NLS-1$
|
||||
+ " main2.cpp \\ # this is a comment\n" //$NON-NLS-1$
|
||||
+ " main3.cpp\n\n", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue_CommentIndentation() {
|
||||
IDocument document = new Document(
|
||||
"SOURCES += main.cpp \\ # Test comment\n" //$NON-NLS-1$
|
||||
+ " main2.cpp \\ # Test comment2\n" //$NON-NLS-1$
|
||||
+ " main3.cpp # Test comment3"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main4.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\ # Test comment\n" //$NON-NLS-1$
|
||||
+ " main2.cpp \\ # Test comment2\n" //$NON-NLS-1$
|
||||
+ " main3.cpp \\ # Test comment3\n" //$NON-NLS-1$
|
||||
+ " main4.cpp", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue_MultipleVariables() {
|
||||
IDocument document = new Document(
|
||||
"SOURCES += main.cpp\n" //$NON-NLS-1$
|
||||
+ "\n" //$NON-NLS-1$
|
||||
+ "QT = app"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main2.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\\n" + //$NON-NLS-1$
|
||||
" main2.cpp\n" + //$NON-NLS-1$
|
||||
"\n" + //$NON-NLS-1$
|
||||
"QT = app", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue_EmptyDocument() {
|
||||
IDocument document = new Document("\t \n\n\t\n\n\n\n"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp\n", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue_VariableDoesNotExist() {
|
||||
IDocument document = new Document("CONFIG += qt debug"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"CONFIG += qt debug\n" //$NON-NLS-1$
|
||||
+ "\n" //$NON-NLS-1$
|
||||
+ "SOURCES += main.cpp\n", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue_VariableDoesNotExist2() {
|
||||
IDocument document = new Document("CONFIG += qt debug\n"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"CONFIG += qt debug\n" //$NON-NLS-1$
|
||||
+ "\n" //$NON-NLS-1$
|
||||
+ "SOURCES += main.cpp\n", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AddValue_VariableDoesNotExist3() {
|
||||
IDocument document = new Document("CONFIG += qt debug\n\n"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.addVariableValue("SOURCES", "main.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"CONFIG += qt debug\n" //$NON-NLS-1$
|
||||
+ "\n" //$NON-NLS-1$
|
||||
+ "\n" //$NON-NLS-1$
|
||||
+ "SOURCES += main.cpp\n", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_RemoveThenAddValue() {
|
||||
IDocument document = new Document(
|
||||
"SOURCES += main.cpp \\\n" //$NON-NLS-1$
|
||||
+ " main2.cpp \\\n" //$NON-NLS-1$
|
||||
+ " main3.cpp\n"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.removeVariableValue("SOURCES", "main3.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
modifier.addVariableValue("SOURCES", "main4.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\\n" //$NON-NLS-1$
|
||||
+ " main2.cpp \\\n" //$NON-NLS-1$
|
||||
+ " main4.cpp\n", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_RemoveValue_FirstLine() {
|
||||
IDocument document = new Document(
|
||||
"SOURCES += main.cpp \\ # Test comment\n" //$NON-NLS-1$
|
||||
+ " main2.cpp \\ # Test comment2\n" //$NON-NLS-1$
|
||||
+ " main3.cpp \\ # Test comment3\n" //$NON-NLS-1$
|
||||
+ " main4.cpp # Test comment4"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.removeVariableValue("SOURCES", "main.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"SOURCES += main2.cpp \\ # Test comment2\n" //$NON-NLS-1$
|
||||
+ " main3.cpp \\ # Test comment3\n" //$NON-NLS-1$
|
||||
+ " main4.cpp # Test comment4", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_RemoveValue_MiddleLine() {
|
||||
IDocument document = new Document(
|
||||
"SOURCES += main.cpp \\ # Test comment\n" //$NON-NLS-1$
|
||||
+ " main2.cpp \\ # Test comment2\n" //$NON-NLS-1$
|
||||
+ " main3.cpp \\ # Test comment3\n" //$NON-NLS-1$
|
||||
+ " main4.cpp # Test comment4"); //$NON-NLS-1$
|
||||
|
||||
QtProjectFileModifier modifier = new QtProjectFileModifier(document);
|
||||
modifier.removeVariableValue("SOURCES", "main2.cpp"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
assertEquals(
|
||||
"SOURCES += main.cpp \\ # Test comment\n" //$NON-NLS-1$
|
||||
+ " main3.cpp \\ # Test comment3\n" //$NON-NLS-1$
|
||||
+ " main4.cpp # Test comment4", //$NON-NLS-1$
|
||||
document.get());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 QNX Software Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.qt.pro.parser.tests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.cdt.internal.qt.ui.pro.parser.QtProjectFileParser;
|
||||
import org.eclipse.cdt.internal.qt.ui.pro.parser.QtProjectVariable;
|
||||
import org.eclipse.jface.text.Document;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.junit.Test;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class QtProjectFileParserTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void test_AssignmentOperator_Equals() {
|
||||
IDocument document = new Document("SOURCES = main.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
List<QtProjectVariable> variables = parser.getAllVariables();
|
||||
assertFalse("Unable to parse variable", variables.isEmpty()); //$NON-NLS-1$
|
||||
assertEquals("Invalid assignment operator", "=", variables.get(0).getAssignmentOperator()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AssignmentOperator_PlusEquals() {
|
||||
IDocument document = new Document("SOURCES += main.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
List<QtProjectVariable> variables = parser.getAllVariables();
|
||||
assertFalse("Unable to parse variable", variables.isEmpty()); //$NON-NLS-1$
|
||||
assertEquals("Invalid assignment operator", "+=", variables.get(0).getAssignmentOperator()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AssignmentOperator_MinusEquals() {
|
||||
IDocument document = new Document("SOURCES -= main.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
List<QtProjectVariable> variables = parser.getAllVariables();
|
||||
assertFalse("Unable to parse variable", variables.isEmpty()); //$NON-NLS-1$
|
||||
assertEquals("Invalid assignment operator", "-=", variables.get(0).getAssignmentOperator()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AssignmentOperator_AsterixEquals() {
|
||||
IDocument document = new Document("SOURCES *= main.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
List<QtProjectVariable> variables = parser.getAllVariables();
|
||||
assertFalse("Unable to parse variable", variables.isEmpty()); //$NON-NLS-1$
|
||||
assertEquals("Invalid assignment operator", "*=", variables.get(0).getAssignmentOperator()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_CommentedVariable() {
|
||||
IDocument document = new Document("# SOURCES += main.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
assertTrue("Found variable even though it was commented", parser.getAllVariables().isEmpty()); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_CommentedVariable2() {
|
||||
IDocument document = new Document("SOURCES # += main.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
assertTrue("Found variable even though it was commented", parser.getAllVariables().isEmpty()); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MalformedVariable() {
|
||||
IDocument document = new Document("MY VARIABLE # += main.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
assertTrue("Found variable even though it was malformed", parser.getAllVariables().isEmpty()); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MalformedVariable2() {
|
||||
IDocument document = new Document("\\SOURCES # += main.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
assertTrue("Found variable even though it was malformed", parser.getAllVariables().isEmpty()); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_FullyQualifiedName() {
|
||||
IDocument document = new Document("fully.qualified.Name += main.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
QtProjectVariable sources = parser.getVariable("fully.qualified.Name"); //$NON-NLS-1$
|
||||
assertNotNull("Unable to parse variable", sources); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SingleLineVariable() {
|
||||
IDocument document = new Document("SOURCES += main.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
QtProjectVariable sources = parser.getVariable("SOURCES"); //$NON-NLS-1$
|
||||
assertNotNull("Unable to parse variable", sources); //$NON-NLS-1$
|
||||
assertTrue("Unable to parse \"main.cpp\" from SOURCES variable", sources.getValueIndex("main.cpp") == 0); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SingleLineVariable_MultipleValues() {
|
||||
IDocument document = new Document("CONFIG += qt debug"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
QtProjectVariable sources = parser.getVariable("CONFIG"); //$NON-NLS-1$
|
||||
assertNotNull("Unable to parse variable", sources); //$NON-NLS-1$
|
||||
assertTrue("Unable to parse \"qt debug\" from SOURCES variable", sources.getValueIndex("qt debug") == 0); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_VariableWithComment() {
|
||||
IDocument document = new Document("SOURCES += main.cpp # this is a comment\n"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
QtProjectVariable sources = parser.getVariable("SOURCES"); //$NON-NLS-1$
|
||||
assertNotNull("Unable to parse variable", sources); //$NON-NLS-1$
|
||||
assertEquals("Unable to parse assignment from SOURCES variable", "+=", sources.getAssignmentOperator()); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
assertTrue("Unable to parse \"main.cpp\" from SOURCES variable", sources.getValueIndex("main.cpp") == 0); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MultilineVariable() {
|
||||
IDocument document = new Document("SOURCES += main.cpp \\\n main2.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
QtProjectVariable sources = parser.getVariable("SOURCES"); //$NON-NLS-1$
|
||||
assertNotNull("Unable to parse variable", sources); //$NON-NLS-1$
|
||||
assertEquals("Incorrect number of lines", sources.getNumberOfLines(), 2); //$NON-NLS-1$
|
||||
assertTrue("Unable to parse \"main.cpp\" from SOURCES variable", sources.getValueIndex("main.cpp") == 0); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
assertTrue("Unable to parse \"main2.cpp\" from SOURCES variable", sources.getValueIndex("main2.cpp") == 1); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MultilineVariable2() {
|
||||
IDocument document = new Document("SOURCES += main.cpp \\\n main2.cpp \\\n main3.cpp"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
QtProjectVariable sources = parser.getVariable("SOURCES"); //$NON-NLS-1$
|
||||
assertNotNull("Unable to parse variable", sources); //$NON-NLS-1$
|
||||
assertEquals("Incorrect number of lines", 3, sources.getNumberOfLines()); //$NON-NLS-1$
|
||||
assertTrue("Unable to parse \"main.cpp\" from SOURCES variable", sources.getValueIndex("main.cpp") == 0); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
assertTrue("Unable to parse \"main2.cpp\" from SOURCES variable", sources.getValueIndex("main2.cpp") == 1); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
assertTrue("Unable to parse \"main3.cpp\" from SOURCES variable", sources.getValueIndex("main3.cpp") == 2); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MalformedMultilineVariable() {
|
||||
IDocument document = new Document("SOURCES += main.cpp \\\n main2.cpp \\"); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
QtProjectVariable sources = parser.getVariable("SOURCES"); //$NON-NLS-1$
|
||||
assertNotNull("Unable to parse variable", sources); //$NON-NLS-1$
|
||||
assertEquals("Incorrect number of lines", 2, sources.getNumberOfLines()); //$NON-NLS-1$
|
||||
assertTrue("Unable to parse \"main.cpp\" from SOURCES variable", sources.getValueIndex("main.cpp") == 0); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
assertTrue("Unable to parse \"main2.cpp\" from SOURCES variable", sources.getValueIndex("main2.cpp") == 1); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MultilineVariable_WithComment() {
|
||||
IDocument document = new Document("SOURCES += main.cpp \\ # this is a comment \n main2.cpp # this is a comment "); //$NON-NLS-1$
|
||||
QtProjectFileParser parser = new QtProjectFileParser(document);
|
||||
|
||||
QtProjectVariable sources = parser.getVariable("SOURCES"); //$NON-NLS-1$
|
||||
assertNotNull("Unable to parse variable", sources); //$NON-NLS-1$
|
||||
assertEquals("Incorrect number of lines", 2, sources.getNumberOfLines()); //$NON-NLS-1$
|
||||
assertTrue("Unable to parse \"main.cpp\" from SOURCES variable", sources.getValueIndex("main.cpp") == 0); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
assertTrue("Unable to parse \"main2.cpp\" from SOURCES variable", sources.getValueIndex("main2.cpp") == 1); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
}
|
|
@ -7,6 +7,9 @@
|
|||
*/
|
||||
package org.eclipse.cdt.qt.tests;
|
||||
|
||||
import org.eclipse.cdt.qt.pro.parser.tests.QtProjectFileModifierTest;
|
||||
import org.eclipse.cdt.qt.pro.parser.tests.QtProjectFileParserTest;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
|
@ -22,6 +25,8 @@ public class AllQtTests extends TestSuite {
|
|||
QtContentAssistantTests.class,
|
||||
QtIndexTests.class,
|
||||
QtRegressionTests.class,
|
||||
QmlRegistrationTests.class);
|
||||
QmlRegistrationTests.class,
|
||||
QtProjectFileModifierTest.class,
|
||||
QtProjectFileParserTest.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,4 +19,5 @@ Require-Bundle: org.eclipse.core.runtime,
|
|||
org.eclipse.cdt.qt.core
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Export-Package: org.eclipse.cdt.internal.qt.ui.assist;x-friends:="org.eclipse.cdt.qt.ui.tests"
|
||||
Export-Package: org.eclipse.cdt.internal.qt.ui.assist;x-friends:="org.eclipse.cdt.qt.ui.tests",
|
||||
org.eclipse.cdt.internal.qt.ui.pro.parser;x-friends:="org.eclipse.cdt.qt.ui.tests"
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<editor
|
||||
class="org.eclipse.cdt.internal.qt.ui.editor.QtProjectFileEditor"
|
||||
default="true"
|
||||
extensions="pro"
|
||||
extensions="pro,pri"
|
||||
id="org.eclipse.cdt.qt.ui.QtProjectFileEditor"
|
||||
name="%qtProjectFileEditor.name">
|
||||
</editor>
|
||||
|
|
|
@ -8,6 +8,11 @@
|
|||
package org.eclipse.cdt.internal.qt.ui;
|
||||
|
||||
import org.eclipse.cdt.core.model.CModelException;
|
||||
import org.eclipse.cdt.internal.qt.ui.resources.QtResourceChangeListener;
|
||||
import org.eclipse.cdt.internal.qt.ui.resources.QtWorkspaceSaveParticipant;
|
||||
import org.eclipse.core.resources.ISaveParticipant;
|
||||
import org.eclipse.core.resources.ISavedState;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
|
@ -44,6 +49,15 @@ public class QtUIPlugin extends AbstractUIPlugin {
|
|||
public void start(BundleContext context) throws Exception {
|
||||
super.start(context);
|
||||
plugin = this;
|
||||
|
||||
// Use a save participant to grab any changed resources while this plugin was inactive
|
||||
QtResourceChangeListener resourceManager = new QtResourceChangeListener();
|
||||
ISaveParticipant saveParticipant = new QtWorkspaceSaveParticipant();
|
||||
ISavedState lastState = ResourcesPlugin.getWorkspace().addSaveParticipant(QtUIPlugin.PLUGIN_ID, saveParticipant);
|
||||
if (lastState != null) {
|
||||
lastState.processResourceChangeEvents(resourceManager);
|
||||
}
|
||||
ResourcesPlugin.getWorkspace().addResourceChangeListener(resourceManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,286 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 QNX Software Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.qt.ui.pro.parser;
|
||||
|
||||
import org.eclipse.cdt.internal.qt.core.QtPlugin;
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
|
||||
/**
|
||||
* Allows for the manipulation of information stored in a Qt Project File. At the moment the only modifiable information is that
|
||||
* which is contained within variables such as the following:
|
||||
*
|
||||
* <pre>
|
||||
* <code>SOURCES += file.cpp \ # This is the first line with value "file.cpp"
|
||||
* file2.cpp # This is the second line with value "file2.cpp"</code>
|
||||
* </pre>
|
||||
*
|
||||
* This class supports the following modifications to variables:
|
||||
* <ul>
|
||||
* <li><b>Add Value</b>: If the specified String does not exist in the given variable then it is added as a new line at the end of
|
||||
* the variable declaration. A line escape (\) is also inserted into the preceding line.</li>
|
||||
* <li><b>Remove Value</b>: If the specified String exists in the given variable then it is removed. The line escape character (\)
|
||||
* is also removed from the preceding line if necessary.</li>
|
||||
* <li><b>Replace Value</b>: If the specified String exists as a line in the given variable, then it is replaced with another
|
||||
* String. All spacing is preserved as only the value itself is modified.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Comments may appear after the line escape character (\) in a variable Declaration. For this case, replace and addition operations
|
||||
* will preserve these comments. However, a comment will not be preserved if its line is deleted during a remove operation.
|
||||
* </p>
|
||||
*/
|
||||
public class QtProjectFileModifier {
|
||||
|
||||
private QtProjectFileParser parser;
|
||||
private IDocument document;
|
||||
|
||||
public QtProjectFileModifier(IDocument doc) {
|
||||
if (doc == null) {
|
||||
throw new IllegalArgumentException("document cannot be null"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
this.document = doc;
|
||||
this.parser = new QtProjectFileParser(doc);
|
||||
}
|
||||
|
||||
public QtProjectFileModifier(QtProjectFileParser parser) {
|
||||
if (parser == null) {
|
||||
throw new IllegalArgumentException("parser cannot be null"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
this.document = parser.getDocument();
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to replace the given value with a new value if it is found within the given variable name. This is a convenience
|
||||
* method equivalent to <code>replaceVariableValue(variable,oldValue,newValue,true)</code> and will only match values that
|
||||
* occupy an entire line within the variable declaration.
|
||||
* <p>
|
||||
* This method does <b>not</b> create a new value if the specified <code>oldValue</code> was not found. If this behavior is
|
||||
* desired, then check for a return of <code>false</code> from this method and then call the <code>addVariableValue</code>
|
||||
* method.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Note:</b> The "entire line" refers to only the value as it appears in the variable declaration. That is, any whitespace
|
||||
* before or after will not be included when matching a value to the "entire line".
|
||||
* </p>
|
||||
*
|
||||
* @param variable
|
||||
* the name of the variable
|
||||
* @param oldValue
|
||||
* the value that will be replaced
|
||||
* @param newValue
|
||||
* the value to replace with
|
||||
* @return whether or not the value was able to be replaced
|
||||
*/
|
||||
public boolean replaceVariableValue(String variable, String oldValue, String newValue) {
|
||||
return replaceVariableValue(variable, oldValue, newValue, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to replace the first instance of <code>oldValue</code> with <code>newValue</code> if it is found within the given
|
||||
* variable name. If <code>matchWholeLine</code> is false, this method will try to match sections of each line with the value of
|
||||
* <code>oldValue</code>. If a match is found, only that portion of the line will be replaced. If <code>matchWholeLine</code> is
|
||||
* true, this method will try to match the entire line with the value of <code>oldValue</code> and will replace that. All other
|
||||
* line spacing and comments are preserved as only the value itself is replaced.
|
||||
* <p>
|
||||
* This method does <b>not</b> create a new value if <code>oldValue</code> was not found. If this behavior is desired, then
|
||||
* check for a return of <code>false</code> from this method and then call the <code>addVariableValue</code> method.
|
||||
* </p>
|
||||
* <p>
|
||||
* <b>Note:</b> The "entire line" refers to only the value as it appears in the variable declaration. That is, any whitespace
|
||||
* before or after will not be included when matching a value to the "entire line".
|
||||
* </p>
|
||||
*
|
||||
* @param variable
|
||||
* the name of the variable
|
||||
* @param oldValue
|
||||
* the value that will be replaced
|
||||
* @param newValue
|
||||
* the value to replace with
|
||||
* @param matchWholeLine
|
||||
* whether or not the value should match the entire line
|
||||
* @return whether or not the value was able to be replaced
|
||||
*/
|
||||
public boolean replaceVariableValue(String variable, String oldValue, String newValue, boolean matchWholeLine) {
|
||||
QtProjectVariable var = parser.getVariable(variable);
|
||||
|
||||
if (var != null) {
|
||||
if (matchWholeLine) {
|
||||
int line = var.getValueIndex(oldValue);
|
||||
if (line >= 0) {
|
||||
return replaceVariableValue(var, line, newValue);
|
||||
}
|
||||
} else {
|
||||
int line = 0;
|
||||
for (String value : var.getValues()) {
|
||||
int offset = value.indexOf(oldValue);
|
||||
if (offset >= 0) {
|
||||
return replaceVariableValue(var,
|
||||
line,
|
||||
var.getValueOffsetForLine(line) + offset,
|
||||
oldValue.length(),
|
||||
newValue);
|
||||
}
|
||||
line++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean replaceVariableValue(QtProjectVariable var, int lineNo, String newValue) {
|
||||
int offset = var.getValueOffsetForLine(lineNo);
|
||||
String value = var.getValueForLine(lineNo);
|
||||
int length = value.length();
|
||||
|
||||
return replaceVariableValue(var, lineNo, offset, length, newValue);
|
||||
}
|
||||
|
||||
private boolean replaceVariableValue(QtProjectVariable var, int lineNo, int offset, int length, String newValue) {
|
||||
try {
|
||||
document.replace(offset, length, newValue);
|
||||
return true;
|
||||
} catch (BadLocationException e) {
|
||||
QtPlugin.log(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds <code>value</code> to the specified variable as a new line and escapes the previous line with a backslash. The escaping
|
||||
* is done in such a way that comments and spacing are preserved on the previous line. If this variable does not exist, a new
|
||||
* one is created at the bottom-most position of the document with the initial value specified by <code>value</code>.
|
||||
*
|
||||
* @param variable
|
||||
* the name of the variable to add to
|
||||
* @param value
|
||||
* the value to add to the variable
|
||||
*/
|
||||
public void addVariableValue(String variable, String value) {
|
||||
QtProjectVariable var = parser.getVariable(variable);
|
||||
|
||||
if (var != null) {
|
||||
if (var.getValueIndex(value) < 0) {
|
||||
int line = var.getNumberOfLines() - 1;
|
||||
String indent = var.getIndentString(line);
|
||||
|
||||
int offset = var.getEndOffset();
|
||||
if (var.getLine(line).endsWith("\n")) { //$NON-NLS-1$
|
||||
offset--;
|
||||
}
|
||||
|
||||
try {
|
||||
document.replace(offset, 0, "\n" + indent + value); //$NON-NLS-1$
|
||||
} catch (BadLocationException e) {
|
||||
QtPlugin.log(e);
|
||||
}
|
||||
|
||||
try {
|
||||
offset = var.getLineEscapeReplacementOffset(line);
|
||||
String lineEscape = var.getLineEscapeReplacementString(line);
|
||||
|
||||
document.replace(offset, 0, lineEscape);
|
||||
} catch (BadLocationException e) {
|
||||
QtPlugin.log(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Variable does not exist, create it
|
||||
String baseVariable = variable + " += " + value + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
// Check the contents of the document and re-format accordingly
|
||||
if (document.get().trim().isEmpty()) {
|
||||
try {
|
||||
document.replace(0, document.getLength(), baseVariable);
|
||||
} catch (BadLocationException e) {
|
||||
QtPlugin.log(e);
|
||||
}
|
||||
} else if (document.get().endsWith("\n")) { //$NON-NLS-1$
|
||||
try {
|
||||
document.replace(document.getLength(), 0, "\n" + baseVariable); //$NON-NLS-1$
|
||||
} catch (BadLocationException e) {
|
||||
QtPlugin.log(e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
document.replace(document.getLength(), 0, "\n\n" + baseVariable); //$NON-NLS-1$
|
||||
} catch (BadLocationException e) {
|
||||
QtPlugin.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes <code>value</code> from the specified variable and removes the previous line escape if necessary. The entire line is
|
||||
* removed including any comments. If the value is not found, nothing happens.
|
||||
*
|
||||
* @param variable
|
||||
* the name of the variable to remove from
|
||||
* @param value
|
||||
* the value to remove from the variable
|
||||
*/
|
||||
public void removeVariableValue(String variable, String value) {
|
||||
QtProjectVariable var = parser.getVariable(variable);
|
||||
|
||||
if (var != null) {
|
||||
int line = var.getValueIndex(value);
|
||||
if (line == 0 && var.getNumberOfLines() > 1) {
|
||||
// Entering this block means we're removing the first line where more lines exist.
|
||||
int offset = var.getValueOffsetForLine(line);
|
||||
int end = var.getValueOffsetForLine(line + 1);
|
||||
|
||||
try {
|
||||
document.replace(offset, end - offset, ""); //$NON-NLS-1$
|
||||
} catch (BadLocationException e) {
|
||||
QtPlugin.log(e);
|
||||
}
|
||||
} else if (line >= 0) {
|
||||
int offset = var.getLineOffset(line);
|
||||
int length = var.getLine(line).length();
|
||||
if (line > 0) {
|
||||
// Remove the previous line feed character
|
||||
offset--;
|
||||
length++;
|
||||
}
|
||||
|
||||
try {
|
||||
document.replace(offset, length, ""); //$NON-NLS-1$
|
||||
} catch (BadLocationException e) {
|
||||
QtPlugin.log(e);
|
||||
}
|
||||
|
||||
// Remove the previous line's line escape character if necessary
|
||||
if (line > 0 && line == var.getNumberOfLines() - 1) {
|
||||
try {
|
||||
offset = var.getLineEscapeOffset(line - 1);
|
||||
length = var.getLineEscapeEnd(line - 1) - offset;
|
||||
|
||||
document.replace(offset, length, ""); //$NON-NLS-1$
|
||||
} catch (BadLocationException e) {
|
||||
QtPlugin.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the <code>IDocument</code> currently being modified by this class.
|
||||
*
|
||||
* @return the document being modified
|
||||
*/
|
||||
public IDocument getDocument() {
|
||||
return document;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 QNX Software Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.qt.ui.pro.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.eclipse.jface.text.DocumentEvent;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.IDocumentListener;
|
||||
|
||||
/**
|
||||
* Very basic parser for Qt Project Files that uses regular expressions. For now, this class only supports finding variables within
|
||||
* a Document that follow the syntax:
|
||||
*
|
||||
* <pre>
|
||||
* <code>VARIABLE_NAME += value1 \ # comment
|
||||
* value2 \ # comment
|
||||
* value3</code>
|
||||
* </pre>
|
||||
*
|
||||
* The assignment operator may be one of =, +=, -=, or *= in accordance with qmake syntax. Variable names are not checked for
|
||||
* semantic validity. That is, this class does not make sure the variable name is a registered qmake variable, nor that there are
|
||||
* multiple instances of a variable in the document.
|
||||
*/
|
||||
public class QtProjectFileParser implements IDocumentListener {
|
||||
|
||||
IDocument document;
|
||||
List<QtProjectVariable> variables;
|
||||
|
||||
public QtProjectFileParser(IDocument doc) {
|
||||
if (doc == null) {
|
||||
throw new IllegalArgumentException("document cannot be null"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
document = doc;
|
||||
variables = parse();
|
||||
document.addDocumentListener(this);
|
||||
}
|
||||
|
||||
public IDocument getDocument() {
|
||||
return document;
|
||||
}
|
||||
|
||||
private List<QtProjectVariable> parse() {
|
||||
// Just build the list from scratch
|
||||
List<QtProjectVariable> variables = new CopyOnWriteArrayList<>();
|
||||
try (Scanner scanner = new Scanner(document.get())) {
|
||||
QtProjectVariable next;
|
||||
while ((next = QtProjectVariable.findNextVariable(scanner)) != null) {
|
||||
variables.add(next);
|
||||
}
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific Qt Project Variable from the provided <code>IDocument</code>. If the variable cannot be found,
|
||||
* <code>null</code> is returned instead.
|
||||
* <p>
|
||||
* <b>Note:</b> This method is greedy in the sense that it returns the first match it finds. If multiple variables exist with
|
||||
* the same name in the <code>IDocument</code>, this method will only return the first match.
|
||||
* </p>
|
||||
*
|
||||
* @param name
|
||||
* the name of the variable
|
||||
* @return the <code>QtProjectVariable</code> or <code>null</code> if it couldn't be found
|
||||
*/
|
||||
public QtProjectVariable getVariable(String name) {
|
||||
for (QtProjectVariable v : variables) {
|
||||
if (v.getName().equals(name)) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all Qt Project Variables found within the provided <code>IDocument</code>. A fresh list is always returned
|
||||
* with the internal list copied into it. As such, modifying this list does not modify the internal list of the parser.
|
||||
*
|
||||
* @return the list of all Qt Project Variables
|
||||
*/
|
||||
public List<QtProjectVariable> getAllVariables() {
|
||||
return new ArrayList<>(variables);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentAboutToBeChanged(DocumentEvent event) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentChanged(DocumentEvent event) {
|
||||
// Re-parse the document every time it changes
|
||||
variables = parse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
// Make sure that we are removed from the document's listeners
|
||||
if (document != null) {
|
||||
document.removeDocumentListener(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,389 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 QNX Software Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.qt.ui.pro.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Contains all information about a variable's representation in a Qt Project (.pro) File. This includes information about offsets,
|
||||
* lengths, and textual representation of various components of a variable declaration such as its:
|
||||
* <ul>
|
||||
* <li>Name, such as "SOURCES"</li>
|
||||
* <li>Assignment operator (= or +=)</li>
|
||||
* <li>Values for a particular line</li>
|
||||
* <li>Comments for a particular line</li>
|
||||
* <li>Line feeds</li>
|
||||
* <li>Line escapes (\)</li>
|
||||
* </ul>
|
||||
* Also contains the static method <code>findNextVariable(Scanner)</code> to perform the regular expressions lookup of the next
|
||||
* variable in a document.
|
||||
*/
|
||||
public class QtProjectVariable {
|
||||
private static final Pattern REGEX = Pattern.compile(
|
||||
"(?m)^\\h*((?:[_a-zA-Z][_a-zA-Z0-9]*\\.)*[_a-zA-Z][_a-zA-Z0-9]*)\\h*(=|\\+=|-=|\\*=)\\h*([^#\\v]*?)\\h*((?:(\\\\)\\h*)?(#[^\\v]*)?$)"); //$NON-NLS-1$
|
||||
private static final Pattern LINE_ESCAPE_REGEX = Pattern.compile("(?m)^(\\h*)([^#\\v]*?)\\h*((?:(\\\\)\\h*)?(#[^\\v]*)?$)"); //$NON-NLS-1$
|
||||
|
||||
private static final int GROUP_VAR_NAME = 1;
|
||||
private static final int GROUP_VAR_ASSIGNMENT = 2;
|
||||
private static final int GROUP_VAR_CONTENTS = 3;
|
||||
private static final int GROUP_VAR_TERMINATOR = 4;
|
||||
private static final int GROUP_VAR_LINE_ESCAPE = 5;
|
||||
private static final int GROUP_VAR_COMMENT = 6;
|
||||
|
||||
private static final int GROUP_LINE_INDENT = 1;
|
||||
private static final int GROUP_LINE_CONTENTS = 2;
|
||||
private static final int GROUP_LINE_TERMINATOR = 3;
|
||||
private static final int GROUP_LINE_LINE_ESCAPE = 4;
|
||||
private static final int GROUP_LINE_COMMENT = 5;
|
||||
|
||||
/**
|
||||
* Finds the next Qt Project Variable within a String using the given Scanner. If there are no variables to be found, this
|
||||
* method will return <code>null</code>.
|
||||
*
|
||||
* @param scanner
|
||||
* the scanner to use for regular expressions matching
|
||||
* @return the next variable or <code>null</code> if none
|
||||
*/
|
||||
public static QtProjectVariable findNextVariable(Scanner scanner) {
|
||||
List<MatchResult> matchResults = new ArrayList<>();
|
||||
|
||||
// Find the start of a variable declaration
|
||||
String match = scanner.findWithinHorizon(REGEX, 0);
|
||||
if (match == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get subsequent lines if the previous one ends with '\'
|
||||
MatchResult matchResult = scanner.match();
|
||||
matchResults.add(matchResult);
|
||||
if (matchResult.group(QtProjectVariable.GROUP_VAR_TERMINATOR).startsWith("\\")) { //$NON-NLS-1$
|
||||
do {
|
||||
match = scanner.findWithinHorizon(LINE_ESCAPE_REGEX, 0);
|
||||
if (match == null) {
|
||||
// This means that we have a newline escape where another line doesn't exist
|
||||
break;
|
||||
}
|
||||
|
||||
matchResult = scanner.match();
|
||||
matchResults.add(matchResult);
|
||||
} while (matchResult.group(QtProjectVariable.GROUP_LINE_TERMINATOR).startsWith("\\")); //$NON-NLS-1$
|
||||
}
|
||||
return new QtProjectVariable(matchResults);
|
||||
}
|
||||
|
||||
private final int startOffset;
|
||||
private final int endOffset;
|
||||
private final String text;
|
||||
|
||||
private final List<MatchResult> matchResults;
|
||||
|
||||
/**
|
||||
* Constructs a project file variable from a list of match results obtained from a <code>Scanner</code>. This constructor is
|
||||
* only intended to be called from within the static method <code>findNextVariable(Scanner)</code>.
|
||||
*
|
||||
* @param matches
|
||||
* list of <code>MatchResult</code>
|
||||
*/
|
||||
private QtProjectVariable(List<MatchResult> matches) {
|
||||
this.startOffset = matches.get(0).start();
|
||||
this.endOffset = matches.get(matches.size() - 1).end();
|
||||
this.matchResults = matches;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (MatchResult m : matches) {
|
||||
sb.append(m.group());
|
||||
}
|
||||
this.text = sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the offset of this variable relative to the start of its containing document.
|
||||
*
|
||||
* @return the offset of this variable
|
||||
*/
|
||||
public int getOffset() {
|
||||
return startOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length of this variable as it appears in its containing document.
|
||||
*
|
||||
* @return the total length of this variable
|
||||
*/
|
||||
public int getLength() {
|
||||
return endOffset - startOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this variable as it appears in the document. For example, the <code>"SOURCES"</code> variable.
|
||||
*
|
||||
* @return the name of this variable
|
||||
*/
|
||||
public String getName() {
|
||||
return matchResults.get(0).group(GROUP_VAR_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* the assignment operator of this variable (<code>+=</code> or <code>"="</code>)
|
||||
*
|
||||
* @return the assignment operator
|
||||
*/
|
||||
public String getAssignmentOperator() {
|
||||
return matchResults.get(0).group(GROUP_VAR_ASSIGNMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of value(s) assigned to this variable. Each entry in the list represents a new line.
|
||||
*
|
||||
* @return a List containing all of the value(s) assigned to this variable
|
||||
*/
|
||||
public List<String> getValues() {
|
||||
List<String> values = new ArrayList<String>();
|
||||
values.add(matchResults.get(0).group(GROUP_VAR_CONTENTS));
|
||||
for (int i = 1; i < matchResults.size(); i++) {
|
||||
values.add(matchResults.get(i).group(GROUP_LINE_CONTENTS));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the indentation of the given line as a String. Mainly used by the QtProjectFileWriter to write back to the Document.
|
||||
*
|
||||
* @param line
|
||||
* the line number to check
|
||||
* @return a <code>String</code> representing the indentation of the given line
|
||||
*/
|
||||
public String getIndentString(int line) {
|
||||
MatchResult match = matchResults.get(line);
|
||||
if (line == 0) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < match.start(GROUP_VAR_CONTENTS) - match.start(); i++) {
|
||||
sb.append(' ');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
return match.group(GROUP_LINE_INDENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the offset of the value portion of a given line relative to the start of its containing document.
|
||||
*
|
||||
* @param line
|
||||
* the line to check
|
||||
* @return the offset of the value
|
||||
*/
|
||||
public int getValueOffsetForLine(int line) {
|
||||
if (line == 0) {
|
||||
return matchResults.get(line).start(GROUP_VAR_CONTENTS);
|
||||
}
|
||||
return matchResults.get(line).start(GROUP_LINE_CONTENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a String representing the value at a specific line of this variable.
|
||||
*
|
||||
* @param line
|
||||
* the line to check
|
||||
* @return the value
|
||||
*/
|
||||
public String getValueForLine(int line) {
|
||||
if (line == 0) {
|
||||
return matchResults.get(line).group(GROUP_VAR_CONTENTS);
|
||||
}
|
||||
return matchResults.get(line).group(GROUP_LINE_CONTENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ideal offset in the containing document at which a line escape can be inserted.
|
||||
*
|
||||
* @param line
|
||||
* the line to check
|
||||
* @return the ideal location for a line escape
|
||||
*/
|
||||
public int getLineEscapeReplacementOffset(int line) {
|
||||
if (line == 0) {
|
||||
return matchResults.get(line).end(GROUP_VAR_CONTENTS);
|
||||
}
|
||||
return matchResults.get(line).end(GROUP_LINE_CONTENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ideal String for the line escape character. This is mostly for spacing requirements and should be used in tandem
|
||||
* with the method <code>getLineEscapeReplacementOffset</code>.
|
||||
*
|
||||
* @param line
|
||||
* the line to check
|
||||
* @return the ideal String for the line escape character
|
||||
*/
|
||||
public String getLineEscapeReplacementString(int line) {
|
||||
int commentOffset = -1;
|
||||
int contentsOffset = -1;
|
||||
if (line == 0) {
|
||||
commentOffset = matchResults.get(line).start(GROUP_VAR_COMMENT);
|
||||
contentsOffset = matchResults.get(line).end(GROUP_VAR_CONTENTS);
|
||||
} else {
|
||||
commentOffset = matchResults.get(line).start(GROUP_LINE_COMMENT);
|
||||
contentsOffset = matchResults.get(line).end(GROUP_LINE_CONTENTS);
|
||||
}
|
||||
|
||||
if (commentOffset > 0) {
|
||||
if (commentOffset - contentsOffset == 0) {
|
||||
return " \\ "; //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
return " \\"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the offset of the line escape for a given line relative to its containing document. This method takes into account
|
||||
* spacing and should be used to determine how to best remove a line escape character from a given line.
|
||||
*
|
||||
* @param line
|
||||
* the line to check
|
||||
* @return the offset of the line escape character
|
||||
*/
|
||||
public int getLineEscapeOffset(int line) {
|
||||
if (line == 0) {
|
||||
return matchResults.get(line).end(GROUP_VAR_CONTENTS);
|
||||
}
|
||||
return matchResults.get(line).end(GROUP_LINE_CONTENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end position relative to the start of the containing document that contains the line escape character of the given
|
||||
* line. This is used for removal of the line escape character and takes into account the spacing of the line.
|
||||
*
|
||||
* @param line
|
||||
* the line to check
|
||||
* @return the end position of the line escape character
|
||||
*/
|
||||
public int getLineEscapeEnd(int line) {
|
||||
int end = -1;
|
||||
if (line == 0) {
|
||||
end = matchResults.get(line).end(GROUP_VAR_LINE_ESCAPE);
|
||||
} else {
|
||||
end = matchResults.get(line).end(GROUP_LINE_LINE_ESCAPE);
|
||||
}
|
||||
|
||||
if (end > 0) {
|
||||
return end;
|
||||
}
|
||||
|
||||
if (line == 0) {
|
||||
return matchResults.get(line).end(GROUP_VAR_TERMINATOR);
|
||||
}
|
||||
return matchResults.get(line).end(GROUP_LINE_TERMINATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the end position of this variable relative to the containing document.
|
||||
*
|
||||
* @return the end position of this variable
|
||||
*/
|
||||
public int getEndOffset() {
|
||||
return matchResults.get(matchResults.size() - 1).end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the full text of this variable as it appears in the document.
|
||||
*
|
||||
* @return the full String of this variable as it appears in the document
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of lines in this variable declaration.
|
||||
*
|
||||
* @return the total number of lines
|
||||
*/
|
||||
public int getNumberOfLines() {
|
||||
return matchResults.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a String representing the given line as it appears in the document.
|
||||
*
|
||||
* @param line
|
||||
* the line to retrieve
|
||||
* @return a String representing the line
|
||||
*/
|
||||
public String getLine(int line) {
|
||||
return matchResults.get(line).group();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the offset of the given line relative to its containing document.
|
||||
*
|
||||
* @param line
|
||||
* the line to retrieve
|
||||
* @return the line's offset in the document
|
||||
*/
|
||||
public int getLineOffset(int line) {
|
||||
return matchResults.get(line).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the line at which the specified value appears. This method checks the whole line for the value and will not match a
|
||||
* subset of that String. This is equivalent to calling <code>getValueIndex(value,false)</code>.
|
||||
*
|
||||
* @param value
|
||||
* the value to search for
|
||||
* @return the line that the value appears on or -1 if it doesn't exist
|
||||
*/
|
||||
public int getValueIndex(String value) {
|
||||
return getValueIndex(value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the line at which the specified value appears. This method checks the whole line for the value and will not match a
|
||||
* subset of that String. If <code>ignoreCase</code> is <code>false</code>, this method searches for the value using
|
||||
* <code>equalsIgnoreCase</code> instead of <code>equals</code>.
|
||||
*
|
||||
* @param value
|
||||
* the value to search for
|
||||
* @param ignoreCase
|
||||
* whether or not the value is case-sensitive
|
||||
* @return the line that the value appears on or -1 if it doesn't exist
|
||||
*/
|
||||
public int getValueIndex(String value, boolean ignoreCase) {
|
||||
int line = 0;
|
||||
for (String val : getValues()) {
|
||||
if (ignoreCase) {
|
||||
if (val.equalsIgnoreCase(value)) {
|
||||
return line;
|
||||
}
|
||||
} else {
|
||||
if (val.equals(value)) {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
line++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the offset of the end of a given line relative to its containing document.
|
||||
*
|
||||
* @param line
|
||||
* the line to check
|
||||
* @return the offset of the end of the line
|
||||
*/
|
||||
public int getLineEnd(int line) {
|
||||
return matchResults.get(line).end();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 QNX Software Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.qt.ui.resources;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.cdt.internal.qt.ui.QtUIPlugin;
|
||||
import org.eclipse.cdt.internal.qt.ui.editor.QtProjectFileKeyword;
|
||||
import org.eclipse.cdt.internal.qt.ui.pro.parser.QtProjectFileModifier;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.jface.text.Document;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
|
||||
/**
|
||||
* Job that calls the <code>QtProjectFileModifier</code> after changes to resources found in Qt Projects in order to update their
|
||||
* <code>SOURCES</code> variable.
|
||||
*/
|
||||
public class QtProjectFileUpdateJob extends Job {
|
||||
|
||||
private List<IResourceDelta> deltaList;
|
||||
|
||||
public QtProjectFileUpdateJob(List<IResourceDelta> deltas) {
|
||||
super("Update Qt Project File(s)"); //$NON-NLS-1$
|
||||
this.deltaList = deltas;
|
||||
}
|
||||
|
||||
private IFile findQtProjectFile(IProject project) throws CoreException {
|
||||
for (IResource member : project.members()) {
|
||||
if (member.getType() == IResource.FILE
|
||||
&& member.getFileExtension().equals("pro")) { //$NON-NLS-1$
|
||||
return (IFile) member;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
// Cache the project files so we don't continuously open them
|
||||
Map<IProject, QtProjectFileModifier> modifierMap = new HashMap<>();
|
||||
Map<IProject, IFile> projectFileMap = new HashMap<>();
|
||||
|
||||
for (IResourceDelta delta : deltaList) {
|
||||
IResource resource = delta.getResource();
|
||||
IProject project = resource.getProject();
|
||||
QtProjectFileModifier modifier = modifierMap.get(project);
|
||||
|
||||
if (modifier == null) {
|
||||
IFile proFile = null;
|
||||
try {
|
||||
proFile = findQtProjectFile(project);
|
||||
} catch (CoreException e) {
|
||||
QtUIPlugin.log("Unable to find Qt Project File", e); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
// We can't update a project file if it doesn't exist
|
||||
if (proFile == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cache the project file under its containing project and read its contents into a Document.
|
||||
projectFileMap.put(project, proFile);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try (InputStream is = proFile.getContents()) {
|
||||
int read = -1;
|
||||
while ((read = is.read()) > 0) {
|
||||
sb.append((char) read);
|
||||
}
|
||||
IDocument document = new Document(sb.toString());
|
||||
modifier = new QtProjectFileModifier(document);
|
||||
modifierMap.put(project, modifier);
|
||||
} catch (IOException e) {
|
||||
QtUIPlugin.log(e);
|
||||
break;
|
||||
} catch (CoreException e) {
|
||||
QtUIPlugin.log(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine from the file extension where we should add this resource
|
||||
String variableKeyword = null;
|
||||
if ("cpp".equals(resource.getFileExtension())) { //$NON-NLS-1$
|
||||
variableKeyword = QtProjectFileKeyword.VAR_SOURCES.getKeyword();
|
||||
} else if ("h".equals(resource.getFileExtension())) { //$NON-NLS-1$
|
||||
variableKeyword = QtProjectFileKeyword.VAR_HEADERS.getKeyword();
|
||||
}
|
||||
|
||||
if ((delta.getFlags() & IResourceDelta.MOVED_FROM) > 0) {
|
||||
// Resource was moved from another location.
|
||||
if (project.getFullPath().isPrefixOf(delta.getMovedFromPath())) {
|
||||
String oldValue = delta.getMovedFromPath().makeRelativeTo(project.getFullPath()).toString();
|
||||
String newValue = resource.getProjectRelativePath().toString();
|
||||
|
||||
if (modifier.replaceVariableValue(variableKeyword, oldValue, newValue)) {
|
||||
// If we successfully replaced the variable, continue. If this line is not executed it means we failed to
|
||||
// replace and the file will be added in the subsequent code for the ADDED case.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if ((delta.getFlags() & IResourceDelta.MOVED_TO) > 0) {
|
||||
// Somewhat edge-case where a file from one Qt Project was moved to a different Qt Project.
|
||||
if (project.getFullPath().isPrefixOf(delta.getMovedToPath())) {
|
||||
// Getting here means that the replace was taken care of by the previous code. Otherwise, it will be removed in
|
||||
// the subsequent code for the REMOVED case.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ((delta.getKind() & IResourceDelta.ADDED) > 0) {
|
||||
String value = resource.getProjectRelativePath().toString();
|
||||
if (value != null) {
|
||||
modifier.addVariableValue(variableKeyword, value);
|
||||
}
|
||||
} else if ((delta.getKind() & IResourceDelta.REMOVED) > 0) {
|
||||
String value = resource.getProjectRelativePath().toString();
|
||||
if (value != null) {
|
||||
modifier.removeVariableValue(variableKeyword, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write all documents to their respective files
|
||||
for (IProject project : projectFileMap.keySet()) {
|
||||
IFile file = projectFileMap.get(project);
|
||||
IDocument document = modifierMap.get(project).getDocument();
|
||||
|
||||
try {
|
||||
file.setContents(new ByteArrayInputStream(document.get().getBytes()), 0, null);
|
||||
} catch (CoreException e) {
|
||||
QtUIPlugin.log(e);
|
||||
}
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 QNX Software Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.qt.ui.resources;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.cdt.internal.qt.core.QtNature;
|
||||
import org.eclipse.cdt.internal.qt.ui.QtUIPlugin;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceChangeEvent;
|
||||
import org.eclipse.core.resources.IResourceChangeListener;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.resources.IResourceDeltaVisitor;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
|
||||
/**
|
||||
* Detects the addition or removal of a file to a Qt project. If one of these resource changes is found, it triggers an update of
|
||||
* the project's *.pro file to reflect the change.
|
||||
*/
|
||||
public class QtResourceChangeListener implements IResourceChangeListener {
|
||||
|
||||
@Override
|
||||
public void resourceChanged(IResourceChangeEvent event) {
|
||||
// No need to check for any events other than POST_CHANGE
|
||||
if ((event.getType() & (IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.POST_BUILD)) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<IResourceDelta> deltaList = new ArrayList<>();
|
||||
IResourceDeltaVisitor visitor = new IResourceDeltaVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(IResourceDelta delta) {
|
||||
IResource resource = delta.getResource();
|
||||
|
||||
if (resource.getType() == IResource.ROOT) {
|
||||
// Always traverse children of the workspace root
|
||||
return true;
|
||||
} else if (resource.getType() == IResource.PROJECT) {
|
||||
// Only traverse children of Qt Projects
|
||||
try {
|
||||
IProject project = (IProject) resource;
|
||||
if (project.hasNature(QtNature.ID)) {
|
||||
return true;
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
QtUIPlugin.log(e);
|
||||
}
|
||||
return false;
|
||||
} else if (resource.getType() == IResource.FOLDER) {
|
||||
// First, make sure this isn't the "build" folder
|
||||
if (resource.getType() == IResource.FOLDER) {
|
||||
if (resource.getName().equals("build")) { //$NON-NLS-1$
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Then check to make sure that the folder lies in a Qt Project
|
||||
try {
|
||||
IProject project = resource.getProject();
|
||||
if (project != null && project.hasNature(QtNature.ID)) {
|
||||
return true;
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
QtUIPlugin.log(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only care about added and removed resources at this point
|
||||
if ((delta.getKind() & (IResourceDelta.ADDED | IResourceDelta.REMOVED)) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ("cpp".equals(resource.getFileExtension()) //$NON-NLS-1$
|
||||
|| "h".equals(resource.getFileExtension())) { //$NON-NLS-1$
|
||||
// If we make it to this point, then we have a .cpp or .h file that's been added to or removed from a Qt
|
||||
// Project. Add it to the list of deltas so we can update the project file later.
|
||||
deltaList.add(delta);
|
||||
}
|
||||
|
||||
// Doesn't really matter since this line can only be reached if we're dealing with a file that shouldn't have
|
||||
// children anyway
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
// Check all projects starting at the workspace root
|
||||
event.getDelta().accept(visitor);
|
||||
} catch (CoreException e) {
|
||||
QtUIPlugin.log(e);
|
||||
}
|
||||
|
||||
// Schedule the job to update the .pro files
|
||||
if (!deltaList.isEmpty()) {
|
||||
new QtProjectFileUpdateJob(deltaList).schedule();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 QNX Software Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.qt.ui.resources;
|
||||
|
||||
import org.eclipse.core.resources.ISaveContext;
|
||||
import org.eclipse.core.resources.ISaveParticipant;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
|
||||
public class QtWorkspaceSaveParticipant implements ISaveParticipant {
|
||||
|
||||
@Override
|
||||
public void doneSaving(ISaveContext context) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareToSave(ISaveContext context) throws CoreException {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(ISaveContext context) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saving(ISaveContext context) throws CoreException {
|
||||
context.needDelta();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 QNX Software Systems and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.qt.ui.text;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.eclipse.cdt.internal.qt.ui.QtUIPlugin;
|
||||
import org.eclipse.cdt.internal.qt.ui.editor.QtProjectFileKeyword;
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.ITextViewer;
|
||||
import org.eclipse.jface.text.contentassist.CompletionProposal;
|
||||
import org.eclipse.jface.text.contentassist.ICompletionProposal;
|
||||
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
|
||||
import org.eclipse.jface.text.contentassist.IContextInformation;
|
||||
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
|
||||
|
||||
public class ContentAssistProcessor implements IContentAssistProcessor {
|
||||
private final IContextInformation[] NO_CONTEXTS = {};
|
||||
private final ICompletionProposal[] NO_COMPLETIONS = {};
|
||||
|
||||
@Override
|
||||
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
|
||||
try {
|
||||
IDocument document = viewer.getDocument();
|
||||
ArrayList<ICompletionProposal> result = new ArrayList<>();
|
||||
|
||||
// Search the list of keywords (case-insensitive)
|
||||
String prefix = lastWord(document, offset).toLowerCase(Locale.ROOT);
|
||||
for (QtProjectFileKeyword keyword : QtProjectFileKeyword.values()) {
|
||||
if (prefix.isEmpty() || keyword.getKeyword().toLowerCase(Locale.ROOT).startsWith(prefix)) {
|
||||
result.add(new CompletionProposal(
|
||||
keyword.getKeyword(),
|
||||
offset - prefix.length(),
|
||||
prefix.length(),
|
||||
keyword.getKeyword().length()));
|
||||
}
|
||||
}
|
||||
return result.toArray(new ICompletionProposal[result.size()]);
|
||||
} catch (Exception e) {
|
||||
QtUIPlugin.log(e);
|
||||
return NO_COMPLETIONS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the valid Java identifier in a document immediately before the given offset.
|
||||
*
|
||||
* @param document
|
||||
* the document
|
||||
* @param offset
|
||||
* the offset at which to start looking
|
||||
* @return the Java identifier preceding this location or a blank string if none
|
||||
*/
|
||||
private String lastWord(IDocument document, int offset) {
|
||||
try {
|
||||
for (int n = offset - 1; n >= 0; n--) {
|
||||
char c = document.getChar(n);
|
||||
if (!Character.isJavaIdentifierPart(c)) {
|
||||
return document.get(n + 1, offset - n - 1);
|
||||
}
|
||||
}
|
||||
return document.get(0, offset);
|
||||
} catch (BadLocationException e) {
|
||||
QtUIPlugin.log(e);
|
||||
}
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
@Override
|
||||
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
|
||||
// No context information for now
|
||||
return NO_CONTEXTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IContextInformationValidator getContextInformationValidator() {
|
||||
// No context information validator
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getCompletionProposalAutoActivationCharacters() {
|
||||
// No auto activation
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getContextInformationAutoActivationCharacters() {
|
||||
// No auto activation
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,9 @@ import org.eclipse.cdt.internal.qt.ui.editor.QtProjectFileKeyword;
|
|||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.IDocumentExtension3;
|
||||
import org.eclipse.jface.text.TextAttribute;
|
||||
import org.eclipse.jface.text.contentassist.ContentAssistant;
|
||||
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
|
||||
import org.eclipse.jface.text.contentassist.IContentAssistant;
|
||||
import org.eclipse.jface.text.presentation.IPresentationReconciler;
|
||||
import org.eclipse.jface.text.presentation.PresentationReconciler;
|
||||
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
|
||||
|
@ -89,4 +92,12 @@ public class QtProjectFileSourceViewerConfiguration extends TextSourceViewerConf
|
|||
return scanner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
|
||||
ContentAssistant contentAssistant = new ContentAssistant();
|
||||
IContentAssistProcessor processor = new ContentAssistProcessor();
|
||||
contentAssistant.setContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE);
|
||||
contentAssistant.setInformationControlCreator(getInformationControlCreator(sourceViewer));
|
||||
return contentAssistant;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue