/**
* 920 Text Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 920 Text Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 920 Text Editor. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jecelyin.highlight;
import android.graphics.Color;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.util.Log;
import com.jecelyin.colorschemes.ColorScheme;
import com.jecelyin.editor.EditorSettings;
import com.jecelyin.editor.JecEditor;
import com.jecelyin.util.FileUtil;
import com.jecelyin.widget.ForegroundColorSpan;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
public class Highlight
{
static {
System.loadLibrary("highlight");
}
public static final int GROUP_TAG_ID = 1;
public static final int GROUP_COMMENT_ID = 2;
public static final int GROUP_STRING_ID = 3;
public static final int GROUP_KEYWORD_ID = 4;
public static final int GROUP_FUNCTION_ID = 5;
public static final int GROUP_ATTR_NAME_ID = 6;
private final static String TAG = "Highlight";
private static HashMap<String, String[]> langTab;
private static ArrayList<String[]> nameTab;
private static int color_tag;
private static int color_string;
private static int color_keyword;
private static int color_function;
private static int color_comment;
private static int color_attr_name;
private String mExt = "";
private int mEndOffset = -1;
private int mStartOffset = -1;
private boolean mStop = true;
private static int mLimitFileSize = 0; //单位:KB
private static ArrayList<ForegroundColorSpan> mSpans = new ArrayList<ForegroundColorSpan>();
public static void init()
{
loadLang();
loadColorScheme();
}
public void setSyntaxType(String file_extension)
{
this.mExt = file_extension;
}
public void stop()
{
this.mStop = true;
}
public void redraw()
{
this.mStop = false;
this.mEndOffset = -1;
this.mStartOffset = -1;
}
/**
*
* @param j
* @param mLayout
* @return 返回[[高亮类型,开始offset, 结束offset],,]
*/
public boolean render(Spannable mText, int startOffset, int endOffset)
{
if(!EditorSettings.ENABLE_HIGHLIGHT || langTab == null)
return false;
if(this.mStop || "".equals(this.mExt))
return false;
if(this.mStartOffset <= startOffset && this.mEndOffset >= endOffset)
return false;
String[] lang = langTab.get(this.mExt);
if(lang == null)
{
return false;
}
//lock it 不然会因为添加了span后导致offset改变,不断地进行高亮
this.mStop = true;
//TimerUtil.start();
//Log.d(TAG, startOffset+"="+endOffset);
this.mStartOffset = startOffset;
this.mEndOffset = endOffset;
String text = mText.subSequence(0, endOffset).toString();
int[] ret = jni_parse(text, JecEditor.TEMP_PATH + File.separator + lang[1]);
//TimerUtil.stop("hg parse");
if(ret == null)
{
this.mStop = false;
return false;
}
int len = ret.length;
if(len < 1 || len % 3.0F != 0)
{
this.mStop = false;
return false;
}
//TimerUtil.start();
//不能清除全陪,因为滚动条需要一个span来按住拖动
//mText.clearSpans();
int color;
int start;
int end;
int index=0;
int bufLen = mSpans.size();
ForegroundColorSpan fcs;
for(ForegroundColorSpan fcs2:mSpans)
{
mText.removeSpan(fcs2);
}
for(int i=0; i<len; i++)
{
switch(ret[i])
{
case GROUP_TAG_ID:
color = color_tag;
break;
case GROUP_STRING_ID:
color = color_string;
break;
case GROUP_KEYWORD_ID:
color = color_keyword;
break;
case GROUP_FUNCTION_ID:
color = color_function;
break;
case GROUP_COMMENT_ID:
color = color_comment;
break;
case GROUP_ATTR_NAME_ID:
color = color_attr_name;
break;
default:
Log.d(TAG, "获取颜色group id失败");
mStop = false;
return false;
}
start = ret[++i];
end = ret[++i];
if(end < startOffset)
continue;
if(index >= bufLen)
{
fcs = new ForegroundColorSpan(color);
mSpans.add(fcs);
} else {
fcs = mSpans.get(index);
fcs.setColor(color);
}
++index;
try {
mText.setSpan(fcs, start, end, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch(Exception e) {
}
}
ret = null;
//TimerUtil.stop("hg 1");
this.mStop = false;
return true;
}
/**
* [{语法名称,其中一个扩展名},,]
*/
public static ArrayList<String[]> getLangList()
{
return nameTab;
}
public static boolean loadLang()
{
String langfile = JecEditor.TEMP_PATH + "/lang.conf";
File file = new File(langfile);
if(!file.isFile())
{
return false;
}
file = null;
langTab = new HashMap<String, String[]>();
nameTab = new ArrayList<String[]>();
try
{
String mData = FileUtil.readFileAsString(langfile, "utf-8");
String[] lines = mData.split("\n");
String[] cols;
for(String line:lines)
{
line = line.trim();
if(line.startsWith("#"))
continue;
cols = line.split(":");
String name = cols[0].trim();
String synfile = cols[1].trim();
String extsString = cols[2].trim();
String[] exts = extsString.split("\\s+");
nameTab.add(new String[] {name, exts[0]});
for(String ext:exts)
{
langTab.put(ext, new String[]{name, synfile});
}
}
Collections.sort(nameTab, new Comparator<String[]>() {
@Override
public int compare(String[] object1, String[] object2)
{
return object1[0].compareToIgnoreCase(object2[2]);
}
});
}catch (Exception e)
{
return false;
}
return true;
}
public static void loadColorScheme()
{
//色彩模块
color_tag = Color.parseColor(ColorScheme.color_tag);
color_string = Color.parseColor(ColorScheme.color_string);
color_keyword = Color.parseColor(ColorScheme.color_keyword);
color_function = Color.parseColor(ColorScheme.color_function);
color_comment = Color.parseColor(ColorScheme.color_comment);
color_attr_name = Color.parseColor(ColorScheme.color_attr_name);
}
public static String getNameByExt(String ext)
{
if(langTab == null)
{
loadLang();
return "";
}
String[] info = langTab.get(ext);
if(info == null)
{
return "";
}
return info[0];
}
private native static int[] jni_parse(String text, String syntaxFile);
}