IDE调试功能开发总结

2020-02-22  本文已影响0人  环宇飞杨

效果图展示

调试页面

需求分析

功能调研

可能开始查找方式不太对,没找到什么好的解决方案,去App store上找了一圈,数量极少,且多以收费内容居多,想着应该是极小众的需求,只能自己去看编译前端知识,好在高亮功能只涉及词法分析,于是拜读《编译原理》,看token解析过程、有限状态机的工作原理。但始终对于规则制定方面很是担心,一是对正则语法不太熟悉,担心漏判,导致高亮功能不能全部覆盖所有场景,二是开发周期长,补充知识是个很漫长的过程,高亮需求本属于用户体验提升的部分,为实现这么个功能放一两个月的开发时间也是够呛。

高亮功能

方案1

  1. 正则匹配文本,得到 注释、字符串、数字、运算符、关键字等 (每种语言写一套)
  2. 依次匹配对应色值。
  3. 编辑时监听变化,重复执行1、2步骤。

这样最简单,但主要的缺点是不支持词和词 之间的关系 比如:

Class Student : NSObject

以上Student按理也应该变色的,但是匹配不到

方案2

Google搜到了js开源框架 Highlight.js ,支持一百多种语言,一百多种主题,市场上流行的IDE色调基本都支持,且有iOS移植框架可以直接使用,项目地址
,但悲催的是需要支持的 meta语言 是公司自己开发的,想要完美适配必须要改js源代码了,源代码500+k,并非小项目,想必改动的工作量也不少。

移植框架的工作原理为:

  1. 其中Highlight类,使用JavaScriptCore框架解析Highlight.js 执行js脚本将代码文本转换为html。
  2. 内部类Theme 解析主题对应的css文件,给NSAttributedString设置颜色字体内容
  3. 内部类CodeAttributeString 遍历span标签,将html转换为NSAttributedString,CodeAttributeString继承自NSTextStorage,通过重写processEditing方法实现实时编辑后的高亮显示。(非全局监听,只监听编辑所在行,很高效)
  4. 将CodeAttributeString设置为UITextview的textContainer属性。
    self.textStorage = [[CodeAttributedString alloc] init];
    
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [self.textStorage addLayoutManager:layoutManager];

    NSTextContainer *textContainer = [[NSTextContainer alloc] init];
    [layoutManager addTextContainer:textContainer];

    self.textView = [[CodeTextView alloc] initWithFrame:self.view.bounds textContainer:textContainer];
  1. 调用setText:实现高亮显示。

断点功能

1.代码行数显示

Highlight框架居然不带行数显示,所以断点没出打,没办法必须加功能。这里有个误区就是代码行数和文字行数并不是一对一的关系,屏幕宽度不一样,代码伸展的size也不一样,所以会有一行代码对应多行文本的情况。

方案1

新建左侧NumberLineView继承自ScrollView,将屏幕空间划分为两部分,右侧textView给左侧提供行数内容,左侧跟随右侧滑动显示。

此方案太low,代码量和逻辑估计也不少,跳过。

方案2

  1. 使用单一UITextview实现,先设置contentInset属性将文字部分右移。
  2. 遍历文字range 得到行数及对应rect信息。
//这块被坑了很久,不知道有系统api已经实现,还打算逐个遍历字符根据屏幕宽度计算rect....
    [self.text enumerateSubstringsInRange:NSMakeRange(0, self.text.length) options:NSStringEnumerationByParagraphs usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
        NSRange range = [self.layoutManager glyphRangeForCharacterRange:substringRange actualCharacterRange:nil];
        CGRect rect = [self.layoutManager boundingRectForGlyphRange:range inTextContainer:self.textContainer];
        //此处 rect 就是每一行文字对应的矩形,随屏幕宽度变化而变化
    }];
获取rect效果图
行数类结构
  1. 重写drawRect方法,将行数文字和rect绘制到左侧空余部分。
- (void)drawRect:(CGRect)rect
{
...
    for (LineNumberModel *model in self.lineModelArray){
          [model.lineStr drawInRect:model.rectValve.CGRectValue];
    }
...
}

2.断点控制

  1. id //不解释
  2. 脚本id //收到远程断点的时候,需要展示对应脚本信息
  3. 行数 //用于和左侧行数栏匹配显示断点
  4. 状态 //用于显示断点启用状态

选中单行功能

  1. 自动选中
    监听到远程中断事件后,正常情况下需要做一系列的定位操作,包括打开页面、展示数据、获取对应脚本、定位到所在行等。
    另外还需要考虑异常场景,所以开始写逻辑前,最好先多想一些测试用例,比如通过断点定位不到脚本的情况,脚本和行数对应不上的情况,或者定位过程中,用户正在操作UI时的情况。
    最怕写的过程中,因为突然想起少考虑了一个重要场景,出现各种花式适配.....这种情况多了,就成了加班的噩梦。所以还不如开始就耐着性子画些脑图,流程图把自己脑子先收拾清楚再说。
  2. 点击选中
    这个比较简单了,textView增加点击手势,遍历数组判断点击区域是否属于行数内某rect范围内,是的话就标记到drawRect里绘制。


    点击选中42行

有个冲突的地方是增加tap手势以后,textView就不能响应弹出键盘了,所以还需要在非代码行数的范围内主动弹出键盘,并将光标定位在点击位置,也是现有API。

if (isNotNumberViewRect){
    self.editable = YES;
    [self becomeFirstResponder];
    CGPoint point = [tap locationInView:tap.view];
    UITextRange *range = [self characterRangeAtPoint:point];
    self.selectedTextRange = range; 
}
  1. 软键盘快捷输入
    软键盘写代码主要有以下缺点:
  1. 检测\n符号
  2. 找出换行前最后一个非空白字符是不是左花括号:
  3. 插入tab并再次换行
  4. 补齐右花括号

远程调试

调试流程

  1. 启动调试
  2. 启动长连接监听远程事件。(断点、异常,或日志)
  3. 断点操作。(增加,删除,启用,禁用)
  4. 收到远程事件,定位UI。
  5. 点击断点下一步、继续的执行逻辑。
上一篇 下一篇

猜你喜欢

热点阅读