Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Tobias Hunger
qt-creator
Commits
df16c1b6
Commit
df16c1b6
authored
Apr 22, 2009
by
Roberto Raggi
Browse files
Initial work on the DUI editor-plugin.
parent
ef8e69d9
Changes
19
Hide whitespace changes
Inline
Side-by-side
src/plugins/duieditor/DuiEditor.mimetypes.xml
0 → 100644
View file @
df16c1b6
<?xml version="1.0"?>
<mime-info
xmlns=
'http://www.freedesktop.org/standards/shared-mime-info'
>
<mime-type
type=
"application/x-dui"
>
<sub-class-of
type=
"text/plain"
/>
<comment>
DUI file
</comment>
<glob
pattern=
"*.dui"
/>
</mime-type>
</mime-info>
src/plugins/duieditor/DuiEditor.pluginspec
0 → 100644
View file @
df16c1b6
<plugin name="DuiEditor" version="1.1.80" compatVersion="1.1.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2008-2009 Nokia Corporation</copyright>
<license>
Commercial Usage
Licensees holding valid Qt Commercial licenses may use this plugin in
accordance with the Qt Commercial License Agreement provided with the
Software or, alternatively, in accordance with the terms contained in
a written agreement between you and Nokia.
GNU Lesser General Public License Usage
Alternatively, this plugin may be used under the terms of the GNU Lesser
General Public License version 2.1 as published by the Free Software
Foundation. Please review the following information to
ensure the GNU Lesser General Public License version 2.1 requirements
will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.</license>
<description>Editor for DUI.</description>
<url>http://www.qtsoftware.com</url>
<dependencyList>
<dependency name="Core" version="1.1.80"/>
<dependency name="TextEditor" version="1.1.80"/>
</dependencyList>
</plugin>
src/plugins/duieditor/duicodecompletion.cpp
0 → 100644
View file @
df16c1b6
#include
"duicodecompletion.h"
#include
"duieditor.h"
#include
<texteditor/basetexteditor.h>
#include
<QtDebug>
using
namespace
DuiEditor
::
Internal
;
DuiCodeCompletion
::
DuiCodeCompletion
(
QObject
*
parent
)
:
TextEditor
::
ICompletionCollector
(
parent
),
m_editor
(
0
),
m_startPosition
(
0
),
m_caseSensitivity
(
Qt
::
CaseSensitive
)
{
}
DuiCodeCompletion
::~
DuiCodeCompletion
()
{
}
Qt
::
CaseSensitivity
DuiCodeCompletion
::
caseSensitivity
()
const
{
return
m_caseSensitivity
;
}
void
DuiCodeCompletion
::
setCaseSensitivity
(
Qt
::
CaseSensitivity
caseSensitivity
)
{
m_caseSensitivity
=
caseSensitivity
;
}
bool
DuiCodeCompletion
::
supportsEditor
(
TextEditor
::
ITextEditable
*
editor
)
{
if
(
qobject_cast
<
ScriptEditor
*>
(
editor
->
widget
()))
return
true
;
return
false
;
}
bool
DuiCodeCompletion
::
triggersCompletion
(
TextEditor
::
ITextEditable
*
)
{
return
false
;
}
int
DuiCodeCompletion
::
startCompletion
(
TextEditor
::
ITextEditable
*
editor
)
{
m_editor
=
editor
;
ScriptEditor
*
edit
=
qobject_cast
<
ScriptEditor
*>
(
m_editor
->
widget
());
if
(
!
edit
)
return
-
1
;
int
pos
=
editor
->
position
();
while
(
editor
->
characterAt
(
pos
-
1
).
isLetterOrNumber
()
||
editor
->
characterAt
(
pos
-
1
)
==
QLatin1Char
(
'_'
))
--
pos
;
m_startPosition
=
pos
;
m_completions
.
clear
();
foreach
(
const
QString
&
word
,
edit
->
words
())
{
TextEditor
::
CompletionItem
item
(
this
);
item
.
m_text
=
word
;
m_completions
.
append
(
item
);
}
return
pos
;
}
void
DuiCodeCompletion
::
completions
(
QList
<
TextEditor
::
CompletionItem
>
*
completions
)
{
// ### FIXME: this code needs to be generalized.
const
int
length
=
m_editor
->
position
()
-
m_startPosition
;
if
(
length
==
0
)
*
completions
=
m_completions
;
else
if
(
length
>
0
)
{
const
QString
key
=
m_editor
->
textAt
(
m_startPosition
,
length
);
/*
* This code builds a regular expression in order to more intelligently match
* camel-case style. This means upper-case characters will be rewritten as follows:
*
* A => [a-z0-9_]*A (for any but the first capital letter)
*
* Meaning it allows any sequence of lower-case characters to preceed an
* upper-case character. So for example gAC matches getActionController.
*/
QString
keyRegExp
;
keyRegExp
+=
QLatin1Char
(
'^'
);
bool
first
=
true
;
foreach
(
const
QChar
&
c
,
key
)
{
if
(
c
.
isUpper
()
&&
!
first
)
{
keyRegExp
+=
QLatin1String
(
"[a-z0-9_]*"
);
keyRegExp
+=
c
;
}
else
if
(
m_caseSensitivity
==
Qt
::
CaseInsensitive
&&
c
.
isLower
())
{
keyRegExp
+=
QLatin1Char
(
'['
);
keyRegExp
+=
c
;
keyRegExp
+=
c
.
toUpper
();
keyRegExp
+=
QLatin1Char
(
']'
);
}
else
{
keyRegExp
+=
QRegExp
::
escape
(
c
);
}
first
=
false
;
}
const
QRegExp
regExp
(
keyRegExp
,
Qt
::
CaseSensitive
);
foreach
(
TextEditor
::
CompletionItem
item
,
m_completions
)
{
if
(
regExp
.
indexIn
(
item
.
m_text
)
==
0
)
{
item
.
m_relevance
=
(
key
.
length
()
>
0
&&
item
.
m_text
.
startsWith
(
key
,
Qt
::
CaseInsensitive
))
?
1
:
0
;
(
*
completions
)
<<
item
;
}
}
}
}
void
DuiCodeCompletion
::
complete
(
const
TextEditor
::
CompletionItem
&
item
)
{
const
QString
toInsert
=
item
.
m_text
;
const
int
length
=
m_editor
->
position
()
-
m_startPosition
;
m_editor
->
setCurPos
(
m_startPosition
);
m_editor
->
replace
(
length
,
toInsert
);
}
bool
DuiCodeCompletion
::
partiallyComplete
(
const
QList
<
TextEditor
::
CompletionItem
>
&
completionItems
)
{
if
(
completionItems
.
count
()
==
1
)
{
complete
(
completionItems
.
first
());
return
true
;
}
else
{
// Compute common prefix
QString
firstKey
=
completionItems
.
first
().
m_text
;
QString
lastKey
=
completionItems
.
last
().
m_text
;
const
int
length
=
qMin
(
firstKey
.
length
(),
lastKey
.
length
());
firstKey
.
truncate
(
length
);
lastKey
.
truncate
(
length
);
while
(
firstKey
!=
lastKey
)
{
firstKey
.
chop
(
1
);
lastKey
.
chop
(
1
);
}
int
typedLength
=
m_editor
->
position
()
-
m_startPosition
;
if
(
!
firstKey
.
isEmpty
()
&&
firstKey
.
length
()
>
typedLength
)
{
m_editor
->
setCurPos
(
m_startPosition
);
m_editor
->
replace
(
typedLength
,
firstKey
);
}
}
return
false
;
}
void
DuiCodeCompletion
::
cleanup
()
{
m_editor
=
0
;
m_startPosition
=
0
;
m_completions
.
clear
();
}
src/plugins/duieditor/duicodecompletion.h
0 → 100644
View file @
df16c1b6
#ifndef DUICODECOMPLETION_H
#define DUICODECOMPLETION_H
#include
<texteditor/icompletioncollector.h>
namespace
TextEditor
{
class
ITextEditable
;
}
namespace
DuiEditor
{
namespace
Internal
{
class
DuiCodeCompletion
:
public
TextEditor
::
ICompletionCollector
{
Q_OBJECT
public:
DuiCodeCompletion
(
QObject
*
parent
=
0
);
virtual
~
DuiCodeCompletion
();
Qt
::
CaseSensitivity
caseSensitivity
()
const
;
void
setCaseSensitivity
(
Qt
::
CaseSensitivity
caseSensitivity
);
virtual
bool
supportsEditor
(
TextEditor
::
ITextEditable
*
editor
);
virtual
bool
triggersCompletion
(
TextEditor
::
ITextEditable
*
editor
);
virtual
int
startCompletion
(
TextEditor
::
ITextEditable
*
editor
);
virtual
void
completions
(
QList
<
TextEditor
::
CompletionItem
>
*
completions
);
virtual
void
complete
(
const
TextEditor
::
CompletionItem
&
item
);
virtual
bool
partiallyComplete
(
const
QList
<
TextEditor
::
CompletionItem
>
&
completionItems
);
virtual
void
cleanup
();
private:
TextEditor
::
ITextEditable
*
m_editor
;
int
m_startPosition
;
QList
<
TextEditor
::
CompletionItem
>
m_completions
;
Qt
::
CaseSensitivity
m_caseSensitivity
;
};
}
// end of namespace Internal
}
// end of namespace DuiEditor
#endif // DUICODECOMPLETION_H
src/plugins/duieditor/duieditor.cpp
0 → 100644
View file @
df16c1b6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**
**************************************************************************/
#include
"duieditor.h"
#include
"duieditorconstants.h"
#include
"duihighlighter.h"
#include
"duieditorplugin.h"
#include
"parser/javascriptengine_p.h"
#include
"parser/javascriptparser_p.h"
#include
"parser/javascriptlexer_p.h"
#include
"parser/javascriptnodepool_p.h"
#include
"parser/javascriptastvisitor_p.h"
#include
"parser/javascriptast_p.h"
#include
<indenter.h>
#include
<coreplugin/icore.h>
#include
<coreplugin/actionmanager/actionmanager.h>
#include
<texteditor/basetextdocument.h>
#include
<texteditor/fontsettings.h>
#include
<texteditor/textblockiterator.h>
#include
<texteditor/texteditorconstants.h>
#include
<texteditor/texteditorsettings.h>
#include
<QtCore/QTimer>
#include
<QtCore/QtDebug>
#include
<QtGui/QMenu>
#include
<QtGui/QComboBox>
enum
{
UPDATE_DOCUMENT_DEFAULT_INTERVAL
=
250
};
using
namespace
JavaScript
::
AST
;
namespace
DuiEditor
{
namespace
Internal
{
class
FindDeclarations
:
protected
Visitor
{
QList
<
Declaration
>
declarations
;
public:
QList
<
Declaration
>
accept
(
JavaScript
::
AST
::
Node
*
node
)
{
JavaScript
::
AST
::
Node
::
acceptChild
(
node
,
this
);
return
declarations
;
}
protected:
using
Visitor
::
visit
;
virtual
bool
visit
(
FunctionExpression
*
)
{
return
false
;
}
virtual
bool
visit
(
FunctionDeclaration
*
ast
)
{
if
(
!
ast
->
name
)
return
false
;
QString
text
=
ast
->
name
->
asString
();
text
+=
QLatin1Char
(
'('
);
for
(
FormalParameterList
*
it
=
ast
->
formals
;
it
;
it
=
it
->
next
)
{
if
(
it
->
name
)
text
+=
it
->
name
->
asString
();
if
(
it
->
next
)
text
+=
QLatin1String
(
", "
);
}
text
+=
QLatin1Char
(
')'
);
Declaration
d
;
d
.
text
=
text
;
#if 0 // ### FIXME
d.startLine = ast->startLine;
d.startColumn = ast->startColumn;
d.endLine = ast->endLine;
d.endColumn = ast->endColumn;
#endif
declarations
.
append
(
d
);
return
false
;
}
virtual
bool
visit
(
VariableDeclaration
*
ast
)
{
if
(
!
ast
->
name
)
return
false
;
Declaration
d
;
d
.
text
=
ast
->
name
->
asString
();
#if 0 // ### FIXME
d.startLine= ast->startLine;
d.startColumn = ast->startColumn;
d.endLine = ast->endLine;
d.endColumn = ast->endColumn;
#endif
declarations
.
append
(
d
);
return
false
;
}
};
ScriptEditorEditable
::
ScriptEditorEditable
(
ScriptEditor
*
editor
,
const
QList
<
int
>&
context
)
:
BaseTextEditorEditable
(
editor
),
m_context
(
context
)
{
}
ScriptEditor
::
ScriptEditor
(
const
Context
&
context
,
QWidget
*
parent
)
:
TextEditor
::
BaseTextEditor
(
parent
),
m_context
(
context
),
m_methodCombo
(
0
)
{
setParenthesesMatchingEnabled
(
true
);
setMarksVisible
(
true
);
setCodeFoldingSupported
(
true
);
setCodeFoldingVisible
(
true
);
setMimeType
(
DuiEditor
::
Constants
::
C_DUIEDITOR_MIMETYPE
);
m_updateDocumentTimer
=
new
QTimer
(
this
);
m_updateDocumentTimer
->
setInterval
(
UPDATE_DOCUMENT_DEFAULT_INTERVAL
);
m_updateDocumentTimer
->
setSingleShot
(
true
);
connect
(
m_updateDocumentTimer
,
SIGNAL
(
timeout
()),
this
,
SLOT
(
updateDocumentNow
()));
connect
(
this
,
SIGNAL
(
textChanged
()),
this
,
SLOT
(
updateDocument
()));
baseTextDocument
()
->
setSyntaxHighlighter
(
new
DuiHighlighter
);
}
ScriptEditor
::~
ScriptEditor
()
{
}
QList
<
Declaration
>
ScriptEditor
::
declarations
()
const
{
return
m_declarations
;
}
QStringList
ScriptEditor
::
words
()
const
{
return
m_words
;
}
Core
::
IEditor
*
ScriptEditorEditable
::
duplicate
(
QWidget
*
parent
)
{
ScriptEditor
*
newEditor
=
new
ScriptEditor
(
m_context
,
parent
);
newEditor
->
duplicateFrom
(
editor
());
DuiEditorPlugin
::
instance
()
->
initializeEditor
(
newEditor
);
return
newEditor
->
editableInterface
();
}
const
char
*
ScriptEditorEditable
::
kind
()
const
{
return
DuiEditor
::
Constants
::
C_DUIEDITOR
;
}
ScriptEditor
::
Context
ScriptEditorEditable
::
context
()
const
{
return
m_context
;
}
void
ScriptEditor
::
updateDocument
()
{
m_updateDocumentTimer
->
start
(
UPDATE_DOCUMENT_DEFAULT_INTERVAL
);
}
void
ScriptEditor
::
updateDocumentNow
()
{
// ### move in the parser thread.
m_updateDocumentTimer
->
stop
();
const
QString
fileName
=
file
()
->
fileName
();
const
QString
code
=
toPlainText
();
JavaScriptParser
parser
;
JavaScriptEnginePrivate
driver
;
JavaScript
::
NodePool
nodePool
(
fileName
,
&
driver
);
driver
.
setNodePool
(
&
nodePool
);
JavaScript
::
Lexer
lexer
(
&
driver
);
lexer
.
setCode
(
code
,
/*line = */
1
);
driver
.
setLexer
(
&
lexer
);
if
(
parser
.
parse
(
&
driver
))
{
FindDeclarations
decls
;
m_declarations
=
decls
.
accept
(
driver
.
ast
());
m_words
.
clear
();
foreach
(
const
JavaScriptNameIdImpl
&
id
,
driver
.
literals
())
m_words
.
append
(
id
.
asString
());
QStringList
items
;
items
.
append
(
tr
(
"<Select Symbol>"
));
foreach
(
Declaration
decl
,
m_declarations
)
items
.
append
(
decl
.
text
);
m_methodCombo
->
clear
();
m_methodCombo
->
addItems
(
items
);
updateMethodBoxIndex
();
}
QList
<
QTextEdit
::
ExtraSelection
>
selections
;
QTextCharFormat
errorFormat
;
errorFormat
.
setUnderlineColor
(
Qt
::
red
);
errorFormat
.
setUnderlineStyle
(
QTextCharFormat
::
WaveUnderline
);
QTextEdit
::
ExtraSelection
sel
;
foreach
(
const
JavaScriptParser
::
DiagnosticMessage
&
d
,
parser
.
diagnosticMessages
())
{
if
(
d
.
isWarning
())
continue
;
int
line
=
d
.
line
;
int
column
=
d
.
column
;
if
(
column
==
0
)
column
=
1
;
QTextCursor
c
(
document
()
->
findBlockByNumber
(
line
-
1
));
sel
.
cursor
=
c
;
sel
.
cursor
.
setPosition
(
c
.
position
()
+
column
-
1
);
sel
.
cursor
.
movePosition
(
QTextCursor
::
EndOfWord
,
QTextCursor
::
KeepAnchor
);
sel
.
format
=
errorFormat
;
selections
.
append
(
sel
);
}
setExtraSelections
(
CodeWarningsSelection
,
selections
);
}
void
ScriptEditor
::
jumpToMethod
(
int
index
)
{
if
(
index
)
{
Declaration
d
=
m_declarations
.
at
(
index
-
1
);
gotoLine
(
d
.
startLine
,
d
.
startColumn
-
1
);
setFocus
();
}
}
void
ScriptEditor
::
updateMethodBoxIndex
()
{
int
line
=
0
,
column
=
0
;
convertPosition
(
position
(),
&
line
,
&
column
);
int
currentSymbolIndex
=
0
;
int
index
=
0
;
while
(
index
<
m_declarations
.
size
())
{
const
Declaration
&
d
=
m_declarations
.
at
(
index
++
);
if
(
line
<
d
.
startLine
)
break
;
else
currentSymbolIndex
=
index
;
}
m_methodCombo
->
setCurrentIndex
(
currentSymbolIndex
);
}
void
ScriptEditor
::
updateMethodBoxToolTip
()
{
}
void
ScriptEditor
::
updateFileName
()
{
}
void
ScriptEditor
::
setFontSettings
(
const
TextEditor
::
FontSettings
&
fs
)
{
TextEditor
::
BaseTextEditor
::
setFontSettings
(
fs
);
DuiHighlighter
*
highlighter
=
qobject_cast
<
DuiHighlighter
*>
(
baseTextDocument
()
->
syntaxHighlighter
());
if
(
!
highlighter
)
return
;
static
QVector
<
QString
>
categories
;
if
(
categories
.
isEmpty
())
{
categories
<<
QLatin1String
(
TextEditor
::
Constants
::
C_NUMBER
)
<<
QLatin1String
(
TextEditor
::
Constants
::
C_STRING
)
<<
QLatin1String
(
TextEditor
::
Constants
::
C_TYPE
)
<<
QLatin1String
(
TextEditor
::
Constants
::
C_KEYWORD
)
<<
QLatin1String
(
TextEditor
::
Constants
::
C_PREPROCESSOR
)
<<
QLatin1String
(
TextEditor
::
Constants
::
C_LABEL
)
<<
QLatin1String
(
TextEditor
::
Constants
::
C_COMMENT
);
}
highlighter
->
setFormats
(
fs
.
toTextCharFormats
(
categories
));
highlighter
->
rehighlight
();
}
bool
ScriptEditor
::
isElectricCharacter
(
const
QChar
&
ch
)
const
{
if
(
ch
==
QLatin1Char
(
'{'
)
||
ch
==
QLatin1Char
(
'}'
))
return
true
;
return
false
;
}
// Indent a code line based on previous
template
<
class
Iterator
>
static
void
indentScriptBlock
(
const
TextEditor
::
TabSettings
&
ts
,
const
QTextBlock
&
block
,
const
Iterator
&
programBegin
,
const
Iterator
&
programEnd
,
QChar
typedChar
)
{
typedef
typename
SharedTools
::
Indenter
<
Iterator
>
Indenter
;
Indenter
&
indenter
=
Indenter
::
instance
();
indenter
.
setTabSize
(
ts
.
m_tabSize
);
indenter
.
setIndentSize
(
ts
.
m_indentSize
);
const
TextEditor
::
TextBlockIterator
current
(
block
);
const
int
indent
=
indenter
.
indentForBottomLine
(
current
,
programBegin
,
programEnd
,
typedChar
);
ts
.
indentLine
(
block
,
indent
);
}
void
ScriptEditor
::
indentBlock
(
QTextDocument
*
doc
,
QTextBlock
block
,
QChar
typedChar
)
{
#if 0
const TextEditor::TextBlockIterator begin(doc->begin());
const TextEditor::TextBlockIterator end(block.next());
indentScriptBlock(tabSettings(), block, begin, end, typedChar);
#else
Q_UNUSED
(
doc
)
Q_UNUSED
(
typedChar
)
TextEditor
::
TabSettings
ts
=
tabSettings
();
const
int
tabSize
=
qMax
(
1
,
ts
.
m_tabSize
);
int
indent
=
0
;
QTextBlock
it
=
block
.
previous
();
for
(;
it
.
isValid
();
it
=
it
.
previous
())
{
const
QString
blockText
=
it
.
text
();
if
(
!
blockText
.
isEmpty
())
{
for
(
int
i
=
0
;
i
<
blockText
.
length
();
++
i
)
{
const
QChar
ch
=
blockText
.
at
(
i
);
if
(
ch
==
QLatin1Char
(
'\t'
))
indent
+=
(
indent
+
tabSize
)
%
tabSize
;