123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- #
- #
- # The Nim Compiler
- # (c) Copyright 2015 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## This module implements the code "prettifier". This is part of the toolchain
- ## to convert Nim code into a consistent style.
- import
- strutils, os, intsets, strtabs
- import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs,
- compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents
- type
- StyleCheck* {.pure.} = enum None, Warn, Auto
- var
- gOverWrite* = true
- gStyleCheck*: StyleCheck
- gCheckExtern*, gOnlyMainfile*: bool
- proc overwriteFiles*() =
- let doStrip = options.getConfigVar("pretty.strip").normalize == "on"
- for i in 0 .. high(gSourceFiles):
- if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and
- (not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx):
- let newFile = if gOverWrite: gSourceFiles[i].fullpath
- else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim")
- try:
- var f = open(newFile, fmWrite)
- for line in gSourceFiles[i].lines:
- if doStrip:
- f.write line.strip(leading = false, trailing = true)
- else:
- f.write line
- f.write(gSourceFiles[i].newline)
- f.close
- except IOError:
- rawMessage(errCannotOpenFile, newFile)
- proc `=~`(s: string, a: openArray[string]): bool =
- for x in a:
- if s.startsWith(x): return true
- proc beautifyName(s: string, k: TSymKind): string =
- # minimal set of rules here for transition:
- # GC_ is allowed
- let allUpper = allCharsInSet(s, {'A'..'Z', '0'..'9', '_'})
- if allUpper and k in {skConst, skEnumField, skType}: return s
- result = newStringOfCap(s.len)
- var i = 0
- case k
- of skType, skGenericParam:
- # Types should start with a capital unless builtins like 'int' etc.:
- if s =~ ["int", "uint", "cint", "cuint", "clong", "cstring", "string",
- "char", "byte", "bool", "openArray", "seq", "array", "void",
- "pointer", "float", "csize", "cdouble", "cchar", "cschar",
- "cshort", "cu", "nil", "expr", "stmt", "typedesc", "auto", "any",
- "range", "openarray", "varargs", "set", "cfloat"
- ]:
- result.add s[i]
- else:
- result.add toUpperAscii(s[i])
- of skConst, skEnumField:
- # for 'const' we keep how it's spelt; either upper case or lower case:
- result.add s[0]
- else:
- # as a special rule, don't transform 'L' to 'l'
- if s.len == 1 and s[0] == 'L': result.add 'L'
- elif '_' in s: result.add(s[i])
- else: result.add toLowerAscii(s[0])
- inc i
- while i < s.len:
- if s[i] == '_':
- if i > 0 and s[i-1] in {'A'..'Z'}:
- # don't skip '_' as it's essential for e.g. 'GC_disable'
- result.add('_')
- inc i
- result.add s[i]
- else:
- inc i
- result.add toUpperAscii(s[i])
- elif allUpper:
- result.add toLowerAscii(s[i])
- else:
- result.add s[i]
- inc i
- proc replaceInFile(info: TLineInfo; newName: string) =
- loadFile(info)
- let line = gSourceFiles[info.fileIndex].lines[info.line-1]
- var first = min(info.col.int, line.len)
- if first < 0: return
- #inc first, skipIgnoreCase(line, "proc ", first)
- while first > 0 and line[first-1] in prettybase.Letters: dec first
- if first < 0: return
- if line[first] == '`': inc first
- let last = first+identLen(line, first)-1
- if differ(line, first, last, newName):
- # last-first+1 != newName.len or
- var x = line.substr(0, first-1) & newName & line.substr(last+1)
- system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
- gSourceFiles[info.fileIndex].dirty = true
- proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
- let beau = beautifyName(s, k)
- if s != beau:
- if gStyleCheck == StyleCheck.Auto:
- sym.name = getIdent(beau)
- replaceInFile(info, beau)
- else:
- message(info, hintName, beau)
- proc styleCheckDefImpl(info: TLineInfo; s: PSym; k: TSymKind) =
- # operators stay as they are:
- if k in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return
- if k in {skType, skGenericParam} and sfAnon in s.flags: return
- if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
- checkStyle(info, s.name.s, k, s)
- template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) =
- when defined(nimfix):
- if gStyleCheck != StyleCheck.None: styleCheckDefImpl(info, s, k)
- template styleCheckDef*(info: TLineInfo; s: PSym) =
- styleCheckDef(info, s, s.kind)
- template styleCheckDef*(s: PSym) =
- styleCheckDef(s.info, s, s.kind)
- proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
- if info.fileIndex < 0: return
- # we simply convert it to what it looks like in the definition
- # for consistency
- # operators stay as they are:
- if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters:
- return
- if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
- let newName = s.name.s
- replaceInFile(info, newName)
- #if newName == "File": writeStackTrace()
- template styleCheckUse*(info: TLineInfo; s: PSym) =
- when defined(nimfix):
- if gStyleCheck != StyleCheck.None: styleCheckUseImpl(info, s)
|