6 changed files with 278 additions and 8 deletions
@ -0,0 +1,237 @@
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// InsertionPoint.cs
|
||||
//
|
||||
// Author:
|
||||
// Mike Krüger <mkrueger@novell.com>
|
||||
//
|
||||
// Copyright (c) 2010 Novell, Inc (http://www.novell.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using ICSharpCode.NRefactory; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
|
||||
namespace CSharpBinding.Refactoring |
||||
{ |
||||
public enum NewLineInsertion |
||||
{ |
||||
None, |
||||
Eol, |
||||
BlankLine |
||||
} |
||||
|
||||
public class InsertionPoint |
||||
{ |
||||
public TextLocation Location { |
||||
get; |
||||
set; |
||||
} |
||||
|
||||
public NewLineInsertion LineBefore { get; set; } |
||||
public NewLineInsertion LineAfter { get; set; } |
||||
|
||||
public InsertionPoint (TextLocation location, NewLineInsertion lineBefore, NewLineInsertion lineAfter) |
||||
{ |
||||
this.Location = location; |
||||
this.LineBefore = lineBefore; |
||||
this.LineAfter = lineAfter; |
||||
} |
||||
|
||||
public override string ToString () |
||||
{ |
||||
return string.Format ("[InsertionPoint: Location={0}, LineBefore={1}, LineAfter={2}]", Location, LineBefore, LineAfter); |
||||
} |
||||
|
||||
void InsertNewLine (IDocument document, NewLineInsertion insertion, ref int offset) |
||||
{ |
||||
string eolMarker = DocumentUtilitites.GetLineTerminator(document, 1); |
||||
string str = null; |
||||
switch (insertion) { |
||||
case NewLineInsertion.Eol: |
||||
str = eolMarker; |
||||
break; |
||||
case NewLineInsertion.BlankLine: |
||||
str = eolMarker + eolMarker; |
||||
break; |
||||
default: |
||||
return; |
||||
} |
||||
|
||||
document.Insert (offset, str); |
||||
offset += str.Length; |
||||
} |
||||
|
||||
public void Insert (IDocument document, string text) |
||||
{ |
||||
int offset = document.GetOffset (Location); |
||||
using (var undo = document.OpenUndoGroup ()) { |
||||
text = DocumentUtilitites.NormalizeNewLines(text, document, Location.Line); |
||||
|
||||
var line = document.GetLineByOffset (offset); |
||||
int insertionOffset = line.Offset + Location.Column - 1; |
||||
offset = insertionOffset; |
||||
InsertNewLine (document, LineBefore, ref offset); |
||||
|
||||
document.Insert (offset, text); |
||||
offset += text.Length; |
||||
InsertNewLine (document, LineAfter, ref offset); |
||||
} |
||||
} |
||||
|
||||
public static List<InsertionPoint> GetInsertionPoints (IDocument document, IUnresolvedTypeDefinition type) |
||||
{ |
||||
if (type == null) |
||||
throw new ArgumentNullException ("type"); |
||||
|
||||
// update type from parsed document, since this is always newer.
|
||||
//type = parsedDocument.GetInnermostTypeDefinition (type.GetLocation ()) ?? type;
|
||||
|
||||
List<InsertionPoint > result = new List<InsertionPoint> (); |
||||
int offset = document.GetOffset (type.Region.Begin); |
||||
if (offset < 0) |
||||
return result; |
||||
while (offset < document.TextLength && document.GetCharAt (offset) != '{') { |
||||
offset++; |
||||
} |
||||
|
||||
var realStartLocation = document.GetLocation (offset); |
||||
result.Add (GetInsertionPosition (document, realStartLocation.Line, realStartLocation.Column)); |
||||
result [0].LineBefore = NewLineInsertion.None; |
||||
|
||||
foreach (var member in type.Members) { |
||||
TextLocation domLocation = member.BodyRegion.End; |
||||
if (domLocation.Line <= 0) { |
||||
domLocation = member.Region.End; |
||||
} |
||||
result.Add (GetInsertionPosition (document, domLocation.Line, domLocation.Column)); |
||||
} |
||||
result [result.Count - 1].LineAfter = NewLineInsertion.None; |
||||
CheckStartPoint (document, result [0], result.Count == 1); |
||||
if (result.Count > 1) { |
||||
result.RemoveAt (result.Count - 1); |
||||
NewLineInsertion insertLine; |
||||
var lineBefore = GetLineOrNull(document, type.BodyRegion.EndLine - 1); |
||||
if (lineBefore != null && lineBefore.Length == GetLineIndent(document, lineBefore).Length) { |
||||
insertLine = NewLineInsertion.None; |
||||
} else { |
||||
insertLine = NewLineInsertion.Eol; |
||||
} |
||||
// search for line start
|
||||
var line = document.GetLineByNumber (type.BodyRegion.EndLine); |
||||
int col = type.BodyRegion.EndColumn - 1; |
||||
while (col > 1 && char.IsWhiteSpace (document.GetCharAt (line.Offset + col - 2))) |
||||
col--; |
||||
result.Add (new InsertionPoint (new TextLocation (type.BodyRegion.EndLine, col), insertLine, NewLineInsertion.Eol)); |
||||
CheckEndPoint (document, result [result.Count - 1], result.Count == 1); |
||||
} |
||||
|
||||
/*foreach (var region in parsedDocument.UserRegions.Where (r => type.BodyRegion.IsInside (r.Region.Begin))) { |
||||
result.Add (new InsertionPoint (new DocumentLocation (region.Region.BeginLine + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol)); |
||||
result.Add (new InsertionPoint (new DocumentLocation (region.Region.EndLine, 1), NewLineInsertion.Eol, NewLineInsertion.Eol)); |
||||
result.Add (new InsertionPoint (new DocumentLocation (region.Region.EndLine + 1, 1), NewLineInsertion.Eol, NewLineInsertion.Eol)); |
||||
}*/ |
||||
result.Sort ((left, right) => left.Location.CompareTo (right.Location)); |
||||
return result; |
||||
} |
||||
|
||||
static void CheckEndPoint (IDocument doc, InsertionPoint point, bool isStartPoint) |
||||
{ |
||||
var line = GetLineOrNull(doc, point.Location.Line); |
||||
if (line == null) |
||||
return; |
||||
|
||||
if (GetLineIndent (doc, line).Length + 1 < point.Location.Column) |
||||
point.LineBefore = NewLineInsertion.BlankLine; |
||||
if (point.Location.Column < line.Length + 1) |
||||
point.LineAfter = NewLineInsertion.Eol; |
||||
} |
||||
|
||||
static void CheckStartPoint (IDocument doc, InsertionPoint point, bool isEndPoint) |
||||
{ |
||||
var line = GetLineOrNull(doc, point.Location.Line); |
||||
if (line == null) |
||||
return; |
||||
if (GetLineIndent (doc, line).Length + 1 == point.Location.Column) { |
||||
int lineNr = point.Location.Line; |
||||
while (lineNr > 1 && GetLineIndent(doc, lineNr - 1).Length == doc.GetLineByNumber (lineNr - 1).Length) { |
||||
lineNr--; |
||||
} |
||||
line = GetLineOrNull(doc, lineNr); |
||||
point.Location = new TextLocation (lineNr, GetLineIndent (doc, line).Length + 1); |
||||
} |
||||
|
||||
if (GetLineIndent (doc, line).Length + 1 < point.Location.Column) |
||||
point.LineBefore = NewLineInsertion.Eol; |
||||
if (point.Location.Column < line.Length + 1) |
||||
point.LineAfter = isEndPoint ? NewLineInsertion.Eol : NewLineInsertion.BlankLine; |
||||
} |
||||
|
||||
static string GetLineIndent(IDocument doc, int lineNumber) |
||||
{ |
||||
return GetLineIndent(doc, GetLineOrNull(doc, lineNumber)); |
||||
} |
||||
|
||||
static string GetLineIndent(IDocument doc, IDocumentLine line) |
||||
{ |
||||
if (line == null) |
||||
return string.Empty; |
||||
else |
||||
return DocumentUtilitites.GetWhitespaceAfter(doc, line.Offset); |
||||
} |
||||
|
||||
static InsertionPoint GetInsertionPosition (IDocument doc, int line, int column) |
||||
{ |
||||
int bodyEndOffset = doc.GetOffset (line, column) + 1; |
||||
var curLine = GetLineOrNull(doc, line); |
||||
if (curLine != null) { |
||||
if (bodyEndOffset < curLine.EndOffset) { |
||||
// case1: positition is somewhere inside the start line
|
||||
return new InsertionPoint (new TextLocation (line, column + 1), NewLineInsertion.Eol, NewLineInsertion.BlankLine); |
||||
} |
||||
} |
||||
|
||||
// -> if position is at line end check next line
|
||||
var nextLine = GetLineOrNull (doc, line + 1); |
||||
if (nextLine == null) // check for 1 line case.
|
||||
return new InsertionPoint (new TextLocation (line, column + 1), NewLineInsertion.BlankLine, NewLineInsertion.BlankLine); |
||||
|
||||
for (int i = nextLine.Offset; i < nextLine.EndOffset; i++) { |
||||
char ch = doc.GetCharAt (i); |
||||
if (!char.IsWhiteSpace (ch)) { |
||||
// case2: next line contains non ws chars.
|
||||
return new InsertionPoint (new TextLocation (line + 1, 1), NewLineInsertion.Eol, NewLineInsertion.BlankLine); |
||||
} |
||||
} |
||||
// case3: whitespace line
|
||||
return new InsertionPoint (new TextLocation (line + 1, 1), NewLineInsertion.Eol, NewLineInsertion.None); |
||||
} |
||||
|
||||
static IDocumentLine GetLineOrNull(IDocument doc, int lineNumber) |
||||
{ |
||||
if (lineNumber >= 1 && lineNumber <= doc.LineCount) |
||||
return doc.GetLineByNumber(lineNumber); |
||||
else |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue